Size: 19920
Comment:
|
Size: 21813
Comment:
|
Deletions are marked like this. | Additions are marked like this. |
Line 17: | Line 17: |
; * pot controlled delay time (0s - .7s) * | ; * rotary encoder (MOD2) delay time (0s - .7s) * |
Line 24: | Line 24: |
; * by the potentiometer (MOD1) on ADC0. the ADC is sampled 256 times, and * ; * the value is averaged to get the full 10 bit depth. this value is then * ; * compared to the current value, and a deadband of 1 lsb is used to * ; * eliminate glitches. if a large enough change has occurred to warrant * ; * updating the delay time, the value is stored as the desired delay. the * ; * actual delay is compared to this desired delay once per sample period, * ; * and the actual delay is either incremented or decremented to bring them * ; * closer together. the delay time is either incremented by 2 samples, or * ; * decremented by 1 sample, as the memory data pointer is always moving * ; * forward by 1 sample. this gives a relative increase of 1 sample or * ; * decrease of 2 samples, basically playing the memory forward at double * ; * speed, or going backwards at normal speed, until the desired delay time * ; * is reached. this process eliminates pops when the potentiometer (MOD1) * ; * is turned. * |
; * by the rotary encoder (MOD2) on PORTJ. if the rotary encoder is turned * ; * to the right, the delay time is increased by a fixed amount. if it is * ; * turned to the left, the delay time is decreased by a fixed amount. the * ; * rotary encoder only changes the desired delay time, and once each sample * ; * sample period, the desired delay is compared to the actual delay. if * ; * they are not equal, the actual delay is either incremented by 2 samples * ; * decreased by one sample. since the memory pointers are incrementing by * ; * 1 sample each sample period, this gives an effective increase of 1 * ; * sample, or decrease of 2 samples per period. this has the effect of * ; * doubling the read rate to go forward, or playing in reverse to go * ; * backwards. this eliminates the audio glitches of quickly jumping to the * ; * new location in the SRAM. * |
Line 53: | Line 51: |
; * r12 desired delay lsb * | ; * r12 * |
Line 56: | Line 54: |
; * r15 switch/adc sample counter * | ; * r15 switch/rotary encoder sample counter * |
Line 77: | Line 75: |
; * NOTES: - all unprocessed (dry) raw data comes in off of the SPI port, via spdr ; - all processed (wet) data goes out over the SPI port, also via spdr ; - all external memory addressing happens over ports D and H ; - all data we are saving and reading back (like our delay audio) travels over ports A and C |
; * NOTES: - all unprocessed (dry) audio data comes in from the SPI port, via ; the SPDR i/o register ; - all processed (wet) audio goes out over the SPI port, also via ; the SPDR i/o register ; - all external memory addressing happens over the PORTD and PORTH ; i/o registers ; - all external memory control happens over PORTG i/o register ; - all data we are saving and reading back (like our delayed audio) ; goes out over the PORTA and PORTC i/o registers, and comes in ; via the PINA and PINC i/o registers |
Line 90: | Line 91: |
; initiate data transfer to codec sbi portb, portb0 ; set bit portb0 (aka DACLRC aka DAC sample rate left/right clock) |
; ### initiate data transfer to the codec sbi portb, portb0 ; set bit PORTB0 (aka DACLRC - DAC sample rate ; left/right clock) |
Line 95: | Line 97: |
; "DACLRC is an alignment clock that controls whether Left or Right channel ; data is present on DACTAT" ; (now on SPI - LEFT MSB) ********************************************************************************************* out spdr, r3 ; send out processed left channel msb (or whatever's currently in r3) to SPI data register cbi portb, portb0 ; clear DACLRC adiw r25:r24, 0x01 ; increment write address pointer by 0x01 adiw r29:r28, 0x01 ; increment read address pointer by 0x01 ldi r22, 0x00 ; setup write high byte with null |
; "DACLRC is an alignment clock that controls ; whether Left or Right channel data is present ; on DACDAT" ; ### now on SPI - LEFT MSB OUT out spdr, r3 ; send out processed left channel msb (or ; whatever's currently in r3) to SPI data register cbi portb, portb0 ; clear DACLRC adiw r25:r24, 0x01 ; increment write address pointer by 1 adiw r29:r28, 0x01 ; increment read address pointer by 1 ldi r22, 0x00 ; setup null register |
Line 112: | Line 116: |
wait1_UID: ; wait to see if processed left channel msb has been sent in r17, spsr ; move spi status register to r17 so we can look at it sbrs r17, spif ; skip next instruction if the SPIF (spi interrupt flag) bit is set |
wait1_UID: ; wait to see if processed left channel msb has ; been sent in r17, spsr ; move SPSPR i/o register (SPI status register) to ; r17 so we evaluate it sbrs r17, spif ; skip next instruction if the SPIF (SPIF interrupt ; flag) bit is set |
Line 117: | Line 124: |
; "When a serial transfer is complete, the SPIF flag is set" rjmp wait1_UID ; aka: loop until SPIF is set |
; "When a serial transfer is complete, the SPIF ; flag is set" rjmp wait1_UID ; keep checking until SPIF is set |
Line 121: | Line 129: |
; ### after wait1, new spi data ready in r7, spdr ; move spi data register into r7 (remember spdr is read/write, page 162) ; this moves the incoming (dry) left channel msb to r7 ; (end of wet/dry left msb transfer) *********************************************************************************** ; (now on SPI - LEFT LSB) *********************************************************************************** |
; ### after wait1, new SPI data ready in r7, spdr ; move SPIDR (SPI data register) into r7 ; (remember SPDR is read/write, page 162) ; this moves the incoming (dry) left channel msb ; to r7 ; ### end of wet/dry left msb transfer ; ### now on SPI - LEFT LSB OUT |
Line 134: | Line 143: |
out portd, r28 ; move read address lsb pointer to portd (see schematic) sts porth, r29 ; move read address msb pointer to portH ; **NOTE: - this is a special instruction because the I/O portH register is wayyy up there in the sram ; - 'out' completes in one cycle, 'sts' completes in two |
out portd, r28 ; move read address lsb pointer to PORTD sts porth, r29 ; move read address msb pointer to PORTH ; **NOTE: - this is a special instruction because ; the i/o PORTH register is wayyy up there in the ; sram - 'out' completes in one cycle, 'sts' ; completes in two |
Line 142: | Line 153: |
; DING! your data is ready, please pick it up! in r2, pina ; (see schematic) D0-7, aka lsb, is on port A, now stored in r2 in r3, pinc ; and D8-15, aka msb, is on port C, now stored in r3 ; ### left channel SRAM retrieve completed |
; DING! your data is ready, please pick it up! in r2, pina ; D0-7, aka lsb, is on PORTA, now stored in r2 in r3, pinc ; D8-15, aka msb, is on PORTC, now stored in r3 ; ### left channel SRAM retrieve completed |
Line 158: | Line 169: |
in r17, spsr ; spi status reg. to r17 (looking familiar?) | in r17, spsr ; SPI status register to r17 (looking familiar?) |
Line 163: | Line 174: |
; - it can only move within 2k of memory (ie, can't jump all the way to the start, or end, of a long program) ; - it doesn't do anything to the stack (so if it loops a thousand times, the stack isn't going to overflow) ; - it takes two cycles ; ### after wait2 is finished, new spi data ready |
; - it can only move within 2k of memory (ie, ; can't jump all the way to the start, or end, ; of a long program) ; - it doesn't do anything to the stack (so if ; it loops a thousand times, the stack isn't ; going to overflow) ; - it takes two cycles ; ### after wait2 is finished, new SPI data ready |
Line 170: | Line 185: |
; **NOTE: that gives us both bytes of the incoming left channel, and both wet bytes have been sent ; we can now move on to the right channel ; (end of wet/dry left lsb transfer) ************************************************************************************ ; (now on SPI - RIGHT MSB) ************************************************************************************ |
; **NOTE: that gives us both bytes of the ; incoming left channel, and both wet bytes have ; been sent so we can now move on to the right ; channel ; ### end of wet/dry left lsb transfer ; ### now on SPI - RIGHT MSB OUT |
Line 180: | Line 196: |
out portd, r24 ; update data address ports to reflect where we want to write |
out portd, r24 ; update data address ports to reflect where we ; want to write |
Line 183: | Line 201: |
out portg, r22 ; woah, portG? what's this? Check out the SRAM data sheet and the schematic: ; r22 is currently 0x00, so we are pulling CE and WE low, and writing our high address bits to zero ; CE (chip enable) and WE (write enable) are asserted low |
out portg, r22 ; woah, PORTG? what's this? Check out the SRAM ; data sheet and the schematic: ; r22 is currently 0x00, so we are pulling CE ; (Chip Enable) and WE (Write Enable) low, and ; writing our high address bits to zero |
Line 188: | Line 209: |
; "Data on input pins io1-io16 is written on the rising edge of WE..." ; "To avoid bus contention, external devices should drio i/o pins only after ; outputs have been disabled with OE (output enable) or WE" ; "A read cycle is accomplished by asserting OE and CE, with WE high." ldi r17, 0xFF ; prepare a salad of ones out ddra, r17 ; send them to the portA direction register out ddrC, r17 ; and to the portC direction register ; (see ATmega3250P data sheet, page 65, 'Switching Between Input and Output') out porta, r6 ; with these two 'out' instructions out portc, r7 ; we send left channel dry data directly to the sram sbi portg, portg2 ; and as soon as we pull WE (write enable) on portG_2 high, ; zzztt! a single audio sample is written into memory, just like that. |
; "Data on input pins IO1-IO16 are written on ; the rising edge of WE..." ; "To avoid bus contention, external devices ; should drive IO pins only after outputs have ; been disabled with OE (output enable) or WE" ; "A read cycle is accomplished by asserting OE ; and CE, with WE high." ldi r17, 0xFF ; prepare a salad of ones out ddra, r17 ; send them to the PORTA direction register out ddrC, r17 ; and to the PORTC direction register ; to set the ports as output ; see ATmega3250P data sheet, page 65, ; 'Switching Between Input and Output' out porta, r6 ; with these two 'out' instructions out portc, r7 ; we send left channel dry data to the SRAM sbi portg, portg2 ; and as soon as we pull WE on PORTG2 high, ; zzztt! a single audio sample is written into ; memory, just like that. |
Line 209: | Line 235: |
out ddra, r22 ; now, we happen to know that r22 contains a bunch of zeros out ddrc, r22 ; so we can use that register to set ports A and C back to an input state. |
out ddra, r22 ; now, we happen to know that r22 contains a out ddrc, r22 ; bunch of zeros so we can use that register to ; set PORTA and PORTC back to an input state. |
Line 220: | Line 244: |
wait3_UID: ; meanwhile, back at the codec, we are still transferring the right channel lsb in r17, spsr ; check out the spsr, check out the spif, sbrs r17, spif ; we know what we're doing here now, right? rjmp wait3_UID ; loop until transfer completed |
wait3_UID: ; meanwhile, back at the codec, we are still ; transferring the right channel lsb in r17, spsr ; check out the SPSR, check out the SPIF, sbrs r17, spif ; we know what we're doing here now, right? rjmp wait3_UID ; loop until transfer completed |
Line 228: | Line 253: |
in r9, spdr ; recieve in (dry) right channel msb ; (end of wet/dry right msb transfer) *********************************************************************************** ; (now on SPI - RIGHT LSB) *********************************************************************************** out spdr, r4 ; send out (wet) right channel lsb |
in r9, spdr ; recieve in (dry) right channel msb ; ### end of wet/dry right msb transfer ; ### now on SPI - RIGHT LSB OUT out spdr, r4 ; send out (wet) right channel lsb |
Line 237: | Line 261: |
out portd, r28 ; set up the address we want to read from sts porth, r29 ; on ports d and h nop ; killing time again... nop ; two cycles, while the SRAM latches the address ; and once more, ding! our data is now waiting on the SRAM data lines in r4, pina ; right lsb in r5, pinc ; right msb (this is audio data we saved in the past) |
out portd, r28 ; set up the address we want to read from sts porth, r29 ; on PORTD and PORTH nop ; killing time again... nop ; two cycles, while the SRAM latches the address ; and once more, ding! our data is now waiting ; on the SRAM data lines in r4, pina ; right lsb in r5, pinc ; right msb (this is audio data we saved in the ; past) |
Line 249: | Line 276: |
adiw r25:r24, 0x01 ; increment the write address (we're going to be using that pointer next) |
adiw r25:r24, 0x01 ; increment the write address (we're going to be ; using that pointer next) |
Line 256: | Line 284: |
wait4_UID: ; checking up on that SPI transfer | wait4_UID: ; checking up on that SPI transfer |
Line 260: | Line 288: |
rjmp wait4_UID ; loop until SPIF is set | rjmp wait4_UID ; loop until SPIF is set |
Line 263: | Line 291: |
in r8, spdr ; bring in right channel lsb ; (end of wet/dry right lsb transfer) *********************************************************************************** ; now that we have both bytes of the right channel dry audio, ; we can store it in the SRAM... |
in r8, spdr ; bring in right channel lsb ; ### end of wet/dry right lsb transfer ; now that we have both bytes of the right ; channel dry audio, we can store it in the ; SRAM... |
Line 271: | Line 302: |
out portd, r24 ; give the sram the write address, lsb sts porth, r25 ; now msb out portg, r22 ; pull WE low, (CE is already low, WE was high for read operations) ldi r17, 0xFF ; prepare a bevy of ones out ddra, r17 ; set porta as output out ddrc, r17 ; set portc as output out porta, r8 ; put the data on the ports, lsb out portc, r9 ; msb sbi portg, portg2 ; zzzt! pull WE high and write the data out ddra, r22 ; reconfigure porta as input out ddrc, r22 ; reconfigure portc as input ; ********************************************************************************** |
out portd, r24 ; give the SRAM the write address, lsb sts porth, r25 ; now msb out portg, r22 ; pull WE low, (CE is already low, WE was high ; for read operations) ldi r17, 0xFF ; prepare a bevy of ones out ddra, r17 ; set PORTA as output out ddrc, r17 ; set PORTC as output out porta, r8 ; put the data on the ports, lsb out portc, r9 ; msb sbi portg, portg2 ; zzzt! pull WE high and write the data out ddra, r22 ; reconfigure PORTA as input out ddrc, r22 ; reconfigure PORTC as input ; **************************************************************************** |
Line 288: | Line 320: |
; ********************************************************************************** ; * The rotary encoder is externally debounced, so we don't have to do that here. ; * You'll see it in the schematic labeled MOD2 on portJ0, portJ1, and portJ2. |
; **************************************************************************** ; * The rotary encoder is externally debounced, so we don't have to do that ; * here. You'll see it in the schematic labeled MOD2 on PORTJ0, PORTJ1, and ; * PORTJ2. |
Line 292: | Line 325: |
; * The encoder's 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 r15 ; well, let's do a little debouncing anyway brne adjust_UID ; brne checks the Z register, ; if r15 was not zero after the last operation, it will branch us to adjust_UID ldi r17, 0x40 ; prepare a constant in r17 mov r15, r17 ; put 0x40 in the sample freq bin to catch all rising edges (results in 1.5ms sampletime) lds r17, pinj ; move port J data into r17 sbrs r17, PINJ0 ; skip next if PINJ0 is set rjmp edgecheck_UID ; if it's not set, is it a falling edge? clt ; clear T reg (in SREG) rjmp switchsample_UID ; done looking at mod2, look at the program selector |
; * The encoder's pin1 is sampled on a transition from high to low on PINJ0. ; * if PINJ1 is high, a left turn occurred, if PINJ1 is low, a right turn ; * occurred. ; **************************************************************************** dec r15 ; well, let's do a little debouncing anyway brne adjust_UID ; brne checks the Z (zero) register, ; if r15 was not zero after the last operation, ; it will branch us to adjust_UID to check if the ; current delay matches the desired delay ldi r17, 0x40 ; prepare a constant in r17 mov r15, r17 ; put 0x40 in the sample register to set the ; rotary encoder sample counter. this is set to ; catch all rising edges (~1.5ms sample time) lds r17, pinj ; move PORTJ data into r17 sbrs r17, PINJ0 ; skip next if PINJ0 is set rjmp edgecheck_UID ; if it's not set, is it a falling edge? clt ; clear T reg (in SREG) to indicate PINJ0 is high rjmp switchsample_UID ; done looking at MOD2, look at the function ; selector switch |
Line 313: | Line 352: |
edgecheck_UID: ; checks for falling edge brts switchsample_UID ; if the T flag in SREG is set, assume the edge was already detected set ; otherwise set the T flag sbrs r17, PINJ1 ; check if PINJ1 is high rjmp upcount_UID ; if PINJ0 has just gone low and PINJ1 is low, a right turn has transpired ; upcount will therefore increase the delay ; otherwise, PINJ1 is high and a left turn has transpired, so we should decrease the delay ; **** downcount ldi r17, 0x01 ; load our decrement amount into r17 (0x01 = 256 samples = 0.006s) sub r13, r17 ; decrement delay MSB rjmp switchsample_UID ; done looking at mod2, look at the program selector ; **** upcount upcount_UID: ; increment delay register ldi r17, 0x01 ; load increment amount into r17 add r13, r17 ; increment MSB |
edgecheck_UID: ; checks for falling edge brts switchsample_UID ; if the T flag in SREG is set, PINJ0 was low ; on a previous sample - so its not an edge set ; otherwise set the T flag to indicate a falling ; edge sbrs r17, PINJ1 ; check if PINJ1 is high rjmp upcount_UID ; if PINJ0 has just gone low and PINJ1 is low, ; a right turn has transpired and ; upcount will therefore increase the delay ; otherwise, PINJ1 is high and a left turn has ; transpired, so we should decrease the delay ; ### downcount ldi r17, 0x01 ; load our decrement amount into r17 ; (0x01 = 256 samples = 0.006s) sub r13, r17 ; decrement desired delay MSB ; **NOTE: there is no overflow checking here, so ; the count will wrap around if it goes too low rjmp switchsample_UID ; done looking at MOD, look at the function ; selector switch ; ### upcount upcount_UID: ; increment desired delay register ldi r17, 0x01 ; load increment amount into r17 add r13, r17 ; increment MSB ; **NOTE: there is no overflow checking here, so ; the count will wrap around if it goes too high |
Line 336: | Line 387: |
switchsample_UID: ; sample the program select dial lds r31, pinj ; put switch data into jump location MSB reg andi r31, 0x78 ; mask off rotary encoder 0b01111000 ldi r17, 0x02 ; 0x02 into r17 lsr r31 ; shift r31 to the right lsr r31 ; shift again add r31, r17 ; convert switch position data to program memory location |
switchsample_UID: ; sample the function selector switch ; done at the same rate as the rotary encoder lds r31, pinj ; put switch data into jump location MSB reg andi r31, 0x78 ; mask off all but rotary switch bits 0b01111000 ; ### convert switch position data to program memory location lsr r31 ; shift switch bits to the right lsr r31 ; shift again (multiply by 4) ldi r17, 0x02 ; prepare a constant to add to the switch data add r31, r17 ; add 2 to the memory position to get past the ; data stored for the main file |
Line 350: | Line 406: |
adjust_UID: ; since we've only changed the desired delay in the previous section, we need to implement that delay ; this checks to see if the delay time is correct, ; and if it's not it makes an effort to move slightly closer to the correct delay andi r26, 0xFE ; is the delay time even? cp r26, r22 ; compare actual delay lsb to zero cpc r27, r13 ; compare with carry actual delay msb with desired delay msb breq done_UID ; If equal, head to done, yay! brsh indexdown_UID ; If the same or higher, branch to indexdown ; otherwise, we can assume it is too low ; **** indexup adiw r27:r26, 0x04 ; so increment delay register by 0x04 rjmp done_UID ; and head to the end ; **** indexdown |
adjust_UID: ; since we've only changed the desired delay in ; the previous section, we need to implement that ; delay ; this checks to see if the delay time is correct, ; and if it's not it makes an effort to move ; slightly closer to the correct delay andi r26, 0xFE ; this sets the delay time to an even number by ; forcing the last bit to be zero. this is ; necessary because we have stereo data, and every ; two memory positions represent one moment in time cp r26, r22 ; compare actual delay lsb to zero (null register) cpc r27, r13 ; compare with carry actual delay msb with desired ; delay msb breq done_UID ; If equal, head to done, yay! brsh indexdown_UID ; If the same or higher, branch to indexdown ; otherwise, we can assume it is too low ; ### indexup adiw r27:r26, 0x04 ; so increment delay register by 0x04 (2 sample ; periods times 2 (stereo) data entries) rjmp done_UID ; and head to the end ; ### indexdown |
Line 366: | Line 433: |
sbiw r27:r26, 0x02 ; decrement delay reg by 0x02 |
sbiw r27:r26, 0x02 ; decrement delay reg by 0x02 (1 sample period ; times 2 (stereo) data entries) |
Line 374: | Line 442: |
done_UID: ; It's been a long hard row to hoe, but we did it! ; oh wait, what? We're going to have to do it again? from the beginning? ; but we still need to get our pointers all lined up! movw r29:r28, r25:r24 ; sync write destination and read address sub r28, r26 ; now subtract by the delay in samples (first lsb) sbc r29, r27 ; subtract msb with carry from previous reti ; return from interrupt so we can get back to our idling |
done_UID: ; It's been a long hard row to hoe, but we did it! ; oh wait, what? We're going to have to do it ; again? from the beginning? ; but we still need to get our memory pointers all ; lined up! movw r29:r28, r25:r24 ; sync write destination and read address sub r28, r26 ; now subtract by the delay in samples (first lsb) sbc r29, r27 ; subtract msb with carry from previous reti ; return from interrupt so we can get back to our ; idling |
DelayTutorial.asm
Writing code in assembly is a lot like that riddle about the farmer who has a chicken, a fox, and a bag of corn that he's trying to take across a river, but he only has one boat to do it with.
But on the bright side, none of our registers are going to eat each other, and we're not going to be attacked by any foxes! (i hope)
1 ; ****************************************************************************
2 ; * program: delay-16b-rotary.asm *
3 ; * UID = Tutorial - unique id to eliminate conflicts between variables, *
4 ; * replace each instance of UID in the code with this unique id. *
5 ; * 16b address space (.7s delay time) *
6 ; * stereo data *
7 ; * rotary encoder (MOD2) delay time (0s - .7s) *
8 ; ****************************************************************************
9 ; * *
10 ; * PROGRAM OVERVIEW *
11 ; * *
12 ; * data is read in from memory and written to the codec at the same time *
13 ; * new data is written to the memory from the codec. the delay time is set *
14 ; * by the rotary encoder (MOD2) on PORTJ. if the rotary encoder is turned *
15 ; * to the right, the delay time is increased by a fixed amount. if it is *
16 ; * turned to the left, the delay time is decreased by a fixed amount. the *
17 ; * rotary encoder only changes the desired delay time, and once each sample *
18 ; * sample period, the desired delay is compared to the actual delay. if *
19 ; * they are not equal, the actual delay is either incremented by 2 samples *
20 ; * decreased by one sample. since the memory pointers are incrementing by *
21 ; * 1 sample each sample period, this gives an effective increase of 1 *
22 ; * sample, or decrease of 2 samples per period. this has the effect of *
23 ; * doubling the read rate to go forward, or playing in reverse to go *
24 ; * backwards. this eliminates the audio glitches of quickly jumping to the *
25 ; * new location in the SRAM. *
26 ; * *
27 ; * register usage - may be redefined in other sections *
28 ; * *
29 ; * r0 *
30 ; * r1 *
31 ; * r2 left lsb out *
32 ; * r3 left msb out *
33 ; * r4 right lsb out *
34 ; * r5 right msb out *
35 ; * r6 left lsb in *
36 ; * r7 left msb in *
37 ; * r8 right lsb in *
38 ; * r9 right msb in *
39 ; * r10 *
40 ; * r11 *
41 ; * r12 *
42 ; * r13 desired delay msb *
43 ; * r14 *
44 ; * r15 switch/rotary encoder sample counter *
45 ; * r16 temporary swap register *
46 ; * r17 temporary swap register *
47 ; * r18 *
48 ; * r19 *
49 ; * r20 *
50 ; * r21 *
51 ; * r22 null register (always equals $00) *
52 ; * r23 *
53 ; * r24 write address lsb *
54 ; * r25 write address msb *
55 ; * r26 actual delay lsb *
56 ; * r27 actual delay msb *
57 ; * r28 read address lsb *
58 ; * r29 read address msb *
59 ; * r30 jump location for interrupt lsb *
60 ; * r31 jump location for interrupt msb *
61 ; * t *
62 ; ****************************************************************************
63
64
65 ; * NOTES: - all unprocessed (dry) audio data comes in from the SPI port, via
66 ; the SPDR i/o register
67 ; - all processed (wet) audio goes out over the SPI port, also via
68 ; the SPDR i/o register
69 ; - all external memory addressing happens over the PORTD and PORTH
70 ; i/o registers
71 ; - all external memory control happens over PORTG i/o register
72 ; - all data we are saving and reading back (like our delayed audio)
73 ; goes out over the PORTA and PORTC i/o registers, and comes in
74 ; via the PINA and PINC i/o registers
75
76
77 ; **********************
78 ; **** PROGRAM START
79 ; **********************
80
81 ; ### initiate data transfer to the codec
82
83 sbi portb, portb0 ; set bit PORTB0 (aka DACLRC - DAC sample rate
84 ; left/right clock)
85 ;
86 ; **NOTE: from WM8731 data sheet, page 36:
87 ; "DACLRC is an alignment clock that controls
88 ; whether Left or Right channel data is present
89 ; on DACDAT"
90
91 ; ### now on SPI - LEFT MSB OUT
92
93 out spdr, r3 ; send out processed left channel msb (or
94 ; whatever's currently in r3) to SPI data register
95 cbi portb, portb0 ; clear DACLRC
96
97 adiw r25:r24, 0x01 ; increment write address pointer by 1
98 adiw r29:r28, 0x01 ; increment read address pointer by 1
99 ldi r22, 0x00 ; setup null register
100
101
102 ; **********************
103 ; **** wait1
104 ; **********************
105
106 wait1_UID: ; wait to see if processed left channel msb has
107 ; been sent
108
109 in r17, spsr ; move SPSPR i/o register (SPI status register) to
110 ; r17 so we evaluate it
111 sbrs r17, spif ; skip next instruction if the SPIF (SPIF interrupt
112 ; flag) bit is set
113 ; **NOTE: from atmega3250P data sheet, page 162:
114 ; "When a serial transfer is complete, the SPIF
115 ; flag is set"
116 rjmp wait1_UID ; keep checking until SPIF is set
117 ; and then...
118
119 ; ### after wait1, new SPI data ready
120
121 in r7, spdr ; move SPIDR (SPI data register) into r7
122 ; (remember SPDR is read/write, page 162)
123 ; this moves the incoming (dry) left channel msb
124 ; to r7
125
126 ; ### end of wet/dry left msb transfer
127 ; ### now on SPI - LEFT LSB OUT
128
129 out spdr, r2 ; send the processed (wet) left lsb out
130
131 ; ### retrieve stored left channel data from SRAM:
132
133 out portd, r28 ; move read address lsb pointer to PORTD
134 sts porth, r29 ; move read address msb pointer to PORTH
135 ; **NOTE: - this is a special instruction because
136 ; the i/o PORTH register is wayyy up there in the
137 ; sram - 'out' completes in one cycle, 'sts'
138 ; completes in two
139
140 nop ; read address msb hits the port, now wait for (1)
141 nop ; a latch time of two cycles... (2)
142
143 ; DING! your data is ready, please pick it up!
144 in r2, pina ; D0-7, aka lsb, is on PORTA, now stored in r2
145 in r3, pinc ; D8-15, aka msb, is on PORTC, now stored in r3
146
147 ; ### left channel SRAM retrieve completed
148
149 ; now we've got a little time to kill, so...
150 adiw r29:r28, 0x01 ; increment read address pointer by 1
151
152
153 ; **********************
154 ; **** wait2
155 ; **********************
156
157 wait2_UID: ; wait to see if wet left channel lsb has been sent
158
159 in r17, spsr ; SPI status register to r17 (looking familiar?)
160 sbrs r17, spif ; skip next if SPIF bit not set...
161 rjmp wait2_UID ; loop until SPIF set
162
163 ; **NOTE: and now a couple of words about rjmp:
164 ; - it can only move within 2k of memory (ie,
165 ; can't jump all the way to the start, or end,
166 ; of a long program)
167 ; - it doesn't do anything to the stack (so if
168 ; it loops a thousand times, the stack isn't
169 ; going to overflow)
170 ; - it takes two cycles
171
172 ; ### after wait2 is finished, new SPI data ready
173
174 in r6, spdr ; receive in dry left channel lsb
175 ; **NOTE: that gives us both bytes of the
176 ; incoming left channel, and both wet bytes have
177 ; been sent so we can now move on to the right
178 ; channel
179
180 ; ### end of wet/dry left lsb transfer
181 ; ### now on SPI - RIGHT MSB OUT
182
183 out spdr, r5 ; send out (wet) right msb
184
185 ; ### writing (dry) left channel to SRAM:
186
187 out portd, r24 ; update data address ports to reflect where we
188 ; want to write
189 sts porth, r25 ; remember 'out' and 'sts'?
190
191 out portg, r22 ; woah, PORTG? what's this? Check out the SRAM
192 ; data sheet and the schematic:
193
194 ; r22 is currently 0x00, so we are pulling CE
195 ; (Chip Enable) and WE (Write Enable) low, and
196 ; writing our high address bits to zero
197
198 ; **NOTE: from AS7C4098 data sheet, page 2:
199 ; "Data on input pins IO1-IO16 are written on
200 ; the rising edge of WE..."
201
202 ; "To avoid bus contention, external devices
203 ; should drive IO pins only after outputs have
204 ; been disabled with OE (output enable) or WE"
205
206 ; "A read cycle is accomplished by asserting OE
207 ; and CE, with WE high."
208
209 ldi r17, 0xFF ; prepare a salad of ones
210 out ddra, r17 ; send them to the PORTA direction register
211 out ddrC, r17 ; and to the PORTC direction register
212 ; to set the ports as output
213 ; see ATmega3250P data sheet, page 65,
214 ; 'Switching Between Input and Output'
215
216 out porta, r6 ; with these two 'out' instructions
217 out portc, r7 ; we send left channel dry data to the SRAM
218
219 sbi portg, portg2 ; and as soon as we pull WE on PORTG2 high,
220 ; zzztt! a single audio sample is written into
221 ; memory, just like that.
222
223 ; ### left channel SRAM write completed
224
225 out ddra, r22 ; now, we happen to know that r22 contains a
226 out ddrc, r22 ; bunch of zeros so we can use that register to
227 ; set PORTA and PORTC back to an input state.
228
229
230 ; **********************
231 ; **** wait3
232 ; **********************
233
234 wait3_UID: ; meanwhile, back at the codec, we are still
235 ; transferring the right channel lsb
236
237 in r17, spsr ; check out the SPSR, check out the SPIF,
238 sbrs r17, spif ; we know what we're doing here now, right?
239 rjmp wait3_UID ; loop until transfer completed
240
241 ; ### end of wait3, new data ready!
242
243 in r9, spdr ; recieve in (dry) right channel msb
244
245 ; ### end of wet/dry right msb transfer
246 ; ### now on SPI - RIGHT LSB OUT
247
248 out spdr, r4 ; send out (wet) right channel lsb
249
250 ; ### retrieve stored right channel data from SRAM
251
252 out portd, r28 ; set up the address we want to read from
253 sts porth, r29 ; on PORTD and PORTH
254
255 nop ; killing time again...
256 nop ; two cycles, while the SRAM latches the address
257
258 ; and once more, ding! our data is now waiting
259 ; on the SRAM data lines
260 in r4, pina ; right lsb
261 in r5, pinc ; right msb (this is audio data we saved in the
262 ; past)
263
264 ; ### right channel SRAM data retrieval completed
265
266 adiw r25:r24, 0x01 ; increment the write address (we're going to be
267 ; using that pointer next)
268
269
270 ; **********************
271 ; **** wait4
272 ; **********************
273
274 wait4_UID: ; checking up on that SPI transfer
275
276 in r17, spsr
277 sbrs r17, spif
278 rjmp wait4_UID ; loop until SPIF is set
279
280 ; ### end of wait4, more new data!
281
282 in r8, spdr ; bring in right channel lsb
283
284 ; ### end of wet/dry right lsb transfer
285
286 ; now that we have both bytes of the right
287 ; channel dry audio, we can store it in the
288 ; SRAM...
289
290 ; ### writing (dry) right channel to SRAM
291
292 out portd, r24 ; give the SRAM the write address, lsb
293 sts porth, r25 ; now msb
294
295 out portg, r22 ; pull WE low, (CE is already low, WE was high
296 ; for read operations)
297 ldi r17, 0xFF ; prepare a bevy of ones
298 out ddra, r17 ; set PORTA as output
299 out ddrc, r17 ; set PORTC as output
300 out porta, r8 ; put the data on the ports, lsb
301 out portc, r9 ; msb
302 sbi portg, portg2 ; zzzt! pull WE high and write the data
303
304 out ddra, r22 ; reconfigure PORTA as input
305 out ddrc, r22 ; reconfigure PORTC as input
306
307
308 ; ****************************************************************************
309 ; **** check rotary encoder and adjust delay time
310 ; ****************************************************************************
311 ; * The rotary encoder is externally debounced, so we don't have to do that
312 ; * here. You'll see it in the schematic labeled MOD2 on PORTJ0, PORTJ1, and
313 ; * PORTJ2.
314 ; *
315 ; * The encoder's pin1 is sampled on a transition from high to low on PINJ0.
316 ; * if PINJ1 is high, a left turn occurred, if PINJ1 is low, a right turn
317 ; * occurred.
318 ; ****************************************************************************
319
320 dec r15 ; well, let's do a little debouncing anyway
321 brne adjust_UID ; brne checks the Z (zero) register,
322 ; if r15 was not zero after the last operation,
323 ; it will branch us to adjust_UID to check if the
324 ; current delay matches the desired delay
325
326 ldi r17, 0x40 ; prepare a constant in r17
327 mov r15, r17 ; put 0x40 in the sample register to set the
328 ; rotary encoder sample counter. this is set to
329 ; catch all rising edges (~1.5ms sample time)
330 lds r17, pinj ; move PORTJ data into r17
331 sbrs r17, PINJ0 ; skip next if PINJ0 is set
332 rjmp edgecheck_UID ; if it's not set, is it a falling edge?
333
334 clt ; clear T reg (in SREG) to indicate PINJ0 is high
335 rjmp switchsample_UID ; done looking at MOD2, look at the function
336 ; selector switch
337
338 ; **********************
339 ; **** edgecheck
340 ; **********************
341
342 edgecheck_UID: ; checks for falling edge
343 brts switchsample_UID ; if the T flag in SREG is set, PINJ0 was low
344 ; on a previous sample - so its not an edge
345 set ; otherwise set the T flag to indicate a falling
346 ; edge
347 sbrs r17, PINJ1 ; check if PINJ1 is high
348 rjmp upcount_UID ; if PINJ0 has just gone low and PINJ1 is low,
349 ; a right turn has transpired and
350 ; upcount will therefore increase the delay
351 ; otherwise, PINJ1 is high and a left turn has
352 ; transpired, so we should decrease the delay
353
354 ; ### downcount
355
356 ldi r17, 0x01 ; load our decrement amount into r17
357 ; (0x01 = 256 samples = 0.006s)
358 sub r13, r17 ; decrement desired delay MSB
359 ; **NOTE: there is no overflow checking here, so
360 ; the count will wrap around if it goes too low
361 rjmp switchsample_UID ; done looking at MOD, look at the function
362 ; selector switch
363
364 ; ### upcount
365
366 upcount_UID: ; increment desired delay register
367 ldi r17, 0x01 ; load increment amount into r17
368 add r13, r17 ; increment MSB
369 ; **NOTE: there is no overflow checking here, so
370 ; the count will wrap around if it goes too high
371
372
373 ; **********************
374 ; **** switchsample
375 ; **********************
376
377 switchsample_UID: ; sample the function selector switch
378 ; done at the same rate as the rotary encoder
379
380 lds r31, pinj ; put switch data into jump location MSB reg
381 andi r31, 0x78 ; mask off all but rotary switch bits 0b01111000
382
383 ; ### convert switch position data to program memory location
384
385 lsr r31 ; shift switch bits to the right
386 lsr r31 ; shift again (multiply by 4)
387 ldi r17, 0x02 ; prepare a constant to add to the switch data
388 add r31, r17 ; add 2 to the memory position to get past the
389 ; data stored for the main file
390
391
392 ; **********************
393 ; **** adjust
394 ; **********************
395
396 adjust_UID: ; since we've only changed the desired delay in
397 ; the previous section, we need to implement that
398 ; delay
399 ; this checks to see if the delay time is correct,
400 ; and if it's not it makes an effort to move
401 ; slightly closer to the correct delay
402
403 andi r26, 0xFE ; this sets the delay time to an even number by
404 ; forcing the last bit to be zero. this is
405 ; necessary because we have stereo data, and every
406 ; two memory positions represent one moment in time
407 cp r26, r22 ; compare actual delay lsb to zero (null register)
408 cpc r27, r13 ; compare with carry actual delay msb with desired
409 ; delay msb
410 breq done_UID ; If equal, head to done, yay!
411 brsh indexdown_UID ; If the same or higher, branch to indexdown
412 ; otherwise, we can assume it is too low
413
414 ; ### indexup
415
416 adiw r27:r26, 0x04 ; so increment delay register by 0x04 (2 sample
417 ; periods times 2 (stereo) data entries)
418 rjmp done_UID ; and head to the end
419
420 ; ### indexdown
421
422 indexdown_UID:
423
424 sbiw r27:r26, 0x02 ; decrement delay reg by 0x02 (1 sample period
425 ; times 2 (stereo) data entries)
426
427
428 ; **********************
429 ; **** done
430 ; **********************
431
432 done_UID: ; It's been a long hard row to hoe, but we did it!
433 ; oh wait, what? We're going to have to do it
434 ; again? from the beginning?
435 ; but we still need to get our memory pointers all
436 ; lined up!
437
438 movw r29:r28, r25:r24 ; sync write destination and read address
439 sub r28, r26 ; now subtract by the delay in samples (first lsb)
440 sbc r29, r27 ; subtract msb with carry from previous
441
442 reti ; return from interrupt so we can get back to our
443 ; idling
444