496 lines
13 KiB
NASM
496 lines
13 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 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
|
|
|
|
cbi REEDOUT1_PORT, REEDOUT1_PINNUM ; set common reed port to low
|
|
sbi REEDOUT1_DDR, REEDOUT1_PINNUM ; set common reed output port as output
|
|
|
|
.ifdef REEDOUT2_DDR
|
|
cbi REEDOUT2_PORT, REEDOUT2_PINNUM ; set common reed port to low
|
|
sbi REEDOUT2_DDR, REEDOUT2_PINNUM ; set common reed output port as output
|
|
.endif
|
|
|
|
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 REED_SetConfig
|
|
;
|
|
; Set config and write to EEPROM
|
|
;
|
|
; @return nothing
|
|
; @param r16 new configuration
|
|
; @clobbers r16, X, (R17)
|
|
|
|
REED_SetConfig:
|
|
sts reedConfigFromEeprom, r16
|
|
ldi xh, HIGH(EEPROM_OFFS_REED_CONF) ; write config to EEPROM
|
|
ldi xl, LOW(EEPROM_OFFS_REED_CONF) ;
|
|
rcall Utils_WriteEepromIncr ; (R17)
|
|
ret
|
|
; @end
|
|
|
|
|
|
|
|
; ---------------------------------------------------------------------------
|
|
; @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
|
|
; @end
|
|
|
|
|
|
|
|
; ---------------------------------------------------------------------------
|
|
; @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, r23, X (R3, R4, R6, R7, R8, R9, R10, R11, R12, R15, R16, R17, R18, R19, R20, R21, R22, X, Y)
|
|
|
|
reedSendChange:
|
|
lds r18, com2Address
|
|
tst r18
|
|
brne reedSendChange_haveAddress
|
|
clc
|
|
ret
|
|
reedSendChange_haveAddress:
|
|
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_WriteReportValue ; (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
|
|
|
|
|
|
|