6s Mono Delay
This program performs a simple delay routine. It stores the incoming data from the codec, and plays back a delayed sample. The input data is mono, and is taken from the left channel. The output is also mono, and is presented on both the left and right channels. However, if you're using a mono plug, you can only mix in the dry signal on the left channel (as there is no input signal on the right).
The pot (MOD1) controls the delay time. It goes from 6ms to 6s. MOD2 does nothing for this program. Most analog delay pedals change the rate at which data is being clocked in and out to change the delay time. This gives a smooth change in audio when the delay is varied, although it also changes the frequency resolution of the signal. To change the delay time in the MICrODEC, the easiest way is to just jump to the new delay time, although this gives an audible pop as the data abruptly switches. In this program, we increment the delay time (or decrement it) at a rate of one sample per sample, until it matches what you want it to be. This gives the effect of frequency doubling (or reversing) during delay transitions, which can be interesting sonically.
1 ; program: delay-18b-pot-mono.asm 2 ; UID = 000033 - unique id to eliminate conflicts between variables 3 ; 18b address space (6s delay time) 4 ; mono data (left in only, output on left and right) 5 ; pot (MOD1) controlled delay time (6ms - 6s) 6 7 ; program overview 8 ; 9 ; data is read in from memory and written out the codec at the same time 10 ; new data is written to the memory from the codec. ADC0 (MOD1) is read 11 ; and averaged over 256 samples to reduce jitter. this value is subtracted 12 ; from the write address to create the desired read address. if the actual 13 ; read address doesnt match the desired read address, it is either 14 ; incremented or decremented by one sample each sample period until it 15 ; matches. this reduces noise during delay time transitions. 16 17 ; register usage - may be redefined in other sections 18 ; 19 ; r0 desired delay time fractional byte 20 ; r1 21 ; r2 left/right lsb out 22 ; r3 left/right msb out 23 ; r4 24 ; r5 25 ; r6 left lsb in 26 ; r7 left msb in 27 ; r8 28 ; r9 29 ; r10 adc accumulator lsb 30 ; r11 adc accumulator msb 31 ; r12 actual delay lsb 32 ; r13 actual delay msb 33 ; r14 adc sample counter 34 ; r15 switch sample counter 35 ; r16 temporary swap register 36 ; r17 temporary swap register 37 ; r18 null register 38 ; r19 adc accumulator fractional byte 39 ; r20 temporary swap register 40 ; r21 actual delay fractional byte 41 ; r22 write address third byte 42 ; r23 read address third byte 43 ; r24 write address lsb 44 ; r25 write address msb 45 ; r26 desired delay lsb 46 ; r27 desired delay msb 47 ; r28 read address lsb 48 ; r29 read address msb 49 ; r30 jump location for interrupt lsb 50 ; r31 jump location for interrupt msb 51 ; t 52 53 ;program starts here first time 54 ldi r30,$23 ; set jump location to program start 55 clr r24 ; clear write register 56 clr r25 57 ldi r22,$00 ; setup write address high byte 58 clr r18 ; setup r18 as null register for carry addition and ddr setting 59 ldi r17,$ff ; setup r17 for ddr setting 60 61 clear_000033: ; clear delay buffer 62 ; eliminates static when first switching to the delay setting 63 64 adiw r25:r24,$01 ; increment write register 65 adc r22,r18 ; increment write third byte 66 cpi r22,$04 ; check if full memory space has been cleared 67 breq cleardone_000033 ; continue until end of buffer reached 68 out portd,r24 ; set address 69 sts porth,r25 70 out portg,r22 ; pull ce low,we low,and set high bits of address 71 out ddra,r17 ; set porta as output for data write 72 out ddrc,r17 ; set portc as output for data write 73 out porta,r18 ; set data 74 out portc,r18 ; r18 is cleared above 75 sbi portg,portg2 ; pull we high to write 76 out ddra,r18 ; set porta as input for data lines 77 out ddrc,r18 ; set portc as input for data lines 78 rjmp clear_000033 ; continue clearing 79 80 cleardone_000033: ; reset registers 81 82 clr r24 ; clear write register 83 clr r25 84 ldi r22,$00 ; setup write address high byte 85 clr r28 ; set read address to minimum delay 86 ldi r29,$ff 87 ldi r23,$07 ; setup read address high byte 88 clr r21 ; set actual delay time to minimum delay 89 ldi r16,$01 90 mov r12,r16 91 clr r13 92 clr r2 ; initialize data output registers 93 clr r3 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,r3 ; send out left channel msb 100 cbi portb,portb0 101 102 ;increment sram addresses 103 adiw r25:r24,$01 ; increment write address 104 adc r22,r18 ; increment write third byte 105 andi r22,$03 ; mask off unsed bits 106 adiw r29:r28,$01 ; increment read address 107 adc r23,r18 ; increment read third byte 108 andi r23,$03 ; mask off unsed bits 109 ori r23,$04 ; set we bit for reading 110 111 wait1_000033: ; check if byte has been sent 112 113 in r17,spsr 114 sbrs r17,spif 115 rjmp wait1_000033 116 in r7,spdr ; recieve in left channel msb 117 out spdr,r2 ; send out left channel lsb 118 119 wait2_000033: ; check if byte has been sent 120 121 in r17,spsr 122 sbrs r17,spif 123 rjmp wait2_000033 124 in r6,spdr ; recieve in left channel lsb 125 out spdr,r3 ; send out right channel msb 126 127 ;write left channel data to sram 128 out portd,r24 ; set address 129 sts porth,r25 130 out portg,r22 ; pull ce low,we low,and set high bits of address 131 ldi r17,$ff 132 out ddra,r17 ; set porta as output for data write 133 out ddrc,r17 ; set portc as output for data write 134 out porta,r6 ; set data 135 out portc,r7 136 sbi portg,portg2 ; pull we high to write 137 out ddra,r18 ; set porta as input for data lines 138 out ddrc,r18 ; set portc as input for data lines 139 140 wait3_000033: ; check if byte has been sent 141 142 in r17,spsr 143 sbrs r17,spif 144 rjmp wait3_000033 145 in r9,spdr ; recieve in right channel msb 146 out spdr,r2 ; send out right channel lsb 147 148 ;get left channel data from sram 149 out portg,r23 ; pull ce low,we high,oe low, and set high bits of address 150 out portd,r28 ; set address 151 sts porth,r29 152 nop ; wait microcontroller setup time of 2 cycles 153 nop 154 in r2,pina ; get data 155 in r3,pinc ; get data 156 157 wait4_000033: ; check if byte has been sent 158 159 in r17,spsr 160 sbrs r17,spif 161 rjmp wait4_000033 162 in r8,spdr ; recieve in left channel lsb 163 164 ; get delay settings 165 lds r17,adcsra ; get adc control register 166 sbrs r17,adif ; check if adc conversion is complete 167 rjmp shift_000033 ; skip adc sampling 168 lds r16,adcl ; get low byte adc value 169 lds r17,adch ; get high byte adc value 170 add r19,r16 ; accumulate adc samples 171 adc r10,r17 172 adc r11,r18 ; r18 is cleared above 173 ldi r17,$f7 174 sts adcsra,r17 ; clear interrupt flag 175 dec r14 ; countdown adc sample clock 176 brne shift_000033 ; get delay time if its been long enough 177 ldi r17,$01 ; check if adc value is less than $000100 178 cp r19,r18 ; r18 is cleared above 179 cpc r10,r17 180 cpc r11,r18 181 brsh deadband_000033 ; check if adc value changed enough to update delay 182 inc r10 ; set minimum delay to $000100 = 6ms 183 clr r19 184 185 deadband_000033: ; check for change in adc value 186 187 movw r17:r16,r11:r10 ; move adc sample to temporary register 188 mov r20,r19 189 sub r20,r0 ; find difference between adc sample and desired delay time 190 sbc r16,r26 191 sbc r17,r27 192 brsh check_000033 ; check for deadband if positive 193 com r20 ; invert if negative 194 com r16 ; using ones complement as it is faster, and only has 1 bit error 195 com r17 196 197 check_000033: ; check if difference is greater than deadband 198 199 cpi r16,$01 ; check if difference is less than 1 lsb 200 cpc r17,r18 ; r18 cleared above 201 brlo empty_000033 ; do nothing if less than 1 lsb 202 movw r27:r26,r11:r10 ; move adc sample to delay time if large enough change 203 andi r19,$fe ; make sure delay time is even 204 mov r0,r19 205 206 empty_000033: ; empty accumulation registers and finish off 207 208 clr r10 ; empty accumulation registers 209 clr r11 210 clr r19 211 212 shift_000033: ; check if delay time is correct 213 214 cp r0,r21 ; compare desired delay to actual delay 215 cpc r26,r12 216 cpc r27,r13 217 breq switchsample_000033 ; do nothing if the same 218 brlo indexdown_000033 219 ldi r17,$02 ; increment delay register 220 add r21,r17 ; this doubles playback speed until desired delay is reached 221 adc r12,r18 ; r18 is cleared above 222 adc r13,r18 223 ldi r17,$03 224 and r13,r17 ; mask off unused bits 225 rjmp switchsample_000033 226 227 indexdown_000033: 228 229 subi r21,$01 ; decrement delay register 230 sbc r12,r18 ; r18 is cleared above 231 sbc r13,r18 ; this plays backwards until desired delay is reached 232 ldi r17,$03 233 and r13,r17 ; mask off unused bits 234 235 switchsample_000033: ; check state of rotary switch 236 237 dec r15 238 brne done_000033 239 lds r16,pinj ; get switch data 240 andi r16,$78 ; mask off rotary switch 241 lsr r16 ; adjust switch position to program memory location 242 lsr r16 243 ldi r17,$02 244 add r16,r17 245 cp r16,r31 ; check if location has changed 246 breq done_000033 ; finish off if no change 247 clr r30 ; reset jump register to new function if it changed 248 mov r31,r16 249 250 done_000033: 251 252 movw r29:r28,r25:r24 ; move write address to read destination register 253 mov r23,r22 ; move write third byte to read third byte 254 sub r28,r21 ; subtract delay time 255 sbc r29,r12 256 sbc r23,r13 257 andi r23,$03 ; mask off unsed bits 258 ori r23,$04 ; set we bit for reading 259 reti 260