; *************************************************************************** ; copyright : (C) 2023 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. * ; *************************************************************************** ; *************************************************************************** ; defines .equ COM2_BUFFER_SIZE = 24 ; CAVE: must change code in COM2_BufferPosToX when changing this! .equ COM2_BUFFER_NUM = 4 .equ COM2_MAXWAIT_US = 100 ; maximum wait time in microseconds when waiting for rising/falling clock .equ COM2_MAINTENANCE_ADDR = 0xc1 .equ COM2_CRC_POLYNOMIAL = 0x97 ; flags for variable payload enqueue function .equ COM2_PAYLOAD_FLAGS_SECONDS = 0x01 .equ COM2_PAYLOAD_FLAGS_UID = 0x02 .equ COM2_PAYLOAD_FLAGS_RESERVED1 = 0x04 .equ COM2_PAYLOAD_FLAGS_NUM0 = 0x08 .equ COM2_PAYLOAD_FLAGS_NUM1 = 0x10 .equ COM2_PAYLOAD_FLAGS_NUM2 = 0x20 .equ COM2_PAYLOAD_FLAGS_NUM3 = 0x40 .equ COM2_PAYLOAD_FLAGS_NUM4 = 0x80 .equ COM2_PAYLOAD_FLAGS_SHIFT_NUM = 3 .equ COM2_MSG_OFFS_DESTADDR = 0 .equ COM2_MSG_OFFS_MSGLEN = 1 .equ COM2_MSG_OFFS_MSGDATA = 2 .equ COM2_MSG_OFFS_CMD = 2 ; first at COM2_MSG_OFFS_MSGDATA .equ COM2_MSG_OFFS_SRCADDR = 3 .equ COM2_MSG_OFFS_PAYLOAD = 4 ; payload for the cmd follows here ; *************************************************************************** ; data .dseg com2DataBegin: com2Address: .byte 1 com2Interrupts: .byte 2 com2StatsPacketsIn: .byte 2 com2StatsIoError: .byte 2 com2StatsContentError: .byte 2 com2StatsNoBufferError: .byte 2 com2StatsIgnored: .byte 2 com2StatsHandled: .byte 2 com2StatsMissed: .byte 2 ; currently not used com2StatsPacketsOut: .byte 2 com2StatsBusyError: .byte 2 com2StatsCollisions: .byte 2 com2RecvBuffersUsed: .byte 1 com2MaxBuffersUsed: .byte 1 com2RecvBuffersWritePos: .byte 1 com2RecvBuffersReadPos: .byte 1 com2RecvBuffers: .byte COM2_BUFFER_SIZE*COM2_BUFFER_NUM com2SendBuffer: .byte COM2_BUFFER_SIZE com2DataEnd: ; *************************************************************************** ; code .cseg COM2_BEGIN: ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ; Module interface ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ; --------------------------------------------------------------------------- ; Com2_Init ; ; IN: ; OUT: ; - CFLAG: set if okay, clear on error ; USED: R16, R17, X, Y Com2_Init: ; preset SRAM data area ldi xh, HIGH(com2DataBegin) ldi xl, LOW(com2DataBegin) clr r16 ldi r17, (com2DataEnd-com2DataBegin) rcall Utils_FillSram ; set address to 0 (will be updated later) clr r16 sts com2Address, r16 ; setup pins and interrupts cbi COM_PORT_DATA, COM_PINNUM_DATA ; disable internal pullup for DATA cbi COM_DDR_DATA, COM_PINNUM_DATA ; set DATA port as input cbi COM_PORT_ATTN, COM_PINNUM_ATTN ; disable internal pullup for ATTN cbi COM_DDR_ATTN, COM_PINNUM_ATTN ; set ATTN port as input sbi COM_IRQ_ADDR_ATTN, COM_IRQ_BIT_ATTN ; enable pin change irq for ATTN line in r16, GIMSK ; enable pin change irq PCIE0 or PCIE1 ori r16, (1< CARRY brcs com2SendByte_setHigh ; HI: +2, LO: +1 com2SendByte_setLow: sbi COM_DDR_DATA, COM_PINNUM_DATA ; +2 set DATA as output Utils_WaitNanoSecs COM_BIT_LENGTH, 9, r22 rjmp com2SendByte_loopEnd ; +2 com2SendByte_setHigh: cbi COM_DDR_DATA, COM_PINNUM_DATA ; +2 set DATA as input, pullup R makes it ONE nop ; +1 (to make pin change available) Utils_WaitNanoSecs COM_BIT_LENGTH/2, 11, r22 ; wait for half a bit length for line to safely settle sbis COM_PIN_DATA, COM_PINNUM_DATA ; +1 if no skip, +2 if skipped rjmp com2SendByte_error ; +2 if error (collision: we wanted line to be high but it is low) Utils_WaitNanoSecs COM_BIT_LENGTH/2, 0, r22 com2SendByte_loopEnd: dec r21 ; +1 brne com2SendByte_loop ; +2, sum per loop: 10 cycles ; send stopbit cbi COM_DDR_DATA, COM_PINNUM_DATA ; +2 set DATA as input, pullup R makes it ONE Utils_WaitNanoSecs COM_BIT_LENGTH, 4, r22 ; wait for one bit length sec ret com2SendByte_error: clc ret ; --------------------------------------------------------------------------- ; com2ReceiveByte ; ; Receive a byte. ; ; IN: ; - nothing ; OUT: ; - CFLAG: set if okay, clear otherwise ; - R16: byte read (if CFLAG set) ; MODIFIED REGS: R16, R20, R21, R22 (R17) com2ReceiveByte: cbi COM_DDR_DATA, COM_PINNUM_DATA ; set DATA port as input cbi COM_PORT_DATA, COM_PINNUM_DATA ; disable internal pullup for DATA ldi r21, 8 ; bits left clr r20 ; byte currently receiving ; wait for startbit rcall com2WaitForDataLow ; (R17) brcc com2ReceiveByte_error Utils_WaitNanoSecs COM_BIT_LENGTH/2, 5, r22 ; goto middle of startbit to maximize sync stability com2ReceiveByte_loop: Utils_WaitNanoSecs COM_BIT_LENGTH, 8, r22 ; 8 cycles used in the complete loop between waits sec ; +1 sbic COM_PIN_DATA, COM_PINNUM_DATA ; LOW: +2, HIGH: +1 rjmp com2ReceiveByte_shiftIn ; HIGH: +2, rjmp, use set CFLAG clc ; LOW: +1 com2ReceiveByte_shiftIn: ror r20 ; +1 dec r21 ; +1 brne com2ReceiveByte_loop ; +2, sum per loop: 8 cycles rcall com2WaitForDataHigh ; wait for start of stopbit brcc com2ReceiveByte_error mov r16, r20 sec ret com2ReceiveByte_error: clc ret ; --------------------------------------------------------------------------- ; com2WaitForDataLow ; ; Waits up to COM2_MAXWAIT_US loops for low data line ; IN: ; OUT: ; - CFLAG: set if okay, clear otherwise ; MODIFIED REGISTERS: r17, r22 com2WaitForDataLow: ldi r17, COM2_MAXWAIT_US com2WaitForDataLow_loop: sbis COM_PIN_DATA, COM_PINNUM_DATA rjmp com2WaitForDataLow_done rcall com2WaitFor1000ns dec r17 brne com2WaitForDataLow_loop clc ; timeout ret com2WaitForDataLow_done: sec ; ok ret ; --------------------------------------------------------------------------- ; com2WaitForDataHigh ; ; Waits up to COM2_MAXWAIT_US loops for high data line ; IN: ; OUT: ; - CFLAG: set if okay, clear otherwise ; MODIFIED REGISTERS: r17, r22, X com2WaitForDataHigh: ldi r17, COM2_MAXWAIT_US com2WaitForDataHigh_loop: sbic COM_PIN_DATA, COM_PINNUM_DATA rjmp com2WaitForDataHigh_done rcall com2WaitFor1000ns dec r17 brne com2WaitForDataHigh_loop clc ; timeout ret com2WaitForDataHigh_done: sec ; ok ret ; --------------------------------------------------------------------------- ; com2WaitForAttnHigh ; ; Waits up to COM2_MAXWAIT_US loops for high ATTN line ; IN: ; OUT: ; - CFLAG: set if okay, clear otherwise ; MODIFIED REGISTERS: r17, r22, X com2WaitForAttnHigh: ldi r17, COM2_MAXWAIT_US com2WaitForAttnHigh_loop: sbic COM_PIN_ATTN, COM_PINNUM_ATTN rjmp com2WaitForAttnHigh_done rcall com2WaitFor1000ns dec r17 brne com2WaitForAttnHigh_loop clc ; timeout ret com2WaitForAttnHigh_done: sec ; ok ret ; --------------------------------------------------------------------------- ; comWaitFor100ns ; ; Waits for 100 nanoseconds. ; IN: ; OUT: ; - CFLAG: set if okay, clear otherwise ; REGS: r17, r22 com2WaitFor1000ns: Utils_WaitNanoSecs 1000, 7, r22 ; wait for 100 nanosecs (minus 3 cycles for rcall, 4 for ret) ret ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ; CRC code ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ; --------------------------------------------------------------------------- ; add checksum byte to buffer ; ; IN: ; - X : pointer to packet buffer ; OUT: ; - CFLAG: set if okay, clear otherwise ; MODIFIED REGS: R16, R17, R18, R19, X com2CalcAndAddChecksumByte: adiw xh:xl, COM2_MSG_OFFS_MSGLEN ld r18, X ; read msg len sbiw xh:xl, COM2_MSG_OFFS_MSGLEN inc r18 ; account for dest address inc r18 ; account for msg len byte ldi r19, COM2_CRC_POLYNOMIAL rcall com2CalcCrc8 ; (R16, R17, R18, R20, X) st X, r16 ; add checksum byte sec ret ; --------------------------------------------------------------------------- ; check message in buffer ; ; IN: ; - X : pointer to packet buffer ; OUT: ; - CFLAG: set if okay, clear otherwise ; MODIFIED REGS: R16, R17, R18, R19, R20, X com2CheckMessageInBuffer: adiw xh:xl, COM2_MSG_OFFS_MSGLEN ld r18, X ; read msg len sbiw xh:xl, COM2_MSG_OFFS_MSGLEN inc r18 ; account for dest address inc r18 ; account for msg len byte ldi r19, COM2_CRC_POLYNOMIAL rcall com2CalcCrc8 ; (R16, R17, R18, R20, X) ld r17, X cp r16, r17 ; should be equal brne com2CheckMessageInBuffer_error sec ret com2CheckMessageInBuffer_error: clc ret ; --------------------------------------------------------------------------- ; calc crc8 checksum using given polynomial ; ; IN: ; - X : pointer to data to calc crc8 for ; - r18: number of bytes to calc crc8 for ; - r19: polynomial to use (e.g. 0x97: HD=4 up to 119 bytes, e.g. detects all 1 to 3 bit errors) ; OUT: ; - r16: crc8 checksum ; - X : point directly behind the checked area ; MODIFIED REGS: R16, R17, R18, R20, X com2CalcCrc8: ldi r16, 0xff ; crc com2CalcCrc8_loop1: ld r17, X+ ; running var eor r16, r17 ldi r20, 8 ; counter for loop2 com2CalcCrc8_loop2: lsl r16 brcc com2CalcCrc8_l1 eor r16, r19 com2CalcCrc8_l1: dec r20 brne com2CalcCrc8_loop2 dec r18 brne com2CalcCrc8_loop1 ret ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ; buffer code ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ; --------------------------------------------------------------------------- ; COM_BufferAlloc ; ; Allocate a transfer buffer. ; IN: ; - nothing ; OUT: ; - CFLAG: set if okay, clear otherwise ; - X: pointer to allocated buffer in SRAM ; MODIFIED REGISTERS: r16, r17, r21 COM2_BufferAlloc: in r21, SREG ; save global interrupt enable bit from SREG cli lds r17, com2RecvBuffersUsed cpi r17, COM2_BUFFER_NUM brcc COM2_AllocBuffer_error ; no buffer available inc r17 ; increment number of buffers used sts com2RecvBuffersUsed, r17 ; store new value lds r16, com2MaxBuffersUsed ; calc max buffers used cp r16, r17 brcc COM2_AllocBuffer_l0 sts com2MaxBuffersUsed, r17 COM2_AllocBuffer_l0: lds r16, com2RecvBuffersWritePos ; get current write pos mov r17, r16 ; increment for next call inc r17 cpi r17, COM2_BUFFER_NUM ; CF set if COM_BUFFER_NUM > R17 brcs COM2_AllocBuffer_l1 clr r17 ; wraparound COM2_AllocBuffer_l1: sts com2RecvBuffersWritePos, r17 ; store new writepos for next caller rcall COM2_BufferPosToX ; (R16, R17) out SREG, r21 ; restore global interrupt enable bit in SREG sec ret COM2_AllocBuffer_error: out SREG, r21 clc ret ; --------------------------------------------------------------------------- ; COM2_BufferDeallocBack ; ; Release a transfer buffer at the end of the list by decreasing the write pos. ; This releases the last allocated buffer! ; ; IN: ; - nothing ; OUT: ; - CFLAG: set if okay, clear otherwise ; MODIFIED REGISTERS: r16, r17, r21 COM2_BufferDeallocBack: in r21, SREG ; save global interrupt enable bit from SREG cli lds r17, com2RecvBuffersUsed tst r17 breq COM2_BufferDeallocBack_error ; no buffer allocated, nothing to release dec r17 sts com2RecvBuffersUsed, r17 ; store new value lds r17, com2RecvBuffersWritePos tst r17 ; 0? brne COM2_BufferDeallocBack_l1 ; nope go directly decrement r17 ldi r17, COM2_BUFFER_NUM ; wrap-around COM2_BufferDeallocBack_l1: dec r17 sts com2RecvBuffersWritePos, r17 out SREG, r21 sec ret COM2_BufferDeallocBack_error: out SREG, r21 clc ret ; --------------------------------------------------------------------------- ; COM2_BufferDeallocFront ; ; Release a transfer buffer by increasing the read pos. ; ; IN: ; - nothing ; OUT: ; - CFLAG: set if okay, clear otherwise ; MODIFIED REGISTERS: r16, r17, r21 COM2_BufferDeallocFront: in r21, SREG ; save global interrupt enable bit from SREG cli lds r17, com2RecvBuffersUsed tst r17 breq COM2_BufferDeallocFront_error ; no buffer allocated, nothing to release dec r17 sts com2RecvBuffersUsed, r17 ; store new value lds r17, com2RecvBuffersReadPos inc r17 cpi r17, COM2_BUFFER_NUM brcs COM2_BufferDeallocFront_l1 clr r17 ; wrap-around COM2_BufferDeallocFront_l1: sts com2RecvBuffersReadPos, r17 out SREG, r21 sec ret COM2_BufferDeallocFront_error: out SREG, r21 clc ret ; --------------------------------------------------------------------------- ; COM2_BufferPosToX ; ; Get a pointer to the SRAM position of the given buffer. ; CAVE: Code must correspond to COM2_BUFFER_SIZE!! ; IN: ; - R16: buffer number (starting with 0) ; OUT: ; - X: pointer to buffer in SRAM ; MODIFIED REGISTERS: R16, R17 COM2_BufferPosToX: ; calculate offset clr r17 mov xl, r16 clr xh lsl xl rol xh ; *2 add xl, r16 adc xh, r17 ; *3 lsl xl rol xh ; *6 lsl xl rol xh ; *12 lsl xl rol xh ; *24 ; add base position of buffers ldi r16, LOW(com2RecvBuffers) ldi r17, HIGH(com2RecvBuffers) add xl, r16 adc xh, r17 ret COM2_END: .equ MODULE_SIZE_COM2 = COM2_END-COM2_BEGIN