Tuto. 5 - I2C Clock
PIC tutorial 5 - I2C Clock
Fig. 1a Board 3 with LCD display and I2C Clock 1
Description:
In the 5th tutorial, we'll stay with I2C communication, but managing real time clocks (PCF8583 or DS1307).
You will need a processor board, a power supply, the LCD display and one of the I2C Clock.
The tutorial will work with a clock library. It will be able to manage either type of chips (PCF8583 or DS1307)
The boards can be powered by one of the PSU boards (powered from USB port), or between 4 to 5V.
Requirements:
Processor boardAny board (board 3 in example)
Extension(s)LCD Display
I2C Clock 1 or 2
Power supplyPSU 1, 2 or 3. Depends on board used.
LanguageCCS C
ProgrammerPicKIT 2
Program:
#define board_id 3
#if ((board_id == 1) | (board_id == 2)) // For board 1 or 2, use alphanumeric LCD
#define DISPLAY_TYPE "Alpha"
#else // else, use Nokia 3310 graphic LCD
#define DISPLAY_TYPE "3310"
#endif
#define I2C_Clock_Chip 1 // 1 for PCF8583, 2 for DS1307
#include "../lib/Boards/mainboard.c" // Load board library
#include "../lib/Display/lcd_display.c" // Load LCD display library
#include "../lib/Memory/I2C_clock.c"
#zero_ram
void main() {
boot_up(); // initialise board
init_display(); // initialise display
init_I2C_Clock(); // initialise RTC
// set_I2C_Clock(0x11, 0x47, 0x00, 0x20, 0x04, 0x11, 0x02); // Set time and date: hh, mm, ss, DD, MM, YY, WD. Uncomment to set clock
while (true) {
get_I2C_Clock(); // Reads date and time
#if (I2C_Clock_Chip == 1 )
rtc_value.year += 0x10;
#endif
display_gotoxy(0,0);
#if (I2C_Clock_Chip == 1 )
printf(display_putc, " -- PCF8583 --");
#else
printf(display_putc, " -- DS1307 --");
#endif
display_gotoxy(0,1); // goto on 1st column second row (2nd line)
printf(display_putc, "Date: %X/%X/%X",rtc_value.day, rtc_value.month, rtc_value.year); // Display date on first line
display_gotoxy(0,2); // goto on 1st column third row (3rd line)
printf(display_putc, "Time: %X:%X:%X",rtc_value.hour, rtc_value.minute, rtc_value.second); // Display time on second line
display_gotoxy(0,3); // goto on 1st column fourth row (4th line)
switch (rtc_value.weekday) { // process weekday and display it
case 0:
printf(display_putc, "Monday");
break;
case 1:
printf(display_putc, "Tuesday");
break;
case 2:
printf(display_putc, "Wednesday");
break;
case 3:
printf(display_putc, "Thursday");
break;
case 4:
printf(display_putc, "Friday");
break;
case 5:
printf(display_putc, "Saturday");
break;
default: printf(display_putc, "Sunday");
}
delay_ms(1000);
}
}
The program defines the processor board used and the type of I2C Clock used (1 or 2 for PCF8583 or DS1307). It is defined intially as 1, so we'll use a PCF8583 clock.
We then load the libraries for the processor, lcd display and I2C clock.
The I2C clock library has a set of functions to initialise, configure, read and set time/date or memory.
Once all libraries are loaded, the processor board, LCD display and clock are initialised.
The line commented is to set initially the date/time in the chip. Once set, make sure you comment it out and reprogram the chip, else the date/time will be set to what is defined in the line at every boot.
The main loop is then entered: we read the clock. We then display on the first line the chip used, on line 2 the date, on line 3 the time and on line 4 the day of the week (Depending on the value of weekday, we display the name of the day (0 is Monday, but you can change it to whatever you want). We wait for a second, before starting the loop again.
Now to the I2C clock library.
#ifndef _I2C_Clock_LIB
#define _I2C_Clock_LIB
#include "../lib/Common/I2C.c"
#ifndef I2C_Clock_Chip // set chip used if not defined
#define I2C_Clock_Chip 1 // 1 for PCF8583, 2 for DS1307
#endif
struct rtc_var { BYTE second; BYTE minute; BYTE hour; BYTE day; BYTE month; BYTE year; BYTE weekday; }
rtc_value;
// Functions prototypes
void config_I2C_Clock(BYTE cfg);
BYTE read_I2C_Clock(BYTE rtcreg);
void write_I2C_Clock(BYTE rtcreg, BYTE rtc_data);
void init_I2C_Clock(void);
void get_I2C_Clock(void);
void set_I2C_Clock(BYTE hour, BYTE minut, BYTE second, BYTE day, BYTE month, BYTE year, BYTE weekday);
void config_I2C_Clock(BYTE cfg) { // Write cfg to config register
i2c_start();
#if (I2C_Clock_Chip == 1 )
i2c_write(I2C_clock);
i2c_write(0x00); // point to config register
#else
i2c_write(I2C_clock2);
i2c_write(0x07); // point to config register
#endif
i2c_write(cfg);
i2c_stop();
}
BYTE read_I2C_Clock(BYTE rtcreg) { // rtc read subroutine
BYTE datain;
i2c_start();
#if (I2C_Clock_Chip == 1 )
i2c_write(I2C_clock); // Device address
#else
i2c_write(I2C_clock2);
#endif
i2c_write(rtcreg); // Send address of register to read
i2c_start(); // Restart
#if (I2C_Clock_Chip == 1 ) // Change data direction
i2c_write(I2C_clock+1);
#else
i2c_write(I2C_clock2+1);
#endif
datain=i2c_read(0); // Now read from slave
i2c_stop();
return(datain);
}
void write_I2C_Clock(BYTE rtcreg, BYTE rtc_data) { // Writes rtc_data at address rtcreg
i2c_start();
#if (I2C_Clock_Chip == 1 )
i2c_write(I2C_clock); // Device address
#else
i2c_write(I2C_clock2); // Device address
#endif
i2c_write(rtcreg);
i2c_write(rtc_data);
i2c_stop();
}
void init_I2C_Clock() {
#if (I2C_Clock_Chip == 1 )
config_I2C_Clock(0x00); // Setup config
#else
BYTE tmp;
config_I2C_Clock(0x90); // Setup config, with 1Hz output on int
tmp = read_I2C_Clock(0x02); // Set in 24h mode
bit_clear(tmp,6);
write_I2C_Clock(0x02, tmp);
tmp = read_I2C_Clock(0x00); // Make sure oscillator is running (bit 7 reg0 = 0)
bit_clear(tmp,7);
write_I2C_Clock(0x00, tmp);
#endif
}
void get_I2C_Clock() {
#if (I2C_Clock_Chip == 1 )
BYTE tmp;
rtc_value.second=read_I2C_Clock(0x02) & 0x7f;
rtc_value.minute=read_I2C_Clock(0x03) & 0x7f;
rtc_value.hour=read_I2C_Clock(0x04) & 0x3f;
tmp=read_I2C_Clock(0x05);
rtc_value.day = tmp & 0x3f; // day is 6 lower bits
rtc_value.year = (tmp & 0xc0) >> 6; // year is 2 upper bits
tmp=read_I2C_Clock(0x06);
rtc_value.month = tmp & 0x1f; // month is 5 lower bits
rtc_value.weekday = (tmp & 0xe0) >> 5; // weekday is 3 upper bits
#else
rtc_value.second=read_I2C_Clock(0x00) & 0x7f;
rtc_value.minute=read_I2C_Clock(0x01) & 0x7f;
rtc_value.hour=read_I2C_Clock(0x02) & 0x7f;
rtc_value.weekday=read_I2C_Clock(0x03) & 0x07;
rtc_value.day=read_I2C_Clock(0x04) & 0x3f;
rtc_value.month=read_I2C_Clock(0x05) & 0x1f;
rtc_value.year=read_I2C_Clock(0x06);
#endif
}
void set_I2C_Clock(BYTE hour, BYTE minut, BYTE second, BYTE day, BYTE month, BYTE year, BYTE weekday) {
#if (I2C_Clock_Chip == 1 )
BYTE tmp;
write_I2C_Clock(0x02, second & 0x7f);
write_I2C_Clock(0x03, minut & 0x7f);
write_I2C_Clock(0x04, hour);
tmp = day & 0x3f;
tmp = tmp | ((year % 4) << 6); // year is 2 upper bits of byte
write_I2C_Clock(0x05, tmp);
tmp = month & 0x1f;
tmp = tmp | (weekday << 5); // weekday is 3 upper bits of byte
write_I2C_Clock(0x06, tmp);
#else
write_I2C_Clock(0x00, second & 0x7f);
write_I2C_Clock(0x01, minut & 0x7f);
write_I2C_Clock(0x02, hour & 0x7f);
write_I2C_Clock(0x03, weekday & 0x07);
write_I2C_Clock(0x04, day & 0x3f);
write_I2C_Clock(0x05, month & 0x1f);
write_I2C_Clock(0x06, year);
#endif
}
#endif
Fig. 1b Board 3 with LCD display and I2C Clock 2
First we load the I2C library and define the chip used, if not already done.
Each function is similar between the different sensor chip. The main differences are in the slave address, registers address and how they store year and week days. On the PCF8583, the year is stored as a number between 0 and 3, so it can count the leap years, but it doesn't keep the actual value of the year. It could be stored in the generic memory of the chip, but managing it complicates the tutorial beyond need.
- config_I2C_Clock send a byte to the configuration register of the RTC.
- init_I2C_Clock configure the chip for 32.768 KHz oscillator and 1Hz output on Int signal.
- read_I2C_Clock returns the byte store at the specified address in the RTC chip.
- write_I2C_Clock writes a byte at the specified address in the RTC chip.
- get_I2C_Clock get date and time from the RTC into the structure rtc_value.
- set_I2C_Clock set the date and time in the RTC (hours, minutes, seconds, day, month, year, week day).
Compile the project and program the target with a PicKIT 2 programmer for example. Connect the processor board to the LCD display board on port B and the I2C clock on the I2C connector, making sure the jumpers on the board match the slave address defined in I2C_Addr.h.
Power up the boards.
The date and time in the chip will be displayed on the LCD display.
To change clock type, just define I2C_Clock_Chip as 2 and recompile. The program is then set for a DS1307 sensor.
Files and links:
Tutorial 5 source files.Processor board 3.
LCD Display board.
I2C Clock 1.
I2C Clock 2.

