PIC C, CCS, Interfacing with a Dallas DS1820 Thermometer


Overview

In routine 1820_1.C a temperature measurement is made on RB.0, RB.1, RB.2 and RB.3. For each measurement, all nine bytes are stored in array buff and displayed on the serial LCD.

Note that on terminals other than RB.0, the temperature (the first two bytes) is falsely reported as zero. This suggest a problem with the strong pullup routine where a hard logic one is maintained on DQ for 250 ms. However, I don't see the problem. If you find it, please let me know.

In routine 1820_2.C the 8-byte ROM address of the DS1820 is fetched and saved to the 16F84's EEPROM and displayed on the LCD display. The ROM address is then fetched and the DS1820 is addressed (match ROM), a temperature measurement is performed and the nine bytes are displayed on the LCD

In routine 1820_3.C, a temperature measurement (Skip ROM) is performed and the nine bytes are displayed. A cyclic redundancy check is then performed on the nine bytes and the result is displayed. Note that the CRC should be 0x00. More discussion of the CRC algorithm appears on my main PIC page.


// 1820_1.C, CCS - PCM  (PIC16F84)
//
// Illustrates an implementation of Dallas 1-wire interface.
//
// Configuration.  DS1820 on RB.0, RB.1, RB.2 and RB.3.  Note a 4.7K
// pullup to +5V is required.  DS1820s configured in parasite power mode.
// That is, VCC connected to ground.
//
// Reads and displays nine bytes from each device in turn and displays
// the result on serial LCD connected to RA.0.
//
// Bug.  Temperature (first two bytes) is zero on RB.1, RB.2 and RB.3 
//
// copyright, Peter H. Anderson, Baltimore, MD, April, '99

#include <16f84.h>
#include <string.h>
#include <defs_f84.h>

#define MAX_SENSORS 4

// 1-wire prototypes
void _1w_init(int sensor);
int _1w_in_byte(int sensor);
void _1w_out_byte(int d, int sensor);
void _1w_pin_hi(int sensor);
void _1w_pin_low(int sensor);
void _1w_strong_pull_up(int sensor);	// not used in this routine

// delay routines
void delay_ms(long t);
void delay_10us(int t);

// LCD routines
void lcd_init(void);
void out_RAM_str(int *s);
void lcd_hex_byte(int val);
void lcd_dec_byte(int val, int digits);
int num_to_char(int val);
void lcd_char(int ch);
void lcd_new_line(void);

#define TxData 0  // output to serial LCD

void main(void)
{
   int buff[9], sensor, n;
   for (sensor=0; sensor<MAX_SENSORS; sensor++)
   {
      _1w_init(sensor);
      _1w_out_byte(0xcc, sensor);  // skip ROM

      _1w_out_byte(0x44, sensor);  // perform temperature conversion
      _1w_strong_pull_up(sensor);

      _1w_init(sensor);
      _1w_out_byte(0xcc, sensor);  // skip ROM

      _1w_out_byte(0xbe, sensor);  // read the result

      for (n=0; n<9; n++)
      {
         buff[n]=_1w_in_byte(sensor);
      }

      lcd_init();
      lcd_hex_byte(sensor);	// display the sensor number
      lcd_new_line();

      for (n=0; n<4; n++)	// display 4 bytes
      {
         lcd_hex_byte(buff[n]);
         lcd_char(' ');
      }

      lcd_new_line();

      for (n=4; n<9; n++)	// displays the other 5 bytes
      {
         lcd_hex_byte(buff[n]);
         lcd_char(' ');
      }

      lcd_new_line();

      delay_ms(1000);
   }
}

// The following are standard 1-Wire routines.
void _1w_init(int sensor)
{
   _1w_pin_hi(sensor);
   _1w_pin_low(sensor);
   delay_10us(50);

   _1w_pin_hi(sensor);
   delay_10us(50);
}

int _1w_in_byte(int sensor)
{
   int n, i_byte, temp, mask;
   mask = 0xff & (~(0x01<<sensor));
   for (n=0; n<8; n++)
   {
      PORTB=0x00;
      TRISB=mask;
      TRISB=0xff;
#asm
      CLRWDT
      NOP
      NOP
#endasm
      temp=PORTB;
      if (temp & ~mask)
      {
        i_byte=(i_byte>>1) | 0x80;	// least sig bit first
      }
      else
      {
        i_byte=i_byte >> 1;
      }
      delay_10us(6);
   }
   return(i_byte);
}

void _1w_out_byte(int d, int sensor)
{
   int n, mask;
   mask = 0xff & (~(0x01<<sensor));
   for(n=0; n<8; n++)
   {
      if (d&0x01)
      {
         PORTB=0;
         TRISB=mask;		// momentary low
         TRISB=0xff;
         delay_10us(6);
      }

      else
      {
          PORTB=0;
          TRISB=mask;
	  delay_10us(6);
          TRISB=0xff;
      }
      d=d>>1;
   }
}

void _1w_pin_hi(int sensor)
{
   TRISB = 0xff;
}

void _1w_pin_low(int sensor)
{
   PORTB = 0x00;
   TRISB = 0xff & (~(0x01 << sensor));
}

void _1w_strong_pull_up(int sensor)	// bring DQ to strong +5VDC
{
   PORTB = 0x01 << sensor;
   TRISB = 0xff & (~(0x01 << sensor));
   delay_ms(250);
   TRISB = 0xff;
}

// delay routines
void delay_10us(int t)
{
#asm
      BCF STATUS, RP0
DELAY_10US_1:
      CLRWDT
      NOP
      NOP
      NOP
      NOP
      NOP
      NOP
      DECFSZ t, F
      GOTO DELAY_10US_1
#endasm
}

void delay_ms(long t)	// delays t millisecs
{
   do
   {
     delay_10us(100);
   } while(--t);
}


// LCD routines
int num_to_char(int val)	// converts val to hex character
{
   int ch;
   if (val < 10)
   {
     ch=val+'0';
   }
   else
   {
     val=val-10;
     ch=val + 'A';
   }
   return(ch);
}

void lcd_char(int ch)	// serial output to PIC-n-LCD, 9600 baud
{
   int n, dly;
   		// start bit + 8 data bits

#asm
       BCF STATUS, RP0
       MOVLW 9
       MOVWF n
       BCF STATUS, C

LCD_CHAR_1:

       BTFSS STATUS, C
       BSF PORTA, TxData
       BTFSC STATUS, C
       BCF PORTA, TxData
       MOVLW 32
       MOVWF dly

LCD_CHAR_2:
       DECFSZ dly, F
       GOTO LCD_CHAR_2
       RRF ch, F
       DECFSZ n, F
       GOTO LCD_CHAR_1

       BCF PORTA, TxData
       CLRWDT
       MOVLW 96
       MOVWF dly

LCD_CHAR_3:
       DECFSZ dly, F
       GOTO LCD_CHAR_3
       CLRWDT
#endasm
}

void lcd_init(void)	// sets TxData in idle state and resets PIC-n-LCD
{
#asm
        BCF STATUS, RP0
        BCF PORTA, TxData
        BSF STATUS, RP0
        BCF TRISA, TxData
        BCF STATUS, RP0
#endasm
   lcd_char(0x0c);
   delay_ms(250);
}

void lcd_new_line(void)	// outputs 0x0d, 0x0a
{
   lcd_char(0x0d);
   delay_ms(10);	// give the PIC-n-LCD time to perform the
   lcd_char(0x0a);	// new line function
   delay_ms(10);
}

void out_RAM_str(int s)
{
   while(*s)
   {
      lcd_char(*s);
      ++s;
   }
}

void lcd_hex_byte(int val) // displays val in hex format
{
   int ch;
   ch = num_to_char((val>>4) & 0x0f);
   lcd_char(ch);
   ch = num_to_char(val&0x0f);
   lcd_char(ch);
}

void lcd_dec_byte(int val, int digits)
// displays byte in decimal as either 1, 2 or 3 digits
{
   int d;
   int ch;
   if (digits == 3)
   {
      d=val/100;
      ch=num_to_char(d);
      lcd_char(ch);
   }
   if (digits >1)	// take the two lowest digits
   {
       val=val%100;
       d=val/10;
       ch=num_to_char(d);
       lcd_char(ch);
   }
   if (digits == 1)	// take the least significant digit
   {
       val = val%100;
   }

   d=val % 10;
   ch=num_to_char(d);
   lcd_char(ch);
}


// Program 1820_2.C
//
// Reads 64-bit address from DS1820, saves to the 16F84's EEPROM
// and displays it on serial LCD.
//
// Uses 64-bit address to perform temperature measurement.  Data is
// is displayed on serial LCD
//
//
// 16F84                                       DS1820
// PortB.0 (term 6) ------------------------------ DQ (term 2)
//
// PORTA, Bit 0 (terminal 17) ------ TX ----------> to RX on Serial LCD
//
//
// copyright, Peter H. Anderson, Baltimore, MD, Apr, 99

#case
#include <16f84.h>
#include <string.h>
#include <defs_f84.h>

void display_lcd(int *d, int num_vals);
void get_16f84_eeprom(int adr, int *d, int num_vals);
void put_16f84_eeprom(int adr, int *d, int num_vals);
void write_16f84_eeprom(int adr, int d);
int read_16f84_eeprom(int adr);

void ds1820_read_rom(int *d, int sensor);
void make_temperature_meas(int *adr, int *d, int sensor);

// 1-wire prototypes
void _1w_init(int sensor);
int _1w_in_byte(int sensor);
void _1w_out_byte(int d, int sensor);
void _1w_pin_hi(int sensor);
void _1w_pin_low(int sensor);
void _1w_strong_pull_up(int sensor);	// not used in this routine

// delay routines
void delay_ms(long t);
void delay_10us(int t);

// LCD routines
void lcd_init(void);
void out_RAM_str(int *s);
void lcd_hex_byte(int val);
void lcd_dec_byte(int val, int digits);
int num_to_char(int val);
void lcd_char(int ch);
void lcd_new_line(void);

#define TxData 0

void main(void)
{
   int dev_adr[8], t_dat[9]; 		// temporary storage

   lcd_init();

   ds1820_read_rom(dev_adr, 0);	// read serial number from DS1820
   display_lcd(dev_adr, 8);		// display the result on LCD
   put_16f84_eeprom(0x00, dev_adr, 8);
		// save to eeprom, beginning at adr 0x00, 8 bytes

   // now fetch the serial number, address and perform a temperature
   // measurment

   get_16f84_eeprom(0x00, dev_adr, 8);
                // fetch from 16F84, 8 bytes and return in array d

   make_temperature_meas(dev_adr, t_dat, 0);
   display_lcd(t_dat, 9);	// display the 9 byte result

#asm
DONE:
   clrwdt
   GOTO DONE
#endasm
}

void display_lcd(int *d, int num_vals)
{
  int n;
  for (n=0; n<num_vals; n++)
  {
     if ((n%4==0) && (n!=0))
     {
        lcd_new_line();
     }
     lcd_hex_byte(d[n]);
     lcd_char(' ');
  }
  lcd_new_line();
}

void put_16f84_eeprom(int adr, int *d, int num_vals)
{
   int n;
   for(n=0; n<num_vals; n++, adr++)
   {
      write_16f84_eeprom(adr, d[n]);
   }
}

void get_16f84_eeprom(int adr, int *d, int num_vals)
{
   int n;
   for(n=0; n<num_vals; n++, adr++)
   {
      d[n]=read_16f84_eeprom(adr);
   }
}

void write_16f84_eeprom(int adr, int d)
{
   EEADR = adr;
   EEDATA = d;

   wren = 1;
   EECON2 = 0x55;	// protection sequence
   EECON2 = 0xaa;

   wr = 1;		// begin programming sequence
   delay_ms(20);
   wren = 0;		// disable write enable
}


int read_16f84_eeprom(int adr)
{
   EEADR=adr;
   rd=1;	// set the read bit
   return(EEDATA);
}

void ds1820_read_rom(int *d, int sensor)
{
   int n;

   _1w_init(sensor);
   _1w_out_byte(0x33, sensor);
   for(n=0; n<8; n++)
   {
      d[n]=_1w_in_byte(sensor);
   }
}

void make_temperature_meas(int *adr, int *d, int sensor)
{
   int n;

   _1w_init(sensor);
   _1w_out_byte(0x55, sensor);	// match ROM
   for(n=0; n<8; n++)	// followed by the 8-byte ROM address
   {
      _1w_out_byte(adr[n], sensor);
   }
   _1w_out_byte(0x44, sensor);	// start temperature conversion
   _1w_strong_pull_up(sensor);

   _1w_init(sensor);
   _1w_out_byte(0x55, sensor);	// match ROM
   for(n=0; n<8; n++)	// followed by the 8-byte ROM address
   {
      _1w_out_byte(adr[n], sensor);
   }
   _1w_out_byte(0xbe, sensor);

   for(n=0; n<9; n++)
   {
      d[n]=_1w_in_byte(sensor);
   }
}

// 1-Wire, delay and LCD routines are the same as 1820_1.C


// 1820_3.C
//
// Cyclic redudancy check (CRC).
//
// Makes measurement and displays the nine values on serial LCD.
// Then calculates CRC and displays.
//
// copyright, Peter H. Anderson, Baltimore, MD, Apr, '99

#case
#include <16f84.h>
#include <string.h>
#include <defs_f84.h>

int calc_crc(int *buff, int num_vals);

// 1-wire prototypes
void _1w_init(int sensor);
int _1w_in_byte(int sensor);
void _1w_out_byte(int d, int sensor);
void _1w_pin_hi(int sensor);
void _1w_pin_low(int sensor);
void _1w_strong_pull_up(int sensor);

// delay routines
void delay_ms(long t);
void delay_10us(int t);

// LCD routines
void lcd_init(void);
void out_RAM_str(int *s);
void lcd_hex_byte(int val);
void lcd_dec_byte(int val, int digits);
int num_to_char(int val);
void lcd_char(int ch);
void lcd_new_line(void);

#define TxData 0

void main(void)
{
   int buff[9], sensor=0, crc, n;

   _1w_init(sensor);
   _1w_out_byte(0xcc, sensor);  // skip ROM

   _1w_out_byte(0x44, sensor);  // perform temperature conversion
   _1w_strong_pull_up(sensor);

   _1w_init(sensor);
   _1w_out_byte(0xcc, sensor);  // skip ROM

   _1w_out_byte(0xbe, sensor);

   for (n=0; n<9; n++)
   {
      buff[n]=_1w_in_byte(sensor);
   }

   lcd_init();

   for (n=0; n<4; n++)
   {
      lcd_hex_byte(buff[n]);
      lcd_char(' ');
   }

   lcd_new_line();

   for (n=4; n<9; n++)
   {
      lcd_hex_byte(buff[n]);
      lcd_char(' ');
   }

   lcd_new_line();

   crc = calc_crc(buff, 9);
   lcd_hex_byte(crc);
   lcd_new_line();

   delay_ms(500);
}   


int calc_crc(int *buff, int num_vals)
{
   int shift_reg=0, data_bit, sr_lsb, fb_bit, i, j;

   for (i=0; i<num_vals; i++)	// for each byte
   {
      for(j=0; j<8; j++)	// for each bit
      {
         data_bit = (buff[i]>>j)&0x01;
         sr_lsb = shift_reg & 0x01;
         fb_bit = (data_bit ^ sr_lsb) & 0x01;
         shift_reg = shift_reg >> 1;
         if (fb_bit)
         {
            shift_reg = shift_reg ^ 0x8c;
         }
      }
   }
   return(shift_reg);
}

// 1_Wire, delay and LCD routines are the same as in 1820_1.c