DelayTutorial.asm [Discussion of code elements, needed revision etc, can go here] {{{#!highlight nasm ; **************************************************************************** ; * program: delay-16b-rotary.asm * ; * UID = Tutorial - unique id to eliminate conflicts between variables, * ; * replace each instance of UID in the code with this unique id. * ; * 16b address space (.7s delay time) * ; * stereo data * ; * pot controlled delay time (0s - .7s) * ; **************************************************************************** ; * ; * PROGRAM OVERVIEW ; * ; * data is read in from memory and written to the codec at the same time ; * new data is written to the memory from the codec. the rotary encoder ; * increments or decrements the read address, adjusting the delay time. ; * ; * register usage - may be redefined in other sections ; * ; * r0 ; * r1 ; * r2 left lsb out ; * r3 left msb out ; * r4 right lsb out ; * r5 right msb out ; * r6 left lsb in ; * r7 left msb in ; * r8 right lsb in ; * r9 right msb in ; * r10 ; * r11 ; * r12 desired delay lsb ; * r13 desired delay msb ; * r14 ; * r15 switch sample counter ; * r16 temporary swap register ; * r17 temporary swap register ; * r18 ; * r19 ; * r20 ; * r21 ; * r22 write address third byte/null register ; * r23 ; * r24 write address lsb ; * r25 write address msb ; * r26 actual delay lsb ; * r27 actual delay msb ; * r28 read address lsb ; * r29 read address msb ; * r30 jump location for interrupt lsb ; * r31 jump location for interrupt msb ; **************************************************************************** ; * 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 ; - I am going to try to notate ; ********************** ; **** PROGRAM START ; ********************** ; initiate data transfer to codec sbi portb, portb0 ; set bit portb0 (aka DACLRC aka DAC sample rate left/right clock) ; ; **NOTE: from WM8731 data sheet, page 36: ; "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 ; ********************** ; **** wait1 ; ********************** 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 ; **NOTE: from atmega3250P data sheet, page 162: ; "When a serial transfer is complete, the SPIF flag is set" rjmp wait1_UID ; aka: loop until SPIF is set ; and then... ; ### 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) *********************************************************************************** out spdr, r2 ; send the processed (wet) left lsb out ; ### retrieve stored left channel data from SRAM: 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 nop ; read address msb hits the port, now wait for (1) nop ; a latch time of two cycles... (2) ; 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 ; ### now we've got a little time to kill, so... adiw r29:r28, 0x01 ; increment read address pointer by 1 ; ********************** ; **** wait2 ; ********************** wait2_UID: ; wait to see if wet left channel lsb has been sent in r17, spsr ; spi status reg. to r17 (looking familiar?) sbrs r17, spif ; skip next if SPIF bit not set... rjmp wait2_UID ; loop until SPIF set ; **NOTE: and now a couple of words about rjmp: ; - 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 in r6, spdr ; receive in dry left channel lsb ; **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) ************************************************************************************ out spdr, r5 ; send out (wet) right msb ; ### writing (dry) left channel to SRAM: out portd, r24 ; update data address ports to reflect where we want to write sts porth, r25 ; remember 'out' and 'sts'? 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 ; **NOTE: from AS7C4098 data sheet, page 2: ; "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. ; ### left channel SRAM write completed 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. ; ********************** ; **** wait3 ; ********************** 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 ; ### end of wait3, new data ready! 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 ; ### retrieve stored right channel data from SRAM 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) ; ### right channel SRAM data retrieval completed adiw r25:r24, 0x01 ; increment the write address (we're going to be using that pointer next) ; ********************** ; **** wait4 ; ********************** wait4_UID: ; checking up on that SPI transfer in r17, spsr sbrs r17, spif rjmp wait4_UID ; loop until SPIF is set ; ### end of wait4, let's get our dry audio in in r8, spdr ; that's the 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 ; ### writing (dry) right channel to SRAM 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 ; ********************************************************************************** ; **** check rotary encoder and adjust delay time ; ********************************************************************************** ; * 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 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: 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 ; ********************** ; **** edgecheck ; ********************** 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 ; ********************** ; **** switchsample ; ********************** 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 ; ********************** ; **** adjust ; ********************** 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 indexdown_UID: sbiw r27:r26, 0x02 ; decrement delay reg by 0x02 ; ********************** ; **** done ; ********************** done_UID: ; It's been a long hard row to hoe, but we did it! ; oh wait, what? I'm going to have to do it again? from the beginning? ; but I still need to get my 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 }}} [[MICrODEC]] [[MicrodecSoftware|Microdec Software]]