=== MICrODEC: Stock Functions === ==== 6s Mono Delay ==== This program performs a simple delay routine. It stores the incoming data from the codec, and plays back a delayed sample. The input data is mono, and is taken from the left channel. The output is also mono, and is presented on both the left and right channels. However, if you're using a mono plug, you can only mix in the dry signal on the left channel (as there is no input signal on the right). The pot (MOD1) controls the delay time. It goes from 6ms to 6s. MOD2 does nothing for this program. Most analog delay pedals change the rate at which data is being clocked in and out, to change the delay time. This gives a smooth change in audio when the delay is varied, although it also changes the frequency resolution of the signal. To change the delay time in the MICrODEC, the easiest way is to just jump to the new delay time, although this gives an audible pop as the data abruptly switches. In this program, we increment the delay time (or decrement it) at a rate of one sample per sample, until it matches what you want it to be. This gives the effect of frequency doubling (or reversing) during delay transitions, which can be interesting sonically. [[attachment:delay_6s_mono.asm|delay_6s_mono.asm]] ---- {{{#!highlight nasm ; program: delay-18b-pot-mono.asm ; UID = 000033 - unique id to eliminate conflicts between variables ; 18b address space (6s delay time) ; mono data (left in only, output on left and right) ; pot (MOD1) controlled delay time (6ms - 6s) ; program overview ; ; data is read in from memory and written out the codec at the same time ; new data is written to the memory from the codec. ADC0 (MOD1) is read ; and averaged over 256 samples to reduce jitter. this value is subtracted ; from the write address to create the desired read address. if the actual ; read address doesnt match the desired read address, it is either ; incremented or decremented by one sample each sample period until it ; matches. this reduces noise during delay time transitions. ; register usage - may be redefined in other sections ; ; r0 desired delay time fractional byte ; r1 ; r2 left/right lsb out ; r3 left/right msb out ; r4 ; r5 ; r6 left lsb in ; r7 left msb in ; r8 ; r9 ; r10 adc accumulator lsb ; r11 adc accumulator msb ; r12 actual delay lsb ; r13 actual delay msb ; r14 adc sample counter ; r15 switch sample counter ; r16 temporary swap register ; r17 temporary swap register ; r18 null register ; r19 adc accumulator fractional byte ; r20 temporary swap register ; r21 actual delay fractional byte ; r22 write address third byte ; r23 read address third byte ; r24 write address lsb ; r25 write address msb ; r26 desired delay lsb ; r27 desired delay msb ; r28 read address lsb ; r29 read address msb ; r30 jump location for interrupt lsb ; r31 jump location for interrupt msb ; t ;program starts here first time ldi r30,$23 ; set jump location to program start clr r24 ; clear write register clr r25 ldi r22,$00 ; setup write address high byte clr r18 ; setup r18 as null register for carry addition and ddr setting ldi r17,$ff ; setup r17 for ddr setting clear_000033: ; clear delay buffer ; eliminates static when first switching to the delay setting adiw r25:r24,$01 ; increment write register adc r22,r18 ; increment write third byte cpi r22,$04 ; check if full memory space has been cleared breq cleardone_000033 ; continue until end of buffer reached out portd,r24 ; set address sts porth,r25 out portg,r22 ; pull ce low,we low,and set high bits of address out ddra,r17 ; set porta as output for data write out ddrc,r17 ; set portc as output for data write out porta,r18 ; set data out portc,r18 ; r18 is cleared above sbi portg,portg2 ; pull we high to write out ddra,r18 ; set porta as input for data lines out ddrc,r18 ; set portc as input for data lines rjmp clear_000033 ; continue clearing cleardone_000033: ; reset registers clr r24 ; clear write register clr r25 ldi r22,$00 ; setup write address high byte clr r28 ; set read address to minimum delay ldi r29,$ff ldi r23,$07 ; setup read address high byte clr r21 ; set actual delay time to minimum delay ldi r16,$01 mov r12,r16 clr r13 clr r2 ; initialize data output registers clr r3 reti ; finish with initialization and wait for next interrupt ;program starts here every time but first ;initiate data transfer to codec sbi portb,portb0 ; toggle slave select pin out spdr,r3 ; send out left channel msb cbi portb,portb0 ;increment sram addresses adiw r25:r24,$01 ; increment write address adc r22,r18 ; increment write third byte andi r22,$03 ; mask off unsed bits adiw r29:r28,$01 ; increment read address adc r23,r18 ; increment read third byte andi r23,$03 ; mask off unsed bits ori r23,$04 ; set we bit for reading wait1_000033: ; check if byte has been sent in r17,spsr sbrs r17,spif rjmp wait1_000033 in r7,spdr ; recieve in left channel msb out spdr,r2 ; send out left channel lsb wait2_000033: ; check if byte has been sent in r17,spsr sbrs r17,spif rjmp wait2_000033 in r6,spdr ; recieve in left channel lsb out spdr,r3 ; send out right channel msb ;write left channel data to sram out portd,r24 ; set address sts porth,r25 out portg,r22 ; pull ce low,we low,and set high bits of address ldi r17,$ff out ddra,r17 ; set porta as output for data write out ddrc,r17 ; set portc as output for data write out porta,r6 ; set data out portc,r7 sbi portg,portg2 ; pull we high to write out ddra,r18 ; set porta as input for data lines out ddrc,r18 ; set portc as input for data lines wait3_000033: ; check if byte has been sent in r17,spsr sbrs r17,spif rjmp wait3_000033 in r9,spdr ; recieve in right channel msb out spdr,r2 ; send out right channel lsb ;get left channel data from sram out portg,r23 ; pull ce low,we high,oe low, and set high bits of address out portd,r28 ; set address sts porth,r29 nop ; wait microcontroller setup time of 2 cycles nop in r2,pina ; get data in r3,pinc ; get data wait4_000033: ; check if byte has been sent in r17,spsr sbrs r17,spif rjmp wait4_000033 in r8,spdr ; recieve in left channel lsb ; get delay settings lds r17,adcsra ; get adc control register sbrs r17,adif ; check if adc conversion is complete rjmp shift_000033 ; skip adc sampling lds r16,adcl ; get low byte adc value lds r17,adch ; get high byte adc value add r19,r16 ; accumulate adc samples adc r10,r17 adc r11,r18 ; r18 is cleared above ldi r17,$f7 sts adcsra,r17 ; clear interrupt flag dec r14 ; countdown adc sample clock brne shift_000033 ; get delay time if its been long enough ldi r17,$01 ; check if adc value is less than $000100 cp r19,r18 ; r18 is cleared above cpc r10,r17 cpc r11,r18 brsh deadband_000033 ; check if adc value changed enough to update delay inc r10 ; set minimum delay to $000100 = 6ms clr r19 deadband_000033: ; check for change in adc value movw r17:r16,r11:r10 ; move adc sample to temporary register mov r20,r19 sub r20,r0 ; find difference between adc sample and desired delay time sbc r16,r26 sbc r17,r27 brsh check_000033 ; check for deadband if positive com r20 ; invert if negative com r16 ; using ones complement as it is faster, and only has 1 bit error com r17 check_000033: ; check if difference is greater than deadband cpi r16,$01 ; check if difference is less than 1 lsb cpc r17,r18 ; r18 cleared above brlo empty_000033 ; do nothing if less than 1 lsb movw r27:r26,r11:r10 ; move adc sample to delay time if large enough change andi r19,$fe ; make sure delay time is even mov r0,r19 empty_000033: ; empty accumulation registers and finish off clr r10 ; empty accumulation registers clr r11 clr r19 shift_000033: ; check if delay time is correct cp r0,r21 ; compare desired delay to actual delay cpc r26,r12 cpc r27,r13 breq switchsample_000033 ; do nothing if the same brlo indexdown_000033 ldi r17,$02 ; increment delay register add r21,r17 ; this doubles playback speed until desired delay is reached adc r12,r18 ; r18 is cleared above adc r13,r18 ldi r17,$03 and r13,r17 ; mask off unused bits rjmp switchsample_000033 indexdown_000033: subi r21,$01 ; decrement delay register sbc r12,r18 ; r18 is cleared above sbc r13,r18 ; this plays backwards until desired delay is reached ldi r17,$03 and r13,r17 ; mask off unused bits switchsample_000033: ; check state of rotary switch dec r15 brne done_000033 lds r16,pinj ; get switch data andi r16,$78 ; mask off rotary switch lsr r16 ; adjust switch position to program memory location lsr r16 ldi r17,$02 add r16,r17 cp r16,r31 ; check if location has changed breq done_000033 ; finish off if no change clr r30 ; reset jump register to new function if it changed mov r31,r16 done_000033: movw r29:r28,r25:r24 ; move write address to read destination register mov r23,r22 ; move write third byte to read third byte sub r28,r21 ; subtract delay time sbc r29,r12 sbc r23,r13 andi r23,$03 ; mask off unsed bits ori r23,$04 ; set we bit for reading reti }}}