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
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...
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.
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.
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!)
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.
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.
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?
Page tested for compliance with INDUSTRY (not MS-only) standards, using the free, publicly accessible validator at validator.w3.org
....... P a g e . . . E n d s .....