An Implementation of Dallas DS1821 Thermostat Programmer



/* DS1821_2.C  Flashlite V25,  DS1821 Thermostat Programmer
**
** This is an implementation of a system to permit a user to custom
** program a DS1821 thermometer.  It was developed for agricultural
** applications including both thermostat and alarm applications.
**
** The low cost of the DS1821 ($2.50 in quantities of 100) makes it
** attractive in applications where other thermostat and alarm
** arrangement are not feasible.
**
**
** The user is prompted to connect the DS1821 to be programmed.
**
** The program checks to see if DS1821 is in Thermostat mode.  If so,
** the DS1821 is toggled to the communications mode.  If the device is
** found, the user is presented with a menu.
**
**
** The user is prompted to either read the values of T_H, T_L and POL
** currently stored in the DS1821, to set a new value of T_H, T_L or POL,
** to display the new values to be programmed, to display the THF and TLF
** falgs, to perform a continual temperature measurement, to save the new
** parameters to the DS1821 or to quit.
**
** When saving, the THF and TLF flag bits are set to zero and T/R is set
** to 1 such that when the DS1821 again boots, it will boot in the
** thermostat mode.
**
** Note that Quitting without Saving will result in the programmed
** settings not being modified.
**
** Flashlite V25                        DS1821 (TO-220)
** P2.1 (term 25) ----------------------- VDD (term 1)
** P2.0 (term 26) ------------------------ DQ (term 3)
**                                        GRD (term 2)
**
** Note that a 4.7K pullup resistor to +5V is required on the DQ lead.
**
** The program also illustrates the use of getch(), toupper(), flashall(),
** and kbhit().
**
** copyright, Peter H. Anderson, Baltimore, MD, Sept, '00
*/

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

typedef unsigned char byte;

byte find_and_configure_device(void);
byte fetch_and_program_settings(void);

byte _1821_make_meas(void);
byte _1821_get_status(void);
void _1821_program_status(byte config);

byte _1821_get_T_H(void);
byte _1821_get_T_L(void);

void _1821_program_T_H(byte T_H);
void _1821_program_T_L(byte T_L);

void _1821_toggle_mode(void);
byte  _1821_presence(void);
     /* same as init except returns if presence pulse */
byte _1w_in_byte(void);
void _1w_out_byte(byte d);
void _1w_pin_hi(void);
void _1w_pin_low(void);

#define SEG 0xf000

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

#define PRESENT 1

#define FALSE 0
#define TRUE !FALSE

#define _1SHOT 0x01     /* status bits in DS1821 */
#define POL 0x02
#define THERMOSTAT 0x04
#define TLF 0x08
#define THF 0x10

byte far *p2, *pm2, *pmc2;    /* note global declaration of pointers */

void main(void)
{

   byte _1821_exists, T_H_setting, T_L_setting, POL_setting, op;

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

   *pmc2 = 0x00;

   while(1)
   {
       _1821_exists = find_and_configure_device();
       /* detect if device exists and if so, config in comm mode */
       if (!_1821_exists)
       {
           printf("Device not found.\n");
           printf("Enter \"X\" to exit.  Any other key to continue.");
           op = getch();
           flushall();
           if (toupper(op) == 'X')
           {
              exit(0);
           }
       }

       else
       {
           printf("Device found.\n");
           fetch_and_program_settings();  /* menu */

           printf("Done\n");
           printf("Enter \"X\" to exit.  Any other key to continue.");
           op = getch();
           printf("\n");
           flushall();
           if (toupper(op) == 'X')
           {
               exit(0);
           }
       }
   }
}

byte fetch_and_program_settings(void)
{
/* menu */
    signed char T_H_new, T_L_new, POL_new, x, op;
    signed char T_H_current, T_L_current, POL_current, T_C;

    /* set proposed settings to the current programmed settings */
    POL_new = (_1821_get_status() & 0x02) >> 1;
    T_H_new = (unsigned char) _1821_get_T_H();
    T_L_new = (unsigned char) _1821_get_T_L();

    while(1)
    {
       printf("Enter \"C\" to display Current programmed settings\n");
       printf("Enter \"N\" to display New settings to be programmed\n");
       printf("Enter \"H\" to set T_HI_new\n");
       printf("Enter \"L\" to set T_LO_new\n");
       printf("Enter \"P\" to set POL_new\n");
       printf("Enter \"M\" to Measure temperature\n");
       printf("Enter \"F\" to View Flags\n");
       printf("Enter \"S\" to Save new settings\n");
       printf("Enter \"Q\" to Quit and toggle to thermostat mode\n");
       flushall();   /* clear out any character that may be in keyboard */
       scanf("%c", &op);
       op=toupper(op);

       switch(op)

       {
           case 'C': /* Current programmed settings */
                   POL_current = (_1821_get_status() & 0x02) >> 1;
                   T_H_current = (unsigned char) _1821_get_T_H();
                   T_L_current = (unsigned char) _1821_get_T_L();

                   printf("Current Programmed Settings:\n");
                   printf("   POL = %d\n", POL_current);
                   printf("   T_H = %d\n", T_H_current);
                   printf("   T_L = %d\n", T_L_current);

                   break;
           case 'N': /* Proposed new settings */
                   printf("New Settings:\n");
                   printf("   POL_new = %d\n", POL_new);
                   printf("   T_H_new = %d\n", T_H_new);
                   printf("   T_L_new = %d\n", T_L_new);
                   break;

           case 'H': /* High trip point */
                   printf("Enter T_H (-55 to 125):  ");
                   scanf("%d", &T_H_new);
                   if ((T_H_new < -55) || (T_H_new > 125));
                   {
                      printf("Invalid T_H\n");
                   }
                   break;

           case 'L': /* Low trip point */
                     printf("Enter T_L (-55 to 125):  ");
                     scanf("%d", &T_L_new);
                     if ((T_L_new < -55) || (T_L_new > 125));
                     {
                         printf("Invalid T_L\n");
                     }
                     break;

           case 'P': /* Thermostat active state */
                     printf("Enter POL (0 or 1):  ");
                     scanf("%d", &POL_new);
                     if ((POL_new < 0) || (POL_new > 1))
                     {
                         printf("Invalid value of POL - %d\n", POL_new);
                     }
                     break;

            case 'M':   /* Measure temperature */
                     printf("Hit any key to halt\n");
                     while (!kbhit())
                     {
                        T_C = _1821_make_meas();
                        printf("T_C = %d\n", T_C);
                     }
                     getch(); /* clear out the keybaord */
                     break;

           case 'F': /* Display THF and TLF flag bits */
                     x = _1821_get_status();
                     if (x & THF)
                     {
                         printf("THF = 1  ");
                     }
                     else
                     {
                         printf("THF = 0  ");
                     }

                     if (x & TLF)
                     {
                         printf("TLF = 1\n");
                     }
                     else
                     {
                         printf("TLF = 0\n");
                     }
                     break;

           case 'Q': /* Quit */
                     _1821_toggle_mode();  /* toggle to thermostat mode */
                     printf("Quit\n");
                     return(1);

           case 'S': /* Save new settings to DS1821 */
                     if (T_H_new <= T_L_new)
                     {
                        printf("Invalid.  T_H is less than T_L\n");
                        break;
                     }
                     else
                     {
                        printf("Saving.\n");
                        x = THERMOSTAT | _1SHOT + (POL_new << 1);
                        /* THF, TLF are zero */
                        _1821_program_status(x);
                        _1821_program_T_H((byte)T_H_new);
                        _1821_program_T_L((byte)T_L_new);
                        break;
                     }

            default:
                     printf("Invalid Command\n");
                     break;
       } /* end of switch */
   }  /* end of while 1 */
}


byte find_and_configure_device(void)
/* finds DS1821 and configures in communications mode
** returns TRUE if found
*/
{
   int x;
   *p2 = 0x00;   /* no power */
   *pm2 = 0xfd;
   printf("Insert DS1821 in Circuit and hit any key to continue:");
   getch();
   printf("\n");

   *p2 = 0x02;
   delay(1000);  /* wait for device to settle */

   if ((*(p2) & 0x01) == 0) /* probably in thermostat mode */
   {
      _1821_toggle_mode();
      x = _1821_presence();
      if (x == !PRESENT)
      {
          return(FALSE);
      }

      else
      {
         return(TRUE);
      }
   }
   else  /* it may already be in communications mode */
   {
       x = _1821_presence();
       if (x == PRESENT)
       {
          return(TRUE);
       }
       else
               /* if not present may be in thermostat mode*/
       {
          _1821_toggle_mode();
          x = _1821_presence();
          if (x == PRESENT)
          {
             return(TRUE);
          }
          else
          {
             return(FALSE);
          }
       }
   }
}

byte _1821_make_meas(void)
{
/* measure temperature */
   byte val;
   _1821_presence();
   _1w_out_byte(0xee);  /* start a temperature */
   delay(1000);   /* wait for it to complete */

   _1821_presence();
   _1w_out_byte(0xaa);  /* read temperature */
   val = _1w_in_byte();
   return(val);
}

byte _1821_get_T_H(void)
{
/* fetch programmed value of high trip point */
   byte val;
   _1821_presence();
   _1w_out_byte(0xa1);  /* read T_H */
   val = _1w_in_byte();
   return(val);
}

byte _1821_get_T_L(void)
{
/* fetch programmed value of low trip point */
   byte val;
   _1821_presence();
   _1w_out_byte(0xa2);  /* read T_L */
   val = _1w_in_byte();
   return(val);
}

byte _1821_get_status(void)
{
/* fetch status register */
   byte val;
   _1821_presence();
   _1w_out_byte(0xac);  /* read status register */
   val = _1w_in_byte();
   return(val);
}

void _1821_program_T_H(byte T)
{
/* Program high trip point */
   _1821_presence();
   _1w_out_byte(0x01);  /* write T_H */
   _1w_out_byte(T);
   delay(50);   /* wait for EEPROM to program */
}

void _1821_program_T_L(byte T)
{
   _1821_presence();
   _1w_out_byte(0x02);  /* write T_L */
   _1w_out_byte(T);
   delay(50);   /* wait for EEPROM to program */
}

void _1821_program_status(byte config)
{
   _1821_presence();
   _1w_out_byte(0x0c);  /* write status */
   _1w_out_byte(config);
   delay(50);   /* wait for EEPROM to program */
}

void _1821_toggle_mode(void)
/* bring V_DD low and pulse DQ low 16 times */
{
   byte n;

   *p2=0x03;;
   *pm2 = 0xfc;
   *p2 = 0x01;     /* bring DQ to a one and V_DD to zero */

   for(n=0; n<16; n++)
   {
      *p2=0x00;
      *p2=0x01;
   }
   *p2 = 0x02;  /* bring V_DD back high */
   *pm2 = 0xfd;
   delay(50);  /* and wait a bit */
}

byte  _1821_presence(void)
{
   byte i, j;
   _1w_pin_low();
   for(i=167; i>0; i--)  ;    /* bring low for 500 us */

   _1w_pin_hi();
   for (j=0; j<100; j++)
   {
      if ((*(p2) & 0x01) == 0)  /* if it goes to zero, it is present */
      {
         for(i=167; i>0; i--)  ;
         return(PRESENT);
      }
   }
   return (!PRESENT);   /* DS1821 never went to zero */
}

byte _1w_in_byte()
{
   byte m, n, i_byte=0, temp;
   for (n=0; n<8; n++)
   {
      *p2 = 0x02;  /* zero in least sig bit pos */
      *pm2 = 0xfc;   /* make it an ouput */
      *pm2 = 0xfd;   /* back to high z */

      temp = *p2;

      if (temp & 0x01)
      {
        i_byte=(i_byte>>1) | 0x80;  /* least sig bit first  */
      }
      else
      {
        i_byte=i_byte >> 1;
      }
      for(m=19; m>0; m--)  /* 60 us */  ;
   }
   return(i_byte);
}

void _1w_out_byte(byte d)
{
   byte m, n;

   for(n=0; n<8; n++)
   {
      if (d&0x01)
      {
         *p2 = 0x02;
         *pm2 = 0xfc;
         *pm2 = 0xfd;
         for(m=19; m>0; m--)  /* 60 us */  ;
      }

      else
      {
         *p2 = 0x02;
         *pm2 = 0xfc;
         for(m=19; m>0; m--)  /* 60 us */  ;
         *pm2 = 0xfd;
      }
      d=d>>1;
   }
}

void _1w_pin_hi(void)
{
   *pm2 = 0xfd;
}

void _1w_pin_low(void)
{
   *p2 = 0x02;
   *pm2 = 0xfc;
}