← Revision 1 as of 2010-08-13 19:13:13
Size: 93
Comment:
|
← Revision 2 as of 2010-08-13 22:15:53 →
Size: 15015
Comment:
|
Deletions are marked like this. | Additions are marked like this. |
Line 3: | Line 3: |
This function implements a voltage controlled oscillator (VCO), with the ability to control its amplitude (voltage controlled amplitude - VCA). The pot (MOD1) set the oscillators base frequency, and the rotary encoder (MOD2) adjusts the functionality of the VCO. The main oscillator is a sinusoid generated from a look-up table in program memory. This consists of a 512 sample, 16 bit, half sine wave. The other half of the sine wave is generated by reading through the table a second time, and inverting all the values. If you were to play through this table at normal rate (44.1ks/s), it would produce a tone of 43Hz. To get other frequencies, it is either read through at slower or faster rate. |
|
Line 9: | Line 15: |
; program: vco.asm ; UID = 000029 - unique id to eliminate conflicts between variables ; sram not used ; mono data in on left channel, identical stereo out ; constant definitions ; .equ maxvalue_000029 = $0200 ; storage location in internal sram for ; envelope max value, as ive run out of registers. .equ decay_000029 = $10 ; decay amount per sample period for envelope ; program overview ; ; a sinewave is generated internally from a lookup table in flash. the ; frequency can be set by either an external voltage applied on the left ; channel input, or via the adc input. the rotary encoder changes the ; functionality, from adc input with varying max values (86Hz, 171Hz, 343Hz, ; 686Hz, 1.4KHz, 2.8kHz, 5.5kHz, 11kHz), to analog input (in/1, in/2, in/4, ; in/8, in/16, in/32, in/64, in/128), to envelope control (8 adc input ; controls, and 8 analog input controls, with the same values as above). in ; envelope control mode, the envelope of the input signal also sets the ; envelope of the vco output. the sinewave is generated from a lookup table ; in program memory, and is interpolated to get non-integer multiples of ; the base frequency. it uses a 512 sample, 16b lookup table. the analog ; input is lowpassed at ~1kHz to give smoother response, and the adc is ; oversampled 256 times and deadbanded to remove glitches. the envelope is ; generated by keeping track of the iput voltage max value, and decaying ; that value with time. ; register usage - may be redefined in other sections ; ; r0 multiply result lsb ; r1 multiply result msb ; r2 low pass accumulation fractional byte ; r3 low pass accumulation lsb ; r4 right/left lsb out/accumulation lsb ; r5 right/left msb out/accumulation msb ; r6 left lsb in ; r7 left msb in ; r8 low pass accumulation msb ; r9 adc accumulation msb ; r10 adc accumulation fractional byte ; r11 adc accumulation lsb ; r12 vco control signal lsb ; r13 vco control signal msb ; r14 rotary encoder counter ; r15 switch/adc sample counter ; r16 temporary swap register ; r17 temporary swap register ; r18 temporary swap register ; r19 temporary swap register ; r20 temporary swap register ; r21 vco function register ; r22 null register ; r23 sinetable lookup address fractional byte ; r24 sinetable lookup address lsb ; r25 sinetable lookup address msb ; r26 adc last value lsb ; r27 adc last value msb ; r28 read/write address lsb ; r29 read/write address msb ; r30 jump location for interrupt lsb ; r31 jump location for interrupt msb ;program starts here first time and after buffer changes ldi r30,$19 ; set jump location to program start clr r24 ; clear write register clr r25 clr r22 ; setup r22 as null register for carry addition and ddr setting ldi r17,$ff ; setup r17 for ddr setting clear_000029: ; clear delay buffer ; eliminates static when first switching to the delay setting 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,r22 ; set data out portc,r22 ; r18 is cleared above sbi portg,portg2 ; pull we high to write out ddra,r22 ; set porta as input for data lines out ddrc,r22 ; set portc as input for data lines inc r24 ; increment write register - only clears first 256 bytes brne clear_000029 ; continue until end of buffer reached cleardone_000029: ; reset registers ldi r28,$20 ; set buffer size for lowpass (44.1kHz/value = cutoff frequency) clr r29 clr r2 ; initialize accumulation registers for lowpass clr r3 clr r8 ldi r21,$18 ; initialize vco state to envelope mod and analog input 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 ;increment sram addreses adiw r29:r28,$01 ; increment read/write address movw r17:r16,r29:r28 ; move to temporary register subi r16,$20 ; remove buffer size for read address sbc r17,r22 ; r22 is cleared above wait1_000029: ; check if byte has been sent in r7,spsr sbrs r7,spif rjmp wait1_000029 in r7,spdr ; recieve in left channel msb out spdr,r4 ; send out left channel lsb ;get delayed data out portd,r16 ; set address sts porth,r17 nop ; wait input latch time of 2 clock cycles nop ; wait input latch time of 2 clock cycles in r0,pina ; get data in r1,pinc ; get data wait2_000029: ; check if byte has been sent in r17,spsr sbrs r17,spif rjmp wait2_000029 in r6,spdr ; recieve in left channel lsb out spdr,r5 ; send out right channel msb ;write left channel data to sram out portd,r28 ; set address sts porth,r29 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,r22 ; set porta as input for data lines out ddrc,r22 ; set portc as input for data lines wait3_000029: ; check if byte has been sent in r17,spsr sbrs r17,spif rjmp wait3_000029 in r17,spdr ; recieve in right channel msb out spdr,r4 ; send out right channel lsb wait4_000029: ; check if byte has been sent in r17,spsr sbrs r17,spif rjmp wait4_000029 in r17,spdr ; recieve in right channel lsb ;accumulate samples for lowpass add r2,r6 ; add in current sample adc r3,r7 sbrc r7,$07 ; check if data is negative ldi r22,$ff ; set high bits if it is adc r8,r22 ; else r22 is cleared above clr r22 ; reset null register sub r2,r0 ; remove last sample in buffer sbc r3,r1 sbrc r1,$07 ; check if data is negative ldi r22,$ff ; set high bits if it is sbc r8,r22 ; else r22 is cleared above movw r7:r6,r3:r2 ; move data to temporary register mov r22,r8 asr r22 ; divide by 32 to normalize output ror r7 ror r6 asr r22 ror r7 ror r6 asr r22 ror r7 ror r6 asr r22 ror r7 ror r6 asr r22 ror r7 ror r6 clr r22 ; reset null register ; vco generation movw r17:r16,r31:r30 ; store z register ;get sample 1 add r23,r12 ; increment sinetable address adc r24,r13 adc r25,r22 ; r22 is cleared above movw r31:r30,r25:r24 ; 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 r25,$01 ; flip sign for half of the values rjmp interpolate_000029 ; dont invert if even neg r18 ; invert if odd adc r19,r22 ; r22 is cleared above neg r19 interpolate_000029: ; multiply sample 1 by distance mov r20,r23 ; get distance from sample 1 com r20 ; invert distance for sample magnitude mulsu r19,r20 ; (signed)ah * b movw r5:r4,r1:r0 mul r18,r20 ; al * b add r4,r1 adc r5,r22 ; r22 is cleared above mov r20,r0 ;get sample 2 adiw r25:r24,$01 ; set to next sample movw r31:r30,r25:r24 ; 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 r25,$01 ; flip sign for half of the values rjmp interpolate1_000029 ; dont invert if even neg r18 ; invert if odd adc r19,r22 ; r22 is cleared above neg r19 interpolate1_000029: ; multiply sample 2 by distance movw r31:r30,r17:r16 ; restore z register sbiw r25:r24,$01 ; reset address mulsu r19,r23 ; (signed)ah * b add r4,r0 ; accumulate samples adc r5,r1 mul r18,r23 ; al * b add r20,r0 ; accumulate samples adc r4,r1 adc r5,r22 ; r22 is cleared above ;find envelope value movw r17:r16,r7:r6 ; move lowpassed value to temporary register tst r17 ; check if value is negative brpl envelope_000029 ; dont invert if positive com r16 ; invert negative values com r17 ; using ones complement to avoid problem at $8000 envelope_000029: ; compare current value to max value lsl r16 ; convert current value to unsigned number rol r17 ; this is done to increase output volume lds r18,maxvalue_000029 ; fetch current max value lds r19,(maxvalue_000029 + 1) cp r16,r18 ; compare current value to max value cpc r17,r19 brlo envelope_dec_000029 ; decrement envelope if below movw r19:r18,r17:r16 ; move value to max value if same or higher rjmp restore_000029 ; dont decrement if just incremented envelope_dec_000029: ; decay the envelope subi r18,decay_000029 ; decay the envelope sbc r19,r22 ; r22 is cleared above brcc restore_000029 ; re-store new max value if no underflow clr r18 ; set envelope to bottom clr r19 restore_000029: ; re-store new max value sts maxvalue_000029,r18 ; re-store new max value sts (maxvalue_000029 + 1),r19 rotary_000029: ; check rotary encoder and adjust function ; rotary encoder is externally debounced, so that is not done here. ; 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 r14 ; reduce the sampling rate to help with debounce brne adcsample_000029 ldi r17,$40 ; adjust sample frequency to catch all rising edges (1.5ms) mov r14,r17 lds r17,pinj ; get switch data sbrs r17,$00 ; check if pin0 is low rjmp edge_000029 ; check if pin0 was low on previous sample clt ; clear state register if back high rjmp adcsample_000029 ; finish off edge_000029: ; check for falling edge brts adcsample_000029 ; do nothing if the edge was already detected set ; set state register to indicate a falling edge occured sbrs r17,$01 ; check if pin1 is high rjmp increment_000029 ; increment playback if right rotation dec r21 rjmp adcsample_000029 ; finish off increment_000029: ; increment playback speed inc r21 adcsample_000029: ; get loop setting lds r17,adcsra ; get adc control register sbrs r17,adif ; check if adc conversion is complete rjmp done_000029 ; skip adc sampling lds r16,adcl ; get low byte adc value lds r17,adch ; get high byte adc value add r10,r16 adc r11,r17 ; accumulate adc samples adc r9,r22 ; accumulate adc samples - r22 is cleared above ldi r17,$f7 sts adcsra,r17 ; clear interrupt flag dec r15 ; countdown adc sample clock brne done_000029 ; move adc value to loop setting after 256 samples lsr r9 ; divide value by 4 to get a 16b value ror r11 ror r10 lsr r9 ror r11 ror r10 ;check if value changed enough to warrant updating movw r17:r16,r27:r26 ; make a copy of last adc value for comparison sub r16,r10 ; find difference between current and last value sbc r17,r11 brcc deadband_000029 ; see if difference is large enough to indicate a change neg r16 ; invert difference if negative adc r17,r22 ; r22 is cleared above neg r17 deadband_000029: ; see if pot has moved or if its just noise cpi r16,$40 ; see if difference is greater than 1 lsb cpc r17,r22 ; r22 is cleared above brlo nochange_000029 ; dont update loop time if difference is not large enough sbrc r21,$03 ; check if in analog mode rjmp update_000029 ; dont adjust frequency range if in analog mode mov r17,r21 ; move vco function to temporary register andi r17,$07 ; mask off lower 3b breq update_000029 ; check if no multiply required frequencyshift1_000029: ; adjust frequency range lsr r11 ; adjust frequency range ror r10 dec r17 brne frequencyshift1_000029 ; keep adjusting till done update_000029: ; update loop time register movw r27:r26,r11:r10 ; move adc value to last value register sbrs r21,$03 ; check if in analog mode movw r13:r12,r27:r26 ; move adc value to vco increment register nochange_000029: ; clear accumulation registers clr r10 ; empty accumulation registers clr r11 clr r9 switchsample_000029: ; check rotary switch state 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_000029: sbrs r21,$03 ; check if in analog mode rjmp done1_000029 ; finish if not mov r17,r21 ; move vco function to temporary register andi r17,$07 ; mask off lower 3b breq offset_000029 ; check if no divide required frequencyshift_000029: ; adjust frequency range asr r7 ; adjust frequency range that input signal effects ror r6 dec r17 brne frequencyshift_000029 ; keep adjusting till done offset_000029: ; add in offset from adc tst r7 ; check if input voltage is negative brmi subtract_000029 ; subtract value and check for low bound add r6,r26 ; else add input voltage to adc value to get frequency adc r7,r27 brcc move_000029 ; check if overflow ldi r17,$ff ; set value to max if overflow mov r6,r17 mov r7,r17 rjmp move_000029 subtract_000029: add r6,r26 ; add input voltage to adc value to get frequency adc r7,r27 ; adding a negative value is the same as subtracting brcs move_000029 ; check if underflow clr r6 ; set value to min if underflow clr r7 move_000029: ; finish off by moving value to vco increment register movw r13:r12,r7:r6 ; move combined value to vco increment register done1_000029: sbrc r21,$04 ; check if in envelope mode rjmp done2_000029 ; add envelope ;reduce output volume because its too loud asr r5 ; divide by 2 ror r4 reti ; return to waiting if not envelope mode done2_000029: ; add envelope ;multiply data by envelope - movw r17:r16,r5:r4 ; move signal to multiply register mulsu r17,r19 ; (signed)ah * (unsigned)bh movw r5:r4,r1:r0 mul r18,r16 ; (unsigned)al * (unsigned)bl movw r7:r6,r1:r0 mulsu r17,r18 ; (signed)ah * (unsigned)bl sbc r5,r22 ; r22 is cleared above add r7,r0 adc r4,r1 adc r5,r22 mul r19,r16 ; (unsigned)bh * (unsigned)al add r7,r0 adc r4,r1 adc r5,r22 reti ; return to waiting |
VCO / VCA
This function implements a voltage controlled oscillator (VCO), with the ability to control its amplitude (voltage controlled amplitude - VCA). The pot (MOD1) set the oscillators base frequency, and the rotary encoder (MOD2) adjusts the functionality of the VCO.
The main oscillator is a sinusoid generated from a look-up table in program memory. This consists of a 512 sample, 16 bit, half sine wave. The other half of the sine wave is generated by reading through the table a second time, and inverting all the values. If you were to play through this table at normal rate (44.1ks/s), it would produce a tone of 43Hz. To get other frequencies, it is either read through at slower or faster rate.
1 ; program: vco.asm
2 ; UID = 000029 - unique id to eliminate conflicts between variables
3 ; sram not used
4 ; mono data in on left channel, identical stereo out
5
6 ; constant definitions
7 ;
8 .equ maxvalue_000029 = $0200 ; storage location in internal sram for
9 ; envelope max value, as ive run out of registers.
10 .equ decay_000029 = $10 ; decay amount per sample period for envelope
11
12 ; program overview
13 ;
14 ; a sinewave is generated internally from a lookup table in flash. the
15 ; frequency can be set by either an external voltage applied on the left
16 ; channel input, or via the adc input. the rotary encoder changes the
17 ; functionality, from adc input with varying max values (86Hz, 171Hz, 343Hz,
18 ; 686Hz, 1.4KHz, 2.8kHz, 5.5kHz, 11kHz), to analog input (in/1, in/2, in/4,
19 ; in/8, in/16, in/32, in/64, in/128), to envelope control (8 adc input
20 ; controls, and 8 analog input controls, with the same values as above). in
21 ; envelope control mode, the envelope of the input signal also sets the
22 ; envelope of the vco output. the sinewave is generated from a lookup table
23 ; in program memory, and is interpolated to get non-integer multiples of
24 ; the base frequency. it uses a 512 sample, 16b lookup table. the analog
25 ; input is lowpassed at ~1kHz to give smoother response, and the adc is
26 ; oversampled 256 times and deadbanded to remove glitches. the envelope is
27 ; generated by keeping track of the iput voltage max value, and decaying
28 ; that value with time.
29
30 ; register usage - may be redefined in other sections
31 ;
32 ; r0 multiply result lsb
33 ; r1 multiply result msb
34 ; r2 low pass accumulation fractional byte
35 ; r3 low pass accumulation lsb
36 ; r4 right/left lsb out/accumulation lsb
37 ; r5 right/left msb out/accumulation msb
38 ; r6 left lsb in
39 ; r7 left msb in
40 ; r8 low pass accumulation msb
41 ; r9 adc accumulation msb
42 ; r10 adc accumulation fractional byte
43 ; r11 adc accumulation lsb
44 ; r12 vco control signal lsb
45 ; r13 vco control signal msb
46 ; r14 rotary encoder counter
47 ; r15 switch/adc sample counter
48 ; r16 temporary swap register
49 ; r17 temporary swap register
50 ; r18 temporary swap register
51 ; r19 temporary swap register
52 ; r20 temporary swap register
53 ; r21 vco function register
54 ; r22 null register
55 ; r23 sinetable lookup address fractional byte
56 ; r24 sinetable lookup address lsb
57 ; r25 sinetable lookup address msb
58 ; r26 adc last value lsb
59 ; r27 adc last value msb
60 ; r28 read/write address lsb
61 ; r29 read/write address msb
62 ; r30 jump location for interrupt lsb
63 ; r31 jump location for interrupt msb
64
65 ;program starts here first time and after buffer changes
66 ldi r30,$19 ; set jump location to program start
67 clr r24 ; clear write register
68 clr r25
69 clr r22 ; setup r22 as null register for carry addition and ddr setting
70 ldi r17,$ff ; setup r17 for ddr setting
71
72 clear_000029: ; clear delay buffer
73 ; eliminates static when first switching to the delay setting
74 out portd,r24 ; set address
75 sts porth,r25
76 out portg,r22 ; pull ce low,we low,and set high bits of address
77 out ddra,r17 ; set porta as output for data write
78 out ddrc,r17 ; set portc as output for data write
79 out porta,r22 ; set data
80 out portc,r22 ; r18 is cleared above
81 sbi portg,portg2 ; pull we high to write
82 out ddra,r22 ; set porta as input for data lines
83 out ddrc,r22 ; set portc as input for data lines
84 inc r24 ; increment write register - only clears first 256 bytes
85 brne clear_000029 ; continue until end of buffer reached
86
87 cleardone_000029: ; reset registers
88
89 ldi r28,$20 ; set buffer size for lowpass (44.1kHz/value = cutoff frequency)
90 clr r29
91 clr r2 ; initialize accumulation registers for lowpass
92 clr r3
93 clr r8
94 ldi r21,$18 ; initialize vco state to envelope mod and analog input
95 reti ; finish with initialization and wait for next interrupt
96
97 ; program starts here every time but first
98 ; initiate data transfer to codec
99 sbi portb,portb0 ; toggle slave select pin
100 out spdr,r5 ; send out left channel msb
101 cbi portb,portb0
102
103 ;increment sram addreses
104 adiw r29:r28,$01 ; increment read/write address
105 movw r17:r16,r29:r28 ; move to temporary register
106 subi r16,$20 ; remove buffer size for read address
107 sbc r17,r22 ; r22 is cleared above
108
109 wait1_000029: ; check if byte has been sent
110
111 in r7,spsr
112 sbrs r7,spif
113 rjmp wait1_000029
114 in r7,spdr ; recieve in left channel msb
115 out spdr,r4 ; send out left channel lsb
116
117 ;get delayed data
118 out portd,r16 ; set address
119 sts porth,r17
120 nop ; wait input latch time of 2 clock cycles
121 nop ; wait input latch time of 2 clock cycles
122 in r0,pina ; get data
123 in r1,pinc ; get data
124
125 wait2_000029: ; check if byte has been sent
126
127 in r17,spsr
128 sbrs r17,spif
129 rjmp wait2_000029
130 in r6,spdr ; recieve in left channel lsb
131 out spdr,r5 ; send out right channel msb
132
133 ;write left channel data to sram
134 out portd,r28 ; set address
135 sts porth,r29
136 out portg,r22 ; pull ce low,we low,and set high bits of address
137 ldi r17,$ff
138 out ddra,r17 ; set porta as output for data write
139 out ddrc,r17 ; set portc as output for data write
140 out porta,r6 ; set data
141 out portc,r7
142 sbi portg,portg2 ; pull we high to write
143 out ddra,r22 ; set porta as input for data lines
144 out ddrc,r22 ; set portc as input for data lines
145
146 wait3_000029: ; check if byte has been sent
147
148 in r17,spsr
149 sbrs r17,spif
150 rjmp wait3_000029
151 in r17,spdr ; recieve in right channel msb
152 out spdr,r4 ; send out right channel lsb
153
154 wait4_000029: ; check if byte has been sent
155
156 in r17,spsr
157 sbrs r17,spif
158 rjmp wait4_000029
159 in r17,spdr ; recieve in right channel lsb
160
161 ;accumulate samples for lowpass
162 add r2,r6 ; add in current sample
163 adc r3,r7
164 sbrc r7,$07 ; check if data is negative
165 ldi r22,$ff ; set high bits if it is
166 adc r8,r22 ; else r22 is cleared above
167 clr r22 ; reset null register
168 sub r2,r0 ; remove last sample in buffer
169 sbc r3,r1
170 sbrc r1,$07 ; check if data is negative
171 ldi r22,$ff ; set high bits if it is
172 sbc r8,r22 ; else r22 is cleared above
173 movw r7:r6,r3:r2 ; move data to temporary register
174 mov r22,r8
175 asr r22 ; divide by 32 to normalize output
176 ror r7
177 ror r6
178 asr r22
179 ror r7
180 ror r6
181 asr r22
182 ror r7
183 ror r6
184 asr r22
185 ror r7
186 ror r6
187 asr r22
188 ror r7
189 ror r6
190 clr r22 ; reset null register
191
192 ; vco generation
193 movw r17:r16,r31:r30 ; store z register
194 ;get sample 1
195 add r23,r12 ; increment sinetable address
196 adc r24,r13
197 adc r25,r22 ; r22 is cleared above
198 movw r31:r30,r25:r24 ; move to z register for data fetch
199 lsl r30 ; adjust pointer for 16b fetch
200 rol r31
201 andi r31,$03 ; limit value to 10b (512 samples x 2 bytes)
202 ori r31,$48 ; set to memory address location where table is stored
203 lpm r18,z+ ; get sine value lsb, increment z register
204 lpm r19,z ; get sine value msb
205 sbrc r25,$01 ; flip sign for half of the values
206 rjmp interpolate_000029 ; dont invert if even
207 neg r18 ; invert if odd
208 adc r19,r22 ; r22 is cleared above
209 neg r19
210
211 interpolate_000029: ; multiply sample 1 by distance
212
213 mov r20,r23 ; get distance from sample 1
214 com r20 ; invert distance for sample magnitude
215 mulsu r19,r20 ; (signed)ah * b
216 movw r5:r4,r1:r0
217 mul r18,r20 ; al * b
218 add r4,r1
219 adc r5,r22 ; r22 is cleared above
220 mov r20,r0
221
222 ;get sample 2
223 adiw r25:r24,$01 ; set to next sample
224 movw r31:r30,r25:r24 ; move to z register for data fetch
225 lsl r30 ; adjust pointer for 16b fetch
226 rol r31
227 andi r31,$03 ; limit value to 10b (512 samples x 2 bytes)
228 ori r31,$48 ; set to memory address location where table is stored
229 lpm r18,z+ ; get sine value lsb, increment z register
230 lpm r19,z ; get sine value msb
231 sbrc r25,$01 ; flip sign for half of the values
232 rjmp interpolate1_000029 ; dont invert if even
233 neg r18 ; invert if odd
234 adc r19,r22 ; r22 is cleared above
235 neg r19
236
237 interpolate1_000029: ; multiply sample 2 by distance
238
239 movw r31:r30,r17:r16 ; restore z register
240 sbiw r25:r24,$01 ; reset address
241 mulsu r19,r23 ; (signed)ah * b
242 add r4,r0 ; accumulate samples
243 adc r5,r1
244 mul r18,r23 ; al * b
245 add r20,r0 ; accumulate samples
246 adc r4,r1
247 adc r5,r22 ; r22 is cleared above
248
249 ;find envelope value
250 movw r17:r16,r7:r6 ; move lowpassed value to temporary register
251 tst r17 ; check if value is negative
252 brpl envelope_000029 ; dont invert if positive
253 com r16 ; invert negative values
254 com r17 ; using ones complement to avoid problem at $8000
255
256 envelope_000029: ; compare current value to max value
257
258 lsl r16 ; convert current value to unsigned number
259 rol r17 ; this is done to increase output volume
260 lds r18,maxvalue_000029 ; fetch current max value
261 lds r19,(maxvalue_000029 + 1)
262 cp r16,r18 ; compare current value to max value
263 cpc r17,r19
264 brlo envelope_dec_000029 ; decrement envelope if below
265 movw r19:r18,r17:r16 ; move value to max value if same or higher
266 rjmp restore_000029 ; dont decrement if just incremented
267
268 envelope_dec_000029: ; decay the envelope
269
270 subi r18,decay_000029 ; decay the envelope
271 sbc r19,r22 ; r22 is cleared above
272 brcc restore_000029 ; re-store new max value if no underflow
273 clr r18 ; set envelope to bottom
274 clr r19
275
276 restore_000029: ; re-store new max value
277
278 sts maxvalue_000029,r18 ; re-store new max value
279 sts (maxvalue_000029 + 1),r19
280
281 rotary_000029: ; check rotary encoder and adjust function
282 ; rotary encoder is externally debounced, so that is not done here.
283 ; pin1 is sampled on a transition from high to low on pin0. if pin1 is
284 ; high, a left turn occured, if pin1 is low, a right turn occured.
285 dec r14 ; reduce the sampling rate to help with debounce
286 brne adcsample_000029
287 ldi r17,$40 ; adjust sample frequency to catch all rising edges (1.5ms)
288 mov r14,r17
289 lds r17,pinj ; get switch data
290 sbrs r17,$00 ; check if pin0 is low
291 rjmp edge_000029 ; check if pin0 was low on previous sample
292 clt ; clear state register if back high
293 rjmp adcsample_000029 ; finish off
294
295 edge_000029: ; check for falling edge
296
297 brts adcsample_000029 ; do nothing if the edge was already detected
298 set ; set state register to indicate a falling edge occured
299 sbrs r17,$01 ; check if pin1 is high
300 rjmp increment_000029 ; increment playback if right rotation
301 dec r21
302 rjmp adcsample_000029 ; finish off
303
304 increment_000029: ; increment playback speed
305
306 inc r21
307
308 adcsample_000029: ; get loop setting
309
310 lds r17,adcsra ; get adc control register
311 sbrs r17,adif ; check if adc conversion is complete
312 rjmp done_000029 ; skip adc sampling
313 lds r16,adcl ; get low byte adc value
314 lds r17,adch ; get high byte adc value
315 add r10,r16
316 adc r11,r17 ; accumulate adc samples
317 adc r9,r22 ; accumulate adc samples - r22 is cleared above
318 ldi r17,$f7
319 sts adcsra,r17 ; clear interrupt flag
320 dec r15 ; countdown adc sample clock
321 brne done_000029 ; move adc value to loop setting after 256 samples
322 lsr r9 ; divide value by 4 to get a 16b value
323 ror r11
324 ror r10
325 lsr r9
326 ror r11
327 ror r10
328
329 ;check if value changed enough to warrant updating
330 movw r17:r16,r27:r26 ; make a copy of last adc value for comparison
331 sub r16,r10 ; find difference between current and last value
332 sbc r17,r11
333 brcc deadband_000029 ; see if difference is large enough to indicate a change
334 neg r16 ; invert difference if negative
335 adc r17,r22 ; r22 is cleared above
336 neg r17
337
338 deadband_000029: ; see if pot has moved or if its just noise
339
340 cpi r16,$40 ; see if difference is greater than 1 lsb
341 cpc r17,r22 ; r22 is cleared above
342 brlo nochange_000029 ; dont update loop time if difference is not large enough
343 sbrc r21,$03 ; check if in analog mode
344 rjmp update_000029 ; dont adjust frequency range if in analog mode
345 mov r17,r21 ; move vco function to temporary register
346 andi r17,$07 ; mask off lower 3b
347 breq update_000029 ; check if no multiply required
348
349 frequencyshift1_000029: ; adjust frequency range
350
351 lsr r11 ; adjust frequency range
352 ror r10
353 dec r17
354 brne frequencyshift1_000029 ; keep adjusting till done
355
356 update_000029: ; update loop time register
357
358 movw r27:r26,r11:r10 ; move adc value to last value register
359 sbrs r21,$03 ; check if in analog mode
360 movw r13:r12,r27:r26 ; move adc value to vco increment register
361
362 nochange_000029: ; clear accumulation registers
363
364 clr r10 ; empty accumulation registers
365 clr r11
366 clr r9
367
368 switchsample_000029: ; check rotary switch state
369
370 lds r16,pinj ; get switch data
371 andi r16,$78 ; mask off rotary switch
372 lsr r16 ; adjust switch position to program memory location
373 lsr r16
374 ldi r17,$02
375 add r16,r17
376 cpse r16,r31 ; check if location has changed
377 clr r30 ; reset jump register to intial state
378 mov r31,r16
379
380 done_000029:
381
382 sbrs r21,$03 ; check if in analog mode
383 rjmp done1_000029 ; finish if not
384 mov r17,r21 ; move vco function to temporary register
385 andi r17,$07 ; mask off lower 3b
386 breq offset_000029 ; check if no divide required
387
388 frequencyshift_000029: ; adjust frequency range
389
390 asr r7 ; adjust frequency range that input signal effects
391 ror r6
392 dec r17
393 brne frequencyshift_000029 ; keep adjusting till done
394
395 offset_000029: ; add in offset from adc
396
397 tst r7 ; check if input voltage is negative
398 brmi subtract_000029 ; subtract value and check for low bound
399 add r6,r26 ; else add input voltage to adc value to get frequency
400 adc r7,r27
401 brcc move_000029 ; check if overflow
402 ldi r17,$ff ; set value to max if overflow
403 mov r6,r17
404 mov r7,r17
405 rjmp move_000029
406
407 subtract_000029:
408
409 add r6,r26 ; add input voltage to adc value to get frequency
410 adc r7,r27 ; adding a negative value is the same as subtracting
411 brcs move_000029 ; check if underflow
412 clr r6 ; set value to min if underflow
413 clr r7
414
415 move_000029: ; finish off by moving value to vco increment register
416
417 movw r13:r12,r7:r6 ; move combined value to vco increment register
418
419 done1_000029:
420
421 sbrc r21,$04 ; check if in envelope mode
422 rjmp done2_000029 ; add envelope
423
424 ;reduce output volume because its too loud
425 asr r5 ; divide by 2
426 ror r4
427 reti ; return to waiting if not envelope mode
428
429 done2_000029: ; add envelope
430
431 ;multiply data by envelope -
432 movw r17:r16,r5:r4 ; move signal to multiply register
433 mulsu r17,r19 ; (signed)ah * (unsigned)bh
434 movw r5:r4,r1:r0
435 mul r18,r16 ; (unsigned)al * (unsigned)bl
436 movw r7:r6,r1:r0
437 mulsu r17,r18 ; (signed)ah * (unsigned)bl
438 sbc r5,r22 ; r22 is cleared above
439 add r7,r0
440 adc r4,r1
441 adc r5,r22
442 mul r19,r16 ; (unsigned)bh * (unsigned)al
443 add r7,r0
444 adc r4,r1
445 adc r5,r22
446 reti ; return to waiting
447