=== MICrODEC: Stock Functions === ==== 700ms Stereo 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 stereo, and is taken from both the left and right channels. The output is also stereo, and is presented on both the left and right channels. This program only uses the first 16b of the SRAM address space. This reduces the total possible delay time, but also makes the math and data fetches much, much easier. Unless the full 6s (3s stereo) sample time is required, its best to only use the 16b address space. The pot (MOD1) controls the delay time. It goes from 3ms to 700ms. 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_700ms_stereo.asm|delay_700ms_stereo.asm]] ---- {{{#!highlight nasm ; program: delay-16b-pot.asm ; UID = 000003 - unique id to eliminate conflicts between variables ; 16b address space (.7s delay time) ; stereo data ; pot (MOD1) controlled delay time (3ms - 700ms) ; 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 ; r1 ; r2 left lsb out ; r3 left msb out ; r4 right lsb out ; r5 right msb out ; r6 left lsb in ; r7 left msb in ; r8 right lsb in ; r9 right msb in ; r10 adc accumulator fractional byte ; r11 adc accumulator lsb ; 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 ; r19 adc accumulator msb ; r20 ; r21 ; r22 null register ; 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,$25 ; 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_000003: ; 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,$01 ; check if 16b memory space has been cleared breq cleardone_000003 ; 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_000003 ; continue clearing cleardone_000003: ; reset registers clr r24 ; clear write register clr r25 ldi r22,$00 ; setup null register clr r28 ; set read address to minimum delay ldi r29,$ff ldi r23,$04 ; setup read address high byte clr r21 ; set actual delay time to minimum delay ldi r16,$01 mov r13,r16 clr r12 clr r2 ; initialize data output registers clr r3 clr r4 clr r5 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 address adiw r25:r24,$01 ; increment write address adiw r29:r28,$01 ; increment read address wait1_000003: ; check if byte has been sent in r17,spsr sbrs r17,spif rjmp wait1_000003 in r7,spdr ; recieve in left channel msb out spdr,r2 ; send out left channel lsb ;get left channel data from sram out portd,r28 ; set address sts porth,r29 nop ; wait input latch time of 2 clock cycles nop ; wait input latch time of 2 clock cycles in r2,pina ; get data in r3,pinc ; get data adiw r29:r28,$01 ; increment read address wait2_000003: ; check if byte has been sent in r17,spsr sbrs r17,spif rjmp wait2_000003 in r6,spdr ; recieve in left channel lsb out spdr,r5 ; 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,r22 ; set porta as input for data lines out ddrc,r22 ; set portc as input for data lines wait3_000003: ; check if byte has been sent in r17,spsr sbrs r17,spif rjmp wait3_000003 in r9,spdr ; recieve in right channel msb out spdr,r4 ; send out right channel lsb ;get right channel data from sram out portd,r28 ; set address sts porth,r29 nop ; wait input latch time of 2 clock cycles nop ; wait input latch time of 2 clock cycles in r4,pina ; get data in r5,pinc ; get data adiw r25:r24,$01 ; increment write address wait4_000003: ; check if byte has been sent in r17,spsr sbrs r17,spif rjmp wait4_000003 in r8,spdr ; recieve in left channel lsb ;write right 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,r8 ; set data out portc,r9 sbi portg,portg2 ; pull we high to write out ddra,r22 ; set porta as input for data lines out ddrc,r22 ; set portc as input for data lines ;get delay settings lds r17,adcsra ; get adc control register sbrs r17,adif ; check if adc conversion is complete rjmp shift_000003 ; skip adc sampling lds r16,adcl ; get low byte adc value lds r17,adch ; get high byte adc value add r10,r16 ; accumulate adc samples adc r11,r17 adc r19,r22 ; r22 is cleared above ldi r17,$f7 sts adcsra,r17 ; clear interrupt flag dec r14 ; countdown adc sample clock brne shift_000003 ; get delay time if its been long enough lsr r19 ; divide adc sample by 4 ror r11 ; makes adc value a 16b number ror r10 lsr r19 ror r11 ror r10 ldi r16,$01 ; check if delay is below minimum cp r10,r22 ; r22 is cleared above cpc r11,r16 brsh deadband_000003 ; check if adc value changed enough to update delay clr r10 ; set minimum delay to $0100 = 3ms mov r11,r16 deadband_000003: ; set the low value of the delay movw r17:r16,r11:r10 ; move adc sample to temporary register sbc r16,r26 ; find difference between adc sample and desired delay time sbc r17,r27 brsh check_000003 ; check for deadband if positive neg r16 ; invert if negative adc r17,r22 ; r22 is cleared above neg r17 ; converts ones complement to twos complement check_000003: ; check if difference is greater than deadband cpi r16,$40 ; check if difference is less than 1 lsb of adc cpc r17,r22 ; r22 cleared above brlo empty_000003 ; do nothing if less than 1 lsb movw r27:r26,r11:r10 ; move adc sample to delay time if large enough change andi r26,$fc ; make sure delay time is a multiple of 4 empty_000003: ; empty accumulation registers and finish off clr r10 ; empty accumulation registers clr r11 clr r19 shift_000003: ; check if delay time is correct cp r26,r12 ; compare desired delay to actual delay cpc r27,r13 breq switchsample_000003 ; do nothing if the same brlo indexdown_000003 ldi r17,$04 ; increment delay register (4 is used for stereo data) add r12,r17 ; this makes it play forward at double speed adc r13,r22 ; until desired delay is reached rjmp switchsample_000003 indexdown_000003: ldi r17,$02 ; decrement delay register (2 is used for stereo data) sub r12,r17 ; this makes is play backwards until desired delay is reached sbc r13,r22 ; r22 is cleared above switchsample_000003: ; check state of rotary switch dec r15 ; countdown switch counter brne done_000003 ; finish off if not ready yet 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_000003 ; finish off if no change clr r30 ; reset jump register to new function if changed mov r31,r16 done_000003: movw r29:r28,r25:r24 ; move write address to read destination register sub r28,r12 ; remove delay time sbc r29,r13 reti ; return to waiting }}}