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).
639 lines
16 KiB
NASM
639 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_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
|
|
|