;***************************************************************************** ; DSC PC1500 Alarm Interface ; Most recent version: 0.6 2025/08/18 ; Target Chip dsPIC30F4012-30I ; ; Tools Used : MPLAB X IDE 2.20 ; ; Hardware: RB0 = CLK input (interrupt on change) ; RB1 = DATA input ; RB2 = DATA low transmit output ; RB3 = ARM Trigger from Kasa Interface (active low) ; RB4 = AC On from Kasa Interface (active low) ; RB5 = Toggle (Kasa switch state) output ; RD0 = Heartbeat/Link OK LED output ; RD1 = Command Fault LED output ; RE0-RE5 are diagnostic LED indicators ; RE0 = READY LED state indicator ; RE1 = ARMED LED state indicator ; RE8 = Learn Pushbutton input ; ;***************************************************************************** .equ __30F4012, 1 .include "p30f4012.inc" ;............................................................................. ; Configuration bits: ;............................................................................. ; config __FOSC, CSW_FSCM_OFF & XT_PLL16 ;16xPLL - 120MHz/30MIPs at 7.3728MHz XTAL ;Fosc=117.9648MHz, Fcy=29.4912MHz config __FOSC, CSW_FSCM_OFF & XTL ;x1 CLK Fosc=7.38728MHz, Fcy=1.8432MHz config __FWDT, WDT_OFF ;Turn off Watchdog Timer config __FBORPOR, PBOR_ON & BORV27 & PWRT_16 & MCLR_EN ;Set Brown-out Reset voltage config __FGS, CODE_PROT_OFF ;Set Code Protection Off ;............................................................................. ; Global Declarations: ;............................................................................. .global __reset ;Label for first line of code. .global __CNInterrupt ;Interrupt on port change (CLK) .global __T1Interrupt ;Declare Timer 1 ISR name global .global __OscillatorFail ;Declare Oscillator Fail trap routine label .global __AddressError ;Declare Address Error trap routine label .global __StackError ;Declare Stack Error trap routine label .global __MathError ;Declare Math Error trap routine label ;............................................................................. ; Variable Storage in Data space (beginning at 0x850) ;............................................................................. .section .data .align 2 CommState: .space 2 ;State of the Communications interface RxDataTemp: .space 2 ;Temporary RxData: .space 2 ;Received data word RxKeyTemp: .space 2 RxKeyData: .space 2 ;Received keypress data BitCounter: .space 2 ;Bit counter for receiver TxKeyData: .space 2 ;Data to transmit TxKeyDataTemp: .space 2 ArmedState: .space 2 ;State of the Armed input ArmedStateOld: .space 2 ;State of Armed input on last loop ;............................................................................. ; Constants ;............................................................................. ;PORT B,D,E I/O Lines to panel .equ CLK,#0 ;RB0 is clock input .equ DATA,#1 ;RB1 is data input .equ DATA_LOW,#2 ;RB2 is data low transmit .equ ARM,#3 ;RB3 is arm input (active low) .equ AC_ON,#4 ;RB4 is AC on input (active low) .equ TOGGLE,#5 ;RB5 is toggle Kasa switch state (on/off) .equ LEARN,#8 ;RE1 is Learn PB input .equ LINK,#0 ;RD0 is LINK/Heartbeat LED output .equ CMD_ERR,#1 ;RD1 is Command Error LED output ;Flags for CommState global variable .equ ERROR,#0 ;Error flag for CommStatus .equ RECD,#1 ;Flag that a status data word was received .equ SENDKEY,#2 ;Send key in buffer TxKeyData, clr in ISR .equ SENDINPROGRESS,#3 ;Signifies Keypress is being sent in ISR .equ KEYRECD,#4 ;Valid Keypress received, set in isr .equ SECONDKEY,#5 ;Second Key is coming - grab the next one .equ ZERORECD,#6 ;Flag that NO key was received, set in isr ;Flags for ArmedState .equ ARMED,#0 ;Flag that input requests arming ;............................................................................. ; Code Section in Program Memory ;............................................................................. .section .text ;Start of Code section ;#################################### ;########### MAIN CODE ########## ;#################################### __reset: ;Setup Stack for Calls in program memory nop nop mov #__SP_init,W15 mov #__SPLIM_init, W0 ;Initialize the Stack Pointer Limit Register mov W0, SPLIM ;Configure processor bset CORCON,#IF ;Select Integer mode for DSP Multiplier bset CORCON,#PSV ;Enable PSV for program memory access (for table access) ;Setup I/O Ports mov #0B1111111111011011,W1 ;RB0=In(CLK),RB1=In(Data),RB2=Out(Data Low Tx) mov W1,TRISB ; RB3=In(Arm),RB4=In(AC_On),RB5=Out(Toggle) mov #0B1111111111111100,W1 ;RD0=Out(Link LED), RD1=Out(Cmd Err LED) mov W1,TRISD mov #0B1111111111000000,W1 ;RE0-5=Out(Diagnostic LEDs),RE8=In(Learn PB) mov W1,TRISE ;Setup Timer 1 for 30ms timout: Assume Tcy = 1.8432MHz mov #0x8000,w1 mov w1,T1CON ;1:1, Int Clk, ON mov #55296,W1 ;Load PR1 for 30ms (link lost timeout) mov w1,PR1 clr TMR1 bclr IFS0,#T1IF ;Clear Interrupt Flag bset IEC0,#T1IE ;Interrupt Enable for Timer 1 ;Setup Timer #2 for 20ms timeout (no interrupt) mov #0x8000,w1 mov w1,T2CON ;Must be same as Timer 1 mov #36865,W1 ;Min delay for start bit high mov w1,PR2 clr TMR2 bclr IFS0,#T2IF ;Clear Interrupt Flag bclr CommState,#SENDKEY ;Enable change notification on external interrupt for CLK input mov #0B1111111111111111,W0 ;Configure Port B for NO analog inputs mov W0,ADPCFG ; needed when using CN Interrupts bset CNEN1,#CN2IE ;Select CN2 as the source (RB0) bclr IFS0,#CNIF ;Clear Interrupt Flag bset IEC0,#CNIE ;Interrupt Enable for Change Notification on CN2 (RB0) ;*********************************************** ;TEST: RUN ONCE ONLY to initialize EEPROM values ; mov #0B0000000001000001,W0 ;Code for Key "1" 100 0001 ; mov #0,w1 ; call WriteEEPROM ; mov #0B0000000001000001,W0 ;Code for Key "1" 100 0001 ; mov #1,w1 ; call WriteEEPROM ; mov #0B0000000001000001,W0 ;Code for Key "1" 100 0001 ; mov #2,w1 ; call WriteEEPROM ; mov #0B0000000001000001,W0 ;Code for Key "1" 100 0001 ; mov #3,w1 ; call WriteEEPROM ;*********************************************** ;Initialize Flags bclr CommState,#SECONDKEY ;Check Initial State of Arm input clr ArmedState clr ArmedStateOld btsc PORTB,#ARM ;ACTIVE-LOW input goto NotArmedInitially bset ArmedStateOld,#ARMED ;Set Armed state to avoid instant bset ArmedState,#ARMED ; Arm/Disarm on power-up NotArmedInitially: ;########################################### ;########### Main Program ########## ;########################################### MainLoop: ;Wait for state of ARM input from Kasa switch to change clr ArmedState ;Get current input state btss PORTB,#ARM bset ArmedState,#ARMED mov ArmedState,w0 mov ArmedStateOld,w1 cpseq w0,w1 goto ArmedStateChange ;Check if Learn Pushbutton pressed btsc PORTE,#LEARN goto LearnCode ;Check Alarm - if armed/disarmed (possibly by manual keypad), set Kasa switch state to match btss RxData,#9 ;Check if Panel is Armed goto PanelDisarmed PanelArmed: btss PORTB,#ARM ;Check if Kasa switch is On (armed) goto SwitchCheckDone btsc PORTB,#AC_ON ;Kasa off, check if power off goto SwitchCheckDone ;Power is off, just ignore bset PORTB,#TOGGLE ;Toggle Kasa switch to on/arm state mov #250,W1 ;250mS delay call Delay bclr PORTB,#TOGGLE goto SwitchCheckDone PanelDisarmed: btsc PORTB,#ARM ;Check if Kasa switch is On (armed) goto SwitchCheckDone bset PORTB,#TOGGLE ;Toggle Kasa switch to on/arm state mov #250,W1 ;250mS delay call Delay bclr PORTB,#TOGGLE SwitchCheckDone: mov #250,W1 ;250mS delay call Delay goto MainLoop ArmedStateChange: mov ArmedState,w0 ;Sae last state as 'Old' mov w0,ArmedStateOld ;If ARM asserted and RDY status, ARM, else error (beep) btsc PORTB,#ARM ;Check if ARM input asserted goto Disarm btss RxData,#8 ;Check if RDY goto CommandError btsc RxData,#9 ;Check if NOT armed goto CommandError goto SendCode Disarm: ;If DISARM asserted, check AC first (only valid if on) ; and then ARMED status, DISARM, else error (beep) btsc PORTB,#AC_ON ;Check if AC is on goto CommandError btss RxData,#9 ;Check if Armed Already goto CommandError goto SendCode goto MainLoop CommandError: bset PORTD,#CMD_ERR goto MainLoop ;If necessary to send codes more than once, do in ISR: copy TxData to temp var in StartBit and clock out for ;n consecutive packets SendCode: ;If asserted, send code one word at a time from EEPROM mov #0,w1 call ReadEEPROM mov w0,w1 mov W1,TxKeyData bset CommState,#SENDKEY mov #250,W1 ;250mS delay call Delay mov #1,w1 call ReadEEPROM mov w0,w1 mov W1,TxKeyData bset CommState,#SENDKEY mov #250,W1 ;250mS delay call Delay mov #2,w1 call ReadEEPROM mov w0,w1 mov W1,TxKeyData bset CommState,#SENDKEY mov #250,W1 ;250mS delay call Delay mov #3,w1 call ReadEEPROM mov w0,w1 mov W1,TxKeyData bset CommState,#SENDKEY mov #250,W1 ;250mS delay call Delay bclr CommState,#SENDKEY goto MainLoop ;Get four keypresses, store in EEPROM LearnCode: bclr CommState,#KEYRECD WaitForFirstCode: btss CommState,#KEYRECD goto WaitForFirstCode mov RxKeyData,w0 mov #0,w1 ;Store in EEPROM call WriteEEPROM bclr CommState,#ZERORECD WaitForFirstZero: btss CommState,#ZERORECD goto WaitForFirstZero bclr CommState,#KEYRECD WaitForSecondCode: btss CommState,#KEYRECD goto WaitForSecondCode mov RxKeyData,w0 mov #1,w1 ;Store in EEPROM call WriteEEPROM bclr CommState,#ZERORECD WaitForSecondZero: btss CommState,#ZERORECD goto WaitForSecondZero bclr CommState,#KEYRECD WaitForThirdCode: btss CommState,#KEYRECD goto WaitForThirdCode mov RxKeyData,w0 mov #2,w1 ;Store in EEPROM call WriteEEPROM bclr CommState,#ZERORECD WaitForThirdZero: btss CommState,#ZERORECD goto WaitForThirdZero bclr CommState,#KEYRECD WaitForFourthCode: btss CommState,#KEYRECD goto WaitForFourthCode mov RxKeyData,w0 mov #3,w1 ;Store in EEPROM call WriteEEPROM bclr CommState,#KEYRECD goto MainLoop ;########################################### ;########### Rx / Tx CODE ########## ;########################################### ;.............................................................................. ; Interrupt Service Routines ; These routines detect CLK and both receive and transmit data via Keybus ; Also detects errors when CLK not received in 30ms ; Operates as a state machine ; ; CN - CLK input, main Rx (status and key data) and Tx key routines ; TMR1 - 30mS timeout reached, link is not established ; TMR2 - No interrupt but indicates 20mS (start bit) ;.............................................................................. ; Clock Missing timer __T1Interrupt: bset CommState,#ERROR bset LATD,#CMD_ERR bclr IFS0, #T1IF ;Clear the Timer1 Interrupt flag Status Bit retfie ; CLK input - resets the clock missing timer ; Check for start bit, if so reset bit clock counters ; Change Notification detects both edges (H->L = cycle starts, L->H = cycle ends) __CNInterrupt: push w0 push w1 clr TMR1 ;Don't let timer 1 overflow as no error here ;Check if start bit (>20mS high so now low) btss IFS0,#T2IF ;>20mS since last clock? goto NotStartBit btsc PORTB,#CLK ;CLK is now low? goto LinkError ;If not, Clk was low for 30ms - Huh? ;Start bit, Clk is low mov #16,W1 ;Rx/Tx sixteen bits mov W1,BitCounter clr TMR2 bclr IFS0,#T2IF ; clr TMR1 clr RxDataTemp btss CommState,#SENDKEY ;Is a keypress to be sent? goto EndIsr bset CommState,#SENDINPROGRESS ;Set only on start bit to ensure whole is sent mov TxKeyData,w1 ;Copy Key Data to temp register for shift mov w1,TxKeyDataTemp ;Misses first (low) clock as this is start bit bclr CommState,#SENDKEY goto EndIsr NotStartBit: ; bclr IFS0,#T2IF ;Clear 20ms timer clr TMR2 btss PORTB,#CLK goto KeyBit ;If CLK low, it is a keypress bit bclr PORTB,#DATA_LOW ;Clear Data line from last Tx'd bit ;CLK high, receive a single bit mov #47,w0 ;100us Delay for data bit to stabilize InnerLoopRx: ;This is a hack, doing a delay inside an ISR dec W0,W0 ;4 cycles this loop btss SR,#Z goto InnerLoopRx rrnc RxDataTemp btsc PORTB,#DATA bset RxDataTemp,#15 ;Set MSB dec BitCounter ;Wait until all sixteen biits received btss SR,#Z goto EndIsr ;Entire data word received/transmitted mov RxDataTemp,W1 ;Copy valid data mov W1,RxData mov W1,PORTE ;TEST: Output to LEDS bclr PORTE,#4 bclr PORTE,#5 btsc RxData,#8 ;Repeat RDY bit bset PORTE,#4 btsc RxData,#9 ;Repeat ARMED bit bset PORTE,#5 bset CommState,#RECD bclr CommState,#ERROR ;Clear ERROR flag and Led bclr PORTD,#CMD_ERR bclr CommState,#SENDINPROGRESS ;Clear only if actually sent ; bclr CommState,#SENDKEY ;If word was sent, clear flag to show it is done ;Check if zero, if not, copy and flag #KEYRECD. Clear flag in main program rrnc RxKeyTemp ;Rotate out start bit cp0 RxKeyTemp btsc SR,#Z goto ZeroRecd ;Grab second key in a row only (first non-zero might be faulty) btsc CommState,#SECONDKEY goto ValidKey bset CommState,#SECONDKEY goto EndIsr ValidKey: bclr CommState,#SECONDKEY ;If Flagged as key already received,ignore btsc CommState,#KEYRECD ;Still has a valid key in buffer goto EndIsr mov RxKeyTemp,W1 ;Copy valid keypress data mov W1,RxKeyData ;Key is not zero ; mov W1,PORTE ;TEST: Output to LEDS bset CommState,#KEYRECD ;Flag that a non-zero key is available goto EndIsr ZeroRecd: bset CommState,#ZERORECD goto EndIsr ;Clock is low, send a single bit during this period, cancel on next transition KeyBit: bclr PORTB,#DATA_LOW ;Send a high (pull-down off) = 0 ;Send Keypress if available btss CommState,#SENDINPROGRESS goto GetKey btsc TxKeyDataTemp,#0 ;Send LSB bit bset PORTB,#DATA_LOW ;Turns on pull-down transistor on Data Line =1 rrnc TxKeyDataTemp ;Rotate next bit into LSB position GetKey: ;CLK low, receive a single bit mov #100,w0 ;215us Delay to sample in middle of bit InnerLoopKeyRx: dec W0,W0 ;4 cycles this loop btss SR,#Z goto InnerLoopKeyRx rrnc RxKeyTemp bclr RxKeyTemp,#15 btss PORTB,#DATA bset RxKeyTemp,#15 ;Set MSB (inverted) goto EndIsr LinkError: bset CommState,#ERROR bset PORTD,#CMD_ERR EndIsr: BCLR IFS0, #CNIF ;Clear the Interrupt flag pop w1 pop w0 RETFIE ;Return from Interrupt Service routine__T2Interrupt: ;########################################### ;########### Routines ########## ;########################################### ; Delay (time in ms, in w1), assuming 1.8 MIPs Delay: push w4 OuterLoop: mov #469,w4 ;1ms InnerLoop: dec W4,W4 ;4 cycles this loop btss SR,#Z goto InnerLoop dec w1,w1 btss SR,#Z goto OuterLoop pop w4 return ;------------------------------------------------------------------ ;EEPROM Functions ; ReadEEPROM - Reads a word (address=w1) to w0 from the EEPROM ; WriteEEPROM - Writes a word (address=w1, data=w0) to the EEPROM ; Addresses in w1 are 0..512 (words) ReadEEPROM: push w2 mov #0x007F,w0 ;EEPROM is located at 0x007FFC00 mov w0,TBLPAG mov #0xFC00,w2 ;Base address rlnc w1,w1 ;Multiply w1 times two to get word Address add w2,w1,w2 tblrdl [w2],w0 ;Table read since EEPROM mapped to prog memory pop w2 return WriteEEPROM: push w2 push w0 mov #0x007F,w0 ;EEPROM is located at 0x007FFC00 mov w0,TBLPAG mov w0,NVMADRU mov #0xFC00,w2 ;Base address rlnc w1,w1 ;Multiply w1 times two to get word Address add w2,w1,w2 mov w2,NVMADR ;Low address mov #0x4044,w0 ;Erase Enable mov w0,NVMCON disi #5 mov #0x55,w0 ;Key to unlock erase cycle mov w0,NVMKEY mov #0xAA,w0 mov w0,NVMKEY bset NVMCON,#WR ;Erase sequence nop nop WaitForEraseDone: btsc NVMCON,#WR goto WaitForEraseDone mov #0x007F,w0 ;EEPROM is located at 0x007FFC00 mov w0,TBLPAG mov w0,NVMADRU mov w2,NVMADR ;Low address pop w0 ;Restore data to write tblwtl w0,[w2] ;Table write mov #0x4004,w0 ;Write Enable mov w0,NVMCON disi #5 mov #0x55,w0 ;Key to unlock write cycle mov w0,NVMKEY mov #0xAA,w0 mov w0,NVMKEY bset NVMCON,#WR ;Write sequence nop nop WaitForWriteDone: btsc NVMCON,#WR goto WaitForWriteDone pop w2 return