You are on page 1of 41

INTRODUCTION TO PIC INTERRUPTS AND THEIR HANDLING IN C

Interrupts are common features in almost all processor family, be it old 8051, AVR, PIC,
ARM or the x86 used in desktops. So their in depth and clear knowledge is required for
successful system software engineers. This guide will explain the interrupt system in
general and their application using PIC18 architecture. We will also learn about handling
of interrupts in HI-TECH C for PIC18.

What are Interrupts?

Interrupts, as the name suggests interrupts the normal execution and Requests and urgent
attention of CPU. Interrupts are situations that the CPU can't predict when they will
happen, they can happen any time, so the CPU does not wait for them. So the CPU keeps
on doing its normal job unless and interrupt occurs. For example when the USART
(Serial Communication Hardware) will receive data is unknown, it can receive data any
time. So the CPU keeps on doing its normal job, which may be for example read
temperature using LM35 sensor and display on LCD. The CPU keeps on doing the
"normal" job, but as soon as the USART receive data it informs the CPU using an
interrupt. The CPU save its current state (so that it can resume), and jumps to the ISR
(interrupt service routine) immediately. Where we can process the command or put it in a
FIFO queue (to process latter). The ISR is generally kept very small and fast. As soon as
the ISR ends, the CPU restores its saved state and resumes where it left. In this way CPU
does not missed any data byte.

Example of sources of Interrupts in PIC18 (also common in other MCUs)

• External interrupts - they are named INTx (like INT0, INT1 etc), they provide a
means for external hardware to generate interrupts. Like if you connect a
touchscreen controller to your PIC MCU. Then the touchscreens PENINT (pen
interrupt) can be connected to INT0 (or any other INTx). Then when ever the pen
(or stylus) touches the screen it will interrupt the CPU. This interrupt will be
explained in details in its own tutorial.
• TIMER interrupts - They are also very common in MCUs. Today's MCUs
comes with very sophisticated timers that can do lots of magic for you. They have
they related interrupts. In most simple situation they can act like alarm clocks that
can interrupt the CPU at predefined intervals. If you toggle a i/o pin in response to
these alarms (interrupts), what you have is a frequency generator! For more info
on timers on PIC18, read this article.
• Analog to Digital Converter Interrupts - A/D Converter takes some time to
complete its operation. So the CPU can either wait for it to complete or set up an
AD conversion complete interrupt. In the latter case CPU can do other tasks while
A/D converter converts the input. As soon as A/D converter completes its job it
will inform CPU to read the value from its buffer. For more info on A/D
Converter of PIC18, read this article.
• Data Interrupts - MCUs have many different types of data i/o engines, like
USART, SPI, I2C, Parallel etc. They can also interrupt the CPU when data
transmission is complete or data arrives from external source. Like an RFID
reader send a packet because a user just brought his or her card near the reader. Or
the GSM module detected an incoming call.

How interrupts are managed?

In general each interrupt source have following related bits.

• Enable Bit - There are suffixed with IE (Interrupt Enable) example TMR0IE
stands for TIMER0 Interrupt Enable. It can be used to enable/disable the related
interrupt. When set to '1' it enables the interrupt.
• Flag Bit - It is set automatically by the related hardware when the interrupt
condition occurs. It is generally suffixed with IF (Interrupt Fag). When it is set to
'1' we know that interrupt has occurred. For example when TMR0IF is set by
TIMER0, it indicates that TIMER0 has overflowed.
• Priority Bit - We can leave this for now to keep things simple. We won't be using
interrupt priority feature of PIC18.

In global level there are following bits to control the interrupts globally.

• GIE - Global Interrupt Enable, enables/disables interrupts globally.


• PEIE - Enable/disable all peripheral interrupts.

Interrupt Vector

It is the address where the CPU jumps when an interrupt occurs. In PIC18 this address is
0008(hex). CPU jumps to this address for any interrupt. The ISR or Interrupt service
routine is placed in this address. The ISR must determine the source of interrupt from the
flag bit(described above).

1 //Main Interrupt Service Routine (ISR)


2 void interrupt ISR()
3 {
4 //Check if it is TMR0 Overflow ISR
5 if(TMR0IE && TMR0IF)
6 {
7 //TMR0 Overflow ISR
8
9 counter++;//Increment Over Flow Counter
10
11 if(counter==76)
12 {
13 //Toggle RB1 (LED)
14 if(RB1==0)
15 RB1=1;
16 else
17 RB1=0;
18
19 counter=0; //Reset Counter
20 }
21
22 //Clear Flag
23 TMR0IF=0;
24 }
25 }

For example in above code snipped. void interrupt ISR() is the main service routine
where the CPU jumps for any interrupt (TIMER,A/D, INTx etc). As soon as we enter the
ISR we check for the source of interrupt. This done using the line

if(TMR0IE && TMROIF)

The above line check is the source of interrupt was TIMER0 (so it checks if
TMR0IF is set or not). It also checks TMR0IE (TIMER0 interrupt enable bit) to make
sure this interrupt is enabled or not. It execute the TIMER0 ISR only of both the
condition are true. One more line on interest is line 23 where we clear the flag, this is
necessary otherwise the interrupt is triggered again.

You should note one more point the GIE (explained above) is cleared
automatically when PIC18 enters ISR, this make sure that interrupts cannot be interrupted
them self. It also automatically sets GIE on return from interrupt, which re enable
interrupts.

In the above code (from line 9 to 20), you can see that for each overflow of timer
(like alarm from an alarm clock) we increment a counter and as soon as this counter
reaches 76 we toggle an LED. And also clear the counter (line 19). This gives us a LED
that is toggled ever 1sec. So you see that LED is controlled fully by the TIMER while
CPU can keep on doing other jobs but LED will always blink at its predefined rate. This
example gives a kind of illustration of multitasking, the LED take care of itself. You don't
have to write anything on the main () function to control the LED. Your main function
could go on normally, for example control a line follower robot. The TIMER will take
care of the LED and toggle it every second. The LED could be used for decoration of the
robot.

Handling interrupt in C

Here I will show you how we can write programs in HI-TECH C that utilizes
interrupt feature of PIC MCU. The interrupt handling is very clean and straightforward in
HI-TECH C (as compare to Microchip C18 Compiler). Please note that interrupt handling
is not a standard feature of C language, so their is significant diffenence between
different compiler in handling interrupts.

Please note that to simplify the situtation, we are not using interrupt priority
feature of PIC18(because this is a guide for first time user of interrput). In HI-TECH C
for PIC18 an ISR (interrupt service routine, that is called when interrupt condition occur)
is defined like this
void interrupt FunctionName(void)

As you can see in above code line the "interrupt" function qualifier is used to mark a
function as ISR. The following point must be kept in mind while defining an ISR :-

1. The return type of ISR must always be void.


2. It must not take any parameters. That's why argument list is set to void.
3. The name of function can be any valid C function name. But I generally use the
name ISR for ISRs.
4. Their can be at most one ISR function. (Two if using interrupt priority).

For more information refer to the HI-TECH C user manual.

HI-TECH C Program for TIMER0 Interrupt.

The following is a simple program that demonstrate use of interrupts. The program is
very simple so does not do any magic but only teaches use of interrupt. Please go through
the program very carefully to check out how every feature described above are used. To
understand the program you also need some knowledge of TIMER0 which is described in
detail in the following article.

• Introduction to PIC18 Timers.

The program blinks a LED on RB1. The project is available for download in the
above page.

1
/*****************************************************************
2
3 A simple example to demonstrate the use of PIC Timers. In this
4 example the TIMER0 is configured as follows.
5
6 *8 Bit Mode
7 *Clock Source from Prescaler
8 *Prescaler = FCPU/256 (Note: FCPU= Fosc/4)
9 *Over flow INT enabled
10
11 As our FCPU=20MHz/4 (We are running from 20MHz XTAL)
12 =5MHz
13
14 Time Period = 0.2uS
15 Prescaller Period = 0.2 x 256 = 51.2uS
16 Overflow Period = 51.2 x 256 = 13107.2 uS
17 = 0.0131072 sec
18
19 So we need 1/0.0131072 Over Flows to count for 1 sec
20 = 76.2939 Overflows
21
22 So we keep a counter to keep track of overflows.
23
24 When an over flow occurs the PIC jumps to ISR where we
25 increment counter. And when counter becomes 76 we toggle
26 RB1 pin. This pin is connected to LED. Therefore we
27 have a LED which is ON for 1 sec and Off for 1sec.
28
29
30 Target Chip: PIC18F4520
31 Target Compiler: HI-TECH C For PIC18 (http://www.htsoft.com/)
32 Project: MPLAP Project File
33
34 Author: Avinash Gupta
35 Copyright (c) 2008-2010
36 eXtreme Electronics, India
37 www.eXtremeElectronics.co.in
38
39 NOTICE
40 -------------
41 NO PART OF THIS WORK CAN BE COPIED, DISTRIBUTED OR PUBLISHED
WITHOUT A
42 WRITTEN PERMISSION FROM EXTREME ELECTRONICS INDIA. THE LIBRARY,
NOR ANY PART
43 OF IT CAN BE USED IN COMMERCIAL APPLICATIONS. IT IS INTENDED TO
BE USED FOR
44 HOBBY, LEARNING AND EDUCATIONAL PURPOSE ONLY. IF YOU WANT TO USE
THEM IN
45 COMMERCIAL APPLICATION PLEASE WRITE TO THE AUTHOR.
46
47
*****************************************************************/
48
49 #include <htc.h>
50
51
52 //Chip Settings
53 __CONFIG(1,0x0200);
54 __CONFIG(2,0X1E1F);
55 __CONFIG(3,0X8100);
56 __CONFIG(4,0X00C1);
57 __CONFIG(5,0XC00F);
58
59 unsigned char counter=0;//Overflow counter
60
61 void main()
62 {
63 //Setup Timer0
64 T0PS0=1; //Prescaler is divide by 256
65 T0PS1=1;
66 T0PS2=1;
67
68 PSA=0; //Timer Clock Source is from Prescaler
69
70 T0CS=0; //Prescaler gets clock from FCPU (5MHz)
71
72 T08BIT=1; //8 BIT MODE
73
74 TMR0IE=1; //Enable TIMER0 Interrupt
75 PEIE=1; //Enable Peripheral Interrupt
76 GIE=1; //Enable INTs globally
77
78 TMR0ON=1; //Now start the timer!
79
80 //Set RB1 as output because we have LED on it
81 TRISB&=0B11111101;
82
83 while(1); //Sit Idle Timer will do every thing!
84 }
85
86 //Main Interrupt Service Routine (ISR)
87 void interrupt ISR()
88 {
89 //Check if it is TMR0 Overflow ISR
90 if(TMR0IE && TMR0IF)
91 {
92 //TMR0 Overflow ISR
93 counter++; //Increment Over Flow Counter
94
95 if(counter==76)
96 {
97 //Toggle RB1 (LED)
98 if(RB1==0)
99 RB1=1;
100 else
101 RB1=0;
102
103 counter=0; //Reset Counter
104 }
105 //Clear Flag
106 TMR0IF=0;
107 }
108 }

Program Walk through

• Line 64 to 72 : Configure the TIMER0. See tutorial on timer for more info.
• Line 74 to 76 : Enables Timer0 interrupt (TMR0IE=1), peripheral interrupt
(PEIE=1) and finally sets GIE=1 to enable interrupts.
• Line 83: The program has noting to do, so we enter an infinite loop and burn
CPU cycles. Your program is free to to any tasks after interrupts are set up and
enabled. We required ISR will automatically called by the hardware to service the
situation. As as soon as service is complete, normal execution will be resumed.
• Line 87: define the ISR.
• Line 90: Checks if it is the timer interrupt (by checking the flag bit TMR0IF) and
if the timer interrupts are enabled (by checking TMR0IE).
• Line 91 to 104: is the service routine of TIMER0 interrupt.
• Line 106: clears the timer interrupt flag, this step is very important before exiting
the ISR or it will be called again!
A Word from the author

Interrupt handling is a vital for any successful and quality embedded code, so
their clear knowledge is very important. The above article described interrupt handling in
details. The same code framework and techniques are applicable for any interrupt enabled
program howsoever complex it is. So I recommend that you go though the article until
you are clear with every concept. They will be very helpful for you to write your own
interrupt enabled programs and understand other programs provided in our website.

Introduction to PIC18’s Timers – PIC Microcontroller Tutorial

Timers are common features of most microcontroller. In simplified terms a timer is


just a register whose value keeps increasing (or decreasing) by a constant rate without the
help of the CPU. The CPU can read or write this register any time. It reads it find out
how much time has elapsed. The Timer register can have the following bit length

• 8 bit timers - These can count between between 0-255


• 16 bit timers - These can count between 0-65536
• 32 bit timers - These can count between 0-4294967296

A timer has a clock source, for example of the clock source of 10KHz is input to a
timer then one increment will take 100uS (micro second). This clock source can be
obtained from the CPU clock. The CPU clock of popular MCU ranges from 1 MHz to
20Mhz, this can be sometimes too fast. To help us out their is a thing called prescaler in
the MCU. The job of prescaler is to divide the CPU clock to obtain a smaller frequency.
The PIC Micro that we will use in this example has the following prescaler division
factors available.

1. 256
2. 128
3. 64
4. 32
5. 16
6. 8
7. 4
8. 2
9. 1 (Prescaler by-passed)

Timers are also called Counters this is because they can be used to count external events.
The following examples illustrate the fact.
Counter Operation.

The above setup can be used to measure the RPM (speed in revolution per
minute) of the rotating wheel. A small magnet is attached in the edge of the wheel.
Whenever this magnet is exactly below the Magnetic sensor its output becomes high.
This output is connected to the T0CKI pin of the MCU. The T0CKI stands for Timer0
Clock Input Pin. So each time the magnet passes by the sensor the timer register inside
the MCU is incremented. You can note one this that all this happens without the help of
CPU. CPU can do other task and read the Timer register only when required.

Overflow

An overflow occurs when a timer register has already counted the maximum
value it can count. At overflow the counter value become 0 again. For example when an 8
bit timer has the value 255 and receives another clock that will set it to 0 and generate an
overflow. An overflow can trigger an interrupt and the ISR can handle it.

Using TIMER0 of PIC18F4520

PIC18F4520 has four different timers. The simplest is the TIMER0 so we will learn
how to use it first. In this example the TIMER0 is configured as follows.

• 8 Bit Mode
• Clock Source from Prescaler
• Prescaler = FCPU/256 (Note: FCPU= Fosc/4)
• Over flow INT enabled

Our FCPU=20MHz/4 (We are running from 20MHz XTAL)


=5MHz

Time Period = 0.2uS


Prescaler Period = 0.2 x 256 = 51.2uS (Prescaler is set to divide frequency by 256)
Overflow Period = 51.2 x 256 = 13107.2 uS (Each over flow takes 256 counts)
= 0.0131072 sec

So we need 1/0.0131072 Overflows to count for 1 sec


= 76.2939 Overflows

For that we maintain a counter to keep track of overflows.

When an over flow occurs the PIC jumps to ISR, where we increment the counter.
And when counter becomes 76 we toggle RB1 pin. This pin is connected to LED.
Therefore we have a LED which is ON for 1 sec and Off for 1sec.

Simple Program to Learn Operation of PIC TIMER0 in HI-TECH C


and MPLAB
/*****************************************************************

A simple example to demonstrate the use of PIC Timers. In this

example the TIMER0 is configured as follows.

*8 Bit Mode
*Clock Source from Prescaler
*Prescaler = FCPU/256 (Note: FCPU= Fosc/4)
*Over flow INT enabled

As our FCPU=20MHz/4 (We are running from 20MHz XTAL)


=5MHz

Time Period = 0.2uS


Prescaller Period = 0.2 x 256 = 51.2uS
Overflow Period = 51.2 x 256 = 13107.2 uS
= 0.0131072 sec

So we need 1/0.0131072 Over Flows to count for 1 sec


= 76.2939 Overflows

So we keep a counter to keep track of overflows.

When an over flow occurs the PIC jumps to ISR where we


increment counter. And when counter becomes 76 we toggle
RB1 pin. This pin is connected to LED. Therefore we
have a LED which is ON for 1 sec and Off for 1sec.

Target Chip: PIC18F4520


Target Compiler: HI-TECH C For PIC18 (http://www.htsoft.com/)
Project: MPLAP Project File
Author: Avinash Gupta
Copyright (c) 2008-2010
eXtreme Electronics, India
www.eXtremeElectronics.co.in

NOTICE
-------------
NO PART OF THIS WORK CAN BE COPIED, DISTRIBUTED OR PUBLISHED WITHOUT A

WRITTEN PERMISSION FROM EXTREME ELECTRONICS INDIA. THE LIBRARY, NOR ANY
PART
OF IT CAN BE USED IN COMMERCIAL APPLICATIONS. IT IS INTENDED TO BE USED
FOR
HOBBY, LEARNING AND EDUCATIONAL PURPOSE ONLY. IF YOU WANT TO USE THEM
IN
COMMERCIAL APPLICATION PLEASE WRITE TO THE AUTHOR.

*****************************************************************/

#include <htc.h>

//Chip Settings
__CONFIG(1,0x0200);
__CONFIG(2,0X1E1F);
__CONFIG(3,0X8100);
__CONFIG(4,0X00C1);
__CONFIG(5,0XC00F);

unsigned char counter=0;//Overflow counter

void main()
{
//Setup Timer0
T0PS0=1; //Prescaler is divide by 256

T0PS1=1;
T0PS2=1;

PSA=0; //Timer Clock Source is from Prescaler

T0CS=0; //Prescaler gets clock from FCPU (5MHz)

T08BIT=1; //8 BIT MODE

TMR0IE=1; //Enable TIMER0 Interrupt


PEIE=1; //Enable Peripheral Interrupt

GIE=1; //Enable INTs globally

TMR0ON=1; //Now start the timer!

//Set RB1 as output because we have LED on it

TRISB&=0B11111101;
while(1); //Sit Idle Timer will do every thing!
}

//Main Interrupt Service Routine (ISR)


void interrupt ISR()
{
//Check if it is TMR0 Overflow ISR

if(TMR0IE && TMR0IF)


{
//TMR0 Overflow ISR
counter++; //Increment Over Flow Counter

if(counter==76)
{
//Toggle RB1 (LED)

if(RB1==0)
RB1=1;
else
RB1=0;

counter=0; //Reset Counter

//Clear Flag
TMR0IF=0;
}
}

The program begins by Setting up TIMER0. The TIMER0 can be configured


using a single register called T0CON which stands for TIMER0 control register. The
details of each bit are given below.

Name TMR0ON T08BIT T0CS T0SE PSA PS2 PS1 PS0


Initial Value 1 1 1 1 1 1 1 1
BIT 7 6 5 4 3 2 1 0

BIT7 - TMR0ON: Timer0 On, set this to 1 to start the timer.

BIT6 - T08BIT: =1 for 8 bit mode and =0 for 16 bit mode.

BIT5 - T0CS: Timer0 clock source. =1 for T0CLK pin input i.e. counter mode. Set to 0
for internal instruction clock.

BIT4 - T0SE: Used in counter mode only. Please see datasheet for details.

BIT3 - PSA: Prescaler Assignment. Set this to 0 to assign prescaler or 1 to by pass it.
BIT2 to BIT0 - PS2 to PS0: Prescaler Division factor select

PS2 PS1 PS0


0 0 0 Divide by 2
0 0 1 Divide by 4
0 1 0 Divide by 8
0 1 1 Divide by 16
1 0 0 Divide by 32
1 0 1 Divide by 64
1 1 0 Divide by 128
1 1 1 Divide by 256

So as per our requirement we have set the bits of T0CON register on program
startup. This configures the TIMER0 as we require. Lets have a look at the code that
initializes the TIMER0

//Setup Timer0
T0PS0=1; //Prescaler is divide by 256
T0PS1=1;
T0PS2=1;
PSA=0; //Timer Clock Source is from Prescaler
T0CS=0; //Prescaler gets clock from FCPU (5MHz)
T08BIT=1; //8 BIT MODE
TMR0IE=1; //Enable TIMER0 Interrupt
PEIE=1; //Enable Peripheral Interrupt
GIE=1; //Enable INTs globally
TMR0ON=1; //Now start the timer!
//Set RB1 as output because we have LED on it
TRISB&=0B11111101;
while(1); //Sit Idle Timer will do every thing!

As you can see in the above code, first we set up the bits of T0CON then we
enable Timer Overflow interrupt, after that peripheral interrupt and finally enable
interrupt globally. PEIE i.e. Peripheral Interrupt bit can enable or disable all peripheral
interrupt. GIE or Global Interrupt bit is the master bit that can disable all interrupts.

Then when set PB1 i/o pins as output so that it can control a LED. The LED is
connected to MCU by using series resistor to GND. Finally we enter an infinite loop that
does nothing. As our timer is set up and running each times it overflows we increment
counter by one. When counter becomes 76 we toggle the RB1 pin and reset the counter
variable.

In HI-TECH C you can create an ISR (that is Interrupt service routine) by using
the keyword interrupt. The ISR function can have any valid C function name. Its
argument should be void and the return type should also be void. In our example we have
named it "ISR".
void interrupt ISR()

Whenever TIMER0 overflow the CPU stops it current operation and jumps to the
ISR. This is called interrupt(or exception) condition. As soon as it enters the ISR we
check if it is the TIMER0 overflow interrupt by checking the TMR0IF (Timer0 interrupt
flag). We also check if the interrupt is enabled (by checking TMR0IE i.e. Timer0
interrupt enable bit). If both the conditions are met we execute the TIMER0 Overflow
ISR Code.

Schematic for Testing TIMER0 of PIC18F4520

Schematic For Testing TIMER0 of PIC18

The circuit is described in this article.

Running Demo On 40 PIN PIC Development Board

The PIC development board from eXtreme Electronics has all core circuit
required in most PIC projects. So we can use it to easily test the above demo program.
All you need to do is to short jumper J5, this will connect the LED to RB1 that's it!

PIC18 Sample Code Downloads

• timer0.c the above sample code file.

Using Analog to Digital Converter (ADC) – PIC Microcontroller Tutorial


Many electrical signals around us are Analog in nature. That means a quantity
varies directly with some other quantity. The first quantity is mostly voltage while that
second quantity can be anything like temperature, pressure, light, force or acceleration.
For example in LM35 temperature sensor the output voltage varies according to the
temperature, so if we could measure voltage, we can measure temperature.

But most of our computer (or Microcontrollers) are digital in nature. They can
only differentiate between HIGH or LOW level on input pins. For example if input is
more than 2.5v it will be read as 1 and if it is below 2.5 then it will be read as 0 (in case
of 5v systems). So we cannot measure voltage directly from MCUs. To solve this
problem most modern MCUs have an ADC unit. ADC stands for analog to digital
converter. It will convert a voltage to a number so that it can be processed by a digital
system like MCU.

This enables us to easily interface all sort of analog devices with MCUs. Some really
helpful examples of analog devices are

1. Light Sensors.
2. Temperature Sensors.
3. Accelerometers.
4. Touch Screens.
5. Microphone for Audio Recording.

And possibly many more.

In this tutorial we will learn to use the internal ADC of PIC18 devices (Example
is for PIC18F4520 which is a 40 PIN device).

Specifications of ADCs

Most important specification of ADCs is the resolution. This specifies how


accurately the ADC measures the analog input signals. Common ADCs are 8 bit, 10 bit
and 12 bit. For example if the reference voltage(explained latter) of ADC is 0 to 5v then a
8 bit ADC will break it in 256 divisions so it can measure it accurately up to 5/256 v=
19mV approx. While the 10 bit ADC will break the range in 5/1024 = 4.8mV approx. So
you can see that the 8 bit ADC can't tell the difference between 1mV and 18mV. The
ADC in PIC18 are 10 bit.

Other specification include (but not limited to) the sampling rate, that means how fast the
ADC can take readings. Microchip claims that pic18f4520's ADC can go as high as 100K
samples per second.

ADC Terminology

Reference Voltage: The reference voltage specifies the minimum and maximum
voltage range of analog input. In PIC 18 there are two reference voltage, one is the Vref-
and one is Vref+. The Vref- specifies the minimum input voltage of analog input while
the Vref+ specifies the maximum. For example if the input signal Vref- is applied to
analog input channel then the result of conversion will be 0 and if voltage equal to Vref+
is applied to the input channel the result will be 1023 (max value for 10bit ADC).

Fig.: ADC Reference Voltage.

The Vref+ and Vref- pins are available in PIN5 and PIN4 of the PIC18F4520
chip. So you can connect the reference voltage here. For a simple design the Vref- is
GND and Vref+ is Vcc. As this is such a common configuration that the ADC can be set
up to use these reference internally. Therefore you do not need to connect these on the
external Vref pins, so you can use them for other purpose.

ADC Channels: The ADC module is connected to several channels via a


multiplexer. The multiplexer can connect the input of the ADC to any of the available
channels. This allows you to connect many analog signals to the MCU (say 3 temperature
sensors). In PIC18F4520 there are 13 analog input channels, they are named AN0, AN1
etc. You can have a look at the pin configuration in the pic18f4520's datasheet to locate
their position.

Acquisition Time: When an specific channel is selected the voltage from that
input channel is stored in an internal holding capacitor. It takes some time for the
capacitor to get fully charged and become equal to the applied voltage. This time is called
acquisition time. The PIC18F4520's ADC provides a programmable acquisition time, so
you can setup the acquisition time. Once acquisition time is over the input channel is
disconnected from the source and the conversions begin. The acquisition times depends
on several factors like the source impedance, Vdd of the system and temperature. You
can refer to the page 227 and 228 in the datasheet for details on its calculation. A safe
value is 2.45uS, so acquisition time must be set to any value more than this.

ADC Clock: ADC requires a clock source to do its conversion, this is called ADC
Clock. The time period of the ADC Clock is called TAD. It is also the time required to
generate 1 bit of conversion. The ADC requires 11 TAD to do a 10 bit conversion. It can
be derived from the CPU clock (called TOSC) by dividing it by a suitable division factor.
There are Seven possible option.

• 2 x TOSC
• 4 x TOSC
• 8 x TOSC
• 16 x TOSC
• 32 x TOSC
• 64 x TOSC
• Internal RC

For Correct A/D Conversion, the A/D conversion clock (TAD) must be as short as
possible but greater than the minimum TAD . See table 26-25 in PIC18F4520 datasheet (or
table 28-29 in PIC18F4550/PIC18F2550 datasheet). It is 0.7uS for PIC18FXXXX device
and 1.4uS for PIC18LFXXXX device.

We are running at 20MHz in our PIC Development board so we set prescaler of 32


TOSC.

Our FOSC = 20MHz


Therefore our FOSC = 1/20MHz
= 50nS

32 TOSC = 32 x 50 nS
= 1600nS
= 1.6uS

1.6 uS is more than the minimum requirement.

You can calculate the value for division factor using the above example in case you
are using crystal of other frequency. Also now we have the TAD we can calculate the
division factor for acquisition time. Acquisition time can be specified in terms of TAD. It
can be set to one of the following values.

• 20 x TAD
• 16 x TAD
• 12 x TAD
• 8 x TAD
• 6 x TAD
• 4 x TAD
• 2 x TAD
• 0 x TAD

As we saw in above paragraph that the safe acquisition time is 2.45uS, so we select 2 x
TAD as acquisition time.
TACQ=2 x TAD
=2 x 1.6uS (Replacing TAD= 1.6uS)
=3.2uS

3.2uS is more than required 2.45uS so its ok.

Programming ADC in HI-TECH C for MPLAB

ADC is connect to the PIC CPU by 3 control register and 2 data register. The
control registers are used to setup and give commands to the ADC. They also provides
the status of ADC. The two data registers holds the 10 bit of converted data. Since each
resister in PIC18 is of 8 bits therefore 2 registers are required to hold the 10bit data.

We will develop two functions to support ADC in our projects. One will help initialize
the module and other will help us select any of the 13 channels and start the conversion.
After the conversion is done it will return us the results.

I am not giving here the description of the control and data registers as they are very
clearly explained in PIC18F4520's datasheet on page 223 to 225. I request you to
download the datasheet and read the description so that you will have an Idea of what
every bit in the registers do. As I told before, ADC is connected to the CPU via three
control register and two data registers. The three control registers are :-

• ADCON0 - Used to select analog input channel,start the conversion, check if the
conversion is done and to switch on/off the module.(We use this in ADCRead()
function.)
• ADCON1 - Used to Select Voltage reference, and to configure ports as Analog of
digital. (We leave these to defaults)
• ADCON2 - Used to select ADC data format, Set acquisition time, ADC clock
setup (We setup these in ADCInit() function)

First we configure the ADC to our needs in the ADCInit() function.

//Function to Initialise the ADC Module


void ADCInit()
{
//We use default value for +/- Vref

//VCFG0=0,VCFG1=0
//That means +Vref = Vdd (5v) and -Vref=GEN

//Port Configuration
//We also use default value here too
//All ANx channels are Analog

/*
ADCON2

*ADC Result Right Justified.


*Acquisition Time = 2TAD
*Conversion Clock = 32 Tosc
*/

ADCON2=0b10001010;
}

You can see that we only set up the ADCON2 register.We setup the ADC as follows

• ADC Result format as Right Justified(Explained latter).


• Acquisition time = 2TAD(As Calculated Above)
• Conversion Clock as 32 TOSC(As Calculated Above)

We also leave ADCON1 to defaults, which implies the following

• +VREF is 5v (Our Vcc)


• -VREF is GND
• All ANx channels are Analog. If you need some of them to do digital I/O then
setup them accordingly.

Now we have our ADC Module setup, when ever you want to do the ADC Conversion in
any channel, simply call ADCRead(). For example to do ADC Conversion on channel 0
and store the result in variable val call the function in the following way.

val=ADCRead(0);

That's it ! the analog value present on AN0 will be converted to a digital value and stored
in variable val.

How the ADCRead() function works?

//Function to Read given ADC channel (0-13)


unsigned int ADCRead(unsigned char ch)
{
if(ch>13) return 0; //Invalid Channel

ADCON0=0x00;

ADCON0=(ch<<2); //Select ADC Channel

ADON=1; //switch on the adc module

GODONE=1; //Start conversion

while(GODONE); //wait for the conversion to finish

ADON=0; //switch off adc

return ADRES;
}
The first line checks if the input channel provided by the user is valid or not. Then
we select ADC channel. After that we switch on the module by setting ADON bit. Then
conversion is started by setting the GODONE bit. As soon as the GODONE bit is set to
1 the module starts the conversion process. As long as the module is busy the GODONE
bits is HIGH, and when the conversion is complete it is cleared by the module. So we
wait in the while loop as long as GODONE is high. Remember that the while loop is
empty (a semi colon just after it), so as long as GODONE is high the CPU will do
nothing. As soon as GODONE is cleared the while loop breaks and we switch of the
module by writing 0 to the ADON bit. Finally the result of conversion is returned,
ADRES register holds the converted value.

Demo program to test PIC ADC Code

We will write a very simple program that will demonstrate ADC usage. The program
will read ADC channel 0 (AN0 PIN) and display its value on LCD Screen. Before
attempting the experiment please read the following tutorials. As you will need the LCD
Support in addition to the ADC Interface code.

• LCD Interfacing with PIC MCU


• Making the LCD Expansion board

The program is intended to be compiled using the HI-TECH C for PIC18 using the
MPLAB IDE. So if you are not familiar with the build steps please see the following
tutorials.

• Downloading, Configuring and Using Basic Development Tools for Microchip


PIC Series of MCUs.

/********************************************************************

ANALOG TO DIGITAL CONVERTOR INTERFACING TEST PROGRAM

---------------------------------------------------------
Simple Program to connect with the internal ADC of PIC MCUs.
The program reads and display the analog input value at AN0.
Requires the PIC18 lcd library.

MCU: PIC18FXXXX Series from Microchip.


Compiler: HI-TECH C Compiler for PIC18 MCUs (http://www.htsoft.com/)

Copyrights 2008-2010 Avinash Gupta


eXtreme Electronics, India

For More Info visit


http://www.eXtremeElectronics.co.in

Mail: me@avinashgupta.com

********************************************************************/
#include <htc.h>
#include "lcd.h"

//Chip Settings
__CONFIG(1,0x0200);
__CONFIG(2,0X1E1F);
__CONFIG(3,0X8100);
__CONFIG(4,0X00C1);
__CONFIG(5,0XC00F);

//Simple Delay Routine


void Wait(unsigned int delay)
{
for(;delay;delay--)
__delay_us(100);
}

//Function to Initialise the ADC Module


void ADCInit()
{
//We use default value for +/- Vref

//VCFG0=0,VCFG1=0
//That means +Vref = Vdd (5v) and -Vref=GEN

//Port Configuration
//We also use default value here too
//All ANx channels are Analog

/*
ADCON2

*ADC Result Right Justified.


*Acquisition Time = 2TAD
*Conversion Clock = 32 Tosc
*/

ADCON2=0b10001010;
}

//Function to Read given ADC channel (0-13)


unsigned int ADCRead(unsigned char ch)
{
if(ch>13) return 0; //Invalid Channel

ADCON0=0x00;

ADCON0=(ch<<2); //Select ADC Channel

ADON=1; //switch on the adc module

GODONE=1;//Start conversion

while(GODONE); //wait for the conversion to finish

ADON=0; //switch off adc


return ADRES;
}
void main()
{
//Let the LCD Module start up
Wait(100);

//Initialize the LCD Module


LCDInit(LS_BLINK);

//Initialize the ADC Module

ADCInit();

//Clear the Module


LCDClear();

//Write a string at current cursor pos


LCDWriteString("ADC Test");

while(1)
{
unsigned int val; //ADC Value

val=ADCRead(0); //Read Channel 0


LCDWriteIntXY(0,1,val,4);

Wait(1000);
}

Basic PIC Hardware to test the ADC Code.

The following image shows the basic hardware required to run the above code. Note that
the crystal frequency is 20MHz, so please use this value only, if you want quick and error
free operation. Or if you are experienced enough you can sort out errors if any.
Fig.: Schematic for PIC ADC Test. (Click To Enlarge/Print)

If you are using our 40 PIN PIC Development Board then most of the hardware is
already done for you. You just need to make and attach the LCD Expansion Board to the
main Dev Board. RV2, which is a 10K variable resistor is used to feed variable voltage
between 0 and 5v to the analog input PIN. When the demo is running you can adjust RV2
can note the change is ADC read out on the LCD Module.

Interfacing LM35 Temperature Sensor with PIC Microcontroller.

The are many cool sensors available now a days, ranging from IR distance sensor
modules, accelerometers, humidity sensors, temperature sensors and many many
more(gas sensors, alcohol sensor, motion sensors, touch screens). Many of these are
analog in nature. That means they give a voltage output that varies directly (and linearly)
with the sensed quantity. For example in LM35 temperature sensor, the output voltage is
10mV per degree centigrade. That means if output is 300mV then the temperature is 30
degrees. In this tutorial we will learn how to interface LM35 temperature sensor with
PIC18F4520 microcontroller and display its output on the LCD module.

First I recommend you to go and read the following tutorial as they are the base of this
small project.

• Interfacing LCD Module with PIC Microcontrollers.


• Making the LCD Expansion Board for PIC18F4520.
• Using the ADC of PIC Microcontrollers.
After reading the ADC tutorial given above you will note the the PIC MCU's ADC gives
us the value between 0-1023 for input voltage of 0 to 5v provided it is configured exactly
as in the above tutorial. So if the reading is 0 then input is 0v, if reading is 1023 then
input is 5v. So in general form if the adc read out is val then voltage is.

unsigned int val;


val=ADCRead(0); //Read Channel 0
voltage= ((val)/1023.0)*5;

The above formula give voltage in Volts, to get Voltage in mili Volts (mV) we must
multiply it with 1000, so

voltage=((val)/1023.0)*5*1000); //Voltage is in mV

since 10mV = 1 degree, to get temperature we must divide it by 10, so

t=((val)/1023.0)*5*100); //t is in degree centigrade

simplifying further we get

t=((val/1023.0)*500);
t=(val*0.48876);

we round off this value, so

t=round(val*0.48876);

remember round() is a standard c library function

Hardware for LM35 based thermometer.

You will need a PIC18F4520 chip running at 20MHz attached with a standard
16x2 LCD Module and LM35 on AN0 pin. LM35 is a 3 pin device as show below.
Fig.: LM35 Temperature Sensor Pinout

connect the +Vs Pin to 5v and GND to GND. The output must be connected to the analog
input pin 0 of the PIC18F4520 MCU. It is labeled AN0 in the datasheet. It is pin number
2 on the 40 pin package. It is also called RA0 because it is shared with PORTA0.

We will use our 40 PIN PIC Development board to realize the project. The base
board has all the basic circuit to run the PIC. The extra part required for this project like
LCD and the LM35 temperature sensor are installed in the expansion board.

C Source Code For PIC Thermometer Project.


/********************************************************************

LM35 Temperature Sensor INTERFACING TEST PROGRAM

---------------------------------------------------------
Simple Program to connect with LM temperature sensor using the
internal ADC of PIC MCU.

The program displays the current environment temperature on


LCD Module.

MCU: PIC18FXXXX Series from Microchip.


Compiler: HI-TECH C Compiler for PIC18 MCUs (http://www.htsoft.com/)

Copyrights 2008-2010 Avinash Gupta


eXtreme Electronics, India

For More Info visit


http://www.eXtremeElectronics.co.in
Mail: me@avinashgupta.com

********************************************************************/
#include <htc.h>

#include <math.h>

#include "lcd.h"

//Chip Settings
__CONFIG(1,0x0200);
__CONFIG(2,0X1E1F);
__CONFIG(3,0X8100);
__CONFIG(4,0X00C1);
__CONFIG(5,0XC00F);

//Simple Delay Routine


void Wait(unsigned int delay)
{
for(;delay;delay--)
__delay_us(100);
}

//Function to Initialise the ADC Module


void ADCInit()
{
//We use default value for +/- Vref

//VCFG0=0,VCFG1=0
//That means +Vref = Vdd (5v) and -Vref=GEN

//Port Configuration
//We also use default value here too
//All ANx channels are Analog

/*
ADCON2

*ADC Result Right Justified.


*Acquisition Time = 2TAD
*Conversion Clock = 32 Tosc
*/

ADCON2=0b10001010;
}

//Function to Read given ADC channel (0-13)


unsigned int ADCRead(unsigned char ch)
{
if(ch>13) return 0; //Invalid Channel

ADCON0=0x00;

ADCON0=(ch<<2); //Select ADC Channel

ADON=1; //switch on the adc module


GODONE=1;//Start conversion

while(GODONE); //wait for the conversion to finish

ADON=0; //switch off adc

return ADRES;
}
void main()
{
//Let the LCD Module start up
Wait(100);

//Initialize the LCD Module


LCDInit(LS_BLINK);

//Initialize the ADC Module

ADCInit();

//Clear the Module


LCDClear();

//Write a string at current cursor pos


LCDWriteString("LM35 Test");
LCDWriteStringXY(4,1,"Degree Celcius");

while(1)
{
unsigned int val; //ADC Value

unsigned int t; //Temperature

val=ADCRead(0); //Read Channel 0

t=round(val*0.48876);//Convert to Degree Celcius

LCDWriteIntXY(0,1,t,3);//Prit IT!

Wait(1000);
}

To compile the above code, lcd.c file must be added to the poject. While the lcd.h,
myutils.h must be present in the same project folder. More instruction is available in
following articles.
PIC18F4520 based Thermometer using LM35 Schematic

PIC18F4520 based Thermometer using LM35 Schematic

General Notes

• For proper working use the components of exact values as shown above.
• Wherever possible use new components.
• Solder everything in a clean way. Major problems arises due to improper
soldering,solder jumps and loose joints.
• Use the exact value crystal shown in schematic.
• Only burning the HEX file to the MCU is NOT enough. PIC18 devices are fairly
complex MCU and can be configured in various ways. Chip is configured using
the CONFIG Bytes. Although all hex file given in our site comes with embedded
CONFIG bytes. But the user must ensure they are programmed to the chip. Any
good programmer has the capability to read the configuration information from
the hex file and transfer it to the MCU. Programs will not run without proper
configuration byte programming. One major job of configuration is to setup
proper oscillator and PLL modes without which the MCU won't execute a single
instruction.
• To compile the above code you need the HI-TECH C and MPLAB IDE. They
must be properly set up and a project with correct settings must be created in
order to compile the code. So I request you to read the following articles to
become familiar with the built steps.
o Setting up HI-TECH C and MPLAB IDE
o Creating First C project in MPLAB
• To understand the code you must have good knowledge of core C language.
Please don't be confused with the basic concept of the language.
• You must be familier with project concept and multi source file concept that used
used in most professional languages like C.
• You need Proteus VSM if you want to develop or debug the project without any
hardware setup.

Interfacing LCD Modules with PIC Microcontrollers.

A large number of embedded project require some type of user interface. This
includes displaying numerical, textual and graphical data to user. For very simple
numerical display we can use 7 segment displays. If the requirement is little more than
that, like displaying some alphanumeric text, we can use LCD Modules. They are cheap
enough to be used in low cost projects. They come in various sizes for different
requirement. A very popular one is 16x2 model. It can display 2 lines of 16 characters.
Other models are 16x4,20x4, 8x1,8x2 etc.

In this tutorial we will learn how we can use such modules with Microchip PIC
Microcontrollers. Here I will present my LCD library which you can use to create LCD
based application/projects quickly. For demo I will use PIC18F4520 Microcontroller but
you can use any PIC18 MCU. But you have to calculate the CONFIG values for correct
setting and CPU clock selection etc. That means the chip should be configured correctly.
See datasheet for more info on CONFIG bytes.

MPLAB Project Creation

First create a MPLAB project as described in this tutorial. Name the project LCD.
Also add a main file called "lcd_test.c". To use my LCD library you need to add it to
your project. Just copy/paste the following files to your project folder.

Header Files

• lcd.h
• myutils.h

Source File

• lcd.c

How to add files to MPLAB Project is described here.

Now you are ready to the library functions to interact with the LCD module.

Sample Program (lcd_test.c)


/********************************************************************
16X2 ALPHANEUMERIC LCD INTERFACING LIBRARY TEST PROGRAM

---------------------------------------------------------

A testing program for our LCD library.

Easy to use library for interfacing 16x2 lcd in 4 bit mode.


MCU: PIC18FXXXX Series from Microchip.
Compiler: HI-TECH C Compiler for PIC18 MCUs (http://www.htsoft.com/)

Copyrights 2008-2009 Avinash Gupta


eXtreme Electronics, India

For More Info visit


http://www.eXtremeElectronics.co.in

Mail: me@avinashgupta.com

********************************************************************/
#include <htc.h>

#include "lcd.h"

//Chip Settings
__CONFIG(1,0x0200);
__CONFIG(2,0X1E1F);
__CONFIG(3,0X8100);
__CONFIG(4,0X00C1);
__CONFIG(5,0XC00F);

//Simple Delay Routine


void Wait(unsigned int delay)
{
for(;delay;delay--)
__delay_us(100);
}

void main()
{
//Let the Module start up

Wait(100);

//Initialize the LCD Module


LCDInit(LS_BLINK);

//Clear the Module


LCDClear();

//Write a string at current cursor pos


LCDWriteString("Good Is Great !!");

Wait(20000);

//Now Clear the display


LCDClear();

LCDWriteString("God Bless all !!");

//Goto POS (X=0,Y=1 i.e. Line 2)


//And Write a string
LCDWriteStringXY(0,1,"<**************>");

Wait(20000);

//Write Some Numbers

for(char i=0;i<100;i++)
{
LCDClear();
LCDWriteInt(i,3);
Wait(3000);
}

LCDClear();
LCDWriteString(" The End ");

//Loop Forever

while(1);

The First thing you should do is to Initialize the LCD Module by calling the fuction
LCDInit(). This will setup the LCD module. Now you can use the various functions
like...

• LCDClear() - For Clearing the screen.


• LCDWriteString(unsigned char *) - For Writing Text Message at Current
cursor position.
• LCDWriteStringXY(x,y,msg) - For Writing Text Message at position x,y
• LCDWriteInt() - For printing integer numbers at Current cursor position.
• LCDWriteIntXY() - For printing integer numbers at position x,y.

For detailed information on these functions please refer to this tutorial. It is for the AVR
microcontrollers but the functions are same for PIC mcus also.

Hardware Connections

Connect the LCD Module with PIC Microcontroller as shown below.


Fig.: LCD Module Interface with PIC Microcontroller.

Programming in C – Tips for Embedded Development.

Here I will highlight some features of C language commonly used in 8 bit


embedded platforms like 8051, AVR and PICs. While programming microcontrollers in
C most of the time we have to deal with registers. Most common tasks are setting and
clearing bits in a register and check whether a bit is 0 or 1 in a given register. So here I
will give detail on those topics, it will help you if you are new to embedded programming
in C and if you get confused when you see some codes.

A Register

A register is simply a collection of some bits (mostly 8 bits in case of 8bit


MCUs). Either each different bit in a register has some purpose or the register as a whole
holds a value. Registers serves as connection between a CPU and a Peripheral device
(like ADC or TIMER). By modifying the register the CPU is actually instructing the
PERIPHERAL to do something or it is configuring it in some way. And by reading a
register, the CPU can know the state of peripheral or read associated data.
Fig.: CPU writing to Peripheral Register

Fig.: CPU Reading from Peripheral Register

Binary Numbers in C

When you write a=110; in C it means you are setting the value of variable"a" to
"one hundred and ten" (in decimal). Many time in embedded programming we are not
interested in the value of a variable but the state of each bits in the variable. Like when
you want to set the bits of a register (MYREG) to a bit pattern like 10010111 (binary).
Then you cannot write MYREG=10010111. Because compiler will interpret 10010111 as
decimal. To specify a binary number in C program you have to prefix it with 0b (zero
followed by b). So if you write

MYREG=0b10010111;

it assigns the bit pattern 10010111 to the bits of Register MYREG.

HEX Numbers in C

In same way if you prefix a number by 0x (a zero followed by x) then compiler interpret
it like a HEX number. So

MYREG=0x10; (10 in HEX is 16 in decimal)

MYREG=0xFF;(Set all bits to 11111111 or decimal 255)

Setting a BIT in Register

Here our aim is to set (set to logical 1) any given bit (say bit 5) of a given register (say
MYREG). The syntax is

MYREG=MYREG | 0b00100000;

The above code will SET bit 5 to 1 leaving all other bits unchanged. What the above code
does is that it ORs each Bit of MYREG with each bit of 0b00100000 and store the value
back in MYREG. If you know how logical OR works then you will get it.

In short you can write the same code as

MYREG|=0b00100000;

Now lets come to practical usage. In practice each bit has got a name according to its
work/function. Say our BIT (the 5th bit) has got name ENABLE, and what it does is clear
by its name,when we set it to 1 it enables the peripheral and when cleared (0) it disables
it. So the right way to set it is.

MYREG|=(1<<ENABLE);

The << is called left shift operator. It shifts the bits of LHS variable left by the amount
on its RHS variable. If you write

b=1<<3;

then, 1 whose binary value is 00000001 is shifted 3 places to left which results in
00001000
So if ENABLE is defined as 5 (as enable is 5th bit) then

MYREG|=(1<<ENABLE);

will result in

MYREG|=(1<<5);

which again result in

MYREG|=(0b00100000);

Now a beginner would ask "What's the Advantage ?". And once you know it you would
realize that advantage is immense!

1. Readability of code: MYREG|=(1<<ENABLE); gives a clue that we are enabling


the peripheral while MYREG|=0b00100000; does not give any clue what it is
doing, we have to go to data sheet and find out which bit actually ENABLEs the
peripheral. While ENABLE=5 is already defined in header files by the developer
of compiler by carefully studying the datasheets of device.
2. Easier Portability: Suppose you use this code many times in your program (and
your program is reasonably large and uses other register also) and you now want
the same code to run on some other MCU model. The new MCU is of similar
family but has slightly different bit scheme, say ENABLE is bit 2 instead of bit 5.
Then you have to find all occurrence of MYREG|=(0b00100000); and change that
to MYREG|=(0b00000100); But if you have used the other method then you
simply need to inform the compiler (by its setting options) that you are going to
use the other MCU and compiler will automatically get the definitions for the new
device. And in this definition ENABLE=2 will already be defined by the compiler
developer. So it will be lot easier.

Clearing a BIT in Register

For clearing a bit logical AND(symbol &) operator is used in place of logical OR
(symbol |). The syntax is as follows

MYREG&=~(1<<ENABLE);
Fig.: How to clear (0) a bit in C language.

This will clear (i.e. set to value 0) a given bit (identified by name ENABLE) in a register
called MYREG. This operation will not affect any other bits of register except ENABLE.

Let us see how it works with the help of following diagram.


Fig.: "Clearing a BIT" how it works?

o now you know how you can selectively clear any bit in any given register. If you want
to clear more than one bit at a time you can write like this

//This will clear bits ENABLE,FAST_MODE and BUSY, leaving all other
bits untouched
MYREG&=(~((1<<ENABLE)|(1<<FAST_MODE)|(1<<BUSY)));

Similarly the syntax for setting(set to 1) multiple bits at a time is as follows


//This will set bits ENABLE,FAST_MODE and BUSY, leaving all other bits
untouched
MYREG|=((1<<ENABLE)|(1<<FAST_MODE)|(1<<BUSY));

Testing The Status of a Bit.

Till now we were modifying the registers either setting or clearing bits. Now we
will learn how can be know that a specific bit is 0 or 1. To Know if a bit is 0 or 1 we
AND it with a AND MASK. Suppose if we want to check bit 5 of a register MYREG
then the AND MASK would be 0b00100000. If we AND this value with the current
value of MYREG then result will be non-zero only if the 5th bit in MYREG is '1' else the
result will be '0'.

The syntax would be like this.

if(MYREG & (1<<ENABLE))


{
//ENABLE is '1' in MYREG
...
}
else
{
//ENABLE is '0' in MYREG
}

General Purpose Digital IO with PIC Microcontrollers.

GPIO( General Purpose IO) is the most basic method of communication between
MCU and external world. These are done with what is called a PORT. Ports are nothing
but a set of bits physically connected to PINs of Microcontroller and available outside the
chip. As we are working on PIC micros and they are 8 bits so the maximum size of a
PORT is 8 bits. Some PORTs have fewer than 8 bits. You can turn each bit in a PORT on
and off under program control. The bits which you set as 1 becomes HIGH and the
physical PIN of Micro is at Vcc(supply voltage, usually 3.3v or 5v). And the PINs which
you clear (=0) becomes low and physical level is 0v(GND).
Fig.: PIC IO Port example, PORTB.

Fig.: PIC IO Port example, Using PORTB, Turning BITs on and off.

You can test the operation of an IO port by using LEDs as shown below.
Fig.: When the bit is set to 0 the LED remains off.

When you write a value 1 to corresponding bit the LED starts glowing.

Fig.: When the bit is set to 1 the LED starts glowing.

PORTs are Named like PORTA,PORTB,PORTC etc. The PIC we are using PIC4550 has
the following PORTs

• PORTA(8bit wide)
• PORTB(8bit wide)
• PORTC(7bit wide)
• PORTD(8bit wide)
• PORTE(4bit wide)
Inputs using PORTs

You have seen how ports can be used to control the outside word. In that we have
used the PORTs as Output. They can also be used to get input from outside. In input
mode we can apply either logic 0 (GND) or 1 (Vcc = 5v) and we can read the status of
PORT and get what input we have on ports. This can be used to interface switches and
sensor with MCU. Any value below Vcc/2 is taken as low and any voltage above Vcc/2
is high.

A simple switch interface is shown below.

Fig.: A Simple switch as Input to PIC Micro.

The CPU can read the required port and take decisions accordingly.

Practical Stuff.

Having the knowledge about digital IO Ports on PIC micro we can begin some practical
experimentations. So how can we actually access the ports in C? Well the answer
follows.

Each PORT in PIC micro has 3 associated registers.

• TRIS: This is the data direction register - As I have told you that port can be used
as both input and output purpose, the direction control (input or output) is done
using this register. Simply make the bit as 1 when you want its as input or 0 for
output.
Example:

TRISB=0xFF; //1111-1111 All Inputs


TRISB=0x00; //0000-0000 All Outputs
TRISB=0x0F;//0000-1111 Bit 7,6,5,4, as Output and Bit 3,2,1,0 as Input

Note: On power up all PORTs are Input.

To Remember this note that 1 looks like 'i' therefore Input, 0 looks line 'o' so Output

• LAT: (LATCH): This register is used to set output values. Anything you want
output on related pins must be put on this.

Example

LATB=0xFF; //All PINs becomes high


LATB=0x00; //All pins becomes Low
LATB=0x03; //0000-0011 BIT 0 and 1 becomes high.

• PORT: This is used to get input from the PORTs. Reading its value gives the
actual levels applied on the PINs. It must be noted that PORT is first configured
as input using the TRIS register. Suppose you connected a switch as shown above
on PORTB-0 then if you read PORTB it will be 0000-0000 if switch is pressed
and 0000-0001 if switch is released.

You might also like