; PROGRAM OVERVIEW
;
; midi data is recieved over RF UNIT and buffered in a FIFO buffer.
; these data are then sent over the USART link as fast as possible.
; if a duplicate packet sequence is received, it is assumed to be a
; retry packet, and any new (concatenated) data is sent out, the rest
; are deleted.


.include "m1281def.inc" ; which micro is being used


; FUSES - fuse settings for micro
;
; BOD Level = 2.7V
; JTAG programming ON
; SPI porgramming ON
; Bootloader is OFF
; External Oscillator
; 


; RF unit settings
.equ des_addr = $4444 ; place unique destination address here
.equ src_addr = $6666 ; place unique source address here
.equ csma_seed = low(src_addr)*$08 ; random backoff exponent - must be unique,<$0800 
.equ pan_id = $a4c3 ; system pan_id
.equ retries = $0e ; high nibble = frame retries,low nibble = 2x(csma retries)
.equ channel = $33 ; tx/rx channel, $2b -> $3a valid


; INTERRUPT VECTORS
.org 0 ; RESET vector
rjmp start
.org int5addr ; RF UNIT interrupt
rjmp clearirq

; PIN ASSIGNMENTS:
;
; PD3 = USART1 TX for MIDI out


; REGISTER ASSIGNMENTS:
;
; r0  
; r1  
; r2  
; r3  
; r4  
; r5  
; r6  
; r7  
; r8  
; r9  
; r10 
; r11 
; r12 
; r13 
; r14 previous frame byte
; r15 previous sequence byte
; r16 temporary register - saved during interrupts
; r17 temporary register - not saved during interrupts
; r18 data read pointer temp storage
; r19 frame length counter
; r20 wait timer
; r21 
; r22 
; r23 
; r24 
; r25 
; xl  data write pointer lsb
; xh  data write pointer msb
; yl  data read pointer lsb
; yh  data read pointer msb
; zl  
; zh  

start: ; configure microcontroller registers
; using external 1MHz oscillator from RF unit
; this gives better frequency accuracy for the USARTs
ldi r16,high(RAMEND)
out SPH,r16 ; Set Stack Pointer to top of RAM
ldi r16,low(RAMEND)
out SPL,r16
ldi r16,$17
out ddrb,r16 ; set ss,sclk,mosi,slp_tr as output
ldi r16,$08
out portb,r16 ; enable pullup for miso
ldi r16,$01 ; spi to 2x speed
out spsr,r16 ; set up spi
ldi r16,$50 ; spi on,msb first,master,mode 0,ck=f/2=500kbps
out spcr,r16 ; set up spi
sbi portb,portb0 ; set ss pin high
cbi portb,portb4 ; set slp_tr low
sbi ddra,dda7 ; set reset pin as output
cbi porta,porta7 ; initiate a reset of rf unit
nop ; wait appropriate hold time (>625ns)
sbi porta,porta7 ; pull reset high on rf unit
ldi r16,$0c
sts eicrb,r16 ; configure int5 for rising edge
ldi r16,$20
out eimsk,r16 ; enable int5

;wait for rf unit to stabilize
ldi r20,$80 ; load the wait timer to 128 cycles (x10/ck)
rcall wait2 ; wait ~(1280/1MHz = 1.2ms) for rf unit to stabilize

;go to trx_off state
rcall txoff ; go to trx_off state on rf unit

;setup clkm - only driver strength changed
ldi r16,$c3 ; load first data byte - write trx_ctrl_0 register
cbi portb,portb0 ; pull ss low
rcall spiwrite ; send data
ldi r16,$01 ; load second data byte - lowest driver strength, clkm = 1MHz
rcall spiwrite ; send data
sbi portb,portb0 ; pull ss high
nop ; idle for a bit as clock changes
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
ldi r21,$ff ; load wait timer to 255 cycles (x10/ck)
rcall wait2 ; wait ~2.5ms to make sure clock is stable

;setup power reduction registers
ldi r16,$eb
sts prr0,r16 ; turn off adc,usart0,t0,t1,t2,twi
ldi r16,$3e
sts prr1,r16 ; turn off t5,t4,t3,usart3,usart2
ldi r16,$90
out acsr,r16 ; turn off comparator


;configure rf unit registers

;setup short_addr_0
ldi r16,$e0 ; load first data byte - write short_addr_0 register
cbi portb,portb0 ; pull ss low
rcall spiwrite ; send data
ldi r16,low(src_addr) ; load second data byte - src_addr lsb
rcall spiwrite ; send data
sbi portb,portb0 ; pull ss high

;setup short_addr_1
ldi r16,$e1 ; load first data byte - write short_addr_1 register
cbi portb,portb0 ; pull ss low
rcall spiwrite ; send data
ldi r16,high(src_addr) ; load second data byte - src_addr msb
rcall spiwrite ; send data
sbi portb,portb0 ; pull ss high

;setup pan_id_0
ldi r16,$e2 ; load first data byte - write pan_id_0 register
cbi portb,portb0 ; pull ss low
rcall spiwrite ; send data
ldi r16,low(pan_id) ; load second data byte - pan_id lsb
rcall spiwrite ; send data
sbi portb,portb0 ; pull ss high

;setup pan_id_1
ldi r16,$e3 ; load first data byte - write pan_id_1 register
cbi portb,portb0 ; pull ss low
rcall spiwrite ; send data
ldi r16,high(pan_id) ; load second data byte - pan_id msb
rcall spiwrite ; send data
sbi portb,portb0 ; pull ss high

;setup phy_cc_ca - set channel id
ldi r16,$c8 ; load byte - write phy_cc_ca register
cbi portb,portb0 ; pull ss low
rcall spiwrite ; send data
ldi r16,channel ; load data byte - 
rcall spiwrite ; send data
sbi portb,portb0 ; pull ss high

;setup phy_tx_pwr - turn crc on
ldi r16,$c5 ; load byte - write phy_tx_pwr register
cbi portb,portb0 ; pull ss low
rcall spiwrite ; send data
ldi r16,$80 ; load data byte - auto_crc=on,pwr=+3dbm
rcall spiwrite ; send data
sbi portb,portb0 ; pull ss high

;setup xah_ctrl - set frame and csma retries
ldi r16,$ec ; load byte - write xah_ctrl register
cbi portb,portb0 ; pull ss low
rcall spiwrite ; send data
ldi r16,retries ; load data byte - frame and csma retries
rcall spiwrite ; send data
sbi portb,portb0 ; pull ss high

;setup csma_seed_1
ldi r16,$ee ; load byte - write csma_seed_1 register
cbi portb,portb0 ; pull ss low
rcall spiwrite ; send data
ldi r16,high(csma_seed) ; load data byte - min_be=0,aack_set=0,i_am_coord=0,csma(10:8)
rcall spiwrite ; send data
sbi portb,portb0 ; pull ss high

;setup csma_seed_0
ldi r16,$ed ; load first data byte - write csma_seed_1 register
cbi portb,portb0 ; pull ss low
rcall spiwrite ; send data
ldi r16,low(csma_seed) ; load data byte - csma(7:0)
rcall spiwrite ; send data
sbi portb,portb0 ; pull ss high

;clear pending irqs on rf unit 
ldi r16,$8f ; load first data byte - read register irq_status command
cbi portb,portb0 ; pull ss low
rcall spiwrite ; send byte and clear pending irqs
sbi portb,portb0 ; pull ss high

;enable irqs on rf unit
ldi r16,$ce ; load first data byte - write register irq_mask command
cbi portb,portb0 ; pull ss low
rcall spiwrite ; send byte
ldi r16,$08 ; load second data byte - trx_end only
rcall spiwrite ; send byte
sbi portb,portb0 ; pull ss high


;setup usart1 - assumes rest of registers are in default mode
ldi r16,$08
sts ucsr1b,r16 ; turn on usart1 tx
ldi r16,$01
sts ubrr1l,r16 ; set usart1 to f/32=31.25kbps
ldi xh,$02 ;  set data write pointer to bottom of sram buffer
clr xl
movw y,x ; set data read pointer to same point as write


;clear pending irqs on micro
ser r16
out eifr,r16 ; clear any interrupt flags that are set

;enable interrupts on micro
sei ; turn on interrupts

;turn on transmitter
rcall rxonaack ; put the rf unit in rx_aack_on state


idle: ; check for data to send over USART

nop
nop
nop
nop
nop
cp xl,yl ; check if new data has arrived
brne senddata ; send data out if present
rjmp idle ; else keep checking


;USART TX ROUTINE

senddata: ; process incoming bytes on usart1

lds r16,ucsr1a ; fetch usart control register
sbrs r16,udre1 ; check if data register is empty
rjmp senddata ; keep checking until its empty
; else send data
ld r16,y+ ; fetch data
andi yh,$02 ; mask off top byte to keep fifo at lower 256
sts udr1,r16 ; load transmit byte
rjmp idle ; check to see if more data to be sent


;RF UNIT COMMANDS

txoff: ; send trx_off command to rf unit and wait till stable

ldi r16,$c2 ; load first data byte - write register trx_state command
cbi portb,portb0 ; pull ss low
rcall spiwrite ; send byte
ldi r16,$08 ; load second data byte - data trx_off bit
rcall spiwrite ; send byte
sbi portb,portb0 ; pull ss high

checktxoff: ; check current state to see if in tx_off state

ldi r16,$81 ; load first data byte - read register trx_status command
cbi portb,portb0 ; pull ss low
rcall spiwrite ; send byte
ldi r16,$00 ; load second data byte - blank for reading
rcall spiwrite ; send byte
sbi portb,portb0 ; pull ss high
cpi r16,$08
brne checktxoff
ret ; return to previous duty

forcetxoff: ; send trx_off command to rf unit and wait till stable

ldi r16,$c2 ; load first data byte - write register trx_state command
cbi portb,portb0 ; pull ss low
rcall spiwrite ; send byte
ldi r16,$03 ; load second data byte - data force_trx_off bit
rcall spiwrite ; send byte
sbi portb,portb0 ; pull ss high

checkforcetxoff: ; check current state to see if in tx_off state

nop ; delay for a bit to make sure the device is off
nop
nop
nop
ldi r16,$81 ; load first data byte - read register trx_status command
cbi portb,portb0 ; pull ss low
rcall spiwrite ; send byte
ldi r16,$00 ; load second data byte - blank for reading
rcall spiwrite ; send byte
sbi portb,portb0 ; pull ss high
cpi r16,$08
brne forcetxoff ; redo if not off - only takes 1us to turn off (changed due to lock up bug of getting stuck in check loop)
ret ; return to previous duty

pllon: ; send pll_on command to rf unit

ldi r16,$c2 ; load first data byte - write register trx_state command
cbi portb,portb0 ; pull ss low
rcall spiwrite ; send byte
ldi r16,$09 ; load second data byte - data pll_on bit
rcall spiwrite ; send byte
sbi portb,portb0 ; pull ss high

checkpllon: ; check current state to see if in pll_on state

ldi r16,$81 ; load first data byte - read register trx_status command
cbi portb,portb0 ; pull ss low
rcall spiwrite ; send byte
ldi r16,$00 ; load second data byte - blank for reading
rcall spiwrite ; send byte
sbi portb,portb0 ; pull ss high
cpi r16,$09
brne checkpllon
ret ; return to previous duty

txonaret: ; send trx_aret_on command to rf unit and wait till stable

ldi r16,$c2 ; load first data byte - write register trx_state command
cbi portb,portb0 ; pull ss low
rcall spiwrite ; send byte
ldi r16,$19 ; load second data byte - data trx_aret_on bit
rcall spiwrite ; send byte
sbi portb,portb0 ; pull ss high

checktxonaret: ; check current state to see if in tx_aret_on state

ldi r16,$81 ; load first data byte - read register trx_status command
cbi portb,portb0 ; pull ss low
rcall spiwrite ; send byte
ldi r16,$00 ; load second data byte - blank for reading
rcall spiwrite ; send byte
sbi portb,portb0 ; pull ss high
cpi r16,$19
brne checktxonaret
ret ; return to previous duty

rxonaack: ; send rx_aack_on command to rf unit and wait till stable

ldi r16,$c2 ; load first data byte - write register trx_state command
cbi portb,portb0 ; pull ss low
rcall spiwrite ; send byte
ldi r16,$16 ; load second data byte - data rx_aack_on bit
rcall spiwrite ; send byte
sbi portb,portb0 ; pull ss high

checkrxonaack: ; check current state to see if in rx_aack_on state

ldi r16,$81 ; load first data byte - read register trx_status command
cbi portb,portb0 ; pull ss low
rcall spiwrite ; send byte
ldi r16,$00 ; load second data byte - blank for reading
rcall spiwrite ; send byte
sbi portb,portb0 ; pull ss high
cpi r16,$16
brne checkrxonaack
ret ; return to previous duty


;GENERAL FUNCTIONS

spiwrite: ; read and write data over spi

out spdr,r16 ; write transmitted byte to spi

wait: ; read spi register to check when done

in r16,spsr ; get spi status register
sbrs r16,spif ; check if done
rjmp wait ; keep checking if not done
in r16,spdr ; save recieved byte
ret ; return to previous duty

wait2: ; wait timer (~12 cylces per value in r20)

nop
nop
nop
nop
nop
nop
nop
nop
dec r20
brne wait2
ret ; return to previous duty


;RF UNIT INTERRUPT ROUTINES

clearirq: ; clear irq register and irq line

push r16 ; save temp register
in r16,sreg ; get sreg
push r16 ; push sreg on stack
ldi r16,$8f ; load first data byte - read register irq_status command
cbi portb,portb0 ; pull ss low
rcall spiwrite ; send byte
sbi portb,portb0 ; pull ss high

checkrf: ; check rf unit state

ldi r16,$81 ; load first data byte - read register trx_status command
cbi portb,portb0 ; pull ss low
rcall spiwrite ; send byte
ldi r16,$00 ; load second data byte - blank for reading
rcall spiwrite ; send byte
sbi portb,portb0 ; pull ss high
cpi r16,$1f ; check if in state transition
breq checkrf ; keep checking till in a new state
cpi r16,$11 ; check if in busy_rx state
breq checkrf ; keep checking till in a new state
cpi r16,$16 ; check if in rx_aack_on state
brne wrongstate ; change back to tx_aret if not
;else check successful transmit
ldi r16,$82 ; load data byte - read trx_state to get trac_status
cbi portb,portb0 ; pull ss low
rcall spiwrite ; send byte
ldi r16,$00 ; load data byte - blank byte for reading
rcall spiwrite ; send byte
sbi portb,portb0 ; pull ss high
andi r16,$e0 ; mask off trac_status
cpi r16,$30 ; check if success or data_pending success
brlo success ; skip if success
;else deal with failure
;continue to scan for data
rjmp irqdone ;  finish off

wrongstate: ; change state in case of error

rcall forcetxoff ; switch to a known state
rcall rxonaack ; put device back to recieve

irqdone: ; finish interrupt and return

pop r16 ; get off stack
out sreg,r16 ; return sreg
pop r16 ; return temporary register
reti ; clear interrupt and return to previous duty

success: ; rx success - get data

;get data from rf unit tx buffer
ldi r16,$20 ; load data byte - read buffer command
cbi portb,portb0 ; pull ss low
rcall spiwrite ; send byte
ldi r16,$00 ; load data byte - blank for reading
rcall spiwrite ; send byte -  frame length byte
subi r16,$0b ; decrease frame length for bits to be removed
breq done7 ; finish if no data present
brlo done7 ; finish if error
mov r17,r16 ; move frame length to temporary buffer
mov r19,r16 ; move frame length to frame length buffer
ldi r16,$00 ; load data byte - blank for reading
rcall spiwrite ; send byte - do not save fcf(7:0)
ldi r16,$00 ; load data byte - blank for reading
rcall spiwrite ; send byte - do not save fcf(15:8)
ldi r16,$00 ; load data byte - blank for reading
rcall spiwrite ; send byte - sequence byte
cp r15,r16 ; check if duplicate transmission
mov r15,r16 ; store sequence byte - z flag not affected by mov command
breq duplicate ; remove duplicate data if sequence byte repeated
;else load data to buffer
ldi r16,$00 ; load data byte - blank for reading
rcall spiwrite ; send byte - do not save des_panid(7:0)
ldi r16,$00 ; load data byte - blank for reading
rcall spiwrite ; send byte - do not save des_panid(15:8)
ldi r16,$00 ; load data byte - blank for reading
rcall spiwrite ; send byte - do not save des_addr(7:0)
ldi r16,$00 ; load data byte - blank for reading
rcall spiwrite ; send byte - do not save des_addr(15:8)
ldi r16,$00 ; load data byte - blank for reading
rcall spiwrite ; send byte - do not save src_addr(7:0)
ldi r16,$00 ; load data byte - blank for reading
rcall spiwrite ; send byte - do not save src_addr(15:8)

getdatarf: ; keep loading data till frame compelete

ldi r16,$00 ; load data byte - blank for reading
rcall spiwrite ; send byte
st x+,r16 ; move byte to buffer
andi xh,$02 ; mask off high byte to keep buffer in lower 256
dec r17 ; decrement frame length byte
brne getdatarf ; keep decrementing until done
;else finish off
sbi portb,portb0 ; pull ss high
rjmp irqdone ; finish off

duplicate: ; deal with duplicate transmission

cp r14,r19 ; check if duplicate packet is the same length as previous
breq done7 ; finish off if the packet was identical
brsh getdatarf ; if previous packet was longer assume a transmitter failure
; and get new data - (could make this a fail state and goto done7, but then
; you would not want done7 to include frame length storage)
;else get new data
sub r17,r14 ; find out how many new bytes of data

getdatarf1: ; clear out data that is redundant

ldi r16,$00 ; load data byte - blank for reading
rcall spiwrite ; send byte
dec r14 ; decrement previous frame length byte
brne getdatarf1 ; keep decrementing until done

getdatarf2: ; get new data

ldi r16,$00 ; load data byte - blank for reading
rcall spiwrite ; send byte
st x+,r16 ; move byte to buffer
andi xh,$02 ; mask off high byte to keep buffer in lower 256
dec r17 ; decrement difference frame length byte
brne getdatarf2 ; keep decrementing until done
;else finish off

done7: ; finish off

sbi portb,portb0 ; pull ss high
mov r14,r19 ; store frame length in case of duplicate transmission
rjmp irqdone ; finish off
