;**************************************** ; ; UCVM ; ==== ; ; Software for UCVM module (Formant Pro ; project) ; ; Copyright (c) 2003, 2004 ; by Carsten Toensmann ; ; History ; ------- ; 14.05.2004 V0.9 ; - UCVM module completed ; 22.05.2004 V1.0 ; - Processing of incoming MIDI messages ; changed from stack to ringbuffer ; handling ; 24.05.2004 V1.0 ; - Menu handling added ; - CV2CV - Function added ; 07.06.2004 V1.0 ; - CV TO MIDI function added ; ;**************************************** .device AT90S8535 ; select microcontroller ;***** definitions .EQU RAMEND = 0x25f .EQU PROGSTART = 0x000 .EQU STACKP = 0x200 .EQU VARIABLES = 0x201 .EQU SPH = 0x3e; Stackpointer adress High Byte .EQU SPL = 0x3d; Stackpointer adress Low Byte .EQU PINA = 0x19; Pin Register Port A .EQU DDRA = 0x1a; Data Direction Register Port A .EQU PORTA = 0x1b; Data Register Port A .EQU DDRB = 0x17; Data Direction Register Port B .EQU PORTB = 0x18; Data Register Port B .EQU PIND = 0x10; Pin Register Port D .EQU DDRD = 0x11; Data Direction Register Port D .EQU PORTD = 0x12; Data Register Port D .EQU PINC = 0x13; Pin Register Port C .EQU DDRC = 0x14; Data Direction Register Port C .EQU PORTC= 0x15; Data Register Port C .EQU GIMSK = 0x3b; General Interrupt Mask Register .EQU TIMSK = 0x39; Timer/Counter Interrupt Mask Register .EQU MCUCR = 0x35; MCU control register .EQU TCCR1A = 0x2f; timer/counter 1 control register A .EQU TCCR1B = 0x2e; timer/counter 1 control register B .EQU TCNT1H = 0x2d; timer/counter 1 value/data register highbyte .EQU TCNT1L = 0x2c; timer/counter 1 value/data register lowbyte .EQU ADMUX = 0x07; ADC channel select .EQU ADCSR = 0x06; ADC control/status register .EQU ADCH = 0x05; ADC result high value register .EQU ADCL = 0x04; ADC result low value register .EQU UDR = 0x0c; UART I/O data register .EQU USR = 0x0b; UART status register .EQU UCR = 0x0a; UART control register .EQU UBRR = 0x09; UART baud rate register .def xl = r26 .def xh = r27 .def yl = r28 .def yh = r29 .def zl = r30 .def zh = r31 ;***** variables .DSEG .ORG VARIABLES row: .BYTE 1; display row col: .BYTE 1; display column curpos: .BYTE 1; display cursor adress vval: .BYTE 8; note values of voices 0 - 7 wpt: .BYTE 1; write pointer ring buffer rpt: .BYTE 1; read pointer ringbuffer ringbuf: .BYTE 32; ring buffer for incoming MIDI messages sqcnt: .BYTE 1; MIDI sequence counter func: .BYTE 1; currently selected function lastvoice: .BYTE 1; last voice gatestat: .BYTE 1; last gate status fcflag: .BYTE 1; did timer interrupt happen? acnvlo: .byte 1; ADC result low value acnvhi: .byte 1; ADC result high value .CSEG ;***** interrupt vectors .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 "Formant / Pro V1.0#" midi2cv_txt: .db " MIDI TO CV #" midimon_txt: .db " MIDI MONITOR #" cv2cv_txt: .db "CV TO CV Quantizer#" cv2midi_txt: .db " CV TO MIDI #" freqcnt_txt: .db "FREQUENCY COUNTER #" voltmeter_txt: .db " VOLTMETER #" debug_txt: .db "Step reached#" fkey_txt_00: .db " #" fkey_txt_01: .db "next enter#" ;**************************************** ; ;***** 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, $04 ; enable timer/counter 1 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 ldi r16, $ff out DDRC, r16; PORTC is output out DDRB, r16; PORTB is output ldi r16,$e0; PORTA: Bit 0-4 input, rest output out DDRA,r16 rcall midi_in_disable rcall midi_out_disable bset 7 ; enable interrupts ;-- Initialization routines rcall init_gate_bank rcall init_voice_bank rcall init_vars rcall init_display rcall init_dac rcall init_uart ;-- Startup routines rcall start_msg ;-- Main Scheduler schedu: ldi zh,high(fkey_txt_01<<1) ldi zl,low(fkey_txt_01<<1) ldi r18,$03; row ldi r19,$01; column rcall text_out ldi r17, $0f; blinking cursor rcall dis_com ldi zl,low(func); get currently selected function ldi zh,high(func) ld r17,z cpi r17,$00 breq fdis00 cpi r17,$01 breq fdis01 cpi r17,$02 breq fdis02 cpi r17,$03 breq fdis03 cpi r17,$04 breq fdis04 cpi r17,$05 brne fdis00 rjmp fdis05 fdis00:;MIDI2CV ldi zh,high(midi2cv_txt<<1) ldi zl,low(midi2cv_txt<<1) ldi r18,$02; row ldi r19,$01; column rcall text_out fdis00wait: rcall fkey cpi r16,$00 breq fdis00wait; no key pressed cpi r16,$01 brne fdis00_cont rjmp fdis_next fdis00_cont: cpi r16,$03 brne fdis00wait rcall midi2cv; enter rjmp schedu fdis01:;MIDIMON ldi zh,high(midimon_txt<<1) ldi zl,low(midimon_txt<<1) ldi r18,$02; row ldi r19,$01; column rcall text_out fdis01wait: rcall fkey cpi r16,$00 breq fdis01wait; no key pressed cpi r16,$01 breq fdis_next; next function selected cpi r16,$03 brne fdis01wait rcall midimon; enter rjmp schedu fdis02:;CV2CV ldi zh,high(cv2cv_txt<<1) ldi zl,low(cv2cv_txt<<1) ldi r18,$02; row ldi r19,$01; column rcall text_out fdis02wait: rcall fkey cpi r16,$00 breq fdis02wait; no key pressed cpi r16,$01 breq fdis_next; next function selected cpi r16,$03 brne fdis02wait rcall cv2cv; enter rjmp schedu fdis03:;CV2MIDI ldi zh,high(cv2midi_txt<<1) ldi zl,low(cv2midi_txt<<1) ldi r18,$02; row ldi r19,$01; column rcall text_out fdis03wait: rcall fkey cpi r16,$00 breq fdis03wait; no key pressed cpi r16,$01 breq fdis_next; next function selected cpi r16,$03 brne fdis03wait rcall cv2midi; enter rjmp schedu fdis04:;Frequency Counter ldi zh,high(freqcnt_txt<<1) ldi zl,low(freqcnt_txt<<1) ldi r18,$02; row ldi r19,$01; column rcall text_out fdis04wait: rcall fkey cpi r16,$00 breq fdis04wait; no key pressed cpi r16,$01 breq fdis_next; next function selected cpi r16,$03 brne fdis04wait rcall freqcnt; enter rjmp schedu fdis05:;Voltmeter ldi zh,high(voltmeter_txt<<1) ldi zl,low(voltmeter_txt<<1) ldi r18,$02; row ldi r19,$01; column rcall text_out fdis05wait: rcall fkey cpi r16,$00 breq fdis05wait; no key pressed cpi r16,$01 breq fdis_next; next function selected cpi r16,$03 brne fdis05wait rcall voltmeter; enter rjmp schedu fdis_next: ldi zl,low(func); get currently selected function ldi zh,high(func) ld r17,z cpi r17,$05; last function reached brne fdis_nextf clr r17 rjmp fdis_first fdis_nextf: inc r17 fdis_first: st z,r17 rcall key_wait rjmp schedu STOP: rjmp STOP ; program end - stop - in case main scheduler has to be left ;**************************************** ; Function VOLTMETER ; ; quantizes Analog 1 In according to ; gate (Analog 2 In) and displays ; output on display ; ;**************************************** voltmeter: push zl push zh push r16 push r17 push r18 push r19 push r20 rcall fentry; function entry ;-- scan for GATE voltmeter_gate: ldi r16,$04; select adc channel 4 = analog input 2 out ADMUX,r16 ldi r16,$88; set ADC operation mode out ADCSR,r16; start conversion sleep; wait for conversion end clr r17 out ADCSR,r17; disable ADC cpi r16,$39; Gate? breq voltmeter_cv cpi r16,$00; Gate off? brne voltmeter_gate voltmeter_gate_off: ldi zl,low(gatestat); last gate status ldi zh,high(gatestat) ld r20,z cpi r20,$00; gate already reset breq voltmeter_gate ldi zl,low(gatestat); last gate status ldi zh,high(gatestat) clr r20 st z,r20 rjmp voltmeter_gate; wait for next gate voltmeter_cv: ldi zl,low(gatestat); last gate status ldi zh,high(gatestat) ld r20,z cpi r20,$01; gate already set breq voltmeter_gate ldi zl,low(gatestat); store gate set ldi zh,high(gatestat) ldi r20,$01 st z,r20 voltmeter_voice: rcall init_wait ldi r16,$03; select adc channel 3 = analog input 1 out ADMUX,r16 ldi r16,$88; set ADC operation mode and enable ADC out ADCSR,r16; start conversion sleep; wait for conversion end clr r17; disable ADC out ADCSR,r17 ldi zl,low(acnvhi) ldi zh,high(acnvhi) ld r18,z rcall display_hex ldi zl,low(acnvlo) ldi zh,high(acnvlo) ld r18,z rcall display_hex rjmp voltmeter_gate voltmeter_exit: pop r20 pop r19 pop r18 pop r17 pop r16 pop zh pop zl ret ;**************************************** ; Function freqcnt ; ; Frequency counter function. ; To count frequency connect square wave ; to Analog In 1 ;**************************************** freqcnt: push r16 push r17 push r18 push r19 push r26 push r27 rcall fentry; function entry ;-- start counting freqcnt_count: ldi r26,$ff; set cycle counter to high value ldi r27,$ff rcall square_cycle; scan a cycle to sync counter rcall fctimer; set 0.5 sec timer freqcnt_next_cycle: rcall square_cycle; wait for one square cycle completed ldi zl,low(fcflag); timer interrupt happened? ldi zh,high(fcflag) ld r16,z cpi r16,$01 breq freqcnt_complete sbiw r27:r26,$01; decrement counter rjmp freqcnt_next_cycle ;-- display result freqcnt_complete: mov r18,r27 rcall display_hex mov r18,r26 rcall display_hex rcall gate_wait rjmp freqcnt_count freqcnt_exit: pop r27 pop r26 pop r19 pop r18 pop r17 pop r16 ret ;**************************************** ; Function SQUARE_CYCLE ; ; scans a square cycle ;**************************************** square_cycle: push r16 ;-- wait for low value of square wave square_cycle_wfl: ldi r16,$03; select adc channel 3 = analog input 1 out ADMUX,r16 ldi r16,$88; set ADC operation mode out ADCSR,r16; start conversion sleep; wait for conversion end cpi r16,$25 brsh square_cycle_wfl ;-- wait for high value of square wave square_cycle_wfh: ldi r16,$03; select adc channel 3 = analog input 1 out ADMUX,r16 ldi r16,$88; set ADC operation mode out ADCSR,r16; start conversion sleep; wait for conversion end cpi r16,$15 brlo square_cycle_wfh square_cycle_exit: pop r16 ret ;**************************************** ; Function KEY_WAIT ; ; waits for some time ;**************************************** key_wait: rcall init_wait rcall init_wait rcall init_wait key_wait_exit: ret ;**************************************** ; Function FKEY ; ; reads function keys ; ; returns r16 ($01,$02,$03) ; ;**************************************** fkey: push r17 clr r16; default: no key is pressed in r17,PINA sbrc r17,0 rjmp fkey_02 ;-- key 1 is pressed ldi r16,$01 rjmp fkey_exit fkey_02: sbrc r17,1 rjmp fkey_03 ;-- key 2 is pressed ldi r16,$02 rjmp fkey_exit fkey_03: sbrc r17,2 rjmp fkey_exit ;-- key 3 is pressed: ldi r16,$03 fkey_exit: pop r17 ret ;**************************************** ; Function CV2MIDI ; ; quantizes Analog 1 In (CV) according to ; gate (Analog 2 In) and generates ; a MIDI message (MIDI OUT) ; ;**************************************** cv2midi: push zl push zh push r16 push r17 push r18 push r19 push r20 rcall fentry; function entry ;-- scan for GATE cv2midi_gate: ldi r16,$04; select adc channel 4 = analog input 2 out ADMUX,r16 ldi r16,$88; set ADC operation mode out ADCSR,r16; start conversion sleep; wait for conversion end clr r17 out ADCSR,r17; disable ADC cpi r16,$39; Gate? breq cv2midi_cv cpi r16,$00; Gate off? brne cv2midi_gate cv2midi_gate_off: ldi zl,low(gatestat); last gate status ldi zh,high(gatestat) ld r20,z cpi r20,$00; gate already reset breq cv2midi_gate ;-- create and send MIDI OFF message ldi zl,low(lastvoice) ldi zh,high(lastvoice) ld r16,z rcall midi_out_enable ldi r18,$90; send channel info clr r19; note off out UDR,r18 sleep out UDR,r16 sleep out UDR,r19 sleep ldi zl,low(gatestat); last gate status ldi zh,high(gatestat) clr r20 st z,r20 rjmp cv2midi_gate; wait for next gate cv2midi_cv: ldi zl,low(gatestat); last gate status ldi zh,high(gatestat) ld r20,z cpi r20,$01; gate already set breq cv2midi_gate ldi zl,low(gatestat); store gate set ldi zh,high(gatestat) ldi r20,$01 st z,r20 cv2midi_voice: rcall init_wait ldi r16,$03; select adc channel 3 = analog input 1 out ADMUX,r16 ldi r16,$88; set ADC operation mode and enable ADC out ADCSR,r16; start conversion sleep; wait for conversion end clr r17; disable ADC out ADCSR,r17 ;-- create and send MIDI ON message ;-- r16 contains ADC conversion result ldi r18,$90; send channel info ldi r17,$24 add r16,r17; create and send note value ldi r19,$30; send note on medium speed rcall midi_out_enable out UDR,r18 sleep out UDR,r16 sleep out UDR,r19 sleep ldi zl,low(lastvoice) ldi zh,high(lastvoice) st z,r16 rjmp cv2midi_gate cv2midi_exit: rcall midi_out_disable pop r20 pop r19 pop r18 pop r17 pop r16 pop zh pop zl ret ;**************************************** ; Function FENTRY ; ; Disables cursor and clears function key ; line ; ;**************************************** fentry: push r17 push r18 push r19 push zl push zh ldi r17, $0c; blinking cursor off rcall dis_com ldi zh,high(fkey_txt_00<<1); clear function texts on display ldi zl,low(fkey_txt_00<<1) ldi r18,$03; row ldi r19,$01; column rcall text_out fentry_exit: pop zh pop zl pop r19 pop r18 pop r17 ret ;**************************************** ; Function CV2CV ; ; quantizes Analog 1 In (CV) according to ; gate (Analog 2 In) and puts it on ; CV OUT 1 ; ;**************************************** cv2cv: push zl push zh push r16 push r17 push r18 push r19 push r20 push r21 push r22 rcall fentry; function entry ;-- scan for GATE cv2cv_gate: rcall init_wait ldi r16,$04; select adc channel 4 = analog input 2 out ADMUX,r16 ldi r16,$88; set ADC operation mode out ADCSR,r16; start conversion sleep; wait for conversion end cpi r16,$39; wait for 5V brsh cv2cv_cv cv2cv_gate_off: clr r17; clear gate 0 rcall reset_gate ldi zl,low(lastvoice) ldi zh,high(lastvoice) ld r16,z rcall set_voice; otherwise voice drift rjmp cv2cv_gate; wait for next gate cv2cv_cv: rcall init_wait ldi r16,$03; select adc channel 3 = analog input 1 out ADMUX,r16 ldi r16,$88; set ADC operation mode out ADCSR,r16; start conversion sleep; wait for conversion end clr r17; set 0 ldi zl,low(lastvoice) ldi zh,high(lastvoice) st z,r16 rcall set_voice rcall set_gate rjmp cv2cv_gate cv2cv_exit: pop r22 pop r21 pop r20 pop r19 pop r18 pop r17 pop r16 pop zh pop zl ret ;**************************************** ; Function MIDI2CV ; ; MIDI to CV Converter ; ;**************************************** midi2cv: push zl push zh push r0 push r1 push r16 push r17 push r18 push r19 push r20 rcall fentry; function entry rcall midi_in_enable midi2cv_wait: ldi zl,low(wpt); read write-pointer ldi zh,high(wpt) ld r0,z ldi zl,low(rpt); read read-pointer ldi zh,high(rpt) ld r1,z cp r1,r0; is anything to do? breq midi2cv_wait; nothing to do ldi zl,low(ringbuf); get ringbuffer start adress ldi zh,high(ringbuf) add zl, r1; add read pointer offset ld r18,z; read MIDI byte from ringbuffer ;-- process MIDI sequence ldi zl,low(sqcnt); get sequence counter ldi zh,high(sqcnt) ld r17,z cpi r17,$01; first byte? brge midi2cv_seq2; if not go to next byte ;-- process first byte cpi r18,$fe; active sensing breq midi2cv_next cpi r18,$80; channel / note event omitted? brsh midi2cv_firstbyte mov r19,r18; complete crippled sequence ldi r18,$90 push r18 mov r18,r19 rjmp midi2cv_seq2 midi2cv_firstbyte: push r18; save first byte ldi r17,$01; increment sequence counter st z,r17 rjmp midi2cv_next midi2cv_seq2: cpi r17,$02; second byte? brge midi2cv_complete push r18; save second byte ldi r17,$02; increment sequence counter st z,r17 rjmp midi2cv_next midi2cv_complete: push r18; save third byte clr r17; reset sequence counter st z,r17 pop r20 pop r19 pop r18 cpi r20,$00; note off? brne midi2cv_analog_03 rcall reset_voice cpi r17,$ff; free voice found? breq midi2cv_next; if not next buffer value rcall reset_gate rjmp midi2cv_next midi2cv_analog_03: mov r18,r19; check note value subi r18,$24 mov r16,R18 midi2cv_analog_out: rcall find_free_voice cpi r17,$ff; free voice found? breq midi2cv_next; if not next buffer value rcall set_voice rcall set_gate midi2cv_next: ldi r16,$1f; buffer end reached? cp r1,r16 brge midi2cv_01 inc r1 rjmp midi2cv_02 midi2cv_01: clr r1 midi2cv_02: ldi zl,low(rpt); read read-pointer ldi zh,high(rpt) st z,r1 rjmp midi2cv_wait midi2cv_exit: rcall midi_in_disable pop r20 pop r19 pop r18 pop r17 pop r16 pop r1 pop r0 pop zl pop zh ret ;**************************************** ; Function FIND_FREE_VOICE ; ; finds a free voice number ; ; returns r17: cv adress ($00 - $07) ;**************************************** find_free_voice: push zl push zh push r16 ldi r17,$ff; default: no free voice ldi zl,low(vval); get voice values start adress ldi zh,high(vval) ld r16,z; load first value cpi r16,$ff; free? brne find_free_voice_1 clr r17; return voice 0 rjmp find_free_voice_exit find_free_voice_1: inc zl; get next voice value ld r16,z; load second value cpi r16,$ff; free? brne find_free_voice_2 ldi r17,$01; return voice 1 rjmp find_free_voice_exit find_free_voice_2: inc zl; get next voice value ld r16,z; load third value cpi r16,$ff; free? brne find_free_voice_3 ldi r17,$02; return voice 2 rjmp find_free_voice_exit find_free_voice_3: inc zl; get next voice value ld r16,z; load fourth value cpi r16,$ff; free? brne find_free_voice_4 ldi r17,$03; return voice 3 rjmp find_free_voice_exit find_free_voice_4: inc zl; get next voice value ld r16,z; load fifth value cpi r16,$ff; free? brne find_free_voice_5 ldi r17,$04; return voice 4 rjmp find_free_voice_exit find_free_voice_5: inc zl; get next voice value ld r16,z; load sixth value cpi r16,$ff; free? brne find_free_voice_6 ldi r17,$05; return voice 5 rjmp find_free_voice_exit find_free_voice_6: inc zl; get next voice value ld r16,z; load seventh value cpi r16,$ff; free? brne find_free_voice_7 ldi r17,$06; return voice 6 rjmp find_free_voice_exit find_free_voice_7: inc zl; get next voice value ld r16,z; load eighth value cpi r16,$ff; free? brne find_free_voice_exit ldi r17,$07; return voice 7 find_free_voice_exit: pop r16 pop zh pop zl ret ;**************************************** ; Function RESET_VOICE ; ; clears a voice ; ; parameter: r19: MIDI note value ; returns r17: adress ($00 - $07) ;**************************************** reset_voice: push zl push zh push r16 subi r19,$24; get note value ldi r17,$ff; default: no voice found ldi zl,low(vval); get voice values start adress ldi zh,high(vval) ld r16,z; load first value cp r16,r19; found? brne reset_voice_1 clr r17; return voice 0 rjmp reset_voice_exit reset_voice_1: inc zl; get next voice value ld r16,z; load second value cp r16,r19; found? brne reset_voice_2 ldi r17,$01; return voice 1 rjmp reset_voice_exit reset_voice_2: inc zl; get next voice value ld r16,z; load third value cp r16,r19; found? brne reset_voice_3 ldi r17,$02; return voice 2 rjmp reset_voice_exit reset_voice_3: inc zl; get next voice value ld r16,z; load fourth value cp r16,r19; found? brne reset_voice_4 ldi r17,$03; return voice 3 rjmp reset_voice_exit reset_voice_4: inc zl; get next voice value ld r16,z; load fifth value cp r16,r19; found? brne reset_voice_5 ldi r17,$04; return voice 4 rjmp reset_voice_exit reset_voice_5: inc zl; get next voice value ld r16,z; load sixth value cp r16,r19; found? brne reset_voice_6 ldi r17,$05; return voice 5 rjmp reset_voice_exit reset_voice_6: inc zl; get next voice value ld r16,z; load seventh value cp r16,r19; found? brne reset_voice_7 ldi r17,$06; return voice 6 rjmp reset_voice_exit reset_voice_7: inc zl; get next voice value ld r16,z; load eighth value cp r16,r19; found? brne reset_voice_exit ldi r17,$07; return voice 7 reset_voice_exit: cpi r17,$ff; voice found breq reset_voice_end; if not leave imediately ldi r16,$ff; clear voice st z,r16 reset_voice_end: pop r16 pop zh pop zl ret ;**************************************** ; Function SET_VOICE ; ; sets a cv for a voice ; ; parameter: r17: cv adress ($00 - $07) ; r16: value ;**************************************** set_voice: push zl push zh rcall dac_out; sets analog value for voice ;-- set cv adress cbi PORTA,5 cbi PORTA,6 cbi PORTA,7 sbrc r17,0 sbi PORTA,5 sbrc r17,1 sbi PORTA,6 sbrc r17,2 sbi PORTA,7 ;-- store value in CV bank cbi PORTC,7; enable CV bank rcall cv_wait sbi PORTC,7; disable CV bank ;-- store value in memory ldi zl,low(vval); begin of memory for note values ldi zh,high(vval) add zl,r17; final note position st z,r16 set_voice_exit: pop zh pop zl ret ;**************************************** ; Function SR_GATE ; ; enables or disables a gate ; ; parameter: r17: gate adress ($00 - $07) ; r16: value ($00, $ff) ;**************************************** sr_gate: rcall dac_out; sets analog value for gate ;-- set gate adress cbi PORTA,5 cbi PORTA,6 cbi PORTA,7 sbrc r17,0 sbi PORTA,5 sbrc r17,1 sbi PORTA,6 sbrc r17,2 sbi PORTA,7 ;-- store value in GATE bank cbi PORTC,6; enable GATE bank rcall cv_wait sbi PORTC,6; disable GATE bank sr_gate_exit: ret ;**************************************** ; Function RESET_GATE ; ; disables gate 1-8 ;**************************************** reset_gate: push r16 ldi r16,$ff; reset gate rcall sr_gate reset_gate_exit: pop r16 ret ;**************************************** ; Function SET_GATE ; ; enables gate 1-8 ;**************************************** set_gate: push r16 clr r16; set gate rcall sr_gate set_gate_exit: pop r16 ret ;**************************************** ; Function INIT_VOICE_BANK ; ; sets cv 0-7 to 0V ;**************************************** init_voice_bank: push r16 push r17 sbi PORTC,7; disable CV-Bank clr r16; set 0V clr r17; set gate adress 0 rcall set_voice clr r16; set 0V inc r17; set gate adress 1 rcall set_voice clr r16; set 0V inc r17; set gate adress 2 rcall set_voice clr r16; set 0V inc r17; set gate adress 3 rcall set_voice clr r16; set 0V inc r17; set gate adress 4 rcall set_voice clr r16; set 0V inc r17; set gate adress 5 rcall set_voice clr r16; set 0V inc r17; set gate adress 6 rcall set_voice clr r16; set 0V inc r17; set gate adress 7 rcall set_voice init_voice_bank_exit: pop r17 pop r16 ret ;**************************************** ; Function INIT_GATE_BANK ; ; disables gate 0-7 ;**************************************** init_gate_bank: push r16 push r17 sbi PORTC,7; disable CV-Bank ldi r16,$ff; reset gate clr r17; set gate adress 0 rcall sr_gate ldi r16,$ff; reset gate inc r17; set gate adress 1 rcall sr_gate ldi r16,$ff; reset gate inc r17; set gate adress 2 rcall sr_gate ldi r16,$ff; reset gate inc r17; set gate adress 3 rcall sr_gate ldi r16,$ff; reset gate inc r17; set gate adress 4 rcall sr_gate inc r17; set gate adress 5 ldi r16,$ff; reset gate rcall sr_gate ldi r16,$ff; reset gate inc r17; set gate adress 6 rcall sr_gate ldi r16,$ff; reset gate inc r17; set gate adress 7 rcall sr_gate init_gate_bank_exit: pop r17 pop r16 ret ;**************************************** ; Function MIDI_IN_ENABLE ; ; enables UART for midi in ;**************************************** midi_in_enable: push r16 ldi r16,$90; prepare UART for reading out UCR,r16 sbi PORTB,1; connect midi_in to RXD midi_in_enable_exit: pop r16 ret ;**************************************** ; Function MIDI_IN_DISABLE ; ; disables UART for midi in ;**************************************** midi_in_disable: push r16 cbi PORTB,1; disconnect midi_in from RXD clr r16; disable UART out UCR,r16 midi_in_disable_exit: pop r16 ret ;**************************************** ; Function MIDI_OUT_ENABLE ; ; enables UART for midi out ;**************************************** midi_out_enable: push r16 ldi r16,$48; prepare UART for sending out UCR,r16 sbi PORTB,0; connect midi_out to TXD midi_out_enable_exit: pop r16 ret ;**************************************** ; Function MIDI_OUT_DISABLE ; ; disables UART for midi out ;**************************************** midi_out_disable: push r16 cbi PORTB,0; disconnect midi_out from TXD clr r16; disable UART out UCR,r16 midi_out_disable_exit: pop r16 ret ;**************************************** ; Function MIDIMON ; ; reads midi in and displays it in hex ; code ;**************************************** midimon: push r17 rcall clear_display ldi r17, $0f; blinking cursor rcall dis_com rcall read_midibuf midimon_exit: pop r17 ret ;**************************************** ; Function READ_MIDIBUF ; ; reads MIDI buffer in a loop ;**************************************** read_midibuf: push zl push zh push r0 push r1 push r16 push r17 push r18 push r19 push r20 rcall midi_in_enable read_midibuf_wait: ldi zl,low(wpt); read write-pointer ldi zh,high(wpt) ld r0,z ldi zl,low(rpt); read read-pointer ldi zh,high(rpt) ld r1,z cp r1,r0; is anything to do? breq read_midibuf_wait; nothing to do bclr 7 ; disable interrupts ldi zl,low(ringbuf); get ringbuffer start adress ldi zh,high(ringbuf) add zl, r1; add read pointer offset ld r18,z; read MIDI byte from ringbuffer cpi r18,$fe; no active sensing breq read_midibuf_next ;-- process MIDI sequence ldi zl,low(sqcnt); get sequence counter ldi zh,high(sqcnt) ld r17,z cpi r17,$01; first byte? brge read_midibuf_seq2; if not go to next byte ;-- process first byte cpi r18,$80; channel / note event omitted? brsh read_midibuf_firstbyte mov r19,r18; complete crippled sequence ldi r18,$90 push r18 mov r18,r19 rjmp read_midibuf_seq2 read_midibuf_firstbyte: push r18; save first byte ldi r17,$01; increment sequence counter st z,r17 rjmp read_midibuf_next read_midibuf_seq2: cpi r17,$02; second byte? brge read_midibuf_complete push r18; save second byte ldi r17,$02; increment sequence counter st z,r17 rjmp read_midibuf_next read_midibuf_complete: rcall midi_in_disable push r18; save third byte clr r17; reset sequence counter st z,r17 pop r20 pop r19 pop r18 rcall display_hex mov r18,r19 rcall display_hex mov r18,r20 rcall display_hex rcall midi_in_enable read_midibuf_next: ldi r16,$1f; buffer end reached? cp r1,r16 brge read_midibuf_01 inc r1 rjmp read_midibuf_02 read_midibuf_01: clr r1 read_midibuf_02: ldi zl,low(rpt); read read-pointer ldi zh,high(rpt) st z,r1 bset 7 ; enable interrupts rjmp read_midibuf_wait read_midibuf_exit: rcall midi_in_disable pop r20 pop r19 pop r18 pop r17 pop r16 pop r1 pop r0 pop zl pop zh ret ;**************************************** ; Function SET_CURSOR ; ; sets cursor according to variable values ; (row,col) ;**************************************** set_cursor: push zl push zh push r17 ldi zl,low(curpos) ldi zh,high(curpos) ld r17,z ori r17,$80 rcall dis_com pop r17 pop zh pop zl ret ;**************************************** ; Function DISPLAY_NIBBLE ; ; displays a nibble at current cursor ; position in hex ;**************************************** display_nibble: push r0 push r17 push r18 andi r18,$0f cpi r18,$0a brge dn01 ldi r17,$30 rjmp dn02 dn01: ldi r17,$37 dn02: add r18,r17 mov r0,r18 rcall dis_text pop r18 pop r17 pop r0 ret ;**************************************** ; Function increment_cursor ;**************************************** increment_cursor: push zl push zh push r16 push r17 ldi zl,low(row) ldi zh,high(row) ld r17,z ldi zl,low(col) ldi zh,high(col) ld r16,z cpi r16,$13; highest col reached? brlo increment_cursor_compute; if not compute adress byte clr r16 cpi r17,$03 brlo increment_cursor_next_row clr r17 rjmp increment_cursor_save_row increment_cursor_next_row: inc r17 increment_cursor_save_row: ldi zl,low(row) ldi zh,high(row) st z,r17 rjmp increment_cursor_compute_row increment_cursor_compute: inc r16 increment_cursor_compute_row: cpi r17,$00 breq increment_cursor_row0 cpi r17,$01 breq increment_cursor_row1 cpi r17,$02 breq increment_cursor_row2 ldi r17,$54;start adress row 3 add r17,r16 ldi zl,low(curpos) ldi zh,high(curpos) st z,r17 rjmp increment_cursor_exit increment_cursor_row2: ldi r17,$14;start adress row 2 add r17,r16 ldi zl,low(curpos) ldi zh,high(curpos) st z,r17 rjmp increment_cursor_exit increment_cursor_row1: ldi r17,$40; start adress row 1 add r17,r16 ldi zl,low(curpos) ldi zh,high(curpos) st z,r17 rjmp increment_cursor_exit increment_cursor_row0: ldi r17,$00; start adress row 0 add r17,r16 ldi zl,low(curpos) ldi zh,high(curpos) st z,r17 increment_cursor_exit: ldi zl,low(col); save new column value ldi zh,high(col) st z,r16 pop r17 pop r16 pop zh pop zl ret ;**************************************** ; Function DISPLAY_HEX ; ; displays a byte (register content) at ; (row,col) and (row,(col+1)) in hex ; ; parameter: r18 ;**************************************** display_hex: rcall set_cursor ;-- display hex value push r18 swap r18 rcall display_nibble rcall increment_cursor rcall set_cursor ;-- display hex value pop r18 rcall display_nibble rcall increment_cursor rcall set_cursor ret ;**************************************** ; Function CLEAR_DISPLAY ; ; clears lcd display ;**************************************** clear_display: push r17 ldi r17, $01; clear display rcall dis_com pop r17 ret ;**************************************** ; Function INIT_UART ; ; initializes uart with 31.250 baud (MIDI) ;**************************************** init_uart: push r16 ldi r16,$0f out UBRR,r16 pop r16 ret ;**************************************** ; Function INIT_VARS ; ; initializes variables ;**************************************** init_vars: push r16 push r0 push zl push zh ;-- display cursor position (row) clr r0 ldi zl,low(row) ldi zh,high(row) st z,r0 ;-- display cursor position (column) ldi zl,low(col) ldi zh,high(col) st z,r0 ;-- display cursor adress ldi zl,low(curpos) ldi zh,high(curpos) st z,r0 ;-- voice values ldi r16,$ff ldi zl,low(vval) ldi zh,high(vval) st z,r16 inc zl st z,r16 inc zl st z,r16 inc zl st z,r16 inc zl st z,r16 inc zl st z,r16 inc zl st z,r16 inc zl st z,r16 ;-- write pointer MIDI IN ring buffer ldi zl,low(wpt) ldi zh,high(wpt) st z,r0 ;-- read pointer MIDI IN ring buffer ldi zl,low(rpt) ldi zh,high(rpt) st z,r0 ;-- MIDI sequence byte counter ldi zl,low(sqcnt) ldi zh,high(sqcnt) st z,r0 ;-- currently selected function ldi zl,low(func) ldi zh,high(func) st z,r0; default: MIDI2CV ;-- last voice ldi zl,low(lastvoice) ldi zh,high(lastvoice) st z,r0; default: no gate set ;-- last gate status ldi zl,low(gatestat) ldi zh,high(gatestat) st z,r0; default: no gate set init_vars_exit: pop zh pop zl pop r0 pop r16 ret ;**************************************** ; Function DAC_OUT ; ; converts byte into a dac corresponding ; bit combination (bits have to be swapped ; to fit to board layout) ; ; bit 2 (in) => bit 5 (out) ; bit 3 (in) => bit 4 (out) ; bit 4 (in) => bit 3 (out) ; bit 5 (in) => bit 2 (out) ; ; bits 0 and 1 are on correct positions ; ; the dac has a 6 bit resolution, so ; bit 6 and 7 are ignored ; ; parameters: r16: byte to be converted ;**************************************** dac_out: rcall init_dac; clear dac ;-- swap around sbrc r16,0; is bit 0 set? sbi PORTC,0; set bit 0 in output register sbrc r16,1; is bit 1 set? sbi PORTC,1; set bit 1 in output register sbrc r16,2; is bit 2 set? sbi PORTC,5; set bit 5 in output register sbrc r16,3; is bit 3 set? sbi PORTC,4; set bit 4 in output register sbrc r16,4; is bit 4 set? sbi PORTC,3; set bit 3 in output register sbrc r16,5; is bit 5 set? sbi PORTC,2; set bit 2 in output register ret ;**************************************** ; Function INIT_DAC ; ; initializes digital analog converter ;**************************************** init_dac: cbi PORTC,0 cbi PORTC,1 cbi PORTC,2 cbi PORTC,3 cbi PORTC,4 cbi PORTC,5 ret ;**************************************** ; Function START_MSG ; ; displays start message ;**************************************** start_msg: push zh push zl push r18 push r19 ldi zh,high(start_txt<<1); Textadresse ldi zl,low(start_txt<<1) ldi r18,$01; row ldi r19,$01; column rcall text_out pop r19 pop r18 pop zl pop zh ret ;**************************************** ; Function TEXT_OUT ; ; Displays a text on lcd module ; ; Parameter: zh, zl - Text adress ; r18 - row ($00 - $03) ; r19 - column($00-$13) ;**************************************** text_out: ldi r20,$23 ldi r17,$00; clear cursor command register cpi r18,$00; check row selection breq add_col cpi r18,$01 brne row_03 ldi r17,$40 rjmp add_col row_03: cpi r18,$02 brne row_04 ldi r17,$14 rjmp add_col row_04: cpi r18,$03 brne add_col ldi r17,$54 add_col: add r17,r19 ori r17,$80 rcall dis_com text_01: lpm; load (Z) into r0 cp r0,r20 brne text_02 ret text_02: rcall dis_text; character to display inc r17 rcall dis_com; next cursor pos adiw zl,1; next text byte rjmp text_01 ;**************************************** ; Function CV_WAIT ; ; Waits for some time ;**************************************** cv_wait: push r16 ldi r16, $00; start value timer/counter 1 out TCNT1L, r16 out TCNT1H, r16 ldi r16, $01; prescale timer/counter 1 = clock out TCCR1B, r16 sleep pop r16 ret ;**************************************** ; Function fctimer ; ; sets timer for interrupting after ; 0.5 sec ;**************************************** fctimer: push r16 clr r16; clear interrupt variable ldi zl,low(fcflag) ldi zh,high(fcflag) st z,r16 ldi r16, $00; start value timer/counter 1 out TCNT1L, r16 out TCNT1H, r16 ldi r16, $03; prescale timer/counter 1 = clock / 64 out TCCR1B, r16 fctimer_exit: pop r16 ret ;**************************************** ; Function GATE_WAIT ; ; Waits for some time ;**************************************** gate_wait: push r16 ldi r16, $00; start value timer/counter 1 out TCNT1L, r16 out TCNT1H, r16 ldi r16, $03; prescale timer/counter 1 = clock / 64 out TCCR1B, r16 sleep pop r16 ret ;**************************************** ; Function INIT_WAIT ; ; Waits for some time ;**************************************** init_wait: push r16 ldi r16, $00; 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 INIT_DISPLAY ; ; Initializes LCD-Module ;**************************************** init_display: push r17 ;------------ configure display module operation mode ldi r17, $38; 2 rows 8 bit interface rcall dis_wait ldi r17, $38; 2 rows 8 bit interface rcall dis_com rcall clear_display ldi r17, $02; cursor home rcall dis_com ldi r17, $06; entry mode rcall dis_com ldi r17, $0c; blinking cursor rcall dis_com pop r17 ret ;**************************************** ; Function DIS_WAIT ; ; Sends a command to display module and ; waits a constant time ; Input Parameter: r17: Command Byte ;**************************************** dis_wait: push r16 ldi r16, $ff; all bits port D (Display) are output out DDRD, r16 ldi r16,$00; RS low (write a command), R/W low (write operation), Enable low out PORTB,r16 out PORTD,r17; write command byte rcall lcd_enable ;-- wait for some time rcall init_wait pop r16 ret ;**************************************** ; Function lcd_enable ; ; Enables display module by setting pin 6, ; wait for some time and disable lcd again ;**************************************** lcd_enable: sbi PORTB,4; Enable high nop; nop; nop; nop; cbi PORTB,4; Enable low ret ;**************************************** ; Function dis_text ; ; Sends a character to display ; Input Parameter: r0: Character ;**************************************** dis_text: push r16 ldi r16, $ff; all bits port D (Display) are output out DDRD, r16 ldi r16,$04; RS high (write a character), R/W low (write operation), Enable low out PORTB,r16 out PORTD,r0; write command byte rcall lcd_enable rcall stat_wait; check display status pop r16 ret ;**************************************** ; Function dis_com ; ; Sends a command to display module ; Input Parameter: r17: Command Byte ;**************************************** dis_com: push r16 ldi r16, $ff; all bits port D (Display) are output out DDRD, r16 ldi r16,$00; RS low (write a command), R/W low (write operation), Enable low out PORTB,r16 out PORTD,r17; write command byte rcall lcd_enable rcall stat_wait; check display status pop r16 ret ;**************************************** ; function stat_wait ; ; waits for status flag of lcd display ; module to clear ;**************************************** stat_wait: push r16 push r17 ;-- read busy flag of display module ldi r16,$00; all bits port D are input out DDRD, r16 sbi PORTB,3; read mode stat_in: sbi PORTB,4; Enable high nop; nop; nop; nop; in r17,PIND; read display data port cbi PORTB,4; Enable low andi r17,$80; isolate bit 7 cpi r17,$80 breq stat_in cbi PORTB,3; read low pop r17 pop r16 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 zl push zh push r16 clr r16 ; stop timer/counter 1 out TCCR1B, r16 ldi r16,$01; set interrupt variable ldi zl,low(fcflag) ldi zh,high(fcflag) st z,r16 pop r16 pop zh pop zl reti ;***** Timer 0 Overflow Handler TIM0_OVF: 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 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 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 ; result has to be divided by 16 ; to fit to 6 bit DAC output ; important: both result registers have ; to be read and ADCL has to be read first ; ; returns r16 with conversion / 16 result, r21 ; with converison low and r22 with conversion high CADC: push zl push zh push r17 ;-- quantize result clr r16; clear result in r17,ADCL push r17 ldi zl,low(acnvlo); ADC result low value ldi zh,high(acnvlo) st z,r17 ;-- process high value of ADC-result in r17,ADCH ldi zl,low(acnvhi); ADC result high value ldi zh,high(acnvhi) st z,r17 sbrc r17,1; is bit 1 set? ori r16,$20; set bit 5 in output register sbrc r17,0; is bit 0 set? ori r16,$10; set bit 4 in output register ;-- process low value of ADC-result pop r17 sbrc r17,7; is bit 7 set? ori r16,$08; set bit 3 in output register sbrc r17,6; is bit 6 set? ori r16,$04; set bit 2 in output register sbrc r17,5; is bit 5 set? ori r16,$02; set bit 1 in output register sbrc r17,4; is bit 4 set? ori r16,$01; set bit 0 in output register ;-- eliminate rounded differences andi r17,$0f; eliminate upper nibble cpi r17,$08 brlo cadc_exit inc r16 cadc_exit: pop r17 pop zh pop zl reti ;***** EEPROM Ready Handler EE_RDY: reti ;***** Analog Comparator Handler ANA_COMP: reti