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 PIC18F4550 microcontroller and DS3231.
The
DS3231 uses I2C protocol to interface with the master device which is
in our example the PIC18F4550 which has one I2C module.
The I2C
protocol uses only two lines: SCL (Serial Clock) and SDA (Serial Data)
which are in the PIC18F4550 pin RB1 and pin RB0 respectively. Hardware Required:
Real time clock & calendar with PIC18F4550 and DS3231 circuit:
The 1602 LCD has 7 data lines which are connected to pins RD0~6, the
DS3231 SCL pin is connected to pin RB1 (#34) and SDA is connected to pin
RB0 (#33) of the PIC18F4550 microcontroller.
In the circuit there are 2 push
buttons (B1 & B2) connected to pin RB2 and pin RB3, the two push
buttons are used to set the time as well as the calendar parameters (minutes, hours, date......). The
button B1 selects the parameter and B2 increments the selected parameter.
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 project the PIC18F4550 uses its internal oscillator and MCLR pin function is disabled. Real time clock & calendar with PIC18F4550 and DS3231 C code:
The C code below was tested with CCS C compiler version 5.051.
The hardware I2C module of the PIC18F4550 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 Code functions: void DS3231_display() : this function prints the time and calendar on the 1602 LCD screen. Before the print it converts the all the wanted data from BCD format to decimal format. int8 edit(parameter, x, y) : I used this function to edit time and calendar parameters. I used a variable named i to distinguish between the parameters:
i = 0, 1 : hours and minutes respectively
i = 2, 3, 4: date, month and year respectively.
After the edit of time/calendar the data have to be converted back to BCD format and written to the DS3231 (it had been converted from BCD format to decimal format by the function void DS3231_display()). void blink() : this small function works as a delay except that it is interrupted by the buttons B1 (connected to RB2) and B2 (connected to RB3). When called and without pressing any button the total time is 10 x 25ms = 250ms. With this function we can see the blinking of the selected parameter with a frequency of 2Hz. So a delay of 250ms comes after the print of the selected parameter and after that delay a 2 spaces is printed which makes the parameter disappears from the LCD and another 250ms delay comes after the print of the 2 spaces.
The complete C code is the one below.
/* Real time clock/calendar (RTCC) using PIC18F4550 & DS3231 CCS C code. Read DS3231 RTC datasheet to understand the code! Time & date parameters can be set using two push buttons connected to RB2 & RB3. 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 <18F4550.h>
#fuses NOMCLR, INTRC_IO, NOWDT,NOPROTECT,NOLVP
#usedelay(clock = 8000000)
#include <lcd.c>
#use fast_io(B)
#use fast_io(D)
#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
}
voidblink(){
int8 j = 0;
while(j < 10 && input(PIN_B2) && input(PIN_B3)){
j++;
delay_ms(25);
}
}
int8 edit(parameter, x, y){
while(!input(PIN_B2)); // Wait until button RB2 releasedwhile(TRUE){
while(!input(PIN_B3)){ // If button RB3 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(x, y);
printf(lcd_putc,"%02u", parameter); // Display parameter
delay_ms(200); // Wait 200ms
}
lcd_gotoxy(x, y);
lcd_putc(" ");
blink();
lcd_gotoxy(x, y); // Print two spaces
printf(lcd_putc,"%02u", parameter); // Print parameterblink();
if(!input(PIN_B2)){ // If button RB2 is pressed
i++; // Increament 'i' for the next parameterreturn parameter; // Return parameter value and exit
}
}
}
void main(){
setup_oscillator(OSC_8MHZ); // Set internal oscillator to 8MHz
set_tris_d(0);
port_b_pullups(TRUE); // Enable PORTB internal pull-ups
lcd_init(); // Initialize LCD module
lcd_putc('\f'); // LCD clearwhile(TRUE){
if(!input(PIN_B2)){ // If RB2 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); // Send register address (time seconds register)
i2c_write(0); // Reset sesonds and start oscillator
i2c_write(minute); // Write minutes value to DS3231
i2c_write(hour); // Write hours 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 (time seconds register)
i2c_start(); // Restart I2C
i2c_write(0xD1); // Initialize data read
second = i2c_read(1); // Read seconds from register 0
minute = i2c_read(1); // Read minutes from register 1
hour = i2c_read(1); // Read hours 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); // Wait 50ms
}
}
The project should work as shown in the video below (in the video the used mcu is PIC16F877A).
Interfacing PIC18F4550 with LM335 analog temperature sensor
As mentioned above, the LM335 is a 3-pin analog device which can measure temperature (converts temperature to analog voltage). This sensor requires an ADC to convert the analog data into digital one. this topic shows how to use PIC18F4550 microcontroller ADC module to measure the ambient temperature using the LM335 sensor.
The LM335 sensor has the following features (from LM335 datasheet):
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 for
example is 3.03 (3030 mV) that means the temperature is: 303 °Kelvin =
30 °Celsius. Hardware Required:
Interfacing PIC18F4550 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 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 PIC18F4550 runs with its internal oscillator @ 8MHz and MCLR pin function is disabled. Interfacing PIC18F4550 with LM335 sensor CCS C Code:
The following C code was tested with CCS PIC C compiler version 5.051.
/* Interfacing PIC18F4550 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 <18F4550.h>
#fuses NOMCLR, INTRC_IO
#device ADC=10
#usedelay(clock = 8MHz)
#include <lcd.c>
char message1[] = "Temp = 00.0 C";
char message2[] = "= 00.0 K";
signedint16 Kelvin, Celsius;
void main(){
setup_oscillator(OSC_8MHZ); // Set internal oscillator to 8MHz
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 LCDwhile(TRUE){
delay_ms(1000); // Wait 1 second
Kelvin = read_adc() * 0.489; // Read analog voltage and convert it to Kelvin (0.489 = 500/1023)
Celsius = Kelvin - 273; // Convert Kelvin to degree Celsiusif(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
}
}
After I built a simple real time clock and calendar using PIC16F877A microcontroller and DS3231 RTC boards, I improved the previous project and I added alarm functions (alarm1 and alarm2) and temperature monitor.
The simple RTCC with PIC16F877A and DS3231 topic: Simple real time clock and calendar using DS3231 and PIC16F877A
The DS3231 RTC has a built-in 2 alarm functions and a digital temperature sensor with an accuracy of ±3°C. Hardware Required:
PIC16F877A microcontroller
20x4 LCD screen
DS3231 RTC board
8 MHz crystal
2 x 22pF ceramic capacitor
10K ohm resistor
10K ohm variable resistor
330 ohm resistor
LED
3 x push button
5V supply source
Breadboard
Jumper wires
Project circuit:
To simplify the circuit, I used the DS3231 board, this board basically contains the main chip which is the DS3231, pull-up resistors (4.7K) of SCL, SDA and INT/SQW lines and coin cell battery holder. There is also 24C32 EEPROM and some other resistors (not used in this project).
The DS3231 board is supplied with 5V as the microcontroller and the 2004 LCD, there are 3 data lined connected between this board and the PIC16F877A, SCL line is connected to RC3, SDA is connected to RC4 and INT line is connected to pin RB0 which is the external interrupt pin of the PIC16F877A. The DS3231 interrupts the microcontroller when there is an alarm.
In the circuit there are 3 push buttons: B1, B2 and B3. These buttons are used to set time, calendar and alarms. Time and calendar can be adjusted with B1 and B2, button B1 selects time or date parameter (time parameters: hours and minutes; calendar parameters: day, date, month and year) and B2 increments the selected parameter. The button B3 and B2 adjust alarm1 and alarm2 parameters (hours, minutes and ON/OFF), button B3 selects the parameter and B2 increments the selected parameter.
There is an LED connected to pin RB4, this LED is used as an alarm indicator, so if there is an alarm the DS3231 pulls down the INT pin which interrupts the microcontroller and the microcontroller turns the LED ON, here button B2 turns both the LED and the occurred alarm OFF.
In this project the MCU runs with 8MHz crystal oscillator. Project C code:
The C code below was tested with CCS PIC C compiler version 5.051.
By reading the datasheet of the DS3231 RTC the code will be more easier!
The hardware I2C module of the MCU is initialized and configured using the following CCS C function 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
Code functions: void DS3231_read() : this function reads time and calendar data from the DS3231 (seconds, minutes, hours, day, date, month and year). void DS3231_display() : displays time and calendar data, before displaying time and calendar data are converted from BCD format to decimal format. This function displays the calendar by calling a function named void calendar_display() . void alarms_read_display() : basically this functions reads alarm1 and alarm2 minutes and hours. It also reads the DS3231 control register, status register and 2 temperature registers.
The other job of this function is to display alarms data (hours, minutes and status) and the temperature value. The alarm status are extracted from the control register. int8 edit(parameter, x, y) : I used this function to edit time, calendar and alarm parameters except the day. I used a variable named i to distinguish between the parameters:
i = 0, 1 : time hours and minutes respectively
i = 2, 3, 4: date month, year respectively
i = 5, 6: alarms hours and minutes respectively
i = 7: alarms status (ON or OFF)
After the edit of time/calendar/alarms the data have to be converted back to BCD format and written to the DS3231.
The MCU turns the LED ON when it was interrupted by the DS3231, the DS3231 sends the interrupt signal (pulls down the INT line) when there has been an alarm. Button B2 resets and turns OFF the alarm. If both alarms are active, button B2 will resets and turns OFF the occurred alarm only and keeps the other as it is. To do that we've to detect which alarm was occurred which can be easily done by reading the status register of the DS3231 (A1IF and A2IF flag bits). Turning ON or OFF an alarm is done by writing to the control register (bits: INTCN, A1IE and A2IE). Always INTCN bit should be 1. I used the following line to write 1 to the INTCN bit and to turn OFF the occurred alarm: i2c_write(4 | (!bit_test(status_reg, 0) & alarm1_status) | ((!bit_test(status_reg, 1) & alarm2_status) << 1));
alarm1_status and alarm2_status are 1-bit variables, for example if alarm1_status is 1 ==> alarm1 is ON and if alarm1_status is 0 ==> alarm1 is OFF. The same thing for alarm2.
The complete C code is below.
/* Real time clock and calendar with 2 alarms and temperature sensing using PIC16F877A & DS3231 CCS C code. Read DS3231 RTC datasheet to understand the code! Time & date parameters can be set using two push buttons connected to RB1 & RB2. Alarm1 and alarm2 can be set using buttons RB3 and RB2. Pin RB4 becomes high when alarm occurred and button RB2 returns it to low and turn the occurred alarm OFF. DS3231 interrupt pin is connected to PIC16F877A interrupt pin RB0. 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
#usedelay(clock = 8MHz)
#use fast_io(B)
#use fast_io(D)
#include <lcd.c>
#use I2C(master, I2C1, FAST = 100000)
int1 alarm1_status, alarm2_status;
char time[] = " : : ",
calendar[] = " / /20 ",
alarm1[] = "A1: : :00", alarm2[] = "A2: : :00",
temperature[] = "T: . C";
int8 i, second, minute, hour, day, date, month, year,
alarm1_minute, alarm1_hour, alarm2_minute, alarm2_hour,
status_reg;
#INT_EXT// External interrupt routinevoid ext_isr(void){
output_high(PIN_B4);
clear_interrupt(INT_EXT);
}
void DS3231_read(){ // Read time & calendar data function
i2c_start(); // Start I2C protocol
i2c_write(0xD0); // DS3231 address
i2c_write(0); // Send register address (seconds register)
i2c_start(); // Restart I2C
i2c_write(0xD1); // Initialize data read
second = i2c_read(1); // Read seconds from register 0
minute = i2c_read(1); // Read minutes 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
}
void alarms_read_display(){ // Read and display alarm1 and alarm2 data functionint8 control_reg, temperature_lsb;
signedint8 temperature_msb;
i2c_start(); // Start I2C protocol
i2c_write(0xD0); // DS3231 address
i2c_write(0x08); // Send register address (alarm1 minutes register)
i2c_start(); // Restart I2C
i2c_write(0xD1); // Initialize data read
alarm1_minute = i2c_read(1); // Read alarm1 minutes
alarm1_hour = i2c_read(1); // Read alarm1 hours
i2c_read(1); // Skip alarm1 day/date register
alarm2_minute = i2c_read(1); // Read alarm2 minutes
alarm2_hour = i2c_read(1); // Read alarm2 hours
i2c_read(1); // Skip alarm2 day/date register
control_reg = i2c_read(1); // Read the DS3231 control register
status_reg = i2c_read(1); // Read the DS3231 status register
i2c_read(1); // Skip aging offset register
temperature_msb = i2c_read(1); // Read temperature MSB
temperature_lsb = i2c_read(0); // Read temperature LSB
i2c_stop(); // Stop I2C protocol// Convert BCD to decimal
alarm1_minute = (alarm1_minute >> 4) * 10 + (alarm1_minute & 0x0F);
alarm1_hour = (alarm1_hour >> 4) * 10 + (alarm1_hour & 0x0F);
alarm2_minute = (alarm2_minute >> 4) * 10 + (alarm2_minute & 0x0F);
alarm2_hour = (alarm2_hour >> 4) * 10 + (alarm2_hour & 0x0F);
// End conversion
alarm1[8] = alarm1_minute % 10 + 48;
alarm1[7] = alarm1_minute / 10 + 48;
alarm1[5] = alarm1_hour % 10 + 48;
alarm1[4] = alarm1_hour / 10 + 48;
alarm2[8] = alarm2_minute % 10 + 48;
alarm2[7] = alarm2_minute / 10 + 48;
alarm2[5] = alarm2_hour % 10 + 48;
alarm2[4] = alarm2_hour / 10 + 48;
alarm1_status = bit_test(control_reg, 0); // Read alarm1 interrupt enable bit (A1IE) from DS3231 control register
alarm2_status = bit_test(control_reg, 1); // Read alarm2 interrupt enable bit (A2IE) from DS3231 control registerif(temperature_msb < 0){
temperature_msb = abs(temperature_msb);
temperature[2] = '-';
}
else
temperature[2] = ' ';
temperature_lsb >>= 6;
temperature[4] = temperature_msb % 10 + 48;
temperature[3] = temperature_msb / 10 + 48;
if(temperature_lsb == 0 || temperature_lsb == 2){
temperature[7] = '0';
if(temperature_lsb == 0) temperature[6] = '0';
else temperature[6] = '5';
}
if(temperature_lsb == 1 || temperature_lsb == 3){
temperature[7] = '5';
if(temperature_lsb == 1) temperature[6] = '2';
else temperature[6] = '7';
}
temperature[8] = 223; // Degree symbol
lcd_gotoxy(11, 1); // Go to column 10 row 1
printf(lcd_putc, temperature); // Display temperature
lcd_gotoxy(21, 1); // Go to column 1 row 3
printf(lcd_putc, alarm1); // Display alarm1
lcd_gotoxy(38, 1); // Go to column 18 row 3if(alarm1_status) lcd_putc("ON "); // If A1IE = 1 print 'ON'else lcd_putc("OFF"); // If A1IE = 0 print 'OFF'
lcd_gotoxy(21, 2); // Go to column 1 row 4
printf(lcd_putc, alarm2); // Display alarm2
lcd_gotoxy(38, 2); // Go to column 18 row 4if(alarm2_status) lcd_putc("ON "); // If A2IE = 1 print 'ON'else lcd_putc("OFF"); // If A2IE = 0 print 'OFF'
}
void calendar_display(){ // Display calendar functionswitch(day){
case 1: strcpy(calendar, "Sun / /20 "); break;
case 2: strcpy(calendar, "Mon / /20 "); break;
case 3: strcpy(calendar, "Tue / /20 "); break;
case 4: strcpy(calendar, "Wed / /20 "); break;
case 5: strcpy(calendar, "Thu / /20 "); break;
case 6: strcpy(calendar, "Fri / /20 "); break;
case 7: strcpy(calendar, "Sat / /20 "); break;
default: strcpy(calendar, "Sat / /20 "); break;
}
calendar[13] = year % 10 + 48;
calendar[12] = year / 10 + 48;
calendar[8] = month % 10 + 48;
calendar[7] = month / 10 + 48;
calendar[5] = date % 10 + 48;
calendar[4] = date / 10 + 48;
lcd_gotoxy(1, 2); // Go to column 1 row 2
printf(lcd_putc, calendar); // Display calendar
}
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[7] = second % 10 + 48;
time[6] = second / 10 + 48;
time[4] = minute % 10 + 48;
time[3] = minute / 10 + 48;
time[1] = hour % 10 + 48;
time[0] = hour / 10 + 48;
calendar_display(); // Call calendar display function
lcd_gotoxy(1, 1); // Go to column 1 row 1
printf(lcd_putc, time); // Display time
}
voidblink(){
int8 j = 0;
while(j < 10 && (input(PIN_B1) || i >= 5) && input(PIN_B2) && (input(PIN_B3) || i < 5)){
j++;
delay_ms(25);
}
}
int8 edit(parameter, x, y){
while(!input(PIN_B1) || !input(PIN_B3)); // Wait until button RB0 is releasedwhile(TRUE){
while(!input(PIN_B2)){ // If button RB2 is pressed
parameter++;
if(((i == 0) || (i == 5)) && parameter > 23) // If hours > 23 ==> hours = 0
parameter = 0;
if(((i == 1) || (i == 6)) && 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;
if(i == 7 && parameter > 1) // For alarms ON or OFF (1: alarm ON, 0: alarm OFF)
parameter = 0;
lcd_gotoxy(x, y);
if(i == 7){ // For alarms ON & OFFif(parameter == 1) lcd_putc("ON ");
else lcd_putc("OFF");
}
else
printf(lcd_putc,"%02u", parameter); // Display parameterif(i >= 5){
DS3231_read(); // Read data from DS3231
DS3231_display(); // Display DS3231 time and calendar
}
delay_ms(200); // Wait 200ms
}
lcd_gotoxy(x, y); // Go to LCD x column and y row
lcd_putc(" "); // Print two spacesif(i == 7) lcd_putc(" "); // Print space (for alarms ON & OFF)blink(); // Call blink function
lcd_gotoxy(x, y); // Go to LCD x column and y rowif(i == 7){ // For alarms ON & OFFif(parameter == 1) lcd_putc("ON ");
else lcd_putc("OFF");
}
else
printf(lcd_putc,"%02u", parameter); // Display parameterblink();
if(i >= 5){
DS3231_read();
DS3231_display();}
if((!input(PIN_B1) && i < 5) || (!input(PIN_B3) && i >= 5)){
i++; // Increment 'i' for the next parameterreturn parameter; // Return parameter value and exit
}
}
}
void main(){
output_b(0);
set_tris_b(0x0F); // Configure RB0 ~ 3 as input pins
set_tris_d(0); // Configure all PORTD pins as outputs
port_b_pullups(TRUE); // Enable PORTB internal pull-ups
enable_interrupts(GLOBAL); // Enable global interrupts
enable_interrupts(INT_EXT_H2L); // Enable external interrupt with edge from high to low
lcd_init(); // Initialize LCD module
lcd_putc('\f'); // LCD clearwhile(TRUE){
if(!input(PIN_B1)){ // If RB1 button is pressed
i = 0;
hour = edit(hour, 1, 1);
minute = edit(minute, 4, 1);
while(!input(PIN_B1)); // Wait until button RB0 releasedwhile(TRUE){
while(!input(PIN_B2)){ // If button RB2 button is pressed
day++; // Increment dayif(day > 7) day = 1;
calendar_display(); // Call display calendar
lcd_gotoxy(1, 2); // Go to column 1 row 2
printf(lcd_putc, calendar); // Display calendar
delay_ms(200);
}
lcd_gotoxy(1, 2); // Go to column 1 row 2
lcd_putc(" "); // Print 3 spacesblink();
lcd_gotoxy(1, 2); // Go to column 1 row 2
printf(lcd_putc, calendar); // Print calendarblink(); // Call blink functionif(!input(PIN_B1)) // If button RB1 is pressedbreak;
}
date = edit(date, 5, 2); // Edit date
month = edit(month, 8, 2); // Edit month
year = edit(year, 13, 2); // Edit year// 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 time & calendar data to DS3231 RTC
i2c_start(); // Start I2C protocol
i2c_write(0xD0); // DS3231 address
i2c_write(0); // Send register address (seconds address)
i2c_write(0); // Reset seconds and start oscillator
i2c_write(minute); // Write minute value to DS3231
i2c_write(hour); // Write hour value to DS3231
i2c_write(day); // Write day value
i2c_write(date); // Write date value to DS3231
i2c_write(month); // Write month value to DS3231
i2c_write(year); // Write year value to DS3231
i2c_stop(); // Stop I2C
delay_ms(200); // Wait 200ms
}
if(!input(PIN_B3)){ // If RB3 button is pressedwhile(!input(PIN_B3)); // Wait until button RB3 released
i = 5;
alarm1_hour = edit(alarm1_hour, 25, 1);
alarm1_minute = edit(alarm1_minute, 28, 1);
alarm1_status = edit(alarm1_status, 38, 1);
i = 5;
alarm2_hour = edit(alarm2_hour, 25, 2);
alarm2_minute = edit(alarm2_minute, 28, 2);
alarm2_status = edit(alarm2_status, 38, 2);
alarm1_minute = ((alarm1_minute / 10) << 4) + (alarm1_minute % 10);
alarm1_hour = ((alarm1_hour / 10) << 4) + (alarm1_hour % 10);
alarm2_minute = ((alarm2_minute / 10) << 4) + (alarm2_minute % 10);
alarm2_hour = ((alarm2_hour / 10) << 4) + (alarm2_hour % 10);
// Write alarms data to DS3231
i2c_start(); // Start I2C
i2c_write(0xD0); // DS3231 address
i2c_write(7); // Send register address (alarm1 seconds)
i2c_write(0); // Write 0 to alarm1 seconds
i2c_write(alarm1_minute); // Write alarm1 minutes value to DS3231
i2c_write(alarm1_hour); // Write alarm1 hours value to DS3231
i2c_write(0x80); // Alarm1 when hours, minutes, and seconds match
i2c_write(alarm2_minute); // Write alarm2 minutes value to DS3231
i2c_write(alarm2_hour); // Write alarm2 hours value to DS3231
i2c_write(0x80); // Alarm2 when hours and minutes match
i2c_write(4 | alarm1_status | (alarm2_status << 1)); // Write data to DS3231 control register (enable interrupt when alarm)
i2c_write(0); // Clear alarm flag bits
i2c_stop(); // Stop I2C
delay_ms(200); // Wait 200ms
}
if(!input(PIN_B2) && input(PIN_B4)){ // When button B2 pressed with alarm (Reset and turn OFF the alarm)
output_low(PIN_B4); // Turn OFF the alarm indicator
i2c_start(); // Start I2C
i2c_write(0xD0); // DS3231 address
i2c_write(0x0E); // Send register address (control register)// Write data to control register (Turn OFF the occurred alarm and keep the other as it is)
i2c_write(4 | (!bit_test(status_reg, 0) & alarm1_status) | ((!bit_test(status_reg, 1) & alarm2_status) << 1));
i2c_write(0); // Clear alarm flag bits
i2c_stop(); // Stop I2C
}
DS3231_read(); // Read time and calendar parameters from DS3231 RTC
alarms_read_display(); // Read and display alarms parameters
DS3231_display(); // Display time & calendar
delay_ms(50); // Wait 50ms
}
}
// End of code
Videos:
The following video shows a simple hardware circuit of the project.
And the second video shows a simulation of the project with Proteus.
The LM35 temperature sensor is three pin device (VCC, OUT and GND)
with an output voltage linearly related to Centigrade temperature. Since
the LM35 output varies with dependent to the temperature we need ADC
(Analog-to-Digital Converter) module to measure this voltage. The ADC
module converts analog data into digital data.
The LM35 output has linear +10mV/°C scale factor means the following:
If the output voltage = 10mV ---> temperature = 1°C
If the output voltage = 100mV ---> temperature = 10°C
If the output voltage = 200mV ---> temperature = 20°C
If the output voltage = 370mV ---> temperature = 37°C
and so on. LM35 Futures (from datasheet):
Calibrated Directly in ° Celsius (Centigrade)
Linear + 10 mV/°C Scale Factor
0.5°C Ensured Accuracy (at +25°C)
Rated for Full −55°C to +150°C Range
Suitable for Remote Applications
Low Cost Due to Wafer-Level Trimming
Operates from 4 to 30 V
Less than 60-μA Current Drain
Low Self-Heating, 0.08°C in Still Air
Nonlinearity Only ±¼°C Typical
Low Impedance Output, 0.1 Ω for 1 mA Load
This topic shows how to interface the microcontroller PIC16F877A with LM35 analog temperature sensor. Hardware Required:
The output of the LM35 temperature sensor is connected to analog channel 0 (AN0) of the PIC16F877A.
In this example the microcontroller runs with crystal oscillator @ 8MHz. Interfacing PIC16F877A with LM35 temperature sensor C code:
The C code below was tested with CCS PIC C compiler version 5.051.
Reading voltage quantity using the ADC gives us a number between 0
and 1023 (10-bit resolution), 0V is represented by 0 and 5V is
represented by 1023. Converting back the ADC digital value is easy and
we can use the following equation for that conversion:
Voltage (in Volts) = ADC reading * 5 / 1023
Multiplying the previous result by 100 (LM35 scale factor is 10mV/°C = 0.01V/°C) will gives the actual temperature:
Temperature(°C) = ADC reading * 0.489
where 0.489 = 500 / 1023
/* Interfacing PIC16F877A with LM35 analog temperature sensor CCS C code. Read LM35 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
#usedelay(clock = 8MHz)
#include <lcd.c>
char temperature[] = " 00.0 C";
unsignedint16 temp;
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
lcd_gotoxy(3, 1); // Go to column 3 row 1
printf(lcd_putc, "Temperature:");
while(TRUE){
delay_ms(1000);
temp = read_adc() * 0.489; // Read analog voltage and convert it to degree celsius (0.489 = 500/1023)if (temp > 99)
temperature[0] = 1 + 48; // Put 1 (of hundred)else
temperature[0] = ' '; // Put space
temperature[1] = (temp / 10) % 10 + 48;
temperature[2] = temp % 10 + 48;
temperature[5] = 223; // Degree symbol
lcd_gotoxy(5, 2); // Go to column 5 row 2
printf(lcd_putc, temperature); // Display LM35 temperature result
}
}
Interfacing PIC16F877A with LM35 videos:
The video below shows a simple hardware circuit of our example.