Files
aqhomecontrol/avr/modules/reed/main.asm
2023-05-14 22:24:05 +02:00

465 lines
12 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. *
; ***************************************************************************
; This module monitors up to two reed contacts to determine whether a windows
; door is open.
;
; It has two modes.
;
; Standard Mode
; -------------
;
; In this mode up to two windows can be monitored. Each reed contact is reported on separately.
; If a window/door is open a value of 255 is reported, 0 otherwise.
; Tilt Detect Mode
; ----------------
;
; In this mode both reed sensors are handled together to determine whether a window
; or door is closed, tilted or fully open.
; - if exactly one of two reed contacts is open the window/door is reported as tilted (value 128)
; - if both reed contacts are open the window/door is reported as fully open (value 255)
; - if both reed contacts are closed the window/door is reported as closed (value 0)
;
;
; The window state is reported via the VALUE message with a value id of VALUE_ID_REED1 for the
; first reed contact (or for the whole window/door if in tilt detection mode), for the second
; reed contact a value id of VALUE_ID_REED2 is used. Value type is always AQHOME_VALUETYPE_DOOR
; (i.e. for doors and windows).
;
; There are basically two types of reed contacts.
; - normally closed (NC): The contact is closed as long as the magnet is near the switch.
; If the magnet is removed the contact opens.
; This is the default working mode of this module.
; - normally open (NO): The contact is open as long as the magnet is near the switch.
; If the magnet is removed the contact closes.
; To account for those different types of working modes this mode is configurable.
; The state byte contains the state for two reed contacts (one in each nibble).
;
; bit meaning
; ------------------------------
; 0 last measurement
; 1 measurement before last
; 2 change occurred
; 3 current stable state
; ***************************************************************************
; defs
.equ REED_STATE_MASK_CURRENT = 0x8
.equ REED_STATE_MASK_CHANGED = 0x4
.equ REED_STATE_MASK_BEFORELAST = 0x2
.equ REED_STATE_MASK_LAST = 0x1
.equ REED_STATE_MASK_LASTVALUES = 0x3 ; bits 0 and 1
.equ REED_STATE_BIT_CURRENT = 3
.equ REED_STATE_BIT_CHANGED = 2
.equ REED_CONFIG_MASK_SET = 0x80
.equ REED_CONFIG_MASK_TILTDETECT = 0x40 ; use both reed contacts to detect tilted windows
.equ REED_CONFIG_MASK_NO = 0x20 ; if set: normally open (e.g. disconnected when magnet near)
.equ REED_CONFIG_BIT_TILTDETECT = 6
.equ REED_CONFIG_BIT_NO = 5
.equ REED_INITIAL_CONFIG = REED_CONFIG_MASK_SET
.equ REED_CFGMOD_CMD_SETCONFIG = 1 ; only command for now
; ***************************************************************************
; data
.dseg
reedDataBegin:
reedConfigFromEeprom: .byte 1
reedState: .byte 1
reedDataEnd:
; ***************************************************************************
; code
.cseg
; ---------------------------------------------------------------------------
; @routine REED_Init
;
; Init module.
;
; @return CFLAG set if okay, cleared on error
; @clobbers r16, r17, X
REED_Init:
; preset SRAM data area
ldi xh, HIGH(reedDataBegin)
ldi xl, LOW(reedDataBegin)
clr r16
ldi r17, (reedDataEnd-reedDataBegin)
rcall Utils_FillSram ; (r17, x)
cbi REEDOUT_PORT, REEDOUT_PINNUM ; set common reed port to low
sbi REEDOUT_DDR, REEDOUT_PINNUM ; set common reed output port as output
cbi REED1_PORT, REED1_PINNUM ; disable internal pullup for DATA
cbi REED1_DDR, REED1_PINNUM ; set reed1 port as input
cbi REED2_PORT, REED2_PINNUM ; disable internal pullup for DATA
cbi REED2_DDR, REED2_PINNUM ; set reed2 port as input
rcall reedSetupConfig ; initial setup from EEPROM, if possible
ret
REED_Fini:
sec
ret
REED_Run:
; clc ; debug
; ret ; debug
push r15
in r15, SREG
cli
rcall reedHandleChanges
out SREG, r15
pop r15
clc
ret
REED_Every100ms:
push r15
in r15, SREG
cli
rcall reedUpdateState ; (r16, r17, r18, r19)
out SREG, r15
pop r15
ret
; ---------------------------------------------------------------------------
; @routine reedSetupConfig
;
; Read initial config from EEPROM or setup from new and write to EEPROM
;
; @clobbers r16, X
reedSetupConfig:
ldi xh, HIGH(EEPROM_OFFS_REED_CONF) ; read initial config from EEPROM
ldi xl, LOW(EEPROM_OFFS_REED_CONF) ;
rcall Utils_ReadEepromIncr ; (r16)
sbiw xh:xl, 1 ; called routine increments X, undo
tst r16
breq reedSetupConfig_noConfig
cpi r16, 0xff
brne reedSetupConfig_haveConfig
reedSetupConfig_noConfig:
ldi r16, REED_INITIAL_CONFIG
; disabled for now
; debug rcall Utils_WriteEepromIncr
reedSetupConfig_haveConfig:
sts reedConfigFromEeprom, r16
reedSetupConfig_end:
ret
; ---------------------------------------------------------------------------
; @routine reedHandleChanges
;
; @clobbers all
reedHandleChanges:
lds r16, reedConfigFromEeprom
sbrc r16, REED_CONFIG_BIT_TILTDETECT
rjmp reedHandleChangesTiltDetectMode
rjmp reedHandleChangesSingleMode
; @end
; ---------------------------------------------------------------------------
; @routine reedHandleChangesSingleMode
;
; @clobbers all
reedHandleChangesSingleMode:
lds r19, reedState
; reed 1
mov r16, r19
ldi r17, VALUE_ID_REED1
push r19
rcall reedSendValueIfChanged
pop r19
andi r16, 0x0f
andi r19, 0xf0
or r19, r16
; reed 2
mov r16, r19
swap r16
ldi r17, VALUE_ID_REED2
push r19
rcall reedSendValueIfChanged
pop r19
andi r16, 0x0f
swap r16
andi r19, 0x0f
or r19, r16
sts reedState, r19
ret
; @end
; ---------------------------------------------------------------------------
; @routine reedHandleChangesTiltDetectMode
;
; Handle changes in multi reed contact mode.
; This allows for detection of tilted windows/doors.
;
; @clobbers all
reedHandleChangesTiltDetectMode:
lds r19, reedState
mov r16, r19
andi r16, (REED_STATE_MASK_CHANGED | (REED_STATE_MASK_CHANGED<<4))
breq reedHandleChangesTiltDetectMode_end ; no changes
; at least one contact changed, handle new state
clr r16
sbrc r19, REED_STATE_BIT_CURRENT
inc r16
sbrc r19, (REED_STATE_BIT_CURRENT+4)
inc r16
; no check for full state (0=closed, 1=tilted, 2=fully open)
tst r16
breq reedHandleChangesTiltDetectMode_closed
cpi r16, 1
breq reedHandleChangesTiltDetectMode_tilted
; otherwise fully open, fallthrough
ldi r16, 255
rjmp reedHandleChangesTiltDetectMode_sendChanges
reedHandleChangesTiltDetectMode_closed:
ldi r16, 0
rjmp reedHandleChangesTiltDetectMode_sendChanges
reedHandleChangesTiltDetectMode_tilted:
ldi r16, 128
reedHandleChangesTiltDetectMode_sendChanges:
ldi r17, VALUE_ID_REED1
push r19
rcall reedSendChange
pop r19
brcc reedHandleChangesTiltDetectMode_end
; changes handled, clear change flags and write back
andi r19, ~(REED_STATE_MASK_CHANGED | (REED_STATE_MASK_CHANGED<<4))
sts reedState, r19
reedHandleChangesTiltDetectMode_end:
ret
; @end
; ---------------------------------------------------------------------------
; @routine reedSendValueIfChanged
;
; @return r16 updated state
; @param r16: current state nibble
; @param r17: value id
; @clobbers r16, r19 (all others)
reedSendValueIfChanged:
push r16
mov r18, r16
andi r16, REED_STATE_MASK_CHANGED
breq reedSendValueIfChanged_l1 ; jmp if not changed
; value changed
clr r16
sbrc r18, REED_STATE_BIT_CURRENT
dec r16 ; if stable state=0 (meaning: closed)
rcall reedSendChange
brcc reedSendValueIfChanged_l1
pop r16
andi r16, ~REED_STATE_MASK_CHANGED ; clear change bit
ret
reedSendValueIfChanged_l1:
pop r16
ret
; @end
; ---------------------------------------------------------------------------
; @routine reedSendChange
;
; @param r16 value
; @param r17 value id
; @clobbers r16, r18, r19, r20, r21, r22, X (R3, R4, R6, R7, R8, R9, R10, R11, R12, R15, R16, R17, R18, R19, R20, R21, R22, X, Y)
reedSendChange:
mov r18, r16 ; value to send in r19:r18
clr r19
ldi r16, 0xff ; dest addr: send to all
ldi r20, 1 ; denominator in r21:r20
clr r21
ldi r22, AQHOME_VALUETYPE_DOOR
ldi xl, LOW(com2SendBuffer)
ldi xh, HIGH(com2SendBuffer)
rcall CPRO_WriteValue ; (R3, R4, R6, R7, R8, R9, R10, R11, R12, R15, R16, R17, R18, R19, R20, R21, X, Y)
rjmp COM2_SendPacket ; (r18, r19, r22, X)
; @end
; ---------------------------------------------------------------------------
; @routine reedUpdateState
;
; Updates SDRAM variable reedState according to current pin states.
;
; @clobbers r16, r17, r18, r19, r20
reedUpdateState:
lds r19, reedState ; current reed state
lds r20, reedConfigFromEeprom
; handle reed 1 (low nibble state)
rcall reedReadValue1 ; (r16)
sbrc r20, REED_CONFIG_BIT_NO
com r16 ; invert reed data if "normally open" contact
andi r16, 1
mov r17, r16
mov r16, r19
rcall reedUpdateStateNibble ; (r18)
; replace lower state nibble with new value
andi r16, 0x0f ; only keep low nibble
andi r19, 0xf0 ; clear out low nibble before or'ing new value in
or r19, r16
; handle reed2 (high nibble state)
rcall reedReadValue2 ; (r16)
sbrc r20, REED_CONFIG_BIT_NO
com r16 ; invert reed data if "normally open" contact
andi r16, 1
mov r17, r16
mov r16, r19
swap r16
rcall reedUpdateStateNibble ; (r18)
swap r16
; replace higher state nibble with new value
andi r16, 0xf0
andi r19, 0x0f
or r19, r16
; write back reed state
sts reedState, r19
ret
; @end
; ---------------------------------------------------------------------------
; @routine reedUpdateStateNibble
;
; @return r16 updated current state nibble
; @param r16 current state nibble
; @param r17 current pin status (o if clear, 1 if set)
; @clobbers r18
reedUpdateStateNibble:
mov r18, r16
andi r18, REED_STATE_MASK_LAST ; clear everything but last measurement
lsl r18 ; shift previous measurement to left
andi r17, 1
or r18, r17 ; or current measurement into value
; store new value in state nibble
andi r16, ~REED_STATE_MASK_LASTVALUES ; clear pin states
or r16, r18 ; put updated measurements back into state nibble
; check for new stable state
tst r18 ; last measurements all zero?
breq reedUpdateStateNibble_stable0
cpi r18, 0x03 ; last measurements all one?
breq reedUpdateStateNibble_stable1
; not a stable state right now
ret
reedUpdateStateNibble_stable0:
sbrs r16, REED_STATE_BIT_CURRENT ; new stable is 0, changed?
ret ; ret if no change
ori r16, REED_STATE_MASK_CHANGED ; flag change
andi r16, ~REED_STATE_MASK_CURRENT ; set new stable value to 0
ret
reedUpdateStateNibble_stable1:
sbrc r16, REED_STATE_BIT_CURRENT ; new stable is 1, changed?
ret ; ret if no change
ori r16, (REED_STATE_MASK_CHANGED | REED_STATE_MASK_CURRENT) ; flag change, set new stable value to 1
ret
; @end
; ---------------------------------------------------------------------------
; @routine reedReadValue1
;
; @return r16 current pin state
reedReadValue1:
clr r16
clc
sbic REED1_PIN, REED1_PINNUM
sec
adc r16, r16
ret
; @end
; ---------------------------------------------------------------------------
; @routine reedReadValue2
;
; @return r16 current pin state
reedReadValue2:
clr r16
clc
sbic REED2_PIN, REED2_PINNUM
sec
adc r16, r16
ret
; @end