Files
aqhomecontrol/avr/comproto_addr.asm
2023-02-05 23:43:16 +01:00

358 lines
9.2 KiB
NASM

; ***************************************************************************
; This sub-module contains code for cooperative address management.
; Because the network of nodes doen't have a central control unit the nodes
; need to assign addresses to themselves with help from the community of existing
; nodes.
;
; The protocol is as follows:
; 1) a node needs an address. It sends the packet "NEED_ADDRESS"
; 2) every node which already has an address sees this message and answers with
; a "HAVE_ADDRESS" packet. The nodes must not answer all at once to avoid
; congestions. Instead, the time it takes for a node to respond depends on
; its own address (e.g. a node with address 10 will wait for 10s before answering)
; 3) the initial node collects all "HAVE_ADDRESS" packets and puts the addresses
; received this way into a bitfield in which for every address received a bit is
; set
; 4) after about 130s all nodes in the address range 1-127 should have answered.
; So after this time the initial node looks at the bitfield of received addresses
; and selects the first unused address (corresponding bit cleared in bitfield).
; For this address the initial node sends a "CLAIM_ADDRESS" packet, three times
; with about 30s between the packets.
; 5) if the to-be-claimed address is already in use by another node, that node will
; send a "DENY_ADDRESS" packet. This is the second line of defense against address
; collisions and should very rarely occur.
; 6) after no node denied the to-be-claimed address the initial node finally takes the
; address and sends a "HAVE_ADDRESS" packet.
; 7) this concludes the address assignemt protocol
; ***************************************************************************
; code
.cseg
; ***************************************************************************
; onEverySecond handlers
cproHandle1sSendingHaveAddress:
; waiting for counter to reach zero to send a HAVE_ADDRESS packet
lds r16, cproAddressWaitCounter
dec r16
sts cproAddressWaitCounter, r16
brne cproHandle1sSendingHaveAddress_done ; counter not 0, done for now
rcall CPRO_EnqueueHaveAddress ; counter is 0, send HAVE_ADDRESS
ldi r16, CPRO_MODE_NORMAL
sts cproMode, r16
cproHandle1sSendingHaveAddress_done:
ret
cproHandle1sGetAddrStarted:
lds r16, cproAddressWaitCounter
dec r16
sts cproAddressWaitCounter, r16
brne cproHandle1sGetAddrStarted_done ; counter not 0, done for now
rcall cproClaimFirstFreeAddr ; counter 0, find first free address and claim it
brcs cproHandle1sGetAddrStarted_done
; no free address, abort TODO: send an error message to bus ("bus full")
ldi r16, CPRO_MODE_NORMAL
sts cproMode, r16
cproHandle1sGetAddrStarted_done:
ret
cproHandle1sClaimingAddr12:
lds r16, cproAddressWaitCounter
dec r16
sts cproAddressWaitCounter, r16
brne cproHandle1sClaimingAddr12_done ; counter not 0, done for now
push r17
lds r19, cproAddrRangeBegin ; currently claimed address
rcall CPRO_EnqueueClaimAddress
pop r17
ldi r16, CPRO_WAITTIME_CLAIMADDR
sts cproAddressWaitCounter, r16
inc r17 ; next mode (CPRO_MODE_CLAIMING_ADDR1 and 2)
sts cproMode, r17
cproHandle1sClaimingAddr12_done:
ret
cproHandle1sClaimingAddr3:
lds r16, cproAddressWaitCounter
dec r16
sts cproAddressWaitCounter, r16
brne cproHandle1sClaimingAddr3_done ; counter not 0, done for now
; claimed given address 3rd time, set address and enter "normal" mode
lds r16, cproAddrRangeBegin ; currently sent address is in cproAddrRangeBegin
in r15, SREG
cli
sts comAddress, r16 ; write address into eeprom
ldi xl, LOW(EEPROM_OFFS_COMADDR)
ldi xh, HIGH(EEPROM_OFFS_COMADDR)
; TODO rcall Utils_WriteEeprom
ldi r16, CPRO_MODE_NORMAL ; set mode to "normal"
sts cproMode, r16
rcall CPRO_EnqueueHaveAddress
out SREG, r15
cproHandle1sClaimingAddr3_done:
ret
; ***************************************************************************
; onPacketReceived handlers
cproHandlePckNeedAddr:
lds r17, cproMode
cpi r17, CPRO_MODE_NORMAL
brne cproHandlePckNeedAddr_done
; enter CPRO_MODE_SENDING_HAVE_ADDRESS mode
lds r16, comAddress
tst r16
breq cproHandlePckNeedAddr_done ; we have no address, don't handle
rcall cproEnterSendingHaveAddressMode
cproHandlePckNeedAddr_done:
sec
ret
cproHandlePckHaveAddr:
lds r17, cproMode
cpi r17, CPRO_MODE_GETADDRSTARTED
brne cproHandlePckHaveAddr_done
; validate address
ldd r16, y+(COM_BUFFER_OFFS_DATA+CPRO_PACKET_HAVEADDR_OFFS_ADDRESS)
tst r16
breq cproHandlePckHaveAddr_done ; invalid address, ignore
cpi r16, 127
brcc cproHandlePckHaveAddr_done ; invalid address, ignore
; set bit corresponding to given address in bitfield of used addresses
dec r16
rcall cproSetBitInBitfield
cproHandlePckHaveAddr_done:
sec
ret
cproHandlePckClaimAddr:
ldd r16, y+(COM_BUFFER_OFFS_DATA+CPRO_PACKET_CLAIMADDR_OFFS_ADDRESS)
tst r16
breq cproHandlePckClaimAddr_done
cpi r16, 0xff
breq cproHandlePckClaimAddr_done
lds r17, comAddress
cp r16, r17
brne cproHandlePckClaimAddr_done
rcall CPRO_EnqueueDenyAddress
cproHandlePckClaimAddr_done:
sec
ret
cproHandleDenyAddr:
ldd r16, y+(COM_BUFFER_OFFS_DATA+CPRO_PACKET_DENYADDR_OFFS_ADDRESS)
lds r17, cproAddrRangeBegin
cp r16, r17
brne cproHandleDenyAddr_done ; not our currently claimed address, ignore
rcall cproClaimNextFreeAddr ; claim next free address
brcs cproHandleDenyAddr_done
; no free address, abort TODO: send an error message to bus ("bus full")
ldi r16, CPRO_MODE_NORMAL
sts cproMode, r16
cproHandleDenyAddr_done:
sec
ret
cproClaimFirstFreeAddr:
rjmp cproFindAndClaimFreeAddr
cproClaimNextFreeAddr:
lds r16, cproAddrRangeBegin
inc r16
sts cproAddrRangeBegin, r16
rjmp cproFindAndClaimFreeAddr
; IN:
; - R16: address to claim
cproFindAndClaimFreeAddr:
lds r16, cproAddrRangeBegin
lds r17, cproAddrRangeEnd
cp r16, r17
brge cproFindAndClaimFreeAddr_error
rcall cproFindFreeAddr
brcc cproFindAndClaimFreeAddr_error
lds r19, cproAddrRangeBegin ; currently claimed address
rcall CPRO_EnqueueClaimAddress
ldi r16, CPRO_WAITTIME_CLAIMADDR
sts cproAddressWaitCounter, r16
ldi r16, CPRO_MODE_CLAIMING_ADDR1
sts cproMode, r16
sec
ret
cproFindAndClaimFreeAddr_error:
clc
ret
cproHandleAddrRange: ; not handled for now
; TODO
clc
ret
cproEnterSendingHaveAddressMode:
sts cproAddressWaitCounter, r16 ; set counter to own address
ldi r16, CPRO_MODE_SENDING_HAVE_ADDRESS
sts cproMode, r16
ret
CPRO_StartGetAddrProcedure:
; reset bitfield of used addresses
ldi xh, HIGH(cproUsedAddresses)
ldi xl, LOW(cproUsedAddresses)
clr r16
ldi r17, 16
rcall Utils_FillSram
; preset range
ldi r16, 1
sts cproAddrRangeBegin, r16
ldi r16, 126
sts cproAddrRangeEnd, r16
; send "NEED_ADDRESS" packet
rcall CPRO_EnqueueNeedAddress
brcc CPRO_StartGetAddrProcedure_error
; set waittimer for sampling of "HAVE_ADDRESS" packets
ldi r16, CPRO_WAITTIME_GETADDR
sts cproAddressWaitCounter, r16
ldi r16, CPRO_MODE_GETADDRSTARTED
sts cproMode, r16
sec
ret
CPRO_StartGetAddrProcedure_error:
clc
ret
; ---------------------------------------------------------------------------
; cproFindFreeAddr
;
; find a free address in the bitfield cproUsedAddresses
;
; IN:
; - nothing
; OUT:
; - CFLAG: set if handled, cleared otherwise
; - R16: free address (if CFLAG set)
; USED: R16, R17, R18, R19, R20, R21 X, (R1, R2, Z)
cproFindFreeAddr:
ldi xl, LOW(cproUsedAddresses)
ldi xh, HIGH(cproUsedAddresses)
lds r16, cproAddrRangeBegin
dec r16
rcall Utils_GetPosAndMaskInBitField ; r1=bit pos, r2=mask (r1, r2, r17, Z)
clr r17
add xl, r1
adc xh, r17 ; X: pointer to byte
mov r17, r2 ; mask
lds r18, cproAddrRangeBegin
dec r18
lds r19, cproAddrRangeEnd
inc r19 ; to make comparision easier
ldi r20, 8
cproFindFreeAddr_byteLoop:
ld r16, X+
cpi r16, 0xff
breq cproFindFreeAddr_nextByte ; byte full, skip
cproFindFreeAddr_bitLoop:
mov r21, r16
and r21, r17
brne cproFindFreeAddr_nextBit
; found a clear bit, return
inc r18
sts cproAddrRangeBegin, r18
mov r16, r18
sec
ret
cproFindFreeAddr_nextBit:
inc r18 ; next address
lsl r17 ; shift mask to the left
brcc cproFindFreeAddr_bitLoop
dec r18 ; take back inc
cproFindFreeAddr_nextByte:
add r18, r20
andi r18, 0xf8 ; clear lower 3 bits
cp r18, r19 ; compare to end address+1
brcc cproFindFreeAddr_allFull
ldi r17, 0x01 ; mask
rjmp cproFindFreeAddr_byteLoop
cproFindFreeAddr_allFull:
clc
ret
; ---------------------------------------------------------------------------
; cproSetBitInBitfield
;
; IN:
; - R16 : bit number to set (0-127)
; OUT:
; - nothing
; USED: R16, R17, X (r1, r2, Z)
cproSetBitInBitfield:
; set bit corresponding to given address in bitfield of used addresses
rcall Utils_GetPosAndMaskInBitField ; get offset into R1, mask into R2 (r1, r2, r17, Z)
ldi xl, LOW(cproUsedAddresses)
ldi xh, HIGH(cproUsedAddresses)
clr r17
add xl, r1
adc xh, r17
ld r16, X
or r16, r2
st X, r16
ret