; ***************************************************************************
; 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 SGP40_FLAGS_PRESENT_BIT    = 7
.equ SGP40_FLAGS_DATAVALID_BIT  = 6

.equ SGP40_CMD_MEASURE          = 0x260f



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

.dseg

sgp40DataBegin:
  sgp40Flags:            .byte 1
  sgp40LastValue:        .byte 2
sgp40DataEnd:



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

.cseg



; ---------------------------------------------------------------------------
; SGP40_Init
;

SGP40_Init:
  ldi xh, HIGH(sgp40DataBegin)
  ldi xl, LOW(sgp40DataBegin)
  clr r16
  ldi r17, (sgp40DataEnd-sgp40DataBegin)
  rcall Utils_FillSram

  ; check presence
  rcall sgp40CheckPresence
  brcc SGP40_Init_error
  lds r16, sgp40Flags
  ori r16, (1<<SGP40_FLAGS_PRESENT_BIT)
  sts sgp40Flags, r16
  sec
  ret
SGP40_Init_error:
  clc
  ret
; @end



; ---------------------------------------------------------------------------
; SGP40_Fini
;

SGP40_Fini:
  sec
  ret
; @end



; ---------------------------------------------------------------------------
; @routine SGP40_GetValue @global
;
; @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

SGP40_GetValue:
  lds r16, sgp40Flags
  andi r16, (1<<SGP40_FLAGS_DATAVALID_BIT)
  brne SGP40_GetValue_gotValue
  clc
  ret
SGP40_GetValue_gotValue:
  lds r18, sgp40LastValue
  lds r19, sgp40LastValue+1
  ldi r20, 1
  clr r21
  sec
  ret
; @end



; ---------------------------------------------------------------------------
; @routine sgp40CheckPresence
;
; Expects interrupts being disabled!
;
; @return CFLAG set if okay, clear on error
; @clobbers R16 (R17, R18, R22)

sgp40CheckPresence:
  ldi r16, (SGP40_ADDR*2)+1 ; read access
  rjmp twiCheckPresence     ; (R16, R17, R18, R22)
; @end



SGP40_MeasureRawSignal:
  in r15, SREG
  push r15
    cli
    lds r17, sgp40Flags
    sbrs r17, SGP40_FLAGS_PRESENT_BIT   ; investigate PRESENT BIT
      rjmp SGP40_MeasureRawSignal_error ; jmp if PRESENT bit clear
    rcall sgp40Measure                  ; R19:18=value
    brcc SGP40_MeasureRawSignal_error
    lds r17, sgp40Flags
    ori r17, (1<<SGP40_FLAGS_DATAVALID_BIT)
    sts sgp40LastValue, r18
    sts sgp40LastValue+1, r19
    sts sgp40Flags, r17
SGP40_MeasureRawSignal_done:
  pop r15
  out SREG, r15
  sec
  ret
SGP40_MeasureRawSignal_error:
  pop r15
  out SREG, r15
  clc
  ret
; @end




; ---------------------------------------------------------------------------
; @routine sgp40Measure
;
; 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.
;
; @return CFLAG   set if okay, clear on error
; @return r19:r18 received raw value
; @clobbers R14, R16, R18, R19, R22

sgp40Measure:
  ; send request
  rcall twiStart                     ; (R22)
  ldi r16, (SGP40_ADDR*2)            ; write access
  rcall twiSendByteExpectAck         ; (R16, R17, R18, R22)
  brcc sgp40Measure_error
  ldi zl, LOW(sgp40FlashDataCmdMeasureDefault*2)
  ldi zh, HIGH(sgp40FlashDataCmdMeasureDefault*2)
  ldi r19, 8
  rcall twiSendFromFlashExpectAck
  brcc sgp40Measure_error              ; no ACK or other error
  ; receive response
  ldi r16, 200
  mov r14, r16
sgp40Measure_loop:                     ; (R22)
    rcall twiRestart
    ldi r16, (SGP40_ADDR*2)+1          ; read access
    rcall twiSendByteExpectAck         ; (R16, R17, R18, R22)
    brcs sgp40Measure_gotValue         ; chip responds, receive values
    dec r14
    breq sgp40Measure_error            ; timeout
    rcall Utils_WaitFor50MicroSecs     ; wait for 100usecs total
    rcall Utils_WaitFor50MicroSecs
  rjmp sgp40Measure_loop
  
sgp40Measure_gotValue:
  rcall twiReceiveByteSendAck          ; (R16, R17, R18, R22)
  brcc sgp40Measure_error
  mov r19, r16                         ; MSByte
  rcall twiReceiveByteSendAck          ; (R16, R17, R18, R22)
  brcc sgp40Measure_error
  mov r18, r16                         ; LSByte
  push r18
    rcall twiReceiveByte               ; R16=CRC8, no ACK (R16, R17, R18, R22)
  pop r18
  brcc sgp40Measure_error
  rcall twiStop                        ; (R22)
  sec
  ret
sgp40Measure_error:
  rcall twiStop                        ; (R22)
  clc
  ret
; @end






sgp40FlashDataCmdMeasureDefault: .db 0x26, 0x0f, 0x80, 0x00, 0xa2, 0x66, 0x66, 0x93



