1

MICrODEC: Stock Functions

700ms Stereo 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 stereo, and is taken from both the left and right channels. The output is also stereo, and is presented on both the left and right channels. This program only uses the first 16b of the SRAM address space. This reduces the total possible delay time, but also makes the math and data fetches much, much easier. Unless the full 6s (3s stereo) sample time is required, its best to only use the 16b address space.

The pot (MOD1) controls the delay time. It goes from 3ms to 700ms. 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.

delay_700ms_stereo.asm


   1 ; program: delay-16b-pot.asm
   2 ; UID = 000003 - unique id to eliminate conflicts between variables
   3 ; 16b address space (.7s delay time)
   4 ; stereo data
   5 ; pot (MOD1) controlled delay time (3ms - 700ms)
   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  
  20 ; r1  
  21 ; r2  left lsb out
  22 ; r3  left msb out
  23 ; r4  right lsb out
  24 ; r5  right msb out
  25 ; r6  left lsb in
  26 ; r7  left msb in
  27 ; r8  right lsb in
  28 ; r9  right msb in
  29 ; r10 adc accumulator fractional byte
  30 ; r11 adc accumulator lsb
  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 
  38 ; r19 adc accumulator msb
  39 ; r20 
  40 ; r21 
  41 ; r22 null register
  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,$25 ; 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_000003: ; 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,$01 ; check if 16b memory space has been cleared
  67 breq cleardone_000003 ; 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_000003 ; continue clearing
  79 
  80 cleardone_000003: ; reset registers
  81 
  82 clr r24 ; clear write register
  83 clr r25
  84 ldi r22,$00 ; setup null register
  85 clr r28 ; set read address to minimum delay
  86 ldi r29,$ff
  87 ldi r23,$04 ; setup read address high byte
  88 clr r21 ; set actual delay time to minimum delay
  89 ldi r16,$01
  90 mov r13,r16
  91 clr r12
  92 clr r2 ; initialize data output registers
  93 clr r3
  94 clr r4
  95 clr r5
  96 reti ; finish with initialization and wait for next interrupt
  97 
  98 ;program starts here every time but first
  99 ;initiate data transfer to codec
 100 sbi portb,portb0 ; toggle slave select pin
 101 out spdr,r3 ; send out left channel msb
 102 cbi portb,portb0
 103 
 104 ;increment sram address
 105 adiw r25:r24,$01 ; increment write address
 106 adiw r29:r28,$01 ; increment read address
 107 
 108 wait1_000003: ; check if byte has been sent
 109 
 110 in r17,spsr
 111 sbrs r17,spif
 112 rjmp wait1_000003
 113 in r7,spdr ; recieve in left channel msb
 114 out spdr,r2 ; send out left channel lsb
 115 
 116 ;get left channel data from sram
 117 out portd,r28 ; set address
 118 sts porth,r29
 119 nop ; wait input latch time of 2 clock cycles
 120 nop ; wait input latch time of 2 clock cycles
 121 in r2,pina ; get data
 122 in r3,pinc ; get data
 123 adiw r29:r28,$01 ; increment read address
 124 
 125 wait2_000003: ; check if byte has been sent
 126 
 127 in r17,spsr
 128 sbrs r17,spif
 129 rjmp wait2_000003
 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,r24 ; set address
 135 sts porth,r25
 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_000003: ; check if byte has been sent
 147 
 148 in r17,spsr
 149 sbrs r17,spif
 150 rjmp wait3_000003
 151 in r9,spdr ; recieve in right channel msb
 152 out spdr,r4 ; send out right channel lsb
 153 
 154 ;get right channel data from sram
 155 out portd,r28 ; set address
 156 sts porth,r29
 157 nop ; wait input latch time of 2 clock cycles
 158 nop ; wait input latch time of 2 clock cycles
 159 in r4,pina ; get data
 160 in r5,pinc ; get data
 161 adiw r25:r24,$01 ; increment write address
 162 
 163 wait4_000003: ; check if byte has been sent
 164 
 165 in r17,spsr
 166 sbrs r17,spif
 167 rjmp wait4_000003
 168 in r8,spdr ; recieve in left channel lsb
 169 
 170 ;write right channel data to sram
 171 out portd,r24 ; set address
 172 sts porth,r25
 173 out portg,r22 ; pull ce low,we low,and set high bits of address
 174 ldi r17,$ff
 175 out ddra,r17 ; set porta as output for data write
 176 out ddrc,r17 ; set portc as output for data write
 177 out porta,r8 ; set data
 178 out portc,r9
 179 sbi portg,portg2 ; pull we high to write
 180 out ddra,r22 ; set porta as input for data lines
 181 out ddrc,r22 ; set portc as input for data lines
 182 
 183 ;get delay settings
 184 lds r17,adcsra ; get adc control register
 185 sbrs r17,adif ; check if adc conversion is complete
 186 rjmp shift_000003 ; skip adc sampling
 187 lds r16,adcl ; get low byte adc value
 188 lds r17,adch ; get high byte adc value
 189 add r10,r16 ; accumulate adc samples
 190 adc r11,r17
 191 adc r19,r22 ; r22 is cleared above
 192 ldi r17,$f7
 193 sts adcsra,r17 ; clear interrupt flag
 194 dec r14 ; countdown adc sample clock
 195 brne shift_000003 ; get delay time if its been long enough
 196 lsr r19 ; divide adc sample by 4
 197 ror r11 ; makes adc value a 16b number
 198 ror r10
 199 lsr r19
 200 ror r11
 201 ror r10
 202 ldi r16,$01 ; check if delay is below minimum
 203 cp r10,r22 ; r22 is cleared above
 204 cpc r11,r16
 205 brsh deadband_000003 ; check if adc value changed enough to update delay
 206 clr r10 ; set minimum delay to $0100 = 3ms
 207 mov r11,r16
 208 
 209 deadband_000003: ; set the low value of the delay
 210 
 211 movw r17:r16,r11:r10 ; move adc sample to temporary register
 212 sbc r16,r26 ; find difference between adc sample and desired delay time
 213 sbc r17,r27
 214 brsh check_000003 ; check for deadband if positive
 215 neg r16 ; invert if negative
 216 adc r17,r22 ; r22 is cleared above
 217 neg r17 ; converts ones complement to twos complement
 218 
 219 check_000003: ; check if difference is greater than deadband
 220 
 221 cpi r16,$40 ; check if difference is less than 1 lsb of adc
 222 cpc r17,r22 ; r22 cleared above
 223 brlo empty_000003 ; do nothing if less than 1 lsb
 224 movw r27:r26,r11:r10 ; move adc sample to delay time if large enough change
 225 andi r26,$fc ; make sure delay time is a multiple of 4
 226 
 227 empty_000003: ; empty accumulation registers and finish off
 228 
 229 clr r10 ; empty accumulation registers
 230 clr r11
 231 clr r19
 232 
 233 shift_000003: ; check if delay time is correct
 234 
 235 cp r26,r12 ; compare desired delay to actual delay
 236 cpc r27,r13
 237 breq switchsample_000003 ; do nothing if the same
 238 brlo indexdown_000003
 239 ldi r17,$04 ; increment delay register (4 is used for stereo data)
 240 add r12,r17 ; this makes it play forward at double speed
 241 adc r13,r22 ; until desired delay is reached
 242 rjmp switchsample_000003
 243 
 244 indexdown_000003:
 245 
 246 ldi r17,$02 ; decrement delay register (2 is used for stereo data)
 247 sub r12,r17 ; this makes is play backwards until desired delay is reached
 248 sbc r13,r22 ; r22 is cleared above
 249 
 250 switchsample_000003: ; check state of rotary switch
 251 
 252 dec r15 ; countdown switch counter
 253 brne done_000003 ; finish off if not ready yet
 254 lds r16,pinj ; get switch data
 255 andi r16,$78 ; mask off rotary switch
 256 lsr r16 ; adjust switch position to program memory location
 257 lsr r16
 258 ldi r17,$02
 259 add r16,r17
 260 cp r16,r31 ; check if location has changed
 261 breq done_000003 ; finish off if no change
 262 clr r30 ; reset jump register to new function if changed
 263 mov r31,r16
 264 
 265 done_000003:
 266 
 267 movw r29:r28,r25:r24 ; move write address to read destination register
 268 sub r28,r12 ; remove delay time
 269 sbc r29,r13
 270 reti ; return to waiting
 271