Dallas DS1307 Real Time Clock (I2C)



/* Program DS1307_1.C (Flashlite V25)
**
** Illustrates an interface with the Dallas DS1307 Real Time Clock.
**
** Writes a time and date to the DS1307 and then continually reads and
** displays on the terminal.
**
** Note that the date and time data is stored in BCD format.
**
** FLASHLIGHT V25                DS1307
**
** P2.1 ------------------------ SCL  ----- To Other
** P2.0 ------------------------ SDA  ----- I2C Devices
**
**
** copyright, Peter H. Anderson, Onya Barnes. Baltimore, MD, Oct, '00
*/

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

#define SEG 0xf000

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

/* #define D */

typedef unsigned char byte;

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

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

/*routines used for DS1307*/
void set_up_clock(byte control_reg);
void write_clock(struct Time *p_t, struct Date *p_d);
void read_clock(struct Time *p_t, struct Date *p_d);

/*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={0x23, 0x59, 0x00}, t;
   struct Date d_base={0x99, 0x02, 0x28, 0x07}, d;

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

   /*no special purpose*/
   *pmc2=0x00;
   /* */
   dirs2=0xff;
   *pm2=dirs2;

   set_up_clock(0x10);
   write_clock(&t_base, &d_base);

   while(1)
   {
      read_clock(&t, &d);
      printf("%0.2x/%0.2x/%0.2x, %0.2x:%0.2x:%0.2x\n",
                d.mo, d.da, d.yr, t.hr, t.mi, t.se);
      printf("%d\n", d.weekday);
      delay(1000);
   }
}

/*DS1307 Routines*/
void set_up_clock(byte control_reg)
{
   i2c_start();
   i2c_out_byte(0xd0);
   i2c_nack();
   i2c_out_byte(0x07);
   i2c_nack();
   i2c_out_byte(control_reg);
   i2c_nack();
   i2c_stop();
}

void write_clock(struct Time *p_t, struct Date *p_d)
{
   i2c_start();
   i2c_out_byte(0xd0);
   i2c_nack();
   i2c_out_byte(0x00);  /* address of first write*/
   i2c_nack();
   i2c_out_byte(p_t->se);
   i2c_nack();
   i2c_out_byte(p_t->mi);
   i2c_nack();
   i2c_out_byte(p_t->hr);     /* 24 hour format - 0x80 for 12 hour time*/
   i2c_nack();
   i2c_out_byte(p_d->weekday);      /* YY TT UUUU*/
   i2c_nack();
   i2c_out_byte(p_d->da);  /* DDD T UUUU*/
   i2c_nack();
   i2c_out_byte(p_d->mo);     /* YY TT UUUU*/
   i2c_nack();
   i2c_out_byte(p_d->yr);
   i2c_nack();
   i2c_stop();
}

void read_clock(struct Time *p_t, struct Date *p_d)
{
   i2c_start();
   i2c_out_byte(0xd0);
   i2c_nack();
   i2c_out_byte(0x00);  /* begin with seconds*/
   i2c_nack();
   i2c_stop();

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


   p_d->weekday = i2c_in_byte() & 0x07;
   i2c_ack();
   p_d->da = i2c_in_byte() & 0x3f;
   i2c_ack();
   p_d->mo = i2c_in_byte() & 0x3f;
   i2c_ack();
   p_d->yr = i2c_in_byte();
   i2c_nack();
   i2c_stop();
}

/* 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
}