Go Down

Topic: DS1822 and getting sensible numbers (Read 2315 times) previous topic - next topic

wackyvorlon

I have managed to get the arduino to talk to a DS1822 that I have handy, retrieving the two bytes of temperature.  However, I haven't managed to get a sensible number in degrees celsius from the values it returns.  Does anyone have any code for this?  I'm not certain how to implement it.

bigengineer

I have posted some code in the 1-wire topic http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1161557194. It is in the readscratchpad function. This code is just a copy of what is in the datasheets. If a DS1822 outputs the same numbers as a DS1820, I haven't checked.

wackyvorlon

Alas, it differs enough to not work properly.  The four most significant bits of the upper temperature byte are used for sign, not just highest bit.  Also, it's 12 bit, so the scale of the number is different.  Instead of 0.5c, it's 0.0625c.  My knowledge of the math isn't good enough to make it work.

yerg2k

http://pdfserv.maxim-ic.com/en/ds/DS1820-DS1820S.pdf

According to the datasheet the MS Byte is the sign.

Make sure your timing is right, if you read the 1-wire thread you will see I ran into difficulties powering for conversion. This particular issue afflicts the ds12x20p, which only runs in "parasite power" mode.

How do you know that the resolution is 12 bits if you can't get it to convert?

wackyvorlon

I've mostly answered my own question by now.  After harassing a few programmer friends, I've puzzled out rudimentary code to talk with the DS1822.  Here it is:

Code: [Select]

#include <OneWire.h>

/* Interface to DS1822 chip, record data to eeprom */

OneWire ds(10);  /* define the pin the interface is on */

void setup(void) {
 Serial.begin(9600);
}

void loop(void) {
 byte i;
 byte present = 0;
 byte data[2];
 byte addr[8];
 int temp;
 
  if ( !ds.search(addr)) {
     Serial.print("No more addresses.\n");
     ds.reset_search();
     return;
 }
 
 
 ds.reset();
 ds.select(addr);
 ds.write(0x44,1);
 delay(1000);
 
 ds.reset();
 ds.select(addr);
 ds.write(0xBE);
 
 data[0] = ds.read();
 data[1] = ds.read();
 /*Serial.print(data[0], HEX);
 Serial.print("  ");
 Serial.print(data[1], HEX);*/

 temp = data[1];
 temp <<= 8;             /* shift left eight bits */
 temp += data[0];        /* add second byte of temperature */
 temp >>= 4;             /* shift right by five to divide by 16 */
 
 /* Thus we output */
 Serial.print("Temp = ");
 Serial.print(temp, DEC);
 Serial.print(" CRC=");
 Serial.print(OneWire::crc8(data,8),HEX);
 Serial.println();
}


There are a few limitations.  The code drops everything after the decimal point, but the chip only has a +-2 degree resolution, so it's good.  It also ignores the sign of it's input.  But, this has the basic math to work, and I am getting reasonable numbers from the chip now.

bdmatic

My first posting, but hopefully useful.

I've put together some of the snippets others have posted on using the ds18s20p and the hc4led display and have a sketch with both of them working, alternating temp display in C and F.

Now I just need to work on making it smaller to have more space for other things.

It seems like using the builting SPI functions could save space - we'll see what i can figure out.

Thanks to all who've posted their code and helped me figure it out.

Code: [Select]
#include <OneWire.h>
#include <stdlib.h>

/* DS18S20 Temperature chip i/o
and  hc4led display  
*/

#define ledClockPin  9
#define ledDataPin  10
#define ledMinDelay  500  
#define ledUpdateDelay 1400
#define tempPin  7



OneWire  ds(tempPin);            // on pin 7 to DQ, 4.7k resistor from +5v to DQ as pullup.
char num_str[5] = "0000";  // display digit conversion space
void setup(void) {         // initialize inputs/outputs
 Serial.begin(9600);      // start serial port
 pinMode(ledDataPin, OUTPUT);  // set display pins
 pinMode(ledClockPin, OUTPUT);    
}

void displayNum(int num){    // updates a munmber 1-9999 on the hc4led
 int i;
 int j;
 char dig[5] ="0000";
 byte digit[10] = {126, 24, 109, 61, 27, 55, 119, 28, 127, 31}; // define numerals (0-9)      
 itoa(num, num_str, 10);
if(num<10000) j=0;
if(num<1000) j=1;
if(num<100)  j=2;
if(num<10)   j=3;
   for(i=3; i>=0; i--){
   dig[i]=num_str[i-j]-48;
   if (dig[i]<0) dig[i]=0;
 }
 for (i=3; i >= 0; i--) {      
   if( i==0 ){
     displayDigit(digit[dig[i]], ledUpdateDelay);
   }
   else {
     displayDigit(digit[dig[i]], ledMinDelay);  
   }
 }
}
void displayDigit(byte dataOut, int curDelay) {  // shifts bits out MSB first, on rising clock:
 int i;     // bit counter
 if (curDelay < ledMinDelay) {
   curDelay = ledMinDelay;
 } // delay  
 for (i=7; i>=0; i--)  {
   if ( dataOut & (1<<i) ) {
     digitalWrite(ledDataPin, HIGH);
   }    
   else                    {
     digitalWrite(ledDataPin, LOW);  
   }
   digitalWrite(ledClockPin, HIGH);     // pulse the clock, longer after the last bit
   if (i == 0) {
     delayMicroseconds(curDelay);
   }
   else {
     delayMicroseconds(ledMinDelay);
   }
   digitalWrite(ledClockPin, LOW);
 }
}

void loop(void) {
 int i;
 byte present = 0;
 byte data[12];
 long temp;
 byte addr[8];

 if ( !ds.search(addr)) {                      //scan for temp sensors
//   Serial.print("No more addresses.\n");
   ds.reset_search();
   return;
 }

 //Serial.print("SensorAddress=");               //lists found sensors
 //for( i = 0; i < 8; i++) {
 //  Serial.print(addr[i], HEX);
 //  Serial.print(" ");
 //}

 if ( OneWire::crc8( addr, 7) != addr[7]) {      // check data quality
   Serial.print("CRC is not valid!\n");
   return;
 }

 //if ( addr[0] != 0x10) {
 //  Serial.print("Device is not a DS18S20 family device.\n");
 //  return;
 //}

 ds.reset();
 ds.select(addr);
 ds.write(0x44,1);         // start conversion, with parasite power on at the end

 delay(1000);     // maybe 750ms is enough, maybe not
 // we might do a ds.depower() here, but the reset will take care of it.

 present = ds.reset();
 ds.select(addr);    
 ds.write(0xBE);         // send it the Read Scratchpad command

 //Serial.print("Present?=");
 //Serial.print(present,HEX);
 //Serial.print(" DATA= ");
 for ( i = 0; i < 9; i++) {           // we need 9 bytes
   data[i] = ds.read();
 //  Serial.print(data[i], HEX);
 //  Serial.print(" ");
 }

 //Serial.print(" CRC=");
 //Serial.print( OneWire::crc8( data, 8), HEX);
 //Serial.print(" num_str= ");
 //for ( i=4; i>=0; i--) {  
 //  Serial.print(dig[i], DEC);
 //  Serial.print(" ");
 //}

 temp = 5*data[0];
 Serial.print(" TempC= ");
 Serial.print(temp, DEC);
 displayNum(temp);
 delay(1500);
 temp = 9*data[0] +320;
 Serial.print(" TempF= ");
 Serial.print(temp, DEC);
 Serial.println();
 displayNum(temp);
}

bigengineer

For some reason I don't get the 12 bit precision from the DS18B20, according to the configuration register (byte 4) it should have 12 bit precision.
What I get is 0 for the MSB and 00101000 for the LSB at room temperature. If I bit shift LSB to the right once I get 20 C, that is more or less correct. But if I understand the datasheet correctly bits 0 to 3 are the extra precision bits and I should shift 4 bits to get a temperature without any precision. So, something like this should work but it doesn't.
Code: [Select]

int temp = 0;
temp = msb;
temp = temp << 8;
temp = temp | lsb;
temp =temp >> 4

For a temperature without any precision 1 shift right of the LSB is enough.
I don't get it, at the front page of the datasheet they speak about 0.5 accuracy, on page 3 they speak about 0.0625 accuracy.

bdmatic

See here for a good description of how to do the extended temp. calculation.

http://frank.bol.ucla.edu/2313Temper8.htm

bigengineer

Hmm, I made a mistake: I read the wrong datasheet. Specifically the temperature accuracy is different between DS18S20 and DS18B20. I read the latter but I have the first.  >:(

Thanks for the link, that should be enough to get it working.

bigengineer

Great, it works!  :)
Just one line of code:
Code: [Select]
temp = (8*lsb+12-data[6])*25/4;
where temp is the temperature*100, lsb byte 0 and data[6] byte 6 from the scratchpad.

Go Up