TITLE "remote.asm: remote control, @ internal clock 4.0MHz" LIST p=12C509A ERRORLEVEL 0, -302 ; Remote switching with 418/433MHz RF transmitters/receivers ; FM coding for tolerant clock regeneration (bits start and end with ; a transition but zeros have an additional transition in between) ; LSB are transmitted first. Bit time is nominal 400us (2.5kBit). ; Clock is lost when an expected transition is more than 50% late. ; ; Frames have 3 bytes with a byte sum of 0: ADDRESS-VALUE-CHECKSUM ; ********** ********** ; * *** * ; +5V VCC ** 1 8 ** GND ground ; * * ; A1 (Q3) Xin/GP5 ** 2 7 ** GP0 MODE: tx (rx) ; * * ; A0 (Q2) Xout/GP4 ** 3 6 ** GP1 KEY (Q0) ; * * ; IN (rxd) mclr/GP3 ** 4 5 ** GP2/toc OUT: txd (Q1) ; * PIC12C509A * ; *********************** ; MODE select: VCC (not connected) = transmitter mode, GND = receiver mode. ; This pin has an internal pullup resistor and is sampled at power on. ; Transmitter Mode ; ---------------- ; OUT delivers the fm coded tx-data (used to supply the AM transmitter). ; When IN changed from low to high an on command (a series of frames with ; value 0xff) when changed from high to low an off command is transmitted. ; A momentary switch may be connected from KEY to ground. When the switch ; is pressed on commands or off commands are transmitted alternately. ; A0 and A1 set the two least significant bits in a frames address byte. ; When idle the device is set to SLEEP mode: tye A1 and A0 to GND or MODE, ; leave unused inputs IN or KEY unconnected for lowest power consumption, ; note that at wake up (a reset) the OUT pin is an input for 300us. ; Receiver Mode ; ------------- ; The receive data is sampled at IN (data from AM receiver). ; The last significant bits in the address field select the output to be ; switched: 0=GP1, 1=GP2, 2=GP4, 3=GP5. Values >= 0x80 turn an output on ; while smaller values turn it off. ; Remarks: ; ORGs are set to odd addresses for future patches to the OTPROM. ; A 12C508 would have done the job as well. __IDLOCS H'ACDC' ; ABBA/BABE/BEAF/FACE/FADE/BAD/DAD/BED.. __CONFIG H'0A' ; no MCLR, no CP, no WDT, internal osc TMR0 EQU 1 PCL EQU 2 STATUS EQU 3 FSR EQU 4 OSCAL EQU 5 GPIO EQU 6 C EQU 0 Z EQU 2 PA0 EQU 5 DEVADR EQU H'A0' ; device address base MODE EQU 0 ; GPIO pins KEY EQU 1 OUT EQU 2 IN EQU 3 A0 EQU 4 A1 EQU 5 ORG H'07' ; RAM tmp0 RES 1 ; temporary storage tmp1 RES 1 tmp2 RES 1 tmp3 RES 1 bits RES 1 ; bits received/transmitted mask RES 1 ; mask to toggle tx pin filt RES 1 ; switch time (input filtering) nexp RES 1 ; expecting 2nd edge of a 0 bit sync RES 1 ; synchronized with rx data inst RES 1 ; last input state addrs RES 1 ; address byte in frame value RES 1 ; data byte (value) in frame cksum RES 1 ; checksum byte in frame xxbuf RES 3 ; receive/transmit buffer ORG H'000' ; reset GOTO main ; ORG H'007' ; main BTFSC STATUS,7 ; wakeup on pin change? GOTO txmode ; yes: skip init code ANDLW H'FC' ; trim oscillator MOVWF OSCAL ; MOVLW B'00000000' ; all GPIO low MOVWF GPIO ; MOVLW B'11111111' ; all inputs TRIS GPIO ; MOVLW B'10011111' ; no wakeup, pullup, timer intern, presc wdt OPTION ; MOVLW H'07' ; clear first ram page MOVWF FSR ; initvar CLRF 0 ; INCF FSR,F ; MOVLW H'1F' ; ANDWF FSR,W ; BTFSS STATUS,Z ; until FSR=0x20 (ignore bits 7..5) GOTO initvar ; CLRF TMR0 ; reset rtcc BTFSS GPIO,MODE ; GOTO rxmode ; always listening (no sleep mode) GOTO txmode ; on command if IN=1, sleep ORG H'047' ; outpin RLF xxbuf,W ; address * 2 ANDLW H'06' ; BTFSS xxbuf+1,7 ; if value < 0x80 IORLW H'08' ; + 8 ADDWF PCL,F ; BSF GPIO,1 ; RETLW 0 ; BSF GPIO,2 ; RETLW 0 ; BSF GPIO,4 ; RETLW 0 ; BSF GPIO,5 ; RETLW 0 ; BCF GPIO,1 ; RETLW 0 ; BCF GPIO,2 ; RETLW 0 ; BCF GPIO,4 ; RETLW 0 ; BCF GPIO,5 ; RETLW 0 ; ORG H'087' ; txcmd MOVLW DEVADR ; initialize address byte MOVWF addrs ; BTFSC GPIO,A1 ; INCF addrs,F ; BTFSC GPIO,A1 ; INCF addrs,F ; BTFSC GPIO,A0 ; INCF addrs,F ; CLRF cksum ; calculate checksum byte MOVF addrs,W ; SUBWF cksum,F ; MOVF value,W ; SUBWF cksum,F ; MOVF addrs,W ; copy to buffer MOVWF xxbuf ; MOVF value,W ; MOVWF xxbuf+1 ; MOVF cksum,W ; MOVWF xxbuf+2 ; MOVLW D'24' ; bit counter MOVWF bits ; CLRF mask ; initialize toggle mask BSF mask,OUT ; txbit MOVF mask,W ; [199] toggle OUT XORWF GPIO,F ; [200] MOVLW D'64' ; [1] MOVWF tmp0 ; [2] dly1 DECFSZ tmp0,F ; (64 * 3) - 1 GOTO dly1 ; RRF xxbuf+2,F ; [194] next bit to carry RRF xxbuf+1,F ; [195] RRF xxbuf,F ; [196] NOP ; [197] MOVF mask,W ; [198] toggle OUT if bit 0 BTFSS STATUS,C ; [199] XORWF GPIO,F ; [200] MOVLW D'64' ; [1] MOVWF tmp1 ; [2] dly2 DECFSZ tmp1,F ; (64 * 3) - 1 GOTO dly2 ; NOP ; [194] NOP ; [195] DECFSZ bits,F ; [196] all bits done? GOTO txbit ; [198] MOVF mask,W ; toggle OUT XORWF GPIO,F ; MOVLW D'66' ; [1] MOVWF tmp0 ; [2] dly3 DECFSZ tmp0,F ; (66 * 3) - 1 GOTO dly3 ; BCF GPIO,OUT ; [200] transmitter off CLRF tmp1 ; 256 * 3 = 768 (clock error) dly4 DECFSZ tmp1,F ; GOTO dly4 ; RETLW 0 ; ORG H'107' ; txmode MOVLW B'11111011' ; 5..3 inputs, 2 output, 1..0 inputs TRIS GPIO ; MOVLW B'00011111' ; wakeup, pullup, timer intern, presc wdt OPTION ; CLRF tmp3 ; timeout: 256 polls CLRF tmp2 ; IN debounce CLRF tmp1 ; KEY debounce poll BTFSC GPIO,KEY ; key pressed? CLRF tmp1 ; no INCF tmp1,F ; BTFSC tmp1,3 ; accept if count 8 (ca 1.5ms) GOTO keypr ; MOVF GPIO,W ; IN pin changed? XORWF inst,W ; ANDLW B'00001000' ; BTFSC STATUS,Z ; skip if != 0 (changed) CLRF tmp2 ; no change, no action INCF tmp2,F ; BTFSC tmp2,3 ; accept if count 8 GOTO inchg ; MOVLW D'63' ; slow down polling loop MOVWF tmp0 ; wai0 DECFSZ tmp0,F ; GOTO wai0 ; DECFSZ tmp3,F ; GOTO poll ; GOTO txend ; keypr COMF value,F ; toggle last command GOTO txmit ; inchg CLRF value ; 00 or FF to value BTFSC GPIO,IN ; COMF value,F ; CALL txcmd ; two extra frames at IN change CALL txcmd ; txmit CALL txcmd ; CALL txcmd ; CALL txcmd ; txend MOVF GPIO,W ; read port before sleeping MOVWF inst ; SLEEP ; ORG H'147' ; rxmode MOVLW B'11001001' ; 5+4+2+1 outputs, 3+0 inputs TRIS GPIO ; CLRWDT ; switch prescaler to tmr0 savely MOVLW B'11010001' ; no wakeup, no pullup, timer intern, presc 4 OPTION ; rxloop BTFSS GPIO,IN ; two way input filtering GOTO siglo ; sighi BTFSC filt,7 ; clear filt if signal was low CLRF filt ; MOVF filt,W ; inc filt: saturation at 127 XORLW D'127' ; BTFSS STATUS,Z ; INCF filt,F ; MOVLW 4 ; accept positive edge at this count XORWF filt,W ; BTFSS STATUS,Z ; GOTO sigend ; GOTO edge ; siglo MOVLW D'128' ; filt = 128 if signal was high BTFSS filt,7 ; MOVWF filt ; INCFSZ filt,W ; inc filt: saturation at 255 INCF filt,F ; MOVLW D'132' ; accept negative edge at this count XORWF filt,W ; BTFSS STATUS,Z ; GOTO sigend ; edge BTFSC sync,7 ; synchronized? GOTO ckedge ; yes BSF sync,7 ; no: set sync and start receiving CLRF bits ; CLRF nexp ; CLRF TMR0 ; GOTO rxloop ; ckedge BTFSC nexp,7 ; expecting a null bit? GOTO store0 ; MOVLW D'75' ; 300us max period for 0 bit SUBWF TMR0,W ; BTFSC STATUS,C ; skip if time shorter GOTO store1 ; MOVLW D'75' ; 300us for second half of 0 bit MOVWF TMR0 ; BSF nexp,7 ; GOTO rxloop ; sigend MOVLW D'150' ; 600us max bit time SUBWF TMR0,W ; BTFSC STATUS,C ; CLRF sync ; synch lost GOTO rxloop ; ORG H'187' ; store0 BCF STATUS,C ; BTFSC STATUS,C ; store1 BSF STATUS,C ; RRF xxbuf+2,F ; store carry RRF xxbuf+1,F ; RRF xxbuf,F ; CLRF TMR0 ; CLRF nexp ; INCFSZ bits,W ; saturation at 255 INCF bits,F ; XORLW D'24' ; BTFSS STATUS,Z ; GOTO rxloop ; rcheck MOVF xxbuf+2,W ; checksum ADDWF xxbuf+1,W ; ADDWF xxbuf,W ; BTFSS STATUS,Z ; GOTO rxloop ; MOVLW DEVADR ; subtract base from address SUBWF xxbuf,F ; MOVLW H'FC' ; must be 0..3 ANDWF xxbuf,W ; BTFSC STATUS,Z ; CALL outpin ; ok: do something GOTO rxloop ; END ;