Tuto. 5 - I2C Clock

PIC tutorial 5 - I2C Clock

Print
Category: PIC Tutorials
Published Date Written by Francois

Tutorial 5a

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)

You can download the files for this tutorial in the zip file pic_tutorial5.zip.

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


Tutorial 5b

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.

Post your comments...

    Copyright 2011. Poker Games. Copyright © 2012 riaDesign