MICrODEC: Stock Functions
3s Stereo Ping-Pong Delay
This program performs a ping-pong delay routine. It stores the incoming data from the codec, and plays back a delayed sample, but reverses the orientation of the channels on playback. In this way, the audio coming in on the left channel comes out on the right after a delay. With a bit of feedback, the signals chase each other from left to right. The input data is stereo, and is taken from the left and right channels. The output is also stereo, and is presented on both the left and right channels.
The pot (MOD1) controls the delay time. It goes from 3ms to 3s. 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.
1 ; program: ping_pong-18b-pot.asm
2 ; UID = 000030 - unique id to eliminate conflicts between variables
3 ; 18b address space (3s delay time)
4 ; stereo data - left and right swap to create ping-pong effect
5 ; pot (MOD1) controlled delay time (3ms - 3s)
6
7 ; program overview
8 ;
9 ; data is read in from memory and written out the codec at the same time
10 ; new data is written to the memory from the codec. ADC0 (MOD1) is read
11 ; and averaged over 256 samples to reduce jitter. this value is subtracted
12 ; from the write address to create the desired read address. if the actual
13 ; read address doesnt match the desired read address, it is either
14 ; incremented or decremented by one sample each sample period until it
15 ; matches. this reduces noise during delay time transitions. the left
16 ; channel input is recorded to the right channel output buffer (and vice
17 ; versa), so the sound bounces back and forth between the two channels.
18
19 ; register usage - may be redefined in other sections
20 ;
21 ; r0 deisred delay time fractional byte
22 ; r1
23 ; r2 left lsb out
24 ; r3 left msb out
25 ; r4 right lsb out
26 ; r5 right msb out
27 ; r6 left lsb in
28 ; r7 left msb in
29 ; r8 right lsb in
30 ; r9 right msb in
31 ; r10 adc accumulator lsb
32 ; r11 adc accumulator msb
33 ; r12 actual delay lsb
34 ; r13 actual delay msb
35 ; r14 adc sample counter
36 ; r15 switch sample counter
37 ; r16 temporary swap register
38 ; r17 temporary swap register
39 ; r18 null register
40 ; r19 adc accumulator fractional byte
41 ; r20 temporary swap register
42 ; r21 actual delay fractional byte
43 ; r22 write address third byte
44 ; r23 read address third byte
45 ; r24 write address lsb
46 ; r25 write address msb
47 ; r26 desired delay lsb
48 ; r27 desired delay msb
49 ; r28 read address lsb
50 ; r29 read address msb
51 ; r30 jump location for interrupt lsb
52 ; r31 jump location for interrupt msb
53 ; t
54
55 ;program starts here first time
56 ldi r30,$25 ; set jump location to program start
57 clr r24 ; clear write register
58 clr r25
59 ldi r22,$00 ; setup write address high byte
60 clr r18 ; setup r18 as null register for carry addition and ddr setting
61 ldi r17,$ff ; setup r17 for ddr setting
62
63 clear_000030: ; clear delay buffer
64 ; eliminates static when first switching to the delay setting
65
66 adiw r25:r24,$01 ; increment write register
67 adc r22,r18 ; increment write third byte
68 cpi r22,$04 ; check if full memory space has been cleared
69 breq cleardone_000030 ; continue until end of buffer reached
70 out portd,r24 ; set address
71 sts porth,r25
72 out portg,r22 ; pull ce low,we low,and set high bits of address
73 out ddra,r17 ; set porta as output for data write
74 out ddrc,r17 ; set portc as output for data write
75 out porta,r18 ; set data
76 out portc,r18 ; r18 is cleared above
77 sbi portg,portg2 ; pull we high to write
78 out ddra,r18 ; set porta as input for data lines
79 out ddrc,r18 ; set portc as input for data lines
80 rjmp clear_000030 ; continue clearing
81
82 cleardone_000030: ; reset registers
83
84 clr r24 ; clear write register
85 clr r25
86 ldi r22,$00 ; setup write address high byte
87 clr r28 ; set read address to minimum delay
88 ldi r29,$ff
89 ldi r23,$07 ; setup read address high byte
90 clr r21 ; set actual delay time to minimum delay
91 ldi r16,$01
92 mov r12,r16
93 clr r13
94 clr r2 ; initialize data output registers
95 clr r3
96 clr r4
97 clr r5
98 reti ; finish with initialization and wait for next interrupt
99
100 ; program starts here every time but first
101 ; initiate data transfer to codec
102 sbi portb,portb0 ; toggle slave select pin
103 out spdr,r3 ; send out left channel msb
104 cbi portb,portb0
105
106 ;increment sram addresses
107 adiw r25:r24,$01 ; increment write address
108 adc r22,r18 ; increment write third byte
109 andi r22,$03 ; mask off unsed bits
110 adiw r29:r28,$01 ; increment read address
111 adc r23,r18 ; increment read third byte
112 andi r23,$03 ; mask off unsed bits
113 ori r23,$04 ; set we bit for reading
114
115 wait1_000030: ; check if byte has been sent
116
117 in r17,spsr
118 sbrs r17,spif
119 rjmp wait1_000030
120 in r7,spdr ; recieve in left channel msb
121 out spdr,r2 ; send out left channel lsb
122
123 ;get left channel data from sram
124 out portg,r23 ; pull ce low, we high, and set high bits of register
125 out portd,r28 ; set address
126 sts porth,r29
127 adiw r29:r28,$01 ; increment read address
128 adc r23,r18 ; placed here to use setup time efficiently
129 andi r23,$03 ; mask off unsed bits
130 ori r23,$04 ; set we bit for reading
131 in r2,pina ; get data
132 in r3,pinc ; get data
133
134 wait2_000030: ; check if byte has been sent
135
136 in r17,spsr
137 sbrs r17,spif
138 rjmp wait2_000030
139 in r6,spdr ; recieve in left channel lsb
140 out spdr,r5 ; send out right channel msb
141
142 ;write left channel data to sram
143 out portd,r24 ; set address
144 sts porth,r25
145 out portg,r22 ; pull ce low,we low,and set high bits of address
146 ldi r17,$ff
147 out ddra,r17 ; set porta as output for data write
148 out ddrc,r17 ; set portc as output for data write
149 out porta,r6 ; set data
150 out portc,r7
151 sbi portg,portg2 ; pull we high to write
152 out ddra,r18 ; set porta as input for data lines
153 out ddrc,r18 ; set portc as input for data lines
154
155 wait3_000030: ; check if byte has been sent
156
157 in r17,spsr
158 sbrs r17,spif
159 rjmp wait3_000030
160 in r9,spdr ; recieve in right channel msb
161 out spdr,r4 ; send out right channel lsb
162
163 ;get right channel data from sram
164 out portg,r23 ; pull ce low,we high,oe low, and set high bits of address
165 out portd,r28 ; set address
166 sts porth,r29
167 adiw r25:r24,$01 ; increment write address
168 adc r22,r18 ; placed here for efficient use of setup time
169 andi r22,$03 ; mask off unsed bits
170 in r4,pina ; get data
171 in r5,pinc ; get data
172
173 wait4_000030: ; check if byte has been sent
174
175 in r17,spsr
176 sbrs r17,spif
177 rjmp wait4_000030
178 in r8,spdr ; recieve in left channel lsb
179
180 ;write right channel data to sram
181 out portd,r24 ; set address
182 sts porth,r25
183 out portg,r22 ; pull ce low,we low,and set high bits of address
184 ldi r17,$ff
185 out ddra,r17 ; set porta as output for data write
186 out ddrc,r17 ; set portc as output for data write
187 out porta,r8 ; set data
188 out portc,r9
189 sbi portg,portg2 ; pull we high to write
190 out ddra,r18 ; set porta as input for data lines
191 out ddrc,r18 ; set portc as input for data lines
192
193 ; get delay settings
194 lds r17,adcsra ; get adc control register
195 sbrs r17,adif ; check if adc conversion is complete
196 rjmp shift_000030 ; skip adc sampling
197 lds r16,adcl ; get low byte adc value
198 lds r17,adch ; get high byte adc value
199 add r19,r16 ; accumulate adc samples
200 adc r10,r17
201 adc r11,r18 ; r18 is cleared above
202 ldi r17,$f7
203 sts adcsra,r17 ; clear interrupt flag
204 dec r14 ; countdown adc sample clock
205 brne shift_000030 ; get delay time if its been long enough
206 ldi r17,$01 ; check if adc value is less than $000100
207 cp r19,r18 ; r18 is cleared above
208 cpc r10,r17
209 cpc r11,r18
210 brsh deadband_000030 ; check if adc value changed enough to update delay
211 inc r10 ; set minimum delay to $000100 = 3ms
212 clr r19
213
214 deadband_000030: ; check for change in adc value
215
216 movw r17:r16,r11:r10 ; move adc sample to temporary register
217 mov r20,r19
218 sub r20,r0 ; find difference between adc sample and desired delay time
219 sbc r16,r26
220 sbc r17,r27
221 brsh check_000030 ; check for deadband if positive
222 com r20 ; invert if negative
223 com r16 ; using ones complement as it is faster, and only has 1 bit error
224 com r17
225
226 check_000030: ; check if difference is greater than deadband
227
228 cpi r16,$01 ; check if difference is less than 1 lsb
229 cpc r17,r18 ; r18 cleared above
230 brlo empty_000030 ; do nothing if less than 1 lsb
231 movw r27:r26,r11:r10 ; move adc sample to delay time if large enough change
232 andi r19,$fc ; make sure delay time is a multiple of 4
233 mov r0,r19
234
235 empty_000030: ; empty accumulation registers and finish off
236
237 clr r10 ; empty accumulation registers
238 clr r11
239 clr r19
240
241 shift_000030: ; check if delay time is correct
242
243 cp r0,r21 ; compare desired delay to actual delay
244 cpc r26,r12
245 cpc r27,r13
246 breq switchsample_000030 ; do nothing if the same
247 brlo indexdown_000030
248 ldi r17,$04 ; increment delay register
249 add r21,r17
250 adc r12,r18 ; r18 is cleared above
251 adc r13,r18
252 ldi r17,$03
253 and r13,r17 ; mask off unused bits
254 rjmp switchsample_000030
255
256 indexdown_000030:
257
258 subi r21,$02 ; decrement delay register
259 sbc r12,r18 ; r18 is cleared above
260 sbc r13,r18
261 ldi r17,$03
262 and r13,r17 ; mask off unused bits
263
264 switchsample_000030: ; check state of rotary switch
265
266 dec r15
267 brne done_000030
268 lds r16,pinj ; get switch data
269 andi r16,$78 ; mask off rotary switch
270 lsr r16 ; adjust switch position to program memory location
271 lsr r16
272 ldi r17,$02
273 add r16,r17 ; move to jump register
274 cp r16,r31 ; check if location has changed
275 breq done_000030 ; finish off if no change
276 clr r30 ; reset jump register to intial state
277 mov r31,r16
278
279 done_000030:
280
281 movw r29:r28,r25:r24 ; move write address to read destination register
282 mov r23,r22 ; move write third byte to read third byte
283 sub r28,r21 ; subtract delay time
284 sbc r29,r12
285 sbc r23,r13
286 andi r23,$03 ; mask off unsed bits
287 ori r23,$04 ; set we bit for reading
288 movw r17:r16,r3:r2 ; swap left and right channels
289 movw r3:r2,r5:r4
290 movw r5:r4,r17:r16
291 reti
292