Professional Documents
Culture Documents
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.
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.
• 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.
• 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.
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).
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
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 :-
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.
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 }
• 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.
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.
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
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.
*8 Bit Mode
*Clock Source from Prescaler
*Prescaler = FCPU/256 (Note: FCPU= Fosc/4)
*Over flow INT enabled
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);
void main()
{
//Setup Timer0
T0PS0=1; //Prescaler is divide by 256
T0PS1=1;
T0PS2=1;
TRISB&=0B11111101;
while(1); //Sit Idle Timer will do every thing!
}
if(counter==76)
{
//Toggle RB1 (LED)
if(RB1==0)
RB1=1;
else
RB1=0;
//Clear Flag
TMR0IF=0;
}
}
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
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.
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!
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.
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
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).
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.
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.
32 TOSC = 32 x 50 nS
= 1600nS
= 1.6uS
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
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)
//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
ADCON2=0b10001010;
}
You can see that we only set up the ADCON2 register.We setup the ADC as follows
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.
ADCON0=0x00;
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.
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.
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.
/********************************************************************
---------------------------------------------------------
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.
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);
//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
ADCON2=0b10001010;
}
ADCON0=0x00;
GODONE=1;//Start conversion
ADCInit();
while(1)
{
unsigned int val; //ADC Value
Wait(1000);
}
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.
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.
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
t=((val/1023.0)*500);
t=(val*0.48876);
t=round(val*0.48876);
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.
---------------------------------------------------------
Simple Program to connect with LM temperature sensor using the
internal ADC of PIC MCU.
********************************************************************/
#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);
//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
ADCON2=0b10001010;
}
ADCON0=0x00;
return ADRES;
}
void main()
{
//Let the LCD Module start up
Wait(100);
ADCInit();
while(1)
{
unsigned int val; //ADC Value
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
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.
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.
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
Now you are ready to the library functions to interact with the LCD module.
---------------------------------------------------------
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);
void main()
{
//Let the Module start up
Wait(100);
Wait(20000);
Wait(20000);
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...
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
A 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;
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
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.
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);
MYREG|=(0b00100000);
Now a beginner would ask "What's the Advantage ?". And once you know it you would
realize that advantage is immense!
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.
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)));
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'.
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.
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.
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.
• 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:
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
• 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.