Files
aqhomecontrol/avr/modules/si7021/main.asm
2023-04-22 00:23:44 +02:00

496 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 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
; ***************************************************************************
; 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_PeriodicMeasurement
;
; Call this routine periodically to take measurements.
; IN:
; - nothing
; OUT:
; - CFLAG: set if okay, clear on error
; USED:
SI7021_PeriodicMeasurement:
in r15, SREG
push r15
cli
lds r17, si7021Flags
mov r16, r17
andi r16, SI7021_FLAGS_LASTWASTEMP
brne si7021PeriodicMeasurement_hum
ori r17, SI7021_FLAGS_LASTWASTEMP
sts si7021Flags, r17
push r17
rcall si7021MeasureTemp
pop r17
brcc si7021PeriodicMeasurement_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 si7021PeriodicMeasurement_done
si7021PeriodicMeasurement_hum:
andi r17, ~SI7021_FLAGS_LASTWASTEMP
sts si7021Flags, r17
push r17
rcall si7021MeasureHumidity
pop r17
brcc si7021PeriodicMeasurement_error
ori r17, SI7021_FLAGS_HUM_VALID
sts si7021Flags, r17
rcall si7021CalcHumidity
sts si7021LastHumidity, r18
sts si7021LastHumidity+1, r19
si7021PeriodicMeasurement_done:
pop r15
out SREG, r15
sec
ret
si7021PeriodicMeasurement_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)
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
Utils_WaitNanoSecs 100000, 0, r22
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_TEMP1 ; 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_HUM1 ; 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_WriteValue
rjmp COM2_SendPacket
SI7021_END:
.equ MODULE_SIZE_SI7021 = SI7021_END-SI7021_BEGIN