==== 3s Stereo Ping-Pong Delay ==== [[attachment:delay_ping_pong.asm|delay_ping_pong.asm]] ---- {{{#!highlight nasm ; program: ping_pong-18b-pot.asm ; UID = 000030 - unique id to eliminate conflicts between variables ; 18b address space (3s delay time) ; stereo data - left and right swap to create ping-pong effect ; pot (MOD1) controlled delay time (3ms - 3s) ; 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. the left ; channel input is recorded to the right channel output buffer (and vice ; versa), so the sound bounces back and forth between the two channels. ; register usage - may be redefined in other sections ; ; r0 deisred delay time fractional byte ; 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 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,$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_000030: ; 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_000030 ; 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_000030 ; continue clearing cleardone_000030: ; 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 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 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_000030: ; check if byte has been sent in r17,spsr sbrs r17,spif rjmp wait1_000030 in r7,spdr ; recieve in left channel msb out spdr,r2 ; send out left channel lsb ;get left channel data from sram out portg,r23 ; pull ce low, we high, and set high bits of register out portd,r28 ; set address sts porth,r29 adiw r29:r28,$01 ; increment read address adc r23,r18 ; placed here to use setup time efficiently andi r23,$03 ; mask off unsed bits ori r23,$04 ; set we bit for reading in r2,pina ; get data in r3,pinc ; get data wait2_000030: ; check if byte has been sent in r17,spsr sbrs r17,spif rjmp wait2_000030 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,r18 ; set porta as input for data lines out ddrc,r18 ; set portc as input for data lines wait3_000030: ; check if byte has been sent in r17,spsr sbrs r17,spif rjmp wait3_000030 in r9,spdr ; recieve in right channel msb out spdr,r4 ; send out right channel lsb ;get right 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 adiw r25:r24,$01 ; increment write address adc r22,r18 ; placed here for efficient use of setup time andi r22,$03 ; mask off unsed bits in r4,pina ; get data in r5,pinc ; get data wait4_000030: ; check if byte has been sent in r17,spsr sbrs r17,spif rjmp wait4_000030 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,r18 ; set porta as input for data lines out ddrc,r18 ; 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_000030 ; 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_000030 ; 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_000030 ; check if adc value changed enough to update delay inc r10 ; set minimum delay to $000100 = 3ms clr r19 deadband_000030: ; 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_000030 ; 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_000030: ; 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_000030 ; do nothing if less than 1 lsb movw r27:r26,r11:r10 ; move adc sample to delay time if large enough change andi r19,$fc ; make sure delay time is a multiple of 4 mov r0,r19 empty_000030: ; empty accumulation registers and finish off clr r10 ; empty accumulation registers clr r11 clr r19 shift_000030: ; check if delay time is correct cp r0,r21 ; compare desired delay to actual delay cpc r26,r12 cpc r27,r13 breq switchsample_000030 ; do nothing if the same brlo indexdown_000030 ldi r17,$04 ; increment delay register add r21,r17 adc r12,r18 ; r18 is cleared above adc r13,r18 ldi r17,$03 and r13,r17 ; mask off unused bits rjmp switchsample_000030 indexdown_000030: subi r21,$02 ; decrement delay register sbc r12,r18 ; r18 is cleared above sbc r13,r18 ldi r17,$03 and r13,r17 ; mask off unused bits switchsample_000030: ; check state of rotary switch dec r15 brne done_000030 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 ; move to jump register cp r16,r31 ; check if location has changed breq done_000030 ; finish off if no change clr r30 ; reset jump register to intial state mov r31,r16 done_000030: 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 movw r17:r16,r3:r2 ; swap left and right channels movw r3:r2,r5:r4 movw r5:r4,r17:r16 reti }}}