; *************************************************************************** ; 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 uartBitbang_SendByte ; ; 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 uartBitbang_SendByte: 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 uartBitbang_SendByte_loop: ; 11 for low bit lsr r16 ; 1+ bit to send -> CARRY brcs uartBitbang_SendByte_setHigh ; HI: +2, LO: +1 uartBitbang_SendByte_setLow: sbi COM_DATA_DDR, COM_DATA_PIN ; +2 set DATA as output cbi COM_DATA_OUTPUT, COM_DATA_PIN ; +2 set DATA low rjmp uartBitbang_SendByte_wait ; +2 uartBitbang_SendByte_setHigh: cbi COM_DATA_DDR, COM_DATA_PIN ; +2 set DATA as input, pullup R makes it ONE cbi COM_DATA_OUTPUT, COM_DATA_PIN ; +2 disable internal pullup for DATA nop ; +1 nop ; +1 uartBitbang_SendByte_wait: Utils_WaitNanoSecs COM_BIT_LENGTH, 12, r22 uartBitbang_SendByte_loopEnd: dec r21 ; +1 brne uartBitbang_SendByte_loop ; +2, sum per loop: 11 cycles ; send stopbit cbi COM_DATA_DDR, COM_DATA_PIN ; +2 set DATA as input, pullup R makes it ONE cbi COM_DATA_OUTPUT, COM_DATA_PIN ; +2 disable internal pullup for DATA Utils_WaitNanoSecs COM_BIT_LENGTH, 1, r22 ; wait for one bit length sec ret ; @end ; --------------------------------------------------------------------------- ; @routine uartBitbangReceiveByte ; ; Read a byte. ; Expects interrupts to be disabled. ; ; @return CFLAG set if okay, clear otherwise ; @return R16 byte received ; @clobbers R16, R17, R20, R21, R22 uartBitbang_ReceiveByte: 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 ldi r16, 10 uartBitbang_ReceiveByte_loopStartBit1: ldi r17, 100 ; wait for 100us uartBitbang_ReceiveByte_loopStartBit2: sbis COM_DATA_INPUT, COM_DATA_PIN rjmp uartBitbang_ReceiveByte_gotStartBit Utils_WaitNanoSecs 1000, 0, r22 ; wait for 1us dec r17 brne uartBitbang_ReceiveByte_loopStartBit2 dec r16 brne uartBitbang_ReceiveByte_loopStartBit1 rjmp uartBitbang_ReceiveByte_error uartBitbang_ReceiveByte_gotStartBit: Utils_WaitNanoSecs COM_HALFBIT_LENGTH, 0, r22 ; goto middle of startbit to maximize sync stability (4) uartBitbang_ReceiveByte_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 uartBitbang_ReceiveByte_shiftIn ; HIGH: +2, rjmp, use set CFLAG clc ; LOW: +1 uartBitbang_ReceiveByte_shiftIn: ror r20 ; +1 dec r21 ; +1 brne uartBitbang_ReceiveByte_loop ; +2, sum per loop: 8 cycles rcall uartBitbang_WaitForDataHigh ; wait for start of stopbit brcc uartBitbang_ReceiveByte_error mov r16, r20 sec ret uartBitbang_ReceiveByte_error: clc ret ; @end ; --------------------------------------------------------------------------- ; @routine uartBitbang_WaitForDataLow ; ; Wait up to 1ms for data pin to become low ; @return CFLAG set if okay, clear otherwise ; @clobbers R17, R22 uartBitbang_WaitForDataLow: 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 uartBitbang_WaitForDataHigh ; ; Wait up to 1ms for data pin to become high ; @return CFLAG set if okay, clear otherwise ; @clobbers R17, R22 uartBitbang_WaitForDataHigh: 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 ; --------------------------------------------------------------------------- ; @routine uartBitbang_WaitForOneBitLength ; ; wait for one bit length (minus cycles for call and ret). ; ; @clobbers r22 uartBitbang_WaitForOneBitLength: Utils_WaitNanoSecs COM_BIT_LENGTH, 7, r22 ; wait for one bit duration (minus RCALL/RET) ret ; @end