; ***************************************************************************
; 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_MeasureTemp:
  in r15, SREG
  push r15
    cli
    lds r17, si7021Flags
    sbrs r17, SI7021_FLAGS_PRESENT_BIT  ; investigate PRESENT BIT
      rjmp SI7021_MeasureTemp_error     ; jmp if PRESENT bit clear
    push r17
      rcall si7021MeasureTemp
    pop r17
    brcc SI7021_MeasureTemp_error
    ori r17, SI7021_FLAGS_TEMP_VALID
    sts si7021Flags, r17
    rcall si7021CalcTemp
    sts si7021LastTemp, r18
    sts si7021LastTemp+1, r19
SI7021_MeasureTemp_done:
  pop r15
  out SREG, r15
  sec
  ret
SI7021_MeasureTemp_error:
  pop r15
  out SREG, r15
  clc
  ret
; @end



SI7021_MeasureHumidity:
  in r15, SREG
  push r15
    cli
    lds r17, si7021Flags
    sbrs r17, SI7021_FLAGS_PRESENT_BIT   ; investigate PRESENT BIT
      rjmp SI7021_MeasureHumidity_error  ; jmp if PRESENT bit clear
    push r17
      rcall si7021MeasureHumidity
    pop r17
    brcc SI7021_MeasureHumidity_error
    ori r17, SI7021_FLAGS_HUM_VALID
    sts si7021Flags, r17
    rcall si7021CalcHumidity             ; 
    sts si7021LastHumidity, r18
    sts si7021LastHumidity+1, r19
SI7021_MeasureHumidity_done:
  pop r15
  out SREG, r15
  sec
  ret
SI7021_MeasureHumidity_error:
  pop r15
  out SREG, r15
  clc
  ret
; @end



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

si7021CheckPresence:
  ldi r16, (SI7021_ADDR*2)+1
  rjmp twiCheckPresence



; ---------------------------------------------------------------------------
; 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 Utils_Mulu16x16_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 Utils_Mulu16x16_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



; ---------------------------------------------------------------------------
; @routine SI7021_GetValue @global
;
; @param   R16     value to get (SI7021_VALUE_TEMP, SI7021_VALUE_HUMIDITY)
; @return  CFLAG   set if value available, cleared otherwise
; @return  R19:R18 value
; @return  R21:R20 denom (e.g. 100, meaning value must be divided by 100)
; @clobbers R16

SI7021_GetValue:
  cpi r16, SI7021_VALUE_TEMP
  breq SI7021_GetValue_temp
  cpi r16, SI7021_VALUE_HUMIDITY
  breq SI7021_GetValue_hum
SI7021_GetValue_clcRet:
  clc
  ret
SI7021_GetValue_temp:
  lds r16, si7021Flags
  andi r16, SI7021_FLAGS_TEMP_VALID
  breq SI7021_GetValue_clcRet
  lds r18, si7021LastTemp            ; value
  lds r19, si7021LastTemp+1
  ldi r20, 100                       ; denominator
  rjmp SI7021_GetValue_finishValue
SI7021_GetValue_hum:
  lds r16, si7021Flags
  andi r16, SI7021_FLAGS_HUM_VALID
  breq SI7021_GetValue_clcRet
  lds r18, si7021LastHumidity        ; value
  lds r19, si7021LastHumidity+1
  ldi r20, 1                         ; denominator
SI7021_GetValue_finishValue:
  clr r21
  sec
  ret
; @end
  




SI7021_END:
.equ MODULE_SIZE_SI7021 = SI7021_END-SI7021_BEGIN


