=== MICrODEC: Stock Functions === ==== Up-Down Sweep ==== This function implements an up-down sweep. This means it plays forward at double speed until it hits the top of the buffer, and then plays in reverse until it hits the bottom. It takes in stereo data, and outputs stereo data. The pot (MOD1) varies the buffer size, within which the function reads back and forth. Since the function just bounces back and forth inside the buffer, there is no buffer boundary problems, although the change in direction at the buffer ends can add a slight high frequency element. The buffer size is settable between and 3ms and 1.5s. [[attachment:updown_sweep.asm|updown_sweep.asm]] ---- {{{#!highlight nasm ; program: up_downsweep-18b-pot.asm ; UID = 000035 - unique id to eliminate conflicts between variables ; 18b address space (3s sample time) ; stereo data ; pot (MOD1) controlled buffer size (3ms - 1.5s) ; program overview ; ; data is read in from memory and written out to the codec at the same time ; new data is written to the memory from the codec. the write address ; increments until it reaches the top of the buffer and then starts at ; zero. the read address increases at twice the rate until it reaches the ; write address, and then decreases until it reaches the write address. ; it continues this back and forth sample playback. 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 buffer size 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 accumulation lsb ; r11 adc accumulation msb ; r12 read address offset lsb ; r13 read address offset msb ; r14 adc/switch sample counter ; r15 ; r16 temporary swap register ; r17 temporary swap register ; r18 null register ; r19 adc accumulation fractional byte ; r20 temporary swap regiser ; r21 read address offset high byte ; r22 write address high byte ; r23 read address high byte ; r24 write address lsb ; r25 write address msb ; r26 desired buffer size lsb ; r27 desired buffer size msb ; r28 read address lsb ; r29 read address msb ; r30 jump location for interrupt lsb ; r31 jump location for interrupt msb ; t forward/reverse indicator ; program starts here first time ; initialze z pointer for correct jump ; this assumes a less than 256 word jump ldi r30,$29 ; 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_000035: ; 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_000035 ; 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_000035 ; continue clearing cleardone_000035: ; reset registers ldi r24,$00 ; initialize write register ldi r25,$00 ldi r22,$00 ; setup write address high byte ldi r28,$00 ; set read address to minimum delay ldi r29,$fe ldi r23,$07 ; setup read address high byte clr r0 ; set buffer size to minimum ldi r26,$01 clr r27 clr r12 ; set read address offset to minimum buffer size ldi r16,$01 mov r13,r16 clr r21 clr r2 ; initialize data output registers clr r3 clr r4 clr r5 clt ; clear t register to start with forward play reti ; finish with initialization and wait for next interrupt ; program starts here all but first time ; this is the point the z-pointer is incremented to ; initiate data transfer to codec sbi portb,portb0 ; toggle slave select pin out spdr,r3 ; send out left channel msb cbi portb,portb0 wait1_000035: ; check if byte has been sent in r17,spsr sbrs r17,spif rjmp wait1_000035 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 for efficient use of setup time andi r23,$03 ; mask off unsed bits ori r23,$04 ; set we/ bit in high byte register in r2,pina ; get data in r3,pinc ; get data wait2_000035: ; check if byte has been sent in r17,spsr sbrs r17,spif rjmp wait2_000035 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_000035: ; check if byte has been sent in r17,spsr sbrs r17,spif rjmp wait3_000035 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, and set high bits of register out portd,r28 ; set address sts porth,r29 adiw r25:r24,$01 ; increment write address adc r22,r18 ; placed here to use setup time efficiently andi r22,$03 ; mask off unsed bits in r4,pina ; get data in r5,pinc ; get data wait4_000035: ; check if byte has been sent in r17,spsr sbrs r17,spif rjmp wait4_000035 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 ;increment write address to next sample position adiw r25:r24,$01 ; increment write address adc r22,r18 ; increment write third byte andi r22,$03 ; mask off unsed bits movw r29:r28,r25:r24 ; move write address to read address mov r23,r22 ;calculate read offset brtc increment_000035 ; move read address forward if in foward mode ldi r16,$04 ; else move read address backwards by 2 add r12,r16 ; this requires incrementing the read offset by 4 adc r13,r18 ; r18 is cleared above adc r21,r18 cp r12,r0 ; check if at bottom of buffer cpc r13,r26 cpc r21,r27 brlo readadd_000035 ; load read address if not at bottom of buffer clt ; else clear t register to indicate forward motion after next sample ; decrement offset by 2 - this halts playback for one sample ; to reduce spikes at transitions increment_000035: ; move read address forward by 4 ; moving read address forward requires reducing the read offset by 2 ldi r16,$02 ; subtract 2 from read offset sub r12,r16 sbc r13,r18 ; r18 is cleared above sbc r21,r18 brne readadd_000035 ; load read address if not at top of buffer ldi r16,$04 ; set to playback last played sample mov r12,r16 ; reduces spikes at transitions set ; else set t register to start reversing on next sample readadd_000035: ; add in read offset sub r28,r12 ; subtract offset from read address sbc r29,r13 sbc r23,r21 andi r23,$03 ; mask off unused bits ori r23,$04 ; set we/ bit for reading adcsample_000035: ; get buffer size from adc lds r17,adcsra ; get adc control register sbrs r17,adif ; check if adc conversion is complete rjmp done_000035 ; skip adc sampling if not 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 done_000035 ; dont get buffer size till its been long enough lsr r11 ; divide adc sample by 2 ror r10 ; as the max buffer size is only $020000 ror r19 ; as the buffer is read backwards half of the time ldi r17,$01 ; check if adc value is less than $000100 cp r10,r17 cpc r11,r18 ; r18 cleared above brsh deadband_000035 ; check if adc value changed enough to update delay mov r10,r17 ; set minimum delay to $000100 = 3ms clr r19 deadband_000035: ; 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 buffer size sbc r16,r26 sbc r17,r27 brsh check_000035 ; 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_000035: ; check if difference is greater than deadband cpi r20,$80 ; check if difference is less than 1 lsb cpc r16,r18 ; r18 cleared above cpc r17,r18 brlo empty_000035 ; 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 buffer size is a multiple of 4 mov r0,r19 empty_000035: ; empty accumulation registers and finish off clr r10 ; empty accumulation registers clr r11 clr r19 switchsample_000035: ; check if at same function 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 cpse r16,r31 ; check if location has changed clr r30 ; reset jump register to intial state mov r31,r16 done_000035: reti ; return to waiting }}}