302 lines
6.7 KiB
NASM
302 lines
6.7 KiB
NASM
; ***************************************************************************
|
|
; copyright : (C) 2025 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. *
|
|
; ***************************************************************************
|
|
|
|
|
|
; ***************************************************************************
|
|
; defines
|
|
|
|
|
|
.equ UART_ERROR_NOTFORME = 1
|
|
.equ UART_ERROR_INVALID = 2
|
|
.equ UART_ERROR_IO = 3
|
|
.equ UART_ERROR_CONTENT = 4
|
|
|
|
|
|
|
|
; ***************************************************************************
|
|
; code
|
|
|
|
.cseg
|
|
|
|
|
|
|
|
UART_Init:
|
|
; set baudrate
|
|
.if clock == 8000000
|
|
ldi r16, 25 ; (19.2Kb/s at 8MHz)
|
|
ldi r17, 0
|
|
.endif
|
|
|
|
.if clock == 1000000
|
|
ldi r16, 2 ; (19.2Kb/s at 1MHz)
|
|
ldi r17, 0
|
|
.endif
|
|
|
|
M_IO_WRITE UBRRH, r17
|
|
M_IO_WRITE UBRRL, r16
|
|
|
|
; set character format
|
|
.ifdef URSEL
|
|
ldi r16, (1<<URSEL) | (1<<USBS) | (1<<UCSZ1) | (1<<UCSZ0)
|
|
.else
|
|
ldi r16, (1<<USBS) | (1<<UCSZ1) | (1<<UCSZ0)
|
|
.endif
|
|
M_IO_WRITE UCSRC, r16
|
|
|
|
ret
|
|
; @end
|
|
|
|
|
|
|
|
; ---------------------------------------------------------------------------
|
|
; @routine UART_SendBytes @global
|
|
;
|
|
; @param X pointer data to send (in RAM)
|
|
; @param r17 number of bytes to send
|
|
; @clobbers R16, R17, X
|
|
|
|
UART_SendBytes:
|
|
tst r17
|
|
breq UART_SendBytes_secRet
|
|
|
|
rcall UART_StartTx ; (R16)
|
|
|
|
; send bytes
|
|
UART_SendBytes_loop1:
|
|
M_IO_READ r16, UCSRA
|
|
sbrs r16, UDRE
|
|
rjmp UART_SendBytes_loop1
|
|
ld r16, X+
|
|
M_IO_WRITE UDR, r16
|
|
dec r17
|
|
brne UART_SendBytes_loop1
|
|
; wait until all data sent
|
|
UART_SendBytes_loop2:
|
|
M_IO_READ r16, UCSRA
|
|
sbrs r16, TXC
|
|
rjmp UART_SendBytes_loop2
|
|
rcall UART_StopTx
|
|
UART_SendBytes_secRet:
|
|
sec
|
|
ret
|
|
; @end
|
|
|
|
|
|
|
|
; ---------------------------------------------------------------------------
|
|
; @routine UART_RecvPacket @global
|
|
;
|
|
; @param X pointer buffer (in RAM)
|
|
; @param r17 maximum number of bytes to receive
|
|
; @param r18 network address to listen to
|
|
; @clobbers r16, r17, X
|
|
|
|
UART_RecvPacket:
|
|
cpi r17, 3
|
|
brcs UART_RecvPacket_invalid
|
|
rcall uartRecvByteWithin10ms ; recv destination address
|
|
brcc UART_RecvPacket_ioError
|
|
cp r16, r18
|
|
breq UART_RecvPacket_forMe
|
|
cpi r16, 0xff
|
|
breq UART_RecvPacket_forMe
|
|
ldi r16, UART_ERROR_NOTFORME
|
|
rjmp UART_RecvPacket_clcRet
|
|
UART_RecvPacket_forMe:
|
|
st X+, r16 ; dest addr
|
|
dec r17
|
|
rcall uartRecvByteWithin10ms ; msg len (R16)
|
|
brcc UART_RecvPacket_ioError
|
|
st X+, r16
|
|
dec r17
|
|
inc r16 ; account for CRC
|
|
cp r17, r16
|
|
brcs UART_RecvPacket_badMsg
|
|
mov r17, r16
|
|
UART_RecvPacket_loop:
|
|
rcall uartRecvByteWithin10ms ; (R16)
|
|
brcc UART_RecvPacket_ioError
|
|
st X+, r16
|
|
dec r17
|
|
brne UART_RecvPacket_loop
|
|
sec
|
|
ret
|
|
UART_RecvPacket_invalid:
|
|
ldi r16, UART_ERROR_INVALID
|
|
rjmp UART_RecvPacket_clcRet
|
|
UART_RecvPacket_badMsg:
|
|
ldi r16, UART_ERROR_CONTENT
|
|
rjmp UART_RecvPacket_clcRet
|
|
UART_RecvPacket_ioError:
|
|
ldi r16, UART_ERROR_IO
|
|
UART_RecvPacket_clcRet:
|
|
clc
|
|
ret
|
|
; @end
|
|
|
|
|
|
|
|
; ---------------------------------------------------------------------------
|
|
; @routine uartRecvByteWithin10ms
|
|
;
|
|
; Wait up to 10ms for incoming byte and read it.
|
|
;
|
|
; @return CFLAG set if okay (data available), cleared on error
|
|
; @return r16 byte received (if CFLAG set)
|
|
; @clobbers: none
|
|
|
|
uartRecvByteWithin10ms:
|
|
push r20
|
|
push r22
|
|
rcall uartWaitForData10ms ; (R20, R22)
|
|
pop r22
|
|
pop r20
|
|
brcc uartRecvByteWithin10ms_end
|
|
M_IO_READ r16, UCSRA ; check for errors
|
|
andi r16, (1<<FE) | (1<<DOR)
|
|
brne uartRecvByteWithin10ms_error
|
|
M_IO_READ r16, UDR ; read data byte
|
|
sec
|
|
ret
|
|
uartRecvByteWithin10ms_error:
|
|
clc
|
|
uartRecvByteWithin10ms_end:
|
|
ret
|
|
; @end
|
|
|
|
|
|
|
|
; ---------------------------------------------------------------------------
|
|
; @routine uartWaitForData10ms
|
|
;
|
|
; Wait for incoming data for max 10 milliseconds.
|
|
;
|
|
; @return CFLAG set if okay (data available), cleared on error
|
|
; @clobbers: r20, r22
|
|
|
|
uartWaitForData10ms:
|
|
.if clock == 8000000
|
|
ldi r20, 80
|
|
.endif
|
|
.if clock == 1000000
|
|
ldi r20, 10
|
|
.endif
|
|
uartWaitForData10ms_loop:
|
|
push r20
|
|
rcall uartWaitForData1000Cycles ; (r20, r22)
|
|
pop r20
|
|
brcs uartWaitForData10ms_gotit
|
|
dec r20
|
|
brne uartWaitForData10ms_loop
|
|
clc
|
|
uartWaitForData10ms_gotit:
|
|
ret
|
|
; @end
|
|
|
|
|
|
|
|
; ---------------------------------------------------------------------------
|
|
; @routine uartWaitForData1000Cycles
|
|
;
|
|
; Wait for incoming data for max 1000 clock cycles
|
|
; (about 1ms at 1MHz, 0.125 at 8MHz)
|
|
;
|
|
; @return CFLAG set if okay (packet received), cleared on error
|
|
; @clobbers: r20, r22
|
|
|
|
uartWaitForData1000Cycles:
|
|
ldi r20, 140 ; 1
|
|
uartWaitForData_loop:
|
|
M_IO_READ r22, UCSRA ; 2
|
|
sbrc r22, RXC ; 2/3
|
|
rjmp uartWaitForData_gotit ; 2
|
|
dec r20 ; 1
|
|
brne uartWaitForData_loop ; 1/2 -> 7 per loop, max about 1000
|
|
clc ; 1
|
|
ret ; 4
|
|
uartWaitForData_gotit:
|
|
sec ; 1
|
|
ret ; 4
|
|
; @end
|
|
|
|
|
|
|
|
|
|
|
|
; ---------------------------------------------------------------------------
|
|
; @routine UART_StartRx @global
|
|
;
|
|
; @clobbers R16
|
|
|
|
UART_StartRx:
|
|
M_IO_READ r16, UCSRA ; clear errors
|
|
cbr r16, (1<<FE) | (1<<DOR) | (1<<UPE)
|
|
sbr r16, (1<<RXC)
|
|
M_IO_WRITE UCSRA, r16
|
|
|
|
M_IO_READ r16, UCSRB
|
|
sbr r16, (1<<RXEN) ; enable receive
|
|
M_IO_WRITE UCSRB, r16
|
|
|
|
ret
|
|
; @end
|
|
|
|
|
|
|
|
; ---------------------------------------------------------------------------
|
|
; @routine UART_StopRx @global
|
|
;
|
|
; @clobbers R16
|
|
|
|
UART_StopRx:
|
|
M_IO_READ r16, UCSRB
|
|
cbr r16, (1<<RXCIE | (1<<RXEN)) ; disable RX complete interrupt, disable receive
|
|
M_IO_WRITE UCSRB, r16
|
|
ret
|
|
; @end
|
|
|
|
|
|
|
|
; ---------------------------------------------------------------------------
|
|
; @routine UART_StartTx @global
|
|
;
|
|
; @param Y pointer to interface data in SRAM (see @ref UART_HW_IFACE_OFFS_STATE)
|
|
; @clobbers R16
|
|
|
|
UART_StartTx:
|
|
M_IO_READ r16, UCSRA
|
|
sbr r16, (1<<TXC) ; clear TXC interrupt
|
|
M_IO_WRITE UCSRA, r16
|
|
|
|
M_IO_READ r16, UCSRB
|
|
sbr r16, (1<<TXEN) ; enable transceive
|
|
M_IO_WRITE UCSRB, r16
|
|
|
|
ret
|
|
; @end
|
|
|
|
|
|
|
|
; ---------------------------------------------------------------------------
|
|
; @routine UART_StopTx @global
|
|
;
|
|
; @param Y pointer to interface data in SRAM (see @ref UART_HW_IFACE_OFFS_STATE)
|
|
; @clobbers R16
|
|
|
|
UART_StopTx:
|
|
M_IO_READ r16, UCSRB
|
|
cbr r16, (1<<UDRIE) | (1<<TXC) | (1<<TXEN) ; disable TX UDRE and TXC1 interrupt, enable transceive
|
|
M_IO_WRITE UCSRB, r16
|
|
|
|
ret
|
|
; @end
|
|
|
|
|
|
|