; *************************************************************************** ; 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. * ; *************************************************************************** ; *************************************************************************** ; macros ; --------------------------------------------------------------------------- ; @macro UART_BB_M_WAIT_FOR_PIN_LOW IN_REG_DATA, IN_PINNUM ; 0 1 ; Wait for a pin to become low ; @param %0 DATA register for input pin (e.g. PINB) ; @param %1 pin number for input (e.g. PORTB1) ; @return CFLAG set if okay, clear otherwise ; @clobbers R17, R22 .macro UART_BB_M_WAIT_FOR_PIN_LOW ldi r17, 200 l_loop: sbis @0, @1 rjmp l_reached Utils_WaitNanoSecs 5000, 0, r22 ; wait for 5us dec r17 brne l_loop clc rjmp l_end l_reached: sec l_end: .endmacro ; @end ; --------------------------------------------------------------------------- ; @macro UART_BB_M_WAIT_FOR_PIN_HIGH IN_REG_DATA, IN_PINNUM ; 0 1 ; Wait for a pin to become high (up to 1ms) ; @param %0 DATA register for input pin (e.g. PINB) ; @param %1 pin number for input (e.g. PORTB1) ; @return CFLAG set if okay, clear otherwise ; @clobbers R17, R22 .macro UART_BB_M_WAIT_FOR_PIN_HIGH ldi r17, 200 l_loop: sbic @0, @1 rjmp l_reached Utils_WaitNanoSecs 5000, 0, r22 ; wait for 5us dec r17 brne l_loop clc rjmp l_end l_reached: sec l_end: .endmacro ; @end ; *************************************************************************** ; code .cseg ; --------------------------------------------------------------------------- ; @routine ioRawInit ; Init raw message subsystem. ; ; @clobbers none ioRawInit: ; setup pins and interrupts cbi COM_DATA_DDR, COM_DATA_PIN ; set DATA port as input cbi COM_DATA_OUTPUT, COM_DATA_PIN ; disable internal pullup for TXD cbi COM_ATTN_DDR, COM_ATTN_PIN ; set ATTN port as input cbi COM_ATTN_OUTPUT, COM_ATTN_PIN ; disable internal pullup for ATTN ret ;@end ; --------------------------------------------------------------------------- ; @routine ioRawSendMsg ; Send a message ; ; @clobbers X (R16, R17, R21, R22) ioRawSendMsg: ioRawSendMsg_loop: ldi r16, 0xff ; expect ATTN high ldi r17, 10 rcall ioWaitForAttnState100ms ; wait for up to 1s brcs ioRawSendMsg_attnHigh ret ioRawSendMsg_attnHigh: ldi xl, LOW(flashSendBuffer) ldi xh, HIGH(flashSendBuffer) rcall ioRawSendPacket ; R16, R22 (R17, R21, X) brcc ioRawSendMsg_loop ret ; @end ; --------------------------------------------------------------------------- ; @routine ioRawWaitForValidMsg ; ; Wait for valid incoming msg ; ; First waits for ATTN line to go high (released) then wait for ATTN ; line to go low. This way we will only start listening at the beginning of ; a message. ; ; @return CFLAG set if okay (packet received), cleared on error ; @clobbers: r16, r17 (r18, r19, r20, r21, r22, X) ioRawWaitForValidMsg: ldi r16, 0xff ; expect ATTN high ldi r17, 100 rcall ioWaitForAttnState100ms ; wait for up to 10s brcc ioRawWaitForValidMsg_end ; ATTN not high, exit ldi r16, 0 ; expect ATTN low ldi r17, 100 rcall ioWaitForAttnState100ms ; wait for up to 10s brcs ioRawWaitForValidMsg_attnLow ret ioRawWaitForValidMsg_attnLow: ldi xl, LOW(flashRecvBuffer) ldi xh, HIGH(flashRecvBuffer) ldi r16, NET_MAINTENANCE_ADDR ldi r17, FLASH_RECVBUFFER_MAXLEN-3 rcall ioRawReceivePacketIntoBuffer brcs ioRawWaitForValidMsg_packetReceived ; wait until ATTN is high (up to 10s) ldi r16, 0xff ; expect ATTN high ldi r17, 100 rcall ioWaitForAttnState100ms ; wait for up to 10s clc ret ioRawWaitForValidMsg_packetReceived: ldi r16, 0xff ; expect ATTN high ldi r17, 100 rcall ioWaitForAttnState100ms ; wait for up to 10s brcc ioRawWaitForValidMsg_end ldi xl, LOW(flashRecvBuffer) ldi xh, HIGH(flashRecvBuffer) rcall NETMSG_CheckMessageInBuffer ioRawWaitForValidMsg_end: ret ; @end ; --------------------------------------------------------------------------- ; @routine ioRawReceivePacketIntoBuffer ; ; Receive a packet into buffer pointed to by X. ; Expects interrupts to be disabled. ; ; @param R16 COM address to listen to ; @param R17 maximum value for accepted msg data (i.e. buffersize minus 3) ; @param X buffer to receive to ; @return CFLAG set if okay (packet received), cleared on error ; @clobbers: r16, r17, r18, X (r19, r20, r21, r22) ioRawReceivePacketIntoBuffer: mov r18, r17 push r16 ; read destination address rcall ioRawReceiveByte ; read byte (R16, R17, R20, R21, R22) pop r17 ; pop from R16 to R17 brcc ioRawReceivePacketIntoBuffer_error #ifndef COM_ACCEPT_ALL_DEST ; accept every destination address ; compare destination address (accept "FF" and own address) cp r16, r17 breq ioRawReceivePacketIntoBuffer_acceptAddr cpi r16, 0xff breq ioRawReceivePacketIntoBuffer_acceptAddr clr r16 ; not for me rjmp ioRawReceivePacketIntoBuffer_error ; clc/ret #endif ioRawReceivePacketIntoBuffer_acceptAddr: st X+, r16 ; store dest address, lock buffer ; read msg length rcall ioRawReceiveByte ; read packet length (R16, R17, R20, R21, R22) brcc ioRawReceivePacketIntoBuffer_error st X+, r16 cp r16, r18 ; (COM2_BUFFER_SIZE-3) brcc ioRawReceivePacketIntoBuffer_error ; packet too long inc r16 ; account for checksum byte mov r17, r16 ioRawReceivePacketIntoBuffer_loop: push r17 rcall ioRawReceiveByte ; read byte (R16, R17, R20, R21, R22) pop r17 brcc ioRawReceivePacketIntoBuffer_error st X+, r16 dec r17 brne ioRawReceivePacketIntoBuffer_loop sec ret ioRawReceivePacketIntoBuffer_error: clc ret ; @end ; --------------------------------------------------------------------------- ; @routine ioRawReceiveByte ; ; Read a byte. ; Expects interrupts to be disabled. ; ; @return CFLAG set if okay, clear otherwise ; @return R16 byte received ; @clobbers R16, R20, R21, R22 (R17) ioRawReceiveByte: cbi COM_DATA_DDR, COM_DATA_PIN ; set DATA port as input cbi COM_DATA_OUTPUT, COM_DATA_PIN ; disable internal pullup for RXD ldi r21, 8 ; bits left clr r20 ; byte currently receiving ; wait for startbit rcall ioRawWaitForDataLow ; (R17, R22) brcc ioRawReceiveByte_error Utils_WaitNanoSecs COM_HALFBIT_LENGTH, 10, r22 ; goto middle of startbit to maximize sync stability ioRawReceiveByte_loop: Utils_WaitNanoSecs COM_BIT_LENGTH, 8, r22 ; 8 cycles used in the complete loop between waits sec ; +1 sbic COM_DATA_INPUT, COM_DATA_PIN ; LOW: +2, HIGH: +1 rjmp ioRawReceiveByte_shiftIn ; HIGH: +2, rjmp, use set CFLAG clc ; LOW: +1 ioRawReceiveByte_shiftIn: ror r20 ; +1 dec r21 ; +1 brne ioRawReceiveByte_loop ; +2, sum per loop: 8 cycles rcall ioRawWaitForDataHigh ; wait for start of stopbit brcc ioRawReceiveByte_error mov r16, r20 sec ret ioRawReceiveByte_error: clc ret ; @end ; --------------------------------------------------------------------------- ; @routine ioRawSendPacket ; ; Send packet over wire, handle ATTN line. ; ; @param X ptr to buffer to send ; @return CFLAGS set if okay, cleared otherwise (errorcode in R16) ; @clobbers R16, R22 (R17, R21, X) ioRawSendPacket: rcall ioRawAcquireBus ; (none) brcc ioRawSendPacket_ret rcall ioRawWaitForOneBitLength ; wait for one bit duration (R22) rcall ioRawWaitForOneBitLength ; wait for one bit duration (R22) adiw xh:xl, NETMSG_OFFS_MSGLEN ld r17, X sbiw xh:xl, NETMSG_OFFS_MSGLEN inc r17 ; account for dest addr inc r17 ; account for msglen byte inc r17 ; account for crc byte ioRawSendPacket_loop: ld r16, X+ rcall ioRawSendByte ; send byte (R16, R21, R22) brcc ioRawSendPacket_releaseBusRet dec r17 brne ioRawSendPacket_loop sec ioRawSendPacket_releaseBusRet: cbi COM_ATTN_DDR, COM_ATTN_PIN ; release ATTN line (by setting direction to IN) ioRawSendPacket_ret: ret ; @end ; --------------------------------------------------------------------------- ; @routine ioRawSendByte ; ; Send a byte. ; We only set the data pin to low at the beginning for the startbit. After that ; we only change the pin direction (e.g. input vs output): ; - for 0 bit: set DDR to output, forcing the data line low ; - for 1 bit: set DDR to input, letting the external pullup R pull the data line to HIGH ; since the output pin is still set to 0 the internal pullup is disabled ; Expects interrupts to be disabled. ; ; @param R16 byte to send ; @return CFLAG set if okay, clear otherwise ; @clobbers R16, R21, R22 ioRawSendByte: cbi COM_DATA_DDR, COM_DATA_PIN ; set DATA port as input cbi COM_DATA_OUTPUT, COM_DATA_PIN ; disable internal pullup for DATA ldi r21, 8 ; +1 bits left ; send startbit sbi COM_DATA_DDR, COM_DATA_PIN ; +2 set DATA as output cbi COM_DATA_OUTPUT, COM_DATA_PIN ; +2 set DATA low Utils_WaitNanoSecs COM_BIT_LENGTH, 1, r22 ; wait for one bit duration ; send data bits ioRawSendByte_loop: ; 9 for low bit lsr r16 ; 1+ bit to send -> CARRY brcs ioRawSendByte_setHigh ; HI: +2, LO: +1 ioRawSendByte_setLow: sbi COM_DATA_DDR, COM_DATA_PIN ; +2 set DATA as output cbi COM_DATA_OUTPUT, COM_DATA_PIN ; +2 set DATA low Utils_WaitNanoSecs COM_BIT_LENGTH, 11, r22 rjmp ioRawSendByte_loopEnd ; +2 ioRawSendByte_setHigh: cbi COM_DATA_DDR, COM_DATA_PIN ; +2 set DATA as input, pullup R makes it ONE nop ; +1 (to make pin change available) Utils_WaitNanoSecs COM_HALFBIT_LENGTH, 0, r22 ; wait for half a bit length for line to safely settle sbis COM_DATA_INPUT, COM_DATA_PIN ; +1 if no skip, +2 if skipped rjmp ioRawSendByte_error ; +2 if error (collision: we wanted line to be high but it is low) Utils_WaitNanoSecs COM_HALFBIT_LENGTH, 11, r22 ioRawSendByte_loopEnd: dec r21 ; +1 brne ioRawSendByte_loop ; +2, sum per loop: 10 cycles ; send stopbit cbi COM_DATA_DDR, COM_DATA_PIN ; +2 set DATA as input, pullup R makes it ONE Utils_WaitNanoSecs COM_BIT_LENGTH, 0, r22 ; wait for one bit length sec ret ioRawSendByte_error: clc ret ; @end ; --------------------------------------------------------------------------- ; @routine ioRawWaitForDataLow ; ; Wait up to 1ms for data pin to become low ; @return CFLAG set if okay, clear otherwise ; @clobbers R17, R22 ioRawWaitForDataLow: cbi COM_DATA_DDR, COM_DATA_PIN ; set DATA port as input cbi COM_DATA_OUTPUT, COM_DATA_PIN ; disable internal pullup for TXD UART_BB_M_WAIT_FOR_PIN_LOW COM_DATA_INPUT, COM_DATA_PIN ret ; @end ; --------------------------------------------------------------------------- ; @routine ioRawWaitForDataHigh ; ; Wait up to 1ms for data pin to become high ; @return CFLAG set if okay, clear otherwise ; @clobbers R17, R22 ioRawWaitForDataHigh: cbi COM_DATA_DDR, COM_DATA_PIN ; set DATA port as input cbi COM_DATA_OUTPUT, COM_DATA_PIN ; disable internal pullup for TXD UART_BB_M_WAIT_FOR_PIN_HIGH COM_DATA_INPUT, COM_DATA_PIN ret ; @end