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.
; 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
Last edited 2010-08-21 by guest · 4 revisions