← Revision 1 as of 2010-08-13 08:24:06 →
Size: 19
Comment:
|
Size: 14502
Comment:
|
Deletions are marked like this. | Additions are marked like this. |
Line 2: | Line 2: |
This function implements a flanger. It is mono, taking in data from the left channel, and presenting the mono output on both the left and right channels. Flanging is based on analog tape and reel recording techniques, where slight pressure is applied to the flange of the reel, slowing down the playback. This creates a time-varying delayed signal, which, when mixed with the original signal, has a swept filter effect. Any time-varying signal can be used, but in this case, we are using a sinusoidal signal, as it introduces the least amount of added frequency content (ramp waves are also common). This sinusoid is created by taking values from a look-up table stored in program memory. The frequency of the sinusoid is set by the pot (MOD1), and is variable from .084Hz to 10.84Hz. Both the depth of the low frequency oscillator (LFO), and the offset delay time can be modified to change the quality of the sound. Unfortunately, there is only one other knob on the MICrODEC (although you could add more), so we had to cram them both onto that knob. By pressing the pushbutton on MOD2, the functionality of the rotary encoder on MOD2 changes. In one state, it modifies the LFO depth, with rotations to the right increasing its amplitude (from 0ms to 6ms). In the other state, it modifies the offset delay, with rotations to the right increasing the delay time (from 0ms to 24ms). In both cases, the rotary encoder sets an 8b value, so it takes quite a few turns to go from one end to the other, although this also gives finer resolution. Try the flanger with large LFO depth and some feedback, and you'll get what we've started calling "drunken sailor mode". It will make your guitar strings sound like rubber bands. [[attachment:flanger_sine.asm|flanger_sine.asm]] ---- {{{#!highlight nasm ; program: flanger-16b-sine.asm ; UID = 000045 - unique id to eliminate conflicts between variables ; 16b address space ; mono data in on left channel, mono data out on both channels ; pot (MOD1) controls lfo frequency ; rotary encoder (MOD2) controls lfo depth and offset delay ; pushbutton (on MOD2) controls wether depth or offset is being modded ; offset delay variable from 90us to 23ms ; program overview ; ; data is read in from the codec and stored to sram. data is read out of ; sram at a variable delay set by an lfo and an offset. the lfo is ; generated from a 16b/512s half sinewave lookup table. this is incremented ; with a 24b number to get very low frequencies. the lfo rate is set via ; the adc, which is oversampled 256 times and deadbanded to get rid of ; glitches. the lfo depth is created by multiplying the lfo signal with ; an 8b depth value, which is set via the rotary encoder. the offset delay ; is an 8b value, which is also set with the rotary encoder, with the ; pushbutton selecting which function is currently active. ; register usage - may be redefined in other sections ; ; r0 multiply result lsb ; r1 multiply result msb ; r2 accumulation lsb ; r3 accumulation mlb ; r4 right/left lsb out/accumulation mhb ; r5 right/left msb out/accumulation msb ; r6 pushbutton state register ; r7 ; r8 adc accumulator fractional byte ; r9 adc accumulator lsb ; r10 adc accumulator msb ; r11 rotary encoder counter ; r12 lfo rate lsb ; r13 lfo rate msb ; r14 null register ; r15 switch sample counter ; r16 temporary swap register ; r17 temporary swap register ; r18 sine wave buffer/multiply msb ; r19 sine wave buffer/multiply msb ; r20 multiply swap register ; r21 multiply swap register ; r22 sinetable lookup address lsb ; r23 sinetable lookup address mlb ; r24 write address lsb ; r25 write address msb ; r26 sinetable lookup address mhb ; r27 sinetable lookup address msb ; r28 delay offset ; r29 lfo depth ; r30 jump location for interrupt lsb ; r31 jump location for interrupt msb ; t rotary encoder edge detect indicator ; program starts here first time ; intialize registers ldi r30,$05 ; increment z pointer to new jump location clr r14 ; clear null register ldi r28,$09 ; initialize delay offset ldi r29,$06 ; intiialize lfo depth reti ; finish with initialization and wait for next interrupt ; program starts here every time but first ; initiate data transfer to codec sbi portb,portb0 ; toggle slave select pin out spdr,r5 ; send out left channel msb cbi portb,portb0 adiw r25:r24,$01 ; increment write address wait1_000045: ; check if byte has been sent in r17,spsr sbrs r17,spif rjmp wait1_000045 in r19,spdr ; recieve in left channel msb out spdr,r4 ; send out left channel lsb wait2_000045: ; check if byte has been sent in r17,spsr sbrs r17,spif rjmp wait2_000045 in r18,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,r14 ; 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,r18 ; set data out portc,r19 sbi portg,portg2 ; pull we high to write out ddra,r14 ; set porta as input for data lines out ddrc,r14 ; set portc as input for data lines wait3_000045: ; check if byte has been sent in r17,spsr sbrs r17,spif rjmp wait3_000045 in r17,spdr ; recieve in right channel msb out spdr,r4 ; send out right channel lsb wait4_000045: ; check if byte has been sent in r17,spsr sbrs r17,spif rjmp wait4_000045 in r17,spdr ; recieve in right channel lsb ; vco generation movw r17:r16,r31:r30 ; store z register ;get sample 1 add r22,r12 ; increment sinetable address adc r23,r13 adc r26,r14 ; r14 is cleared above adc r27,r14 movw r31:r30,r27:r26 ; move to z register for data fetch lsl r30 ; adjust pointer for 16b fetch rol r31 andi r31,$03 ; limit value to 10b (512 samples x 2 bytes) ori r31,$48 ; set to memory address location where table is stored lpm r18,z+ ; get sine value lsb, increment z register lpm r19,z ; get sine value msb sbrc r27,$01 ; flip sign for half of the values rjmp interpolate_000045 neg r18 adc r19,r14 ; r14 is cleared above neg r19 interpolate_000045: ; multiply sample 1 by distance movw r21:r20,r23:r22 ; get distance from sample 1 com r20 ; invert distance com r21 mulsu r19,r21 ; (signed)Ah * (unsigned)Bh - multiply high bytes movw r5:r4,r1:r0 ; store high bytes result for later mul r18,r20 ; (unsigned)Al * (unsigned)Bl ; multiply low bytes movw r3:r2,r1:r0 ; store low byets for later mulsu r19,r20 ; (signed)Ah * (unsigned)Bl - multiply middle bytes sbc r5,r14 ; r14 is cleared above - subtract sign bit add r3,r0 ; accumulate result adc r4,r1 adc r5,r14 ; r14 is cleared above mul r21,r18 ; (unsigned)Bh * (unsigned)Al - multiply middle bytes add r3,r0 ; accumulate result adc r4,r1 adc r5,r14 ; r14 is cleared above ;get sample 2 adiw r27:r26,$01 ; set to next sample movw r31:r30,r27:r26 ; move to z register for data fetch lsl r30 ; adjust pointer for 16b fetch rol r31 andi r31,$03 ; limit value to 10b (512 samples x 2 bytes) ori r31,$48 ; set to memory address location where table is stored lpm r18,z+ ; get sine value lsb, increment z register lpm r19,z ; get sine value msb sbrc r27,$01 ; flip sign for half of the values rjmp interpolate1_000045 neg r18 adc r19,r14 ; r14 is cleared above neg r19 interpolate1_000045: ; multiply sample 2 by distance sbiw r27:r26,$01 ; reset address movw r31:r30,r17:r16 ; restore z register mulsu r19,r23 ; (signed)Ah * (unsigned)Bh - multiply high bytes add r4,r0 ; accumulate result adc r5,r1 mul r18,r22 ; (unsigned)Al * (unsigned)Bl ; multiply low bytes add r2,r0 ; accumulate result adc r3,r1 adc r4,r14 ; r14 is cleared above adc r5,r14 mulsu r19,r22 ; (signed)Ah * (unsigned)Bl - multiply middle bytes sbc r5,r14 ; r14 is cleared above - subtract sign bit add r3,r0 ; accumulate result adc r4,r1 adc r5,r14 ; r14 is cleared above mul r23,r18 ; (unsigned)Bh * (unsigned)Al - multiply middle bytes add r3,r0 ; accumulate result adc r4,r1 adc r5,r14 ; r14 is cleared above ;set lfo depth - 8b value ldi r16,$80 ; convert lfo to unsigned number add r5,r16 movw r19:r18,r5:r4 ; move lfo signal to multiply register mov r21,r29 ; move lfo depth to multiply register mul r19,r21 ; (unsigned)ah * (unsigned)b movw r5:r4,r1:r0 mul r21,r18 ; (unsigned)b * (unsigned)al add r4,r1 adc r5,r14 ; r14 is cleared above ;add lfo to delay movw r17:r16,r25:r24 ; move current location to read address mov r20,r28 ; move delay offset to temporary register clr r21 ; clear temporary high byte lsl r20 ; multiply delay time by 4 rol r21 lsl r20 rol r21 sub r16,r20 ; remove delay offset sbc r17,r21 sec ; set the carry bit so all values are reduced by 1 lsb sbc r16,r5 ; remove lfo time sbc r17,r14 ; r14 is cleared above ;get left channel sample 1 from sram out portd,r16 ; set address sts porth,r17 nop ; wait setup period of two cycles nop in r18,pina ; get data in r19,pinc ; get data ;multiply sample 1 by distance mov r20,r4 ; get distance from sample 1 mulsu r19,r20 ; (signed)ah * b movw r5:r4,r1:r0 mul r18,r20 ; al * b add r4,r1 adc r5,r14 ; r14 is cleared above mov r3,r0 ;get left channel sample 2 from sram subi r16,$ff ; set to next sample sbci r17,$ff ; done this way because there is no addi or adci out portd,r16 ; set address sts porth,r17 nop ; wait setup period of two cycles nop in r18,pina ; get data in r19,pinc ; get data ;multiply sample 2 by distance com r20 ; get distance to sample 2 mulsu r19,r20 ; (signed)ah * b add r4,r0 ; accumulate result adc r5,r1 mul r18,r20 ; al * b add r3,r0 ; accumulate result add r4,r1 adc r5,r14 ; r14 is cleared above ;check rotary encoder and adjust lfo depth ; although rotary encoder is externally debounced, it is done here again. ; pin1 is sampled on a transition from high to low on pin0. if pin1 is ; high, a left turn occured, if pin1 is low, a right turn occured. dec r11 ; check if time to sample rotary encoder brne adcsample_000045 ; continue if not ldi r17,$40 ; adjust sample frequency to catch all rising edges (1.5ms) mov r11,r17 lds r17,pinj ; get encoder/pushbutton data mov r16,r17 ; store pushbutton data to temporary register eor r16,r6 ; check if pushbutton changed value and r16,r17 ; check if rising edge sbrc r16,$02 ; continue if not rjmp pushbutton_000045 ; go to pushbutton routine if pressed ldi r20,$80 ; mask off the high bit of the pushbutton state register and r6,r20 ; pinj7 should be 0, as there is no i/o attached to it or r6,r17 ; store pushbutton data for next sample comparison sbrs r17,$02 ; check if pushbutton pressed rjmp adcsample_000045 ; do not allow any parameter change while button pressed sbrs r17,$00 ; check if pin0 is low rjmp edge_000045 ; check if pin0 was low on previous sample clt ; clear state register if back high rjmp adcsample_000045 ; finish off edge_000045: ; check for falling edge brts adcsample_000045 ; do nothing if edge was already detected set ; set t register to indicate edge detected ldi r21,$01 ; prepare for addition or subtraction sbrs r6,$07 ; check which function is being modded rjmp lfo_000045 ; do lfo function sbrs r17,$01 ; check if pin1 is high rjmp increment_000045 ; increment desired delay if right rotation dec r28 ; decrement delay register else cp r28,r21 ; r21 set to $01 above brsh adcsample_000045 ; continue if not mov r28,r16 ; set delay to min rjmp adcsample_000045 ; finish off increment_000045: ; increment desired delay register add r28,r21 ; increment delay register brcc adcsample_000045 ; check if overflow occured ser r28 ; set delay to max adcsample_000045: ; sample adc for lfo rate lds r17,adcsra ; get adc control register sbrs r17,adif ; check if adc conversion is complete rjmp done_000045 ; skip adc sampling lds r16,adcl ; get low byte adc value lds r17,adch ; get high byte adc value add r8,r16 ; accumulate adc samples adc r9,r17 adc r10,r14 ; r14 is cleared above ldi r17,$f7 sts adcsra,r17 ; clear interrupt flag dec r15 ; countdown adc sample clock brne done_000045 ; get delay time if its been long enough deadband_000045: ; set the low value of the delay lsr r10 ; divide adc value by 16 ror r9 ror r8 lsr r10 ror r9 ror r8 lsr r9 ; r10 is now empty ror r8 lsr r9 ror r8 movw r17:r16,r9:r8 ; move adc sample to temporary register ldi r21,$80 ; add in offset of min lfo rate ($0080) add r16,r21 adc r17,r14 ; r14 is cleared above sub r16,r12 ; find difference between adc sample and current lfo rate sbc r17,r13 brsh check_000045 ; check for deadband if positive neg r16 ; invert if negative adc r17,r14 ; r14 is cleared above neg r17 check_000045: ; check if difference is greater than deadband cpi r16,$10 ; check if difference is less than 1 adc lsb cpc r17,r14 ; r14 cleared above brlo empty_000045 ; do nothing if less than 1 adc lsb movw r13:r12,r9:r8 ; move adc sample to lfo rate register add r12,r21 ; add in offset of min lfo rate ($0080) adc r13,r14 ; r14 is cleared above empty_000045: ; empty accumulation registers and finish off clr r8 ; empty accumulation registers clr r9 clr r10 switchsample_000045: ; check rotary switch 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 cpse r16,r31 ; check if location has changed clr r30 ; reset jump register to intial state mov r31,r16 done_000045: reti ; return to waiting lfo_000045: ; modify lfo depth parameter sbrs r17,$01 ; check if pin1 is high rjmp increment1_000045 ; increment desired delay if right rotation sub r29,r21 ; decrement lfo depth register else brcc adcsample_000045 ; check if underflow clr r29 ; set depth to min rjmp adcsample_000045 ; finish off increment1_000045: ; increment desired delay register add r29,r21 ; increment lfo depth register brcc adcsample_000045 ; check if overflow occured ser r29 ; set depth to max rjmp adcsample_000045 ; finish off pushbutton_000045: ; edge detect ldi r20,$80 ; toggle msb to inidicate function change eor r6,r20 and r6,r20 ; mask off the high bit or r6,r17 ; store pushbutton data for next sample comparison rjmp adcsample_000045 ; finish off }}} |
Flanger
This function implements a flanger. It is mono, taking in data from the left channel, and presenting the mono output on both the left and right channels.
Flanging is based on analog tape and reel recording techniques, where slight pressure is applied to the flange of the reel, slowing down the playback. This creates a time-varying delayed signal, which, when mixed with the original signal, has a swept filter effect. Any time-varying signal can be used, but in this case, we are using a sinusoidal signal, as it introduces the least amount of added frequency content (ramp waves are also common). This sinusoid is created by taking values from a look-up table stored in program memory. The frequency of the sinusoid is set by the pot (MOD1), and is variable from .084Hz to 10.84Hz.
Both the depth of the low frequency oscillator (LFO), and the offset delay time can be modified to change the quality of the sound. Unfortunately, there is only one other knob on the MICrODEC (although you could add more), so we had to cram them both onto that knob. By pressing the pushbutton on MOD2, the functionality of the rotary encoder on MOD2 changes. In one state, it modifies the LFO depth, with rotations to the right increasing its amplitude (from 0ms to 6ms). In the other state, it modifies the offset delay, with rotations to the right increasing the delay time (from 0ms to 24ms). In both cases, the rotary encoder sets an 8b value, so it takes quite a few turns to go from one end to the other, although this also gives finer resolution.
Try the flanger with large LFO depth and some feedback, and you'll get what we've started calling "drunken sailor mode". It will make your guitar strings sound like rubber bands.
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