; ***************************************************************************
; 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.   *
; ***************************************************************************





; ---------------------------------------------------------------------------
; com2SendByte
;
; 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
; IN:
; - R16: byte to send
; OUT:
; - CFLAG: set if okay, clear otherwise
; MODIFIED REGS: R16, R21, R22

com2SendByte:
  ldi r21, 8                                     ; +1 bits left
  ; send startbit
  cbi COM_PORT_DATA, COM_PINNUM_DATA             ; +2 set DATA low
  sbi COM_DDR_DATA, COM_PINNUM_DATA              ; +2 set DATA as output
  Utils_WaitNanoSecs COM_BIT_LENGTH, 5, r22      ; wait for one bit duration
  ; send data bits
com2SendByte_loop:                                ; 9 for low bit
    lsr r16                                      ; 1+ bit to send -> 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                     ; (R16, R17, R22)
  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: r16 (r17, r22)

com2WaitForDataLow:
  clr r16
  rjmp com2WaitForDataState1ms



; ---------------------------------------------------------------------------
; com2WaitForDataHigh
;
; Waits up to COM2_MAXWAIT_US loops for high data line
; IN:
; OUT:
; - CFLAG: set if okay, clear otherwise
; MODIFIED REGISTERS: r16 (r17, r22)

com2WaitForDataHigh:
  ldi r16, 0xff
  rjmp com2WaitForDataState1ms



; ---------------------------------------------------------------------------
; com2WaitForAttnHigh
;
; Waits up to COM2_MAXWAIT_US loops for high ATTN line
; IN:
; OUT:
; - CFLAG: set if okay, clear otherwise
; REGS: r16 (r17, r22)

com2WaitForAttnHigh:
  ldi r16, 0xff
  rjmp com2WaitForAttnState1ms



; ---------------------------------------------------------------------------
; com2WaitForDataState1ms
;
; Waits up to 100ms for high DATA line
; IN:
; - R16: state to wait for (00 for low, 0xff for high)
; OUT:
; - CFLAG: set if state reached, cleared otherwise
; REGS: R17, R22

com2WaitForDataState1ms:
  ldi r17, 100
com2WaitForDataState1ms_loop:
    in r22, COM_PIN_DATA
    eor r22, r16
    andi r22, (1<<COM_PINNUM_DATA)
    breq com2WaitForDataState1ms_stateReached
    Utils_WaitNanoSecs 10000, 0, r22              ; wait for 10us
    dec r17
  brne com2WaitForDataState1ms_loop
  clc
  ret
com2WaitForDataState1ms_stateReached:
  sec
  ret



; ---------------------------------------------------------------------------
; com2WaitForAttnState1ms
;
; Waits up to 100ms for high DATA line
; IN:
; - R16: state to wait for (00 for low, 0xff for high)
; OUT:
; - CFLAG: set if state reached, cleared otherwise
; REGS: R17, R22

com2WaitForAttnState1ms:
  ldi r17, 100
com2WaitForAttnState1ms_loop:
    in r22, COM_PIN_ATTN
    eor r22, r16
    andi r22, (1<<COM_PINNUM_ATTN)
    breq com2WaitForAttnState1ms_stateReached
    Utils_WaitNanoSecs 10000, 0, r22              ; wait for 10us
    dec r17
  brne com2WaitForAttnState1ms_loop
  clc
  ret
com2WaitForAttnState1ms_stateReached:
  sec
  ret



