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




; ***************************************************************************
; data

.dseg

twiMasterDataBegin:
twiMasterScanEnabled: .byte 1
twiMasterLastScanned: .byte 1
twiMasterDataEnd:



; ***************************************************************************
; code

.cseg

TWIMASTER_BEGIN:


; ---------------------------------------------------------------------------
; macros

.macro twiClockHi
  cbi TWI_DDR_SCL, TWI_PINNUM_SCL  ; set to input
  cbi TWI_PORT_SCL, TWI_PINNUM_SCL ; disable internal pullup
.endmacro


.macro twiClockLo
  sbi TWI_DDR_SCL, TWI_PINNUM_SCL  ; set to output
  cbi TWI_PORT_SCL, TWI_PINNUM_SCL ; set to low
.endmacro


.macro twiDataHi
  cbi TWI_DDR_SDA, TWI_PINNUM_SDA  ; set to input
  cbi TWI_PORT_SDA, TWI_PINNUM_SDA ; disable internal pullup
.endmacro


.macro twiDataLo
  sbi TWI_DDR_SDA, TWI_PINNUM_SDA  ; set to output
  cbi TWI_PORT_SDA, TWI_PINNUM_SDA ; set to low
.endmacro


; ---------------------------------------------------------------------------
; TWI_Master_Init
;
; IN:
; OUT:
; - CFLAG: set if okay, clear on error
; USED:

TWI_Master_Init:
  twiClockHi
  twiDataHi

  ; preset SRAM data area
  ldi xh, HIGH(twiMasterDataBegin)
  ldi xl, LOW(twiMasterDataBegin)
  clr r16
  ldi r17, (twiMasterDataEnd-twiMasterDataBegin)
  rcall Utils_FillSram

  sec
  ret



; ---------------------------------------------------------------------------
; TWI_Master_Fini
;
; IN:
; OUT:
; USED:

TWI_Master_Fini:
  sec
  ret



; ---------------------------------------------------------------------------
; TWI_Master_Run
;
; IN:
; OUT:
; USED:

TWI_Master_Run:
  clc
  ret



; ---------------------------------------------------------------------------
; @routine twiCheckPresence
;
; Expects interrupts being disabled!
;
; @param r16 address to check (with read bit set!)
; @return CFLAG set if okay, clear on error
; @clobbers (R16, R17, R18, R22)

twiCheckPresence:
  rcall twiStart                   ; (R22)
  rcall twiSendByte                ; (R16, R17, R18, R22)
;  rcall twiSendByteExpectAck       ; (R16, R17, R18, R22)
  brcc twiCheckPresence_notfound
  rcall twiStop                    ; (R22)
  sec
  ret
twiCheckPresence_notfound:
  rcall twiStop                    ; (R22)
  clc
  ret
; @end



; ---------------------------------------------------------------------------
; twiStart
;
; Send START condition on the bus.
;
; IN:
; - nothing
; OUT:
; - nothing
; USED: R22

twiStart:
  twiDataHi
  Utils_WaitNanoSecs TWI_BIT_LENGTH, 0, r22
  twiClockHi
  Utils_WaitNanoSecs TWI_BIT_LENGTH, 0, r22
  twiDataLo
  Utils_WaitNanoSecs TWI_BIT_LENGTH, 0, r22
  twiClockLo
  Utils_WaitNanoSecs TWI_BIT_LENGTH, 0, r22
  ret



; ---------------------------------------------------------------------------
; twiStop
;
; Send STOP condition on the bus.
;
; IN:
; - nothing
; OUT:
; - nothing
; USED: R22

twiStop:
  twiDataLo
  Utils_WaitNanoSecs TWI_BIT_LENGTH, 0, r22
  twiClockHi
  Utils_WaitNanoSecs TWI_BIT_LENGTH, 0, r22
  twiDataHi
  Utils_WaitNanoSecs TWI_BIT_LENGTH, 0, r22
  ret



; ---------------------------------------------------------------------------
; twiRestart
;
; Send START condition again (needed for some chips to send address in write mode
; and receive data afterwards in read mode).
;
; IN:
; - nothing
; OUT:
; - nothing
; USED: R22


twiRestart:
  twiDataHi
  twiClockHi
  rjmp twiStart



; ---------------------------------------------------------------------------
; twiReadBit
;
; Read a bit from the line (handling SCL line and possible clock stretching).
;
; IN:
; - nothing
; OUT:
; - CARRY flag set: okay, cleared on error
; - R16: bit read (if CARRY flag set), i.e. 0x00 or 0x01
; USED: R16, R22

twiReadBit:
  twiDataHi
  Utils_WaitNanoSecs TWI_BIT_LENGTH, 0, r22
  twiClockHi
  Utils_WaitNanoSecs TWI_BIT_LENGTH, 0, r22
  ldi r16, 100
twiReadBit_loop:
    sbic TWI_PIN_SCL, TWI_PINNUM_SCL
      rjmp twiReadBit_clockReleased
    Utils_WaitNanoSecs TWI_BIT_LENGTH/20, 0, r22
    dec r16
  brne twiReadBit_loop
  ; timeout
  clc
  ret
twiReadBit_clockReleased:
  Utils_WaitNanoSecs TWI_BIT_LENGTH, 0, r22
  ldi r16, 1
  sbis TWI_PIN_SDA, TWI_PINNUM_SDA
    clr r16
  Utils_WaitNanoSecs TWI_BIT_LENGTH, 0, r22
  twiClockLo
  Utils_WaitNanoSecs TWI_BIT_LENGTH, 0, r22
  sec
  ret



; ---------------------------------------------------------------------------
; twiWriteBit
;
; Write a bit to the line (handling SCL and SDA line and possible clock stretching).
;
; IN:
; - R16: bit to send (either 0x00 or 0x01)
; OUT:
; - CARRY flag set: okay, cleared on error
; USED: R16, R22

twiWriteBit:
  twiClockLo
  Utils_WaitNanoSecs TWI_BIT_LENGTH, 0, r22
  tst r16
  breq twiWriteBit_write0
  twiDataHi
  rjmp twiWriteBit_written
twiWriteBit_write0:
  twiDataLo
twiWriteBit_written:
  Utils_WaitNanoSecs TWI_BIT_LENGTH, 0, r22
  twiClockHi
  Utils_WaitNanoSecs TWI_BIT_LENGTH, 0, r22
  ldi r16, 100
twiWriteBit_loop:
    sbic TWI_PIN_SCL, TWI_PINNUM_SCL
      rjmp twiWriteBit_clockReleased
    Utils_WaitNanoSecs TWI_BIT_LENGTH/20, 0, r22
    dec r16
  brne twiWriteBit_loop
  ; timeout
  clc
  ret
twiWriteBit_clockReleased:
  Utils_WaitNanoSecs TWI_BIT_LENGTH, 0, r22
  twiClockLo
  Utils_WaitNanoSecs TWI_BIT_LENGTH, 0, r22
  sec
  ret



; ---------------------------------------------------------------------------
; @routine twiSendFromFlashExpectAck
;
; Write bytes from flash to TWI expecting ACK bits.
;
; @param Z pointer to data in FLASH to send (prepared for "LPM"!)
; @param r19 number of bytes to send
; @return CFLAG set if okay, cleared on error
; @clobbers R16, R17, R18, R22, Z

twiSendFromFlashExpectAck:
twiSendFromFlashExpectAck_loop:
    lpm r16, Z+
    rcall twiSendByteExpectAck         ; (R16, R17, R18, R22)
    brcc twiSendFromFlashExpectAck_ret
    dec r19
  brne twiSendFromFlashExpectAck_loop
  sec
twiSendFromFlashExpectAck_ret:
  ret
; @end


; ---------------------------------------------------------------------------
; twiSendByte
;
; Write a byte to the bus and reads the ACK bit back.
;
; IN:
; - R16: byte to send
; OUT:
; - CARRY flag set: okay, cleared on error
; - R16: ACK bit read (0x00 or 0x01)
; USED: R16, R17, R18, R22

twiSendByte:
  mov r17, r16
  ldi r18, 8
twiSendByte_loop:
    clr r16
    lsl r17                     ; shift MSB out into carry flag
    rol r16                     ; shift carry flag into r16
    rcall twiWriteBit           ; (R16, R22)
    brcc twiSendByte_error
    dec r18
  brne twiSendByte_loop
  rcall twiReadBit              ; r16: ACK bit received
  brcc twiSendByte_error
  sec
  ret
twiSendByte_error:
  clc
  ret



; ---------------------------------------------------------------------------
; twiSendByteExpectAck
;
; Write a byte to the bus and expect ACK response.
;
; IN:
; - R16: byte to send
; OUT:
; - CARRY flag set: okay, cleared on error (including case of non-ACK response)
; USED: R16, R17, R18, R22

twiSendByteExpectAck:
  rcall twiSendByte
  brcc twiSendByteExpectAck_error
  tst r16
  brne twiSendByteExpectAck_error
  sec
  ret
twiSendByteExpectAck_error:
  clc
  ret



; ---------------------------------------------------------------------------
; twiReceiveByte
;
; Read a byte from the bus. Does not send an ACK bit, this must be done by the caller,
; if required.
;
; IN:
; OUT:
; - CARRY flag set: okay, cleared on error
; - R16: byte received
; USED: R16, R17, R18, R22

twiReceiveByte:
  clr r17
  ldi r18, 8
twiReceiveByte_loop:
    rcall twiReadBit           ; (R16, R22)
    brcc twiReceiveByte_error
    lsr r16
    rol r17
    dec r18
  brne twiReceiveByte_loop
  mov r16, r17
  sec
  ret
twiReceiveByte_error:
  clc
  ret



; ---------------------------------------------------------------------------
; twiReceiveByteSendAck
;
; Read a byte from the bus. Sends ACK bit.
;
; IN:
; OUT:
; - CARRY flag set: okay, cleared on error
; - R16: byte received
; USED: R16, R17, R18, R22

twiReceiveByteSendAck:
  rcall twiReceiveByte
  brcc twiReceiveByteSendAck_error
  push r16
    clr r16
    rcall twiWriteBit  ; send ACK (R16, R22)
  pop r16
  sec
  ret
twiReceiveByteSendAck_error:
  clc
  ret



#ifdef WITH_DEBUG

TWI_Master_ScanNext:
  in r15, SREG
  cli
  lds r16, twiMasterScanEnabled
  tst r16
  breq TWI_Master_ScanNext_end
  lds r16, twiMasterLastScanned
  tst r16
  brne TWI_Master_ScanNext_inc
  ldi r16, 7
TWI_Master_ScanNext_inc:
  inc r16
  cpi r16, 121
  breq TWI_Master_ScanNext_finshed
  ; check current address
  mov r21, r16
  rcall twiStart
  sec
  rol r16            ; (ADDR<<1) + 1: read from address
  rcall twiSendByte
  brcc TWI_Master_ScanNext_abort ; clock stretching too long, bus error
  sts twiMasterLastScanned, r21
  rcall twiStop
  tst r16
  brne TWI_Master_ScanNext_noAnswer
  ; send message about available address
  mov r1, r21
  ldi r16, 1
  mov r2, r16
  ldi r16, 255 ; send to all
  push r15
    rcall CPRO_EnqueueTwiBusMember
  pop r15
  rjmp TWI_Master_ScanNext_end
TWI_Master_ScanNext_noAnswer:
  ; send message about available address
;  mov r1, r21
;  clr r2
;  ldi r16, 255 ; send to all
;  push r15
;    rcall CPRO_EnqueueTwiBusMember
;  pop r15
  rjmp TWI_Master_ScanNext_end
TWI_Master_ScanNext_abort:
  rcall twiStop
  rjmp TWI_Master_ScanNext_end
TWI_Master_ScanNext_finshed:
  clr r16
  sts twiMasterScanEnabled, r16
TWI_Master_ScanNext_end:
  out SREG, r15
  ret
#endif



TWIMASTER_END:
.equ MODULE_SIZE_TWIMASTER = TWIMASTER_END-TWIMASTER_BEGIN

