PIC16f877A Document
PIC16f877A Document
PIC16f877A Document
www.pantechsolutions.net
21
1
Contents
PIC16F877A ................................................................................................................................................... 1
PIC16F877A GPIO Tutorial ............................................................................................................................ 6
PIC16F877A – DC Motor Interfacing ........................................................................................................... 15
PIC16F877A – Relay Interfacing .................................................................................................................. 18
PIC16F877A – LCD 4Bit Interfacing ............................................................................................................. 21
PIC16F877A – Keypad Interfacing ............................................................................................................... 27
PIC16F877A – Timer/Counter ..................................................................................................................... 33
PIC16F877A – USART .................................................................................................................................. 42
PIC16F877A – ADC ...................................................................................................................................... 49
PIC16F877A- I2C .......................................................................................................................................... 56
PIC16F877A – Interrupt Tutorial ................................................................................................................. 66
Serial Interrupt – PIC16F877A Interrupt Tutorial........................................................................................ 73
PIC16F877A
Introduction
2
This is the Series of tutorials on the PIC16F877A Microcontroller. The aim of this series is to
provide easy and practical examples that anyone can understand. In this article, we will see the
PIC16F877A Introduction and features.
PIC Introduction
wattTM technology.
The four features that might make you use a 16F887 instead of a 16F877 (A) are
External gate.
Volt Reference.
Nano WattTM.
Internal Clock.
The gate could be used to more accurately capture an input time e.g. for a
reciprocal frequency counter.
The volt reference means you don’t need an external reference although it will
probably not be useful for highly accurate operation. It is definitely more useful in
a battery-powered operation where you want to compare the input battery voltage
to a known reference e.g. using the comparator and the internal 0.6V reference.
Nano WattTM could be useful for battery-powered operations.
The internal clock is useful for lab development (not for accuracy) and for general
operation – it can also be set to 31kHz so consuming less power.
All the above depend on your specific application requirements.
PIC16F877A has 40 pins by 33 paths of I/O. The 40 pins make it easier to use the
peripherals as the functions are spread out over the pins. This makes it easier to
4
decide what external devices to attach without worrying too much if there are
enough pins to do the job.
One of the main advantages is that each pin is only shared between two or three
functions so it’s easier to decide what the pin function (other devices have up to 5
functions for a pin).
A slight disadvantage of the device is that it has no internal oscillator so you will
need an external crystal or another clock source. However the internal oscillator is
only 1% accurate and adding a crystal (max 20MHz crystal – for 5MHz internal
instruction cycle) and two 15pF capacitors is not a great chore – the accuracy will
be 100ppm depending on the crystal used.
Pin out
The pin out of the 16F877A is:
:
5
Register Description
TRISX This Register is used for Select that respected IO port as a input or output.
PORTX This is the IO Port
Note: Here ‘x’ could be A,B,C,D,E so on depending on the number of ports
supported by the controller.
TRISX Register
Before accessing the PORTX register, we should declare that port whether input or
output. So this register is used to select that direction. If you set 0 that IO port will
act as the output port. If you set 1 that IO port will act as an input port. Just see the
snippet below. Then you will understand.
TRISB = 0xff; // Configure PORTB as Input.
TRISC = 0x00; // Configure PORTC as Output.
TRISD = 0x0F; // Configure lower nibble of PORTD as Input and higher nibble as
Output
TRISD = (1<<0) | (1<3) | (1<<6); // Configure PD0,PD3,PD6 as Input and others
as Output
PORTX Register
This register is used to read/write the data from/to port pins. Writing 1’s to PORTx
will make the corresponding PORTx pins as HIGH. Similarly writing 0’s to
PORTx will make the corresponding PORTx pins as LOW. PORTX registers and
their alternate functions are shown in the below table.
PORT Number of Pins Alternative Function
PORTA 6 (PA0-PA5) ADC
PORTB 8 (PB0-PB7) Interrupts
PORTC 8 (PC0-PC7) UART,I2C,PWM
PORTD 8 (PD0-PD7) Parallel Slave Port
PORTE 3 (PE0-PB2) ADC
I think this is enough to make code. Let’s move into the coding part.
LED Interfacing
8
Code
In this code, I’ve connected LEDs to Port D.
#include<htc.h>
void delay()
{
unsigned int a;
for(a=0;a<10000;a++);
}
void main()
{
TRISD=0; //Port D is act as Output
while(1) {
PORTD=0xFF; //Port D ON
delay();
PORTD=0x00; //Port D OFF
delay();
}
}
Output
9
Switch Interfacing
Code
In this code, Switch is connected into Port D. LEDs are connected into Port B.
#include<htc.h>
#define sw RD0 //Switch is connected at PORTD.0
void delay()
{
unsigned int a;
for(a=0;a<10000;a++);
}
void main()
{
TRISB=0; //Port B act as Output
TRISD=0xff; //Port D act as Input
while(1) {
if(!sw) {
PORTB=0xff; //LED ON
} else
PORTB=0; //LED OFF
}
10
Pin Description
Pin Function Name
11
No
1 Ground (0V) Ground
2 Supply voltage; 5V (4.7V – 5.3V) Vcc
3 Contrast adjustment; through a variable resistor VEE
Selects command register when low; and data register
4 Register Select
when high
5 Low to write to the register; High to read from the register Read/write
6 Sends data to data pins when a high to low pulse is given Enable
7 DB0
8 DB1
9 DB2
10 DB3
8-bit data pins
11 DB4
12 DB5
13 DB6
14 DB7
15 Backlight VCC (5V) Led+
16 Backlight Ground (0V) Led-
The LCD display module requires 3 control lines as well as either 4 or 8 I/O lines
for the data bus. The user may select whether the LCD is to operate with a 4-bit
data bus or an 8-bit data bus. If a 4-bit data bus is used the LCD will require a total
of 7 data lines (3 control lines plus the 4 lines for the data bus). If an 8-bit data bus
is used the LCD will require a total of 11 data lines (3 control lines plus the 8 lines
for the data bus).
The three control lines are referred to as EN, RS, and RW.
The EN line is called “Enable.” This control line is used to tell the LCD that you
are sending it data. To send data to the LCD, your program should make sure this
line is low (0) and then set the other two control lines and/or put data on the data
bus. When the other lines are completely ready, bring ENhigh (1) and wait for the
minimum amount of time required by the LCD datasheet (this varies from LCD to
LCD), and end by bringing it low (0) again.
The RS line is the “Register Select” line. When RS is low (0), the data is to be
treated as a command or special instruction (such as clear screen, position cursor,
etc.). When RS is high (1), the data being sent is text data which should be
displayed on the screen. For example, to display the letter “T” on the screen you
would set RS high.
The RW line is the “Read/Write” control line. When RW is low (0), the
information on the data bus is being written to the LCD. When RW is high (1), the
program is effectively querying (or reading) the LCD. Only one instruction (“Get
12
LCD status”) is a read command. All others are write commands–so RW will
almost always be low.
Finally, the data bus consists of 4 or 8 lines (depending on the mode of operation
selected by the user). In the case of an 8-bit data bus, the lines are referred to as
DB0, DB1, DB2, DB3, DB4, DB5, DB6, and DB7.
LCD Commands
Code
#include<pic.h>
#define rs RC0
#define rw RC1
#define en RC2
#define delay for(j=0;j<1000;j++)
int j;
void lcd_init();
void cmd(unsigned char a);
void dat(unsigned char b);
void show(unsigned char *s);
__CONFIG( FOSC_HS & WDTE_OFF & PWRTE_OFF & CP_OFF &
BOREN_ON & LVP_OFF & CPD_OFF & WRT_OFF & DEBUG_OFF);
void main()
{
unsigned int i;
TRISB=TRISC0=TRISC1=TRISC2=0;
lcd_init();
cmd(0x8A); //forcing the cursor at 0x8A position
show("WELCOME TO Pantech");
while(1) {
for(i=0;i<15000;i++);
cmd(0x18);
for(i=0;i<15000;i++);
}
}
void lcd_init()
{
cmd(0x38);
cmd(0x0c);
14
cmd(0x06);
cmd(0x80);
}
void cmd(unsigned char a)
{
PORTB=a;
rs=0;
rw=0;
en=1;
delay;
en=0;
}
void dat(unsigned char b)
{
PORTB=b;
rs=1;
rw=0;
en=1;
delay;
en=0;
}
void show(unsigned char *s)
{
while(*s) {
dat(*s++);
}
}
Working Algorithm
Forward
EN Pin High (En1 = 1 or En2 = 1)
17
#include<pic.h>
#define in1 RB0
#define in2 RB1
#define sw RB2
Void main ()
{
TRISB0=0;
TRISB1=0;
TRISB2=1;
while(1) {
if(sw==1) {
in1=1;
in2=0;
} else {
in1=in2=0;
}
}
}
Output
18
Notes:
The maximum current capacity of L293 is 600mA/channel. So do not use a motor
that consumes more than that.
The supply voltage range of L293 is between 4.5 and 36V DC. So you can use a
motor falling in that range.
Mostly in Robotic application, we will use DC gear Motor. So the same logic is
used for that Gear motor also.
COM = Common, always connect to NC; it is the moving part of the switch.
NC = Normally Closed, COM is connected to this when the relay coil is off.
NO = Normally Open, COM is connected to this when the relay coil is on.
You can easily understand the concept of Relay by looking the below image.
Prerequisites
Before Interfacing, everyone should know about the Relay Driver which is used to
interface the relay to the microcontroller.
L293d – Relay Driver Working
Relay Interfacing with PIC16F877A
Circuit Diagram
In this tutorial, I’m connecting One Relay in Port B.0. And the switch is connected
into Port B.1.
20
Code
If you want to make Relay ON, Just give the High Signal (1) to that pin that is
connected to ULN2803. The concept of this code is just to turn On the Relay when
the button is pressed.
#include<htc.h>
#define relay RB0
#define sw RB1
void main()
{
TRISB0=0;
TRISB1=1;
while(1) {
if(sw==1){
relay=1;
} else {
relay=0;
}
}
}
Output
Introduction
8-bit mode – Using 8 data lines in LCD (totally 8 data lines are there)
4-bit mode – Using only 4 data lines in the LCD module
8-bit mode is already working and that looks awesome. Then why we are going to
4-bit mode? This is the question that comes to mind whenever I said 4-bit mode.
Yeah, that 8-bit mode is nice. But Just assume. I’m doing one project which
requires more number of hardware. But PIC16F877A has only 33 GPIOs. So in
that time I can use this 4-bit mode and reduce the pin required for the LCD
module. Am I right? Great. That’s why 4-bit mode also important. Already we
know the LED’s operation. If we want to enable 4-bit mode we have to do small
modifications in the normal method. Let’s see that.
In initializing time we have to give 0x28 commands. That’s all.
LCD Initializing
void lcd_init()
{
cmd(0x02);
cmd(0x28);
cmd(0x0e);
cmd(0x06);
cmd(0x80);
}
22
Sending command
Here everything is the same except the way of data writing. Here we have only 4
bits. So we need to send nibble by nibble. So first we need to send first nibble then
followed by the second. See that code. I’m writing into Port B’s last 4 bits.
PORTB&=0x0F;
PORTB|=(b&0xf0);
en=1;
lcd_delay();
en=0;
lcd_delay();
PORTB&=0x0f;
PORTB|=(b<<4&0xf0);
en=1;
lcd_delay();
en=0;
lcd_delay();
}
Circuit Diagram
24
Code
#include <pic.h>
__CONFIG( FOSC_HS & WDTE_OFF & PWRTE_OFF & CP_OFF &
BOREN_ON & LVP_OFF & CPD_OFF & WRT_OFF & DEBUG_OFF);
#define rs RD2
#define en RD3
void lcd_init();
void cmd(unsigned char a);
void dat(unsigned char b);
void show(unsigned char *s);
void lcd_delay();
void main()
{
unsigned int i;
TRISB=TRISD2=TRISD3=0;
lcd_init();
cmd(0x90);
25
show("www.EmbeTronicX.com");
while(1)
{
for(i=0;i<15000;i++);
cmd(0x18);
for(i=0;i<15000;i++);
}
}
void lcd_init()
{
cmd(0x02);
cmd(0x28);
cmd(0x0e);
cmd(0x06);
cmd(0x80);
}
void cmd(unsigned char a)
{
rs=0;
PORTB&=0x0F;
PORTB|=(a&0xf0);
en=1;
lcd_delay();
en=0;
lcd_delay();
PORTB&=0x0f;
PORTB|=(a<<4&0xf0);
en=1;
lcd_delay();
en=0;
lcd_delay();
}
void dat(unsigned char b)
{
rs=1;
PORTB&=0x0F;
PORTB|=(b&0xf0);
en=1;
lcd_delay();
en=0;
26
lcd_delay();
PORTB&=0x0f;
PORTB|=(b<<4&0xf0);
en=1;
lcd_delay();
en=0;
lcd_delay();
}
void show(unsigned char *s)
{
while(*s) {
dat(*s++);
}
}
void lcd_delay()
{
unsigned int lcd_delay;
for(lcd_delay=0;lcd_delay<=1000;lcd_delay++);
}
Output
27
Code
#include <pic.h>
#define rs RD2
#define en RD3
#define R1 RB0
#define R2 RB1
#define R3 RB2
#define R4 RB3
#define C1 RB4
#define C2 RB5
#define C3 RB6
#define C4 RB7
void lcd_init();
void cmd(unsigned char a);
29
void main()
{
unsigned int i;
TRISD=0;
lcd_init();
keyinit();
unsigned char b;
cmd(0x80);
show(" Enter the Key ");
while(1)
{
cmd(0xc7);
b=key();
dat(b);
}
}
void lcd_init()
{
cmd(0x02);
cmd(0x28);
cmd(0x0e);
cmd(0x06);
cmd(0x80);
}
rs=0;
PORTD&=0x0F;
PORTD|=(a&0xf0);
en=1;
lcd_delay();
en=0;
lcd_delay();
PORTD&=0x0f;
PORTD|=(a<<4&0xf0);
en=1;
lcd_delay();
en=0;
lcd_delay();
}
void lcd_delay()
31
{
unsigned int lcd_delay;
for(lcd_delay=0;lcd_delay<=1000;lcd_delay++);
}
void keyinit()
{
TRISB=0XF0;
OPTION_REG&=0X7F; //ENABLE PULL UP
}
else if(C1!=0&&C2==0&&C3!=0&&C4!=0)
colloc=1;
else if(C1!=0&&C2!=0&&C3==0&&C4!=0)
colloc=2;
else if(C1!=0&&C2!=0&&C3!=0&&C4==0)
colloc=3;
while(C1==0||C2==0||C3==0||C4==0);
return (keypad[rowloc][colloc]);
}
Output
Whatever we are typing in the keypad, it will display that character in LCD
Module.
33
PIC16F877A – Timer/Counter
This article is a continuation of the series of tutorials on the PIC16F877A
Microcontroller. The aim of this series is to provide easy and practical examples
that anyone can understand. In our previous tutorial, we have seen LED Interfacing
with PIC16F877A (GPIO). Now we will see the PIC16F877A Timer Tutorial. If
you read this tutorial, you could generate this precise delay for your applications.
PIC16F877A Timer Tutorial
As the name suggests these are used to measure the time or generate the accurate
time delay. The microcontroller can also generate/measure the required time delays
by running loops, but the timer relieves the CPU from that redundant and repetitive
task, allowing it to allocate maximum processing time for other tasks.
The timer is nothing but a simple binary counter that can be configured to count
clock pulses(Internal/External). Once it reaches the max value, it will roll back to
zero setting up an OverFlow flag and generates the interrupt if enabled.
PIC16F877a has three timers.
Timer0 (8-bit timer)
Timer1 (16-bit timer)
Timer2 (8-bit timer)
All Timers can act as a timer or counter or PWM Generation. now we will see each
one by one.
To start using a timer we should understand some of the fancy terms like 8-bit/16-
bit timer, Prescaler, Timer interrupts, and Focs. Now, let us see what each one
really means. As said earlier there are both the 8-bit and 16-bit Timers in our
PIC16F877A. The main difference between them is that the 16-bit Timer has a
much better Resolution than the 8-bit Timer.
Prescaler is a name for the part of a microcontroller that divides oscillator clock
before it will reach logic that increases timer status. The range of the Prescaler id
is from 1 to 256 and the value of the Prescaler can be set using the OPTION
Register (we will see this register later).
As the timer increments and when it reaches its maximum value of 255 (for 8-bit
timers) or 65536 (for 16-bit timers), it will trigger an interrupt and initialize itself
to 0 back again. This interrupt is called as the Timer Interrupt. This interrupt
informs the MCU that this particular time has lapped.
The Fosc stands for Frequency of the Oscillator, it is the frequency of the
Crystal used. The time taken for the Timer register depends on the value of
Prescaler and the value of the Fosc.
34
Timer 0
• The Timer0 module timer/counter has the following features:
• 8-bit timer/counter
• Readable and writable
• 8-bit software programmable Prescaler
• Internal or external clock select
• Interrupt on overflow from FFh to 00h
• Edge select for external clock
Registers used for Timer0
• OPTION_REG
• TMR0
• INTCON
OPTION_REG
We perform all the necessary settings with OPTION_REG Register. The size of
the register is 8 bits.
RBPU: PORTB Pull-up Enable bit (This bit is not used for timers)
• 1 = PORTB pull-ups are disabled
• 0 = PORTB pull-ups are enabled by individual port latch values
Note: There is only one Prescaler available which is mutually exclusively shared
between the Timer0 module and the Watchdog Timer. A Prescaler assignment for
the Timer0 module means that there is no Prescaler for the Watchdog Timer and
vice versa. This Prescaler is not accessible but can be configured
using PS2:PS0 bits of OPTION_REG.
INTCON Register
Timer0 Code
In this code LED is connected to Port B. Those LEDs are blinking every 1 second.
#include<pic.h>
void t0delay();
void main()
{
TRISB=0;
OPTION_REG=0x07; //Prescale is assigned to Timer 0, Prescaler value = 256,
Fclk = 11.0592MHz
while(1) {
PORTB=0xff;
t0delay();
PORTB=0x00;
t0delay();
}
}
void t0delay() // 1 second
{
int i;
for(i=0;i<42;i++) {
while(!T0IF);
T0IF=0;
}
}
Timer 1
The timer TMR1 module is a 16-bit timer/counter with the following features:
• 16-bit timer/counter with two 8-Bit registers TMR1H/TMR1L
• Readable and writable
• software programmable Prescaler up to 1:8
• Internal or external clock select
• Interrupt on overflow from FFFFh to 00h
• Edge select for external clock
Registers used for Timer1
• T1CON
• TMR1 (TMRIH, TMRIL)
• PIR1
38
T1CON Register
PIR1 Register
This register contains the Timer1 overflow flag. (TMR1IF).
TMR1IF – TMR1 overflow Interrupt Flag bit.
This flag marks the end of ONE cycle count. The flag needs to be reset in the
software if you want to do another cycle count. We can read the value of the
register TMR1 and write it into it. We can reset its value at any given moment
(write) or we can check if there is a certain numeric value that we need (read).
Delay Calculation for 1 second
Timer1 Code
In this code LED is connected to Port B. Those LEDs are blinking every 1 second.
#include<pic.h>
void t1delay();
void main()
{
TRISB=0;
T1CON=0x01; //Prescale value = 1:1, It using Internal clock, Timer 1 ON
while(1) {
PORTB=0xff;
t1delay();
PORTB=0;
t1delay();
}
}
void t1delay()
{
int i;
40
for(i=0;i<42;i++) {
TMR1H=TMR1L=0;
while(!TMR1IF);
TMR1IF=0;
}
}
Timer 2
• The TImer2 module is an 8-bit timer/counter with the following features:
• 8-bit timer/counter
• Readable and writable
• Software programmable Prescaler/PostScaler up to 1:16
• Interrupt on overflow from FFh to 00h
Registers used for Timer2
• T2CON
• TMR2
• PIR1
• PR2
T2CON Register
• 01 = Prescaler is 4
• 1x = Prescaler is 16
TMR2 & PR2 Register
• TMR2 – The register in which the “initial” count value is written.
• PR2 – The register in which the final or the maximum count value is written.
PIR1 Register
This register contains the Timer2 overflow flag(TMR2IF).
Delay Calculation for 1 second
Timer2 Code
In this code LED is connected to Port B. Those LEDs are blinking every 1 second.
#include<pic.h>
#include<htc.h>
void t2delay();
void main()
{
TRISB=0;
T2CON=0b01111000; //postscale=16,prescale=1,timer off
while(1)
{
PORTB=255;
t2delay();
42
PORTB=0;
t2delay();
}
}
void t2delay()
{
unsigned int i;
T2CON|=(1<<2); //timer2 on
for(i=0;i<675;i++)
{
while(!TMR2IF);
TMR2IF=0;
}
}
PIC16F877A – USART
Prerequisites
If you are new to UART please go through our previous article about UART.
• Basic UART (Serial Communication)
time which can be used to communicate with peripheral devices, such as CRT
terminals and personal computers.
Given the desired baud rate and FOSC, the nearest integer value for the SPBRG
register can be calculated using the below formula.
Calculation:
• My Fosc = 11.0592MHz (You can put your board Fosc)
• Baud Rate = 9600
• 9600 = 11059200 / ( 64X + 64)
• 64X+64 = 1152
• X = 17.
If we want to generate 9600 Baudrate (Fosc = 11.0592MHz) you have to set 17 to
SPBRG Register.
• Feed the value for baud rate that you have calculated using the above
formula to SPBRG Register
void ser_int()
{
TXSTA=0x20; //BRGH=0, TXEN = 1, Asynchronous Mode, 8-bit mode
RCSTA=0b10010000; //Serial Port enabled,8-bit reception
SPBRG=17; //9600 baudrate for 11.0592Mhz
TXIF=RCIF=0;
}
Transmit
• Load the new char to be transmitted into THR.
• Wait till the char is transmitted. TXIF will be set when the TXREG is
empty.
• Clear the TXIF for the next cycle.
void tx(unsigned char a)
{
TXREG=a;
while(!TXIF);
TXIF = 0;
}
Receive
• Wait till the Data is received. RCIF will be set once the data is received in
the RCREG register.
• Clear the receiver flag(RCIF) for the next cycle.
• Copy/Read the received data from the RCREG register.
unsigned char rx()
{
while(!RCIF);
RCIF=0;
return RCREG;
}
47
Full code
This program first transmits some strings (EmbeTronicX: Enter the letters on the
keyboard). Then it will act as an echo. Whatever we pressed on the keyboard, it
will print that in the serial terminal.
#include<htc.h>
__CONFIG( FOSC_HS & WDTE_OFF & PWRTE_OFF & CP_OFF &
BOREN_ON & LVP_OFF & CPD_OFF & WRT_OFF & DEBUG_OFF);
void ser_int();
void tx(unsigned char);
unsigned char rx();
void txstr(unsigned char *);
void main()
{
TRISC6=0; //Output (TX)
TRISC7=1; //Input (RX)
ser_int();
txstr("(Pantech): Enter the letters in keyboard\n\r\r");
while(1) {
tx(rx());
}
}
void ser_int()
{
TXSTA=0x20; //BRGH=0, TXEN = 1, Asynchronous Mode, 8-bit mode
RCSTA=0b10010000; //Serial Port enabled,8-bit reception
SPBRG=17; //9600 baudrate for 11.0592Mhz
TXIF=RCIF=0;
}
void tx(unsigned char a)
{
TXREG=a;
while(!TXIF);
TXIF = 0;
}
48
PIC16F877A – ADC
This article is a continuation of the series of tutorials on the PIC16F877A
Microcontroller. The aim of this series is to provide easy and practical examples
that anyone can understand. In our previous tutorial, we have seen PIC16F877A
USART Tutorial. Now we are going to see PIC16F877A ADC Tutorial.
PIC16F877A ADC Tutorial
Prerequisites
• LCD Interfacing with PIC16F877A
Introduction
Microcontrollers are very useful especially when it comes to communicating with
other devices, such as sensors, motors, switches, memory, and even another
microcontroller. As we all know many interface methods have been developed
over years to solve the complex problems of balancing needs of features, cost, size,
power consumption, reliability, etc. but the ADC Analog-to-Digital converter
remains famous among all. Using this ADC we can connect any type of Analog
sensor.
PIC16F877A ADC Module
The Analog-to-Digital (A/D) Converter module has eight for the 40/44-pin
devices.
The conversion of an analog input signal results in a corresponding 10-bit digital
number. The A/D module has high and low-voltage reference input that is software
selectable to some combination of VDD, VSS, RA2, or RA3.
The A/D converter has the unique feature of being able to operate while the device
is in Sleep mode. To operate in Sleep, the A/D clock must be derived from the
A/D’s internal RC oscillator.
PIC16F877A ADC Pins
ADC Channel Pin
Channel 0 RA0/AN0 (Port A)
Channel 1 RA1/AN1 (Port A)
Channel 2 RA2/AN2/VRef- (Port A)
Channel 3 RA3/AN3/VRef+ (Port A)
50
ADCS1-ADCS0: A/D Conversion Clock Select bits. These bits are based on
ADCON1 Reister’s ADCS2 bit.
A/D Result High Register (ADRESH) & A/D Result Low Register (ADRESL)
The ADRESH: ADRESL registers contain the 10-bit result of the A/D conversion.
When the A/D conversion is complete, the result is loaded into this A/D Result
register pair, the GO/DONE bit (ADCON0<2>) is cleared and the A/D interrupt
flag bit ADIF is set. The block diagram of the A/D module is shown below.
53
Steps to follow
To do an A/D Conversion, follow these steps:
1. Configure the A/D module:
• Configure analog pins/voltage reference and digital I/O (ADCON1)
• Select A/D input channel (ADCON0)
• Select A/D conversion clock (ADCON0)
• Turn on A/D module (ADCON0)
2. Configure A/D interrupt (if desired):
• Clear ADIF bit
• Set ADIE bit
• Set PEIE bit
• Set GIE bit
Programming
Whenever value changes in Potentiometer it will display in LCD Module. In this
pin, you can connect any sensors. You can download the full project here.
#include<pic.h>
55
PIC16F877A- I2C
57
Most of the PIC microcontrollers have built-in Master Synchronous Serial Port
(MSSP).
BF: This is the Buffer Full status bit. In the transmit mode this bit will set when we
write data to SSPBUF register and it is cleared when the data is shifted out. In the
receive mode, this bit will set when the data or address is received in the SSPBUF
register and it is cleared when we read the SSPBUF register.
UA: This is the Update Address bit and is used only in 10-bit address mode. It
indicates that the user needs to update the address in the SSPADD register.
R/W: This is the Read/Write bit information. In the slave mode, it indicates the
status of the R/W bit during the last address match. In the master mode, 1 indicates
that transmit is in progress and vice versa.
S: This bit indicates that a Start bit is detected last and it will be cleared
automatically during Reset.
P: As above, this bit indicates that a Stop bit is detected last and it will be cleared
automatically during Reset.
D/A: This is the data or addresses indicator bit and it is used only in slave mode. If
it is set, the last byte received was data otherwise it will be addressed.
59
CKE: Setting this bit enables SMBus specific inputs. SMBus is another bus similar
to I2C, which is compatible with each other.
SMP: Setting this bit disables slew rate control and vice versa.
SSPCON1 – MSSP Control Register 1
The SSPCON1 register is readable and writable, which is used to control the I2C.
SSPM0 ~ SSPM3: These are synchronous serial port mode select bits.
1111 = I2C Slave mode, 10-bit address with Start and Stop bit interrupts enabled
1110 = I2C Slave mode, 7-bit address with Start and Stop bit interrupts enabled
1011 = I2C Firmware Controlled Master mode (Slave Idle)
1000 = I2C Master mode, clock = FOSC/(4 * (SSPADD + 1))
0111 = I2C Slave mode, 10-bit address
0110 = I2C Slave mode, 7-bit address
CKP: This is the SCL clock release control bit. It is used only in slave mode.
Setting this bit releases the clock. If zero, it holds the clock (clock stretch).
SSPEN: This is the synchronous serial port enable bit. Setting this bit enables the
serial port.
SSPOV: This is receiving an overflow indicator bit. If this bit is set during receive
mode, it indicates that a byte is received while SSPBUF is holding the previous
value. And it has no application in transmit mode. We must clear this bit in
software.
WCOL: It is the write collision detect bit. If this bit is set during master to transmit
mode, it indicates that a write to SSPBUF register was attempted when I2C
conditions were not valid for a transmission to be started. And if it is set during a
slave to transmit mode, it indicates that the SSPBUF register is written when it is
transmitting the previous word. We must clear this bit in software.
SSPCON2 – MSSP Control Register 2
The SSPCON2 register is readable and writable, which is used to control the I2C.
SEN: Start Condition or Stretch Enable bit. In master mode, setting this bit to
initiate start condition on SCL & SDA pins and it will be automatically cleared by
60
the hardware. And in slave mode setting this bit enables clock stretching for both
slave receive and slave transmit. If it is cleared in slave mode, clock stretching is
enabled only for slave transmit.
RSEN: Repeated start condition enable bit. This bit has application only in master
mode. Setting this bit will initiate repeated start condition on both SCL & SDA
pins and it will be cleared automatically in hardware.
PEN: Stop condition enable bit. This bit has application only in master mode.
Setting this bit will initiate stop condition on both SCL & SDA pins and it will be
automatically cleared in hardware.
RCEN: Receive enable bit. This bit also has application only in master mode.
Setting this bit enables receive mode for I2C.
ACKEN: Acknowledge sequence enable bit. Setting this bit initiates acknowledge
sequence on SCL & SDA lines and it will send ACKDT (see below) bit. This bit
will be automatically cleared in hardware. It has application only in master receive
mode.
ACKDT: Acknowledge data bit. 1 means not acknowledge and 0 means
acknowledge. This value will be transmitted when we set the ACKEN bit (above).
This bit has application only in master receive mode.
ACKSTAT: Acknowledge status bit. 1 indicates that acknowledgment was not
received from the slave and vice versa. This bit has applications in master transmit
mode only.
GCEN: General call enable bit. Setting this bit enables interrupt when a general
call address is received in the register SSPSR.
SSPBUF & SSPSR & SSPADD
SSPSR is the shift register used for shifting data in or out. SSPBUF is the buffer
register to which data bytes are written to or read from.
SSPADD register holds the slave device address when the SSP is configured in
I2C Slave mode. When the SSP is configured in Master mode, the lower seven bits
of SSPADD act as the baud rate generator reload value.
In receive operations, SSPSR and SSPBUF together create a double-buffered
receiver. When SSPSR receives a complete byte, it is transferred to SSPBUF and
the SSPIF interrupt is set.
During transmission, the SSPBUF is not double buffered. A write to SSPBUF will
write to both SSPBUF and SSPSR.
Circuit Diagram
LCD:
61
• RS – RC0
• RW – RC1
• EN – RC2
• Data Lines – Port B
EEPROM:
• SDA – RC4
• SCL – RC3
dat(i2c_read_byte(0x0000));dat(i2c_read_byte(0x0001));
dat(i2c_read_byte(0x0002));dat(i2c_read_byte(0x0003));
dat(i2c_read_byte(0x0004));dat(i2c_read_byte(0x0005));
dat(i2c_read_byte(0x0006));dat(i2c_read_byte(0x0007));
dat(i2c_read_byte(0x0008));dat(i2c_read_byte(0x0009));
dat(i2c_read_byte(0x000a));while(1);
}
void delay1()
{
unsigned int j,k;for(j=0;j<60000;j++) { for(k=0;k<2;k++); }
}
I2C.H
#define write_cmd 0xA0
#define read_cmd 0xA1
void i2c_init();void i2c_start();
void i2c_stop();void i2c_restart();
void i2c_ack();void i2c_nak();
void waitmssp();void i2c_send(unsigned char dat);
void i2c_send_byte(unsigned char addr,unsigned char *count);
unsigned char i2c_read();unsigned char i2c_read_byte(unsigned char addr);
void i2c_init()
{
TRISC3=TRISC4=1;
SSPCON=0x28; //SSP Module as Master
SSPADD=((11059200/4)/100)-1; //Setting Clock Speed, My PCLK =
11.0592MHz
}
void i2c_start()
{
SEN=1;
waitmssp();
}
void i2c_stop()
{
PEN=1;
waitmssp();
}
void i2c_restart()
63
{
RSEN=1;
waitmssp();
}
void i2c_ack()
{
ACKDT=0;
ACKEN=1;
waitmssp();
}
void i2c_nak()
{
ACKDT=1;
ACKEN=1;
waitmssp();
}
void waitmssp()
{
while(!SSPIF);
SSPIF=0;
}
void i2c_send(unsigned char dat)
{
L1: SSPBUF=dat;
waitmssp();
while(ACKSTAT){i2c_restart;goto L1;}
}
void i2c_send_byte(unsigned char addr,unsigned char *count)
{
i2c_start();
i2c_send(write_cmd);
i2c_send(addr>>8);
i2c_send(addr);
while(*count) {
i2c_send(*count++);
}
i2c_stop();
}
{
RCEN=1;
waitmssp();
return SSPBUF;
}
unsigned char i2c_read_byte(unsigned char addr)
{
unsigned char rec;
L: i2c_restart();
SSPBUF=write_cmd;
waitmssp();
while(ACKSTAT){goto L;}
i2c_send(addr>>8);
i2c_send(addr);
i2c_restart();
i2c_send(read_cmd);
rec=i2c_read();
i2c_nak();
i2c_stop();
return rec;
}
LCD.H
#define rs RC0#define rw RC1
#define en RC2#define delay for(i=0;i<1000;i++)
int i;
void lcd_init();
void cmd(unsigned char a);
void dat(unsigned char b);
void show(unsigned char *s);
void lcd_init()
{
TRISB=TRISC0=TRISC1=TRISC2=0;
cmd(0x38);
cmd(0x0c);
cmd(0x06);
cmd(0x80);
}
{
PORTB=a;
rs=0;
rw=0;
en=1;
delay;
en=0;
}
void dat(unsigned char b)
{
PORTB=b;
rs=1;
rw=0;
en=1;
delay;
en=0;
}
void show(unsigned char *s)
{
while(*s) {
dat(*s++);
}
}
Output – PIC16F877A I2C Tutorial
66
Prerequisites
Before learning PIC16F877A Interrupt Tutorial, we should know the basic interrupts and their
functioning. Please refer to these below links.
• External
• Timer 0
• RB Port Change
• Parallel Slave Port Read/Write
• A/D Converter
• USART Receive
• USART Transmit
• Synchronous Serial Port
• CCP1 (Capture, Compare, PWM)
• CCP2 (Capture, Compare, PWM)
• TMR2 to PR2 Match
• Comparator
• EEPROM Write Operation
• Bus Collision
In this tutorial, we will see USART Interrupts, Timer Interrupts, External Interrupts.
67
1 = At least one of the RB7:RB4 pins changed state; a mismatch condition will continue to set
the bit. Reading PORTB will end the mismatch condition and allow the bit to be cleared (must be
cleared in software).
0 = None of the RB7:RB4 pins have changed state
OPTION_REG
The OPTION_REG Register is a readable and writable register, which contains various control bits to
configure the TMR0 Prescaler/WDT Postscaler (single assignable register known also as the Prescaler),
the external INT interrupt, TMR0, and the weak pull-ups on PORTB.
RBPU: PORTB Pull-up Enable bit (This bit is not used for timers)
INTEDG Interrupt Edge Select bit
1 = Interrupt on the rising edge of RB0/INT pin
0 = Interrupt on the falling edge of RB0/INT pin
Note: There is only one Prescaler available which is mutually exclusively shared between the
Timer0 module and the Watchdog Timer. A Prescaler assignment for the Timer0 module means
that there is no Prescaler for the Watchdog Timer and vice versa. This Prescaler is not accessible
but can be configured using PS2:PS0 bits of OPTION_REG.
PIE1 Register
The PIE1 register contains the individual enable bits for the peripheral interrupts.
Note (1): PSPIE is reserved on PIC16F873A/876A devices; always maintain this bit clear.
ADIE: A/D Converter Interrupt Enable bit
1 = Enables the A/D converter interrupt
0 = Disables the A/D converter interrupt
PIR1 Register
The PIR1 register contains the individual flag bits for the peripheral interrupts.
Note: Interrupt flag bits are set when an interrupt condition occurs regardless of the state of its
corresponding enable bit or the global enable bit, GIE (INTCON<7>). User software should
ensure the appropriate interrupt bits are clear prior to enabling an interrupt.
Note (1): PSPIF is reserved on PIC16F873A/876A devices; always maintain this bit clear.
ADIF: A/D Converter Interrupt Flag bit
1 = An A/D conversion completed
0 = The A/D conversion is not complete
PIE2 Register
The PIE2 register contains the individual enable bits for the CCP2 peripheral
interrupt, the SSP bus collision interrupts, EEPROM writes operation interrupt, and
the comparator interrupt.
PIR2 Register
The PIR2 register contains the flag bits for the CCP2 interrupt, the SSP bus
collision interrupt, EEPROM write operation interrupt and the comparator
interrupt.
1 = A bus collision has occurred in the SSP when configured for I2C Master mode
0 = No bus collision has occurred
LCD:
RS – RC0
RW – RC1
EN – RC2
UART:
TX – RC6
RX – RC7
74
Programming
In this program, I have added only the main code. You can find the header files
and full project here. My PCLK is 11.0592MHz.
#include<pic.h>
#include"serial.h"
#include"lcd.h"
__CONFIG( FOSC_HS & WDTE_OFF & PWRTE_OFF & CP_OFF & BOREN_ON & LVP_OFF &
CPD_OFF & WRT_OFF & DEBUG_OFF);
unsigned int z;
void main()
{
TRISD=0;
INTCON|=0b11000000;
PIE1=0b00100000;
lcd_init();
serial_init();
while(1) {
cmd(0x01);
}
}
Output