DelayTutorial.asm
[Discussion of code elements, needed revision etc, can go here]
; ****************************************************************************
; * program: delay-16b-rotary.asm *
; * UID = dan_practice - unique id to eliminate conflicts between variables *
; * 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
Last edited 2010-07-18 by DanielMcAnulty · 2 revisions