Philips PCF8583 RTC (I2C) - Writing and Reading Time



/* 8583_1.C, Flashlite V25
**
** Writes a base time and date to clock.  Reads clock about every
** second and displays on terminal.
**
** Note that time and date data is stored in BCD.  In this routine
** natural binary is converted to BCD when writing to the PCF8583.  When
** reading data, the quanties are converted to natural binary.
**
** Flashlite V25              PCF8583
**
** P2.1  ------------------- SCL (term 6) ----- To Other
** P2.0  ------------------- SDA (term 5) ----- I2C Devices
**
** I2C address is 0xa0 or 0xa2 depending on strapping of A0 (terminal 3)
** In this example, A0 is at ground.
**
** copyright, Peter H. Anderson, Onya Barnes, Baltimore, MD, October, '00
*/

#include <stdio.h>
#include <dos.h>

typedef unsigned char byte;

#define SEG 0xf000

#define P2_OFFSET 0xff10
#define PM2_OFFSET 0xff11
#define PMC2_OFFSET 0xff12

struct Time
{
   byte hr;
   byte mi;
   byte se;
};

struct Date
{
   byte yr;
   byte mo;
   byte da;
   byte weekday;
};

/* routines used for PCF8583 */
void _8583_configure_control_register(byte control_reg);
void _8583_write_clock(struct Time *p_t, struct Date *p_d);
void _8583_read_clock(struct Time *p_t, struct Date *p_d);

void _8583_display_date_time(struct Time *p_t, struct Date *p_d);

void put_dec_byte(byte v, byte places);
byte to_BCD(byte natural_binary);
byte to_natural_binary(byte BCD);

/*common i2c routines*/
byte i2c_in_byte(void);
void i2c_out_byte(byte o_byte);
void i2c_nack(void);
void i2c_ack(void);
void i2c_start(void);
void i2c_stop(void);
void i2c_high_sda(void);
void i2c_low_sda(void);
void i2c_high_scl(void);
void i2c_low_scl(void);


/* note global */
byte far *p2, far *pm2, far *pmc2;
byte dirs2, outs2;

void main(void)
{

   struct Time t_base, t;
   struct Date d_base, d;

   clrscr();

   p2=MK_FP(SEG, P2_OFFSET);
   pm2=MK_FP(SEG, PM2_OFFSET);
   pmc2=MK_FP(SEG, PMC2_OFFSET);

   dirs2=0xff;

   *pmc2=0x00;
   *pm2=dirs2;

   t_base.hr=23;  t_base.mi=59;  t_base.se=00;
   d_base.yr=3; d_base.mo=2; d_base.da=28; d_base.weekday=1;
   /* Note that year is 0 through 3.  Thus, if the base year is 2000
   ** this is Feb 28, 2003, Monday.
   */
   _8583_display_date_time(&t_base, &d_base);

   _8583_configure_control_register(0x00); /* 32.768 kHz, no alarm */
   _8583_write_clock(&t_base, &d_base);

   while(!kbhit())
   {
      _8583_read_clock(&t, &d);
      _8583_display_date_time(&t, &d);

      delay(1000);
   }
}

void _8583_write_clock(struct Time *p_t, struct Date *p_d)
{
/* Note that most sig bit of hours is 12/24 hour format
** 4 year is in bits 7 and 6 of 0x05.  Lower six bits are day in BCD
** Location 0x06.  Weeks day is in bits 7, 6, 5 and month in lower
** five bits.
*/

   byte v;

   i2c_start();
   i2c_out_byte(0xa0);
   i2c_nack();
   i2c_out_byte(0x01);  /* address of first write */
   i2c_nack();
   i2c_out_byte(0x00);  /* hundreths of a second */
   i2c_nack();
   v = to_BCD(p_t->se);
   i2c_out_byte(v);
   i2c_nack();
   v = to_BCD(p_t->mi);
   i2c_out_byte(v);  /*  location 0x03 */
   i2c_nack();
   v = to_BCD(p_t->hr);
   i2c_out_byte(v);
       /* 24 hour format - 0x80 for 12 hour time */
   i2c_nack();
   v = to_BCD(p_d->da);
   i2c_out_byte((p_d->yr << 6) | v);
                     /* YY TT UUUU */
   i2c_nack();
   v = to_BCD(p_d->mo);
   i2c_out_byte(
          (p_d->weekday << 5) | v );
                     /* DDD T UUUU */
   i2c_nack();
   i2c_stop();
}

void _8583_read_clock(struct Time *p_t, struct Date *p_d)
{
   byte v;
   i2c_start();
   i2c_out_byte(0xa0);
   i2c_nack();
   i2c_out_byte(0x02);  /* begin with seconds */
   i2c_nack();

   i2c_start();
   i2c_out_byte(0xa1);
   i2c_nack();
   v = i2c_in_byte() & 0x7f;
   p_t->se = to_natural_binary(v);
   i2c_ack();
   v = i2c_in_byte();
   p_t->mi = to_natural_binary(v);
   i2c_ack();
   v = i2c_in_byte() & 0x3f;
   p_t->hr = to_natural_binary(v);
   i2c_ack();

   v = i2c_in_byte();
   p_d->yr = v >> 6;    /* year is in two most sig bits */
   v = v & 0x3f;        /* day in lower six bits */
   p_d->da = to_natural_binary(v);
   i2c_ack();

   v = i2c_in_byte();
   p_d->weekday = v >> 5;
   v = v & 0x1f;
   p_d->mo = to_natural_binary(v);
   i2c_nack();
   i2c_stop();
}

void _8583_configure_control_register(byte control_reg)
{
   i2c_start();
   i2c_out_byte(0xa0);
   i2c_nack();
   i2c_out_byte(0x00);  /* control register address */
   i2c_nack();
   i2c_out_byte(control_reg);
   i2c_nack();
   i2c_stop();
}

void _8583_display_date_time(struct Time *p_t, struct Date *p_d)
{
  /*assumes base year of 2000*/
  printf("%0.2d/%0.2d/%0.2d\n", p_d->yr +00, p_d->mo, p_d->da);
  printf("%.02:%0.2d:%0.2d\n", p_t->hr, p_t->mi, p_t->se);
  printf("%d\n", p_d->weekday);

}

byte to_BCD(byte natural_binary)
{
   return ( ((natural_binary/10) << 4) + natural_binary%10 );
}

byte to_natural_binary(byte BCD)
{
   return(  ((BCD >> 4) * 10) + (BCD & 0x0f)  );
}


/* Common I2C Routines*/

byte i2c_in_byte(void)
{

   byte i_byte=0, n;

   i2c_high_sda();
   for (n=0; n<8; n++)
   {
      i2c_high_scl();
      if (*(p2) & 0x01)
      {
         i_byte = (i_byte << 1) | 0x01; /* msbit first */
      }
      else
      {
         i_byte = i_byte << 1;
      }
      i2c_low_scl();
   }
   return(i_byte);
}

void i2c_out_byte(byte o_byte)
{
   byte n;

   for(n=0; n<8; n++)
   {
      if(o_byte&0x80)
      {
         i2c_high_sda();
      }
      else
      {
         i2c_low_sda();
      }
      i2c_high_scl();
      i2c_low_scl();
      o_byte = o_byte << 1;
   }
   i2c_high_sda();
}

void i2c_nack(void)
{
   i2c_high_sda();   /*data at one*/
   i2c_high_scl();   /*clock pulse*/
   i2c_low_scl();
}

void i2c_ack(void)
{
   i2c_low_sda(); /*bring data low and clock*/
   i2c_high_scl();
   i2c_low_scl();
   i2c_high_sda();
}


void i2c_start(void)
{
   i2c_low_scl();
   i2c_high_sda();
   i2c_high_scl();   /* bring SDA low while SCL is high*/
   i2c_low_sda();
   i2c_low_scl();
}

void i2c_stop(void)
{
   i2c_low_scl();
   i2c_low_sda();
   i2c_high_scl();
   i2c_high_sda();   /*bring SDA high while SCL is high*/
         /*idle is SDA high and SCL high*/
}

void i2c_high_sda(void)
{
   dirs2 = dirs2 | 0x01;
   *pm2 = dirs2;
   #ifdef D /* used for debugging */
      printf("SDA_HIGH %x\n", dirs2);
      delay(500);
   #endif
}

void i2c_low_sda(void)
{
   outs2 = outs2 & (~0x01);   /* zero the output pin */
   *p2 = outs2;
   dirs2 = dirs2 & (~0x01);    /* and output */
   *pm2 = dirs2;
   #ifdef D
      printf("SDA_LOW %x\n", dirs2);
      delay(500);
   #endif
}

void i2c_high_scl(void)
{
   dirs2 = dirs2 | 0x02;  /* bring SCL to high impedance */
   *pm2 = dirs2;
   #ifdef D
      printf("SCL_HIGH %x\n", dirs2);
      delay(500);
   #endif
}

void i2c_low_scl(void)
{
   outs2 = outs2 & (~0x02);
   *p2 = outs2;            /* zero the output pin */
   dirs2 = dirs2 & (~0x02);    /* and output */
   *pm2 = dirs2;
   #ifdef D
      printf("SCL_LOW %x\n", dirs2);
      delay(500);
   #endif
}