MICrODEC: Stock Functions
Up-Down Sweep
This function implements an up-down sweep. This means it plays forward at double speed until it hits the top of the buffer, and then plays in reverse until it hits the bottom. It takes in stereo data, and outputs stereo data. The pot (MOD1) varies the buffer size, within which the function reads back and forth.
Since the function just bounces back and forth inside the buffer, there is no buffer boundary problems, although the change in direction at the buffer ends can add a slight high frequency element. The buffer size is settable between and 3ms and 1.5s.
1 ; program: up_downsweep-18b-pot.asm
2 ; UID = 000035 - unique id to eliminate conflicts between variables
3 ; 18b address space (3s sample time)
4 ; stereo data
5 ; pot (MOD1) controlled buffer size (3ms - 1.5s)
6
7 ; program overview
8 ;
9 ; data is read in from memory and written out to the codec at the same time
10 ; new data is written to the memory from the codec. the write address
11 ; increments until it reaches the top of the buffer and then starts at
12 ; zero. the read address increases at twice the rate until it reaches the
13 ; write address, and then decreases until it reaches the write address.
14 ; it continues this back and forth sample playback. ADC0 (MOD1) is read
15 ; and averaged over 256 samples to reduce jitter. this value is subtracted
16 ; from the write address to create the desired read address. if the actual
17 ; read address doesnt match the desired read address, it is either
18 ; incremented or decremented by one sample each sample period until it
19 ; matches. this reduces noise during delay time transitions.
20
21 ; register usage - may be redefined in other sections
22 ;
23 ; r0 desired buffer size fractional byte
24 ; r1
25 ; r2 left lsb out
26 ; r3 left msb out
27 ; r4 right lsb out
28 ; r5 right msb out
29 ; r6 left lsb in
30 ; r7 left msb in
31 ; r8 right lsb in
32 ; r9 right msb in
33 ; r10 adc accumulation lsb
34 ; r11 adc accumulation msb
35 ; r12 read address offset lsb
36 ; r13 read address offset msb
37 ; r14 adc/switch sample counter
38 ; r15
39 ; r16 temporary swap register
40 ; r17 temporary swap register
41 ; r18 null register
42 ; r19 adc accumulation fractional byte
43 ; r20 temporary swap regiser
44 ; r21 read address offset high byte
45 ; r22 write address high byte
46 ; r23 read address high byte
47 ; r24 write address lsb
48 ; r25 write address msb
49 ; r26 desired buffer size lsb
50 ; r27 desired buffer size msb
51 ; r28 read address lsb
52 ; r29 read address msb
53 ; r30 jump location for interrupt lsb
54 ; r31 jump location for interrupt msb
55 ; t forward/reverse indicator
56
57 ; program starts here first time
58 ; initialze z pointer for correct jump
59 ; this assumes a less than 256 word jump
60 ldi r30,$29 ; set jump location to program start
61 clr r24 ; clear write register
62 clr r25
63 ldi r22,$00 ; setup write address high byte
64 clr r18 ; setup r18 as null register for carry addition and ddr setting
65 ldi r17,$ff ; setup r17 for ddr setting
66
67 clear_000035: ; clear delay buffer
68 ; eliminates static when first switching to the delay setting
69
70 adiw r25:r24,$01 ; increment write register
71 adc r22,r18 ; increment write third byte
72 cpi r22,$04 ; check if full memory space has been cleared
73 breq cleardone_000035 ; continue until end of buffer reached
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,r18 ; set data
80 out portc,r18 ; r18 is cleared above
81 sbi portg,portg2 ; pull we high to write
82 out ddra,r18 ; set porta as input for data lines
83 out ddrc,r18 ; set portc as input for data lines
84 rjmp clear_000035 ; continue clearing
85
86 cleardone_000035: ; reset registers
87
88 ldi r24,$00 ; initialize write register
89 ldi r25,$00
90 ldi r22,$00 ; setup write address high byte
91 ldi r28,$00 ; set read address to minimum delay
92 ldi r29,$fe
93 ldi r23,$07 ; setup read address high byte
94 clr r0 ; set buffer size to minimum
95 ldi r26,$01
96 clr r27
97 clr r12 ; set read address offset to minimum buffer size
98 ldi r16,$01
99 mov r13,r16
100 clr r21
101 clr r2 ; initialize data output registers
102 clr r3
103 clr r4
104 clr r5
105 clt ; clear t register to start with forward play
106 reti ; finish with initialization and wait for next interrupt
107
108 ; program starts here all but first time
109 ; this is the point the z-pointer is incremented to
110 ; initiate data transfer to codec
111 sbi portb,portb0 ; toggle slave select pin
112 out spdr,r3 ; send out left channel msb
113 cbi portb,portb0
114
115 wait1_000035: ; check if byte has been sent
116
117 in r17,spsr
118 sbrs r17,spif
119 rjmp wait1_000035
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 for efficient use of setup time
129 andi r23,$03 ; mask off unsed bits
130 ori r23,$04 ; set we/ bit in high byte register
131 in r2,pina ; get data
132 in r3,pinc ; get data
133
134 wait2_000035: ; check if byte has been sent
135
136 in r17,spsr
137 sbrs r17,spif
138 rjmp wait2_000035
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_000035: ; check if byte has been sent
156
157 in r17,spsr
158 sbrs r17,spif
159 rjmp wait3_000035
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, and set high bits of register
165 out portd,r28 ; set address
166 sts porth,r29
167 adiw r25:r24,$01 ; increment write address
168 adc r22,r18 ; placed here to use setup time efficiently
169 andi r22,$03 ; mask off unsed bits
170 in r4,pina ; get data
171 in r5,pinc ; get data
172
173 wait4_000035: ; check if byte has been sent
174
175 in r17,spsr
176 sbrs r17,spif
177 rjmp wait4_000035
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 ;increment write address to next sample position
194 adiw r25:r24,$01 ; increment write address
195 adc r22,r18 ; increment write third byte
196 andi r22,$03 ; mask off unsed bits
197 movw r29:r28,r25:r24 ; move write address to read address
198 mov r23,r22
199
200 ;calculate read offset
201 brtc increment_000035 ; move read address forward if in foward mode
202 ldi r16,$04 ; else move read address backwards by 2
203 add r12,r16 ; this requires incrementing the read offset by 4
204 adc r13,r18 ; r18 is cleared above
205 adc r21,r18
206 cp r12,r0 ; check if at bottom of buffer
207 cpc r13,r26
208 cpc r21,r27
209 brlo readadd_000035 ; load read address if not at bottom of buffer
210 clt ; else clear t register to indicate forward motion after next sample
211 ; decrement offset by 2 - this halts playback for one sample
212 ; to reduce spikes at transitions
213
214 increment_000035: ; move read address forward by 4
215 ; moving read address forward requires reducing the read offset by 2
216 ldi r16,$02 ; subtract 2 from read offset
217 sub r12,r16
218 sbc r13,r18 ; r18 is cleared above
219 sbc r21,r18
220 brne readadd_000035 ; load read address if not at top of buffer
221 ldi r16,$04 ; set to playback last played sample
222 mov r12,r16 ; reduces spikes at transitions
223 set ; else set t register to start reversing on next sample
224
225 readadd_000035: ; add in read offset
226
227 sub r28,r12 ; subtract offset from read address
228 sbc r29,r13
229 sbc r23,r21
230 andi r23,$03 ; mask off unused bits
231 ori r23,$04 ; set we/ bit for reading
232
233 adcsample_000035: ; get buffer size from adc
234
235 lds r17,adcsra ; get adc control register
236 sbrs r17,adif ; check if adc conversion is complete
237 rjmp done_000035 ; skip adc sampling if not
238 lds r16,adcl ; get low byte adc value
239 lds r17,adch ; get high byte adc value
240 add r19,r16 ; accumulate adc samples
241 adc r10,r17
242 adc r11,r18 ; r18 is cleared above
243 ldi r17,$f7
244 sts adcsra,r17 ; clear interrupt flag
245 dec r14 ; countdown adc sample clock
246 brne done_000035 ; dont get buffer size till its been long enough
247 lsr r11 ; divide adc sample by 2
248 ror r10 ; as the max buffer size is only $020000
249 ror r19 ; as the buffer is read backwards half of the time
250 ldi r17,$01 ; check if adc value is less than $000100
251 cp r10,r17
252 cpc r11,r18 ; r18 cleared above
253 brsh deadband_000035 ; check if adc value changed enough to update delay
254 mov r10,r17 ; set minimum delay to $000100 = 3ms
255 clr r19
256
257 deadband_000035: ; check for change in adc value
258
259 movw r17:r16,r11:r10 ; move adc sample to temporary register
260 mov r20,r19
261 sub r20,r0 ; find difference between adc sample and desired buffer size
262 sbc r16,r26
263 sbc r17,r27
264 brsh check_000035 ; check for deadband if positive
265 com r20 ; invert if negative
266 com r16 ; using ones complement as it is faster, and only has 1 bit error
267 com r17
268
269 check_000035: ; check if difference is greater than deadband
270
271 cpi r20,$80 ; check if difference is less than 1 lsb
272 cpc r16,r18 ; r18 cleared above
273 cpc r17,r18
274 brlo empty_000035 ; do nothing if less than 1 lsb
275 movw r27:r26,r11:r10 ; move adc sample to delay time if large enough change
276 andi r19,$fc ; make sure buffer size is a multiple of 4
277 mov r0,r19
278
279 empty_000035: ; empty accumulation registers and finish off
280
281 clr r10 ; empty accumulation registers
282 clr r11
283 clr r19
284
285 switchsample_000035: ; check if at same function
286
287 lds r16,pinj ; get switch data
288 andi r16,$78 ; mask off rotary switch
289 lsr r16 ; adjust switch position to program memory location
290 lsr r16
291 ldi r17,$02
292 add r16,r17 ; move to jump register
293 cpse r16,r31 ; check if location has changed
294 clr r30 ; reset jump register to intial state
295 mov r31,r16
296
297 done_000035:
298
299 reti ; return to waiting
300