HOME - - - - - Delphi Tutorials TOC - - - - - - Other material for programmers
INDEX TO MicroLan / 1-WIRE PROGRAMMING TUTORIALS Delicious Bookmark this on Delicious Recommend to StumbleUpon

Using the CRC built into Dallas 1-Wire chips for MicroLans

This tutorial addresses a fundamental MicroLan programming issue, common to working with all 1-Wire chips. Sounds good, doesn't it?! But it is an issue many hobbyist duck. If you are new to 1-Wire, you don't NEED to use CRCs...

I know it works under Lazarus, strongly believe it works without changes under Delphi.

For help with using Lazarus with 1-Wire... it is easy... see my 1-Wire with Lazarus tutorials.

=====

First... a "quick" answer, for those fluent in Delphi and Lazarus, checksums, and 1-Wire. Everything you need is, I think, present in the following. Yes... I know I've done it in a pedestrian manner. I left it thus to have it accessible to as many readers as possible. Make it fancier to your heart's content. I'd love to see your answers.....

//------------------------------------------
function bCalcChecksum(b0,b1,b2,b3,b4,b5,b6,b7:byte):byte;
//CRC calcs.....

//This code developed using Lazarus, but
//I believe it would run with no changes under
//Delphi.

//For more about doing 1-Wire under Delphi...
//... it's easy... see......
//

//This code explained in
//http://sheepdogguides.com/dst1b.htm
//I must have stood on the shoulders
//of others when I wrote this, but
//cannot now figure out whose shoulders.
//Perhaps some sample code in the Delphi
//SDK from Dallas?

//=====================================
//If you send this the bytes of the ID
//(the "unique 64-bit ROM code") of a
//1-Wire chip, family code LAST (in b7),
//the result should be zero.

//---------------
//If you send this the bytes resulting
//from a ReadScratchpad command to a
//1-Wire chip in REVERSE order of
//the order they arrive from the
//first EIGHT TMTouchByte($FF)s after
//the TMTouchByte($BE) that started the
//1-Wire chip SENDING the contents of
//its scratchpad, the function
//will calculate the number which
//should MATCH the NINTH number fetched
//by TMTouchByte($FF). If it matches,
//then none of the 9 bytes were corrupted
//in transmission. In connection with
//this... beware a little "gotcha"...
//The TMEX documentation tell you that
//if a TMTouchByte($FF) returns $FF,
//"it means that the MicroLan is
//shorted". Well... if the MicroLan
//is shorted, TMTouchByte($FF) WILL
//return $FF. BUT!!! SOMETIMES getting
//and $FF simply means that the chip
//you were listening to SENT an $FF.

//-----------------------------------
//If you send this the bytes from a scratch
//pad read, the result should match the
//9th byte returned, the one that follows
//on after the 8 bytes from the scratchpad.

var bAns:byte;
  iaCRC: array [0..7] of byte;//Each should
      //only ever be 0 or 1
  iCRCTmp,c1:integer;
  baBits: array [0..63] of byte;//these will
      //only ever hold a 0 or 1 in any element.
      //baBits in this is doing what OutBinArray
      //did in the original I created this from.

procedure BitsToArray(bStrt,b0:byte); //SR of
      //function bCalcChecksum
//Starting with element bStrt of baBits, fill
      //the 8 elements of bStrt with the
      //bits in b0.
begin
baBits[bStrt-7]:=(b0 and 1);
baBits[bStrt-6]:=(b0 and 2) div 2;
baBits[bStrt-5]:=(b0 and 4) div 4;
baBits[bStrt-4]:=(b0 and 8) div 8;
baBits[bStrt-3]:=(b0 and 16) div 16;
baBits[bStrt-2]:=(b0 and 32) div 32;
baBits[bStrt-1]:=(b0 and 64) div 64;
baBits[bStrt]:=(b0 and 128) div 128;
end;//of BitsToArray(bStrt,b0:byte),
    //SR of function bCalcChecksum

begin//main block of function bCalcChecksum
BitsToArray(63,b0);
BitsToArray(55,b1);
BitsToArray(47,b2);
BitsToArray(39,b3);
BitsToArray(31,b4);
BitsToArray(23,b5);
BitsToArray(15,b6);
BitsToArray(7,b7);

for c1:=0 to 7 do iaCRC[c1]:=0;

for c1:=0 to 63 do begin
   iCRCTmp:=iaCRC[0] XOR baBits[c1];
   iaCRC[0]:=iaCRC[1];
   iaCRC[1]:=iaCRC[2];
   iaCRC[2]:=iaCRC[3] XOR iCRCTmp;
   iaCRC[3]:=iaCRC[4] XOR iCRCTmp;
   iaCRC[4]:=iaCRC[5];
   iaCRC[5]:=iaCRC[6];
   iaCRC[6]:=iaCRC[7];
   iaCRC[7]:=iCRCTmp;
   end;//for...

bAns:=iaCRC[7]*128+iaCRC[6]*64+
        iaCRC[5]*32+iaCRC[4]*16+
        iaCRC[3]* 8+iaCRC[2]*4+
        iaCRC[1]* 2+iaCRC[0];

result:=bAns;
end;//bCalcChecksum


CRCs for Arduino and Pi DS18B20 users...

This is very much a "sidebar" for this page, but I found very little on the net about using the 1-Wire checksums in ANY environment... and we SHOULD be using them! If you are using 1-Wire chips with an Arduino or Pi, you are probably taking some shortcuts which are fine, for limited use of the chips. But even in those circumstances, using the checksum provided would probably be wise. I can't say I fully understood it, but there was some stuff that looked good in a forum discussion of checksums for 1-Wire on Arduinos. (I think it is the 8-bit version which is most immediately useful.)

Right... back to the Lazarus/ Delphi 1-Wire checksums matter...

What's that all about... for Alphie and anyone else.

If the above "went too quickly", here is some additional explanation. Re-read the comment parts of what is above first, though, as there are some things there that I won't repeat... in particular the "gotcha" about when TMTouchByte($FF) returns $FF.

What is a CRC, and why do we care?

Suppose you were on the phone with a friend, and had to share a really important telephone number. Further suppose the number consisted of just 6 digits... 555-1212.

One thing... not perfect, but a start towards "what is a CRC?"... you could do, after telling your friend the number, is to ask the friend to add the digits of the number together...

5+5+5+1+2+1+2

... which makes 21. If your friend gets 21, then the friend MAY have written the number down correctly. (Of course if you add the digits of 555-2121 up you get 21 as well, and transposition errors are common... but we'll come to that.)

There are times when using computers when strings of numbers are sent between devices. If device "A" does something like adding up the digits it tried to send, and then sends the total to device B, and device B (independently) adds up the digits it received, and compares that total to the total that device A sent, if they disagree, you can assume that one or more of the digits sent got scrambled in transmission. (It could be digits in the data, or digits in the checksum. The system will catch either source of data corruption, won't it, if you think for just a moment.)

There are tricks you can use make the system to catch transpositions, too.

For example, you could, before simply adding the digits, you might add 1 to the first digit, 2 to the second, 3 to the third, etc. Then the checksums of 555-1212 and 555-2121 would result in different totals.

If you want more on this type of data stream checking, Wikipedia can help.

Checking Data Streams in MicroLan Work

Many, many 1-Wire chips send data to the master device. If you ask for a "scratchpad read" the 1-Wire chip has sent the 8 bytes of data in its scratchpad, plus a "checksum"... i.e. a sophisticated version of a "total of the digits sent". (This is why, in the code scraps below, you will see an array for receiving 8 bytes from the scratchpad set up as, say,

baReceiveBuffer array [0..8] of byte;

In the cleaner code, above, the bytes go into b0,b1,b2... b7). Crude. But simple.

"Isn't that a mistake?", you ask yourself. Surely for 8 bytes, you want...

baReceiveBuffer array [0..7] of byte;

... or ...

baReceiveBuffer array [1..8] of byte;

Well... Yes. For 8 bytes either of those would suit. But you are being sent NINE bytes: The eight data bytes, plus one more byte with the checksum.

In the code above, the 9th byte is not sent to the subroutine. the code above can be used for two things...

1) Checking what was sent following a ScratchPad Read. In this use, you read the 8 bytes of data, pass them to the subroutine above, read the final byte being sent by the 1-Wire chip... the checksum... and compare that with the number from the subroutine. If they match, the data came through without corruption.

2) Checking the validity of a number that you think is a chip ID. In this use, you send the 8 bytes of the chip ID to the subroutine. If it returns 0, then the ID is valid. (It may not, of course, be the chip YOU have... but at least the chip ID is possible!)


Three diversions:

a) "CRC" / "checksum": Go back to my example of adding the digits to check that a telephone number has been understood properly. The total of the digits is a "checksum". It was calculated by a simple, nameless, rule. The checksum used in MicroLan programming is calculated by a rule which gives a "cyclic redundancy check", or CRC. I sometimes carelessly refer to the checksum as "the CRC".

b) I haven't always understood the "nine bytes" issue. You will see cases in my 1-Wire tutorials where I've only harvested the 8 data bytes, and just ignored the checksum. Hence, I sometimes only give the relevant array 8 elements.

c) I have recollections of hearing something about the 1-Wire checksum being a 16 bit entity. At May 2012, I have come to believe that I must have misunderstood something. I now believe that the checksum is only a byte, i.e. just 8 bits. And I have found that if you and the results of the TMTouchBytes with $FF, you get good results. (It returns a 16 bit value. I'm not aware of it, except when signaling an error, returning 1s in the upper 8 bits, but started anding with $FF, just because it "felt right". Apologies for references to 16 bits elsewhere.

The ((OLD!)) code...

This code was an early attempt at these issues. The code at the top of the page is MUCH better. The following has little to recommend it... but it does have some test data in it, if you want to figure out what is going on.

Apologies for rough edges... frankly, I should probably cut this out of the page, and you should probably stop reading here... although there are odds and general ends at the BOTTOM of the page which might be of interest.

unit PSD022u1;

//Created with Delphi 4, but doesn't use "clever" features... should be fine
//  in Lazarus and other versions of Delphi.

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

type
  TPSD022f1 = class(TForm)
    PSD022f1: TButton;
    Label1: TLabel;
    Label2: TLabel;
    buQuit: TButton;
    procedure PSD022f1Click(Sender: TObject);
    procedure buQuitClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    baInput: array [0..7] of byte;
    bAns:byte;
  end;

var
  PSD022f1: TPSD022f1;

implementation

{$R *.DFM}

procedure TPSD022f1.PSD022f1Click(Sender: TObject);
var iaCRC: array [0..7] of byte;//Each should only ever be 0 or 1
    iCRC,iCRCTmp,c1:integer;
    baBits: array [0..63] of byte;//actually only holding one bit in each element.
      //baBits doing in my prgm what OutBinArray does in original

  procedure BitsToArray(bStrt,b0:byte); //SR of Button1
  //Starting with element bStrt of baBits, fill 8 elements of bStrt
  //   with the bits in b0.
  var bTmp:byte;
    iCRCTmp:integer;
  begin
  baBits[bStrt-7]:=(b0 and 1);
  baBits[bStrt-6]:=(b0 and 2) div 2;
  baBits[bStrt-5]:=(b0 and 4) div 4;
  baBits[bStrt-4]:=(b0 and 8) div 8;
  baBits[bStrt-3]:=(b0 and 16) div 16;
  baBits[bStrt-2]:=(b0 and 32) div 32;
  baBits[bStrt-1]:=(b0 and 64) div 64;
  baBits[bStrt]:=(b0 and 128) div 128;
  end;

begin

baInput[0]:=$12;//0001.0010
baInput[1]:=$AA;//1010.1010
baInput[2]:=$12;//0001.0010
baInput[3]:=$5F;//0101.1111
baInput[4]:=$14;//0001.0100
baInput[5]:=$A2;//1010.0010
baInput[6]:=$12;//0001.0010
baInput[7]:=$12;//0001.0010
//12/AA/12/5F/14/A2/12/12 SHOULD GIVE crc=$6D


BitsToArray(63,baInput[0]);
BitsToArray(55,baInput[1]);
BitsToArray(47,baInput[2]);
BitsToArray(39,baInput[3]);
BitsToArray(31,baInput[4]);
BitsToArray(23,baInput[5]);
BitsToArray(15,baInput[6]);
BitsToArray(7,baInput[7]);


for c1:=0 to 7 do iaCRC[c1]:=0;

for c1:=0 to 63 do begin
   iCRCTmp:=iaCRC[0] XOR baBits[c1];
   iaCRC[0]:=iaCRC[1];
   iaCRC[1]:=iaCRC[2];
   iaCRC[2]:=iaCRC[3] XOR iCRCTmp;
   iaCRC[3]:=iaCRC[4] XOR iCRCTmp;
   iaCRC[4]:=iaCRC[5];
   iaCRC[5]:=iaCRC[6];
   iaCRC[6]:=iaCRC[7];
   iaCRC[7]:=iCRCTmp;
   end;//for...


{For checking conversion of bits in iaCRC to byte in bTmp
iaCRC[7]:=1;
iaCRC[6]:=1;
iaCRC[5]:=1;
iaCRC[4]:=0;
iaCRC[3]:=0;
iaCRC[2]:=1;
iaCRC[1]:=1;
iaCRC[0]:=1;
}

{
iaCRC[7]:=baBits[0];
iaCRC[6]:=baBits[8];
iaCRC[5]:=baBits[16];
iaCRC[4]:=baBits[24];
iaCRC[3]:=baBits[32];
iaCRC[2]:=baBits[40];
iaCRC[1]:=baBits[48];
iaCRC[0]:=baBits[56];
}

bAns:=iaCRC[7]*128+iaCRC[6]*64+iaCRC[5]*32+iaCRC[4]*16+
      iaCRC[3]*8+iaCRC[2]*4+iaCRC[1]*2+iaCRC[0];
label1.caption:='$'+inttohex(bAns,2);
end;

procedure TPSD022f1.buQuitClick(Sender: TObject);
begin
close;
end;

end.

The code was created with Delphi 4, but doesn't use "clever" features. It should be fine in Lazarus and other versions of Delphi.

The resulting .exe just has a button. When you click it, the program applies the right rule to 8 hard coded bytes, and gives you the checksum which they create, if you apply the Dallas CRC rule to them.

As I said: The program is a simple instance of applying the rule, which you are free to adapt to incorporate in your work... and all responsibility for any results is YOURS. Use it free... free of charge, and free of obligation or liability on my part.

So... into the details...

The 8 bytes from which the program calculates the checksum are supplied to the program in an array called baInput, which has elements 0 to 7 inclusive.

After the calculation is finished, the result is in the global 8 bit variable bAns.

Inside the program, there are two arrays, iCRC[0..7] and baBits[0..63]. I'm pretty sure that every element of both of these only ever holds a single bit, a 0 or a 1, in other words... but I was in a hurry and sloppily used unnecessarily capacious data types.



So... and abrupt end!...

I hope that helps you to use the CRC checksum provided by the 1-Wire system. I'd write you a nicer conclusion to this tutorial, but maybe you'd rather I went off, finished up some polishing and testing of the code, and got the .zip online for you?



   Search this site or the web        powered by FreeFind
 
  Site search Web search
Site Map    What's New    Search   BEWARE: There is stuff at my other two sites that this search won't reveal. Go to either site (see links below) and use that site's FreeFind search button.
In addition to the tutorials for which this page serves as Table of Contents, I have other sites with material you might find useful.....

Sheepdog Software homepage.
My Arunet homepage.

... and some links to specific pages within them you might want....

Main index to MicroLan stuff.
Some pages for programmers.
Using the parallel port with programs written in Delphi.



Ad from page's editor: Yes.. I do enjoy compiling these things for you... hope they are helpful. However.. this doesn't pay my bills!!! If you find this stuff useful, (and you run an MS-DOS or Windows PC) please visit my freeware and shareware page, download something, and circulate it for me? Links on your page to this page would also be appreciated!
Click here to visit editor's freeware, shareware page.

Link to Tutorials main page
Here is how you can contact this page's author, Tom Boyd.


Valid HTML 4.01 Transitional Page tested for compliance with INDUSTRY (not MS-only) standards, using the free, publicly accessible validator at validator.w3.org


If this page causes a script to run, why? Because of things like Google panels, and the code for the search button. Why do I mention scripts? Be sure you know all you need to about spyware.

....... P a g e . . . E n d s .....