Using The PIC Microcontroller
Based on feedback on this article, we now have a new 8-pin custom programmed PIC designed for infrared serial communications & remote control applications. Click HERE for details.
Parts Kits now in stock for this application HERE.
Many applications can benefit from an infrared communications link. This article will hopefully shed some light on how to send & receive serial data between microcontrollers such as the BASIC Stamp, or PICMicro programmed with PicBasic.
Note: You can build these simple circuits, cut & paste the code into the PicBasic Pro compiler, and have a full-blown serial infrared communications network operating between several PICMicro's or even a BASIC Stamp. You do not need the PicBasic compiler to do this. If you can program a PIC, we have sample code at the end of this article for an 8-pin PIC that will generate the 38KHz carrier for this application instead of using the hardware PWM feature of the more expensive PICMicro.
Now - let's dive right in to the code.
' Setting up hardware PWM for 38KHz operation. DEFINE LOADER_USED 1'Setup for boot-loader programming TRISC.2 = 0 ' CCP1 (PortC.2 = Output) PR2 = 25 ' Set PWM Period for approximately 38KHz CCPR1L = 13 ' Set PWM Duty-Cycle to 50% CCP1CON = %00001100 ' Select PWM Mode T2CON = %00000100 ' Timer2 = ON + 1:1 prescale TRISB.0 = 0 ADDRESS VAR BYTE DAT VAR BYTE ID VAR BYTE ADDRESS = 10 ID = 25 BEGIN: FOR DAT = 0 TO 255 SEROUT PORTB.0,4,[ID,ADDRESS,DAT] PAUSE 100 NEXT GOTO BEGIN
The hardware PWM is configured in the beginning of our code. After we define the PWM frequency, and duty cycle, our code can proceed to performing other tasks while the hardware continues to operate in the background. Notice how program execution falls into a tight loop once it reaches the routine BEGIN. Even though we stay within this loop sending serial data continuously, the PWM signal continues without the need for software intervention to generate the 38KHz carrier.
Hardware peripherals offer true multi-tasking not available otherwise. Yes - you'll hear some people boasting of using interrupts or "C" for multi-tasking, but it's still a far cry far from the real thing. Background hardware operation is the "real deal" -- period.
How it works:
This example was tested on the FLASH-Lab 77 development board with PicBasic Pro for rapid development. The FLASH-Lab 77 boards use a boot-loader program. If you're not using the boot-loader, just strip the line DEFINE LOADER_USED 1 line from the code above, and proceed as normal.
PortC.2 on the PIC16F877 also functions as the hardware PWM output pin (CCP1) when not being used for normal I/O operations. Several registers need to be configured to use the PWM feature.
Timer2 (TMR2), and PR2 (timer2 module's period register) are used to establish the period. Figure 1 shows a graphical representation of how this works.
As shown in the previous code segment, we first have to load the timer2 period register (PR2) to setup the period for our required frequency (38KHz) output. A PWM output (Figure 1) has a time base (period) and a time that the output stays high (duty cycle). The frequency of the PWM is the inverse of the period - which is (1/period).
Figure 1 shows the period from TMR2 = PR2 to TMR2 = PR2. This represents one complete cycle. To determine the period required to generate a frequency of 38KHz, simply take 1/38KHz, or 1/38,000. The result is 1/38,000 = 0.000026316 (26.3uS). We know that in order to generate our 38KHz frequency, we'll need each period to be approximately 26 microseconds. A duty cycle of 50% would need 13uS high, and 13uS low on the I/O-pin to generate our required frequency of 38KHz at a duty cycle of 50%.
Frequency ( f ) and period ( P ) are inversely proportional. f = 1/P, & P = 1/f.
To calculate the value to be loaded into PR2:
We'll use a prescale value of 1 for timer2.
4 * 1 * 38,000 = 152,000
We'll load 25 into PR2, and accept the minimal error.
Figure 2 shows how to find the maximum PWM resolution (in bits) for a given PWM frequency, with our selected oscillator frequency. This application uses the PIC16F877 with a 4MHz oscillator, so we need to calculate: Log (4MHz/38KHz) / Log(2) to find our maximum resolution in bits....
Log (4,000,000/38,000 ) = 2.022
Note: Resolution refers to the resolution of the duty cycle, and not the actual frequency of the PWM signal.
I randomly chose a duty cycle of 50% for this application. To setup the duty cycle, there are two registers that need to be configured.
CCPRL1 contains the eight (most significant bits), and CCP1CON <4:5> (CCP1CON bits 4 and 5) contain the two (least significant bits). Since we will only have a maximum of 6-bits resolution, we only need to load the CCPR1 register. CCP1CON bits 4 & 5 will be loaded with 0's.
To figure the value to load into CCPRL1 for 38KHz @ 4MHz with a 50% duty cycle:
(PR2 + 1) * TMR2 prescale * 50% Duty Cycle =
value for CCPRL1, or
0 ' CCP1 (PortC.2 = Output)
Above is the code we use. Notice register CCP1CON is loaded with a binary value that sets bits 2 and 3. These are CCP1M3 and CCP1M2, and set the PWM mode. Timer2 T2CON bit 2 is set to start the timer. Bits 1 and 0 are left clear for a prescaler of 1. TRISC.2 sets up portC.2 as an output which is necessary to have portC.2 output the 38KHz carrier (PWM) frequency.
Now that we have our 38KHz carrier with a ~50% duty cycle setup, we'll need a circuit that will combine our carrier and serial data signal.
Note: We are actually just controlling the carrier frequency with our serial data-stream by turning the carrier ON/OFF for the duration of each data-bit.
Using the 74HCT132 NAND Schmitt trigger as shown below in Figure 6, simplifies the procedure. One input will be connected directly to PortC.2 which will generate our 38KHz carrier using the PIC hardware PWM feature. The remaining input will connect to PortB.0 which will be used to output our serial data-stream.
The NAND output (pin #3) will only go to logic 0 when both inputs 1 & 2 are at logic 1. This effectively holds the PNP drive transistor OFF until both inputs are at logic 1. Since the carrier frequency is 38KHz, and considerably faster than our serial data, the serial data will cause the transistor to turn ON during each logic 1 data-bit.
To further explain what's happening, think of it as the serial data just turning the carrier ON/OFF for the same bit-time as each data-bit in the serial data-stream.
This combination of both inputs to the NAND will give us the 38KHz modulated data signal on the output pin of the NAND.
Here's what both signals being applied to inputs 1 & 2 of the NAND gate look like when connected to an O-scope.
Figure 3 shows the two separate signals being applied to the two inputs (1 & 2) of the NAND Schmitt Trigger. Trace A is the actual serial data being sent out from the PIC on PortB.0. Trace B is the 38KHz PWM carrier being generated by the PIC hardware, and output on PortC.2 to the input of the NAND gate.
Now look at the modulated data signal on (Trace A) coming out of the NAND gate on pin #3.
Notice from trace A (signal being fed to the base of the PNP drive transistor) that the base of the transistor & our serial data is now modulated at 38KHz, and inverted. Trace B still shows the 38KHz carrier from portC.2 into pin 1 of the NAND gate.
Figure 5 (shown below) shows our serial data being input to the NAND (trace B), and the actual output of data from the PNA4602M 38KHz IR detector module (trace A).
Notice how the output signal from the detector (trace A) has the 38KHz carrier stripped from the incoming data-signal. The detector module removes the carrier (38KHz), and outputs the data (minus) the 38KHz carrier.
The slight phase shift you see below is due to the response times of the infrared detector module, drive transistor, and NAND gate combined.
Below is the simple transmitter circuit. To use the circuit below, just connect CCP1 of the PIC you use (PortC.2 for the PIC16F877) to one of the two inputs to the NAND gate. Use any other I/O-pin you want to send serial data to the other input of the NAND gate.
Tip: Don't substitute another NAND gate for this project. The 74HCT132 is a NAND Schmitt Trigger. The Schmitt Trigger function helps to stabilize circuit oscillation.
A simple two input NAND Schmitt Trigger is used to combine the two incoming signals into an output data signal modulated at 38KHz.
The receiver is very simple. Just take the serial data directly from the data output pin of the detector module as shown in figure 7.
For the receiver, I used the BASIC Stamp II for rapid testing & verification of operation. Converting this code to PicBasic is simple. Here's the BS2 code.
' BS2 program to test IR communication program ' using the PIC16F877 hardware PWM. SYNCH CON 25 'Establish synchronization byte BAUD CON 396 'NON INVERTED 2400 baud (MAX) DAT VAR byte 'Data storage variable DIRH = %11111111 'All outputs ADDRESS VAR BYTE START: SERIN 0,BAUD,[WAIT(SYNCH),ADDRESS,DAT] OUTH = dat GOTO START
Notice how the Stamp waits until it receives the SYNCH byte first before accepting the remaining data. This helps to synchronize the receiver to the transmitter. The address byte shows one method of talking to multiple receivers by using addressing. This is only an example. You can use the address byte to force the program to only respond if it receives its own unique address first, or ignore the technique altogether.
DAT is the data (byte) that will be transferred to the Stamp port pins P8 to P15. Connect 8 LEDs to the Stamp I/O-pins P8 to P15. Use 470 ohm series resistors for each LED to limit current to acceptable levels. When IR data is received, you can verify reception visibly by watching the LED's count from 0 to 255.
Note: Due to the limited response time of the detector module, the maximum baud-rate is normally around 2400. If you experiment with this project - you'll see first-hand how increasing the baud-rate will degrade data.
The simple infrared transmitter circuit shown above in Figure 6 can also be used with various encoder ICs such as the Holtek 4-bit HT-12E encoder, and even the 8-bit versions as well. The only requirement is that you have a stable oscillator such as the PIC using hardware PWM, a 555 timer circuit, or another oscillator source capable of providing a stable carrier frequency.
The data output pin of the encoder provides the serial data on the transmitter. Use the circuit shown below in Figure 8 when using the Holtek decoder IC's instead of serial data with a microcontroller.
The circuit shown below is a simple inverter circuit, and necessary for use with the HT-12D and other decoders. If you prefer, you can use this receiver circuit for serial as well. Either one will work with serial data, but use the one below for the HT-12D decoder IC.
Tip: The PNA4602M output has an internal pull-up resistor. This causes the output to hold a logic 1 (high) on the base of the NPN transistor. The HT-12D decoder IC will go into low power or sleep mode when the data input pin is at ground conserving power in the receiver circuit until data is being received.
If you don't own PicBasic, or simply don't wish to commit a large PIC such as the PIC16F877 or F876 to this simple task, you can easily use the smaller 8-pin version (PIC12C508) to generate the 38KHz carrier.
Here's a short code-sample for generating a 38KHz frequency using the small 8-pin PIC12C508. It's not hardware PWM, but it works very well, and it's cheap.
Note: For higher precision, use an external crystal or ceramic resonator. The internal oscillator for the PIC12C508 is a low precision oscillator. For most instances - the internal oscillator works fine, but increased precision of the carrier frequency will increase receiver sensitivity - and hence increase the overall operating range.
To use an external oscillator with the sample code below - replace IntRC_OSC with XT_OSC before assembling the code, and burning the PIC12C508.
Assembled with MPASMWIN V03.30 Download the .HEX file in .txt format HERE Just save the file, then re-name it with the .hex file extension.
PROCESSOR 12c508 #include "p12c508.inc" __CONFIG _MCLRE_OFF & _CP_OFF & _WDT_OFF & _IntRC_OSC #DEFINE PORT B'11111101' MOVF OSCCAL MOVLW PORT TRIS GPIO BEGIN BCF GPIO, 1 ;1uS NOP ;2uS each nop is 1uS long NOP ;3uS NOP ;4uS NOP ;5uS NOP ;6uS NOP ;7uS NOP ;8uS NOP ;9uS NOP ;10uS NOP ;11uS NOP ;12uS NOP ;13uS NOP ;14uS NOP ;15uS NOP ;16uS NOP ;17uS NOP ;18uS NOP ;19uS BSF GPIO, 1 ;1uS Begin HIGH duty cycle NOP ;2uS NOP ;3uS NOP ;4uS NOP ;5uS GOTO BEGIN ;2uS (26uS total for 38KHz) END
The tiny 8-pin PIC12C508 can easily be programmed to output a steady 38KHz carrier. If you just need the 38KHz oscillator for this project - this PIC is very inexpensive, and works quite well.
The assembly code shown above will provide a duty cycle of approximately 27%. Adjusting the time between the BCF and BSF commands, will adjust the duty cycle. Be sure to maintain an overall time (period) of 26uS to keep the frequency stable at 38KHz.
Tip #1: Try swapping the BCF & BSF instructions around - or just split the difference between each command to set & clear the I/O-pin GPIO.1.
Tip #2: If you really want to crank power to the LED, try reducing the duty-cycle of the carrier to around 1uS, and using a very small (or no) current limiting resistor for the IR LED. You'll want to replace the 2N4403 LED drive transistor with one that can handle this power level.
Port-pin GPIO.1 is simply connected to one of the NAND inputs, and will replace the larger PIC used in this application. The serial data can then come directly from a PIC or BASIC Stamp I/O-pin, and you're ready to go with infrared data communications.
Until the next project - have fun.......;o]
Copyright © 1999-2008