avr: started working on new SPI-like COM protocol.

use a clock and a data line to introduce synchronisation into the
protocol to be able to work with the wide range of mcu speeds (no need for
exact timing, no need for exact calibration).
This commit is contained in:
Martin Preuss
2025-07-19 09:42:02 +02:00
parent 535a695c50
commit bdd710fc5c
7 changed files with 1675 additions and 0 deletions

664
avr/modules/com2w/com2w.asm Normal file
View File

@@ -0,0 +1,664 @@
; ***************************************************************************
; 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_COM2W_COM2W_H
#define AVR_MODULES_COM2W_COM2W_H
.dseg
com2w_iface: .byte COM2W_IFACE_SIZE
.cseg
; ---------------------------------------------------------------------------
; @routine COM2W_Init
;
; @clobbers
COM2W_Init:
ldi yl, LOW(com2w_iface)
ldi yh, HIGH(com2w_iface)
rcall NET_Interface_Init ; (R16, R17, X)
; 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
; setup pin-change interrupt for CLK
rcall com2wEnableClkIrq
inr r16, COM_IRQ_ADDR_CLK
sbr r16, (1<<COM_IRQ_BIT_CLK) ; enable pin change irq for ATTN line
outr COM_IRQ_ADDR_CLK, r16
inr r16, GIMSK ; enable pin change irq PCIE0 or PCIE1
sbr r16, (1<<COM_IRQ_GIMSK_CLK)
outr GIMSK, r16
ldi r16, (1<<COM_IRQ_GIFR_CLK) ; clear pending irq by writing 1 to ATTN bit
outr GIFR, r16
ret
; @end
; ---------------------------------------------------------------------------
; @routine COM2W_Every100ms @global
;
; @clobbers R16, Y
COM2W_Every100ms:
ldi yl, LOW(com2w_iface)
ldi yh, HIGH(com2w_iface)
push r15
in r15, SREG
cli
rcall NET_Interface_Periodically ; (R16)
rcall com2wSendNextPkg
out SREG, r15
pop r15
ret
; @end
; ---------------------------------------------------------------------------
; @routine com2wReceiveNextPkg
;
; Receive packet.
;
; @param Y pointer to start of interface data
; @clobbers R16, R17, R18, R19, R20, R22, R24, R25, X
com2wReceiveNextPkg:
; ldi r20, 3 ; make sure clock remains low for at least 15us
; rcall com2wWaitForClockHighMulti5Us ; (R20, R22)
; brcs com2wReceiveNextPkg_end ; clock got high quite soon, maybe just flaky line
rcall NET_Buffer_Alloc ; R16=buffer num (R16, R17, X)
brcs com2wReceiveNextPkg_gotBuffer
ldi r16, NET_IFACE_OFFS_ERR_NOBUF_LOW
rcall NET_Interface_IncCounter16 ; (R24, R25)
rjmp com2wReceiveNextPkg_end
com2wReceiveNextPkg_gotBuffer:
push r16 ; buffer number
rcall NET_Interface_SetIfaceNumInBuffer ; (R16, R17)
adiw xh:xl, 1
ldd r18, Y+NET_IFACE_OFFS_ADDRESS
ldi r19, NET_BUFFERS_SIZE-1
rcall com2wReceiveAndCheckMsg ; (R16, R17, R18, R19, R20, R22, R24, R25)
pop r16
brcc com2wReceiveNextPkg_relBuffer
rcall NET_AddIncomingMsgNum ; (R17, R18, X)
brcs com2wReceiveNextPkg_end
; debug
push r18
push r19
rcall LedSimple_SetFastTiming
pop r19
pop r18
push r16
ldi r16, NET_IFACE_OFFS_ERR_MISSED_LOW
rcall NET_Interface_IncCounter16 ; (R24, R25)
pop r16
; fall-through to release buffer
com2wReceiveNextPkg_relBuffer:
rcall NET_Buffer_ReleaseByNum ; (R16, X)
com2wReceiveNextPkg_end:
ret
; @end
; ---------------------------------------------------------------------------
; @routine com2wReceiveAndCheckMsg
;
; 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
; @clobbers R16 (R17, R18, R19, R20, R22, R24, R25)
com2wReceiveAndCheckMsg:
push xl
push xh
rcall com2wRecvMsg ; (r16, r17, r18, r19, r20, r22, r24, r25, X)
pop xh
pop xl
brcs com2wReceiveAndCheckMsg_recvd
; fall-through, return with CF cleared (from com2wRecvMsg)
ret
com2wReceiveAndCheckMsg_recvd:
push xl
push xh
rcall NETMSG_CheckMessageInBuffer ; (R16, R17, R18, R19, R20, X)
pop xh
pop xl
brcs com2wReceiveAndCheckMsg_msgOk
ldi r16, NET_IFACE_OFFS_ERR_CONTENT_LOW
rcall NET_Interface_IncCounter16 ; (R24, R25)
clc
ret
com2wReceiveAndCheckMsg_msgOk:
ldi r16, NET_IFACE_OFFS_PACKETSIN_LOW
rcall NET_Interface_IncCounter16 ; (R24, R25)
sec
ret
; @end
; ---------------------------------------------------------------------------
; @routine com2wSendNextPkg @global
;
; Check whether there is an outgoing message in interface data
; and send it if possible.
;
; @return CFLAG set if okay, clear on error
; @param Y pointer to start of interface data
; @clobbers (R16, R17, R18, R20, R22, R24, R25, X)
com2wSendNextPkg:
rcall NET_Interface_PeekNextOutgoingMsgNum ; (R17, R18, X)
brcc com2wSendNextPkg_end
rcall NET_Buffer_Locate ; get pointer to buffer (R17)
brcc com2wSendNextPkg_end
adiw xh:xl, 1 ; skip buffer header
rcall com2wSendMsg ; (R16, R18, R20, R22, R24, R25, X)
brcc com2wSendNextPkg_end
rcall NET_Interface_GetNextOutgoingMsgNum ; remove from stack (R17, R18, X)
rcall NET_Buffer_ReleaseByNum ; release buffer (R16, X)
sec
com2wSendNextPkg_end:
ret
; @end
; ---------------------------------------------------------------------------
; @routine com2wSendMsg
;
; @param X pointer to bytes to send
; @param Y pointer to interface data in SRAM
; @return CFLAG set if message sent, cleared otherwise
; @clobbers R16, R18 (R20, R22, R24, R25, X)
com2wSendMsg:
ldi r20, 11 ; wait for about 55us for clock low
rcall com2wWaitForClockLowMulti5Us
brcs com2wSendMsg_busy ; CLK got low while waiting, so line is busy
push r15
in r15, SREG
cli ; atomic disable irq and set CLK low
rcall com2wDisableClkIrq ; (none)
rcall com2wClkSetLow ; reserve bus (none)
out SREG, r15
pop r15
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
rcall com2wEnableClkIrq ; (none)
ldi r16, NET_IFACE_OFFS_PACKETSOUT_LOW
rcall NET_Interface_IncCounter16 ; (R24, R25)
sec
ret
com2wSendMsg_busy:
ldi r16, NET_IFACE_OFFS_ERR_BUSY_LOW
rcall NET_Interface_IncCounter16 ; (R24, R25)
clc
ret
; @end
; ---------------------------------------------------------------------------
; @routine com2wSendBytes
;
; @param R18 number of bytes to send
; @param X pointer to bytes to send
; @param Y pointer to interface data in SRAM
; @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 com2wWaitTime1 ; longer wait period (R22)
lsr r16
brcs com2wSendByte_send1
rcall com2wDataSetLow
rjmp com2wSendByte_sent
com2wSendByte_send1:
rcall com2wDataSetHigh
com2wSendByte_sent:
rcall com2wWaitTime2 ; shorter wait period (R22)
push r15
in r15, SREG
cli ; ensure time period by disabling irqs
rcall com2wClkSetHigh
rcall com2wWaitTime1 ; longer wait period (R22)
out SREG, r15
pop r15
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
; check destination address
rjmp com2wRecvMsg_forMe ; DEBUG: don't check address
cp r16, r21
breq com2wRecvMsg_forMe
cpi r16, 0xff
breq com2wRecvMsg_forMe
clr r16
rjmp com2wRecvMsg_clcRet
com2wRecvMsg_forMe:
; 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:
ldi r16, NET_IFACE_OFFS_ERR_MSGSIZE_LOW
rjmp com2wRecvMsg_incCounterRet
com2wRecvMsg_eIo:
ldi r16, NET_IFACE_OFFS_ERR_IO_LOW
com2wRecvMsg_incCounterRet:
rcall NET_Interface_IncCounter16 ; (R24, R25)
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, 31 ; wait up to 155us for clock low
rcall com2wWaitForClockLowMulti5Us ; (R20, R22)
brcs com2wRecvByte_waitForClkHigh
ldi r20, 31 ; wait up to 155us for clock low
rcall com2wWaitForClockLowMulti5Us ; (R20, R22)
brcc com2wRecvByte_end
com2wRecvByte_waitForClkHigh:
ldi r20, 31 ; wait up to 155us for clock high
rcall com2wWaitForClockHighMulti5Us ; (R20, R22)
brcs com2wRecvByte_readBit
ldi r20, 31 ; wait up to 155us for clock high
rcall com2wWaitForClockHighMulti5Us ; (R20, R22)
brcc com2wRecvByte_end
com2wRecvByte_readBit:
; 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 com2wEnableClkIrq
;
; @clobbers
com2wEnableClkIrq:
push r16
inr r16, COM_IRQ_ADDR_CLK
sbr r16, (1<<COM_IRQ_BIT_CLK) ; enable pin change irq for CLK line
outr COM_IRQ_ADDR_CLK, r16
pop r16
ret
; @end
; ---------------------------------------------------------------------------
; @routine com2wDisableClkIrq
;
; @clobbers none
com2wDisableClkIrq:
push r16
inr r16, COM_IRQ_ADDR_CLK
cbr r16, (1<<COM_IRQ_BIT_CLK) ; disable pin change irq for CLK line
outr COM_IRQ_ADDR_CLK, r16
pop r16
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 com2wWaitForClockHighMulti5Us
;
; Wait for high CLK
;
; @param R20 multiple of 5us to wait (e.g. "2" for "10" us, max: 64)
; @return CFLAG set if okay (state reached), cleared on error
; @clobbers: r20, r22
com2wWaitForClockHighMulti5Us:
.if clock == 8000000
add r20, r20 ; *2
add r20, r20 ; *4
add r20, r20 ; *8
.endif
.elif clock == 1000000
; nothing to do
.else
.error "Unhandled clock speed"
.endif
com2wWaitForClockHighMulti5Us_loop: ; 5 cycles per loop
sbic COM_CLK_INPUT, COM_CLK_PIN ; +2 if skipped, +1 if not
rjmp com2wWaitForClockHighMulti5Us_stateReached ; +2
dec r20 ; +1
brne com2wWaitForClockHighMulti5Us_loop ; +2
clc ; +1
ret ; +4
com2wWaitForClockHighMulti5Us_stateReached:
sec ; +1
ret ; +4
; @end
; ---------------------------------------------------------------------------
; @routine com2wWaitForClockLowMulti5Us
;
; Wait for low CLK
;
; @param R20 multiple of 5us to wait (e.g. "2" for "10" us, max: 64)
; @return CFLAG set if okay (state reached), cleared on error
; @clobbers: r20, r22
com2wWaitForClockLowMulti5Us:
.if clock == 8000000
add r20, r20 ; *2
add r20, r20 ; *4
add r20, r20 ; *8
.endif
.elif clock == 1000000
; nothing to do
.else
.error "Unhandled clock speed"
.endif
com2wWaitForClockLowMulti5Us_loop: ; 5 cycles per loop
sbis COM_CLK_INPUT, COM_CLK_PIN ; +2 if skipped, +1 if not
rjmp com2wWaitForClockLowMulti5Us_stateReached ; +2
dec r20 ; +1
brne com2wWaitForClockLowMulti5Us_loop ; +2
clc ; +1
ret ; +4
com2wWaitForClockLowMulti5Us_stateReached:
sec ; +1
ret ; +4
; @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
; ---------------------------------------------------------------------------
; @routine com2wPcintIsr @global @isr
;
; ISR for PCINT0/1
;
; @clobbers: none
com2wPcintIsr:
push r15
in r15, SREG
sbic COM_CLK_INPUT, COM_CLK_PIN
rjmp com2wPcintIsr_end
; low, read packet
push r16
push r17
push r18
push r19
push r20
push r21
push r22
push r24
push r25
push xl
push xh
push yl
push yh
ldi yl, LOW(com2w_iface)
ldi yh, HIGH(com2w_iface)
rcall com2wReceiveNextPkg ; (R16, R17, R18, R19, R20, R21, R22, R24, R25, X)
pop yh
pop yl
pop xh
pop xl
pop r25
pop r24
pop r22
pop r21
pop r20
pop r19
pop r18
pop r17
pop r16
com2wPcintIsr_end:
out SREG, r15
pop r15
reti
; @end
#endif ; AVR_MODULES_COM2W_COM2W_H

View File

@@ -0,0 +1,638 @@
; ***************************************************************************
; 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_COM2W_COM2W1_H
#define AVR_MODULES_COM2W_COM2W1_H
.dseg
com2w1_iface: .byte COM2W_IFACE_SIZE
.cseg
; ---------------------------------------------------------------------------
; @routine COM2W1_Init
;
; @clobbers
COM2W1_Init:
ldi yl, LOW(com2w1_iface)
ldi yh, HIGH(com2w1_iface)
rcall NET_Interface_Init ; (R16, R17, X)
ldi r16, COM2W_MODE_IDLE
rcall com2wSetMode ; (R17)
; setup CLK line (as input, disable internal pull-up resistor)
cbi COM_CLK1_DDR, COM_CLK1_PIN ; set CLK as input
.ifdef COM_CLK1_PUE
inr r16, COM_CLK1_PUE
cbr r16, (1<<COM_CLK1_PIN) ; disable pullup on CLK
outr COM_CLK1_PUE, r16
.else
cbi COM_CLK1_OUTPUT, COM_CLK1_PIN ; disable pullup on CLK
.endif
; setup DATA line (as input, disable internal pull-up resistor)
cbi COM_DATA1_DDR, COM_DATA1_PIN ; set DATA as input
.ifdef COM_DATA1_PUE
inr r16, COM_DATA1_PUE
cbr r16, (1<<COM_DATA1_PIN) ; disable pullup on DATA
outr COM_DATA1_PUE, r16
.else
cbi COM_DATA1_OUTPUT, COM_DATA1_PIN ; disable pullup on DATA
.endif
; setup pin-change interrupt for CLK
rcall com2w1EnableClkIrq
inr r16, COM_IRQ_ADDR_CLK1
sbr r16, (1<<COM_IRQ_BIT_CLK1) ; enable pin change irq for ATTN line
outr COM_IRQ_ADDR_CLK1, r16
inr r16, GIMSK ; enable pin change irq PCIE0 or PCIE1
sbr r16, (1<<COM_IRQ_GIMSK_CLK1)
outr GIMSK, r16
ldi r16, (1<<COM_IRQ_GIFR_CLK1) ; clear pending irq by writing 1 to ATTN bit
outr GIFR, r16
ret
; @end
; ---------------------------------------------------------------------------
; @routine COM2W1_Periodically @global
;
; @clobbers R16, Y
COM2W1_Periodically:
ldi yl, LOW(com2w1_iface)
ldi yh, HIGH(com2w1_iface)
rcall com2wPeriodically
ret
; @end
; ---------------------------------------------------------------------------
; @routine com2w1EnableClkIrq
;
; @clobbers
com2w1EnableClkIrq:
push r16
inr r16, COM_IRQ_ADDR_CLK1
sbr r16, (1<<COM_IRQ_BIT_CLK1) ; enable pin change irq for CLK line
outr COM_IRQ_ADDR_CLK1, r16
pop r16
ret
; @end
; ---------------------------------------------------------------------------
; @routine com2w1DisableClkIrq
;
; @clobbers none
com2w1DisableClkIrq:
push r16
inr r16, COM_IRQ_ADDR_CLK1
cbr r16, (1<<COM_IRQ_BIT_CLK1) ; disable pin change irq for CLK line
outr COM_IRQ_ADDR_CLK1, r16
pop r16
ret
; @end
; ---------------------------------------------------------------------------
; @routine com2w1ClkSetHigh
;
; @clobbers none
com2w1ClkSetHigh:
cbi COM_CLK1_DDR, COM_CLK1_PIN ; set CLK as input
.ifdef COM_CLK1_PUE
; cbi COM_CLK1_PUE, COM_CLK1_PIN ; disable pullup on CLK
.else
cbi COM_CLK1_OUTPUT, COM_CLK1_PIN ; disable pullup on CLK
.endif
ret
; @end
; ---------------------------------------------------------------------------
; @routine com2w1ClkSetLow
;
; @clobbers none
com2w1ClkSetLow:
sbi COM_CLK1_DDR, COM_CLK1_PIN ; set CLK as output
cbi COM_CLK1_OUTPUT, COM_CLK1_PIN ; set CLK low
ret
; @end
; ---------------------------------------------------------------------------
; @routine com2w1DataSetHigh
;
; @clobbers none
com2w1DataSetHigh:
cbi COM_DATA1_DDR, COM_DATA1_PIN ; set DATA as input
.ifdef COM_DATA1_PUE
; cbi COM_DATA1_PUE, COM_CLK1_PIN ; disable pullup on DATA
.else
cbi COM_DATA1_OUTPUT, COM_DATA1_PIN ; disable pullup on DATA
.endif
ret
; @end
; ---------------------------------------------------------------------------
; @routine com2w1DataSetLow
;
; @clobbers none
com2w1DataSetLow:
sbi COM_DATA1_DDR, COM_DATA1_PIN ; set DATA as output
cbi COM_DATA1_OUTPUT, COM_DATA1_PIN ; set DATA low
ret
; @end
; ---------------------------------------------------------------------------
; @routine com2w1ReadNextBit
;
; @clobbers r16, r17, r18, r20, r22
com2w1ReadNextBit:
ldi r20, 50 ; wait for up to 250us for clock rise
rcall com2w1WaitForClockHighMulti5Us ; (R20, R22)
brcc com2w1ReadNextBit_end
; clock is high now, read bit
inr r17, COM_DATA1_INPUT
; reset read timer (for leaving skipping mode)
clr r16
std Y+NET_IFACE_OFFS_READTIMER, r16
; check mode
ldd r16, Y+COM2W_IFACE_OFFS_MODE
cpi r16, COM2W_MODE_READING
brne com2w1ReadNextBit_end
; handle received bit
ldd r16, Y+COM2W_IFACE_OFFS_CURRBYTE
ldd r18, Y+COM2W_IFACE_OFFS_BITCOUNTER
andi r17, (1<<COM_DATA1_PIN)
clc
breq com2w1ReadNextBit_clockData
sec
com2w1ReadNextBit_clockData:
ror r16
std Y+COM2W_IFACE_OFFS_CURRBYTE, r16
inc r18 ; bit counter
std Y+COM2W_IFACE_OFFS_BITCOUNTER, r18
cpi r18, 8
brne com2w1ReadNextBit_end
; write byte into buffer
push xl
push xh
rcall com2wByteRecvd ; (r16, r17, r18, X)
pop xh
pop xl
; prepare for next byte
clr r16
std Y+COM2W_IFACE_OFFS_BITCOUNTER, r16
std Y+COM2W_IFACE_OFFS_CURRBYTE, r16
com2w1ReadNextBit_end:
ret
; @end
; ---------------------------------------------------------------------------
; @routine com2w1WaitForClockHighMulti5Us
;
; Wait for high CLK
;
; @param R20 multiple of 5us to wait (e.g. "2" for "10" us, max: 64)
; @return CFLAG set if okay (state reached), cleared on error
; @clobbers: r20, r22
com2w1WaitForClockHighMulti5Us:
.if clock == 8000000
add r20, r20 ; *2
add r20, r20 ; *4
add r20, r20 ; *8
.endif
.elif clock == 1000000
; nothing to do
.else
.error "Unhandled clock speed"
.endif
com2w1WaitForClockHighMulti5Us_loop: ; 5 cycles per loop
sbic COM_CLK1_INPUT, COM_CLK1_PIN ; +2 if skipped, +1 if not
rjmp com2w1WaitForClockHighMulti5Us_stateReached ; +2
dec r20 ; +1
brne com2w1WaitForClockHighMulti5Us_loop ; +2
clc ; +1
ret ; +4
com2w1WaitForClockHighMulti5Us_stateReached:
sec ; +1
ret ; +4
; @end
; ---------------------------------------------------------------------------
; @routine com2w1WaitForClockLowMulti5Us
;
; Wait for low CLK
;
; @param R20 multiple of 5us to wait (e.g. "2" for "10" us, max: 64)
; @return CFLAG set if okay (state reached), cleared on error
; @clobbers: r20, r22
com2w1WaitForClockLowMulti5Us:
.if clock == 8000000
add r20, r20 ; *2
add r20, r20 ; *4
add r20, r20 ; *8
.endif
.elif clock == 1000000
; nothing to do
.else
.error "Unhandled clock speed"
.endif
com2w1WaitForClockLowMulti5Us_loop: ; 5 cycles per loop
sbis COM_CLK1_INPUT, COM_CLK1_PIN ; +2 if skipped, +1 if not
rjmp com2w1WaitForClockLowMulti5Us_stateReached ; +2
dec r20 ; +1
brne com2w1WaitForClockLowMulti5Us_loop ; +2
clc ; +1
ret ; +4
com2w1WaitForClockLowMulti5Us_stateReached:
sec ; +1
ret ; +4
; @end
; ---------------------------------------------------------------------------
; @routine com2w1SendMsg
;
; @param X pointer to bytes to send
; @param Y pointer to interface data in SRAM
; @return CFLAG set if message sent, cleared otherwise
; @clobbers R16, R18 (R20, R22, R24, R25, X)
com2w1SendMsg:
ldi r20, 11 ; wait for about 55us for clock low
rcall com2w1WaitForClockLowMulti5Us
brcs com2w1SendMsg_busy ; CLK got low while waiting, so line is busy
push r15
in r15, SREG
cli ; atomic disable irq and set CLK low
rcall com2w1DisableClkIrq ; (none)
rcall com2w1ClkSetLow ; reserve bus (none)
out SREG, r15
pop r15
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 com2w1SendBytes ; (r16, r17, r18, r22, X)
rcall com2w1ClkSetHigh ; make sure bus is released
rcall com2w1DataSetHigh
rcall com2w1EnableClkIrq ; (none)
ldi r16, NET_IFACE_OFFS_PACKETSOUT_LOW
rcall NET_Interface_IncCounter16 ; (R24, R25)
sec
ret
com2w1SendMsg_busy:
ldi r16, NET_IFACE_OFFS_ERR_BUSY_LOW
rcall NET_Interface_IncCounter16 ; (R24, R25)
clc
ret
; @end
; ---------------------------------------------------------------------------
; @routine com2w1SendBytes
;
; @param R18 number of bytes to send
; @param X pointer to bytes to send
; @param Y pointer to interface data in SRAM
; @clobbers: r16, r18, X (r17, r22)
com2w1SendBytes:
com2w1SendBytes_loop:
rcall com2w1ClkSetLow ; (none)
rcall com2wWaitTime1 ; longer wait period (R22)
ld r16, X+
rcall com2w1SendByte ; (R16, R17, R22)
dec r18
brne com2w1SendBytes_loop
ret
; @end
; ---------------------------------------------------------------------------
; @routine com2w1SendByte
;
; @param R16 byte to send
; @clobbers: r16, r17 (r22)
com2w1SendByte:
ldi r17, 8
com2w1SendByte_loop:
rcall com2w1ClkSetLow
rcall com2wWaitTime1 ; longer wait period (R22)
lsr r16
brcs com2w1SendByte_send1
rcall com2w1DataSetLow
rjmp com2w1SendByte_sent
com2w1SendByte_send1:
rcall com2w1DataSetHigh
com2w1SendByte_sent:
rcall com2wWaitTime2 ; shorter wait period (R22)
push r15
in r15, SREG
cli ; ensure time period by disabling irqs
rcall com2w1ClkSetHigh
rcall com2wWaitTime1 ; longer wait period (R22)
out SREG, r15
pop r15
dec r17
brne com2w1SendByte_loop
ret
; @end
; ---------------------------------------------------------------------------
; @routine COM2W1_Run @global
;
; @return CFLAG set if something done, cleared otherwise
; @clobbers all
COM2W1_Run:
ldi yl, LOW(com2w1_iface)
ldi yh, HIGH(com2w1_iface)
rjmp com2w1RunMode ; (all but Y)
; @end
; ---------------------------------------------------------------------------
; @routine com2w1RunMode
;
; @clobbers all
com2w1RunMode:
cpi r16, COM2W_MODE_NUM
brcs COM2W1_Run_jump
ldi r16, COM2W_MODE_IDLE ; unknown mode, set to idle
rcall com2wSetMode ; (R17)
sec
ret
COM2W1_Run_jump:
ldi zl, LOW(com2w1ModeJumpTable)
ldi zh, HIGH(com2w1ModeJumpTable)
add zl, r16
adc zh, r16
sub zh, r16
ijmp
com2w1ModeJumpTable:
rjmp com2w1RunIdle
rjmp com2w1RunReading
rjmp com2w1RunSkipping
rjmp com2w1RunWriting
; @end
; ---------------------------------------------------------------------------
; @routine com2w1RunIdle
;
; @param Y pointer to interface data in SRAM
; @clobbers R16, R17, R22, R24, R25, X
com2w1RunIdle:
rjmp com2w1RunIdle_end ; DEBUG
push r15
in r15, SREG
cli
; look for outbound message
rcall NET_Interface_PeekNextOutgoingMsgNum ; r16=msgNum
brcs com2w1RunIdle_haveMsg
out SREG, r15
pop r15
clc
rjmp com2w1RunIdle_end
com2w1RunIdle_haveMsg:
mov r24, r16
ldi r16, COM2W_MODE_WRITING
rcall com2wSetMode ; (R17)
mov r16, r24
out SREG, r15
pop r15
push r16
rcall NET_Buffer_Locate ; (R17)
adiw xh:xl, 1
rcall com2w1SendMsg ; (R16, R17, R22, R24, R25, X)
push r15
inr r15, SREG ; save SREG (no CLI, we want to save CFLAG only)
ldi r16, COM2W_MODE_IDLE
rcall com2wSetMode ; (R17)
out SREG, r15 ; restore SREG
pop r15
pop r16
brcc com2w1RunIdle_end
push r15
in r15, SREG
cli
rcall NET_Interface_GetNextOutgoingMsgNum ; take current msg off the queue
rcall NET_Buffer_ReleaseByNum ; (R16, X)
out SREG, r15
pop r15
sec
com2w1RunIdle_end:
ret
; @end
; ---------------------------------------------------------------------------
; @routine com2w1RunReading
;
; @param Y pointer to interface data in SRAM
; @clobbers none
com2w1RunReading:
; check for timeout (Y+NET_IFACE_OFFS_READTIMER)
ldd r16, Y+NET_IFACE_OFFS_READTIMER
cpi r16, COM2W_READING_MAXREADCOUNTER
brcc com2w1RunReading_goIdle
ldd r16, Y+COM2W_IFACE_OFFS_MODECOUNTER
cpi r16, COM2W_READING_MAXMODECOUNTER
brcc com2w1RunReading_goIdle
clc
rjmp com2w1RunReading_end
com2w1RunReading_goIdle:
ldi r16, NET_IFACE_OFFS_ERR_IO_LOW
rcall NET_Interface_IncCounter16 ; (R24, R25)
ldi r16, COM2W_MODE_IDLE
rcall com2wSetMode ; (r17)
sec
com2w1RunReading_end:
ret
; @end
; ---------------------------------------------------------------------------
; @routine com2w1RunSkipping
;
; @param Y pointer to interface data in SRAM
; @clobbers r16 (r17)
com2w1RunSkipping:
; check for timeout (Y+NET_IFACE_OFFS_READTIMER)
ldd r16, Y+NET_IFACE_OFFS_READTIMER
cpi r16, COM2W_SKIPPING_MAXREADCOUNTER
brcc com2w1RunSkipping_goIdle
ldd r16, Y+COM2W_IFACE_OFFS_MODECOUNTER
cpi r16, COM2W_SKIPPING_MAXMODECOUNTER
brcc com2w1RunSkipping_goIdle
clc
rjmp com2w1RunSkipping_end
com2w1RunSkipping_goIdle:
ldi r16, COM2W_MODE_IDLE
rcall com2wSetMode ; (r17)
sec
com2w1RunSkipping_end:
ret
; @end
; ---------------------------------------------------------------------------
; @routine com2w1RunWriting
;
; @param Y pointer to interface data in SRAM
; @clobbers none
com2w1RunWriting:
; TODO: check for timeout
clc
ret
; @end
; ---------------------------------------------------------------------------
; @routine COM2W1_ClkChangeIsr @global @isr
;
; @clobbers none
COM2W1_ClkChangeIsr:
push r15
in r15, SREG
push r16
inr r16, COM_CLK1_INPUT ; read clk state early
rcall COM2W1_HandleClockInterrupt
pop r16
out SREG, r15
pop r15
reti
; @end
; ---------------------------------------------------------------------------
; @routine COM2W1_HandleClockInterrupt @global
;
; @param r16 data from COM_CLKn_INPUT
; @clobbers none
COM2W1_HandleClockInterrupt:
push r16
push r17
push r18
push xl
push xh
push yl
push yh
ldi yl, LOW(com2w1_iface)
ldi yh, HIGH(com2w1_iface)
rcall com2w1ActOnClock ; (r16, r17, r18, X)
pop yh
pop yl
pop xh
pop xl
pop r18
pop r17
pop r16
ret
; @end
; ---------------------------------------------------------------------------
; @routine com2w1ActOnClock
;
; @param r16 data from COM_CLKn_INPUT
; @clobbers r16 (r17, r18, X)
com2w1ActOnClock:
andi r16, (1<<COM_CLK1_PIN)
brne com2w1ActOnClock_end
ldd r17, Y+COM2W_IFACE_OFFS_MODE
cpi r17, COM2W_MODE_READING
breq com2w1ActOnClock_readBit
cpi r17, COM2W_MODE_IDLE
brne com2w1ActOnClock_end
rcall com2wStartReading ; (r16, r17, X)
com2w1ActOnClock_readBit:
push r20
push r22
rcall com2w1ReadNextBit ; (r16, r17, r18, r20, r22)
pop r22
pop r20
com2w1ActOnClock_end:
ret
; @end
#endif ; AVR_MODULES_COM2W_COM2W1_H

View File

@@ -0,0 +1,244 @@
; ***************************************************************************
; 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_COM2W_COMMON_H
#define AVR_MODULES_COM2W_COMMON_H
; ---------------------------------------------------------------------------
; @routine com2wPeriodically @global
;
; @clobbers R16, Y
com2wPeriodically:
push r15
in r15, SREG
cli
rcall NET_Interface_Periodically
ldd r16, Y+COM2W_IFACE_OFFS_MODECOUNTER
inc r16
breq com2wPeriodically_end
std Y+COM2W_IFACE_OFFS_MODECOUNTER, r16
com2wPeriodically_end:
out SREG, r15
pop r15
ret
; @end
; ---------------------------------------------------------------------------
; @routine com2wSetMode
;
; Doesn't change processor status flags!
;
; @param R16 mode
; @clobbers R17
com2wSetMode:
push r15
in r15, SREG
cli
ldd r17, Y+COM2W_IFACE_OFFS_MODE
cp r16, r17
breq com2wSetMode_end
std Y+COM2W_IFACE_OFFS_MODE, r16
clr r17
std Y+COM2W_IFACE_OFFS_MODECOUNTER, r17
com2wSetMode_end:
out SREG, r15
pop r15
ret
; @end
; ---------------------------------------------------------------------------
; @routine com2wStartReading
;
; @param Y pointer to interface data in SRAM
; @clobbers R16, R17, X
com2wStartReading:
mov xl, yl
mov xh, yh
adiw xh:xl, COM2W_IFACE_OFFS_BUFFER
std Y+COM2W_IFACE_OFFS_BUFPOS_LOW, xl
std Y+COM2W_IFACE_OFFS_BUFPOS_HIGH, xh
ldi r16, COM2W_BUFFER_SIZE
std Y+COM2W_IFACE_OFFS_BUFLEFT, r16
clr r16
std Y+COM2W_IFACE_OFFS_BUFUSED, r16
ldi r16, COM2W_MODE_READING
rcall com2wSetMode ; (R17)
ret
; @end
; ---------------------------------------------------------------------------
; @routine com2wByteRecvd
;
; @param r16 byte received
; @param Y pointer to interface data
; @return CFLAG set if okay, cleared on error
; @clobbers r16, r17, r18, X
com2wByteRecvd:
ldd xl, Y+COM2W_IFACE_OFFS_BUFPOS_LOW
ldd xh, Y+COM2W_IFACE_OFFS_BUFPOS_HIGH
ldd r17, Y+COM2W_IFACE_OFFS_BUFLEFT
ldd r18, Y+COM2W_IFACE_OFFS_BUFUSED
tst r17
breq com2wByteRecvd_overflow
st X+, r16
std Y+COM2W_IFACE_OFFS_BUFPOS_LOW, xl
std Y+COM2W_IFACE_OFFS_BUFPOS_HIGH, xh
inc r18
std Y+COM2W_IFACE_OFFS_BUFUSED, r18
dec r17
std Y+COM2W_IFACE_OFFS_BUFLEFT, r17
breq com2wByteRecvd_msgComplete
cpi r18, 2
sec
brne com2wByteRecvd_end
; determine msg size
inc r16 ; last byte was payload length, add byte for crc
cp r17, r16 ; compare remaining length against remaining space
brcs com2wByteRecvd_eMsgSize
std Y+COM2W_IFACE_OFFS_BUFLEFT, r16
tst r16
sec
brne com2wByteRecvd_end
com2wByteRecvd_msgComplete:
push r19 ; pushing these registers is now only needed *here* for every
push r20 ; message received. Otherwise they would have been pushed
push r24 ; on every bit adding much more execution time to the
push r25 ; irq service routine
push zl
push zh
rcall com2wMsgReceived ; (R16, R17, R18, R19, R20, R24, R25, X, Z)
pop zh
pop zl
pop r25
pop r24
pop r20
pop r19
rjmp com2wByteRecvd_end
com2wByteRecvd_overflow:
ldi r16, NET_IFACE_OFFS_ERR_MISSED_LOW
rjmp com2wByteRecvd_error
com2wByteRecvd_eMsgSize:
ldi r16, NET_IFACE_OFFS_ERR_MSGSIZE_LOW
com2wByteRecvd_error:
push r24
push r25
rcall NET_Interface_IncCounter16 ; (R24, R25)
pop r25
pop r24
ldi r16, COM2W_MODE_SKIPPING ; error, enter skipping mode
rcall com2wSetMode
clc
com2wByteRecvd_end:
ret
; @end
; ---------------------------------------------------------------------------
; @routine com2wMsgReceived
;
; @param Y pointer to interface data in SRAM
; @return CFLAG set if okay, cleared on error
; @clobbers R16, R17, R18, X, Z (R19, R20, R24, R25)
com2wMsgReceived:
mov xl, yl
mov xh, yh
adiw xh:xl, COM2W_IFACE_OFFS_BUFFER
mov zl, xl ; Z=buffer in IFACE
mov zh, xh
rcall NETMSG_CheckMessageInBuffer ; (R16, R17, R18, R19, R20, X)
brcc com2wMsgReceived_econtent
; msg valid, alloc buffer
rcall NET_Buffer_Alloc ; X=buffer, R16=bufnum (R16, R17, X)
brcc com2wMsgReceived_enobuf
mov r18, r16 ; buffer num
rcall NET_Interface_SetIfaceNumInBuffer ; (R16, R17)
adiw xh:xl, 1 ; skip buffer header
ldd r17, Y+COM2W_IFACE_OFFS_BUFUSED ; always is at least 2 here
com2wMsgReceived_copyLoop:
ld r16, Z+
st X+, r16
dec r17
brne com2wMsgReceived_copyLoop
mov r16, r18 ; buffer num
rcall NET_AddIncomingMsgNum ; (R17, R18, X)
brcc com2wMsgReceived_enoadd
ldi r16, NET_IFACE_OFFS_PACKETSIN_LOW
rcall NET_Interface_IncCounter16 ; (R24, R25)
sec
rjmp com2wMsgReceived_setIdleAndEnd
com2wMsgReceived_enoadd:
rcall NET_Buffer_ReleaseByNum
rjmp com2wMsgReceived_enobuf
com2wMsgReceived_enobuf:
ldi r16, NET_IFACE_OFFS_ERR_NOBUF_LOW
rjmp com2wMsgReceived_err
com2wMsgReceived_econtent:
ldi r16, NET_IFACE_OFFS_ERR_CONTENT_LOW
com2wMsgReceived_err:
rcall NET_Interface_IncCounter16 ; (R24, R25)
clc
com2wMsgReceived_setIdleAndEnd:
ldi r16, COM2W_MODE_IDLE
rcall com2wSetMode ; (R17, doesn't change CFLAG!)
com2wMsgReceived_end:
ret
; @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_COM2W_COMMON_H

View File

@@ -0,0 +1,74 @@
; ***************************************************************************
; 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_COM2W_DEFS_H
#define AVR_MODULES_COM2W_DEFS_H
.equ COM2W_WAITTIME1 = 30000
.equ COM2W_WAITTIME2 = 10000
.equ COM2W_SKIPPING_MAXREADCOUNTER = 1
.equ COM2W_SKIPPING_MAXMODECOUNTER = 20 ; stay max 2s in skipping mode
.equ COM2W_READING_MAXREADCOUNTER = 1
.equ COM2W_READING_MAXMODECOUNTER = 20 ; stay max 2s in reading mode
;
; .equ COM2W_DATA0_DDR = DDRA
; .equ COM2W_DATA0_INPUT = PINA
; .equ COM2W_DATA0_OUTPUT = PORTA
; .equ COM2W_DATA0_PUE = PUEA
; .equ COM2W_DATA0_PIN = PORTA2
;
; .equ COM2W_CLK0_DDR = DDRA
; .equ COM2W_CLK0_INPUT = PINA
; .equ COM2W_CLK0_OUTPUT = PORTA
; .equ COM2W_CLK0_PUE = PUEA
; .equ COM2W_CLK0_PIN = PORTA0
; .equ COM2W_IRQ_ADDR_CLK0 = PCMSK0
; .equ COM2W_IRQ_BIT_CLK0 = PCINT0 ; bit 0 in PCMSK0
; .equ COM2W_IRQ_GIFR_CLK0 = PCIF0
; .equ COM2W_IRQ_GIMSK_CLK0 = PCIE0
.equ COM2W_BUFFER_SIZE = NET_BUFFERS_SIZE-1
.equ COM2W_MODE_IDLE = 0
.equ COM2W_MODE_READING = 1
.equ COM2W_MODE_SKIPPING = 2
.equ COM2W_MODE_WRITING = 3
.equ COM2W_MODE_NUM = 4
.equ COM2W_IFACE_OFFS_BEGIN = NET_IFACE_SIZE
.equ COM2W_IFACE_OFFS_CURRBYTE = COM2W_IFACE_OFFS_BEGIN
.equ COM2W_IFACE_OFFS_BITCOUNTER = COM2W_IFACE_OFFS_BEGIN+1
.equ COM2W_IFACE_OFFS_MODE = COM2W_IFACE_OFFS_BEGIN+2
.equ COM2W_IFACE_OFFS_MODECOUNTER = COM2W_IFACE_OFFS_BEGIN+3
.equ COM2W_IFACE_OFFS_BUFPOS_LOW = COM2W_IFACE_OFFS_BEGIN+4
.equ COM2W_IFACE_OFFS_BUFPOS_HIGH = COM2W_IFACE_OFFS_BEGIN+5
.equ COM2W_IFACE_OFFS_BUFUSED = COM2W_IFACE_OFFS_BEGIN+6
.equ COM2W_IFACE_OFFS_BUFLEFT = COM2W_IFACE_OFFS_BEGIN+7
.equ COM2W_IFACE_OFFS_BUFFER = COM2W_IFACE_OFFS_BEGIN+8
.equ COM2W_IFACE_SIZE = COM2W_IFACE_OFFS_BUFFER+COM2W_BUFFER_SIZE
#endif ; AVR_MODULES_COM2W_DEFS_H