Interfacing with a Dallas DS1624 Thermometer


/* Program DS1624_1.C
**
** Illustrates an interface with a Dallas DS1624 Thermometer and EEPROM.
** This device is controled using the Philips I2C protocol.
**
** Continually performs temperature measurement and displays
** using the full 13 bit result.
**
** The fractional part of the temperature is in the high five bits of the
** second byte fecthed from the DS1624. Thus, 10101000 would be nominally
** 500/1000 + 125/1000 + 31/1000.
**
** Flashlite               DS1624
**
** P2.1 ------------------> SCL
** P2.0 <-----------------> SDA
**
** Note that the local address (A2, A1 A0) is strapped for 0.
**
** Note that this implementation does not deal with temperatures less
** than 0.
**
** copyright Peter H. Anderson and Onya Barnes, Baltimore, MD, Sept, '00
*/

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

#define SEG 0xf000

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

typedef unsigned char byte;


/* function in program */
int ds1624_compute_fraction(byte t_fract);
void ds1624_config(byte dev_adr, byte mode);
void ds1624_init(byte dev_adr);
void ds1624_meas_temp(byte dev_adr, byte *p_whole, byte *p_fract);

/* 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;
byte far *pm2;
byte far *pmc2;
byte dirs2, outs2;

void main(void)
{
   byte t_whole, t_fract;
   int fract;

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

   dirs2=0xff;    /* make all of P2 inputs */

   *pmc2=0x00;
   *pm2=dirs2;

   ds1624_config(0x00,0x01);  /* dev adr is 0x00, mode is 0x01 - 1 shot */
   while(1)
   {
      ds1624_init(0x00);
      /* note that pointers are passed */
      ds1624_meas_temp(0x00, &t_whole, &t_fract);
      fract = ds1624_compute_fraction(t_fract)/10;
       /*convert to decimal format*/
      printf("%d.%d\n", t_whole, fract);
      delay(1000);
   }
}

int ds1624_compute_fraction(byte t_fract)
/* converts high five bits in t_fract to a number in the range of
** 0 - 1000.
*/
{
   int sum = 0;
   byte y, n;
   const int dec_vals[5] = {31, 62, 125, 250, 500};
                     /* 31.25 / 1000, 62.5/1000, 125/1000, etc*/
   y = t_fract >> 3; /* fractional part is now in lowest five bits*/
   for (n=0; n<5; n++)
   {
      if (y&0x01)
      {
         sum = sum + dec_vals[n];
      }
      y = y >> 1;
   }
   return(sum);
}

void ds1624_config(byte dev_adr, byte mode)
/* configures DS1624 in specified mode */
{
   i2c_start();
   i2c_out_byte(0x90 | (dev_adr << 1));
   i2c_nack();
   i2c_out_byte(0xac);     /* access configuration */
   i2c_nack();
   i2c_out_byte(mode);
   i2c_nack();
   i2c_stop();
   delay(25);        /* wait for EEPROM to program */
}

void ds1624_init(byte dev_adr)
{
   i2c_start();
   i2c_out_byte(0x90 | (dev_adr << 1));
   i2c_nack();
   i2c_out_byte(0xee);     /*start conversion*/
   i2c_nack();
   i2c_stop();
   delay(1000);
}

void ds1624_meas_temp(byte dev_adr, byte *p_whole, byte *p_fract)
/* fetches temperature result.  Values t_whole and t_fract returned using
** pointers
*/
{
   i2c_start();
   i2c_out_byte(0x90 | (dev_adr << 1));
   i2c_nack();
   i2c_out_byte(0xaa);  /* fetch temperature */
   i2c_nack();
   i2c_start();         /* no intermediate stop */
   i2c_out_byte(0x90 | (dev_adr << 1) | 0x01);
   i2c_nack();
   *p_whole=i2c_in_byte(); /* value pointed to by p_whole */
   i2c_ack();
   *p_fract=i2c_in_byte();
   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
}