Files
aqhomecontrol/avr/modules/ccs811/main.asm
2024-12-15 18:20:54 +01:00

480 lines
11 KiB
NASM

; ***************************************************************************
; 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_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)
.equ CCS811_INTERVAL = 60
; ***************************************************************************
; data
.dseg
ccs811DataBegin:
ccs811Flags: .byte 1
ccs811Timer: .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
ldi r16, CCS811_INTERVAL
sts ccs811Timer, r16
; 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_OnTimer:
in r15, SREG
push r15
cli
lds r16, ccs811Flags
andi r16, CCS811_FLAGS_ACTIVATED
breq CCS811_OnTimer_ret
lds r16, ccs811Timer
dec r16
cpi r16, 2
breq CCS811_OnTimer_measure
cpi r16, 1
breq CCS811_OnTimer_sendCO2
tst r16
breq CCS811_OnTimer_sendTVOC
sts ccs811Timer, r16
rjmp CCS811_OnTimer_ret
CCS811_OnTimer_measure:
lds r16, ccs811Flags
andi r16, ~CCS811_FLAGS_VALIDDATA
sts ccs811Flags, r16
rcall ccs811ReadMeasurement
; brcc CCS811_OnTimer_ret
brcc CCS811_OnTimer_storetimerret
; rcall ccs811SendDebug ; debug
lds r16, ccs811ResponseData+4
andi r16, (1<<CCS811_STATUS_DATAREADY_BIT)
; breq CCS811_OnTimer_ret
breq CCS811_OnTimer_storetimerret ; jmp if data not ready
lds r16, ccs811Flags
ori r16, CCS811_FLAGS_VALIDDATA
sts ccs811Flags, r16
rjmp CCS811_OnTimer_storetimerret
CCS811_OnTimer_sendCO2:
lds r16, ccs811Flags
andi r16, CCS811_FLAGS_VALIDDATA
; breq CCS811_OnTimer_ret
breq CCS811_OnTimer_storetimerret
rcall ccs811SendCo2
rjmp CCS811_OnTimer_storetimerret
CCS811_OnTimer_sendTVOC:
lds r16, ccs811Flags
andi r16, CCS811_FLAGS_VALIDDATA
; breq CCS811_OnTimer_ret
breq CCS811_OnTimer_storetimerret
rcall ccs811SendTvoc
; rjmp CCS811_OnTimer_storetimerret
CCS811_OnTimer_storetimerret:
lds r16, ccs811Timer
dec r16
brne CCS811_OnTimer_storetimer
ldi r16, CCS811_INTERVAL
CCS811_OnTimer_storetimer:
sts ccs811Timer, r16
CCS811_OnTimer_ret:
pop r15
out SREG, r15
clc
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
ccs811SendDebug:
ldi r16, 0xff ; destination address
ldi r17, VALUE_ID_DEBUG ; value id
ldi r22, 0
lds r18, ccs811ResponseData ; value
lds r19, ccs811ResponseData+1
lds r20, ccs811ResponseData+2
lds r21, ccs811ResponseData+3
ldi xl, LOW(com2SendBuffer)
ldi xh, HIGH(com2SendBuffer)
rcall CPRO_WriteReportValue
rjmp COM2_SendPacket
ccs811SendCo2:
ldi r17, VALUE_ID_CO2 ; value id
ldi r22, AQHOME_VALUETYPE_CO2
lds r18, ccs811ResponseData+1 ; value
lds r19, ccs811ResponseData
rjmp ccs811SendAny
ccs811SendTvoc:
ldi r17, VALUE_ID_TVOC ; value id
ldi r22, AQHOME_VALUETYPE_TVOC
lds r18, ccs811ResponseData+3 ; value
lds r19, ccs811ResponseData+2
rjmp ccs811SendAny
ccs811SendAny:
mov r16, r18 ; check for FF FF
and r16, r19
inc r16
brne ccs811SendAny_send ; send only if data is not FFFF
clc
ret
ccs811SendAny_send:
ldi r16, 0xff ; destination address
ldi r20, 1
clr r21
ldi xl, LOW(com2SendBuffer)
ldi xh, HIGH(com2SendBuffer)
rcall CPRO_WriteReportValue
rjmp COM2_SendPacket
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