TITLE "dimm.asm: rc dimmer, @ internal clock 4.0MHz" LIST p=12C509A ERRORLEVEL 0, -302 ; Remotely controlled dimmer with 418/433MHz RF transmitters/receivers ; Derived from remote.asm. ; 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 ; Values range from 0 (off) to 0xFF (fully on). ; A value of 1 toggles the lamp off/on. ; A value of 2 increments brightness. ; A value of 3 decrements brightness. ; Timing is optimized for 50Hz power lines. ; The power consumption of the PIC 12C509 is very low (say 2mA) since ; OUT only delivers a short pulse to fire a triac. The RF-receiver ; needs about 3mA; low enough for a cheep transformerless power supply. ; C4u7 R100 1N4148 ------- ; P 220 VAC -----||---/\/\/\---+--->|---+----| 78L05 |----+----- VCC ; 630V .25W | | ------- | ; ZD12V C470u | C10u+10n ; | | | | ; N ---------------------------+--------+--------+--------+----- GND ; ********** ********** ; * *** * ; +5V VCC ** 1 8 ** GND ground ; * * ; CLK (inc) Xin/GP5 ** 2 7 ** GP0 A0 ; * * ; MODE Xout/GP4 ** 3 6 ** GP1 A1 ; * * ; RXD (key) mclr/GP3 ** 4 5 ** GP2/toc OUT (txd) ; * PIC12C509A * ; *********************** ; MODE select: GND = receiver mode, VCC = transmitter mode. ; Receiver Mode ; ------------- ; The receive data is sampled at RXD (data from AM receiver). ; OUT is used to drive a triac or a random switching solid-state relay. ; A0 and A1 set the two least significant bits in a frames address byte. ; The CLK pin is used for zero crossing detection: connect it over a ; 10MOhm resistor to the AC line. ; Transmitter Mode ; ---------------- ; A simple two button transmitter: one button to apply power and the ; other from (key) input to ground. When the power button is pressed ; with key open the lamp is toggled on/off. Holding key low at startup ; transmits a series of increments followed by an on and finally by ; an off command, then sleep mode is entered to save power. ; (txd) delivers the fm coded tx-data (to supply the AM transmitter). ; A0 and A1 set the two last bits in a frames address byte. ; CLK is not used and should be connected to GND or VCC. ; Remarks: ; ORGs are set to odd addresses for future patches to the OTPROM. ; A 12C508 would have done the job as well. __IDLOCS H'ACAC' ; 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'B0' ; device address base A0 EQU 0 ; GPIO pins A1 EQU 1 MODE EQU 4 OUT EQU 2 ; (receiver) RXD EQU 3 CLK EQU 5 TXD EQU 2 ; (transmitter) KEY EQU 3 INC 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 filt RES 1 ; switch time (input filtering) nexp RES 1 ; expecting 2nd edge of a 0 bit sync RES 1 ; synchronized with rx data phase RES 1 ; clk input polarity addrs RES 1 ; address byte in frame value RES 1 ; data byte (value) in frame cksum RES 1 ; checksum byte in frame timer RES 1 ; time since last zero crossing xtime RES 1 ; time to fire triac ytime RES 1 ; xtime storage when xtime off tlock RES 1 ; lock toggles in series (ca. 80ms) ORG H'000' ; reset ANDLW H'FC' ; trim oscillator MOVWF OSCAL ; MOVLW B'00000000' ; all GPIO low MOVWF GPIO ; MOVLW B'11111011' ; GP2 output, others 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 ; transmit commands, then sleep ORG H'047' ; txcmd MOVWF value ; MOVLW DEVADR ; initialize address byte MOVWF addrs ; MOVF GPIO,W ; ANDLW 3 ; ADDWF addrs,F ; CLRF cksum ; calculate checksum byte MOVF addrs,W ; SUBWF cksum,F ; MOVF value,W ; SUBWF cksum,F ; MOVLW D'24' ; bit counter MOVWF bits ; CLRF tmp2 ; initialize toggle mask for TXD BSF tmp2,TXD ; txbit MOVF tmp2,W ; [199] toggle TXD XORWF GPIO,F ; [200] MOVLW D'64' ; [1] MOVWF tmp0 ; [2] dly1 DECFSZ tmp0,F ; (64 * 3) - 1 GOTO dly1 ; RRF cksum,F ; [194] next bit to carry RRF value,F ; [195] RRF addrs,F ; [196] NOP ; [197] MOVF tmp2,W ; [198] toggle TXD 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 tmp2,W ; toggle TXD XORWF GPIO,F ; MOVLW D'66' ; [1] MOVWF tmp0 ; [2] dly3 DECFSZ tmp0,F ; (66 * 3) - 1 GOTO dly3 ; BCF GPIO,TXD ; [200] transmitter off CLRF tmp1 ; 256 * 3 = 768 (clock error) dly4 DECFSZ tmp1,F ; GOTO dly4 ; RETLW 0 ; ORG H'087' ; edge DECFSZ timer,W ; dec timer: saturation at 1 DECF timer,F ; BCF GPIO,OUT ; reset output pulse MOVF xtime,W ; pulse on if xtime == timer XORWF timer,W ; (xtime = 0 never matches) BTFSC STATUS,Z ; BSF GPIO,OUT ; xedge BTFSC sync,7 ; synchronized? GOTO ckedge ; yes BSF sync,7 ; no: set sync and start receiving CLRF bits ; CLRF nexp ; CLRF TMR0 ; GOTO back ; 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 back ; ORG H'0A7' ; store0 BCF STATUS,C ; GOTO storex ; store1 BSF STATUS,C ; storex RRF cksum,F ; store carry RRF value,F ; RRF addrs,F ; CLRF TMR0 ; CLRF nexp ; INCFSZ bits,W ; saturation at 255 INCF bits,F ; XORLW D'24' ; BTFSS STATUS,Z ; GOTO back ; rcheck MOVF cksum,W ; sum check ADDWF value,W ; ADDWF addrs,W ; BTFSS STATUS,Z ; GOTO back ; MOVF GPIO,W ; address check ANDLW 3 ; SUBWF addrs,W ; XORLW DEVADR ; BTFSS STATUS,Z ; GOTO back ; action MOVF value,W ; MOVWF tmp0 ; DECF tmp0,F ; value = 1? BTFSC STATUS,Z ; GOTO togval ; CLRF ytime ; discard toggle value DECF tmp0,F ; value = 2? BTFSC STATUS,Z ; GOTO incval ; DECF tmp0,F ; value = 3? BTFSC STATUS,Z ; GOTO decval ; MOVWF xtime ; store new value 0 or 4..255 GOTO back ; togval BTFSC tlock,0 ; toggle commands locked? GOTO back ; DECF tlock,F ; no: accept and lock toggles for 80ms MOVF xtime,W ; currently on or off? BTFSC STATUS,Z ; GOTO togon ; MOVWF ytime ; save current xtime to ytime CLRF xtime ; GOTO back ; togon MOVF ytime,W ; if ytime = 0: xtime fully on MOVWF xtime ; BTFSC STATUS,Z ; DECF xtime,F ; GOTO back ; incval MOVLW D'47' ; if 0: skip 1..47 MOVF xtime,F ; to avoid flickering BTFSC STATUS,Z ; MOVWF xtime ; INCFSZ xtime,W ; saturation at 255 INCF xtime,F ; GOTO back ; decval MOVF xtime,F ; down to 0 BTFSS STATUS,Z ; DECF xtime,F ; MOVLW D'47' ; turn off values below 47 SUBWF xtime,W ; to avoid flickering BTFSS STATUS,C ; CLRF xtime ; back BTFSS phase,7 ; GOTO _rxloop ; GOTO rxloop ; ; rxloop needs only 39 cycles (10ms/256) ; zero crossing detection is done 8 times per loop ; rxloop is for the positive power wave, _rxloop for the negative ; receiving data may cause a small flickering on active channels ORG H'107' ; pcross BCF STATUS,C ; update toggle locking RRF tlock,F ; (shift out 1 bits) CLRF timer ; DECF phase,F ; INCF xtime,W ; double start pulse if fully on GOTO rxbeg ; rxloop BTFSS GPIO,CLK ; [1] zero crossing check #1 GOTO ncross ; DECFSZ timer,W ; [3] dec timer: saturation at 1 DECF timer,F ; [4] BTFSS GPIO,CLK ; [5] zero crossing check #2 GOTO ncross ; BCF GPIO,OUT ; [7] reset output pulse MOVF xtime,W ; [8] pulse on if xtime == timer XORWF timer,W ; [9] (xtime = 0 never matches) BTFSS GPIO,CLK ; [10] zero crossing check #3 GOTO ncross ; rxbeg BTFSC STATUS,Z ; [12] BSF GPIO,OUT ; [13] MOVLW D'150' ; [14] synch lost if bit time > 600us BTFSS GPIO,CLK ; [15] zero crossing check #4 GOTO ncross ; SUBWF TMR0,W ; [17] BTFSC STATUS,C ; [18] CLRF sync ; [19] BTFSS GPIO,CLK ; [20] zero crossing check #5 GOTO ncross ; NOP ; [22] BTFSS GPIO,RXD ; [23] two way input filtering GOTO sighi ; [25] BTFSS GPIO,CLK ; [25] zero crossing check #6 GOTO ncross ; MOVLW D'255' ; [27] filt = 255 if signal was high BTFSS filt,7 ; [28] MOVWF filt ; [29] BTFSS GPIO,CLK ; [30] zero crossing check #7 GOTO ncross ; BTFSS filt,6 ; [32] max. 64 low samples GOTO rxlp35 ; [34] DECF filt,F ; [34] BTFSS GPIO,CLK ; [35] zero crossing check #8 GOTO ncross ; BTFSC filt,1 ; [37] accept neg edge at this count GOTO rxloop ; [39] BCF filt,6 ; [39] edge done: no more samples GOTO edge ; [2] sighi BTFSS GPIO,CLK ; [26] zero crossing check #6 GOTO ncross ; BTFSC filt,7 ; [28] filt = 0 if signal was low CLRF filt ; [29] BTFSS GPIO,CLK ; [30] zero crossing check #7 GOTO ncross ; BTFSC filt,6 ; [32] max. 64 high samples GOTO rxlp35 ; [34] INCF filt,F ; [34] BTFSS GPIO,CLK ; [35] zero crossing check #8 GOTO ncross ; BTFSS filt,1 ; [37] accept pos edge at this count GOTO rxloop ; [39] BSF filt,6 ; [39] edge done: no more samples GOTO edge ; [2] rxlp35 BTFSS GPIO,CLK ; [35] zero crossing? GOTO ncross ; NOP ; [37] GOTO rxloop ; [39] ORG H'157' ; ncross BCF STATUS,C ; update toggle locking RRF tlock,F ; (shift out 1 bits) CLRF timer ; CLRF phase ; INCF xtime,W ; double start pulse if fully on GOTO _rxbeg ; _rxloop BTFSC GPIO,CLK ; [1] zero crossing check #1 GOTO pcross ; DECFSZ timer,W ; [3] dec timer: saturation at 1 DECF timer,F ; [4] BTFSC GPIO,CLK ; [5] zero crossing check #2 GOTO pcross ; BCF GPIO,OUT ; [7] reset output pulse MOVF xtime,W ; [8] pulse on if xtime == timer XORWF timer,W ; [9] (xtime = 0 never matches) BTFSC GPIO,CLK ; [10] zero crossing check #3 GOTO pcross ; _rxbeg BTFSC STATUS,Z ; [12] BSF GPIO,OUT ; [13] MOVLW D'150' ; [14] synch lost if bit time > 600us BTFSC GPIO,CLK ; [15] zero crossing check #4 GOTO pcross ; SUBWF TMR0,W ; [17] BTFSC STATUS,C ; [18] CLRF sync ; [19] BTFSC GPIO,CLK ; [20] zero crossing check #5 GOTO pcross ; NOP ; [22] BTFSS GPIO,RXD ; [23] two way input filtering GOTO _sighi ; [25] BTFSC GPIO,CLK ; [25] zero crossing check #6 GOTO pcross ; MOVLW D'255' ; [27] filt = 255 if signal was high BTFSS filt,7 ; [28] MOVWF filt ; [29] BTFSC GPIO,CLK ; [30] zero crossing check #7 GOTO pcross ; BTFSS filt,6 ; [32] max. 64 low samples GOTO _rxlp35 ; [34] DECF filt,F ; [34] BTFSC GPIO,CLK ; [35] zero crossing check #8 GOTO pcross ; BTFSC filt,1 ; [37] accept neg edge at this count GOTO _rxloop ; [39] BCF filt,6 ; [39] edge done: no more samples GOTO edge ; [2] _sighi BTFSC GPIO,CLK ; [26] zero crossing check #6 GOTO pcross ; BTFSC filt,7 ; [28] filt = 0 if signal was low CLRF filt ; [29] BTFSC GPIO,CLK ; [30] zero crossing check #7 GOTO pcross ; BTFSC filt,6 ; [32] max. 64 high samples GOTO _rxlp35 ; [34] INCF filt,F ; [34] BTFSC GPIO,CLK ; [35] zero crossing check #8 GOTO pcross ; BTFSS filt,1 ; [37] accept pos edge at this count GOTO _rxloop ; [39] BSF filt,6 ; [39] edge done: no more samples GOTO edge ; [2] _rxlp35 BTFSC GPIO,CLK ; [35] zero crossing? GOTO pcross ; NOP ; [37] GOTO _rxloop ; [39] ORG H'1A7' ; rxmode CLRWDT ; switch prescaler to tmr0 savely MOVLW B'10010001' ; no wakeup, pullup, timer intern, presc 4 OPTION ; MOVLW B'00000000' ; all GPIO low MOVWF GPIO ; CLRF sync ; init some ram: an init loop worked in CLRF filt ; mpsim but not on the real chip ... CLRF xtime ; CLRF ytime ; CLRF phase ; CLRF TMR0 ; reset rtcc BTFSS GPIO,A0 ; optional: for even address DECF xtime,F ; start with lamp fully on BTFSS GPIO,CLK ; phase? GOTO ncross ; rxloop for neg. CLK pin GOTO pcross ; rxloop for pos. CLK pin ORG H'1C7' ; txmode CLRF GPIO ; all outputs low MOVLW B'11001011' ; 5,4,2 outputs, 3,1,0 inputs TRIS GPIO ; MOVLW B'10011111' ; no wakeup, pullup, timer intern, presc wdt OPTION ; BTFSS GPIO,KEY ; GOTO fadeup ; MOVLW 1 ; toggle (repeat won't toggle again) CALL txcmd ; MOVLW 1 ; CALL txcmd ; MOVLW 1 ; CALL txcmd ; SLEEP ; fadeup MOVLW D'210' ; 210 steps (ca 4.2s) MOVWF tmp3 ; floop MOVLW 2 ; command: inc BTFSS GPIO,INC ; MOVLW 3 ; or command: dec CALL txcmd ; ca. 10ms MOVLW D'20' ; delay 10ms MOVWF tmp1 ; wai4 MOVLW D'166' ; 500us MOVWF tmp0 ; wai3 DECFSZ tmp0,F ; GOTO wai3 ; DECFSZ tmp1,F ; GOTO wai4 ; DECFSZ tmp3,F ; GOTO floop ; MOVLW D'255' ; on command (repeat to be sure) CALL txcmd ; MOVLW D'255' ; CALL txcmd ; MOVLW D'255' ; CALL txcmd ; MOVLW D'100' ; 1000ms MOVWF tmp2 ; wai2 MOVLW D'20' ; 10ms MOVWF tmp1 ; wai1 MOVLW D'166' ; 500us MOVWF tmp0 ; wai0 DECFSZ tmp0,F ; GOTO wai0 ; DECFSZ tmp1,F ; GOTO wai1 ; DECFSZ tmp2,F ; GOTO wai2 ; MOVLW 0 ; repeat off command CALL txcmd ; MOVLW 0 ; CALL txcmd ; MOVLW 0 ; CALL txcmd ; SLEEP ; END ;