; *************************************************************************** ; copyright : (C) 2024 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. * ; *************************************************************************** ; --------------------------------------------------------------------------- ; @macro m_uart_irq_handle_tick ; ; @clobbers any ; ; @param %0 COM_DATA_DDR ; @param %1 COM_DATA_INPUT ; @param %2 COM_DATA_OUTPUT ; @param %3 COM_DATA_PIN ; @param %4 COM_ATTN_DDR ; @param %5 COM_ATTN_INPUT ; @param %6 COM_ATTN_OUTPUT ; @param %7 COM_ATTN_PIN ; @param %8 uartIrqDataIface1 .macro m_uart_irq_handle_tick ldi yl, LOW(@8) ldi yh, HIGH(@8) ldi zl, LOW(_jumptable) ldi zh, HIGH(_jumptable) rjmp UART_Irq_HandleTick _jumptable: rjmp _readDataBit ; read DATA BIT (r16=bit) rjmp _readAttnBit ; read ATTN BIT (r16=bit) rjmp _writeDataBit ; write DATA BIT (r16=bit) rjmp _writeAttnBit ; write ATTN BIT (r16=bit) _readDataBit: clr r16 sbic @1, @3 inc r16 ret _readAttnBit: clr r16 sbic @5, @7 inc r16 ret _writeDataBit: tst r16 brne _writeDataBit1 sbi @0, @3 ; set data line to output cbi @2, @3 ; set line LOW ret _writeDataBit1: cbi @0, @3 ; set data line to input (external pullup R makes it HIGH) cbi @2, @3 ; disable internal pullup ret _writeAttnBit: tst r16 brne _writeAttnBit1 sbi @4, @7 ; set ATTN line to output (makes it LOW) cbi @2, @7 ; set line LOW ret _writeAttnBit1: cbi @4, @7 ; set ATTN line to output (external pullup R makes it HIGH) cbi @2, @7 ; disable internal pullup ret .endmacro ; @end .dseg uartIrqBase: .byte 4 ;uartIrqState: .byte 1 ;uartIrqCounter: .byte 1 ;uartIrqStateCounter: .byte 1 ;uartIrqCurrentByte: .byte 1 .cseg UART_IRQ_BEGIN: ; --------------------------------------------------------------------------- ; @routine UART_Irq_InitIface @global ; ; @param Y pointer to interface data in SRAM (see @ref UART_IRQ_IFACE_OFFS_STATE) ; @clobbers any UART_Irq_InitIface: ; preset SRAM data area mov xh, yh mov xl, yl clr r16 ldi r17, UART_IRQ_IFACE_SIZE rcall Utils_FillSram ret ; @end ; --------------------------------------------------------------------------- ; @routine UART_Irq_HandleTick @global ; ; @param Y pointer to interface data in SRAM (see @ref UART_IRQ_IFACE_OFFS_STATE) ; @param Z pointer to IFACE jump table for the given interface ; @clobbers r16, r17, X UART_Irq_HandleTick: ldd r16, Y+UART_IRQ_IFACE_OFFS_TICKCOUNTER tst r16 breq UART_Irq_HandleTick_handle dec r16 std Y+UART_IRQ_IFACE_OFFS_TICKCOUNTER, r16 UART_Irq_HandleTick_handle: ldd r16, Y+UART_IRQ_IFACE_OFFS_STATE rjmp uart_irq_JumpToStateFunction ; (r16, r17, r18, r19, X) ; @end ; --------------------------------------------------------------------------- ; @routine uart_irq_JumpToStateFunction ; ; @param r16 function ; @param Y pointer to interface data in SRAM (see @ref UART_IRQ_IFACE_OFFS_STATE) ; @param Z pointer to IFACE jump table for the given interface ; @clobbers r16, X (r17) uart_irq_JumpToStateFunction: cpi r16, UART_IRQ_STATE_COUNT ; invalid function num? brcc uart_irq_JumpToStateFunction_end ldi xl, LOW(uart_irq_state_jumptable) ldi xh, HIGH(uart_irq_state_jumptable) add xl, r16 clr r16 adc xh, r16 push xl push xh uart_irq_JumpToStateFunction_end: ret ; indirect jump to address we just pushed to the stack uart_irq_state_jumptable: rjmp uart_irq_handle_idle ; (R16) rjmp uart_irq_handle_waitforattnhigh ; (R16) rjmp uart_irq_handle_waitforstartbit ; (R16) rjmp uart_irq_handle_waitfordatabit ; (R16) rjmp uart_irq_handle_waitforstopbit ; (R16, R17, X) rjmp uart_irq_handle_sendingattn ; (R16) rjmp uart_irq_handle_sendingstartbit ; (R16, R17, X) rjmp uart_irq_handle_sendingdatabit ; (R16) rjmp uart_irq_handle_sendingstopbit ; (R16) ; @end ; --------------------------------------------------------------------------- ; @routine uart_irq_ReadDataBit ; ; @param Y pointer to interface data in SRAM (see @ref UART_IRQ_IFACE_OFFS_STATE) ; @param Z pointer to IFACE jump table for the given interface ; @clobbers r16 uart_irq_ReadDataBit: adiw zh:zl, UART_IRQ_IFACE_PINFN_READDATA push zl push zh sbiw zh:zl, UART_IRQ_IFACE_PINFN_READDATA ret ; indirect jump to address we just pushed to the stack ; @end ; --------------------------------------------------------------------------- ; @routine uart_irq_ReadAttnBit ; ; @param Y pointer to interface data in SRAM (see @ref UART_IRQ_IFACE_OFFS_STATE) ; @param Z pointer to IFACE jump table for the given interface ; @clobbers r16 uart_irq_ReadAttnBit: adiw zh:zl, UART_IRQ_IFACE_PINFN_READATTN push zl push zh sbiw zh:zl, UART_IRQ_IFACE_PINFN_READATTN ret ; indirect jump to address we just pushed to the stack ; @end ; --------------------------------------------------------------------------- ; @routine uart_irq_WriteDataBit ; ; @param Y pointer to interface data in SRAM (see @ref UART_IRQ_IFACE_OFFS_STATE) ; @param Z pointer to IFACE jump table for the given interface ; @clobbers none uart_irq_WriteDataBit: adiw zh:zl, UART_IRQ_IFACE_PINFN_WRITEDATA push zl push zh sbiw zh:zl, UART_IRQ_IFACE_PINFN_WRITEDATA ret ; indirect jump to address we just pushed to the stack ; @end ; --------------------------------------------------------------------------- ; @routine uart_irq_WriteAttnBit ; ; @param Y pointer to interface data in SRAM (see @ref UART_IRQ_IFACE_OFFS_STATE) ; @param Z pointer to IFACE jump table for the given interface ; @clobbers none uart_irq_WriteAttnBit: adiw zh:zl, UART_IRQ_IFACE_PINFN_WRITEATTN push zl push zh sbiw zh:zl, UART_IRQ_IFACE_PINFN_WRITEATTN ret ; indirect jump to address we just pushed to the stack ; @end ; --------------------------------------------------------------------------- ; @routine uart_irq_handle_idle ; ; @param Y pointer to interface data in SRAM (see @ref UART_IRQ_IFACE_OFFS_STATE) ; @param Z pointer to IFACE jump table for the given interface ; @clobbers R16 uart_irq_handle_idle: rjmp uart_irq_handle_idle_checksend ; DEBUG!! rcall uart_irq_ReadAttnBit ; (R16) tst r16 brne uart_irq_handle_idle_checksend ; jump if ATTN line high ; ATTN low, start receiving ldi r16, UART_IRQ_STATE_WAITFORSTARTBIT std Y+UART_IRQ_IFACE_OFFS_STATE, r16 ldi r16, UART_IRQ_TIME_BITTIME10 std Y+UART_IRQ_IFACE_OFFS_TICKCOUNTER, r16 rjmp uart_irq_handle_idle_end uart_irq_handle_idle_checksend: ; check whether there is something to send ldd r16, Y+UART_IRQ_IFACE_OFFS_WRITEBUF_USED tst r16 breq uart_irq_handle_idle_end ; nothing to send ; start sending clr r16 rcall uart_irq_WriteAttnBit ; (none) ldi r16, UART_IRQ_STATE_SENDINGATTN std Y+UART_IRQ_IFACE_OFFS_STATE, r16 ldi r16, UART_IRQ_TIME_BITTIME2 std Y+UART_IRQ_IFACE_OFFS_TICKCOUNTER, r16 uart_irq_handle_idle_end: ret ; @end ; --------------------------------------------------------------------------- ; @routine uart_irq_handle_waitforstartbit ; ; @param Y pointer to interface data in SRAM (see @ref UART_IRQ_IFACE_OFFS_STATE) ; @param Z pointer to IFACE jump table for the given interface ; @clobbers R16 uart_irq_handle_waitforstartbit: rcall uart_irq_ReadAttnBit ; (R16) tst r16 brne uart_irq_handle_waitforstartbit_toidle ; ATTN still low rcall uart_irq_ReadDataBit ; (R16) tst r16 breq uart_irq_handle_waitforstartbit_datalow ; DATA still high ldd r16, Y+UART_IRQ_IFACE_OFFS_TICKCOUNTER tst r16 brne uart_irq_handle_waitforstartbit_end ldi r16, UART_IRQ_STATE_WAITFORATTNHIGH std Y+UART_IRQ_IFACE_OFFS_STATE, r16 rjmp uart_irq_handle_waitforstartbit_end uart_irq_handle_waitforstartbit_datalow: ldi r16, UART_IRQ_STATE_WAITFORDATABIT std Y+UART_IRQ_IFACE_OFFS_STATE, r16 ldi r16, UART_IRQ_TIME_BITTIME1_5 std Y+UART_IRQ_IFACE_OFFS_TICKCOUNTER, r16 ldi r16, 8 ; 8 bit std Y+UART_IRQ_IFACE_OFFS_STATECOUNTER, r16 clr r16 std Y+UART_IRQ_IFACE_OFFS_STATEDATA, r16 rjmp uart_irq_handle_waitforstartbit_end uart_irq_handle_waitforstartbit_toidle: ldi r16, UART_IRQ_STATE_IDLE std Y+UART_IRQ_IFACE_OFFS_STATE, r16 uart_irq_handle_waitforstartbit_end: ret ; @end ; --------------------------------------------------------------------------- ; @routine uart_irq_handle_waitfordatabit ; ; @param Y pointer to interface data in SRAM (see @ref UART_IRQ_IFACE_OFFS_STATE) ; @param Z pointer to IFACE jump table for the given interface ; @clobbers R16 uart_irq_handle_waitfordatabit: rcall uart_irq_ReadAttnBit ; (R16) tst r16 brne uart_irq_handle_waitfordatabit_toidle ldd r16, Y+UART_IRQ_IFACE_OFFS_TICKCOUNTER tst r16 brne uart_irq_handle_waitfordatabit_end ; counter=0: read data rcall uart_irq_ReadDataBit ; (R16) ror r16 ; rotate bit into carry flag ldd r16, Y+UART_IRQ_IFACE_OFFS_STATEDATA ; doesn't change carry flag ror r16 ; rotate carry flag into data byte std Y+UART_IRQ_IFACE_OFFS_STATEDATA, r16 ldd r16, Y+UART_IRQ_IFACE_OFFS_STATECOUNTER dec r16 brne uart_irq_handle_waitfordatabit_bytecomplete ldi r16, UART_IRQ_TIME_BITTIME1 ; read next bit std Y+UART_IRQ_IFACE_OFFS_TICKCOUNTER, r16 rjmp uart_irq_handle_waitfordatabit_end uart_irq_handle_waitfordatabit_bytecomplete: ldi r16, UART_IRQ_STATE_WAITFORSTOPBIT std Y+UART_IRQ_IFACE_OFFS_STATE, r16 ldi r16, UART_IRQ_TIME_BITTIME1_5 std Y+UART_IRQ_IFACE_OFFS_TICKCOUNTER, r16 rjmp uart_irq_handle_waitfordatabit_end uart_irq_handle_waitfordatabit_toidle: ldi r16, UART_IRQ_STATE_IDLE std Y+UART_IRQ_IFACE_OFFS_STATE, r16 uart_irq_handle_waitfordatabit_end: ret ; @end ; --------------------------------------------------------------------------- ; @routine uart_irq_handle_waitforstopbit ; ; @param Y pointer to interface data in SRAM (see @ref UART_IRQ_IFACE_OFFS_STATE) ; @param Z pointer to IFACE jump table for the given interface ; @clobbers R16 (R17, X) uart_irq_handle_waitforstopbit: rcall uart_irq_ReadAttnBit ; (R16) tst r16 brne uart_irq_handle_waitforstopbit_toidle rcall uart_irq_ReadDataBit tst r16 brne uart_irq_handle_waitforstopbit_datahigh ; still low ldd r16, Y+UART_IRQ_IFACE_OFFS_TICKCOUNTER tst r16 breq uart_irq_handle_waitforstopbit_abortmsg ; timeout, abort rjmp uart_irq_handle_waitforstopbit_end uart_irq_handle_waitforstopbit_datahigh: ldd r16, Y+UART_IRQ_IFACE_OFFS_STATEDATA ; store received byte rcall uart_irq_rdbuf_writebyte ; (R17, X) brcc uart_irq_handle_waitforstopbit_abortmsg ; readbuffer full, abort ldi r16, UART_IRQ_STATE_WAITFORSTARTBIT ; wait for next byte std Y+UART_IRQ_IFACE_OFFS_STATE, r16 ldi r16, UART_IRQ_TIME_BITTIME10 std Y+UART_IRQ_IFACE_OFFS_TICKCOUNTER, r16 rjmp uart_irq_handle_waitforstopbit_end uart_irq_handle_waitforstopbit_abortmsg: ldi r16, UART_IRQ_STATE_WAITFORATTNHIGH std Y+UART_IRQ_IFACE_OFFS_STATE, r16 rjmp uart_irq_handle_waitforstopbit_end uart_irq_handle_waitforstopbit_toidle: ldi r16, UART_IRQ_STATE_IDLE std Y+UART_IRQ_IFACE_OFFS_STATE, r16 uart_irq_handle_waitforstopbit_end: ret ; @end ; --------------------------------------------------------------------------- ; @routine uart_irq_handle_waitforattnhigh ; ; @param Y pointer to interface data in SRAM (see @ref UART_IRQ_IFACE_OFFS_STATE) ; @param Z pointer to IFACE jump table for the given interface ; @clobbers R16 uart_irq_handle_waitforattnhigh: rcall uart_irq_ReadAttnBit ; (R16) tst r16 breq uart_irq_handle_waitforattnhigh_end ; still low uart_irq_handle_waitforattnhigh_toidle: ldi r16, UART_IRQ_STATE_IDLE std Y+UART_IRQ_IFACE_OFFS_STATE, r16 uart_irq_handle_waitforattnhigh_end: ret ; @end ; --------------------------------------------------------------------------- ; @routine uart_irq_handle_sendingattn ; ; @param Y pointer to interface data in SRAM (see @ref UART_IRQ_IFACE_OFFS_STATE) ; @param Z pointer to IFACE jump table for the given interface ; @clobbers R16 uart_irq_handle_sendingattn: ldd r16, Y+UART_IRQ_IFACE_OFFS_TICKCOUNTER tst r16 brne uart_irq_handle_sendingattn_end ; send start bit clr r16 rcall uart_irq_WriteDataBit ; (none) ldi r16, UART_IRQ_STATE_SENDINGSTARTBIT std Y+UART_IRQ_IFACE_OFFS_STATE, r16 ldi r16, UART_IRQ_TIME_BITTIME1 std Y+UART_IRQ_IFACE_OFFS_TICKCOUNTER, r16 uart_irq_handle_sendingattn_end: ret ; @end ; --------------------------------------------------------------------------- ; @routine uart_irq_handle_sendingstartbit ; ; @param Y pointer to interface data in SRAM (see @ref UART_IRQ_IFACE_OFFS_STATE) ; @param Z pointer to IFACE jump table for the given interface ; @clobbers R16 (R17, X) uart_irq_handle_sendingstartbit: ldd r16, Y+UART_IRQ_IFACE_OFFS_TICKCOUNTER tst r16 brne uart_irq_handle_sendingstartbit_end ; get current data byte from ringbuffer rcall uart_irq_wrbuf_readbyte ; (R17, X) std Y+UART_IRQ_IFACE_OFFS_STATEDATA, r16 ; send data bit ldd r16, Y+UART_IRQ_IFACE_OFFS_STATEDATA andi r16, 1 rcall uart_irq_WriteDataBit ; (none) ldd r16, Y+UART_IRQ_IFACE_OFFS_STATEDATA ror r16 std Y+UART_IRQ_IFACE_OFFS_STATEDATA, r16 ldi r16, UART_IRQ_STATE_SENDINGDATABIT std Y+UART_IRQ_IFACE_OFFS_STATE, r16 ldi r16, UART_IRQ_TIME_BITTIME1 std Y+UART_IRQ_IFACE_OFFS_TICKCOUNTER, r16 ldi r16, 8 std Y+UART_IRQ_IFACE_OFFS_STATECOUNTER, r16 uart_irq_handle_sendingstartbit_end: ret ; @end ; --------------------------------------------------------------------------- ; @routine uart_irq_handle_sendingdatabit ; ; @param Y pointer to interface data in SRAM (see @ref UART_IRQ_IFACE_OFFS_STATE) ; @param Z pointer to IFACE jump table for the given interface ; @clobbers R16 uart_irq_handle_sendingdatabit: ldd r16, Y+UART_IRQ_IFACE_OFFS_TICKCOUNTER tst r16 brne uart_irq_handle_sendingdatabit_end ; all bits sent? ldd r16, Y+UART_IRQ_IFACE_OFFS_STATECOUNTER dec r16 breq uart_irq_handle_sendingdatabit_tosendstopbit std Y+UART_IRQ_IFACE_OFFS_STATECOUNTER, r16 ; send next data bit ldd r16, Y+UART_IRQ_IFACE_OFFS_STATEDATA andi r16, 1 rcall uart_irq_WriteDataBit ; (none) ldd r16, Y+UART_IRQ_IFACE_OFFS_STATEDATA ror r16 std Y+UART_IRQ_IFACE_OFFS_STATEDATA, r16 ldi r16, UART_IRQ_TIME_BITTIME1 ; send next bit std Y+UART_IRQ_IFACE_OFFS_TICKCOUNTER, r16 rjmp uart_irq_handle_sendingstartbit_end uart_irq_handle_sendingdatabit_tosendstopbit: ; send stop bit ldi r16, 1 rcall uart_irq_WriteDataBit ; (none) ldi r16, UART_IRQ_STATE_SENDINGSTOPBIT std Y+UART_IRQ_IFACE_OFFS_STATE, r16 ldi r16, UART_IRQ_TIME_BITTIME1 std Y+UART_IRQ_IFACE_OFFS_TICKCOUNTER, r16 uart_irq_handle_sendingdatabit_end: ret ; @end ; --------------------------------------------------------------------------- ; @routine uart_irq_handle_sendingstopbit ; ; @param Y pointer to interface data in SRAM (see @ref UART_IRQ_IFACE_OFFS_STATE) ; @param Z pointer to IFACE jump table for the given interface ; @clobbers R16 uart_irq_handle_sendingstopbit: ldd r16, Y+UART_IRQ_IFACE_OFFS_TICKCOUNTER tst r16 brne uart_irq_handle_sendingstopbit_end ; more to send? ldd r16, Y+UART_IRQ_IFACE_OFFS_WRITEBUF_USED tst r16 breq uart_irq_handle_sendingstopbit_toidle ; nothing in buffer, go idle ; wait some time before sending next byte ldi r16, UART_IRQ_STATE_SENDINGATTN std Y+UART_IRQ_IFACE_OFFS_STATE, r16 ldi r16, UART_IRQ_TIME_BITTIME1 std Y+UART_IRQ_IFACE_OFFS_TICKCOUNTER, r16 rjmp uart_irq_handle_sendingstopbit_end uart_irq_handle_sendingstopbit_toidle: ldi r16, 1 rcall uart_irq_WriteAttnBit ; (none) ldi r16, UART_IRQ_STATE_IDLE std Y+UART_IRQ_IFACE_OFFS_STATE, r16 uart_irq_handle_sendingstopbit_end: ret ; --------------------------------------------------------------------------- ; @routine uart_irq_rdbuf_writebyte ; ; @param R16 byte to write ; @param Y base address (for "LDD Y+nn" and "STD Y+nn") ; @return CFLAG set if okay, cleared on error (i.e. buffer full) ; @clobbers R17, X uart_irq_rdbuf_writebyte: m_ringbuffer_y_writebyte \ UART_IRQ_IFACE_READBUF_SIZE, \ UART_IRQ_IFACE_OFFS_READBUF_USED, \ UART_IRQ_IFACE_OFFS_READBUF_RDPOS, \ UART_IRQ_IFACE_OFFS_READBUF_WRPOS, \ UART_IRQ_IFACE_OFFS_READBUF_BUF ret ; @end ; --------------------------------------------------------------------------- ; @routine uart_irq_rdbuf_readbyte ; ; @param Y base address (for "LDD Y+nn" and "STD Y+nn") ; @return CFLAG set if okay, cleared on error (i.e. buffer full) ; @return R16 byte read ; @clobbers R17, X uart_irq_rdbuf_readbyte: m_ringbuffer_y_readbyte \ UART_IRQ_IFACE_READBUF_SIZE, \ UART_IRQ_IFACE_OFFS_READBUF_USED, \ UART_IRQ_IFACE_OFFS_READBUF_RDPOS, \ UART_IRQ_IFACE_OFFS_READBUF_WRPOS, \ UART_IRQ_IFACE_OFFS_READBUF_BUF ret ; @end ; --------------------------------------------------------------------------- ; @routine uart_irq_wrbuf_writebyte ; ; @param R16 byte to write ; @param Y base address (for "LDD Y+nn" and "STD Y+nn") ; @return CFLAG set if okay, cleared on error (i.e. buffer full) ; @clobbers R17, X uart_irq_wrbuf_writebyte: m_ringbuffer_y_writebyte \ UART_IRQ_IFACE_WRITEBUF_SIZE, \ UART_IRQ_IFACE_OFFS_WRITEBUF_USED, \ UART_IRQ_IFACE_OFFS_WRITEBUF_RDPOS, \ UART_IRQ_IFACE_OFFS_WRITEBUF_WRPOS, \ UART_IRQ_IFACE_OFFS_WRITEBUF_BUF ret ; @end ; --------------------------------------------------------------------------- ; @routine uart_irq_wrbuf_readbyte ; ; @param Y base address (for "LDD Y+nn" and "STD Y+nn") ; @return CFLAG set if okay, cleared on error (i.e. buffer full) ; @return R16 byte read ; @clobbers R17, X uart_irq_wrbuf_readbyte: m_ringbuffer_y_readbyte \ UART_IRQ_IFACE_WRITEBUF_SIZE, \ UART_IRQ_IFACE_OFFS_WRITEBUF_USED, \ UART_IRQ_IFACE_OFFS_WRITEBUF_RDPOS, \ UART_IRQ_IFACE_OFFS_WRITEBUF_WRPOS, \ UART_IRQ_IFACE_OFFS_WRITEBUF_BUF ret ; @end ; --------------------------------------------------------------------------- ; @routine uart_irq_timer_init ; ; setup timer0 for irq every 52/4 ns uart_irq_timer_init: ; setup timer for IRQ every 52/4 ns ldi r16, (1<