; This program uses the PIC16F84 to: ; - receive one specific infrared RC5 signal and store it ; - receive RC5 infrared signals and compare them to the stored one ; - output a signal to the computer's serial port's RING pin if they match ; - receive from the computer a time delay (expressed in time units of 2.097152 seconds) ; - count down from this delay to 0, and output a signal to the computer's serial port's ; RING pin ; ; Used pins on the PIC: ; RA0: "data ACK" signal (to computer) ; RA1: RING (to computer) ; RA2: Signal that RC5 message has been received (to a LED) ; RA3: Signal that delay countdown is in process (to a LED) ; RB1: "data ready" signal (from computer) ; RB2: "data" ligne (from computer) ; RB3: read/write flag (low: read, high: write) for RC5 ; RB4: infrared signal ; ; Setting RB3 to high will store the received RC5 signal to EEPROM ; Setting RB3 to low will compare the received RC5 signal with the version in EEPROM ; and send a RING to the computer if they match ; ; Reading data from the computer (for setting the delay) is done as follow: ; - wait for interrupt (or poll in MainLoop?) (RB1) ; - read the data (RB2) ; - send ACK (RA0) ; - wait for data ready (RB1) to go down ; - set ACK down (RA0) list p=pic16f84a include "p16f84a.inc" __FUSES _XT_OSC & _PWRTE_ON & _WDT_OFF & _CP_OFF RAMbase equ d'12' cblock RAMbase flags W_Temp S_Temp Prev_TMR0 RC5_Timer_Ovfl Delay RC5_Bit_Count RTC_H RTC_L RTC_64 RC5_3 RC5_2 RC5_1 RC5_0 Ring_Counter_H Ring_Counter_L RTC_Temp_H RTC_Temp_L RTC_Bit_Counter endc ; ***** Definitions ***** #define TH1 d'10' ; threshold below which we count 1 bit in RC5 ; TH1 = 10 * 128 us #define TH2 d'07' ; threshold below which we count 2 bits in RC5 ; TH2 = 17 * 128 us ; TH1 is a value between the length of 1 bit (889 us) ; and the length of 2 bits (1778 us) in mancheser coding ; TH2 is well above the length of 2 bits. #define Long_Delay 0x02 ; value after which we consider the delay to be long #define Prev_RC5_Val flags,0 ; Last RC5 bit received #define Reading_RC5 flags,1 ; RC5 data reading in process #define RC5_Enable flags,2 ; unused #define RC5_Data_Ready flags,3 ; RC5 data ready (e.g. for signaling the computer) #define Reading_Data flags,4 ; In the process of reading RTC data #define RTC_Enable flags,5 ; Set when the RTC is running #define RTC_Ring_Time flags,6 ; If set, tells the Main_Loop to it's time to Ring #define SEL_BANK_0 bcf STATUS,RP0 ; Select Bank 0 #define SEL_BANK_1 bsf STATUS,RP0 ; Select Bank 1 #define Data_ACK PORTA,0 ; Data acknowledged #define Ring_Output PORTA,1 ; Ring signal output (to the computer's serial port RING pin) #define RC5_LED PORTA,2 ; LED flashing when RC5 operation is completed #define Timer_LED PORTA,3 ; LED blinking when timer goes #define Data_Ready PORTB,1 ; Data from computer is ready for reading #define Data PORTB,2 ; Data line (1 bit) #define RC5_RW_Input PORTB,3 ; RC5 signal: memorize (set) or compare (clear) to existing? #define IR_Input PORTB,4 ; Infrared module input #define CARRY STATUS,C #define ZERO STATUS,Z #define EEPROM_RC5_0 0x00 ; EEPROM registers for storing RC5 code #define EEPROM_RC5_1 0x01 #define EEPROM_RC5_2 0x02 #define EEPROM_RC5_3 0x03 ; ***** RESET ***** org 0000h Reset_Vector goto Main_Start nop nop nop ; ; ***** Interrupt Vector ***** ; Interrupt_Vector if (Interrupt_Vector != 0x004 ) ERROR "Warning Interrupts must start at address 0x004" endif Push movwf W_Temp ; Save W swapf STATUS,W movwf S_Temp ; Save STATUS SEL_BANK_0 Disable_Int bcf INTCON,GIE ; Disable all Interrupts btfsc INTCON,GIE ; Are Interrupts disabled ? goto Disable_Int ; NO, try again btfsc INTCON,T0IF ; Skip TMR0 overflow interrupt? goto TMR0_Int ; NO, goto TMR0 btfsc INTCON,RBIF ; Skip PORTB4's Interrupt? goto RB4_Int ; NO, goto RB0 Pop swapf S_Temp,W movwf STATUS ; Return STATUS swapf W_Temp,F swapf W_Temp,W ; Return W retfie ; Return setting GIE ; ;***** Manage RB4 Int ***** ; RB4_Int bcf INTCON,T0IE ; Disable timer overflow when we handle this one bcf Prev_RC5_Val ; Set the previous RC5 value to 0 btfss IR_Input ; Current Input level is 1? bsf Prev_RC5_Val ; NO: Previous RC5 value was 1 bcf INTCON,RBIF ; Clear interrupt flag ; Now we have the value. ; Let's see how many bits we got (1 or 2) movf TMR0,W movwf Delay ; Delay <- TMR0 movf Prev_TMR0,W subwf Delay,F ; Delay <- TMR0 - Prev_TMR0 ; Delay = delay since last interruption btfss CARRY ; Is the result >= 0? (CARRY == 1) goto RC5_Decrement_Timer_Ovfl ; NO: TMR0 overflew at least once, decrement RC5_Timer_Ovfl goto RC5_Is_Timer_Ovfl_Null ; YES: Check if RC5_Timer_Ovfl == 0 RC5_Decrement_Timer_Ovfl movf RC5_Timer_Ovfl,F btfss ZERO ; Is it Null? (Z == 1) decf RC5_Timer_Ovfl,F ; NO: then we can decrement RC5_Timer_Ovfl safely ; YES: We probably missed a TMR0 overflow due to disabled ; timer interrupts while processing RC5 signals. ; As it doesn't make sense to decrement the overflow counter ; if it is null, we don't do it. RC5_Is_Timer_Ovfl_Null movf RC5_Timer_Ovfl,F ; Check if RC5_Timer_Ovfl == 0 after the adjustments btfss ZERO ; Is it? (Z == 1) goto RC5_Long_Delay ; NO: there was a long delay, more than 1 overflow) movlw TH1 ; YES: let's check now how many bits we got subwf Delay,F ; Let's check if delay > TH1 (delay - TH1 > 0) ; Delay = delay - TH1 btfss CARRY ; Is it? (CARRY == 1) goto RC5_1_bit ; NO: We got 1 bit movlw TH2 ; YES: Let's then check if we got 2 bits subwf Delay,F ; Check if delay > TH2 (delay - TH2 > 0) ; Delay = delay - TH1 - TH2 btfss CARRY ; Is it? (CARRY == 1) goto RC5_2_bits ; NO: We got 2 bits goto RC5_Long_Delay ; YES: Long delay RC5_1_bit call Add_1_bit_to_RC5 goto RC5_Check_Bit_Count RC5_2_bits call Add_2_bits_to_RC5 goto RC5_Check_Bit_Count RC5_Long_Delay ; A long delay occured btfss Reading_RC5 ; RC5 data reading bit set ? goto RC5_First_bit ; NO: beginning of RC5 frame ; YES: end of RC5 frame call Add_1_bit_to_RC5 ; Add last bit call RC5_LED_Off ;call Do_Something_With_The_Data_Before_It_Gets_Erased ? RC5_First_bit call Clear_RC5 ; Clear all RC5-related registers (incl. flags) bsf Reading_RC5 ; start reading now call RC5_LED_On RC5_Check_Bit_Count movlw d'28' subwf RC5_Bit_Count,W ; How many bits have we got? 28? btfss ZERO ; goto End_RB4_Int ; NO: nothing to do bcf Reading_RC5 ; YES: stop reading now bcf INTCON,RBIE ; disable RB4 (RC5-related) interrupts call RC5_LED_Off bsf RC5_Data_Ready End_RB4_Int clrf RC5_Timer_Ovfl ; Reset TMR0 overflow counter for RC5 timing movf TMR0,W ; Save the current timer value movwf Prev_TMR0 bcf INTCON,T0IF ; clear potential timer overflow interrupt occuring during this handler bsf INTCON,T0IE ; Re-enable timer overflow interrupt goto Pop ; ;***** Manage TMR0 int ***** ; TMR0_Int bcf INTCON,T0IF ; Clear TMR0 interrupt flag ; Counting TMR0 overflows for RC5 timing ; not very accurate, since if we are in the RC5 interrupt handler, ; we ignore this interrupt :( btfss Reading_RC5 ; Are we reading an RC5 frame? goto TMR0_Inc_RC5_Timer_Ovfl ; NO: don't detect an "end of frame" movf RC5_Timer_Ovfl,W ; YES: detect an "end of frame" sublw Long_Delay ; Check if RC5_TMR0_Ovfl > Long_Delay (Long_Delay - RC5_Timer_Ovfl < 0) btfsc CARRY ; Is the result < 0 (CARRY == 0)? goto TMR0_Inc_RC5_Timer_Ovfl ; NO: then increment goto TMR0_RC5_End_of_Frame ; YES: See if it is the end of an RC5 frame TMR0_Inc_RC5_Timer_Ovfl incf RC5_Timer_Ovfl,F ; Increment the TMR0 overflow counter goto TMR0_Next ; and continue TMR0_RC5_End_of_Frame call Add_0_to_RC5 ; Add 0 as last bit (only possible choice) bcf Reading_RC5 ; stop reading now bcf INTCON,RBIE ; disable RB4 (RC5-related) interrupts call RC5_LED_Off ; Set the LED off bsf RC5_Data_Ready ; Signal the Main_Loop that the data is ready TMR0_Next ; Real Time Clock (64 TMR0 overflows = ~2 seconds) btfss RTC_Enable ; Is the RTC running? goto TMR0_Int_End ; NO: skip this part decfsz RTC_64,F ; Count 1/64th of 2 seconds. 64th round? goto TMR0_Int_End ; NO: nothing happens movlw d'64' ; YES: Reinitialize counter to 64 movwf RTC_64 call Blink_Timer_LED ; Blink the timer's LED decfsz RTC_L,F ; Count -1 second. Zero? goto TMR0_Int_End ; NO: nothing more happens movf RTC_H,F ; YES: btfss ZERO ; Is RTC_H == 0? goto TMR0_Dec_RTC_H ; NO: Count -1 in RTC_H goto TMR0_Ring ; YES: Ring the computer TMR0_Dec_RTC_H decf RTC_H,F ; goto TMR0_Int_End ; TMR0_Ring bcf RTC_Enable ; bsf RTC_Ring_Time ; TMR0_Int_End goto Pop ; End of Interrup Vector ; ;***** RC5 register routines ***** ; Clear_RC5 ; ** SUBROUTINE ** clrf RC5_0 ; clear RC5 registers (all 4) clrf RC5_1 clrf RC5_2 clrf RC5_3 clrf RC5_Bit_Count movlw d'11110000' andwf flags,F ; Clear only the lower 4 bits of flags (RC5-related) return ; return from subroutine Shift_RC5 ; ** SUBROUTINE ** rlf RC5_0,F ; left shift RC5_0. MSb in carry rlf RC5_1,F ; left shift RC5_1. Carry to LSb, MSb in carry rlf RC5_2,F ; left shift RC5_2. Carry to LSb, MSb in carry rlf RC5_3,F ; left shift RC5_3. Carry to LSb, MSb in carry return ; return from subroutine Add_1_to_RC5 ; ** SUBROUTINE ** call Shift_RC5 ; Shift the RC5 registr by 1 bit bsf RC5_0,0 ; Set LSb to 1 return ; return from subroutine Add_0_to_RC5 ; ** SUBROUTINE ** call Shift_RC5 ; Shift the RC5 registr by 1 bit bcf RC5_0,0 ; Set LSb to 1 return ; return from subroutine Add_1_bit_to_RC5 incf RC5_Bit_Count,F btfss Prev_RC5_Val ; Previous RC5 value == 1? goto Add_one_0_bit_to_RC5 ; NO: Add one '0' bit Add_one_1_bit_to_RC5 ; YES: Add one '1' bit call Add_1_to_RC5 return Add_one_0_bit_to_RC5 call Add_0_to_RC5 return Add_2_bits_to_RC5 incf RC5_Bit_Count,F incf RC5_Bit_Count,F btfss Prev_RC5_Val ; Previous RC5 value == 1? goto Add_two_0_bit_to_RC5 ; NO: Add two '0' bits Add_two_1_bit_to_RC5 ; YES: Add two '1' bits call Add_1_to_RC5 call Add_1_to_RC5 return Add_two_0_bit_to_RC5 call Add_0_to_RC5 call Add_0_to_RC5 return ; ;***** Blink Timer LED ***** ; Blink_Timer_LED btfsc RTC_L,0 ; Is RTC seconds even? goto Timer_LED_On ; NO: LED on goto Timer_LED_Off ; YES: LED off Timer_LED_On bsf Timer_LED return Timer_LED_Off bcf Timer_LED return ; ;***** RC5 LED ON/OFF routines *** ; RC5_LED_On bsf RC5_LED return RC5_LED_Off bcf RC5_LED return ; ;***** Ring ***** ; Ring bsf Ring_Output ; Start Ring signal movlw 0xFF ; Loop for ~50 ms (256 * 65 * 3 cycles @ 1 MHz) movwf Ring_Counter_L movlw 0x41 movwf Ring_Counter_H Ring_Loop decfsz Ring_Counter_L,F ; this inner waiting loop takes about 3 cycles goto Ring_Loop decfsz Ring_Counter_H,F goto Ring_Loop bcf Ring_Output ; Stop Ring signal return ; ;***** Main Program ***** ; Main_Start SEL_BANK_1 movlw b'00000110' ; Select internal timer mode for TMR0 ; Prescaler assigned to TMR0 ; Set prescaler to 1:128 (b'110'). Then: ; TMR0 increment = 0.128 ms ; TMR0 overflow int = 32.768 ms movwf OPTION_REG SEL_BANK_0 movlw d'64' ; Initialize RTC_64 counter to 64. RTC_64 reaching 0 = 2.097152 s movwf RTC_64 clrf RTC_L ; Clear RTC registers (Low and High) clrf RTC_H call Clear_RC5 ; Clear RC_5 registers movlw 0xFF movwf RC5_Timer_Ovfl ; Initialize RC5_Timer_Ovfl to FF: first int is a long delay clrf PORTB ; Initialize port B SEL_BANK_1 movlw 0x1E ; Set RB4,RB3,RB2,RB1 to input movwf TRISB movlw 0x00 ; Set PortA<4:0> to output movwf TRISA SEL_BANK_0 bsf INTCON,T0IE ; Enable TMR0 Interrupt bsf INTCON,RBIE ; Enable RB4 Interrupt on change bsf INTCON,GIE ; Enable all Interrupts Main_Loop btfsc RC5_Data_Ready ; Is the RC5 data available? goto Process_RC5_Data ; YES: Process the RC5 data btfsc Data_Ready ; NO: then is the RTC programming data ready? goto Process_RTC_Data ; YES: Process RTC data btfsc RTC_Ring_Time ; NO: then do we need to ring the computer? goto RTC_Ring ; YES: then ring it goto Main_Loop ; NO: Loop ; ; RC5 Data processing ; Process_RC5_Data movlw b'00111111' andwf RC5_2,F ; set the RC5 Toggle bit (2 bits in Manchester) to 0 btfss RC5_RW_Input ; YES: write to EEPROM, then? goto Compare_RC5_to_EEPROM ; NO: compare to EEPROM version goto Store_RC5_to_EEPROM ; YES: write to EEPROM Compare_RC5_to_EEPROM movlw EEPROM_RC5_0 ; movwf EEADR ; Address to read SEL_BANK_1 bsf EECON1, RD ; EE Read SEL_BANK_0 movf EEDATA, W ; W = EEDATA xorwf RC5_0,W ; Compare RC5_0 and EEPROM_RTC_0 btfss ZERO ; Is the result Null (RC5_0 == EEPROM_RC5_0)? goto Compare_RC5_End ; NO: go back to main loop ; YES: Check next RC5 byte movlw EEPROM_RC5_1 ; movwf EEADR ; Address to read SEL_BANK_1 bsf EECON1, RD ; EE Read SEL_BANK_0 movf EEDATA, W ; W = EEDATA xorwf RC5_1,W ; Compare RC5_1 and EEPROM_RC5_1 btfss ZERO ; Is the result Null (RC5_1 == EEPROM_RC5_1)? goto Compare_RC5_End ; NO: go back to main loop ; YES: Check next RC5 byte movlw EEPROM_RC5_2 ; movwf EEADR ; Address to read SEL_BANK_1 bsf EECON1, RD ; EE Read SEL_BANK_0 movf EEDATA, W ; W = EEDATA xorwf RC5_2,W ; Compare RC5_2 and EEPROM_RC5_2 btfss ZERO ; Is the result Null (RC5_2 == EEPROM_RC5_2)? goto Compare_RC5_End ; NO: go back to main loop ; YES: Check next RC5 byte movlw EEPROM_RC5_3 ; movwf EEADR ; Address to read SEL_BANK_1 bsf EECON1, RD ; EE Read SEL_BANK_0 movf EEDATA, W ; W = EEDATA xorwf RC5_3,W ; Compare RC5_3 and EEPROM_RC5_3 btfss ZERO ; Is the result Null (RC5_3 == EEPROM_RC5_3)? goto Compare_RC5_End ; NO: go back to main loop ; YES: They match. Let's wake up the computer (Ring) call Ring Compare_RC5_End bcf RC5_Data_Ready ; Clear the flag bcf INTCON,RBIF ; Clear the flag of a previous RC5 interrupt bsf INTCON,RBIE ; Re-enable RC5 interrupts goto Main_Loop Store_RC5_to_EEPROM bcf RC5_Data_Ready ; Clear the flag bcf INTCON,GIE ; Disable all Interrupts SEL_BANK_1 bsf EECON1,WREN ; Enable Write ; Write RC5_0 to EEPROM SEL_BANK_0 movlw EEPROM_RC5_0 ; Address of register EEPROM_RC5_0... movwf EEADR ; ... to EEADR movf RC5_0,W ; Content of register RC5_0... movwf EEDATA ; ... to EEPROM SEL_BANK_1 movlw 0x55 ; movwf EECON2 ; Write 55h movlw 0xaa ; movwf EECON2 ; Write AAh bsf EECON1,WR ; Set WR bit Store_RC5_Wait_Byte_0 btfss EECON1,EEIF ; Has the interrupt occured? goto Store_RC5_Wait_Byte_0 ; NO: Wait some more bcf EECON1,EEIF ; YES: Clear the interrupt flag before continuing ; Write RC5_1 to EEPROM SEL_BANK_0 movlw EEPROM_RC5_1 ; Address of register EEPROM_RC5_1... movwf EEADR ; ... to EEADR movf RC5_1,W ; Content of register RC5_1... movwf EEDATA ; ... to EEPROM SEL_BANK_1 movlw 0x55 ; movwf EECON2 ; Write 55h movlw 0xaa ; movwf EECON2 ; Write AAh bsf EECON1,WR ; Set WR bit Store_RC5_Wait_Byte_1 btfss EECON1,EEIF ; Has the interrupt occured? goto Store_RC5_Wait_Byte_1 ; NO: Wait some more bcf EECON1,EEIF ; YES: Clear the interrupt flag before continuing ; Write RC5_2 to EEPROM SEL_BANK_0 movlw EEPROM_RC5_2 ; Address of register EEPROM_RC5_2... movwf EEADR ; ... to EEADR movf RC5_2,W ; Content of register RC5_2... movwf EEDATA ; ... to EEPROM SEL_BANK_1 movlw 0x55 ; movwf EECON2 ; Write 55h movlw 0xaa ; movwf EECON2 ; Write AAh bsf EECON1,WR ; Set WR bit Store_RC5_Wait_Byte_2 btfss EECON1,EEIF ; Has the interrupt occured? goto Store_RC5_Wait_Byte_2 ; NO: Wait some more bcf EECON1,EEIF ; YES: Clear the interrupt flag before continuing ; Write RC5_3 to EEPROM SEL_BANK_0 movlw EEPROM_RC5_3 ; Address of register EEPROM_RC5_3... movwf EEADR ; ... to EEADR movf RC5_3,W ; Content of register RC5_3... movwf EEDATA ; ... to EEPROM SEL_BANK_1 movlw 0x55 ; movwf EECON2 ; Write 55h movlw 0xaa ; movwf EECON2 ; Write AAh bsf EECON1,WR ; Set WR bit Store_RC5_Wait_Byte_3 btfss EECON1,EEIF ; Has the interrupt occured? goto Store_RC5_Wait_Byte_3 ; NO: Wait some more bcf EECON1,EEIF ; YES: Clear the interrupt flag before continuing ; End of writing bcf EECON1,WREN ; Disable Write SEL_BANK_0 bcf INTCON,RBIF ; Clear the flag of a previous RC5 interrupt bsf INTCON,RBIE ; Re-enable RC5 interrupts bsf INTCON,GIE ; Enable all Interrupts goto Main_Loop ; ; RTC input data processing ; Process_RTC_Data btfsc Reading_Data ; Are we already reading RTC data? goto Read_RTC_Data ; YES: skip initialization clrf RTC_Temp_L ; No: Initialize. Clear the Temp registers clrf RTC_Temp_H movlw d'16' ; Set the bit counter to 16 movwf RTC_Bit_Counter bsf Reading_Data ; Set the reading flag Read_RTC_Data rlf RTC_Temp_L,F ; Shift the RTC_Temp rlf RTC_Temp_H,F bcf RTC_Temp_L,0 ; Set the btfsc Data ; Read the data bsf RTC_Temp_L,0 bsf Data_ACK ; Set Data_ACK Wait_ACK_of_ACK btfsc Data_Ready ; Is the Data_Ready signal to 0 (i.e. other has got our ACK) goto Wait_ACK_of_ACK ; NO: then wait until it goes to 0 bcf Data_ACK ; YES: clear the Data_Ready signal decfsz RTC_Bit_Counter,F ; 1 more bit read. Have we read all 16 bits now? goto Main_Loop ; NO: there will be another data bit, hopefully Save_RTC_Data ; Move the data to the RTC counters bcf Reading_Data ; Clear the reading flag bcf INTCON,T0IE ; Disable TMR0 Interrupt (necessary?) movf RTC_Temp_L,W ; Move RTC_Temp_L to RTC_L movwf RTC_L movf RTC_Temp_H,W ; Move RTC_Temp_H to RTC_H movwf RTC_H movlw d'64' ; Initialize RTC_64 counter to 64. movwf RTC_64 bsf RTC_Enable ; Restart the RTC bsf INTCON,T0IE ; Re-enable TMR0 Interrupt (see disable's comment) goto Main_Loop ; ; RTC Event processing ; RTC_Ring bcf RTC_Ring_Time ; Clear the time-to-ring flag call Ring ; and ring goto Main_Loop ; ; Code End ; END