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



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

.equ CCS811_FLAGS_ACTIVATED      = 0x80
.equ CCS811_FLAGS_RESETTED       = 0x40
.equ CCS811_FLAGS_VALIDDATA      = 0x20

.equ CCS811_VALUE_TVOC           = 0x01
.equ CCS811_VALUE_CO2            = 0x02

.equ CCS811_WAITMS_AFTER_RESET   = 50
.equ CCS811_WAITMS_AFTER_START   = 50
                                 
.equ CCS811_REG_STATUS           = 0x00
.equ CCS811_REG_MEASMODE         = 0x01
.equ CCS811_REG_DATA             = 0x02
.equ CCS811_REG_APP_START        = 0xf4
.equ CCS811_REG_SWRESET          = 0xff

.equ CCS811_STATUS_FWMODE_BIT    = 7
.equ CCS811_STATUS_APPVALID_BIT  = 4
.equ CCS811_STATUS_DATAREADY_BIT = 3
.equ CCS811_STATUS_ERROR_BIT     = 0

.equ CCS811_MEASUREMODE_DM2_BIT  = 6
.equ CCS811_MEASUREMODE_DM1_BIT  = 5
.equ CCS811_MEASUREMODE_DM0_BIT  = 4

.equ CCS811_MEASUREMODE = (0<<CCS811_MEASUREMODE_DM2_BIT) | (1<<CCS811_MEASUREMODE_DM1_BIT) | (1<<CCS811_MEASUREMODE_DM0_BIT)
;.equ CCS811_MEASUREMODE = (0<<CCS811_MEASUREMODE_DM2_BIT) | (1<<CCS811_MEASUREMODE_DM1_BIT) | (0<<CCS811_MEASUREMODE_DM0_BIT)



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

.dseg

ccs811DataBegin:
  ccs811Flags:            .byte 1
  ccs811ResponseData:     .byte 5
ccs811DataEnd:



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

.cseg


CCS811_BEGIN:



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

CCS811_Init:
  ldi xh, HIGH(ccs811DataBegin)
  ldi xl, LOW(ccs811DataBegin)
  clr r16
  ldi r17, (ccs811DataEnd-ccs811DataBegin)
  rcall Utils_FillSram

  ; check for fw mode, valid app
  rcall ccs811ReadStatus
  brcc CCS811_Init_error
  
  ldi r17, (1<<CCS811_STATUS_FWMODE_BIT)     ; already in FW mode?
  and r17, r16
  breq CCS811_Init_startApp
  rcall ccs811Reset
  brcc CCS811_Init_error

  lds r17, ccs811Flags
  ori r17, CCS811_FLAGS_RESETTED
  sts ccs811Flags, r17
  
  ldi r16, CCS811_WAITMS_AFTER_RESET
  rcall ccs811WaitMs
  
  rcall ccs811ReadStatus
  brcc CCS811_Init_error

CCS811_Init_startApp:
; check for valid app
  andi r16, (1<<CCS811_STATUS_APPVALID_BIT)
  breq CCS811_Init_error                     ; no valid app

  ; start app
  rcall ccs811StartApp
  brcc CCS811_Init_error

  ldi r16, CCS811_WAITMS_AFTER_START
  rcall ccs811WaitMs

  rcall ccs811ReadStatus
  brcc CCS811_Init_error
  andi r16, (1<<CCS811_STATUS_FWMODE_BIT) ; in firmware mode?
  breq CCS811_Init_error                  ; no, error

  ; set measurement mode
  ldi r16, CCS811_MEASUREMODE
  rcall ccs811SetMeasureMode
  brcc CCS811_Init_error

  lds r17, ccs811Flags
  ori r17, CCS811_FLAGS_ACTIVATED
  sts ccs811Flags, r17
  sec
  ret
CCS811_Init_error:
  clc
  ret


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

CCS811_Fini:
  sec
  ret



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

CCS811_OnEveryMinute:
  in r15, SREG
  push r15
    cli
    lds r16, ccs811Flags
    andi r16, CCS811_FLAGS_ACTIVATED
    breq CCS811_OnTimer_ret

    lds r16, ccs811Flags
    andi r16, ~CCS811_FLAGS_VALIDDATA
    sts ccs811Flags, r16
    rcall ccs811ReadMeasurement
    brcc CCS811_OnTimer_ret
    lds r16, ccs811ResponseData+4
    andi r16, (1<<CCS811_STATUS_DATAREADY_BIT)
    breq CCS811_OnTimer_ret      ; jmp if data not ready
    lds r16, ccs811Flags
    ori r16, CCS811_FLAGS_VALIDDATA
    sts ccs811Flags, r16
CCS811_OnTimer_ret:
  pop r15
  out SREG, r15
  ret



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

ccs811CheckPresence:
  rcall twiStart
  ldi r16, (CCS811_ADDR*2)+1
  rcall twiSendByte
  brcc ccs811CheckPresence_notfound
  rcall twiStop
  sec
  ret

ccs811CheckPresence_notfound:
  rcall twiStop
  clc
  ret


ccs811ReadStatus:
  rcall twiStart                         ; (R22)
  ldi r16, (CCS811_ADDR*2)               ; write access
  rcall twiSendByteExpectAck
  brcc ccs811ReadStatus_error
  ldi r16, CCS811_REG_STATUS
  rcall twiSendByteExpectAck
  brcc ccs811ReadStatus_error
  rcall twiRestart                       ; (R22)
  ldi r16, (CCS811_ADDR*2)+1             ; read access
  rcall twiSendByteExpectAck
  brcc ccs811ReadStatus_error
  rcall twiReceiveByte                   ; don't send ACK
  brcc ccs811ReadStatus_error
  rcall twiStop                          ; (R22)
  sec
  ret
ccs811ReadStatus_error:
  rcall twiStop
  clc
  ret
; @end



ccs811StartApp:
  rcall twiStart                         ; (R22)
  ldi r16, (CCS811_ADDR*2)               ; write access
  rcall twiSendByteExpectAck
  brcc ccs811StartApp_error
  ldi r16, CCS811_REG_APP_START
  rcall twiSendByteExpectAck
  brcc ccs811StartApp_error
  rcall twiStop                          ; (R22)
  sec
  ret
ccs811StartApp_error:
  rcall twiStop
  clc
  ret
; @end


; @clobbers r16, r17, r22

ccs811SetMeasureMode:
  mov r19, r16
  rcall twiStart                         ; (R22)
  ldi r16, (CCS811_ADDR*2)               ; write access
  rcall twiSendByteExpectAck
  brcc ccs811SetMeasureMode_error
  ldi r16, CCS811_REG_MEASMODE
  rcall twiSendByteExpectAck
  brcc ccs811SetMeasureMode_error
  mov r16, r19
  rcall twiSendByteExpectAck
  brcc ccs811SetMeasureMode_error
  rcall twiStop                          ; (R22)
  sec
  ret
ccs811SetMeasureMode_error:
  rcall twiStop
  clc
  ret
; @end



ccs811Reset:
  rcall twiStart                         ; (R22)
  ldi r16, (CCS811_ADDR*2)               ; write access
  rcall twiSendByteExpectAck
  brcc ccs811Reset_error
  ldi r16, CCS811_REG_SWRESET
  rcall twiSendByteExpectAck
  brcc ccs811Reset_error
  ldi r16, 0x11
  rcall twiSendByteExpectAck
  brcc ccs811Reset_error
  ldi r16, 0xe5
  rcall twiSendByteExpectAck
  brcc ccs811Reset_error
  ldi r16, 0x72
  rcall twiSendByteExpectAck
  brcc ccs811Reset_error
  ldi r16, 0x8a
  rcall twiSendByteExpectAck
  brcc ccs811Reset_error
  rcall twiStop                          ; (R22)
  sec
  ret
ccs811Reset_error:
  rcall twiStop
  clc
  ret
; @end



ccs811ReadMeasurement:
  ldi xh, HIGH(ccs811ResponseData)
  ldi xl, LOW(ccs811ResponseData)
  ldi r16, CCS811_REG_DATA
  ldi r17, 5
  rjmp ccs811ReadData
; @end



; @param r16 reg to read
; @param r17 number of bytes to read
; @param X pointer to buffer to receive data
; @return CFLAG set if okay, cleared on error
; @return r16 number of bytes received if CFLAG set

ccs811ReadData:
  mov r20, r16
  mov r19, r17
  rcall twiStart                         ; (R22)
  ldi r16, (CCS811_ADDR*2)               ; write access
  rcall twiSendByteExpectAck             ; (R16, R17, R18, R22)
  brcc ccs811ReadData_error
  mov r16, r20
  rcall twiSendByteExpectAck
  brcc ccs811ReadData_error
  rcall twiRestart                       ; (R22)
  ldi r16, (CCS811_ADDR*2)+1             ; read access
  rcall twiSendByteExpectAck
  brcc ccs811ReadData_error

  clr r20
ccs811ReadData_loop1:
    cpi r19, 1
    breq ccs811ReadData_recvLast
    rcall twiReceiveByteSendAck            ; (R16, R17, R18, R22)
    rjmp ccs811ReadData_recvd
ccs811ReadData_recvLast:
    rcall twiReceiveByte                   ; don't send ACK (R16, R17, R18, R22)
ccs811ReadData_recvd:
    brcc ccs811ReadData_error
    st X+, r16
    inc r20
    dec r19
  brne ccs811ReadData_loop1
  rcall twiStop                          ; (R22)
  mov r16, r20
  sec
  ret
ccs811ReadData_error:
  rcall twiStop
  clc
  ret
; @end



; ---------------------------------------------------------------------------
; @routine CCS811_GetValue @global
;
; @param   R16     value to get (CCS811_VALUE_TVOC, CCS811_VALUE_CO2)
; @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 

CCS811_GetValue:
  lds r17, ccs811Flags
  andi r17, CCS811_FLAGS_VALIDDATA
  breq CCS811_GetValue_noData
  cpi r16, CCS811_VALUE_TVOC
  breq CCS811_GetValue_retTvoc
  cpi r16, CCS811_VALUE_CO2
  breq CCS811_GetValue_retCo2
CCS811_GetValue_noData:
  clc
  ret
CCS811_GetValue_retTvoc:
  lds r18, ccs811ResponseData+3      ; value
  lds r19, ccs811ResponseData+2
  ldi r20, 1
  clr r21
  rjmp CCS811_GetValue_secRet
CCS811_GetValue_retCo2:
  lds r18, ccs811ResponseData+1      ; value
  lds r19, ccs811ResponseData
  ldi r20, 1
  clr r21
CCS811_GetValue_secRet:
  sec
  ret
; @end




ccs811WaitMs:
ccs811WaitMs_loop1:
    ldi r17, 10
ccs811WaitMs_loop2:
      rcall Utils_WaitFor100MicroSecs
      dec r17
    brne ccs811WaitMs_loop2
    dec r16
  brne ccs811WaitMs_loop1
  ret  
; @end






CCS811_END:
.equ MODULE_SIZE_CCS811 = CCS811_END-CCS811_BEGIN


