Directly use pins, no complicated redirections. Router modules will probably use real UARTs or MCUs programmed as UARTs.
375 lines
12 KiB
NASM
375 lines
12 KiB
NASM
|
|
|
|
|
|
; ***************************************************************************
|
|
; defines
|
|
|
|
.equ COM_MAXWAIT = 200 ; maximum loop count when waiting for rising/falling clock (TODO: Make frequency-dependant)
|
|
|
|
|
|
.equ COM_BUFFER_FLAGS_DONE = 0x80
|
|
.equ COM_BUFFER_FLAGS_RECEIVED = 0x40
|
|
|
|
|
|
|
|
|
|
; ***************************************************************************
|
|
; data
|
|
|
|
.dseg
|
|
|
|
|
|
comDataBegin:
|
|
comFlags: .byte 1
|
|
comAddress: .byte 1
|
|
comStatsPacketsIn: .byte 2
|
|
comStatsPacketsOut: .byte 2
|
|
comStatsRecvErrs: .byte 2
|
|
comStatsCollisions: .byte 2
|
|
comStatsMissed: .byte 2
|
|
comRingBuffer: .byte RINGBUFFER_OFFS_DATA+COM_RINGBUFFER_SIZE
|
|
comDataEnd:
|
|
|
|
|
|
|
|
; ***************************************************************************
|
|
; code
|
|
|
|
.cseg
|
|
|
|
|
|
|
|
; ---------------------------------------------------------------------------
|
|
; Com_Init
|
|
;
|
|
; IN:
|
|
; OUT:
|
|
; - CFLAG: set if okay, clear on error
|
|
; USED:
|
|
|
|
Com_Init:
|
|
; preset SRAM data area
|
|
ldi xh, HIGH(comDataBegin)
|
|
ldi xl, LOW(comDataBegin)
|
|
clr r16
|
|
ldi r17, (comDataEnd-comDataBegin)
|
|
rcall Utils_FillSram
|
|
|
|
; init ringbuffer
|
|
ldi r16, COM_RINGBUFFER_SIZE
|
|
ldi yl, LOW(comRingBuffer)
|
|
ldi yh, HIGH(comRingBuffer)
|
|
rcall RingBuffer_Init
|
|
|
|
; setup pins and interrupts
|
|
sbi COM_DDR_DATA, COM_PINNUM_DATA ; set DATA port as input
|
|
cbi COM_PORT_DATA, COM_PINNUM_DATA ; disable internal pullup for DATA
|
|
|
|
sbi COM_DDR_ATTN, COM_PINNUM_ATTN ; set ATTN port as input
|
|
cbi COM_PORT_ATTN, COM_PINNUM_ATTN ; disable internal pullup for ATTN
|
|
|
|
sbi COM_IRQ_ADDR_ATTN, COM_IRQ_BIT_ATTN ; enable pin change irq for ATTN line
|
|
in r16, GIMSK ; enable pin change irq PCIE0 or PCIE1
|
|
ori r16, (1<<COM_IRQ_GIMSK_ATTN)
|
|
out GIMSK, R16
|
|
in r16, GIFR ; clear pending irq
|
|
andi r16, ~(1<<COM_IRQ_GIFR_ATTN)
|
|
out GIFR, r16
|
|
|
|
sec
|
|
ret
|
|
|
|
|
|
|
|
; ---------------------------------------------------------------------------
|
|
; comSendPacket
|
|
;
|
|
; Send a packet. (TODO: input buffer in x, length in r16)
|
|
;
|
|
; IN:
|
|
; - R18: pos pointer inside ring buffer to read from next
|
|
; OUT:
|
|
; - CFLAG: set if okay, clear otherwise
|
|
; MODIFIED REGS: R10, R11, R12, R16, R20 (R3, R21, R22)
|
|
|
|
comSendPacket:
|
|
ldi yl, LOW(comRingBuffer)
|
|
ldi yh, HIGH(comRingBuffer)
|
|
ldd r19, y+RINGBUFFER_OFFS_MAXSIZE
|
|
rcall RingBuffer_Read ; read entry length from buffer (advances r18) (R3, R22)
|
|
cpi r16, 3 ; at least 3 bytes?
|
|
brcs comSendPacket_error ; too few bytes
|
|
mov r10, r16 ; r10: number of bytes at current ringbuffer pos
|
|
dec r10
|
|
dec r10
|
|
dec r10 ; r10: number of bytes in message
|
|
clr r11 ; r11: XOR checksum
|
|
mov r20, r18 ; store pos of flags in buffer for later ref
|
|
rcall RingBuffer_Read ; read flags from buffer
|
|
mov r12, r16 ; r12: flags
|
|
rcall RingBuffer_Read ; read dest address from buffer
|
|
eor r11, r16 ; calculate checksum
|
|
rcall comSendByte ; send destination address (R16, R21, R22)
|
|
brcc comSendPacket_error
|
|
mov r16, r10 ; send msg length
|
|
eor r11, r16 ; calculate checksum
|
|
rcall comSendByte ; send destination address
|
|
brcc comSendPacket_error
|
|
tst r10 ; payload?
|
|
breq comSendPacket_sendCrc ; no payload, go send checksum
|
|
comSendPacket_loop:
|
|
rcall RingBuffer_Read ; read byte from buffer
|
|
eor r11, r16 ; calculate checksum
|
|
rcall comSendByte ; send byte
|
|
brcc comSendPacket_error
|
|
dec r10
|
|
brne comSendPacket_loop
|
|
comSendPacket_sendCrc:
|
|
mov r16, r10 ; send checksum
|
|
rcall comSendByte ; send byte
|
|
brcc comSendPacket_error
|
|
mov r18, r20 ; pos of flags byte in ringbuffer
|
|
mov r16, r12
|
|
ori r16, COM_BUFFER_FLAGS_DONE ; set flag
|
|
rcall RingBuffer_Write ; write new flags (R3, R22)
|
|
sec
|
|
ret
|
|
|
|
comSendPacket_error:
|
|
clc
|
|
ret
|
|
|
|
|
|
|
|
; ---------------------------------------------------------------------------
|
|
; comReceivePacket
|
|
;
|
|
; Receive a packet. If we are the recipient of the packet then space will be allocated inside the
|
|
; given ringbuffer and the received packet will be stored there.
|
|
;
|
|
; Packages are stored in the buffer like this:
|
|
; - 1 byte : buffer size
|
|
; - 1 byte: flags (e.g. WAN_BUFFER_RECEIVED)
|
|
; - 1 byte : destination address
|
|
; - n bytes: packet (i.e. buffer size -2 bytes)
|
|
;
|
|
; IN:
|
|
; - nothing
|
|
; OUT:
|
|
; - CFLAG: set if okay, clear otherwise
|
|
; MODIFIED REGS: R1, R2, R3, R16, R17, R18, R19 (R20, R21, R22, XL, XH)
|
|
|
|
comReceivePacket:
|
|
ldi yl, LOW(comRingBuffer)
|
|
ldi yh, HIGH(comRingBuffer)
|
|
clr r1 ; r1: checksum
|
|
; read destination address
|
|
rcall comReceiveByte ; read byte
|
|
brcc comReceivePacket_error
|
|
; compare destination address (accept "0" and own address)
|
|
tst r16
|
|
breq comReceivePacket_acceptAddr
|
|
lds r17, comAddress
|
|
cp r16, r17
|
|
breq comReceivePacket_acceptAddr
|
|
clc ; not for me
|
|
ret
|
|
comReceivePacket_acceptAddr:
|
|
mov r3, r16 ; r3: destination address (maybe we need to forward the packet)
|
|
eor r1, r16
|
|
rcall comReceiveByte ; read packet length (R16, R17, R20, R21, R22)
|
|
brcc comReceivePacket_error
|
|
; alloc ring buffer
|
|
mov r17, r16 ; r17: packet length
|
|
inc r16 ; packet length bytes + length byte
|
|
inc r16 ; + flags
|
|
inc r16 ; + dest addr
|
|
mov r2, r16 ; r2: number of bytes allocated in ringbuffer
|
|
push r17
|
|
rcall RingBuffer_Alloc ; (r16, r17, r18, r19, r20, r21)
|
|
pop r17
|
|
brcc comReceivePacket_error
|
|
mov r18, r16 ; r18: pos in ring buffer
|
|
ldd r19, y+RINGBUFFER_OFFS_MAXSIZE ; max size
|
|
mov r16, r2 ; write number of bytes allocated to ringbuffer
|
|
ori r16, COM_BUFFER_FLAGS_RECEIVED
|
|
rcall RingBuffer_Write ; (r3, r22)
|
|
ldi r16, COM_BUFFER_FLAGS_RECEIVED ; write flags to ringbuffer
|
|
rcall RingBuffer_Write ; (r3, r22)
|
|
mov r16, r3 ; write destination address for packet we saved earlier
|
|
rcall RingBuffer_Write ; (r3, r22)
|
|
; read packet (except XOR byte, that will be read later)
|
|
comReceivePacket_loop1:
|
|
rcall comReceiveByte ; (R16, R17, R20, R21, R22)
|
|
brcc comReceivePacket_error_release_buffer
|
|
eor r1, r16
|
|
rcall RingBuffer_Write ; (r3, r22)
|
|
dec r17
|
|
brne comReceivePacket_loop1
|
|
; read XOR byte, don't store
|
|
rcall comReceiveByte ; (R16, R17, R20, R21, R22)
|
|
brcc comReceivePacket_error
|
|
eor r1, r16
|
|
brne comReceivePacket_error_release_buffer ; XOR should be 0 here, otherwise error
|
|
sec
|
|
ret
|
|
comReceivePacket_error_release_buffer:
|
|
mov r16, r2 ; number of bytes allocated in ringbuffer
|
|
rcall RingBuffer_DeallocWrite ; (r17, r18, r19, r20, r21)
|
|
comReceivePacket_error:
|
|
clc
|
|
ret
|
|
|
|
|
|
|
|
|
|
; ---------------------------------------------------------------------------
|
|
; comSendByte
|
|
;
|
|
; Send a byte.
|
|
; We only set the data pin to low at the beginning for the startbit. After that
|
|
; we only change the pin direction (e.g. input vs output):
|
|
; - for 0 bit: set DDR to output, forcing the data line low
|
|
; - for 1 bit: set DDR to input, letting the external pullup R pull the data line to HIGH
|
|
; since the output pin is still set to 0 the internal pullup is disabled
|
|
; IN:
|
|
; - R16: byte to send
|
|
; OUT:
|
|
; - CFLAG: set if okay, clear otherwise
|
|
; MODIFIED REGS: R16, R21, R22
|
|
|
|
comSendByte:
|
|
ldi r21, 8 ; +1 bits left
|
|
; send startbit
|
|
cbi COM_PORT_DATA, COM_PINNUM_DATA ; +2 set DATA low
|
|
sbi COM_DDR_DATA, COM_PINNUM_DATA ; +2 set DATA as output
|
|
Utils_WaitNanoSecs COM_BIT_LENGTH, 5, r22 ; wait for on bit duration
|
|
; send remaining bits
|
|
comSendByte_loop:
|
|
lsr r16 ; 1+ bit to send -> CARRY
|
|
brcc comSendByte_setHigh ; HI: +2, LO: +1
|
|
comSendByte_setLow:
|
|
sbi COM_DDR_DATA, COM_PINNUM_DATA ; +2 set DATA as output
|
|
nop
|
|
rjmp comSendByte_waitBit ; +2
|
|
comSendByte_setHigh:
|
|
cbi COM_DDR_DATA, COM_PINNUM_DATA ; +2 set DATA as input, pullup R makes it ONE
|
|
nop ; +1 (to make pin change available)
|
|
sbic COM_PIN_DATA, (1<<COM_PINNUM_DATA) ; +1 if okay, +2 otherwise
|
|
rjmp comSendByte_error ; +2 if error
|
|
comSendByte_waitBit: ; 7 cycles in this loop until now
|
|
Utils_WaitNanoSecs COM_BIT_LENGTH, 10, r22
|
|
dec r21 ; +1
|
|
brne comSendByte_loop ; +2, sum per loop: 10 cycles
|
|
; send stopbit
|
|
cbi COM_DDR_DATA, COM_PINNUM_DATA ; +2 set DATA as input, pullup R makes it ONE
|
|
Utils_WaitNanoSecs COM_BIT_LENGTH, 4, r22
|
|
sec
|
|
ret
|
|
|
|
comSendByte_error:
|
|
clc
|
|
ret
|
|
|
|
|
|
|
|
; ---------------------------------------------------------------------------
|
|
; comReceiveByte
|
|
;
|
|
; Receive a byte.
|
|
;
|
|
; IN:
|
|
; - nothing
|
|
; OUT:
|
|
; - CFLAG: set if okay, clear otherwise
|
|
; - R16: byte read (if CFLAG set)
|
|
; MODIFIED REGS: R16, R20, R21, R22 (R17)
|
|
|
|
comReceiveByte:
|
|
sbi COM_DDR_DATA, COM_PINNUM_DATA ; set DATA port as input
|
|
cbi COM_PORT_DATA, COM_PINNUM_DATA ; disable internal pullup for DATA
|
|
ldi r21, 8 ; bits left
|
|
clr r20 ; byte currently receiving
|
|
; wait for startbit
|
|
rcall comWaitForDataLow
|
|
brcc comReceiveByte_error
|
|
Utils_WaitNanoSecs COM_BIT_LENGTH/2, 5, r22 ; goto middle of startbit to maximize sync stability
|
|
comReceiveByte_loop:
|
|
Utils_WaitNanoSecs COM_BIT_LENGTH, 8, r22 ; 8 cycles used in the complete loop between waits
|
|
sec ; +1
|
|
sbic COM_PIN_DATA, COM_PINNUM_DATA ; LOW: +2, HIGH: +1
|
|
rjmp comReceiveByte_shiftIn ; HIGH: +2, rjmp, use set CFLAG
|
|
clc ; LOW: +1
|
|
comReceiveByte_shiftIn:
|
|
ror r20 ; +1
|
|
dec r21 ; +1
|
|
brne comReceiveByte_loop ; +2, sum per loop: 8 cycles
|
|
rcall comWaitForDataHigh ; wait for start of stopbit
|
|
; brcc comReceiveByte_error
|
|
mov r16, r20
|
|
sec
|
|
ret
|
|
comReceiveByte_error:
|
|
clc
|
|
ret
|
|
|
|
|
|
|
|
; ---------------------------------------------------------------------------
|
|
; comWaitForDataLow
|
|
;
|
|
; Waits up to COM_MAXWAIT loops for low data line
|
|
; IN:
|
|
; OUT:
|
|
; - CFLAG: set if okay, clear otherwise
|
|
; MODIFIED REGISTERS: r17, r22, X
|
|
|
|
comWaitForDataLow:
|
|
ldi r17, COM_MAXWAIT
|
|
|
|
comWaitForDataLow_loop:
|
|
sbis COM_PIN_DATA, COM_PINNUM_DATA
|
|
rjmp comWaitForDataLow_done
|
|
dec r17
|
|
brne comWaitForDataLow_loop
|
|
clc ; timeout
|
|
ret
|
|
|
|
comWaitForDataLow_done:
|
|
sec ; ok
|
|
ret
|
|
|
|
|
|
|
|
; ---------------------------------------------------------------------------
|
|
; comWaitForDataHigh
|
|
;
|
|
; Waits up to COM_MAXWAIT loops for high data line
|
|
; IN:
|
|
; OUT:
|
|
; - CFLAG: set if okay, clear otherwise
|
|
; MODIFIED REGISTERS: r17, r22, X
|
|
|
|
comWaitForDataHigh:
|
|
ldi r17, COM_MAXWAIT
|
|
|
|
comWaitForDataHigh_loop:
|
|
sbic COM_PIN_DATA, COM_PINNUM_DATA
|
|
rjmp comWaitForDataHigh_done
|
|
dec r17
|
|
brne comWaitForDataHigh_loop
|
|
clc ; timeout
|
|
ret
|
|
|
|
comWaitForDataHigh_done:
|
|
sec ; ok
|
|
ret
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|