Attachment 'fullwave_delay_lowpass.asm'
Download 1 ; program: fullwave-delay-lowpass.asm
2 ; UID = 000055 - unique id to eliminate conflicts between variables
3 ; mono data (left in only, delayed output on left, direct out on right)
4 ; pot (MOD1) controls the delay time (0s - 1.5s)
5 ; rotary encoder (MOD2) controls the cutoff frequency
6
7 ; program overview
8 ;
9 ; data is read in from the codec, and negative values are inverted to
10 ; positive values. all values are then shifted down to mid-rail and
11 ; multiplied by 2 to normalize the output. these values are then written
12 ; to memory, and read back out and accumulated. this creates a simple
13 ; moving average low pass filter. the delay time (and corresponding cutoff
14 ; frequency) is set with the rotary encoder (MOD2). there are 7 different
15 ; cutoff frequencies, with rotations to the right increasing the cutoff
16 ; frequnecy. changing the cutoff frequency restarts the program to blank
17 ; the accumulation buffer. the delay time is set with the pot (MOD1). the
18 ; adc is oversampled 256 times and deadbanded to reduce jitter. turning the
19 ; pot all the way to the right overlaps the delay and lowpass buffers,
20 ; causing some distortions.
21
22 ; register usage - may be redefined in other sections
23 ;
24 ; r0 adc accumulation fractional byte
25 ; r1 adc accumulation lsb
26 ; r2 adc accumulation msb
27 ; r3
28 ; r4 left lsb out
29 ; r5 left msb out
30 ; r6 left lsb in
31 ; r7 left msb in
32 ; r8 right output lsb
33 ; r9 right output msb
34 ; r10 accumulation lsb
35 ; r11 accumulation mlb
36 ; r12 accumulation mhb
37 ; r13 accumulation msb
38 ; r14 rotary encoder counter
39 ; r15 adc/switch sample counter
40 ; r16 temporary swap register
41 ; r17 temporary swap register
42 ; r18 null register
43 ; r19 cutoff frequency
44 ; r20 temporary register
45 ; r21
46 ; r22 actual delay lsb
47 ; r23 actual delay msb
48 ; r24 write address lsb
49 ; r25 write address msb
50 ; r26 desired delay lsb
51 ; r27 desired delay msb
52 ; r28 read address lsb
53 ; r29 read address msb
54 ; r30 jump location for interrupt lsb
55 ; r31 jump location for interrupt msb
56 ; t rotary encoder state bit
57
58 ;program starts here first time and after buffer changes
59 ldi r30,$1c ; set jump location to program start
60 ldi r16,$08 ; set lowpass buffer size to mid range
61 ldi r19,$03 ; initialize cutoff frequency to midrange
62
63 restart_000055: ; restart location for clearing memory
64
65 clr r24 ; clear write register
66 clr r25
67 clr r18 ; setup r18 as null register for carry addition and ddr setting
68 ldi r17,$ff ; setup r17 for ddr setting
69
70 clear_000055: ; clear lowpass buffer
71 ; required to ensure an accurate accumulation
72 out portd,r24 ; set address
73 sts porth,r25
74 out portg,r18 ; pull ce low,we low,and set high bits of address
75 out ddra,r17 ; set porta as output for data write
76 out ddrc,r17 ; set portc as output for data write
77 out porta,r18 ; set data
78 out portc,r18 ; r18 is cleared above
79 sbi portg,portg2 ; pull we high to write
80 out ddra,r18 ; set porta as input for data lines
81 out ddrc,r18 ; set portc as input for data lines
82 inc r24 ; increment write register - only clears first 256 bytes
83 brne clear_000055 ; continue until end of buffer reached
84
85 cleardone_000055: ; reset registers
86
87 mov r24,r16 ; set buffer size for lowpass
88 clr r28 ; set read address
89 clr r29
90 clr r10 ; initialize accumulation registers
91 clr r11
92 clr r12
93 clr r13
94 reti ; finish with initialization and wait for next interrupt
95
96 ; program starts here every time but first
97 ; initiate data transfer to codec
98 sbi portb,portb0 ; toggle slave select pin
99 out spdr,r5 ; send out left channel msb
100 cbi portb,portb0
101
102 ;increment sram addreses
103 adiw r25:r24,$01 ; increment write address
104 adiw r29:r28,$01 ; increment read address
105
106 wait1_000055: ; check if byte has been sent
107
108 in r17,spsr
109 sbrs r17,spif
110 rjmp wait1_000055
111 in r7,spdr ; recieve in left channel msb
112 out spdr,r4 ; send out left channel lsb
113
114 wait2_000055: ; check if byte has been sent
115
116 in r17,spsr
117 sbrs r17,spif
118 rjmp wait2_000055
119 in r6,spdr ; recieve in left channel lsb
120 out spdr,r9 ; send out right channel msb
121
122 ;fullwave rectify left data
123 sbrs r7,$07 ; check if negative
124 rjmp normalize_000055
125 com r6 ; invert data if negative (using ones complement to avoid problem at $8000)
126 com r7
127
128 normalize_000055: ; normalize data since its all positive values now
129
130 lsl r6 ; multiply data by two
131 rol r7 ; data is unsigned integer at this point
132 ldi r16,$80 ; convert to signed integer
133 add r7,r16
134
135 wait3_000055: ; check if byte has been sent
136
137 in r17,spsr
138 sbrs r17,spif
139 rjmp wait3_000055
140 in r17,spdr ; recieve in right channel msb
141 out spdr,r8 ; send out right channel lsb
142
143 ;write rectified left channel data to sram
144 out portd,r24 ; set address
145 sts porth,r25
146 out portg,r18 ; pull ce low,we low,and set high bits of address
147 ldi r17,$ff
148 out ddra,r17 ; set porta as output for data write
149 out ddrc,r17 ; set portc as output for data write
150 out porta,r6 ; set data
151 out portc,r7
152 sbi portg,portg2 ; pull we high to write
153 out ddra,r18 ; set porta as input for data lines
154 out ddrc,r18 ; set portc as input for data lines
155
156 wait4_000055: ; check if byte has been sent
157
158 in r17,spsr
159 sbrs r17,spif
160 rjmp wait4_000055
161 in r17,spdr ; recieve in left channel lsb
162
163 ;get left channel data from sram
164 out portd,r28 ; set address
165 sts porth,r29
166 nop ; wait input latch time of 2 clock cycles
167 nop
168 in r4,pina ; get data
169 in r5,pinc ; get data
170
171 ;accumulate samples for lowpass
172 add r10,r6 ; add in current sample
173 adc r11,r7
174 sbrc r7,$07 ; check if data is negative
175 ldi r18,$ff ; set high bits if it is
176 adc r12,r18 ; r18 is cleared above
177 adc r13,r18
178 clr r18 ; reset null register
179 sub r10,r4 ; remove last sample in buffer
180 sbc r11,r5
181 sbrc r5,$07 ; check if data is negative
182 ldi r18,$ff ; set high bits if it is
183 sbc r12,r18 ; r18 is cleared above
184 sbc r13,r18
185 clr r18 ; reset null register
186
187 mov r4,r10 ; divide by 256 and move to ouptput register
188 mov r5,r11
189 mov r17,r12
190 tst r19 ; check if no dividing necessary
191 breq store_000055 ; keep dividing till the right size
192 mov r16,r19 ; move cutoff to temporary register
193
194 divide_000055: ; divide accumulation for proper scaling
195
196 asr r17 ; divide accumulation
197 ror r5
198 ror r4
199 dec r16 ; check if done
200 brne divide_000055 ; keep dividing till the right size
201
202 store_000055: ; store lowpassed data to memory
203
204 movw r9:r8,r5:r4 ; move immediate data to right output
205 movw r17:r16,r25:r24 ; move write address to temporary register
206 subi r17,$01 ; move to delay buffer location
207 out portd,r16 ; set address
208 sts porth,r17
209 out portg,r18 ; pull ce low,we low,and set high bits of address
210 ldi r20,$ff
211 out ddra,r20 ; set porta as output for data write
212 out ddrc,r20 ; set portc as output for data write
213 out porta,r4 ; set data
214 out portc,r5
215 sbi portg,portg2 ; pull we high to write
216 out ddra,r18 ; set porta as input for data lines
217 out ddrc,r18 ; set portc as input for data lines
218
219 ;fetch delayed data from memory
220 sub r16,r22 ; subtract delay time
221 sbc r17,r23
222 out portd,r16 ; set address
223 sts porth,r17
224 nop ; wait input latch time of 2 clock cycles
225 nop
226 in r4,pina ; get data
227 in r5,pinc ; put delayed data to left output
228
229 rotary_000055: ; check rotary encoder and adjust cutoff frequency
230 ; although rotary encoder is externally debounced, it is done here again.
231 ; pin1 is sampled on a transition from high to low on pin0. if pin1 is
232 ; high, a left turn occured, if pin1 is low, a right turn occured.
233 dec r14 ; check if time to sample rotary encoder
234 brne shift_000055 ; continue if not
235 ldi r17,$40 ; adjust sample frequency to catch all rising edges (1.5ms)
236 mov r14,r17
237 lds r17,pinj ; get switch data
238 sbrs r17,$00 ; check if pin0 is low
239 rjmp edge_000055 ; check if pin0 was low on previous sample
240 clt ; clear state register if back high
241 rjmp shift_000055 ; finish off
242
243 edge_000055: ; check for falling edge
244
245 brts shift_000055 ; do nothing if edge was already detected
246 set ; set t register to indicate edge detected
247 sbrs r17,$01 ; check if pin1 is high
248 rjmp increment_000055 ; increase cutoff frequency if right rotation
249 cpi r19,$06 ; else check if at max value
250 brsh shift_000055 ; finish off if at max
251 inc r19 ; incrementing cutoff value decreases cutoff frequency
252 rjmp buffer_000055 ; reset accumulation buffer
253
254 increment_000055: ; increase cutoff frequency
255
256 cpi r19,$01 ; check if cutoff at min value
257 brlo shift_000055 ; finish off if at min
258 dec r19 ; decrementing cutoff value increases cutoff frequency
259
260 buffer_000055: ; adjust buffer size
261
262 movw r29:r28,r25:r24 ; move write address to read address
263 ldi r16,$01 ; initialize the offset register
264 tst r19 ; check if any shifting is required
265 breq bufferload_000055
266 mov r17,r19 ; move cutoff to temporary register
267
268 shift1_000055: ; shift in zeros to make correct buffer size
269
270 lsl r16 ; increment buffer size
271 dec r17
272 brne shift1_000055 ; keep shifting until done
273
274 bufferload_000055: ; load buffer size
275
276 rjmp restart_000055 ; clear accumulation buffer
277
278 shift_000055: ; check if delay time is correct
279
280 cp r26,r22 ; compare desired delay to actual delay
281 cpc r27,r23
282 breq adcsample_000055 ; do nothing if the same
283 brlo indexdown_000055
284 ldi r17,$02 ; increment delay register
285 add r22,r17
286 adc r23,r18 ; r18 is cleared above
287 rjmp adcsample_000055
288
289 indexdown_000055:
290
291 ldi r17,$01 ; decrement delay register
292 sub r22,r17
293 sbc r23,r18 ; r18 is cleared above
294
295 adcsample_000055: ; get delay settings
296
297 lds r17,adcsra ; get adc control register
298 sbrs r17,adif ; check if adc conversion is complete
299 rjmp done_000055 ; skip adc sampling
300 lds r16,adcl ; get low byte adc value
301 lds r17,adch ; get high byte adc value
302 add r0,r16 ; accumulate adc samples
303 adc r1,r17
304 adc r2,r18 ; r18 is cleared above
305 ldi r17,$f7
306 sts adcsra,r17 ; clear interrupt flag
307 dec r15 ; countdown adc sample clock
308 brne done_000055 ; get delay time if its been long enough
309 lsr r2 ; divide adc sample by 4 to make 16b value
310 ror r1
311 ror r0
312 lsr r2
313 ror r1
314 ror r0
315
316 deadband_000055: ; check if adc has changed enough to warrant update
317
318 movw r17:r16,r1:r0 ; move adc sample to temporary register
319 sub r16,r26 ; find difference between adc sample and desired delay time
320 sbc r17,r27
321 brsh check_000055 ; check for deadband if positive
322 neg r16 ; invert if negative
323 adc r17,r18 ; r18 is cleared above
324 neg r17
325
326 check_000055: ; check if difference is greater than deadband
327
328 cpi r16,$40 ; check if difference is less than 1 lsb
329 cpc r17,r18 ; r18 cleared above
330 brlo empty_000055 ; do nothing if less than 1 lsb
331 movw r27:r26,r1:r0 ; move adc sample to delay time if large enough change
332 andi r26,$fe ; make sure delay time is a multiple of 2
333
334 empty_000055: ; empty accumulation registers and finish off
335
336 clr r0 ; empty adc accumulation registers
337 clr r1
338 clr r2
339
340 switchsample_000055: ; check rotary switch
341
342 lds r16,pinj ; get switch data
343 andi r16,$78 ; mask off rotary switch
344 lsr r16 ; adjust switch position to program memory location
345 lsr r16
346 ldi r17,$02
347 add r16,r17
348 cp r16,r31 ; check if location has changed
349 breq done_000055 ; finish off if no change
350 clr r30 ; reset jump register to new location
351 mov r31,r16
352
353 done_000055: ; normalize data and move to read buffer
354
355 reti ; return to waiting
356
Attached Files
To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.You are not allowed to attach a file to this page.