How to Use RTC with Arduino and LCD
How to make an accurate clock using the Real Time Clock IC DS1307. The time will be shown on a LCD display.
Step 1: How to Use RTC with Arduino and LCD
Requirements
- Computer running Arduino 1.6.5
- Arduino
- Jumper wires
- Breadboard
- Parts from parts list
You can substitute the Arduino with another IC from ATMEL, but make sure it has the sufficient number of input and output pins along with I2C capability. I'm using the ATMega168A-PU. If you do that, you will need a programmer. I have the AVR MKII ISP programmer.
It is recommended that the reader is familiar with breadboarding, programming in the Arduino environment, and has some knowledge of the C programming language. The two programs below should not need additional explanation.
Introduction
How do microcontrollers keep track of time and date? The regular microcontroller has a timer function that starts at 0 (zero) when power is applied, and then it starts to count. In the Arduino world, we can use the millis() function to reset how many milliseconds have passed since the power was applied. When you disconnect and reconnect the power, it starts all over again. This is not so convenient when it comes to clocks and dates.
That is where the Real Time Clock--or the RTC--chip comes in handy. This IC, with a 3v coin cell battery or another 3v power supply, keeps track of the time and date. The clock/calendar provides seconds, minutes, hours, day, date, month, and year information. The IC corrects for months with 30/31 days and leap years. The communication is done with the I2C bus. The I2C bus will not be discussed here.
If the main circuit's Vcc falls below Vbat, the RTC switches automatically over to a low-power battery backup mode. The backup battery is usually a 3v coin cell battery connected to PIN 3 and GND. This way, the IC is still keeping track of time and date, and when power is applied to the main circuitry, the microcontroller gets the current time and date.
In this project we are using the DS1307. On this IC, PIN 7 is a SQW/OUT pin. You can use this pin to flash a LED, or to clock the microcontroller. We will do both. The following image, from the datasheet, helps us understand the SQW/OUT.
This table helps you with the frequency:
Freq | BIT7 & BIT6 & BIT5 | BIT4 | BIT3 & BIT2 | BIT1 | BIT0 |
---|---|---|---|---|---|
1Hz | 0 | 1 | 0 | 0 | 0 |
4.096Hz | 0 | 1 | 0 | 0 | 1 |
8.192Hz | 0 | 1 | 0 | 1 | 0 |
32.768Hz | 0 | 1 | 0 | 1 | 1 |
If you connect an LED and a resistor to PIN 7, and want it to flash with 1Hz frequency, you write 0b00010000 to the control register memory address. If you want 4.096 Hz, you would write 0b000100001. Now you would need an oscilloscope to see the pulses, since the LED is flashing so fast that it looks like it is constant on. We are using 1Hz.
Hardware
Here's the block diagram for what we want.
We want:
- ISP (In System Programming) to program the microcontroller
- Push buttons to set the time and date
- The microcontroller to communicate with the RTC via I2C
- Display the time and date on the LCD
The schematic diagram:
Click on image for full size.
Partlist
This is a screenshot from Eagle:
Software
We will be using two different sketches in this how-to: one that writes the time and date to the RTC, and one that reads the time and date from the RTC. I have done it like this so you will have a better overview of what is going on. We will be using the same circuit as above for both the programs.
First we will write the time and date to the RTC, which is like setting the time on a watch.
We are using two switches. One is to increase the hour, minute, date, month, year and day of week, and the other is to choose between them. The application does not read values from any critical sensors, so we are using interrupts to check if a switch is pressed and to handle the switch bounce. For more information on switch bounce read this.
The following code will set the values and write them to the RTC:
// Include header files
#include <Wire.h>
#include <LiquidCrystal.h>
// LCD pin definitions
#define RS 9
#define E 10
#define D4 8
#define D5 7
#define D6 6
#define D7 5
LiquidCrystal lcd(RS, E, D4, D5, D6, D7);
// Interrupt 0 is hardware pin 4 (digital pin 2)
int btnSet = 0;
// Interrupt 1 is hardware pin 5 (digital pin 3)
int btnSel = 1;
// Interrupt state
int togBtnSet = false;
int togBtnSel = false;
// Time and date variables
int tmpHour = 0;
int tmpMinute = 0;
int tmpDate = 0;
int tmpMonth = 0;
int tmpYear = 0;
int tmpDay = 0;
int tmpSecond = 0;
int counterVal = 0;
// Variable to keep track of where we are in the "menu"
int myMenu[6]; // 0=Hour, 1=Minutes, 2=date, 3=MOnth, 4=Year, 5=DOW
int menuCounter = 0;
// A array of the weekday
char* days[] = { "NA", "Mon", "Tue", "Wed", "Thu", "Fre", "Sat", "Sun" };
void setup() {
// Interrupt declaration, execute increaseValue/nextItem function
// when btnXXX is RISING
attachInterrupt(btnSet, increaseValue, RISING);
attachInterrupt(btnSel, nextItem, RISING);
Wire.begin();
lcd.begin(16,2);
showWelcome();
}
// Interrupt function
void increaseValue()
{
// Variables
static unsigned long lastInterruptTime = 0;
// Making a timestamp
unsigned long interruptTime = millis();
// If timestamp - lastInterruptTime is greater than 200
if (interruptTime - lastInterruptTime > 200)
{
// Toggle the variable
togBtnSet = !togBtnSet;
// Increase the counterVal by 1
counterVal++;
}
// Setting lastInterruptTime equal to the timestamp
// so we know we moved on
lastInterruptTime = interruptTime;
}
// Next menuItem Interrupt function
void nextItem()
{
static unsigned long lastInterruptTime = 0;
unsigned long interruptTime = millis();
if (interruptTime - lastInterruptTime > 200)
{
togBtnSel = !togBtnSel;
// Increase the menu counter so we move to next item
menuCounter++;
// Placing the counterVal in the menucounters array position
myMenu[menuCounter] = counterVal;
// Reset counterVal, now we start at 0 on the next menuItem
counterVal = 0;
}
lastInterruptTime = interruptTime;
}
// Function that convert decimal numbers to binary
byte decToBCD(byte val)
{
return ((val/10*16) + (val));
}
// Short welcome message, now we know everything is OK
void showWelcome()
{
lcd.setCursor(2,0);
lcd.print("Hello world.");
lcd.setCursor(3,1);
lcd.print("I'm alive.");
delay(500);
lcd.clear();
}
// Funcion to set the hour
void setHour()
{
lcd.setCursor(0,0);
lcd.print("Set hour. ");
// Checks if interrupt has occured = button pressed
if (togBtnSet)
{
// Update array value with counterVal
myMenu[menuCounter] = counterVal;
lcd.setCursor(7,1);
// Print the new value
lcd.print(myMenu[menuCounter]); lcd.print(" ");
}
else
{
// Update array value with counterVal
myMenu[menuCounter] = counterVal;
lcd.setCursor(7,1);
// Print the new value
lcd.print(myMenu[menuCounter]); lcd.print(" ");
}
}
// Function to set minutes
void setMinute()
{
lcd.setCursor(0,0);
lcd.print("Set minute. ");
if (togBtnSet)
{
myMenu[menuCounter] = counterVal;
lcd.setCursor(7,1);
lcd.print(myMenu[menuCounter]); lcd.print(" ");
}
else
{
myMenu[menuCounter] = counterVal;
lcd.setCursor(7,1);
lcd.print(myMenu[menuCounter]); lcd.print(" ");
}
}
// Function to set date
void setDate()
{
lcd.setCursor(0,0);
lcd.print("Set date. ");
if (togBtnSet)
{
myMenu[menuCounter] = counterVal;
lcd.setCursor(7,1);
lcd.print(myMenu[menuCounter]); lcd.print(" ");
}
else
{
myMenu[menuCounter] = counterVal;
lcd.setCursor(7,1);
lcd.print(myMenu[menuCounter]); lcd.print(" ");
}
}
// Function to set month
void setMonth()
{
lcd.setCursor(0,0);
lcd.print("Set month. ");
if (togBtnSet)
{
myMenu[menuCounter] = counterVal;
lcd.setCursor(7,1);
lcd.print(myMenu[menuCounter]); lcd.print(" ");
}
else
{
myMenu[menuCounter] = counterVal;
lcd.setCursor(7,1);
lcd.print(myMenu[menuCounter]); lcd.print(" ");
}
}
// Function to set year
void setYear()
{
lcd.setCursor(0,0);
lcd.print("Set year. ");
if (togBtnSet)
{
myMenu[menuCounter] = counterVal;
lcd.setCursor(7,1);
lcd.print(myMenu[menuCounter]); lcd.print(" ");
}
else
{
myMenu[menuCounter] = counterVal;
lcd.setCursor(7,1);
lcd.print(myMenu[menuCounter]); lcd.print(" ");
}
}
// Function to set the day of week
void setDOW()
{
lcd.setCursor(0,0);
lcd.print("Set day (1=mon).");
if (togBtnSet)
{
myMenu[menuCounter] = counterVal;
lcd.setCursor(7,1);
lcd.print(myMenu[menuCounter]); lcd.print(" ");
}
else
{
myMenu[menuCounter] = counterVal;
lcd.setCursor(7,1);
lcd.print(myMenu[menuCounter]); lcd.print(" ");
}
}
// Write the data to the RTC
void writeRTC()
{
Wire.beginTransmission(0x68);
Wire.write(0); // Start address
Wire.write(0x00); // seconds
Wire.write(decToBCD(myMenu[1])); // convert tmpMinutes to BCD and write them
Wire.write(decToBCD(myMenu[0])); // convert tmpHour to BCD and write them
Wire.write(decToBCD(myMenu[5])); // convert tmpDay to BCD and write them
Wire.write(decToBCD(myMenu[2])); // convert tmpDate to BCD and write them
Wire.write(decToBCD(myMenu[3])); // convert tmpMonth to BCD and write them
Wire.write(decToBCD(myMenu[4])); // convert tmpYear to BCD and write them
Wire.write(0b00010000); // enable 1Hz Square wave on PIN7
Wire.endTransmission(); // close the transmission
}
// Show the time
// You need to use the other program to see the RTC is working
void showTime()
{
lcd.setCursor(0,0);
lcd.print(" ");
lcd.print(myMenu[0]); lcd.print(":"); // hour
lcd.print(myMenu[1]); lcd.print(":"); lcd.print("00 "); // minute
lcd.setCursor(3,1);
lcd.print(days[myMenu[5]]); lcd.print(" "); // DOW
lcd.print(myMenu[2]); lcd.print("."); // date
lcd.print(myMenu[3]); lcd.print("."); // month
lcd.print(myMenu[4]); lcd.print(" "); // year
// Call the writeRTC function
writeRTC();
}
void loop()
{
if (menuCounter == 0)
{
setHour();
}
if (menuCounter == 1)
{
setMinute();
}
if (menuCounter == 2)
{
setDate();
}
if (menuCounter == 3)
{
setMonth();
}
if (menuCounter == 4)
{
setYear();
}
if (menuCounter == 5)
{
setDOW();
}
if (menuCounter == 6)
{
showTime();
}
}
- Comments(0)
- Likes(0)