;**************************************** ; ; KeyScan ; ======= ; ; Software for simple keyboard controller ; (Formant Pro project) ; ; Copyright (c) 2004, 2005 ; by Carsten Toensmann ; ; History ; ------- ; 28.12.2004 V0.9 ; - first test version ; ;**************************************** .include "2313def.inc" ;***** definitions .equ PROGSTART = $000 .equ VARIABLES = $c1 .equ STACKP = $c0 ;***** variables .dseg .org VARIABLES notebuf: .byte 8; memory area for 8 note values .cseg ;***** interrupt vectors .org PROGSTART rjmp RESET_HANDLER ; Reset Handler rjmp EXT_INT0_HANDLER ; IRQ0 Handler rjmp EXT_INT1_HANDLER ; IRQ1 Handler rjmp TIM_CAPT1_HANDLER ; Timer 1 Capture Handler rjmp TIM_COMP1_HANDLER ; Timer 1 Compare A Handler rjmp TIM_OVF1_HANDLER ; Timer 1 Overflow Handler rjmp TIM_OVF0_HANDLER ; Timer 0 Overflow Handler rjmp UART_RXC_HANDLER ; UART RX Complete Handler rjmp UART_DRE_HANDLER ; UDR Empty Handler rjmp UART_TXC_HANDLER ; UART TX Complete Handler rjmp ANA_COMP_HANDLER ; Analog Comparator Handler ;***** constants notes: .db $3a,$ff,$ff,$ff,$3c,$38,$3b,$39,$1d,$1a,$18,$19 .db $1b,$1f,$1c,$1e,$2a,$2d,$2f,$2e,$2c,$28,$2b,$29 .db $15,$12,$10,$11,$13,$17,$14,$16,$22,$25,$27,$26 .db $24,$20,$23,$21,$0d,$0a,$08,$09,$0b,$0f,$0c,$0e .db $32,$35,$37,$36,$34,$30,$33,$31,$05,$02,$00,$01 .db $03,$07,$04,$06 ;***** main program MAIN: ;-- set stackpointer ldi r16,STACKP out SPL,r16 ;-- configure chip operation ldi r16,$00 ; disable external interrupts out GIMSK,r16 ldi r16,$80 ; enable timer/counter 1 overflow interrupt out TIMSK,r16 ldi r16,$20 ; 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 ldi r16,$ff out DDRB,r16; PORTB is completely output ldi r16,$3f out DDRD,r16; Bit 0-5 of PORTD is output, rest input bset 7 ; enable interrupts ;-- clear note variables ldi r16,$ff ldi xl,low(notebuf) ldi xh,high(notebuf) st x,r16 adiw xl,1 st x,r16 adiw xl,1 st x,r16 adiw xl,1 st x,r16 adiw xl,1 st x,r16 adiw xl,1 st x,r16 adiw xl,1 st x,r16 adiw xl,1 st x,r16 ;-- configure UART ldi r16,$13; init uart MIDI = 31.250 BAUD out UBRR,r16 ldi r16,$48; prepare UART for sending out UCR,r16 ldi r16,$FE; send some Active Sensing to clear UART out UDR,r16; after RESET chaos sleep out UDR,r16 sleep out UDR,r16 sleep out UDR,r16 sleep out UDR,r16 sleep ldi r19,$90; set midi channel ldi r23,$24; note value offset ;***** main scheduler START_SCAN: rcall clear_flags; clear all pressed key flags cbi PORTB,3; enable multiplexer clr r16; set first transmitter scan adress clr r17; set first receiver scan adress clr r18; clear note counter ldi zh,high(notes<<1); set note value pointer ldi zl,low(notes<<1) CHECK_KEY: rcall SET_TRANSMITTER_ADR rcall SET_RECEIVER_ADR rcall DEBOUNCE in r22,PIND; read scan input pin andi r22,$40; select pin 6 of PORTD cpi r22,$00; key pressed? breq NEXT_KEY lpm; read note value into r0 mov r20,r0 add r20,r23 ;-- is MIDI event already sent? rcall new_note NEXT_KEY: inc r16; set next transmitter scan adress cpi r16,$08; 8 key block completed? brlt NEXT_NOTE clr r16 inc r17; set next receiver scan adress NEXT_NOTE: inc r18; set next note counter cpi r18,$40; highest key reached? brne next_check rcall note_off rjmp start_scan next_check: adiw zl,1 rjmp CHECK_KEY STOP: rjmp STOP ; program end - stop - in case main scheduler has to be left ;**************************************** ;** Subroutines ;**************************************** clear_flags: ;-- clear all pressed key flags 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 cpi r16,$ff breq clear_flags_01 andi r16,$7f st x,r16 clear_flags_01: adiw xl,1 inc r17 cpi r17,$08 brne clear_flags_02 pop r17 pop r16 pop xh pop xl ret ;**************************************** note_off: ;-- send note off for all released keys ;-- input: r19: midi channel push xl push xh push r16 push r17 push r18 push r21 clr r17; clear counter ldi xl,low(notebuf); set note variables start adress ldi xh,high(notebuf) note_off_02: ld r16,x cpi r16,$ff brne note_off_01 note_off_03: adiw xl,1 inc r17 cpi r17,$08 breq note_off_exit rjmp note_off_02 note_off_01: mov r18,r16 andi r16,$80; isolate bit 7 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 OUT UDR,r16; send note value sleep OUT UDR,r21; send volume sleep ldi r16,$ff; mark note variable as free st x,r16 rjmp note_off_03; next stored value note_off_exit: pop r21 pop r18 pop r17 pop r16 pop xh pop xl ret new_note: ;-- is MIDI event already sent? ;-- input: r19: midi channel ;-- r20: note value push xl push xh push r16 push r17 push r21 clr r17; clear counter ldi xl,low(notebuf); set note variables start adress ldi xh,high(notebuf) new_note_01: ld r16,x; compare note value to memory andi r16,$7f; clear confirm bit push r20 cp r20,r16 breq confirm_note; key is still pressed pop r20 adiw xl,1 inc r17 cpi r17,$08 breq send_midi rjmp new_note_01 send_midi: ;-- it is a new note, so send midi ldi r21,$20; set volume out UDR,r19; send midi channel sleep OUT UDR,r20; send note value sleep OUT UDR,r21; send volume sleep ;-- find free note variable clr r17; clear counter ldi xl,low(notebuf); set note variables start adress ldi xh,high(notebuf) new_note_02: ld r16,x cpi r16,$ff push r20 breq confirm_note pop r20 adiw xl,1 inc r17 cpi r17,$08 brlt new_note_02 rjmp new_note_exit push r20 confirm_note: pop r20 ori r20,$80; set confirm bit st x,r20 new_note_exit: pop r21 pop r17 pop r16 pop xh pop xl ret ;**************************************** DEBOUNCE: ;-- waits a little time push r16 push r17 clr r17 DEBOUNCE_02: clr r16 DEBOUNCE_01: inc r16 cpi r16,$ff brne DEBOUNCE_01 inc r17 cpi r17,$0a brne DEBOUNCE_02 pop r17 pop r16 ret ;**************************************** SET_TRANSMITTER_ADR: ;-- sets transmitter scan adress ;-- input: r16 andi r16,$07; only bits 0-3 are relevant out PORTB,r16 ret ;**************************************** SET_RECEIVER_ADR: ;-- sets receiver scan adress ;-- input: r17 push r17 andi r17,$07; only bits 0-2 are relevant lsl r17; move to pin 3-5 lsl r17 lsl r17 out PORTD,r17 pop r17 ret ;**************************************** ;** Interrupt routines ;**************************************** RESET_HANDLER: rjmp MAIN EXT_INT0_HANDLER: reti EXT_INT1_HANDLER: reti TIM_CAPT1_HANDLER: reti TIM_COMP1_HANDLER: reti TIM_OVF1_HANDLER: push r16 clr r16 ; stop timer/counter 1 out TCCR1B, r16 pop r16 reti TIM_OVF0_HANDLER: reti UART_RXC_HANDLER: reti UART_DRE_HANDLER: reti UART_TXC_HANDLER: reti ANA_COMP_HANDLER: reti