;**************************************** ; ; Unet_KBD ; ======== ; ; Software for Keyboard Interface Module ; (Toensmann Micronet Project) ; ; Copyright (c) 2009 ; by Carsten Toensmann ; ; History ; ------- ; 23.05.2009 V0.1 ; - Start of development ; 05.06.2009 V0.1 ; - Display interface completed ; 15.06.2009 V0.1 ; - Keyboard interface and MIDI ; completed ; 27.06.2009 V0.1 ; - Interfaces for switch, joystick and ; rotary encoders completed ; 12.07.2009 V0.1 ; - EEPROM storage implemented ;**************************************** .include "8535def.inc";microcontroller ;***** definitions .equ PROGSTART = 0x000 .equ STACKP = 0x200 .equ VARIABLES = 0x201 .equ DATA_OUT = PORTC .equ DATA_IN = PINC .equ SWITCH = PINA .equ ENCODER = PINA .equ DATADIR = DDRC .equ ROWLEN = 0x40; 2 row lcd display row offset .equ MAXNOTES = 10; size of note buffer .equ BLOCK_0 = 0; keyboard block number 0 .equ BLOCK_1 = 1; keyboard block number 1 .equ BLOCK_2 = 2; keyboard block number 2 .equ BLOCK_3 = 3; keyboard block number 3 .equ BLOCK_4 = 4; keyboard block number 4 .equ BLOCK_5 = 5; keyboard block number 5 .equ BLOCK_6 = 6; keyboard block number 6 .equ BLOCK_7 = 7; keyboard block number 7 .equ ZERO = 0 .equ BLOCKLEN = 8; length of key block .equ NOTE_OFFSET = $24 .equ JOY_X = $03; ADC channel X axis of joystick .equ JOY_Y = $02; ADC channel Y axis of joystick .equ PITCHBEND = $e0; Pichbend controller byte .equ CNTRLCHANGE = $b0; Modulation controller change .equ MODULATIONHI = $01; Modulation controller MSB .equ MODULATIONLO = $21; Modulation controller LSB ;***** variables .DSEG .ORG VARIABLES notebuf: .byte MAXNOTES; memory area for note values velobuf: .byte 1; memory for velocity of key strokes noffsent: .byte 1; reminder flag for note off was sent adc_high: .byte 1; AD conversion result high byte adc_low: .byte 1; AD conversion result low byte joyx_high: .byte 1; X position high byte joyx_low: .byte 1; X position low byte joyy_high: .byte 1; Y position high byte joyy_low: .byte 1; Y position low byte encodval_1: .byte 1; scan of rotary encoder 1 encodval_2: .byte 1; scan of rotary encoder 2 switchpos: .byte 1; switch position (on / off) wpt: .byte 1; write pointer ring buffer rpt: .byte 1; read pointer ringbuffer kvs: .byte 1; current key velocity sensitivity jss: .byte 1; current joystick sensitivity ringbuf: .byte 32; ring buffer for incoming MIDI messages ;***** interrupt vectors .CSEG .ORG PROGSTART rjmp RESET ; Reset Handler rjmp EXT_INT0 ; IRQ0 Handler rjmp EXT_INT1 ; IRQ1 Handler rjmp TIM2_COMP ; Timer 2 Compare Handler rjmp TIM2_OVF ; Timer 2 Overflow Handler rjmp TIM1_CAPT ; Timer 1 Capture Handler rjmp TIM1_COMPA ; Timer 1 Compare A Handler rjmp TIM1_COMPB ; Timer 1 Compare B Handler rjmp TIM1_OVF ; Timer 1 Overflow Handler rjmp TIM0_OVF ; Timer 0 Overflow Handler rjmp SPI_STC ; SPI Transfer Complete Handler rjmp UART_RXC ; UART RX Complete Handler rjmp UART_DRE ; UDR Empty Handler rjmp UART_TXC ; UART TX Complete Handler rjmp CADC ; AD Conversion Complete Handler rjmp EE_RDY ; EEPROM Ready Handler rjmp ANA_COMP ; Analog Comparator Handler ;***** constants start_txt: .db "ToensUNet V1.0 #" joyx_txt: .db "X:# " joyy_txt: .db "Y:# " kvs_txt: .db " KVS:#"; Key Velocity Sensitivity jss_txt: .db " JSS:#"; JoyStick Sensitivity swe_txt: .db "E#"; switch position on 'external' swi_txt: .db "I#"; switch position on 'internal' key_values: .db $01,$09,$11,$19,$21,$29,$31,$39 .db $02,$0a,$12,$1a,$22,$2a,$32,$3a .db $03,$0b,$13,$1b,$23,$2b,$33,$3b .db $04,$0c,$14,$1c,$24,$2c,$34,$3c .db $05,$0d,$15,$1d,$25,$2d,$35,$3d .db $06,$0e,$16,$1e,$26,$2e,$36,$ff .db $07,$0f,$17,$1f,$27,$2f,$37,$ff .db $08,$10,$18,$20,$28,$30,$38,$ff ;**************************************** ; ;***** Main Program ; ;**************************************** START: ;-- set stackpointer ldi r16, high(STACKP) out SPH, r16 ldi r16, low(STACKP) out SPL, r16 ;-- configure chip operation ldi r16, $00 ; disable external interrupts out GIMSK, r16 ldi r16, $05 ; enable timer/counter 1 overflow interrupt and timer/counter 0 overflow interrupt out TIMSK, r16 ldi r16, $40 ; enable sleep function out MCUCR, r16 ldi r16, $00 ; timer/counter 1 no compare no pwm out TCCR1A, r16 out TCCR1B, r16; stop timer/counter 1 ; Port direction configuration ; ---------------------------- ;-- PORT A ldi r16,$20; Pin 5 input, rest output out DDRA,r16 ;-- PORT B ser r16; all pins output out DDRB, r16 ;-- PORT C: data bus, controlled by function rcall set_databus_read ;-- PORT D ldi r16,$FE; PORTD Pin 0(RXD) input, rest output out DDRD, r16 bset 7 ; enable interrupts ;-- Initialization routines rcall init_keyboard rcall init_display rcall init_midi rcall init_encoder rcall init_switch rcall init_joystick MAIN: rcall check_switch ;-- load switch position from memory ldi xl,low(switchpos) ldi xh,high(switchpos) ld r16,x tst r16; switch on? breq external_operation ;-- internal operation rcall check_joystick rcall check_encoder rcall clear_flags; clear 'key is still pressed' flag in note buffer rcall scan_kbd_block_0 rcall scan_kbd_block_1 rcall scan_kbd_block_2 rcall scan_kbd_block_3 rcall scan_kbd_block_4 rcall scan_kbd_block_5 rcall scan_kbd_block_6 rcall scan_kbd_block_7 rcall note_off; send 'note off' for all released keys rjmp MAIN ;-- external operation external_operation: rcall midi_mon rjmp MAIN STOP: rjmp STOP ; program end - stop - in case of leaving main scheduler ;**************************************** ; ;***** Common functions ; ;**************************************** ;---------------------------------------- ; Function int_division (A / B) ; ; Well, not a real division, more a ; weightening ; ; Parameter: r17: value high byte ; r16: value low byte ; r18: 'divisor' ;---------------------------------------- int_division: push r19 push r20 cpi r18, $10; full sensitivity breq int_division_exit lsr r16; r17:r16 :/ 64 => r16 lsr r16 lsr r16 lsr r16 lsr r16 lsr r16 lsl r17 lsl r17 add r16,r17 mov r20,r16 clr r17 clr r19 clr r16 int_division_01: cp r19,r18 breq int_division_exit add r16,r20 inc r19 rjmp int_division_01 int_division_exit: pop r20 pop r19 ret ;---------------------------------------- ; Function init_joystick ; ; Write init value to joystick variables ; to force joystick handling ;---------------------------------------- init_joystick: push xl push xh push r16 ser r16; write $ff to joystick variables ldi xl,low(joyx_low); joy X low value ldi xh,high(joyx_low) st x,r16 ldi xl,low(joyx_high); joy X high value ldi xh,high(joyx_high) st x,r16 ldi xl,low(joyy_low); joy Y low value ldi xh,high(joyy_low) st x,r16 ldi xl,low(joyy_high); joy Y high value ldi xh,high(joyy_high) st x,r16 pop r16 pop xh pop xl ret ;---------------------------------------- ; Function init_switch ; ; Write init value to switch status variable ; to force switch handling ;---------------------------------------- init_switch: push xl push xh push r16 ser r16; write $ff to switchpos ldi xl,low(switchpos) ldi xh,high(switchpos) st x,r16 pop r16 pop xh pop xl ret ;---------------------------------------- ; Function init_encoder ; ; Init of 'poti position' of each encoder ; with max value / position after ISP ; eeprom initialisation ;---------------------------------------- init_encoder: push r20 push r21 push xl push xh ;-- First encoder clr r20; read first encoder byte rcall read_eeprom cpi r21,$ff; initial value? brne init_encoder_01; if not continue ldi r21,$10; save max value rcall write_eeprom init_encoder_01: ldi xl,low(kvs); provide it in variable ldi xh,high(kvs) st x,r21 ;-- Second encoder ldi r20,$01; read second encoder byte rcall read_eeprom cpi r21,$ff; initial value? brne init_encoder_02; if not continue ldi r21,$10; save max value rcall write_eeprom init_encoder_02: ldi xl,low(jss); provide it in variable ldi xh,high(jss) st x,r21 pop xh pop xl pop r21 pop r20 ret ;---------------------------------------- ; Function read_eeprom ; ; Reads data from eeprom. Only byte 0 to ; 255 can be used. ; ; Input: r20: No. of eeprom byte (0 - $ff) ; ; Output: r21: result of read operation ;---------------------------------------- read_eeprom: push r16 clr r16; only byte 0 to 255 can be used out EEARH, r16; encoder address high byte out EEARL, r20; encoder address low byte ldi r16,$09; enable EEPROM ready interrupt and EEPROM read function out EECR,r16; read EEPROM into data register sleep; wait for EEPROM process to terminate in r21,EEDR; copy EEPROM data into working register clr r16 out EECR,r16; disable EEPROM access pop r16 ret ;---------------------------------------- ; Function write_eeprom ; ; Writes data to eeprom. Only byte 0 to ; 255 can be used. ; ; Input: r20: No. of eeprom byte (0 - $ff) ; r21: Data to be written ;---------------------------------------- write_eeprom: push r16 clr r16; only byte 0 to 255 can be used out EEARH, r16; encoder address high byte out EEARL, r20; encoder address low byte out EEDR,r21; copy data from working register to EEPROM data register bclr 7; disable interrupt ldi r16,$0c; enable EEPROM ready interrupt, EEPROM master write (prepare write) out EECR,r16 ldi r16,$0a; enable EEPROM ready interrupt, EEPROM write (do write) out EECR,r16 bset 7 ; enable interrupts sleep; wait for EEPROM process to terminate clr r16 out EECR,r16; disable EEPROM access pop r16 ret ;---------------------------------------- ; Function mod_encod_1 ; ; modifies current position of pot1 ; according to encoder movement (up down) ; ; input: r16: 01: move up ; 02: move down ;---------------------------------------- mod_encod_1: push r20 push r21 push xl push xh ;-- load last position clr r20; read EEPROM byte 0 rcall read_eeprom ;-- check encoder direction cpi r16,$01; up? brne mod_encod_1_01; if not decrement position cpi r21,$10; high value? breq mod_encod_1_02; if yes leave function inc r21; next position rjmp mod_encod_1_03 mod_encod_1_01: tst r21; low value? breq mod_encod_1_02; if yes leave function dec r21 mod_encod_1_03: rcall write_eeprom ldi xl,low(kvs); provide it in variable ldi xh,high(kvs) st x,r21 rcall display_pos1 mod_encod_1_02: pop xh pop xl pop r21 pop r20 ret ;---------------------------------------- ; Function mod_encod_2 ; ; modifies current position of pot2 ; according to encoder movement (up down) ; ; input: r16: 01: move up ; 02: move down ;---------------------------------------- mod_encod_2: push r20 push r21 push xl push xh ;-- load last position ldi r20,$01; read EEPROM byte 1 rcall read_eeprom ;-- check encoder direction cpi r16,$01; up? brne mod_encod_2_01; if not decrement position cpi r21,$10; high value? breq mod_encod_2_02; if yes leave function inc r21; next position rjmp mod_encod_2_03 mod_encod_2_01: tst r21; low value? breq mod_encod_2_02; if yes leave function dec r21 mod_encod_2_03: rcall write_eeprom ldi xl,low(jss); provide it in variable ldi xh,high(jss) st x,r21 rcall display_pos2 mod_encod_2_02: pop xh pop xl pop r21 pop r20 ret ;---------------------------------------- ; Function check_encoder ; ; Reads the pin values of rotary encoders ;---------------------------------------- check_encoder: push xl push xh push r16 push r17 push r20 push r21 ;-- read encoder data in r16,ENCODER; read port with rotary encoders push r16; save for analysis of second encoder ;-- analysis of first encoder andi r16,$03; select pin 1 and 0 for first encoder signals ldi xl,low(encodval_1); last encoder scan result ldi xh,high(encodval_1) ld r17,x cp r16,r17; new value detected? breq check_encoder_01; if no leave function ;-- new value detected mov r21,r16; save tst r16; both pins cleared? breq check_encoder_04; if yes store and exit cpi r16,$03; both pins set? breq check_encoder_04; if yes store and exit ;-- pins are 1 or 2 tst r17; last scan result was zero? breq check_encoder_03; if yes process rising edge ;-- process falling edge ldi r20,$03; invert bit 0 and 1 from r16 eor r16,r20 ;-- process new scan result check_encoder_03: rcall mod_encod_1 check_encoder_04: mov r16,r21 st x,r16 ;-- check second encoder check_encoder_01: pop r16; restore encoder scan for second encoder ;-- analysis of second encoder andi r16,$c0; select pin 7 and 6 for second encoder signals lsr r16; shift to bit 1 and 0 lsr r16 lsr r16 lsr r16 lsr r16 lsr r16 ldi xl,low(encodval_2); last encoder scan result ldi xh,high(encodval_2) ld r17,x cp r16,r17; new value detected? breq check_encoder_05; if no leave function ;-- new value detected mov r21,r16; save tst r16; both pins cleared? breq check_encoder_06; if yes store and exit cpi r16,$03; both pins set? breq check_encoder_06; if yes store and exit ;-- pins are 1 or 2 tst r17; last scan result was zero? breq check_encoder_07; if yes process rising edge ;-- process falling edge ldi r20,$03; invert bit 0 and 1 from r16 eor r16,r20 ;-- process new scan result check_encoder_07: rcall mod_encod_2 check_encoder_06: mov r16,r21 st x,r16 ;-- exit function check_encoder_05: pop r21 pop r20 pop r17 pop r16 pop xh pop xl ret ;---------------------------------------- ; Function check_joystick ; ; Reads the X and Y position of joystick ;---------------------------------------- check_joystick: push xl push xh push r16 push r17 push r18 push r19 push r20 push r21 ldi r19,$05; col clr r20; flag: did any change happen? ;-- read joystick X axis ldi r16,JOY_X; select adc channel X axis out ADMUX,r16 ldi r16,$88; set ADC operation mode out ADCSR,r16; start conversion sleep; wait for conversion end ldi xl,low(adc_low); ADC result low value ldi xh,high(adc_low) ld r16,x ldi xl,low(joyx_low); joy X low value ldi xh,high(joyx_low) ld r17,x cp r16,r17; value changed? breq check_joystick_01; if not continue with X high value ser r20; change happened st x,r16 check_joystick_01: mov r21,r16; save for further processing ldi xl,low(adc_high); ADC result high value ldi xh,high(adc_high) ld r16,x ldi xl,low(joyx_high); joy X high value ldi xh,high(joyx_high) ld r17,x cp r16,r17; value changed? breq check_joystick_02; if not check for display ser r20; change happened st x,r16 check_joystick_02: ;-- display X value tst r20; display a new X value? breq check_joystick_03; if not continue with Y axis rcall cursor_home ldi r18,$01; row rcall display_hex mov r16,r21; restore r16 rcall display_hex rcall send_midi_x clr r20 rcall display_mask; display jostick prefixes ;-- read joystick Y axis check_joystick_03: ldi r16,JOY_Y; select adc channel Y axis out ADMUX,r16 ldi r16,$88; set ADC operation mode out ADCSR,r16; start conversion sleep; wait for conversion end ldi xl,low(adc_low); ADC result low value ldi xh,high(adc_low) ld r16,x ldi xl,low(joyy_low); joy Y low value ldi xh,high(joyy_low) ld r17,x cp r16,r17; value changed? breq check_joystick_04; if not continue with Y high value ser r20; change happened st x,r16 check_joystick_04: mov r21,r16; save for further processing ldi xl,low(adc_high); ADC result high value ldi xh,high(adc_high) ld r16,x ldi xl,low(joyy_high); joy Y high value ldi xh,high(joyy_high) ld r17,x cp r16,r17; value changed? breq check_joystick_05; if not check for display ser r20; change happened st x,r16 check_joystick_05: ;-- display Y value tst r20; display a new Y value? breq check_joystick_06; if not leave function rcall cursor_home clr r18; row rcall display_hex mov r16,r21; restore r16 rcall display_hex rcall send_midi_y rcall display_mask; display jostick prefixes check_joystick_06: clr r16 out ADCSR,r16; disable ADC pop r21 pop r20 pop r19 pop r18 pop r17 pop r16 pop xh pop xl ret ;---------------------------------------- ; Function send_midi_x ; ; Creates and sends MIDI message which is ; configured to joystick X axis movement ;---------------------------------------- send_midi_x: push xl push xh push r16 push r17 push r18 ldi xl,low(joyx_low); get joy X low value ldi xh,high(joyx_low) ld r17,x ldi xl,low(joyx_high); get joy X high value ldi xh,high(joyx_high) ld r18,x rcall cr_midi_databytes; change data format to midi ;-- send midi message ldi r16,CNTRLCHANGE; send controller info out UDR,r16 sleep ldi r16,MODULATIONHI; send controller info out UDR,r16 sleep out UDR,r18; send controller high byte sleep ldi r16,MODULATIONLO; send controller info out UDR,r16 sleep out UDR,r17; send controller low byte sleep pop r18 pop r17 pop r16 pop xh pop xl ret ;---------------------------------------- ; Function send_midi_y ; ; Creates and sends MIDI message which is ; configured to joystick Y axis movement ;---------------------------------------- send_midi_y: push xl push xh push r16 push r17 push r18 ldi xl,low(joyy_low); get joy Y low value ldi xh,high(joyy_low) ld r17,x ldi xl,low(joyy_high); get joy Y high value ldi xh,high(joyy_high) ld r18,x rcall cr_midi_databytes; change data format to midi ;-- send midi message ldi r16,PITCHBEND; send controller info out UDR,r16 sleep out UDR,r17; send pitchbend low byte sleep out UDR,r18; send pitchbend high byte sleep pop r18 pop r17 pop r16 pop xh pop xl ret ;---------------------------------------- ; Function cr_midi_databytes ; ; Creates midi data bytes of the format ; MSB LSB ; 0xxxxxxx 0xxx0000 ; from 10 bit ADC result of the format ; MSB LSB ; 000000xx xxxxxxxx ; ; I/O: r17: LSB ; r18: MSB ;---------------------------------------- cr_midi_databytes: push r19 ;-- create output LSB mov r19,r17; save LSB for further processing andi r17,$07; keep only bit 0 - 2 lsl r17; move them to position 4 - 6 lsl r17 lsl r17 lsl r17 ;-- create output MSB lsr r19; move LSB 3 times to the right lsr r19 lsr r19 andi r18,$03; isolate bit 0 and 1 of MSB lsl r18; move bit 0 and 1 to position 5 and 6 lsl r18 lsl r18 lsl r18 lsl r18 or r18,r19; add LSB high digits to MSB pop r19 ret ;---------------------------------------- ; Function check_switch ; ; Checks the condition of the external ; switch (on / off) ;---------------------------------------- check_switch: push zh push zl push xh push xl push r16 push r17 push r18 push r19 ldi r18,$01; row clr r19; column ;-- load old status from memory ldi xl,low(switchpos) ldi xh,high(switchpos) ld r17,x ;-- check switch position (on / off) in r16,SWITCH; read port A andi r16,$10; isolate bit 4 cp r16,r17; anything changed? breq check_switch_02; if not leave function st x,r16; save new status rcall clear_display; prepare display for new output rcall cursor_home tst r16; switch on? breq check_switch_01; if not modus "external" ;-- modus "internal" rcall display_mask rcall init_joystick; force joystick scan rcall check_joystick ldi zh,high(swi_txt<<1); text address ldi zl,low(swi_txt<<1) rcall text_out; writes text on display rjmp check_switch_02 ;-- modus "external" check_switch_01: ldi zh,high(swe_txt<<1); text address ldi zl,low(swe_txt<<1) rcall text_out; writes text on display check_switch_02: pop r19 pop r18 pop r17 pop r16 pop xl pop xh pop zl pop zh ret ;---------------------------------------- ; Function DEBUG ; ; Writes a bit mask 10101010 on data bus ; and stops run ;---------------------------------------- debug: rcall set_databus_write; set direction of databus to output ldi r16,$aa ; test bit mask on data bus out DATA_OUT,r16 rjmp stop ;---------------------------------------- ; Function clear_databus ; ; Writes a bit mask 00000000 on data bus ;---------------------------------------- clear_databus: push r16 rcall set_databus_write; set direction of databus to output clr r16; zero bit mask on data bus out DATA_OUT,r16 pop r16 ret ;---------------------------------------- ; Function SET_DATABUS_WRITE ; ; Sets direction of data bus to output ;---------------------------------------- set_databus_write: push r16 ser r16; set data bus as output out DATADIR, r16 pop r16 ret ;---------------------------------------- ; Function SET_DATABUS_READ ; ; Sets direction of data bus to input ;---------------------------------------- set_databus_read: push r16 clr r16; set data bus as input out DATADIR, r16 pop r16 ret ;---------------------------------------- ; Function INIT_WAIT ; ; Waits for some time, depending on crystal ; With an 8Mhz crystal this function waits ; for 0.065536 secs ;---------------------------------------- init_wait: push r16 clr r16; start value timer/counter 1 out TCNT1L, r16 out TCNT1H, r16 ldi r16, $02; prescale timer/counter 1 = clock / 8 out TCCR1B, r16 sleep pop r16 ret ;---------------------------------------- ; Function debounce_elem ; ; Waits for some time, depending on crystal ; With an 8Mhz crystal this function waits ; for 0.000032 secs ;---------------------------------------- debounce_elem: push r16 clr r16; start value timer/counter 1 out TCNT0, r16 ldi r16, $01; prescale timer/counter = clock out TCCR0, r16 sleep pop r16 ret ;---------------------------------------- ; Function debounce ; ; Waits for some time, depending on crystal ;---------------------------------------- debounce: push r16; counter clr r16; init counter debounce_01: rcall debounce_elem; wait some time inc r16; next counter cpi r16,$04; number of iterations brlt debounce_01; if not reached once again pop r16 ret ;---------------------------------------- ; Function set_buffer ; ; ; Initialises a memory area (buffer) of a ; given size with a given value ; ; Input: r16: value to be stored ; xl, xh: buffer start address ; r17: length of buffer ;---------------------------------------- set_buffer: push r18 clr r18; initial counter value set_buffer_01: st x,r16; write byte adiw xl,1; next buffer position inc r18; next counter cp r18,r17; check end of buffer reached brlt set_buffer_01; not yet reached pop r18 ret ;**************************************** ; ;***** MIDI functions ; ; µC pin functions: ; ; PD2(16): enable MIDI in ; PD3(17): enable iMIDI out ; PD4(18): enable iMIDI in ; PD5(19): enable MIDI out ; ;**************************************** ;---------------------------------------- ; Function init_midi ; ; o Initializes UART with 31250 baud ; (MIDI, Clock devider value corresponds to ; 8 Mhz crystal and 31250 baud) ; o Enables MIDI I/O ;---------------------------------------- init_midi: push xl push xh push r16 ;-- clear read / write pointer of MIDI input buffer clr r16 ldi xl,low(wpt) ldi xh,high(wpt) st x,r16 ldi xl,low(rpt) ldi xh,high(rpt) st x,r16 ;-- set clock divider for baud rate ldi r16,$0f; clock devider out UBRR,r16 ;-- prepare hardware connections sbi PORTD,2; enable MIDI in sbi PORTD,3; enable iMIDI out cbi PORTD,4; disable iMIDI in sbi PORTD,5; enable MIDI out rcall set_uart_write; prepare UART for sending rcall note_off_all; send note off for all notes pop r16 pop xh pop xl ret ;---------------------------------------- ; Function set_uart_write ; ; Prepares UART for sending (midi out) ;---------------------------------------- set_uart_write: push r16 ldi r16,$48; prepare UART for sending out UCR,r16 ser r16; load ACTIVE SENSING out UDR,r16; cleanup initial uart content sleep out UDR,r16 sleep pop r16 ret ;---------------------------------------- ; Function disable_uart ; ; disables UART ;---------------------------------------- disable_uart: push r16 clr r16; disable UART out UCR,r16 pop r16 ret ;---------------------------------------- ; Function midi_mon ; ; Shows incoming serial data on display ;---------------------------------------- midi_mon: push xl push xh push r16 push r17 push r18 push r19 push r20 push r21 rcall set_uart_read; prepare UART for reading clr r18; row clr r19; col ldi r21,ROWLEN rcall cursor_home ldi r16, $0f; blinking cursor rcall dis_com ;-- check for received data midi_mon_01: ldi xl,low(wpt); get buffer write pointer ldi xh,high(wpt) ld r17,x ldi xl,low(rpt); get buffer read pointer ldi xh,high(rpt) ld r20,x cp r17,r20; anything to do? brne midi_mon_03; if yes process data ;-- check switch rcall check_switch ldi xl,low(switchpos) ldi xh,high(switchpos) ld r16,x tst r16; switch on external? breq midi_mon_01; if yes stay in midi_mon function ;-- actions before leaving midi_mon function ldi r16,$48; prepare UART for sending out UCR,r16 ldi r16, $0c; cursor off rcall dis_com rjmp midi_mon_04; leave function ;-- read byte from buffer midi_mon_03: ldi xl,low(ringbuf); get buffer start address ldi xh,high(ringbuf) add xl,r20; compute current buffer address ld r16,x; read midi byte ;-- display midi byte rcall display_hex inc r19; next cursor position cpi r19,$10; end of row reached? brlt midi_mon_05; if not continue clr r19; first col tst r18; actual row first row? breq midi_mon_06; if yes set next row clr r18; else start from display beginning rcall cursor_home rjmp midi_mon_05 midi_mon_06: ldi r18,$01;move cursor to begin of next line rcall cursor_home ;-- compute next read pointer address midi_mon_05: inc r20 cpi r20,$1f; max buffer reached? brlt midi_mon_02; if not continue clr r20; reset read buffer midi_mon_02: ldi xl,low(rpt); get buffer read pointer ldi xh,high(rpt) st x,r20; save new read pointer rjmp midi_mon_01; check for next byte midi_mon_04: pop r21 pop r20 pop r19 pop r18 pop r17 pop r16 pop xh pop xl ret ;---------------------------------------- ; Function set_uart_read ; ; Prepares UART for reading (midi in) ;---------------------------------------- set_uart_read: push r16 ldi r16,$90; prepare UART for reading out UCR,r16 pop r16 ret ;---------------------------------------- ; Function note_off_all ; ; Send note off for all notes ;---------------------------------------- note_off_all: push xl push xh push r16; counter push r17; note value push r18; data byte to send clr r16; init counter ldi r17,NOTE_OFFSET; start note value note_off_all_01: inc r17; next note value ldi r18,$90; send midi channel out UDR,r18 sleep mov r18,r17; send note value out UDR,r18 sleep clr r18; send zero volume (note off) out UDR,r18 sleep inc r16; next counter cpi r16,$3d; note 61 reached? brlt note_off_all_01 ;-- clear note buffer clr r16; initial buffer value ldi r17, MAXNOTES ldi xl,low(notebuf); initial buffer address ldi xh,high(notebuf) rcall set_buffer; write buffer ;-- clear velocity buffer ldi xl,low(velobuf); initial buffer address ldi xh,high(velobuf) ldi r17,$01; buffer length rcall set_buffer; write buffer ;-- register that note_off_all was executed to avoid multiple calls ldi xl,low(noffsent); set variable start adress ldi xh,high(noffsent) ser r16 st x,r16 pop r18 pop r17 pop r16 pop xh pop xl ret ;**************************************** ; ;***** Keyboard functions ; ; µC pin functions: ; ; PB0(01): Bit 2 of keyboard block adress ; PB1(02): Bit 1 of keyboard block adress ; PB2(03): Bit 0 of keyboard block adress ; ; PA7(33): read keyboard ; PA6(34): freeze keboard data ; PA5(35): provide data of keys 1-4 ; of one block scan on data bus ; PD7(21): provide data of keys 5-8 ; of one block scan on data bus ; ;**************************************** ;---------------------------------------- ; Function init_keyboard ; ; ; initialises keyboard scan ;---------------------------------------- init_keyboard: ;-- disable keyboard interface cbi PORTA,5; GAB1: don't provide key data 1-4 cbi PORTD,7; GAB2: don't provide key data 5-8 ret ;---------------------------------------- ; Function scan_kbd_block_0 ; ; ; scans first keyboard block with the keys ; 01: c0 ; 09: g#0 ; 17: e1 ; 25: c2 ; 33: g#2 ; 41: e3 ; 49: c4 ; 57: g#4 ; by setting block adress 4 (100) ; ; uses r20 (lower keyboard block) ; r17 (higher keyboard block) ; r16 Keyboard block number ;---------------------------------------- scan_kbd_block_0: push r16 push r17 push r20 sbi PORTB,0; Bit 2 of keyboard block adress cbi PORTB,1; Bit 1 of keyboard block adress cbi PORTB,2; Bit 0 of keyboard block adress rcall debounce rcall scan_kbd_block; enables keyboard scan and reads data ldi r16,BLOCK_0 rcall process_keyblock pop r20 pop r17 pop r16 ret ;---------------------------------------- ; Function scan_kbd_block_1 ; ; ; scans second keyboard block with the keys ; 02: c#0 ; 10: a0 ; 18: f1 ; 26: c#2 ; 34: a2 ; 42: f3 ; 50: c#4 ; 58: a4 ; by setting block adress 2 (010) ; ; uses r20 (lower keyboard block) ; r17 (higher keyboard block) ; r16 Keyboard block number ;---------------------------------------- scan_kbd_block_1: push r16 push r17 push r20 cbi PORTB,0; Bit 2 of keyboard block adress sbi PORTB,1; Bit 1 of keyboard block adress cbi PORTB,2; Bit 0 of keyboard block adress rcall debounce rcall scan_kbd_block; enables keyboard scan and reads data ldi r16,BLOCK_1 rcall process_keyblock pop r20 pop r17 pop r16 ret ;---------------------------------------- ; Function scan_kbd_block_2 ; ; ; scans third keyboard block with the keys ; 03: d0 ; 11: b0 ; 19: f#1 ; 27: d2 ; 35: b2 ; 43: f#3 ; 51: d4 ; 59: b4 ; by setting block adress 6 (110) ; ; uses r20 (lower keyboard block) ; r17 (higher keyboard block) ; r16 Keyboard block number ;---------------------------------------- scan_kbd_block_2: push r16 push r17 push r20 sbi PORTB,0; Bit 2 of keyboard block adress sbi PORTB,1; Bit 1 of keyboard block adress cbi PORTB,2; Bit 0 of keyboard block adress rcall debounce rcall scan_kbd_block; enables keyboard scan and reads data ldi r16,BLOCK_2 rcall process_keyblock pop r20 pop r17 pop r16 ret ;---------------------------------------- ; Function scan_kbd_block_3 ; ; ; scans fourth keyboard block with the keys ; 04: d#0 ; 12: h0 ; 20: g1 ; 28: d#2 ; 36: h2 ; 44: g3 ; 52: d#4 ; 60: h4 ; by setting block adress 1 (001) ; ; uses r20 (lower keyboard block) ; r17 (higher keyboard block) ; r16 Keyboard block number ;---------------------------------------- scan_kbd_block_3: push r16 push r17 push r20 cbi PORTB,0; Bit 2 of keyboard block adress cbi PORTB,1; Bit 1 of keyboard block adress sbi PORTB,2; Bit 0 of keyboard block adress rcall debounce rcall scan_kbd_block; enables keyboard scan and reads data ldi r16,BLOCK_3 rcall process_keyblock pop r20 pop r17 pop r16 ret ;---------------------------------------- ; Function scan_kbd_block_4 ; ; ; scans fifth keyboard block with the keys ; 05: e0 ; 13: c1 ; 21: g#1 ; 29: e2 ; 37: c3 ; 45: g#3 ; 53: e4 ; 61: c5 ; by setting block adress 0 (000) ; ; uses r20 (lower keyboard block) ; r17 (higher keyboard block) ; r16 Keyboard block number ;---------------------------------------- scan_kbd_block_4: push r16 push r17 push r20 cbi PORTB,0; Bit 2 of keyboard block adress cbi PORTB,1; Bit 1 of keyboard block adress cbi PORTB,2; Bit 0 of keyboard block adress rcall debounce rcall scan_kbd_block; enables keyboard scan and reads data ldi r16,BLOCK_4 rcall process_keyblock pop r20 pop r17 pop r16 ret ;---------------------------------------- ; Function scan_kbd_block_5 ; ; ; scans sixth keyboard block with the keys ; 06: f0 ; 14: c#1 ; 22: a1 ; 30: f2 ; 38: c#3 ; 46: a3 ; 54: f4 ; 62: n. a. ; by setting block adress 7 (111) ; ; uses r20 (lower keyboard block) ; r17 (higher keyboard block) ; r16 Keyboard block number ;---------------------------------------- scan_kbd_block_5: push r16 push r17 push r20 sbi PORTB,0; Bit 2 of keyboard block adress sbi PORTB,1; Bit 1 of keyboard block adress sbi PORTB,2; Bit 0 of keyboard block adress rcall debounce rcall scan_kbd_block; enables keyboard scan and reads data ldi r16,BLOCK_5 rcall process_keyblock pop r20 pop r17 pop r16 ret ;---------------------------------------- ; Function scan_kbd_block_6 ; ; ; scans seventh keyboard block with the keys ; 07: f#0 ; 15: d1 ; 23: b1 ; 31: f#2 ; 39: d3 ; 47: b3 ; 55: f#4 ; 63: n. a. ; by setting block adress 3 (011) ; ; uses r20 (lower keyboard block) ; r17 (higher keyboard block) ; r16 Keyboard block number ;---------------------------------------- scan_kbd_block_6: push r16 push r17 push r20 cbi PORTB,0; Bit 2 of keyboard block adress sbi PORTB,1; Bit 1 of keyboard block adress sbi PORTB,2; Bit 0 of keyboard block adress rcall debounce rcall scan_kbd_block; enables keyboard scan and reads data ldi r16,BLOCK_6 rcall process_keyblock pop r20 pop r17 pop r16 ret ;---------------------------------------- ; Function scan_kbd_block_7 ; ; ; scans eighth keyboard block with the keys ; 08: g0 ; 16: d#1 ; 24: h1 ; 32: g2 ; 40: d#3 ; 48: h3 ; 56: g4 ; 64: n. a. ; by setting block adress 5 (101) ; ; uses r20 (lower keyboard block) ; r17 (higher keyboard block) ; r16 Keyboard block number ;---------------------------------------- scan_kbd_block_7: push r16 push r17 push r20 sbi PORTB,0; Bit 2 of keyboard block adress cbi PORTB,1; Bit 1 of keyboard block adress sbi PORTB,2; Bit 0 of keyboard block adress rcall debounce rcall scan_kbd_block; enables keyboard scan and reads data ldi r16,BLOCK_7 rcall process_keyblock pop r20 pop r17 pop r16 ret ;---------------------------------------- ; Function scan_kbd_block ; ; enables keyboard scan and reads data ; ; returns lower keybord block in r20 ; higher keyboard block in r17 ;---------------------------------------- scan_kbd_block: push r16 push r18 push r19 sbi PORTA,5; GAB1: provide key data 1-4 rcall set_databus_read; set databus input in r20,DATA_IN; read keyboard data cbi PORTA,5; GAB1: don't provide key data 1-4 sbi PORTD,7; GAB2: provide key data 5-8 rcall set_databus_read; set databus input in r17,DATA_IN; read keyboard data cbi PORTD,7; GAB2: don't provide key data 5-8 pop r19 pop r18 pop r16 ret ;---------------------------------------- ; Function process_keyblock ; ; analyses a keyblock scan (r20 and r17), ; determins the keys pressed, gets the ; note values of the actual block (r16) ; and stores them into the note buffer, ; determins key velocity and sends MIDI ; message ; ; input: r16: keyboard block number ; r20: lower block half ; r17: higher block half ;---------------------------------------- process_keyblock: push xl; pointer to velocity buffer push xh push zl; pointer to list of note values push zh push r19; counter ldi xl,low(velobuf); velocity buffer address ldi xh,high(velobuf) ;-- compute address of note value constant clr r19; counter := 0 ldi zh,high(key_values<<1); start address list of key values ldi zl,low(key_values<<1) process_keyblock_03: cp r19,r16; destination block number reached? breq process_keyblock_02 adiw zl,BLOCKLEN; add offset inc r19; next counter rjmp process_keyblock_03 ;-- check lower key block process_keyblock_02: tst r20; check if zero breq process_keyblock_01 sbrc r20,0; second contact of first key? rcall key_1 sbrc r20,1; first contact of first key? rcall velo_1 sbrc r20,2; second contact of second key? rcall key_2 sbrc r20,3; first contact of second key? rcall velo_2 sbrc r20,4; second contact of third key? rcall key_3 sbrc r20,5; first contact of third key? rcall velo_3 sbrc r20,6; second contact of fourth key? rcall key_4 sbrc r20,7; first contact of fourth key? rcall velo_4 ;-- check upper key block process_keyblock_01: tst r17; check if zero breq process_keyblock_exit sbrc r17,0; second contact of fifth key? rcall key_5 sbrc r17,1; first contact of fifth key? rcall velo_5 sbrc r17,2; second contact of sixth key? rcall key_6 sbrc r17,3; first contact of sixth key? rcall velo_6 sbrc r17,4; second contact of seventh key? rcall key_7 sbrc r17,5; first contact of seventh key? rcall velo_7 sbrc r17,6; second contact of eighth key? rcall key_8 sbrc r17,7; first contact of eighth key? rcall velo_8 process_keyblock_exit: pop r19 pop zh pop zl pop xh pop xl ret ;---------------------------------------- ; Function velo_1 ; ; Processes velocity of key 1 of scanned ; key block ;---------------------------------------- velo_1: push r18 push r19 ldi r19,$01; bit 0 mov r18,r20; provide current half of key block rcall process_velo pop r19 pop r18 ret ;---------------------------------------- ; Function velo_2 ; ; Processes velocity of key 2 of scanned ; key block ;---------------------------------------- velo_2: push r18 push r19 ldi r19,$04; bit 2 mov r18,r20; provide current half of key block rcall process_velo pop r19 pop r18 ret ;---------------------------------------- ; Function velo_3 ; ; Processes velocity of key 3 of scanned ; key block ;---------------------------------------- velo_3: push r18 push r19 ldi r19,$10; bit 4 mov r18,r20; provide current half of key block rcall process_velo pop r19 pop r18 ret ;---------------------------------------- ; Function velo_4 ; ; Processes velocity of key 4 of scanned ; key block ;---------------------------------------- velo_4: push r18 push r19 ldi r19,$40; bit 6 mov r18,r20; provide current half of key block rcall process_velo pop r19 pop r18 ret ;---------------------------------------- ; Function velo_5 ; ; Processes velocity of key 5 of scanned ; key block ;---------------------------------------- velo_5: push r18 push r19 ldi r19,$01; bit 0 mov r18,r17; provide current half of key block rcall process_velo pop r19 pop r18 ret ;---------------------------------------- ; Function velo_6 ; ; Processes velocity of key 6 of scanned ; key block ;---------------------------------------- velo_6: push r18 push r19 ldi r19,$04; bit 2 mov r18,r17; provide current half of key block rcall process_velo pop r19 pop r18 ret ;---------------------------------------- ; Function velo_7 ; ; Processes velocity of key 7 of scanned ; key block ;---------------------------------------- velo_7: push r18 push r19 ldi r19,$10; bit 4 mov r18,r17; provide current half of key block rcall process_velo pop r19 pop r18 ret ;---------------------------------------- ; Function velo_8 ; ; Processes velocity of key 8 of scanned ; key block ;---------------------------------------- velo_8: push r18 push r19 ldi r19,$40; bit 6 mov r18,r17; provide current half of key block rcall process_velo pop r19 pop r18 ret ;---------------------------------------- ; Function process_velo ; ; Processes velocity of key of scanned ; key block. ; Each recognized contact decreases a ; byte value until the second contact ; of a key stroke happens. The remaining ; value in the velocity byte is an ; equivalent to the key velocity ; ; Input: xl, xh: Address of velocity byte ; of key stroke ; r18: current half of key block ; r19: bit mask which key ;---------------------------------------- process_velo: push r16 push r19 push r18 push r21 push r22 push yl push yh ;-- check if second contact alraedy happened and r18,r19; isolate bit of current key stroke tst r18; keystroke complete? brne process_velo_exit; if not continue ;-- decrease velocity byte process_velo_01: ldi r22,$f0; for complement construction ldi yl,low(kvs); load keyboard sensitivity value ldi yh,high(kvs) ld r21,y ;-- compute minimum velocity value tst r21; is zero? breq process_velo_02 dec r21 process_velo_02: lsl r21; r21 *= 16 lsl r21 lsl r21 lsl r21 sub r22,r21; build complement ld r16,x; load old velocity value dec r16; decrease velocity byte cp r16,r22; minimum value reached? breq process_velo_exit; then exit st x,r16; store new value process_velo_exit: pop yh pop yl pop r22 pop r21 pop r18 pop r19 pop r16 ret ;---------------------------------------- ; Function key_1 ; ; Processes key 1 of scanned key block ;---------------------------------------- key_1: rcall process_key; no offset in list of note values ret ;---------------------------------------- ; Function key_2 ; ; Processes key 2 of scanned key block ;---------------------------------------- key_2: push zl push zh adiw zl,$01; offset in list of note values rcall process_key pop zh pop zl ret ;---------------------------------------- ; Function key_3 ; ; Processes key 3 of scanned key block ;---------------------------------------- key_3: push zl push zh adiw zl,$02; offset in list of note values rcall process_key pop zh pop zl ret ;---------------------------------------- ; Function key_4 ; ; Processes key 4 of scanned key block ;---------------------------------------- key_4: push zl push zh adiw zl,$03; offset in list of note values rcall process_key pop zh pop zl ret ;---------------------------------------- ; Function key_5 ; ; Processes key 5 of scanned key block ;---------------------------------------- key_5: push zl push zh adiw zl,$04; offset in list of note values rcall process_key pop zh pop zl ret ;---------------------------------------- ; Function key_6 ; ; Processes key 6 of scanned key block ;---------------------------------------- key_6: push zl push zh adiw zl,$05; offset in list of note values rcall process_key pop zh pop zl ret ;---------------------------------------- ; Function key_7 ; ; Processes key 7 of scanned key block ;---------------------------------------- key_7: push zl push zh adiw zl,$06; offset in list of note values rcall process_key pop zh pop zl ret ;---------------------------------------- ; Function key_8 ; ; Processes key 1 of scanned key block ;---------------------------------------- key_8: push zl push zh adiw zl,$07; offset in list of note values rcall process_key pop zh pop zl ret ;---------------------------------------- ; Function process_key ; ; Processes key of scanned key block ; ; Input: zl,zh: Address of note value ;---------------------------------------- process_key: push r0 push xl push xh push r16 push r17 push r18 push r19 ldi r19,NOTE_OFFSET lpm; load (Z) into r0 => load note value from list ;-- Check if midi sequence is already sent clr r17; clear counter ldi xl,low(notebuf); set note variables / buffer start adress ldi xh,high(notebuf) new_note_01: ld r16,x; compare note value to memory andi r16,$7f; clear confirm bit push r0 cp r0,r16 breq confirm_note; key is still pressed pop r0 adiw xl,1 inc r17 cpi r17,MAXNOTES ; 10 voice polyphonic brlt new_note_01 ;-- find free note variable clr r17; clear counter ldi xl,low(notebuf); set note variables start adress ldi xh,high(notebuf) new_note_02: push r0; save note value on stack ld r16,x; load register from data memory tst r16; compare with initial value breq send_midi; send note, save note value + confirm bit in data segment pop r0 adiw xl,1 inc r17 cpi r17,MAXNOTES brlt new_note_02 rjmp process_key_exit send_midi: ;-- it is a new note, so send midi pop r0 ldi r18,$90; send midi channel out UDR,r18 sleep mov r18,r0 add r18,r19 OUT UDR,r18; send note value sleep push xl push xh ldi xl,low(velobuf); send note volume ldi xh,high(velobuf) ld r18,x pop xh pop xl lsr r18; r18:=r18/2, otherwise volume too strong inc r18 OUT UDR,r18 sleep ;-- register that note_off_all can be called push xl push xh ldi xl,low(noffsent); set variable start adress ldi xh,high(noffsent) clr r18 st x,r16 pop xh pop xl push r0 confirm_note: ;-- save note value + confirm bit in data segment pop r0 mov r16,r0 ori r16,$80; set confirm bit st x,r16 process_key_exit: pop r19 pop r18 pop r17 pop r16 pop xh pop xl pop r0 ret ;---------------------------------------- ; Function clear_flags ; ; Clears key_is_still_pressed - flag in ; note buffer ;---------------------------------------- clear_flags: ;-- clear all confirm bits of pressed keys push xl push xh push r16 push r17 clr r17; clear counter ldi xl,low(notebuf); set note variables start adress ldi xh,high(notebuf) clear_flags_02: ld r16,x tst r16; currenly not used? breq clear_flags_01; then skip andi r16,$7f; else clear Bit 7 st x,r16 clear_flags_01: adiw xl,1 inc r17 cpi r17,MAXNOTES brne clear_flags_02 pop r17 pop r16 pop xh pop xl ret ;---------------------------------------- ; Function note_off ; ; Send note off for all released keys ;---------------------------------------- note_off: push xl push xh push r16 push r17 push r18 push r19 push r20 push r21 push r22 ldi r19,$90; midi channel ldi r20,NOTE_OFFSET clr r22; check if a key is pressed clr r17; clear counter ldi xl,low(notebuf); set note variables start adress ldi xh,high(notebuf) note_off_02: ld r16,x tst r16; initial value? brne note_off_01; if not continue note_off_03: adiw xl,1; next buffer address inc r17; next counter cpi r17,MAXNOTES; max. reached? breq note_off_exit; if yes exit function rjmp note_off_02; else continue operation note_off_01: mov r18,r16 andi r16,$80; isolate bit 7 sbrc r16,7; register in r22 if a key is still pressed inc r22 cpi r16,$80; bit still set? breq note_off_03; then next stored value ;-- send note off mov r16,r18 andi r16,$7f; clear bit 7 clr r21; clear volume out UDR,r19; send midi channel sleep mov r19,r16 add r19,r20 OUT UDR,r19; send note value sleep OUT UDR,r21; send volume sleep clr r16; mark note variable as free st x,r16 push xl; save actual key note buffer address push xh adiw xl,MAXNOTES; jump to velocity buffer st x,r16; clear velocity byte pop xh; restore key note buffer address pop xl rjmp note_off_03; next stored value note_off_exit: tst r22; is any key pressed at all? brne note_off_exit_01 ldi xl,low(noffsent); set variable start adress ldi xh,high(noffsent) ld r16,x; load variable content tst r16 brne note_off_exit_01 rcall note_off_all note_off_exit_01: pop r22 pop r21 pop r20 pop r19 pop r18 pop r17 pop r16 pop xh pop xl ret ;**************************************** ; ;***** Display functions ; ; Hardware connections: ; ; Pin display = Pin µC ; RS(4) = PD6(20) (Data / Command) ; RW(5) = PB4(05) (Read / Write) ; EN(6) = PB3(04) (Enable / Disable) ;**************************************** ;---------------------------------------- ; Function display_pos1 ; ; display position of encoder 1 ;---------------------------------------- display_pos1: push r16 push r18 push r19 push r20 push r21 ;-- read encoder positions from EEPROM clr r20; read EEPROM byte 0 rcall read_eeprom ;-- display value mov r16,r21; copy eeprom byte to display register rcall cursor_home clr r18; row ldi r19,$0e; col rcall display_hex pop r21 pop r20 pop r19 pop r18 pop r16 ret ;---------------------------------------- ; Function display_pos2 ; ; display position of encoder 2 ;---------------------------------------- display_pos2: push r16 push r18 push r19 push r20 push r21 ;-- read encoder positions from EEPROM ldi r20,$01; read EEPROM byte 1 rcall read_eeprom ;-- display value mov r16,r21; copy eeprom byte to display register rcall cursor_home ldi r18,$01; row ldi r19,$0e; col rcall display_hex pop r21 pop r20 pop r19 pop r18 pop r16 ret ;---------------------------------------- ; Function display_mask ; ; creates display mask ;---------------------------------------- display_mask: push zh push zl push r18 push r19 ;-- Joystick X position ldi zh,high(joyx_txt<<1); text address ldi zl,low(joyx_txt<<1) ldi r18,$00; row ldi r19,$04; column rcall text_out; writes text on display ;-- Joystick Y position ldi zh,high(joyy_txt<<1); text address ldi zl,low(joyy_txt<<1) ldi r18,$01; row ldi r19,$04; column rcall text_out; writes text on display ;-- Key Velocity Sensitivity ldi zh,high(kvs_txt<<1); text address ldi zl,low(kvs_txt<<1) ldi r18,$00; row ldi r19,$09; column rcall text_out; writes text on display ;-- JoyStick Sensitivity ldi zh,high(jss_txt<<1); text address ldi zl,low(jss_txt<<1) ldi r18,$01; row ldi r19,$09; column rcall text_out; writes text on display ;-- KVS and JSS values rcall display_pos1 rcall display_pos2 pop r19 pop r18 pop zl pop zh ret ;---------------------------------------- ; Function start_message ; ; displays start messages ;---------------------------------------- start_message: push zh push zl push r18 push r19 ;-- write reset message on display ldi zh,high(start_txt<<1); text address ldi zl,low(start_txt<<1) ldi r18,$00; row ldi r19,$01; column rcall text_out; writes text on display ;--wait some time clr r18; init counter start_message_01: rcall init_wait; waits for ca. 0.065536 secs inc r18; next counter cpi r18,$14; number of iterations brlt start_message_01; if not reached once again rcall clear_display pop r19 pop r18 pop zl pop zh ret ;---------------------------------------- ; Function DISPLAY_HEX ; ; displays a byte (register content) at ; (row,col) and (row,(col+1)) in hex ; format ; ; parameter: r16: byte to display ; r18: row ; r19: column ;---------------------------------------- display_hex: push r16; save data byte for reuse swap r16; swap upper and lower half nibble rcall display_nibble; display upper half nibble pop r16; reload data byte inc r19; next cursor position rcall display_nibble; display lower half nibble ret ;---------------------------------------- ; Function DISPLAY_NIBBLE ; ; displays ascii value of nibble ; ; parameter: r16: nibble to display ; r18: row ; r19: column ;---------------------------------------- display_nibble: push r0 push r17 andi r16,$0f; clear bits 4-7 cpi r16,$0a; conversion necessary? brge dn01; yes, move to ascii 'a' ldi r17,$30; no, move to ascii '0' rjmp dn02 dn01: ldi r17,$37; move to ascii 'a' dn02: add r16,r17; compute ascii value of nibble mov r0,r16 mov r16,r19; set cursor column rcall set_cursor; move cursor to destination row rcall dis_text; send character to display pop r17 pop r0 ret ;---------------------------------------- ; Function cursor_home ; ; Moves cursor to first display position ;---------------------------------------- cursor_home: push r16 ldi r16, $02; cursor home rcall dis_com; send command to display pop r16 ret ;---------------------------------------- ; Function set_cursor ; ; Moves cursor to destination position ; ; Parameter: r16 destination column ; r18 destination row ;---------------------------------------- set_cursor: push r19 clr r19; default row offset 0 cpi r18,$00; check row selection breq sc_continue; no row addition necessary ldi r19,ROWLEN; offset 1st digit of 2nd row sc_continue: add r16,r19; add column to compute target digit in display ori r16,$80; create cursor command rcall dis_com; send command to display pop r19 ret ;---------------------------------------- ; Function TEXT_OUT ; ; Displays a text on lcd module ; ; Parameter: zh, zl - Text adress ; r18 - row ($00 - $01) ; r19 - column($00-$13) ;---------------------------------------- text_out: push r16 push r20 rcall cursor_home ldi r20,$23; end character in text constant mov r16,r19; set cursor command register / column counter rcall set_cursor; move cursor to destination row text_01: lpm; load (Z) into r0 => load character cp r0,r20; end of text reached? brne text_02; if not send character to display pop r20 pop r16 ret text_02: rcall dis_text; send character to display inc r16; next cursor position rcall dis_com; send command to display adiw zl,1; next text byte position rjmp text_01; same procedure for next character ;---------------------------------------- ; Function dis_text ; ; Sends a character to display ; Input Parameter: r0: Character ;---------------------------------------- dis_text: rcall set_databus_write; set direction of databus to output sbi PORTD,6; RS high (character operation) cbi PORTB,4; R/W low (write operation) cbi PORTB,3; Enable low out DATA_OUT,r0; write command byte rcall lcd_submit; submit command to display rcall stat_wait; wait for display to complete operation ret ;---------------------------------------- ; Function INIT_DISPLAY ; ; Initializes LCD-Module and writes ; start message ;---------------------------------------- init_display: push r16 ldi r16, $38; set function: 2 rows 8 bit interface 5x7 dots rcall dis_wait; call display without status check (necessary first time) ldi r16, $38; once again set function: 2 rows 8 bit interface 5x7 dots rcall dis_com; call display with status check rcall clear_display rcall cursor_home ldi r16, $06; entry mode: cursor move direction increase, display is not shifted rcall dis_com; send command to display ldi r16, $0c; display on, cursor off, blinking off rcall dis_com; send command to display rcall start_message; displays reset message pop r16 ret ;---------------------------------------- ; Function CLEAR_DISPLAY ; ; clears lcd display ;---------------------------------------- clear_display: push r16 ldi r16, $01; clear display rcall dis_com; send command to display pop r16 ret ;---------------------------------------- ; Function display_off ; ; switch off lcd module ;---------------------------------------- display_off: push r16 ldi r16,$08; switch off display rcall dis_com; send command to display pop r16 ret ;---------------------------------------- ; function stat_wait ; ; waits for status flag of lcd display ; module to clear meaning lcd has completed ; operation ;---------------------------------------- stat_wait: push r16 rcall set_databus_read; set direction of databus to input sbi PORTB,4; R/W high (LCD read operation) stat_in: sbi PORTB,3; enable LCD nop; wait some time nop; nop; nop; in r16,DATA_IN; read data_bus cbi PORTB,3; disable LCD andi r16,$80; isolate bit 7 (LCD status bit) cpi r16,$80 breq stat_in cbi PORTB,4; R/W low (LCD write operation) pop r16 ret ;---------------------------------------- ; Function DIS_WAIT ; ; Sends a command to display module and ; waits a constant time ; Input Parameter: r16: Command Byte ;---------------------------------------- dis_wait: rcall set_databus_write; set direction of databus to output cbi PORTD,6; RS low (it's a command operation) cbi PORTB,4; R/W low (LCD write operation) cbi PORTB,3; Enable low out DATA_OUT,r16; write command byte rcall lcd_submit; submit data to LCD rcall init_wait; wait for some time ret ;---------------------------------------- ; Function dis_com ; ; Sends a command to display module ; Input Parameter: r16: Command Byte ;---------------------------------------- dis_com: rcall set_databus_write; set direction of databus to output cbi PORTD,6; RS low (write a command) cbi PORTB,4; R/W low (write operation) cbi PORTB,3; Enable low out DATA_OUT,r16; write command byte rcall lcd_submit; submit command to LCD rcall stat_wait; check display status ret ;---------------------------------------- ; Function lcd_submit ; ; Enables display module by setting pin 6, ; wait for some time and disable lcd again ;---------------------------------------- lcd_submit: sbi PORTB,3; enable LCD nop; wait some time nop; nop; nop; cbi PORTB,3; disable LCD ret ;**************************************** ; ;***** Interrupts ; ;**************************************** ;---------------------------------------- ;***** Reset Handler ;---------------------------------------- RESET: rjmp START ;***** IRQ0 Handler EXT_INT0: reti ;***** IRQ1 Handler EXT_INT1: reti ;***** Timer 2 Compare Handler TIM2_COMP: reti ;***** Timer 2 Overflow Handler TIM2_OVF: reti ;***** Timer 1 Capture Handler TIM1_CAPT: reti ;***** Timer 1 Compare A Handler TIM1_COMPA: reti ;***** Timer 1 Compare B Handler TIM1_COMPB: reti ;---------------------------------------- ;***** Timer 1 Overflow Handler ;---------------------------------------- TIM1_OVF: push r16 clr r16 ; stop timer/counter 1 out TCCR1B, r16 pop r16 reti ;---------------------------------------- ;***** Timer 0 Overflow Handler ;---------------------------------------- TIM0_OVF: push r16 clr r16 ; stop timer/counter 0 out TCCR0, r16 pop r16 reti ;***** SPI Transfer Complete Handler SPI_STC: reti ;---------------------------------------- ;***** UART RX Complete Handler ; reads MIDI byte and stores it in ; ringbuffer ;---------------------------------------- UART_RXC: push zl push zh push r0 push r16 push r18 in r18,UDR; read MIDI Byte from UART ldi zl,low(wpt); get write position ldi zh,high(wpt) ld r0,z ldi zl,low(ringbuf) ldi zh,high(ringbuf) add zl,r0 st z,r18; save MIDI byte in ringbuf ldi r16,$1f cp r0,r16; reached high end of ringbuf? brge uart_rxc_01 inc r0 rjmp uart_rxc_02 uart_rxc_01: clr r0 uart_rxc_02: ldi zl,low(wpt); set write position ldi zh,high(wpt) st z,r0 uart_rxc_exit: pop r18 pop r16 pop r0 pop zh pop zl reti ;***** UDR Empty Handler UART_DRE: reti ;***** UART TX Complete Handler UART_TXC: reti ;---------------------------------------- ;***** AD Conversion Complete Handler ; max. value: $3ff <=> 5V ; important: both result registers have ; to be read and ADCL has to be read first ;---------------------------------------- CADC: push zl push zh push r16 push r17 push r18 ;-- process ADC-result in r16,ADCL in r17,ADCH ;-- get divisor from joystick sensitivity variable ldi zl,low(jss); joystick sensitivity ldi zh,high(jss) ld r18,z rcall int_division; divide (r17 r16 / r18) ;-- store values in variables ldi zl,low(adc_low); ADC result low value ldi zh,high(adc_low) st z,r16 ldi zl,low(adc_high); ADC result high value ldi zh,high(adc_high) st z,r17 pop r18 pop r17 pop r16 pop zh pop zl reti ;***** EEPROM Ready Handler EE_RDY: reti ;***** Analog Comparator Handler ANA_COMP: reti