Attachment 'flanger_sine.asm'
Download 1 ; program: flanger-16b-sine.asm
2 ; UID = 000045 - unique id to eliminate conflicts between variables
3 ; 16b address space
4 ; mono data in on left channel, mono data out on both channels
5 ; pot (MOD1) controls lfo frequency
6 ; rotary encoder (MOD2) controls lfo depth and offset delay
7 ; pushbutton (on MOD2) controls wether depth or offset is being modded
8 ; offset delay variable from 90us to 23ms
9
10 ; program overview
11 ;
12 ; data is read in from the codec and stored to sram. data is read out of
13 ; sram at a variable delay set by an lfo and an offset. the lfo is
14 ; generated from a 16b/512s half sinewave lookup table. this is incremented
15 ; with a 24b number to get very low frequencies. the lfo rate is set via
16 ; the adc, which is oversampled 256 times and deadbanded to get rid of
17 ; glitches. the lfo depth is created by multiplying the lfo signal with
18 ; an 8b depth value, which is set via the rotary encoder. the offset delay
19 ; is an 8b value, which is also set with the rotary encoder, with the
20 ; pushbutton selecting which function is currently active.
21
22 ; register usage - may be redefined in other sections
23 ;
24 ; r0 multiply result lsb
25 ; r1 multiply result msb
26 ; r2 accumulation lsb
27 ; r3 accumulation mlb
28 ; r4 right/left lsb out/accumulation mhb
29 ; r5 right/left msb out/accumulation msb
30 ; r6 pushbutton state register
31 ; r7
32 ; r8 adc accumulator fractional byte
33 ; r9 adc accumulator lsb
34 ; r10 adc accumulator msb
35 ; r11 rotary encoder counter
36 ; r12 lfo rate lsb
37 ; r13 lfo rate msb
38 ; r14 null register
39 ; r15 switch sample counter
40 ; r16 temporary swap register
41 ; r17 temporary swap register
42 ; r18 sine wave buffer/multiply msb
43 ; r19 sine wave buffer/multiply msb
44 ; r20 multiply swap register
45 ; r21 multiply swap register
46 ; r22 sinetable lookup address lsb
47 ; r23 sinetable lookup address mlb
48 ; r24 write address lsb
49 ; r25 write address msb
50 ; r26 sinetable lookup address mhb
51 ; r27 sinetable lookup address msb
52 ; r28 delay offset
53 ; r29 lfo depth
54 ; r30 jump location for interrupt lsb
55 ; r31 jump location for interrupt msb
56 ; t rotary encoder edge detect indicator
57
58 ; program starts here first time
59 ; intialize registers
60 ldi r30,$05 ; increment z pointer to new jump location
61 clr r14 ; clear null register
62 ldi r28,$09 ; initialize delay offset
63 ldi r29,$06 ; intiialize lfo depth
64 reti ; finish with initialization and wait for next interrupt
65
66 ; program starts here every time but first
67 ; initiate data transfer to codec
68 sbi portb,portb0 ; toggle slave select pin
69 out spdr,r5 ; send out left channel msb
70 cbi portb,portb0
71
72 adiw r25:r24,$01 ; increment write address
73
74 wait1_000045: ; check if byte has been sent
75
76 in r17,spsr
77 sbrs r17,spif
78 rjmp wait1_000045
79 in r19,spdr ; recieve in left channel msb
80 out spdr,r4 ; send out left channel lsb
81
82 wait2_000045: ; check if byte has been sent
83
84 in r17,spsr
85 sbrs r17,spif
86 rjmp wait2_000045
87 in r18,spdr ; recieve in left channel lsb
88 out spdr,r5 ; send out right channel msb
89
90 ;write left channel data to sram
91 out portd,r24 ; set address
92 sts porth,r25
93 out portg,r14 ; pull ce low,we low,and set high bits of address
94 ldi r17,$ff
95 out ddra,r17 ; set porta as output for data write
96 out ddrc,r17 ; set portc as output for data write
97 out porta,r18 ; set data
98 out portc,r19
99 sbi portg,portg2 ; pull we high to write
100 out ddra,r14 ; set porta as input for data lines
101 out ddrc,r14 ; set portc as input for data lines
102
103 wait3_000045: ; check if byte has been sent
104
105 in r17,spsr
106 sbrs r17,spif
107 rjmp wait3_000045
108 in r17,spdr ; recieve in right channel msb
109 out spdr,r4 ; send out right channel lsb
110
111 wait4_000045: ; check if byte has been sent
112
113 in r17,spsr
114 sbrs r17,spif
115 rjmp wait4_000045
116 in r17,spdr ; recieve in right channel lsb
117
118 ; vco generation
119 movw r17:r16,r31:r30 ; store z register
120 ;get sample 1
121 add r22,r12 ; increment sinetable address
122 adc r23,r13
123 adc r26,r14 ; r14 is cleared above
124 adc r27,r14
125 movw r31:r30,r27:r26 ; move to z register for data fetch
126 lsl r30 ; adjust pointer for 16b fetch
127 rol r31
128 andi r31,$03 ; limit value to 10b (512 samples x 2 bytes)
129 ori r31,$48 ; set to memory address location where table is stored
130 lpm r18,z+ ; get sine value lsb, increment z register
131 lpm r19,z ; get sine value msb
132 sbrc r27,$01 ; flip sign for half of the values
133 rjmp interpolate_000045
134 neg r18
135 adc r19,r14 ; r14 is cleared above
136 neg r19
137
138 interpolate_000045: ; multiply sample 1 by distance
139
140 movw r21:r20,r23:r22 ; get distance from sample 1
141 com r20 ; invert distance
142 com r21
143 mulsu r19,r21 ; (signed)Ah * (unsigned)Bh - multiply high bytes
144 movw r5:r4,r1:r0 ; store high bytes result for later
145 mul r18,r20 ; (unsigned)Al * (unsigned)Bl ; multiply low bytes
146 movw r3:r2,r1:r0 ; store low byets for later
147 mulsu r19,r20 ; (signed)Ah * (unsigned)Bl - multiply middle bytes
148 sbc r5,r14 ; r14 is cleared above - subtract sign bit
149 add r3,r0 ; accumulate result
150 adc r4,r1
151 adc r5,r14 ; r14 is cleared above
152 mul r21,r18 ; (unsigned)Bh * (unsigned)Al - multiply middle bytes
153 add r3,r0 ; accumulate result
154 adc r4,r1
155 adc r5,r14 ; r14 is cleared above
156
157 ;get sample 2
158 adiw r27:r26,$01 ; set to next sample
159 movw r31:r30,r27:r26 ; move to z register for data fetch
160 lsl r30 ; adjust pointer for 16b fetch
161 rol r31
162 andi r31,$03 ; limit value to 10b (512 samples x 2 bytes)
163 ori r31,$48 ; set to memory address location where table is stored
164 lpm r18,z+ ; get sine value lsb, increment z register
165 lpm r19,z ; get sine value msb
166 sbrc r27,$01 ; flip sign for half of the values
167 rjmp interpolate1_000045
168 neg r18
169 adc r19,r14 ; r14 is cleared above
170 neg r19
171
172 interpolate1_000045: ; multiply sample 2 by distance
173
174 sbiw r27:r26,$01 ; reset address
175 movw r31:r30,r17:r16 ; restore z register
176 mulsu r19,r23 ; (signed)Ah * (unsigned)Bh - multiply high bytes
177 add r4,r0 ; accumulate result
178 adc r5,r1
179 mul r18,r22 ; (unsigned)Al * (unsigned)Bl ; multiply low bytes
180 add r2,r0 ; accumulate result
181 adc r3,r1
182 adc r4,r14 ; r14 is cleared above
183 adc r5,r14
184 mulsu r19,r22 ; (signed)Ah * (unsigned)Bl - multiply middle bytes
185 sbc r5,r14 ; r14 is cleared above - subtract sign bit
186 add r3,r0 ; accumulate result
187 adc r4,r1
188 adc r5,r14 ; r14 is cleared above
189 mul r23,r18 ; (unsigned)Bh * (unsigned)Al - multiply middle bytes
190 add r3,r0 ; accumulate result
191 adc r4,r1
192 adc r5,r14 ; r14 is cleared above
193
194 ;set lfo depth - 8b value
195 ldi r16,$80 ; convert lfo to unsigned number
196 add r5,r16
197 movw r19:r18,r5:r4 ; move lfo signal to multiply register
198 mov r21,r29 ; move lfo depth to multiply register
199 mul r19,r21 ; (unsigned)ah * (unsigned)b
200 movw r5:r4,r1:r0
201 mul r21,r18 ; (unsigned)b * (unsigned)al
202 add r4,r1
203 adc r5,r14 ; r14 is cleared above
204
205 ;add lfo to delay
206 movw r17:r16,r25:r24 ; move current location to read address
207 mov r20,r28 ; move delay offset to temporary register
208 clr r21 ; clear temporary high byte
209 lsl r20 ; multiply delay time by 4
210 rol r21
211 lsl r20
212 rol r21
213 sub r16,r20 ; remove delay offset
214 sbc r17,r21
215 sec ; set the carry bit so all values are reduced by 1 lsb
216 sbc r16,r5 ; remove lfo time
217 sbc r17,r14 ; r14 is cleared above
218
219 ;get left channel sample 1 from sram
220 out portd,r16 ; set address
221 sts porth,r17
222 nop ; wait setup period of two cycles
223 nop
224 in r18,pina ; get data
225 in r19,pinc ; get data
226
227 ;multiply sample 1 by distance
228 mov r20,r4 ; get distance from sample 1
229 mulsu r19,r20 ; (signed)ah * b
230 movw r5:r4,r1:r0
231 mul r18,r20 ; al * b
232 add r4,r1
233 adc r5,r14 ; r14 is cleared above
234 mov r3,r0
235
236 ;get left channel sample 2 from sram
237 subi r16,$ff ; set to next sample
238 sbci r17,$ff ; done this way because there is no addi or adci
239 out portd,r16 ; set address
240 sts porth,r17
241 nop ; wait setup period of two cycles
242 nop
243 in r18,pina ; get data
244 in r19,pinc ; get data
245
246 ;multiply sample 2 by distance
247 com r20 ; get distance to sample 2
248 mulsu r19,r20 ; (signed)ah * b
249 add r4,r0 ; accumulate result
250 adc r5,r1
251 mul r18,r20 ; al * b
252 add r3,r0 ; accumulate result
253 add r4,r1
254 adc r5,r14 ; r14 is cleared above
255
256 ;check rotary encoder and adjust lfo depth
257 ; although rotary encoder is externally debounced, it is done here again.
258 ; pin1 is sampled on a transition from high to low on pin0. if pin1 is
259 ; high, a left turn occured, if pin1 is low, a right turn occured.
260 dec r11 ; check if time to sample rotary encoder
261 brne adcsample_000045 ; continue if not
262 ldi r17,$40 ; adjust sample frequency to catch all rising edges (1.5ms)
263 mov r11,r17
264 lds r17,pinj ; get encoder/pushbutton data
265 mov r16,r17 ; store pushbutton data to temporary register
266 eor r16,r6 ; check if pushbutton changed value
267 and r16,r17 ; check if rising edge
268 sbrc r16,$02 ; continue if not
269 rjmp pushbutton_000045 ; go to pushbutton routine if pressed
270 ldi r20,$80 ; mask off the high bit of the pushbutton state register
271 and r6,r20 ; pinj7 should be 0, as there is no i/o attached to it
272 or r6,r17 ; store pushbutton data for next sample comparison
273 sbrs r17,$02 ; check if pushbutton pressed
274 rjmp adcsample_000045 ; do not allow any parameter change while button pressed
275 sbrs r17,$00 ; check if pin0 is low
276 rjmp edge_000045 ; check if pin0 was low on previous sample
277 clt ; clear state register if back high
278 rjmp adcsample_000045 ; finish off
279
280 edge_000045: ; check for falling edge
281
282 brts adcsample_000045 ; do nothing if edge was already detected
283 set ; set t register to indicate edge detected
284 ldi r21,$01 ; prepare for addition or subtraction
285 sbrs r6,$07 ; check which function is being modded
286 rjmp lfo_000045 ; do lfo function
287 sbrs r17,$01 ; check if pin1 is high
288 rjmp increment_000045 ; increment desired delay if right rotation
289 dec r28 ; decrement delay register else
290 cp r28,r21 ; r21 set to $01 above
291 brsh adcsample_000045 ; continue if not
292 mov r28,r16 ; set delay to min
293 rjmp adcsample_000045 ; finish off
294
295 increment_000045: ; increment desired delay register
296
297 add r28,r21 ; increment delay register
298 brcc adcsample_000045 ; check if overflow occured
299 ser r28 ; set delay to max
300
301 adcsample_000045: ; sample adc for lfo rate
302
303 lds r17,adcsra ; get adc control register
304 sbrs r17,adif ; check if adc conversion is complete
305 rjmp done_000045 ; skip adc sampling
306 lds r16,adcl ; get low byte adc value
307 lds r17,adch ; get high byte adc value
308 add r8,r16 ; accumulate adc samples
309 adc r9,r17
310 adc r10,r14 ; r14 is cleared above
311 ldi r17,$f7
312 sts adcsra,r17 ; clear interrupt flag
313 dec r15 ; countdown adc sample clock
314 brne done_000045 ; get delay time if its been long enough
315
316 deadband_000045: ; set the low value of the delay
317
318 lsr r10 ; divide adc value by 16
319 ror r9
320 ror r8
321 lsr r10
322 ror r9
323 ror r8
324 lsr r9 ; r10 is now empty
325 ror r8
326 lsr r9
327 ror r8
328 movw r17:r16,r9:r8 ; move adc sample to temporary register
329 ldi r21,$80 ; add in offset of min lfo rate ($0080)
330 add r16,r21
331 adc r17,r14 ; r14 is cleared above
332 sub r16,r12 ; find difference between adc sample and current lfo rate
333 sbc r17,r13
334 brsh check_000045 ; check for deadband if positive
335 neg r16 ; invert if negative
336 adc r17,r14 ; r14 is cleared above
337 neg r17
338
339 check_000045: ; check if difference is greater than deadband
340
341 cpi r16,$10 ; check if difference is less than 1 adc lsb
342 cpc r17,r14 ; r14 cleared above
343 brlo empty_000045 ; do nothing if less than 1 adc lsb
344 movw r13:r12,r9:r8 ; move adc sample to lfo rate register
345 add r12,r21 ; add in offset of min lfo rate ($0080)
346 adc r13,r14 ; r14 is cleared above
347
348 empty_000045: ; empty accumulation registers and finish off
349
350 clr r8 ; empty accumulation registers
351 clr r9
352 clr r10
353
354 switchsample_000045: ; check rotary switch
355
356 lds r16,pinj ; get switch data
357 andi r16,$78 ; mask off rotary switch
358 lsr r16 ; adjust switch position to program memory location
359 lsr r16
360 ldi r17,$02
361 add r16,r17
362 cpse r16,r31 ; check if location has changed
363 clr r30 ; reset jump register to intial state
364 mov r31,r16
365
366 done_000045:
367
368 reti ; return to waiting
369
370 lfo_000045: ; modify lfo depth parameter
371
372 sbrs r17,$01 ; check if pin1 is high
373 rjmp increment1_000045 ; increment desired delay if right rotation
374 sub r29,r21 ; decrement lfo depth register else
375 brcc adcsample_000045 ; check if underflow
376 clr r29 ; set depth to min
377 rjmp adcsample_000045 ; finish off
378
379 increment1_000045: ; increment desired delay register
380
381 add r29,r21 ; increment lfo depth register
382 brcc adcsample_000045 ; check if overflow occured
383 ser r29 ; set depth to max
384 rjmp adcsample_000045 ; finish off
385
386 pushbutton_000045: ; edge detect
387
388 ldi r20,$80 ; toggle msb to inidicate function change
389 eor r6,r20
390 and r6,r20 ; mask off the high bit
391 or r6,r17 ; store pushbutton data for next sample comparison
392 rjmp adcsample_000045 ; finish off
393
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.