Sunday, August 27, 2017

Interfacing PIC16F877A with LM335 analog temperature sensor


The LM335 temperature sensor is an analog device which requires an ADC module to convert the analog data which is the voltage output from the LM335 into digital data. The LM335 has the following features:
  • Directly Calibrated to the Kelvin Temperature Scale
  • 1°C Initial Accuracy Available
  • Operates from 400 μA to 5 mA
  • Less than 1-Ω Dynamic Impedance
  • Easily Calibrated
  • Wide Operating Temperature Range
  • 200°C Overrange
  • Low Cost
The LM135 has a breakdown voltage directly proportional to absolute temperature at 10 mV/°K. If the LM335 output voltage is for example is 3.03 (3030 mV) that means the temperature is: 303 °Kelvin = 30 °Celsius.
This topic shows how to interface the LM335 temperature sensor with PIC16F877A microcontroller.
Hardware Required:
  • PIC16F877A microcontroller
  • LM335 Temperature sensor - datasheet
  • 16x2 LCD Screen
  • 8MHz crystal
  • 2 x 22pF ceramic capacitor
  • 10K ohm potentiometer or variable resistor
  • 2.2K ohm resistor
  • +5V Power supply source
  • Breadboard
  • Jumper wires
Interfacing PIC16F877A with LM335 temperature sensor circuit:
PIC16F877A with LM335 temperature sensor circuit

The LM335 sensor has 3 pins (from left to right):
Pin 1 for calibration, not used in this example
Pin 2: output
Pin 3: GND (ground).
The output pin of the LM335 sensor is connected to analog channel 0 (AN0). I chose the 2.2K ohm because as written in the datasheet for optimum accuracy the current flows through the LM335 should be 1mA. For example if the temperature = 27°C, the output will be 3.00V and assume the supply voltage is exactly 5.00V that means the current flows through the sensor is ( 5 - 3)/2.2 = 0.90mA which is good enough. Also the value 2.2K is a standard value and well used.
The 1602 (16x2) LCD screen is connected to pins RD0~6. The 10K variable resistor is used to adjust the brightness of the screen.
In this example the PIC16F877A runs with 8MHz crystal oscillator.
Interfacing PIC16F877A with LM335 sensor CCS C Code:
The following C code was tested with CCS PIC C compiler version 5.051.
/* Interfacing PIC16F877A with LM335 analog temperature sensor CCS C code.
   Read LM335 datasheet to understand the code!
   http://ccspicc.blogspot.com/
   electronnote@gmail.com
*/

//LCD module connections
#define LCD_RS_PIN      PIN_D0
#define LCD_RW_PIN      PIN_D1
#define LCD_ENABLE_PIN  PIN_D2
#define LCD_DATA4       PIN_D3
#define LCD_DATA5       PIN_D4
#define LCD_DATA6       PIN_D5
#define LCD_DATA7       PIN_D6
//End LCD module connections

#include <16F877A.h>
#fuses HS,NOWDT,NOPROTECT,NOLVP
#device ADC=10
#use delay(clock = 8MHz)
#include <lcd.c>

char message1[] = "Temp =  00.0 C";
char message2[] =      "=  00.0 K";
signed int16 Kelvin, Celsius;
void main(){
  setup_adc(ADC_CLOCK_INTERNAL);                 // ADC Module uses its internal oscillator
  setup_adc_ports(AN0);                          // Configure AN0 pin as analog
  set_adc_channel(0);                            // Select channel 0 (AN0)
  lcd_init();                                    // Initialize LCD module
  lcd_putc('\f');                                // Clear LCD
  while(TRUE){
    delay_ms(1000);
    Kelvin = read_adc() * 0.489;                 // Read analog voltage and convert it to Kelvin (0.489 = 500/1023)
    Celsius = Kelvin - 273;                      // Convert Kelvin to degree Celsius
    if(Celsius < 0){
      Celsius = abs(Celsius);                    // Absolute value
      message1[7] = '-';                         // Put minus '-' sign
    }
    else
      message1[7]  = ' ';                        // Put space ' '
    if (Celsius > 99)
      message1[7]  = 1 + 48;                     // Put 1 (of hundred)
    message1[8]  = (Celsius / 10) % 10  + 48;
    message1[9]  =  Celsius % 10  + 48;
    message1[12] = 223;                          // Degree symbol
    message2[2]  = (Kelvin / 100) % 10 + 48;
    message2[3]  = (Kelvin / 10) % 10 + 48;
    message2[4] = Kelvin % 10 + 48;
    lcd_gotoxy(1, 1);                            // Go to column 1 row 1
    printf(lcd_putc, message1);                  // Display message1
    lcd_gotoxy(6, 2);                            // Go to column 6 row 2
    printf(lcd_putc, message2);                  // Display message2
  }
}
Interfacing PIC16F877A with LM335 sensor videos:
The following video shows a breadboard hardware circuit of the example.


and the second video shows simulation using Proteus software.



Saturday, August 26, 2017

Simple real time clock and calendar using DS3231 and PIC16F877A


Interfacing PIC16F877A with DS3231
The DS3231 is a low cost , extremely accurate real time clock with a built-in crystal oscillator. The characteristics of the DS3231 make it one of the best choices for real time clock chips.
This project shows how to build is simple real time clock and calendar (RTCC) using PIC16F877A microcontroller and DS3231.
The DS3231 uses I2C protocol to interface with the master device which is in our example the PIC16F877A which has one I2C module.
The I2C protocol uses only two lines: SCL (Serial Clock) and SDA (Serial Data) which are in the PIC16F877A RC3 and RC4 respectively.

Hardware Required:
  • PIC16F877A microcontroller
  • 16x2 LCD screen
  • 8 MHz crystal
  • 2 x 22pF ceramic capacitor
  • 10K ohm resistor
  • 2 x push button
  • 5V supply source
  • Breadboard
  • Jumper wires
DS3231 board contains the following components:
  • DS3231 (or DS3232) RTC - datasheet
  •  2 x 10K ohm resistors
  • 0.1uF ceramic capacitor
  • 3V coin cell battery
Simple real time clock and calendar using DS3231 and PIC16F877A circuit:
Real time clock / calendar using PIC16F877A microcontroller and DS3231 RTC circuit
The 16x2 LCD has 7 data lines which are connected to pins RD0~6, the DS3231 SCL pin is connected to pin RC3 (#18) and SDA is connected to pin RC4 (#23) of the PIC16F877A.
In the circuit there are 2 push buttons (B1 & B2) connected to pin RB0 and pin RB1, the two push buttons are used to set the time as well as the calendar parameters. The button B1 selects the parameter and B1 increments the selected parameter as shown in the videos below.

The 3V cell battery is used as a backup to keep time and date running in case of main power failure. The circuit can work without this battery but its pin (#14) has to be grounded.
In this example the PIC16F877A runs with 8MHz crystal oscillator.
Simple real time clock and calendar using DS3231 and PIC16F877A CCS C code:
The following C code was tested with CCS PIC C compiler version 5.051.
The hardware I2C module of the PIC16F877A is initialized and configured using the function below with a speed of 100KHz:
#use I2C(master, I2C1, FAST = 100000)
master: set the microcontroller to the master mode
I2C1: use first I2C module.
The DS3231 works with BCD format only and to convert the BCD to decimal and vise versa I used the following functions (example for minute variable):
minute = (minute >> 4) * 10 + (minute & 0x0F);                                  // Convert BCD to decimal
minute = ((minute / 10) << 4) + (minute % 10);                                   // Convert decimal to BCD
The complete C code is the one below.
/* Simple real time clock/calendar (RTCC) using PIC16F877A & DS3231 (DS3232) CCS C code.
   Read DS3231 RTC datasheet to understand the code!
   Time & date parameters can be set using two push buttons connected to RB0 & RB1.
   http://ccspicc.blogspot.com/
   electronnote@gmail.com
*/

//LCD module connections
#define LCD_RS_PIN      PIN_D0
#define LCD_RW_PIN      PIN_D1
#define LCD_ENABLE_PIN  PIN_D2
#define LCD_DATA4       PIN_D3
#define LCD_DATA5       PIN_D4
#define LCD_DATA6       PIN_D5
#define LCD_DATA7       PIN_D6
//End LCD module connections

#include <16F877A.h>
#fuses HS,NOWDT,NOPROTECT,NOLVP                       
#use delay(clock = 8MHz)
#use fast_io(D)
#include <lcd.c>
#use I2C(master, I2C1, FAST = 100000)

char time[]     = "TIME:  :  :  ";
char calendar[] = "DATE:  /  /20  ";
int8  i, second, minute, hour, date, month, year;
void DS3231_display(){
  // Convert BCD to decimal
  second = (second >> 4) * 10 + (second & 0x0F);
  minute = (minute >> 4) * 10 + (minute & 0x0F);
  hour = (hour >> 4) * 10 + (hour & 0x0F);
  date = (date >> 4) * 10 + (date & 0x0F);
  month = (month >> 4) * 10 + (month & 0x0F);
  year = (year >> 4) * 10 + (year & 0x0F);
  // End conversion
  time[12]     = second % 10  + 48;
  time[11]     = second / 10  + 48;
  time[9]      = minute % 10  + 48;
  time[8]      = minute / 10  + 48;
  time[6]      = hour % 10  + 48;
  time[5]      = hour / 10  + 48;
  calendar[14] = year % 10 + 48;
  calendar[13] = year / 10  + 48;
  calendar[9]  = month % 10 + 48;
  calendar[8]  = month / 10 + 48;
  calendar[6]  = date % 10 + 48;
  calendar[5]  = date / 10 + 48;
  lcd_gotoxy(1, 1);                              // Go to column 1 row 1
  printf(lcd_putc, time);                        // Display time
  lcd_gotoxy(1, 2);                              // Go to column 1 row 2
  printf(lcd_putc, calendar);                    // Display calendar
}
void blink(){
  int8 j = 0;
  while(j < 10 && input(PIN_B0) && input(PIN_B1)){
    j++;
    delay_ms(25);
  }
}
unsigned int8 edit(parameter, xx, yy){
  while(!input(PIN_B0));                         // Wait until button RB0 is released
  while(TRUE){
    while(!input(PIN_B1)){                       // If button RB1 is pressed
      parameter++;
      if(i == 0 && parameter > 23)               // If hours > 23 ==> hours = 0
        parameter = 0;
      if(i == 1 && parameter > 59)               // If minutes > 59 ==> minutes = 0
        parameter = 0;
      if(i == 2 && parameter > 31)               // If date > 31 ==> date = 1
        parameter = 1;
      if(i == 3 && parameter > 12)               // If month > 12 ==> month = 1
        parameter = 1;
      if(i == 4 && parameter > 99)               // If year > 99 ==> year = 0
        parameter = 0;
      lcd_gotoxy(xx, yy);
      printf(lcd_putc,"%02u", parameter);        // Display parameter
      delay_ms(200);                             // Wait 200ms
    }
    lcd_gotoxy(xx, yy);
    lcd_putc("  ");
    blink();
    lcd_gotoxy(xx, yy);                          // Display two spaces
    printf(lcd_putc,"%02u", parameter);          // Display parameter
    blink();
    if(!input(PIN_B0)){                          // If button RB0 is pressed
      i++;                                       // Increament 'i' for the next parameter
      return parameter;                          // Return parameter value and exit
    }
  }
}
void main(){
  set_tris_d(0);
  port_b_pullups(TRUE);                          // Enable PORTB internal pull-ups
  lcd_init();                                    // Initialize LCD module
  lcd_putc('\f');                                // LCD clear
  while(TRUE){
    if(!input(PIN_B0)){                          // If RB0 button is pressed
      i = 0;
      hour = edit(hour, 6, 1);
      minute = edit(minute, 9, 1);
      date = edit(date, 6, 2);
      month = edit(month, 9, 2);
      year = edit(year, 14, 2);
      // Convert decimal to BCD
      minute = ((minute / 10) << 4) + (minute % 10);
      hour = ((hour / 10) << 4) + (hour % 10);
      date = ((date / 10) << 4) + (date % 10);
      month = ((month / 10) << 4) + (month % 10);
      year = ((year / 10) << 4) + (year % 10);
      // End conversion
      // Write data to DS3231 RTC
      i2c_start();                               // Start I2C protocol
      i2c_write(0xD0);                           // DS3231 address
      i2c_write(0);
      i2c_write(0);                              // Reset sesonds and start oscillator
      i2c_write(minute);                         // Write minute value to DS3231
      i2c_write(hour);                           // Write hour value to DS3231
      i2c_write(1);                              // Write day value (not used)
      i2c_write(date);                           // Write date value to DS3231
      i2c_write(month);                          // Write month value to DS3231
      i2c_write(year);                           // Write year value to DS3231
      delay_ms(200);                             // Wait 200ms
    }
    i2c_start();                                  // Start I2C protocol
    i2c_write(0xD0);                              // DS3231 address
    i2c_write(0);                                 // Send register address
    i2c_start();                                  // Restart I2C
    i2c_write(0xD1);                              // Initialize data read
    second = i2c_read(1);                         // Read seconds from register 0
    minute = i2c_read(1);                         // Read minuts from register 1
    hour   = i2c_read(1);                         // Read hour from register 2
    i2c_read(1);                                  // Read day from register 3 (not used)
    date   = i2c_read(1);                         // Read date from register 4
    month  = i2c_read(1);                         // Read month from register 5
    year   = i2c_read(0);                         // Read year from register 6
    i2c_stop();                                   // Stop I2C protocol
    DS3231_display();                             // Diaplay time & calendar
    delay_ms(50);
  }
}
Simple real time clock and calendar using DS3231 and PIC16F877A video:
The following video shows a hardware circuit of the example.


and the second video shows the simulation of the example using Proteus.


PIC16F877A + DS3231 simulation file download.

Tuesday, August 1, 2017

RTOS Example with PIC16F887 microcontroller


The RTOS (Real Time operating System) allows more than one task to run simultaneously (in parallel), for example reading from an analog channel, blinking an LED, setting the duty cycle of a PWM signal.....
This small article shows an RTOS example using PIC16F887 microcontroller and CCS PIC C compiler.
In this example we've 5 LEDs connected to the PIC16F887 microcontroller as shown in the circuit below. It is designed that the 5 LEDs are blinking but each one has its blinking frequency.
RTOS example using PIC16F887 microcontroller
In this example the PIC16F887 MCU uses its internal oscillator which is configured in the software @ 4MHz and MCLR pin function is disabled.
RTOS Example with PIC16F887 microcontroller CCS C code:
As mentioned above our RTOS has 5 LEDs with different frequencies (periods = 1/frequency).
The first LED which is connected to RB0 pin has the following task with a period of 500ms (250ms x 2):
#task(rate = 250ms, max = 50ms)
void led1(){
  output_toggle(PIN_B0);
}

The same thing for the other LEDs.
In CCS C compiler the RTOS is configured using the following line:
#use rtos(timer = 0, minor_cycle = 50ms)
Here Timer0 is selected for the RTOS but other timers (Timer1 or Timer2) can also be used.
The complete C code is the one below.
// RTOS example for PIC16F887 with CCS PIC C compiler
// PIC16F887 internal oscillator used @ 4MHz
// Timer0 is used for the RTOS
// http://ccspicc.blogspot.com/
// electronnote@gmail.com

#include <16F887.h>
#fuses NOMCLR, NOBROWNOUT, NOLVP, INTRC_IO
#use delay(clock = 4MHz)
#use fast_io(B)
#use rtos(timer = 0, minor_cycle = 50ms)

#task(rate = 250ms, max = 50ms)                  // 1st RTOS task (executed every 250ms)
void led1(){
  output_toggle(PIN_B0);
}
#task(rate = 500ms, max = 50ms)                  // 2nd RTOS task (executed every 500ms)
void led2(){
  output_toggle(PIN_B1);
}
#task(rate = 750ms, max = 50ms)                  // 3rd RTOS task (executed every 750ms)
void led3(){
  output_toggle(PIN_B2);
}
#task(rate = 1000ms, max = 50ms)                 // 4th RTOS task (executed every 1000ms)
void led4(){
  output_toggle(PIN_B3);
}
#task(rate = 1250ms, max = 50ms)                 // 5th RTOS task (executed every 1250ms)
void led5(){
  output_toggle(PIN_B4);
}
void main(){
  setup_oscillator(OSC_4MHZ);                    // Set the internal oscillator to 8MHz
  output_b(0);                                   // All PORTB register pins are zeros
  set_tris_b(0);                                 // Configure PORTB pins as outputs
  rtos_run();                                    // Start all the RTOS tasks
}
The following video shows Proteus simulation of the RTOS example:


Proteus simulation file can be downloaded from the following link:
Download

Tuesday, July 18, 2017

Interfacing DS3231 with PIC16F887 microcontroller


Hardware circuit for PIC16F887 and DS3231 RTC 
The datasheet of DS3231 RTC (real time clock) says that it is a low-cost, extremely accurate I2C real-time clock (RTC) with an integrated temperature compensated crystal oscillator (TCXO) and crystal. The DS3231 is much better than the DS1307 which means that it is a very good choice for persons who sell real time clock products.
The DS3231 RTC comes with an internal oscillator which means there is no need for a 32.768KHz crystal oscillator.
This topic shows the interfacing of the DS3231 RTC with PIC16F887 microcontroller with full adjustment of time and date parameters.
Like the DS1307, the DS3231 uses I2C protocol which uses two lines SCL and SDA. The PIC16F887 has a hardware I2C module where the SCL and SDA pins are mapped to RC3 and RC4 respectively.
Hardware Required:
  • PIC16F887 microcontroller
  • DS3231 (or DS3232) RTC -- Datasheet
  • 16x2 LCD screen
  • 2 x push button
  • 10K ohm variable resistor
  • 2 x 10K ohm resistors
  • 0.1µF capacitor (decoupling capacitor needed for the DS3231)
  • 3V coin cell battery
  • 5V voltage source
  • Protoboard
  • Jumper wires
Interfacing DS3231 with PIC16F887 microcontroller circuit:
Interfacing PIC16F887 with DS3231 RTC with set buttons circuit
As we can see in the circuit there is a 16x2 LCD to display time and date, this LCD is connected to PORTD. The SCL and SDA pins of the DS3231 are connected to SCL (RC3) and SDA (RC4) pins of the PIC16F887 microcontroller. Two pull-up resistors of 10K are needed for the SCL and SDA lines, if these two resistors are not connected to whole circuit will not work at all and the LCD may not display any thing.
The two buttons which are connected to pins RB0 and RB1, these buttons are used to set the time as well as the date as shown in the videos below.
The 3V cell battery is used as a backup to keep time and date running in case of main power failure. The circuit can work without this battery but its pin (#14) has to be grounded.
The internal oscillator of the PIC16F887 is used and MCLR pin function is disabled.

Interfacing DS3231 with PIC16F887 microcontroller CCS C code:
The C code below is for CCS PIC C compiler (tested with version 5.051).
Please read the DS3231 datasheet to understand the code.
In CCS C it is easy to initialize the I2C module of the PIC microcontroller using the following line:
#use I2C(master, I2C1, FAST = 100000)
Where:
master: set the microcontroller to the master mode
I2C1: use first I2C module (PIC16F887 has only one module)
FAST = 100000 : set the speed to100KHz
he DS3231 works with BCD format only and to convert the BCD to decimal and vise versa I used the following lines (example for minute variable):
minute = (minute >> 4) * 10 + (minute & 0x0F);                                  // Convert BCD to decimal
minute = ((minute / 10) << 4) + (minute % 10);                                  // Convert decimal to BCD
The full code is as the one below.
/* Real time clock using PIC16F887 & DS3231 (DS3232) CCS C code
   Read DS3231 RTC datasheet to understand the code!
   Internal oscillator used @ 8MHz
   http://ccspicc.blogspot.com/
   electronnote@gmail.com
*/

//LCD module connections
#define LCD_RS_PIN PIN_D0
#define LCD_RW_PIN PIN_D1
#define LCD_ENABLE_PIN PIN_D2
#define LCD_DATA4 PIN_D3
#define LCD_DATA5 PIN_D4
#define LCD_DATA6 PIN_D5
#define LCD_DATA7 PIN_D6
//End LCD module connections

#include <16F887.h>
#fuses NOMCLR NOBROWNOUT NOLVP INTRC_IO
#use delay(clock = 8MHz)
#include <lcd.c>
#use fast_io(B)
#use I2C(master, I2C1, FAST = 100000)

char time[] = "TIME:  :  :  ";
char calendar[] = "DATE:  /  /20  ";
unsigned int8  i, second, minute, hour, date, month, year;
void DS3231_display(){
  // Convert BCD to decimal
  second = (second >> 4) * 10 + (second & 0x0F);
  minute = (minute >> 4) * 10 + (minute & 0x0F);
  hour = (hour >> 4) * 10 + (hour & 0x0F);
  date = (date >> 4) * 10 + (date & 0x0F);
  month = (month >> 4) * 10 + (month & 0x0F);
  year = (year >> 4) * 10 + (year & 0x0F);
  // End conversion
  time[12]     = second % 10  + 48;
  time[11]     = second / 10  + 48;
  time[9]      = minute % 10  + 48;
  time[8]      = minute / 10  + 48;
  time[6]      = hour % 10  + 48;
  time[5]      = hour / 10  + 48;
  calendar[14] = year % 10 + 48;
  calendar[13] = year / 10  + 48;
  calendar[9]  = month % 10 + 48;
  calendar[8]  = month / 10 + 48;
  calendar[6]  = date % 10 + 48;
  calendar[5]  = date / 10 + 48;
  lcd_gotoxy(1, 1);                              // Go to column 1 row 1
  printf(lcd_putc, time);                        // Display time
  lcd_gotoxy(1, 2);                              // Go to column 1 row 2
  printf(lcd_putc, calendar);                    // Display calendar
}
void blink(){
  int8 j = 0;
  while(j < 10 && input(PIN_B0) && input(PIN_B1)){
    j++;
    delay_ms(25);
  }
}
unsigned int8 edit(parameter, xx, yy){
  while(!input(PIN_B0));                         // Wait until button RB0 is released
  while(TRUE){
    while(!input(PIN_B1)){                       // If button RB1 is pressed
      parameter++;
      if(i == 0 && parameter > 23)               // If hours > 23 ==> hours = 0
        parameter = 0;
      if(i == 1 && parameter > 59)               // If minutes > 59 ==> minutes = 0
        parameter = 0;
      if(i == 2 && parameter > 31)               // If date > 31 ==> date = 1
        parameter = 1;
      if(i == 3 && parameter > 12)               // If month > 12 ==> month = 1
        parameter = 1;
      if(i == 4 && parameter > 99)               // If year > 99 ==> year = 0
        parameter = 0;
      lcd_gotoxy(xx, yy);
      printf(lcd_putc,"%02u", parameter);        // Display parameter
      delay_ms(200);                             // Wait 200ms
    }
    lcd_gotoxy(xx, yy);
    lcd_putc("  ");
    blink();
    lcd_gotoxy(xx, yy);                          // Display two spaces
    printf(lcd_putc,"%02u", parameter);          // Display parameter
    blink();
    if(!input(PIN_B0)){                          // If button RB0 is pressed
      i++;                                       // Increament 'i' for the next parameter
      return parameter;                          // Return parameter value and exit
    }
  }
}
void main(){
  setup_oscillator(OSC_8MHZ);                    // Set internal oscillator to 8MHz
  port_b_pullups(3);                             // Enable internal pull-ups for RB0 & RB1
  lcd_init();                                    // Initialize LCD module
  lcd_putc('\f');                                // LCD clear
  while(TRUE){
    if(!input(PIN_B0)){                          // If RB0 button is pressed
      i = 0;
      hour = edit(hour, 6, 1);
      minute = edit(minute, 9, 1);
      date = edit(date, 6, 2);
      month = edit(month, 9, 2);
      year = edit(year, 14, 2);
      // Convert decimal to BCD
      minute = ((minute / 10) << 4) + (minute % 10);
      hour = ((hour / 10) << 4) + (hour % 10);
      date = ((date / 10) << 4) + (date % 10);
      month = ((month / 10) << 4) + (month % 10);
      year = ((year / 10) << 4) + (year % 10);
      // End conversion
      // Write data to DS3231 RTC
      i2c_start();                               // Start I2C protocol
      i2c_write(0xD0);                           // DS3231 address
      i2c_write(0);
      i2c_write(0);                              // Reset sesonds and start oscillator
      i2c_write(minute);                         // Write minute value to DS3231
      i2c_write(hour);                           // Write hour value to DS3231
      i2c_write(1);                              // Write day value (not used)
      i2c_write(date);                           // Write date value to DS3231
      i2c_write(month);                          // Write month value to DS3231
      i2c_write(year);                           // Write year value to DS3231
      delay_ms(200);                             // Wait 200ms
    }
    i2c_start();                                  // Start I2C protocol
    i2c_write(0xD0);                              // DS3231 address
    i2c_write(0);                                 // Send register address
    i2c_start();                                  // Restart I2C
    i2c_write(0xD1);                              // Initialize data read
    second = i2c_read(1);                         // Read seconds from register 0
    minute = i2c_read(1);                         // Read minuts from register 1
    hour   = i2c_read(1);                         // Read hour from register 2
    i2c_read(1);                                  // Read day from register 3 (not used)
    date   = i2c_read(1);                         // Read date from register 4
    month  = i2c_read(1);                         // Read month from register 5
    year   = i2c_read(0);                         // Read year from register 6
    i2c_stop();                                   // Stop I2C protocol
    DS3231_display();                             // Diaplay time & calendar
    delay_ms(50);
  }
}
The following video shows the interfacing of PIC16F887 with the DS3231 in real hardware circuit:


And the following video shows Proteus simulation:


PIC16F887 and DS3231 RTC Proteus simulation file download:
Download

Thursday, July 6, 2017

DS1307 Interfacing with PIC16F887



This article shows how to build a simple real time clock/calendar using PIC16F887 and DS1307 RTC chip.
The DS1307 is an 8-pin integrated circuit uses I2C communication protocol to communicate with master device which is in our case the PIC16F887 microcontroller.
The PIC16F887 microcontroller has an I2C (IIC) module with two pins are used for data transfer. These are the RC3/SCK/SCL pin, which is the clock (SCL), and the RC4/SDI/SDA pin, which is the data (SDA).
The DS1307 can count seconds, minutes, hours, day, date, month and year with leap-year up to year 2100.
The DS1307 receives and transfers data (clock data and calendar data) as BCD format, so after receiving data we have to convert these data into decimal data, and before writing data to the DS1307 we have to convert this data from decimal to BCD format. For example we have the BCD number 33, converting this number into decimal gives 21.
In the CCS C code I used the following line to covert a number 'x' from BCD to decimal:
x = (x >> 4) * 10 + (x & 0x0F);
And to go from decimal to BCD I used this line:
x = ((x / 10) << 4) + (x % 10);
Hardware Required:
  • PIC16F887 Microcontroller
  • DS1307 RTC (datasheet)
  • 16x2 LCD screen
  • 32.768 KHz crystal
  • 10K potentiometer or variable resistor
  • 2 x 10K ohm resistors
  • 3V Coin cell battery
  • 0.1µF Ceramic capacitor (optional)
  • 5V Power supply source
  • Breadboard
  • Jumper wires
DS1307 Interfacing with PIC16F887 circuit:
The circuit diagram of the example is given below.
PIC16F887 and DS1307 RTC circuit
In this example the PIC16F887 MCU uses its internal oscillator and MCLR pin function is disabled.
SCL pin of PIC16F887 (pin number 18) is connected to the SCL pin of the DS1307 (pin number 6) and SDA pin of PIC16F887 (pin number 23) is connected to the SDA pin of the DS1307 (pin number 5).
The 3V cell battery is used to keep the time running if the main power is off.
The two resistors R1 & R2 are pull-up resistors, they are necessary for the I2C protocol.
In the circuit there are two buttons to set time and calendar. The button B1 selects time or calendar parameter (minutes, hours, date, month and year) and B2 increments the selected parameter.
DS1307 Interfacing with PIC16F887 CCS PIC C compiler code:
The code below can display time and calendar where their parameters such as minutes using two push buttons.
This code is tested with version 5.051.
/* Real time clock using PIC16F887 & DS1307 CCS C code
   Read DS1307 RTC datasheet to understand the code!
   Internal oscillator used @ 8MHz
   http://ccspicc.blogspot.com/
   electronnote@gmail.com
*/

//LCD module connections
#define LCD_RS_PIN PIN_D0
#define LCD_RW_PIN PIN_D1
#define LCD_ENABLE_PIN PIN_D2
#define LCD_DATA4 PIN_D3
#define LCD_DATA5 PIN_D4
#define LCD_DATA6 PIN_D5
#define LCD_DATA7 PIN_D6
//End LCD module connections

#include <16F887.h>
#fuses NOMCLR NOBROWNOUT NOLVP INTRC_IO
#use delay(clock = 8MHz)
#include <lcd.c>
#use fast_io(B)
#use I2C(master, I2C1, FAST = 100000)

char time[] = "TIME:  :  :  ";
char calendar[] = "DATE:  /  /20  ";
unsigned int8  second, minute, hour, date, month, year, day;
void ds1307_display(){
  // Convert BCD to decimal
  second = (second >> 4) * 10 + (second & 0x0F);
  minute = (minute >> 4) * 10 + (minute & 0x0F);
  hour = (hour >> 4) * 10 + (hour & 0x0F);
  date = (date >> 4) * 10 + (date & 0x0F);
  month = (month >> 4) * 10 + (month & 0x0F);
  year = (year >> 4) * 10 + (year & 0x0F);
  // End conversion
  time[12]     = second % 10  + 48;
  time[11]     = second / 10  + 48;
  time[9]      = minute % 10  + 48;
  time[8]      = minute / 10  + 48;
  time[6]      = hour % 10  + 48;
  time[5]      = hour / 10  + 48;
  calendar[14] = year % 10 + 48;
  calendar[13] = year / 10  + 48;
  calendar[9]  = month % 10 + 48;
  calendar[8]  = month / 10 + 48;
  calendar[6]  = date % 10 + 48;
  calendar[5]  = date / 10 + 48;
  lcd_gotoxy(1, 1);                              // Go to column 1 row 1
  printf(lcd_putc, time);                        // Display time
  lcd_gotoxy(1, 2);                              // Go to column 1 row 2
  printf(lcd_putc, calendar);                    // Display calendar
}
void ds1307_write(unsigned int8 address, data_){
  i2c_start();                                   // Start I2C protocol
  i2c_write(0xD0);                               // DS1307 address
  i2c_write(address);                            // Send register address
  i2c_write(data_);                              // Write data to the selected register
  i2c_stop();                                    // Stop I2C protocol
}
void main(){
  setup_oscillator(OSC_8MHZ);                    // Set internal oscillator to 8MHz
  port_b_pullups(3);                             // Enable internal pull-ups for RB0 & RB1
  lcd_init();                                    // Initialize LCD module
  lcd_putc('\f');                                // LCD clear
  while(TRUE){
   if(!input(PIN_B0)){                           // If RB0 button is pressed
    lcd_putc('\f');                              // LCD clear
    lcd_gotoxy(5, 1);                            // Go to column 5 row 1
    lcd_putc("Minute:");
    delay_ms(200);
    while(TRUE){
     if(!input(PIN_B1))                          // If RB1 button is pressed
      minute++;                                  // Increment minutes
     if(minute > 59)
      minute = 0;
     lcd_gotoxy(8, 2);                           // Go to column 8 row 2
     printf(lcd_putc,"%02u", minute);
     if(!input(PIN_B0))
      break;
     delay_ms(200);
    }
    lcd_putc('\f');                              // LCD clear
    lcd_gotoxy(6, 1);                            // Go to column 6 row 1
    lcd_putc("Hour:");
    delay_ms(200);
    while(TRUE){
     if(!input(PIN_B1))
      hour++;
     if(hour > 23)
      hour = 0;
     lcd_gotoxy(8, 2);                           // Go to column 8 row 2
     printf(lcd_putc,"%02u", hour);
     if(!input(PIN_B0))
      break;
     delay_ms(200);
    }
    lcd_putc('\f');                              // LCD clear
    lcd_gotoxy(6, 1);                            // Go to column 6 row 1
    lcd_putc("Date:");
    delay_ms(200);
    while(TRUE){
     if(!input(PIN_B1))
      date++;
     if(date > 31)
      date = 1;
     lcd_gotoxy(8, 2);                           // Go to column 8 row 2
     printf(lcd_putc,"%02u", date);
     if(!input(PIN_B0))
      break;
     delay_ms(200);
    }
    lcd_putc('\f');                              // LCD clear
    lcd_gotoxy(6, 1);                            // Go to column 6 row 1
    lcd_putc("Month:");
    delay_ms(200);
    while(TRUE){
     if(!input(PIN_B1))
      month++;
     if(month > 12)
      month = 1;
     lcd_gotoxy(8, 2);                           // Go to column 8 row 2
     printf(lcd_putc,"%02u", month);
     if(!input(PIN_B0))
      break;
     delay_ms(200);
    }
    lcd_putc('\f');                              // LCD clear
    lcd_gotoxy(6, 1);                            // Go to column 6 row 1
    lcd_putc("Year:");
    lcd_gotoxy(7, 2);                            // Go to column 7 row 1
    lcd_putc("20");
    delay_ms(200);
    while(TRUE){
     if(!input(PIN_B1))
      year++;
     if(year > 99)
      year = 0;
     lcd_gotoxy(9, 2);                           // Go to column 9 row 2
     printf(lcd_putc,"%02u", year);
     if(!input(PIN_B0)){
      // Convert decimal to BCD
      minute = ((minute / 10) << 4) + (minute % 10);
      hour = ((hour / 10) << 4) + (hour % 10);
      date = ((date / 10) << 4) + (date % 10);
      month = ((month / 10) << 4) + (month % 10);
      year = ((year / 10) << 4) + (year % 10);
      // End conversion
      ds1307_write(1, minute);                   // Write minute value to DS1307
      ds1307_write(2, hour);
      ds1307_write(4, date);
      ds1307_write(5, month);
      ds1307_write(6, year);
      ds1307_write(0, 0);                        //Reset seconds and start oscillator
      delay_ms(200);
      break;
     }
     delay_ms(200);
    }
   }
   i2c_start();                                  // Start I2C protocol
   i2c_write(0xD0);                              // DS1307 address
   i2c_write(0);                                 // Send register address
   i2c_start();                                  // Restart I2C
   i2c_write(0xD1);                              // Initialize data read
   second = i2c_read(1);                         // Read seconds from register 0
   minute = i2c_read(1);                         // Read minuts from register 1
   hour   = i2c_read(1);                         // Read hour from register 2
   day    = i2c_read(1);                         // Read day from register 3
   date   = i2c_read(1);                         // Read date from register 4
   month  = i2c_read(1);                         // Read month from register 5
   year   = i2c_read(0);                         // Read year from register 6
   i2c_stop();                                   // Stop I2C protocol
   ds1307_display();                             // Diaplay time & calendar
   delay_ms(50);
  }
}

The following video shows project simulation with Proteus:


Simulation file can be downloaded from this link:
DS1307 + PIC16F887 Simulation

Tuesday, July 4, 2017

Interfacing PIC16F887 with LM335 temperature sensor



PIC16F887 + LM335 sensor brotoboard hardware circuit
This is an example showing how to connect LM335 temperature sensing device with PIC16F887 microcontroller.
The LM335 is an analog device which requires an ADC module to convert the analog data which is the voltage output from the LM335 into digital data. The LM335 has the following features:
  • Directly Calibrated to the Kelvin Temperature Scale
  • 1°C Initial Accuracy Available
  • Operates from 400 μA to 5 mA
  • Less than 1-Ω Dynamic Impedance
  • Easily Calibrated
  • Wide Operating Temperature Range
  • 200°C Overrange
  • Low Cost
The LM135 has a breakdown voltage directly proportional to absolute temperature at 10 mV/°K. If the LM335 output voltage is for example is 3.03 (3030 mV) that means the temperature is: 303 °Kelvin = 30 °Celsius.
Hardware Required:
  • PIC16F887 Microcontroller
  • LM335 Temperature sensor
  • 16x2 LCD Screen
  • 10K ohm potentiometer or variable resistor
  • 2.2K ohm resistor
  • 0.1µF Ceramic capacitor (optional)
  • +5V Power supply source
  • Breadboard
  • Jumper wires
Interfacing PIC16F887 with LM335 sensor circuit:
This is our example circuit where the microcontroller uses its internal oscillator.
The LM335 has 3 pins:
Pin 1 for calibration, not used in this example
Pin 2: output
Pin 3: GND (ground)
PIC16F887 microcontroller + LM335 temperature sensor device circuit diagram
Interfacing PIC16F887 with LM335 C code:
The C code used in this example is as the one below where it has been compiled with CCS PIC C compiler version 5.051.
/* Interfacing PIC16F887 with LM335 analog temperature sensor CCS C code
   Read LM335 datasheet to understand the code!
   Internal oscillator used @ 8MHz
   http://ccspicc.blogspot.com/
   electronnote@gmail.com
*/

//LCD module connections
#define LCD_RS_PIN PIN_D0
#define LCD_RW_PIN PIN_D1
#define LCD_ENABLE_PIN PIN_D2
#define LCD_DATA4 PIN_D3
#define LCD_DATA5 PIN_D4
#define LCD_DATA6 PIN_D5
#define LCD_DATA7 PIN_D6
//End LCD module connections

#include <16F887.h>
#fuses NOMCLR NOBROWNOUT NOLVP INTRC_IO
#device ADC = 10
#use delay(clock = 8MHz)
#include <lcd.c>

char message1[] = "Temp =  00.0 C";
char message2[] =      "=  00.0 K";
signed int16 Kelvin, Celsius;
void main(){
  setup_oscillator(OSC_8MHZ);                    // Set the internal oscillator to 8MHz
  setup_adc(ADC_CLOCK_INTERNAL);                 // ADC Module uses its internal oscillator
  setup_adc_ports(sAN0);                         // Configure AN0 pin as analog
  set_adc_channel(0);                            // Select channel 0 (AN0)
  lcd_init();                                    // Initialize LCD module
  lcd_putc('\f');                                // Clear LCD
  while(TRUE){
    delay_ms(1000);
    Kelvin = read_adc() * 0.489;                 // Rea analog voltage and convert it to Kelvin (0.489 = 500/1023)
    Celsius = Kelvin - 273;                      // Convert Kelvin to degree Celsius
    if(Celsius < 0){
      Celsius = abs(Celsius);                    // Absolute value
      message1[7] = '-';                         // Put minus '-' sign
    }
    else
      message1[7]  = ' ';                        // Put space ' '
    if (Celsius > 99)
      message1[7]  = 1 + 48;                     // Put 1 (of hundred)
    message1[8]  = (Celsius / 10) % 10  + 48;
    message1[9]  =  Celsius % 10  + 48;
    message1[12] = 223;                          // Degree symbol
    message2[2]  = (Kelvin / 100) % 10 + 48;
    message2[3]  = (Kelvin / 10) % 10 + 48;
    message2[4] = Kelvin % 10 + 48;
    lcd_gotoxy(1, 1);                            // Go to column 1 row 1
    printf(lcd_putc, message1);                  // Display message1
    lcd_gotoxy(6, 2);                            // Go to column 6 row 2
    printf(lcd_putc, message2);                  // Display message2
  }
}
The following video shows Proteus simulation of LM335 and PIC16F887:


Proteus simulation file can be downloaded from this link:
PIC16F887 + LM335 - Proteus

Reference:
LM335 Datasheet