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



; ***************************************************************************
; defines

.equ SI7021_FLAGS_PRESENT_BIT  = 7

.equ SI7021_FLAGS_PRESENT      = 0x80
.equ SI7021_FLAGS_TEMP_VALID   = 0x40
.equ SI7021_FLAGS_HUM_VALID    = 0x20
.equ SI7021_FLAGS_LASTWASTEMP  = 0x10
.equ SI7021_FLAGS_ERROR_MASK   = 0x07


.equ SI7021_VALUE_TEMP         = 0x01
.equ SI7021_VALUE_HUMIDITY     = 0x02


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

.dseg

si7021DataBegin:
  si7021Flags:            .byte 1
  si7021FirmwareRevision: .byte 1
  si7021LastTemp:         .byte 2
  si7021LastHumidity:     .byte 2
si7021DataEnd:



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

.cseg


SI7021_BEGIN:



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

SI7021_Init:
  ldi xh, HIGH(si7021DataBegin)
  ldi xl, LOW(si7021DataBegin)
  clr r16
  ldi r17, (si7021DataEnd-si7021DataBegin)
  rcall Utils_FillSram
  ; check presence
  rcall si7021CheckPresence
  brcc SI7021_Init_error
  lds r16, si7021Flags
  ori r16, SI7021_FLAGS_PRESENT
  sts si7021Flags, r16
  ; get firmware revision
;  rcall si7021ReadFirmwareRevision
;  brcc SI7021_Init_error
;  sts si7021FirmwareRevision, r16
  sec
  ret
SI7021_Init_error:
  clc
  ret


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

SI7021_Fini:
  sec
  ret



; ---------------------------------------------------------------------------
; SI7021_OnTimer
;
; Call this routine periodically to take measurements.
; IN:
; - nothing
; OUT:
; - CFLAG: set if okay, clear on error
; USED:

SI7021_OnTimer:
  in r15, SREG
  push r15
    cli
    lds r17, si7021Flags
    sbrs r17, SI7021_FLAGS_PRESENT_BIT  ; investigate PRESENT BIT
      rjmp SI7021_OnTimer_error         ; jmp if PRESENT bit clear
    mov r16, r17
    andi r16, SI7021_FLAGS_LASTWASTEMP
    brne SI7021_OnTimer_hum
    ori r17, SI7021_FLAGS_LASTWASTEMP
    sts si7021Flags, r17
    push r17
      rcall si7021MeasureTemp
    pop r17
    brcc SI7021_OnTimer_error
    ori r17, SI7021_FLAGS_TEMP_VALID
    sts si7021Flags, r17
    rcall si7021CalcTemp                 ; calculate temp*100 from sensor value
    sts si7021LastTemp, r18
    sts si7021LastTemp+1, r19
    rjmp SI7021_OnTimer_done
SI7021_OnTimer_hum:
    andi r17, ~SI7021_FLAGS_LASTWASTEMP
    sts si7021Flags, r17
    push r17
      rcall si7021MeasureHumidity
    pop r17
    brcc SI7021_OnTimer_error
    ori r17, SI7021_FLAGS_HUM_VALID
    sts si7021Flags, r17
    rcall si7021CalcHumidity
    sts si7021LastHumidity, r18
    sts si7021LastHumidity+1, r19
SI7021_OnTimer_done:
  pop r15
  out SREG, r15
  sec
  ret
SI7021_OnTimer_error:
  pop r15
  out SREG, r15
  clc
  ret



; ---------------------------------------------------------------------------
; si7021CheckPresence
;
; Expects interrupts being disabled!
;
; IN:
; - nothing
; OUT:
; - CFLAG: set if okay, clear on error
; USED:

si7021CheckPresence:
  rcall twiStart
  ldi r16, (SI7021_ADDR*2)+1
  rcall twiSendByte
  brcc si7021CheckPresence_notfound
  rcall twiStop
  sec
  ret

si7021CheckPresence_notfound:
  rcall twiStop
  clc
  ret



#if 0
; ---------------------------------------------------------------------------
; si7021ReadFirmwareRevision
;
; Expects interrupts being disabled!
; Currently doesn't work (my SI7021 doesn't accept the command).
;
; IN:
; - nothing
; OUT:
; - CFLAG: set if okay, clear on error
; - R16: firmware revision (0xff: version 1.0, 0x20: version 2.0)
; USED:

si7021ReadFirmwareRevision:
  rcall twiStart
  ldi r16, (SI7021_ADDR*2)               ; write access
  rcall twiSendByteExpectAck
  brcc si7021ReadFirmwareRevision_error1
  ldi r16, 0x84
  rcall twiSendByteExpectAck
  brcc si7021ReadFirmwareRevision_error2
  ldi r16, 0xb8
  rcall twiSendByteExpectAck
  brcc si7021ReadFirmwareRevision_error3
  rcall twiRestart
  ldi r16, (SI7021_ADDR*2)+1             ; read access
  rcall twiSendByteExpectAck
  brcc si7021ReadFirmwareRevision_error4
  rcall twiReceiveByte                   ; don't send ACK
  brcc si7021ReadFirmwareRevision_error5
  rcall twiStop
  sec
  ret

si7021ReadFirmwareRevision_error1:
  ldi r16, 1
  rcall si7021SetError
  rjmp si7021ReadFirmwareRevision_error
si7021ReadFirmwareRevision_error2:
  ldi r16, 2
  rcall si7021SetError
  rjmp si7021ReadFirmwareRevision_error
si7021ReadFirmwareRevision_error3:
  ldi r16, 3
  rcall si7021SetError
  rjmp si7021ReadFirmwareRevision_error
si7021ReadFirmwareRevision_error4:
  ldi r16, 4
  rcall si7021SetError
  rjmp si7021ReadFirmwareRevision_error
si7021ReadFirmwareRevision_error5:
  ldi r16, 5
  rcall si7021SetError
  rjmp si7021ReadFirmwareRevision_error
  
si7021ReadFirmwareRevision_error:
  rcall twiStop
  clc
  ret
#endif


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

si7021MeasureTemp:
  ldi r16, 0xf3
  rjmp si7021MeasureAny



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

si7021MeasureHumidity:
  ldi r16, 0xf5
  rjmp si7021MeasureAny



; ---------------------------------------------------------------------------
; si7021MeasureAny
;
; Sends the given command to the sensor and reads the result.
; Synchronization is done by sending re-start sequences until the sensor answers with
; the value.
; IN:
; - R16: command to send
; OUT:
; - CFLAG: set if okay, clear on error
; USED: R14, R16, R18, R19

si7021MeasureAny:
  rcall twiStart                           ; (R22)
  push r16
    ldi r16, (SI7021_ADDR*2)               ; write access
    rcall twiSendByteExpectAck             ; (R16, R17, R18, R22)
  pop r16
  brcc si7021MeasureAny_error
  rcall twiSendByteExpectAck               ; (R16, R17, R18, R22)
  brcc si7021MeasureAny_error              ; no ACK or other error

  ldi r16, 200
  mov r14, r16
si7021MeasureAny_loop:                     ; (R22)
    rcall twiRestart
    ldi r16, (SI7021_ADDR*2)+1             ; read access
    rcall twiSendByteExpectAck             ; (R16, R17, R18, R22)
    brcs si7021MeasureAny_gotValue         ; chip responds, receive values
    dec r14
    breq si7021MeasureAny_error            ; timeout
    rcall Utils_WaitFor50MicroSecs         ; wait for 100usecs total
    rcall Utils_WaitFor50MicroSecs
  rjmp si7021MeasureAny_loop
  
si7021MeasureAny_gotValue:
  rcall twiReceiveByteSendAck               ; (R16, R17, R18, R22)
  brcc si7021MeasureAny_error
  mov r19, r16                              ; MSByte
  rcall twiReceiveByte                      ; no ACK (R16, R17, R18, R22)
  brcc si7021MeasureAny_error
  mov r18, r16
  rcall twiStop                             ; (R22)
  ldi r16, 0
  rcall si7021SetError                      ; (none)
  sec
  ret
si7021MeasureAny_error:
  rcall twiStop                             ; (R22)
  clc
  ret



; ---------------------------------------------------------------------------
; si7021SetError
;
; Set error code in flags.
; IN:
; - R16: error code to set (0-7)
; OUT:
; - CFLAG: set if okay, clear on error
; USED: none


si7021SetError:
  push r17
    lds r17, si7021Flags
    andi r17, ~SI7021_FLAGS_ERROR_MASK
    or r17, r16
    sts si7021Flags, r17
  pop r17
  ret



; ---------------------------------------------------------------------------
; si7021CalcTemp
;
; Calculates temperatur * 100.
; Uses formula from specs: temp=(175.72*TEMP_CODE/65536)-46.85.
;
; IN:
; - R19:R18: Temperature value read from sensor
; OUT:
; - R19:R18: calculated temperature(Celsius) * 100
; USED: R16, R17, R18, R19, R20, R21, R22, R23, R24, R25, R26

si7021CalcTemp:
  mov r20, r18
  mov r21, r19
  ldi r23, HIGH(17572)
  ldi r22, LOW(17572)
  rcall si7021Mulu16x16_32 ; result is in R19:R18:R17:R16, we only use R19-R18 -> /65536
  ldi r16, LOW(4685)
  ldi r17, HIGH(4685)
  sub r18, r16
  sbc r19, r17
  ret



; ---------------------------------------------------------------------------
; si7021CalcHumidity
;
; Calculates humidity in percent.
; Uses formula from specs: rel humidity=(125*HUMIDITY_CODE/65536)-6.
;
; IN:
; - R19:R18: Humidity value read from sensor
; OUT:
; - R19:R18: calculated relative humidity
; USED: R16, R17, R18, R19, R20, R21, R22, R23, R24, R25, R26

si7021CalcHumidity:
  mov r20, r18
  mov r21, r19
  clr r23
  ldi r22, 125
  rcall si7021Mulu16x16_32 ; result is in R19:R18:R17:R16, we only use R19-R18 -> /65536
  ldi r16, 6
  clr r17
  sub r18, r16
  sbc r19, r17
  ret



; ---------------------------------------------------------------------------
; si7021Mulu16x16_32
;
; Multiplies two unsigned 16 bit values resulting in one 32 bit value.
; This is a rather simple but reasonable fast routine working just like you would
; when doing multiplications with pen and paper.
; TODO: adapt for signed values
;
; IN:
; - R21:R20: 16 bit value A
; - R23:R22: 16 bit value B
; OUT:
; - R19:R18:R17:R16: 32 bit result
; USED: R16, R17, R18, R19, R20, R21, R22, R23, R24, R25, R26
; R19:R18:R17:R16 = R21:R20 * R23:R22
; R19:R18:R17:R16 = R25:R24:R21:R20 * R23:R22
si7021Mulu16x16_32:
  clr r19
  clr r18
  clr r17
  clr r16
  clr r25
  clr r24
  ldi r26, 16                     ; 16 bit multiplicator
si7021Muls16x16_32_loop:
    lsr r23
    ror r22
    brcc si7021Muls16x16_32_noadd ; current digit in B is 0, don't add shifted A to result
    add r16, r22
    adc r17, r23
    adc r18, r24
    adc r19, r25
;    brcs si7021Muls16x16_32_overflow ; can't happen
si7021Muls16x16_32_noadd:
    dec r26
    breq si7021Muls16x16_32_done
    lsl r20
    rol r21
    rol r24
    rol r25
;    brcs si7021Muls16x16_32_overflow ; can't happen
  rjmp si7021Muls16x16_32_loop
si7021Muls16x16_32_done:
  clc
  ret
;si7021Muls16x16_32_overflow:  ; never reached. Multiplying 2 16 bit values can't overflow 32 bit
;  sec
;  ret



SI7021_SendTemp:
  lds r16, si7021Flags
  andi r16, SI7021_FLAGS_TEMP_VALID
  brne SI7021_SendTemp_haveValue
  sec
  ret
SI7021_SendTemp_haveValue:
  ldi r16, 0xff                      ; destination address
  ldi r17, VALUE_ID_SI7021_TEMP            ; value id
  ldi r22, AQHOME_VALUETYPE_TEMP
  lds r18, si7021LastTemp            ; value
  lds r19, si7021LastTemp+1
  ldi r20, 100                       ; denominator
  clr r21
  rjmp SI7021_SendValue



SI7021_SendHumidity:
  lds r16, si7021Flags
  andi r16, SI7021_FLAGS_HUM_VALID
  brne SI7021_SendHumidity_haveValue
  clc
  ret
SI7021_SendHumidity_haveValue:
  ldi r16, 0xff                      ; destination address
  ldi r17, VALUE_ID_SI7021_HUM       ; value id
  ldi r22, AQHOME_VALUETYPE_HUMIDITY
  lds r18, si7021LastHumidity        ; value
  lds r19, si7021LastHumidity+1
  ldi r20, 1                         ; denominator
  clr r21
  rjmp SI7021_SendValue


SI7021_SendValue:
  ldi xl, LOW(com2SendBuffer)
  ldi xh, HIGH(com2SendBuffer)
  rcall CPRO_WriteReportValue
  rjmp COM2_SendPacket






SI7021_END:
.equ MODULE_SIZE_SI7021 = SI7021_END-SI7021_BEGIN


