Files
aqhomecontrol/avr/modules/flash/io_com2w.asm
2025-08-19 23:03:15 +02:00

525 lines
16 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. *
; ***************************************************************************
#ifndef AVR_MODULES_FLASH_IO_COM2W_H
#define AVR_MODULES_FLASH_IO_COM2W_H
.equ COM2W_WAITTIME1 = 17000
.equ COM2W_WAITTIME2 = 10000
; ***************************************************************************
; code
.cseg
; ---------------------------------------------------------------------------
; @routine ioRawInit
;
; Init raw message subsystem.
;
ioRawInit:
; setup CLK line (as input, disable internal pull-up resistor)
cbi COM_CLK_DDR, COM_CLK_PIN ; set CLK as input
.ifdef COM_CLK_PUE
inr r16, COM_CLK_PUE
cbr r16, (1<<COM_CLK_PIN) ; disable pullup on CLK
outr COM_CLK_PUE, r16
.else
cbi COM_CLK_OUTPUT, COM_CLK_PIN ; disable pullup on CLK
.endif
; setup DATA line (as input, disable internal pull-up resistor)
cbi COM_DATA_DDR, COM_DATA_PIN ; set DATA as input
.ifdef COM_DATA_PUE
inr r16, COM_DATA_PUE
cbr r16, (1<<COM_DATA_PIN) ; disable pullup on DATA
outr COM_DATA_PUE, r16
.else
cbi COM_DATA_OUTPUT, COM_DATA_PIN ; disable pullup on DATA
.endif
ret
; @end
; ---------------------------------------------------------------------------
; @routine ioRawSendMsg
;
; Send a message
;
ioRawSendMsg:
ioRawSendMsg_loop:
ldi xl, LOW(flashSendBuffer)
ldi xh, HIGH(flashSendBuffer)
ldi r19, FLASH_RECVBUFFER_MAXLEN
rcall com2wSendMsg
brcc ioRawSendMsg_loop
ret
; @end
; ---------------------------------------------------------------------------
; @routine ioRawWaitForValidMsg
;
; Wait for valid incoming msg
;
; @return CFLAG set if okay (packet received), cleared on error
ioRawWaitForValidMsg:
; wait for CLK low
ldi r17, 20 ; wait for up to 10s
ioRawWaitForValidMsg_waitLowLoop:
.ifdef LED_PIN
sbi LED_PIN, LED_PINNUM ; toggle
.endif
ldi r18, 5 ; wait up to 500ms
rcall com2wWaitForClockLowMulti100ms ; (r18, r19, r20, r22)
brcs ioRawWaitForValidMsg_recvMsg
dec r17
brne ioRawWaitForValidMsg_waitLowLoop
clc ; no msg received
rjmp ioRawWaitForValidMsg_end
ioRawWaitForValidMsg_recvMsg:
; receive message
ldi xl, LOW(flashRecvBuffer)
ldi xh, HIGH(flashRecvBuffer)
ldi r18, NET_MAINTENANCE_ADDR
ldi r19, FLASH_RECVBUFFER_MAXLEN
rcall com2wRecvMsg
brcc ioRawWaitForValidMsg_end
; validate CRC
ldi xl, LOW(flashRecvBuffer)
ldi xh, HIGH(flashRecvBuffer)
rcall NETMSG_CheckMessageInBuffer ; (R16, R17, R18, R19, R20, X)
ioRawWaitForValidMsg_end:
ret
; @end
; ---------------------------------------------------------------------------
; @routine com2wWaitForClockLowMulti100ms
;
; wait for multiple of 100ms
;
; @param R18 number of 100ms to wait
; @return CFLAG set if okay (state reached), cleared on error
; @clobbers r18, r19, r20, r22
com2wWaitForClockLowMulti100ms:
com2wWaitForClockLowMulti100ms_loop1:
ldi r19, 100
com2wWaitForClockLowMulti100ms_loop2:
ldi r20, 100 ; wait for 1ms for clock low
rcall com2wWaitForClockLowMulti10Us ; (R20, R22)
brcs com2wWaitForClockLowMulti100ms_ret
dec r19
brne com2wWaitForClockLowMulti100ms_loop2
dec r18
brne com2wWaitForClockLowMulti100ms_loop1
clc
com2wWaitForClockLowMulti100ms_ret:
ret
; @end
; ---------------------------------------------------------------------------
; @routine com2wSendMsg
;
; @param X pointer to bytes to send
; @return CFLAG set if message sent, cleared otherwise
; @clobbers R18, R20 (R16, R17, R20, R22, X)
com2wSendMsg:
ldi r20, 6 ; wait for about 60us for clock low
rcall com2wWaitForClockLowMulti10Us
brcs com2wSendMsg_busy ; CLK got low while waiting, so line is busy
rcall com2wClkSetLow ; reserve bus (none)
adiw xh:xl, NETMSG_OFFS_MSGLEN
ld r18, X
sbiw xh:xl, NETMSG_OFFS_MSGLEN
inc r18 ; adjust for DESTADDR
inc r18 ; adjust for MSGLEN
inc r18 ; adjust for CRCBYTE
rcall com2wWaitTime1 ; longer wait period (R22)
rcall com2wSendBytes ; (r16, r17, r18, r22, X)
rcall com2wClkSetHigh ; make sure bus is released
rcall com2wDataSetHigh
sec
ret
com2wSendMsg_busy:
clc
ret
; @end
; ---------------------------------------------------------------------------
; @routine com2wSendBytes
;
; @param R18 number of bytes to send
; @param X pointer to bytes to send
; @clobbers: r16, r18, X (r17, r22)
com2wSendBytes:
com2wSendBytes_loop:
rcall com2wClkSetLow ; (none)
rcall com2wWaitTime1 ; longer wait period (R22)
ld r16, X+
rcall com2wSendByte ; (R16, R17, R22)
dec r18
brne com2wSendBytes_loop
ret
; @end
; ---------------------------------------------------------------------------
; @routine com2wSendByte
;
; @param R16 byte to send
; @clobbers: r16, r17 (r22)
com2wSendByte:
ldi r17, 8
com2wSendByte_loop:
rcall com2wClkSetLow
rcall com2wWaitTime2 ; shorter wait period (R22)
lsr r16
brcs com2wSendByte_send1
rcall com2wDataSetLow
rjmp com2wSendByte_sent
com2wSendByte_send1:
rcall com2wDataSetHigh
com2wSendByte_sent:
Utils_WaitNanoSecs 5000, 0, r22 ; wait for very short time to ensure data is stable when clock rises
rcall com2wClkSetHigh
rcall com2wWaitTime2 ; shorter wait period (R22)
dec r17
brne com2wSendByte_loop
ret
; @end
; ---------------------------------------------------------------------------
; @routine com2wRecvMsg
;
; Receive a packet into buffer pointed to by X.
; Expects interrupts to be disabled.
;
; @param R18 COM address to listen to
; @param R19 max buffer size
; @param X buffer to receive to
; @return CFLAG set if msg received, cleared on error (see R16)
; @return R16 if CFLAG cleared: 0=message not for this node, otherwise error
; @clobbers r16, r17, r18, r19, r20, r22, r24, r25, X
com2wRecvMsg:
mov r21, r18 ; address
; read destination address
rcall com2wRecvByte ; (r17, r18, r20, r22)
brcc com2wRecvMsg_eIo
; store in buffer
subi r19, 1
brcs com2wRecvMsg_eBadSize
st X+, r16
; read remaining msg size
rcall com2wRecvByte ; (r17, r18, r20, r22)
brcc com2wRecvMsg_eIo
; store in buffer
subi r19, 1
brcs com2wRecvMsg_eBadSize
st X+, r16
inc r16 ; account for CRC byte
sub r19, r16
brcs com2wRecvMsg_eBadSize
mov r19, r16
com2wRecvMsg_loop:
rcall com2wRecvByte ; (r17, r18, r20, r22)
brcc com2wRecvMsg_eIo
st X+, r16
dec r19
brne com2wRecvMsg_loop
sec
rjmp com2wRecvMsg_end
com2wRecvMsg_eBadSize:
com2wRecvMsg_eIo:
com2wRecvMsg_clcRet:
clc
com2wRecvMsg_end:
ret
; @end
; ---------------------------------------------------------------------------
; @routine com2wRecvByte
;
; @return CFLAG set if byte received, cleared on error
; @return r16 byte received
; @clobbers r17, r18, r20, r22
com2wRecvByte:
ldi r17, 8
clr r16
com2wRecvByte_loop:
ldi r20, 70 ; wait up to 700us for clock low
rcall com2wWaitForClockLowMulti10Us ; (R20, R22)
brcc com2wRecvByte_end
ldi r20, 70 ; wait up to 700us for clock high
rcall com2wWaitForClockHighMulti10Us ; (R20, R22)
brcc com2wRecvByte_end
; handle received bit
inr r18, COM_DATA_INPUT
andi r18, (1<<COM_DATA_PIN)
clc
breq com2wRecvByte_clockData
sec
com2wRecvByte_clockData:
ror r16
dec r17
brne com2wRecvByte_loop
sec
com2wRecvByte_end:
ret
; @end
; ---------------------------------------------------------------------------
; @routine com2wClkSetHigh
;
; @clobbers none
com2wClkSetHigh:
cbi COM_CLK_DDR, COM_CLK_PIN ; set CLK as input
.ifdef COM_CLK_PUE
; cbi COM_CLK_PUE, COM_CLK_PIN ; disable pullup on CLK
.else
cbi COM_CLK_OUTPUT, COM_CLK_PIN ; disable pullup on CLK
.endif
ret
; @end
; ---------------------------------------------------------------------------
; @routine com2wClkSetLow
;
; @clobbers none
com2wClkSetLow:
sbi COM_CLK_DDR, COM_CLK_PIN ; set CLK as output
cbi COM_CLK_OUTPUT, COM_CLK_PIN ; set CLK low
ret
; @end
; ---------------------------------------------------------------------------
; @routine com2wDataSetHigh
;
; @clobbers none
com2wDataSetHigh:
cbi COM_DATA_DDR, COM_DATA_PIN ; set DATA as input
.ifdef COM_DATA_PUE
; cbi COM_DATA_PUE, COM_CLK_PIN ; disable pullup on DATA
.else
cbi COM_DATA_OUTPUT, COM_DATA_PIN ; disable pullup on DATA
.endif
ret
; @end
; ---------------------------------------------------------------------------
; @routine com2wDataSetLow
;
; @clobbers none
com2wDataSetLow:
sbi COM_DATA_DDR, COM_DATA_PIN ; set DATA as output
cbi COM_DATA_OUTPUT, COM_DATA_PIN ; set DATA low
ret
; @end
; ---------------------------------------------------------------------------
; @routine com2wWaitForClockLowMulti10Us
;
; Wait for low CLK
;
; @param R20 multiple of 10us to wait (e.g. "2" for "20" us)
; @return CFLAG set if okay (state reached), cleared on error
; @clobbers: r20, r22
com2wWaitForClockLowMulti10Us:
.if clock > 1000000
; begin version for > 1000000 Hz
ldi r22, clock/1000000
com2wWaitForClockLowMulti10Us_loop0:
push r20
com2wWaitForClockLowMulti10Us_loop: ; 10 cycles per loop
sbis COM_CLK_INPUT, COM_CLK_PIN ; +2 if skipped, +1 if taken
rjmp com2wWaitForClockLowMulti10Us_stateReached ; (+2 if taken)
sbis COM_CLK_INPUT, COM_CLK_PIN ; +2 if skipped, +1 if taken
rjmp com2wWaitForClockLowMulti10Us_stateReached ; (+2 if taken)
sbis COM_CLK_INPUT, COM_CLK_PIN ; +2 if skipped, +1 if taken
rjmp com2wWaitForClockLowMulti10Us_stateReached ; (+2 if taken)
nop ; +1
dec r20 ; +1
brne com2wWaitForClockLowMulti10Us_loop ; +2 if TRUE, +1 if FALSE
pop r20
dec r22 ; +1
brne com2wWaitForClockLowMulti10Us_loop0 ; +1 if FALSE, +2 if TRUE
clc ; +1
ret ; +4
com2wWaitForClockLowMulti10Us_stateReached:
pop r20
sec ; +1
ret ; +4
; end version for > 1000000 Hz
.elif clock < 1000000
.error "Clock speed too low"
.else
; begin version for 1000000 Hz
com2wWaitForClockLowMulti10Us_loop: ; 10 cycles per loop
sbis COM_CLK_INPUT, COM_CLK_PIN ; +2 if skipped, +1 if taken
rjmp com2wWaitForClockLowMulti10Us_stateReached ; (+2 if taken)
sbis COM_CLK_INPUT, COM_CLK_PIN ; +2 if skipped, +1 if taken
rjmp com2wWaitForClockLowMulti10Us_stateReached ; (+2 if taken)
sbis COM_CLK_INPUT, COM_CLK_PIN ; +2 if skipped, +1 if taken
rjmp com2wWaitForClockLowMulti10Us_stateReached ; (+2 if taken)
nop ; +1
dec r20 ; +1
brne com2wWaitForClockLowMulti10Us_loop ; +2 if TRUE, +1 if FALSE
clc ; +1
ret ; +4
com2wWaitForClockLowMulti10Us_stateReached:
sec ; +1
ret ; +4
; end version for 1000000 Hz
.endif
; @end
; ---------------------------------------------------------------------------
; @routine com2wWaitForClockHighMulti10Us
;
; Wait for high CLK
;
; @param R20 multiple of 10us to wait (e.g. "2" for "20" us)
; @return CFLAG set if okay (state reached), cleared on error
; @clobbers: r20, r22
com2wWaitForClockHighMulti10Us:
.if clock > 1000000
; begin version for > 1000000 Hz
ldi r22, clock/1000000
com2wWaitForClockHighMulti10Us_loop0:
push r20
com2wWaitForClockHighMulti10Us_loop: ; 10 cycles per loop
sbic COM_CLK_INPUT, COM_CLK_PIN ; +2 if skipped, +1 if taken
rjmp com2wWaitForClockHighMulti10Us_stateReached ; (+2 if taken)
sbic COM_CLK_INPUT, COM_CLK_PIN ; +2 if skipped, +1 if taken
rjmp com2wWaitForClockHighMulti10Us_stateReached ; (+2 if taken)
sbic COM_CLK_INPUT, COM_CLK_PIN ; +2 if skipped, +1 if taken
rjmp com2wWaitForClockHighMulti10Us_stateReached ; (+2 if taken)
nop ; +1
dec r20 ; +1
brne com2wWaitForClockHighMulti10Us_loop ; +2 if TRUE, +1 if FALSE
pop r20
dec r22 ; +1
brne com2wWaitForClockHighMulti10Us_loop0 ; +1 if FALSE, +2 if TRUE
clc ; +1
ret ; +4
com2wWaitForClockHighMulti10Us_stateReached:
pop r20
sec ; +1
ret ; +4
; end version for > 1000000 Hz
.elif clock < 1000000
.error "Clock speed too low"
.else
; begin version for 1000000 Hz
com2wWaitForClockHighMulti10Us_loop: ; 10 cycles per loop
sbic COM_CLK_INPUT, COM_CLK_PIN ; +2 if skipped, +1 if taken
rjmp com2wWaitForClockHighMulti10Us_stateReached ; (+2 if taken)
sbic COM_CLK_INPUT, COM_CLK_PIN ; +2 if skipped, +1 if taken
rjmp com2wWaitForClockHighMulti10Us_stateReached ; (+2 if taken)
sbic COM_CLK_INPUT, COM_CLK_PIN ; +2 if skipped, +1 if taken
rjmp com2wWaitForClockHighMulti10Us_stateReached ; (+2 if taken)
nop ; +1
dec r20 ; +1
brne com2wWaitForClockHighMulti10Us_loop ; +2 if TRUE, +1 if FALSE
clc ; +1
ret ; +4
com2wWaitForClockHighMulti10Us_stateReached:
sec ; +1
ret ; +4
; end version for 1000000 Hz
.endif
; @end
; ---------------------------------------------------------------------------
; @routine com2wWaitTime1
;
; waits for longer period (e.g. 30ns)
;
; @clobbers R22
com2wWaitTime1:
Utils_WaitNanoSecs COM2W_WAITTIME1, 7, r22 ; wait for longer time (minus RCALL and RET)
ret
; @end
; ---------------------------------------------------------------------------
; @routine com2wWaitTime2
;
; waits for shorter period (e.g. 10ns)
;
; @clobbers R22
com2wWaitTime2:
Utils_WaitNanoSecs COM2W_WAITTIME2, 7, r22 ; wait for shorter time (minus RCALL and RET)
ret
; @end
#endif ; AVR_MODULES_FLASH_IO_COM2W_H