Files
aqhomecontrol/avr/modules/uart_irq/iface.asm
Martin Preuss 7707cb0a82 avr started working on irq driven uart module.
will be used for routers and usb-serial interface.
2024-10-31 18:50:53 +01:00

577 lines
15 KiB
NASM

; ***************************************************************************
; copyright : (C) 2024 by Martin Preuss
; email : martin@libchipcard.de
;
; ***************************************************************************
; * This file is part of the project "AqHome". *
; * Please see toplevel file COPYING of that project for license details. *
; ***************************************************************************
; ---------------------------------------------------------------------------
; @macro m_uart_irq_handle_tick
;
; @clobbers any
;
; @param %0 COM_DATA_DDR
; @param %1 COM_DATA_INPUT
; @param %2 COM_DATA_OUTPUT
; @param %3 COM_DATA_PIN
; @param %4 COM_ATTN_DDR
; @param %5 COM_ATTN_INPUT
; @param %6 COM_ATTN_OUTPUT
; @param %7 COM_ATTN_PIN
; @param %8 uartIrqDataIface1
.macro m_uart_irq_handle_tick
ldi yl, LOW(@8)
ldi yh, HIGH(@8)
ldi zl, LOW(_jumptable)
ldi zh, HIGH(_jumptable)
rjmp UART_Irq_HandleTick
_jumptable:
rjmp _readDataBit ; read DATA BIT (r16=bit)
rjmp _readAttnBit ; read ATTN BIT (r16=bit)
rjmp _writeDataBit ; write DATA BIT (r16=bit)
rjmp _writeAttnBit ; write ATTN BIT (r16=bit)
_readDataBit:
clr r16
sbic @1, @3
inc r16
ret
_readAttnBit:
clr r16
sbic @5, @7
inc r16
ret
_writeDataBit:
tst r16
brne _writeDataBit1
cbi @0, @3 ; set data line to output (makes it LOW)
ret
_writeDataBit1:
sbi @0, @3 ; set data line to output (external pullup R makes it HIGH)
ret
_writeAttnBit:
tst r16
brne _writeAttnBit1
cbi @4, @7 ; set ATTN line to output (makes it LOW)
ret
_writeAttnBit1:
sbi @4, @7 ; set ATTN line to output (external pullup R makes it HIGH)
ret
.endmacro
; @end
; ---------------------------------------------------------------------------
; @macro m_ringbuffer_writebyte
;
; @param R16 byte to write
; @param Y base address (for "LDD Y+nn" and "STD Y+nn")
; @param %0 constant maxBytes
; @param %1 offset to Y to access usedBytes variable (for "LDD Y+nn" and "STD Y+nn")
; @param %2 offset to Y to access readPos variable
; @param %3 offset to Y to access writePos variable
; @param %4 offset to Y to access buffer
; @return CFLAG set if okay, cleared on error (i.e. buffer full)
; @clobbers R17, X
.macro m_ringbuffer_writebyte
ldd r17, Y+@1 ; usedBytes
cpi r17, @0 ; maxBytes
brcc l_end
inc r17
std Y+@1, r17 ; usedBytes
ldd r17, Y+@3 ; writePos
mov xl, yl
mov xh, yh
adiw xh:xl, @4
add xl, r17
adc xh, r17
sub xh, r17
st X, r16
inc r17
cpi r17, @0
brcs l_store
clr r17
l_store:
std Y+@3, r17 ; writePos
sec
l_end:
.endmacro
; @end
; ---------------------------------------------------------------------------
; @macro m_ringbuffer_readbyte
;
; @param Y base address (for "LDD Y+nn" and "STD Y+nn")
; @param %0 constant maxBytes
; @param %1 offset to Y to access usedBytes variable (for "LDD Y+nn" and "STD Y+nn")
; @param %2 offset to Y to access readPos variable
; @param %3 offset to Y to access writePos variable
; @param %4 offset to Y to access buffer
; @return CFLAG set if okay, cleared on error (i.e. buffer full)
; @return R16 byte read
; @clobbers R17, X
.macro m_ringbuffer_readbyte
ldd r17, Y+@1 ; usedBytes
tst r17
clc
breq l_end
dec r17
std Y+@1, r17 ; usedBytes
ldd r17, Y+@2 ; readPos
mov xl, yl
mov xh, yh
adiw xh:xl, @4
add xl, r17
adc xh, r17
sub xh, r17
ld r16, X
inc r17
cpi r17, @0
brcs l_store
clr r17
l_store:
std Y+@2, r17 ; readPos
sec
l_end:
.endmacro
.dseg
uartIrqBase: .byte 4
;uartIrqState: .byte 1
;uartIrqCounter: .byte 1
;uartIrqStateCounter: .byte 1
;uartIrqCurrentByte: .byte 1
.cseg
UART_IRQ_BEGIN:
; ---------------------------------------------------------------------------
; @routine UART_Irq_HandleTick @global
;
; @param Y pointer to interface data in SRAM (see @ref UART_IRQ_IFACE_OFFS_STATE)
; @param Z pointer to IFACE jump table for the given interface
; @clobbers any
UART_Irq_HandleTick:
ldd r16, Y+UART_IRQ_IFACE_OFFS_TICKCOUNTER
tst r16
breq UART_Irq_HandleTick_handle
dec r16
std Y+UART_IRQ_IFACE_OFFS_TICKCOUNTER, r16
UART_Irq_HandleTick_handle:
ldd r16, Y+UART_IRQ_IFACE_OFFS_STATE
rjmp uart_irq_JumpToStateFunction
; @end
; r16: function
uart_irq_JumpToStateFunction:
cpi r16, UART_IRQ_STATE_COUNT ; invalid function num?
brcc uart_irq_JumpToStateFunction_end
ldi r18, LOW(uart_irq_state_jumptable)
ldi r19, HIGH(uart_irq_state_jumptable)
add r18, r16
clr r16
adc r19, r16
push r18
push r19
uart_irq_JumpToStateFunction_end:
ret ; indirect jump to address we just pushed to the stack
uart_irq_state_jumptable:
rjmp uart_irq_handle_idle
rjmp uart_irq_handle_waitforattnhigh
rjmp uart_irq_handle_waitforstartbit
rjmp uart_irq_handle_waitfordatabit
rjmp uart_irq_handle_waitforstopbit
rjmp uart_irq_handle_sendingattn
rjmp uart_irq_handle_sendingstartbit
rjmp uart_irq_handle_sendingdatabit
rjmp uart_irq_handle_sendingstopbit
; z=iface jumptable
uart_irq_ReadDataBit:
adiw zh:zl, UART_IRQ_IFACE_PINFN_READDATA
push zl
push zh
sbiw zh:zl, UART_IRQ_IFACE_PINFN_READDATA
ret ; indirect jump to address we just pushed to the stack
uart_irq_ReadAttnBit:
adiw zh:zl, UART_IRQ_IFACE_PINFN_READATTN
push zl
push zh
sbiw zh:zl, UART_IRQ_IFACE_PINFN_READATTN
ret ; indirect jump to address we just pushed to the stack
uart_irq_WriteDataBit:
adiw zh:zl, UART_IRQ_IFACE_PINFN_WRITEDATA
push zl
push zh
sbiw zh:zl, UART_IRQ_IFACE_PINFN_WRITEDATA
ret ; indirect jump to address we just pushed to the stack
uart_irq_WriteAttnBit:
adiw zh:zl, UART_IRQ_IFACE_PINFN_WRITEATTN
push zl
push zh
sbiw zh:zl, UART_IRQ_IFACE_PINFN_WRITEATTN
ret ; indirect jump to address we just pushed to the stack
; y=iface sram data
; z=iface jumptable
uart_irq_handle_idle:
rcall uart_irq_ReadAttnBit
tst r16
brne uart_irq_handle_idle_checksend ; jump if ATTN line high
; ATTN low, start receiving
ldi r16, UART_IRQ_STATE_WAITFORSTARTBIT
std Y+UART_IRQ_IFACE_OFFS_STATE, r16
ldi r16, UART_IRQ_TIME_BITTIME10
std Y+UART_IRQ_IFACE_OFFS_TICKCOUNTER, r16
rjmp uart_irq_handle_idle_end
uart_irq_handle_idle_checksend:
; check whether there is something to send
ldd r16, Y+UART_IRQ_IFACE_OFFS_WRITEBUF_USED
tst r16
breq uart_irq_handle_idle_end ; nothing to send
; start sending
clr r16
rcall uart_irq_WriteAttnBit
ldi r16, UART_IRQ_STATE_SENDINGATTN
std Y+UART_IRQ_IFACE_OFFS_STATE, r16
ldi r16, UART_IRQ_TIME_BITTIME2
std Y+UART_IRQ_IFACE_OFFS_TICKCOUNTER, r16
uart_irq_handle_idle_end:
ret
uart_irq_handle_waitforstartbit:
rcall uart_irq_ReadAttnBit
tst r16
brne uart_irq_handle_waitforstartbit_toidle
; ATTN still low
rcall uart_irq_ReadDataBit
tst r16
breq uart_irq_handle_waitforstartbit_datalow
; DATA still high
ldd r16, Y+UART_IRQ_IFACE_OFFS_TICKCOUNTER
tst r16
brne uart_irq_handle_waitforstartbit_end
ldi r16, UART_IRQ_STATE_WAITFORATTNHIGH
std Y+UART_IRQ_IFACE_OFFS_STATE, r16
rjmp uart_irq_handle_waitforstartbit_end
uart_irq_handle_waitforstartbit_datalow:
ldi r16, UART_IRQ_STATE_WAITFORDATABIT
std Y+UART_IRQ_IFACE_OFFS_STATE, r16
ldi r16, UART_IRQ_TIME_BITTIME1_5
std Y+UART_IRQ_IFACE_OFFS_TICKCOUNTER, r16
ldi r16, 8 ; 8 bit
std Y+UART_IRQ_IFACE_OFFS_STATECOUNTER, r16
clr r16
std Y+UART_IRQ_IFACE_OFFS_STATEDATA, r16
rjmp uart_irq_handle_waitforstartbit_end
uart_irq_handle_waitforstartbit_toidle:
ldi r16, UART_IRQ_STATE_IDLE
std Y+UART_IRQ_IFACE_OFFS_STATE, r16
uart_irq_handle_waitforstartbit_end:
ret
uart_irq_handle_waitfordatabit:
rcall uart_irq_ReadAttnBit
tst r16
brne uart_irq_handle_waitfordatabit_toidle
ldd r16, Y+UART_IRQ_IFACE_OFFS_TICKCOUNTER
tst r16
brne uart_irq_handle_waitfordatabit_end
; counter=0: read data
rcall uart_irq_ReadDataBit
ror r16 ; rotate bit into carry flag
ldd r16, Y+UART_IRQ_IFACE_OFFS_STATEDATA ; doesn't change carry flag
ror r16 ; rotate carry flag into data byte
std Y+UART_IRQ_IFACE_OFFS_STATEDATA, r16
ldd r16, Y+UART_IRQ_IFACE_OFFS_STATECOUNTER
dec r16
brne uart_irq_handle_waitfordatabit_bytecomplete
ldi r16, UART_IRQ_TIME_BITTIME1 ; read next bit
std Y+UART_IRQ_IFACE_OFFS_TICKCOUNTER, r16
rjmp uart_irq_handle_waitfordatabit_end
uart_irq_handle_waitfordatabit_bytecomplete:
ldi r16, UART_IRQ_STATE_WAITFORSTOPBIT
std Y+UART_IRQ_IFACE_OFFS_STATE, r16
ldi r16, UART_IRQ_TIME_BITTIME1_5
std Y+UART_IRQ_IFACE_OFFS_TICKCOUNTER, r16
rjmp uart_irq_handle_waitfordatabit_end
uart_irq_handle_waitfordatabit_toidle:
ldi r16, UART_IRQ_STATE_IDLE
std Y+UART_IRQ_IFACE_OFFS_STATE, r16
uart_irq_handle_waitfordatabit_end:
ret
uart_irq_handle_waitforstopbit:
rcall uart_irq_ReadAttnBit
tst r16
brne uart_irq_handle_waitforstopbit_toidle
rcall uart_irq_ReadDataBit
tst r16
brne uart_irq_handle_waitforstopbit_datahigh
; still low
ldd r16, Y+UART_IRQ_IFACE_OFFS_TICKCOUNTER
tst r16
breq uart_irq_handle_waitforstopbit_abortmsg ; timeout, abort
rjmp uart_irq_handle_waitforstopbit_end
uart_irq_handle_waitforstopbit_datahigh:
ldd r16, Y+UART_IRQ_IFACE_OFFS_STATEDATA ; store received byte
rcall uart_irq_rdbuf_writebyte
brcc uart_irq_handle_waitforstopbit_abortmsg ; readbuffer full, abort
ldi r16, UART_IRQ_STATE_WAITFORSTARTBIT ; wait for next byte
std Y+UART_IRQ_IFACE_OFFS_STATE, r16
ldi r16, UART_IRQ_TIME_BITTIME10
std Y+UART_IRQ_IFACE_OFFS_TICKCOUNTER, r16
rjmp uart_irq_handle_waitforstopbit_end
uart_irq_handle_waitforstopbit_abortmsg:
ldi r16, UART_IRQ_STATE_WAITFORATTNHIGH
std Y+UART_IRQ_IFACE_OFFS_STATE, r16
rjmp uart_irq_handle_waitforstopbit_end
uart_irq_handle_waitforstopbit_toidle:
ldi r16, UART_IRQ_STATE_IDLE
std Y+UART_IRQ_IFACE_OFFS_STATE, r16
uart_irq_handle_waitforstopbit_end:
ret
uart_irq_handle_waitforattnhigh:
rcall uart_irq_ReadAttnBit
tst r16
breq uart_irq_handle_waitforattnhigh_end ; still low
uart_irq_handle_waitforattnhigh_toidle:
ldi r16, UART_IRQ_STATE_IDLE
std Y+UART_IRQ_IFACE_OFFS_STATE, r16
uart_irq_handle_waitforattnhigh_end:
ret
uart_irq_handle_sendingattn:
ldd r16, Y+UART_IRQ_IFACE_OFFS_TICKCOUNTER
tst r16
brne uart_irq_handle_sendingattn_end
; send start bit
clr r16
rcall uart_irq_WriteDataBit
ldi r16, UART_IRQ_STATE_SENDINGSTARTBIT
std Y+UART_IRQ_IFACE_OFFS_STATE, r16
ldi r16, UART_IRQ_TIME_BITTIME1
std Y+UART_IRQ_IFACE_OFFS_TICKCOUNTER, r16
uart_irq_handle_sendingattn_end:
ret
uart_irq_handle_sendingstartbit:
ldd r16, Y+UART_IRQ_IFACE_OFFS_TICKCOUNTER
tst r16
brne uart_irq_handle_sendingstartbit_end
; store current data byte from ringbuffer
rcall uart_irq_wrbuf_readbyte
std Y+UART_IRQ_IFACE_OFFS_STATEDATA, r16
; send data bit
ldd r16, Y+UART_IRQ_IFACE_OFFS_STATEDATA
andi r16, 1
rcall uart_irq_WriteDataBit
ldd r16, Y+UART_IRQ_IFACE_OFFS_STATEDATA
ror r16
std Y+UART_IRQ_IFACE_OFFS_STATEDATA, r16
ldi r16, UART_IRQ_STATE_SENDINGDATABIT
std Y+UART_IRQ_IFACE_OFFS_STATE, r16
ldi r16, UART_IRQ_TIME_BITTIME1
std Y+UART_IRQ_IFACE_OFFS_TICKCOUNTER, r16
ldi r16, 8
std Y+UART_IRQ_IFACE_OFFS_STATECOUNTER, r16
uart_irq_handle_sendingstartbit_end:
ret
uart_irq_handle_sendingdatabit:
ldd r16, Y+UART_IRQ_IFACE_OFFS_TICKCOUNTER
tst r16
brne uart_irq_handle_sendingdatabit_end
; all bits sent?
ldd r16, Y+UART_IRQ_IFACE_OFFS_STATECOUNTER
dec r16
breq uart_irq_handle_sendingdatabit_tosendstopbit
std Y+UART_IRQ_IFACE_OFFS_STATECOUNTER, r16
; send next data bit
ldd r16, Y+UART_IRQ_IFACE_OFFS_STATEDATA
andi r16, 1
rcall uart_irq_WriteDataBit
ldd r16, Y+UART_IRQ_IFACE_OFFS_STATEDATA
ror r16
std Y+UART_IRQ_IFACE_OFFS_STATEDATA, r16
ldi r16, UART_IRQ_TIME_BITTIME1 ; send next bit
std Y+UART_IRQ_IFACE_OFFS_TICKCOUNTER, r16
rjmp uart_irq_handle_sendingstartbit_end
uart_irq_handle_sendingdatabit_tosendstopbit:
; send stop bit
ldi r16, 1
rcall uart_irq_WriteDataBit
ldi r16, UART_IRQ_STATE_SENDINGSTOPBIT
std Y+UART_IRQ_IFACE_OFFS_STATE, r16
ldi r16, UART_IRQ_TIME_BITTIME1
std Y+UART_IRQ_IFACE_OFFS_TICKCOUNTER, r16
uart_irq_handle_sendingdatabit_end:
ret
uart_irq_handle_sendingstopbit:
ldd r16, Y+UART_IRQ_IFACE_OFFS_TICKCOUNTER
tst r16
brne uart_irq_handle_sendingstopbit_end
; more to send?
ldd r16, Y+UART_IRQ_IFACE_OFFS_WRITEBUF_USED
tst r16
breq uart_irq_handle_sendingstopbit_toidle ; nothing in buffer, go idle
; wait some time before sending next byte
ldi r16, UART_IRQ_STATE_SENDINGATTN
std Y+UART_IRQ_IFACE_OFFS_STATE, r16
ldi r16, UART_IRQ_TIME_BITTIME1
std Y+UART_IRQ_IFACE_OFFS_TICKCOUNTER, r16
rjmp uart_irq_handle_sendingstopbit_end
uart_irq_handle_sendingstopbit_toidle:
ldi r16, 1
rcall uart_irq_WriteAttnBit
ldi r16, UART_IRQ_STATE_IDLE
std Y+UART_IRQ_IFACE_OFFS_STATE, r16
uart_irq_handle_sendingstopbit_end:
ret
; ---------------------------------------------------------------------------
; @routine uart_irq_rdbuf_writebyte
;
; @param R16 byte to write
; @param Y base address (for "LDD Y+nn" and "STD Y+nn")
; @return CFLAG set if okay, cleared on error (i.e. buffer full)
; @clobbers R17, X
uart_irq_rdbuf_writebyte:
m_ringbuffer_writebyte \
UART_IRQ_IFACE_READBUF_SIZE, \
UART_IRQ_IFACE_OFFS_READBUF_USED, \
UART_IRQ_IFACE_OFFS_READBUF_RDPOS, \
UART_IRQ_IFACE_OFFS_READBUF_WRPOS, \
UART_IRQ_IFACE_OFFS_READBUF_BUF
ret
; @end
; ---------------------------------------------------------------------------
; @routine uart_irq_rdbuf_readbyte
;
; @param Y base address (for "LDD Y+nn" and "STD Y+nn")
; @return CFLAG set if okay, cleared on error (i.e. buffer full)
; @return R16 byte read
; @clobbers R17, X
uart_irq_rdbuf_readbyte:
m_ringbuffer_readbyte \
UART_IRQ_IFACE_READBUF_SIZE, \
UART_IRQ_IFACE_OFFS_READBUF_USED, \
UART_IRQ_IFACE_OFFS_READBUF_RDPOS, \
UART_IRQ_IFACE_OFFS_READBUF_WRPOS, \
UART_IRQ_IFACE_OFFS_READBUF_BUF
ret
; @end
; ---------------------------------------------------------------------------
; @routine uart_irq_wrbuf_writebyte
;
; @param R16 byte to write
; @param Y base address (for "LDD Y+nn" and "STD Y+nn")
; @return CFLAG set if okay, cleared on error (i.e. buffer full)
; @clobbers R17, X
uart_irq_wrbuf_writebyte:
m_ringbuffer_writebyte \
UART_IRQ_IFACE_WRITEBUF_SIZE, \
UART_IRQ_IFACE_OFFS_WRITEBUF_USED, \
UART_IRQ_IFACE_OFFS_WRITEBUF_RDPOS, \
UART_IRQ_IFACE_OFFS_WRITEBUF_WRPOS, \
UART_IRQ_IFACE_OFFS_WRITEBUF_BUF
ret
; @end
; ---------------------------------------------------------------------------
; @routine uart_irq_wrbuf_readbyte
;
; @param Y base address (for "LDD Y+nn" and "STD Y+nn")
; @return CFLAG set if okay, cleared on error (i.e. buffer full)
; @return R16 byte read
; @clobbers R17, X
uart_irq_wrbuf_readbyte:
m_ringbuffer_readbyte \
UART_IRQ_IFACE_WRITEBUF_SIZE, \
UART_IRQ_IFACE_OFFS_WRITEBUF_USED, \
UART_IRQ_IFACE_OFFS_WRITEBUF_RDPOS, \
UART_IRQ_IFACE_OFFS_WRITEBUF_WRPOS, \
UART_IRQ_IFACE_OFFS_WRITEBUF_BUF
ret
; @end
UART_IRQ_END:
.equ MODULE_SIZE_UART_IRQ = UART_IRQ_END-UART_IRQ_BEGIN