This tutorial needs editing for layout, but there's good information here already. The program was built almost "from scratch" in March 2011, after I'd been doing 1-Wire work for a while, and Delphi programming for a Very Long while. Some of the Bad Bits in some of my earlier efforts have been avoided in what follows.
=====
This tutorial will show you how to use the main feature of the DS2423 1-Wire chip.
In addition to the usual "DQ" pin, that connects the DS2423 to a MicroLan, it has two additional pins. They are inputs. The chip counts how many negative edges appear on those inputs. When the chip is connected to a MicroLan, the host, or master, of the MicroLan can ask the DS2423 to "send in" the count so far. (The negative edges on each input are counted separately.
===== We're going to build a very simple little program....
Click on the "Read Counter A register" button, and the program reads the register in the counter chip, and reports how many negative edges have been seen on input A.
It will be "messy" and "badly designed" in places, but I hope it will convey the essentials of what we want to do. And if you can grasp the essentials of working with one 1-Wire chip, you've done 80% of the work of using your next 1-Wire chip... there is a lot of common material from one to another.
This program isn't meant to be a polished commercial product. I leave the polishing to you. It is meant to help you see "the skeleton" of what is needed to read the DS2423.
Here we go....
===== Start by getting your DS2423 hooked up. Use the Dallas- supplied OneWireViewer to be sure your device is visible on your MicroLan. A DS2423's ID will end with "1D". Be sure that whatever inputs you have connected to one or both of the DS2423's inputs are working.
In the OneWireViewer, you can look at what's in the DS2423's counter registers. In the following, the Counter A register holds 0, and the Counter B register holds $12. (I'll try to revisit this, expand!)
Now that your hardware is in place, we'll start on the software....
Create a folder for this. Call it DD91. ("Delphi Demo", number 91)
Start a new application.
Name the form dd91f1. Save the project as DD91.dpr; save the unit as dd91u1.pas
Add copy of IBTMEXPW.PAS to folder you are working in. (It comes from Dallas, and is also in the zip with the sourcecode of this DS2423 reading application which I've provided for you to download. Read the Readme.txt file.
Add iBTMEXPW to uses clause.
========
Create the following global variables....
//These data types have been checked carefully 3/11. Care was //taken over what does Delphi use for things that TMEX may //give different names to, e.g. TMEX uses "short" for //16 bit signed integers, whereas Delphi calls that //data type "Smallint". (And remember that Delphi ALSO has //"Shortint", which is the name for 8 bit signed integers. cardTmp:Cardinal;//32bit unsigned.. for full result from //reading a DS2423 counter register. siTmp,siPortNum,siPortType:Smallint;//Not ShortInt. //For things that TMEX calls "short": 16bit signed siResult:Smallint;//For what TMEndSession, TMSetUp, //TMTouchByte, and TMStrongAccess return liSHandle,liDallErr:Longint;//For things that TMEX calls "long": 32 bit signed, //e.g. what TMExtendedStartSession returns. Usually a smallint would //do for liDallErr, but there is one function, TMExtendedStartSession, //which returns a longint, and we may need to transfer what was //returned by it to the error flag variable. sChipID2423:string[16]; baRomID2423:array[0..7] of byte; bWhichCall:byte; siaRom:array[0..7] of Smallint; baStateBuffer:array[0..15360] of byte;
==========
To turn on Range Checking, put....
{$R+}
... right after the well placed, but unrelated....
{$R *.DFM}
========
Put two buttons on the form...
Name/ caption buCounterA/ Counter A register buCounterB/ Counter B register
Put four labels on the form...
laCRa, laErrA, laCRb, laErrB
The three "A" items go in one row, the three "B" items in a second row.
========
Create a function...
function Tdd90f1.cardCounterRegHolds(bWhich:byte):cardinal;
.. which, for now, consists simply of....
function Tdd90f1.cardCounterRegHolds(bWhich:byte):cardinal; begin if bWhich=1 then result:=1111 // no ; here else result:=2222 end;//of cardCounterRegHolds
==========
Create OnClick handlers for your two buttons as follows. We could do this more "cleverly", but CLARITY is the point here. We've already, wisely, arranged for ONE function to read EITHER CounterRegister, which WAS worth doing....
procedure Tdd90f1.buCounterAClick(Sender: TObject); begin laCRa.caption:=inttostr(cardCounterRegHolds(1)); end; procedure Tdd90f1.buCounterBClick(Sender: TObject); begin laCRb.caption:=inttostr(cardCounterRegHolds(2)); end;
==========
Okay.. you've had the simple bits. Get ready to work now...
===========
In FormCreate Handler....
//In a "real" application, some of the following would be done with ini files. //The next line needs to be set according to where // YOUR adapter is connected. "4" says it is on port 4. siPortNum:=4; //The next line needs to be set according to what type // of adapter YOU have. 5 is good for serial port adapters, // e.g. a DS9097U, and some adapters which connect through // a virtual serial port created with a USB interface, // e.g. the "LinkUSB" from iButtonLink. Use 1 for a DS9097E siPortType:=5; //Here begins a crude, but "programmer friendly" way // prepare for later moving a chip's serial number from // a fairly "human readable" representation into an array // from which it can subsequently (and repeatedly) be moved // quickly into the block of memory, the buffer, through // which things flow going to and from the TMEX code.... //The next line must be filled with the serial number of YOUR DS2423 sChipID2423:='170000000DD1311D'; //"Human friendly" //Now the parts of that are moved to where they must be.... baRomID2423[7]:=strtoint('$'+copy(sChipID2423,1,2)); (*MSB of chip's unique id.*) baRomID2423[6]:=strtoint('$'+copy(sChipID2423,3,2)); baRomID2423[5]:=strtoint('$'+copy(sChipID2423,5,2)); baRomID2423[4]:=strtoint('$'+copy(sChipID2423,7,2)); baRomID2423[3]:=strtoint('$'+copy(sChipID2423,9,2)); baRomID2423[2]:=strtoint('$'+copy(sChipID2423,11,2)); baRomID2423[1]:=strtoint('$'+copy(sChipID2423,13,2)); baRomID2423[0]:=strtoint('$'+copy(sChipID2423,15,2)); //Here ends the moving of the chip's serial number to the array. caption:='dd91 from SheepdogSoftware.co.uk Version:'+ver; application.title:='dd91'; end;// of FormCreate
=====
Replace the original, simple, cardCounterRegHolds with the following. (You can download a zip with the finished sourcecode, but using that would be less of a learning experience for you. If you DO take that route, do remember to edit the lines in the FormCreate handler which define the serial number of the DS2423 the program is set to run with, and the port and adapter codes.)
function Tdd91f1.cardCounterRegHolds(bWhich:byte):cardinal; //This also uses two global values bWhichCall, and liDallErr. // This is Bad Programming.... // ... and can be avoided... but not without making the // heart of this less obvious. var bTmp:byte; cardTmp:cardinal; wCS:word;//A 16 bit unsigned variable to hold the checksum //sent as part of the record from the DS2423 procedure DealWithErrorFlags(bW:byte);//A subroutine of cardCounterRegHolds //Note this can be used after MOST TMxxx calls, but not after some, e.g. //TMExtendedStartSession. This only deals with the "Usual", less than zero //error codes. Note that the interpretation of, say, "-1" will vary from //context to context. begin if siResult<0 then begin bWhichCall:=bW; liDallErr:=siResult; end; end; // of DealWithErrorFlags, a subroutine of cardCounterRegHolds begin //cardCounterRegHolds cardTmp:=0;// Establish what is to be returned by the function, //along with error information in the global variables //bWhichCall and liDallErr in the event that the counter //registers cannot be read. A counter register MIGHT contain //zero, so zero could be a valid result from the function. //When the function cannot return a valid result, it will //return a consistent meaningless "answer", due to this //initialization of cardTmp. bWhichCall:=0;// The global variables bWhichCall and liDallErr liDallErr:=0; // between them report back to whoever called //cardCounterRegHolds. //If bWhichCall is zero, it signifies that no error was seen, // and in this case, the contents of liDallErr is irrelevant. //If bWhichCall is not zero, the value tells you where in // cardCounterRegHolds ONE OF the calls to a TMEX function // an error was reported. (There may have been more than one // case of a TMEX function call giving rise to an error // report. //If bWhichCall is not zero, the value in liDallErr is the // error code returned by the TMEX function call that reported // an error and set bWhich call to something other than zero. //Try to look past the many lines connected with bWhichCall and // liDallErr in what follows, and see the ESSENCE hiding in // all the confusion. liSHandle:=TMExtendedStartSession(siPortNum,siPortType,nil); if liSHandle<1 then begin bWhichCall:=10; liDallErr:=liSHandle; //if liSHandle was 0, it means port was not available, // it was being used by another application //if liSHandle was less than 0, it was reporting a // TMEX Session Error Return Code: See TMEX // help material in SDK. end; if bWhichCall=0 //i.e. no errors yet then siResult:=TMSetUp(liSHandle); if siResult<0 then DealWithErrorFlags(12);//deal with some of the possible //error conditions if siResult<>1 then begin // deal with other errors TMSetUp can report bWhichCall:=12; liDallErr:=siResult; //See TMEX help material in SDK for interpretation. //If siResult was 2, for instance, it flags a shorted MicroLan. end; //Next, Load buffer in TMEX workspace with ID of chip //to be accessed. It MAY not need to be done again //and again if we are always working with the same //chip, but in other programs we may be accessing //several chips in turn. //And yes, we are shoving 8 bit unsigned numbers into //16 bit unsigned elements in an array... Why, I'm //not quite sure... but it works! Probably down to //how the TMEX code was written, but I can't see why //it would be written to hold 8 eight bit entities //in a space twice the size needed, with alternate //8 bit stretches filled with zeros. for bTmp:=0 to 7 do siaROM[bTmp]:=baRomID2423[bTmp]; //And now move the serial number from the "staging area" //to where it is needed for subsequent needs. //I've been somewhat clumsy here... you could manage //multiple chips and their IDs more elegantly. siResult:=TMRom(liSHandle,@baStateBuffer,@siaROM); DealWithErrorFlags(14); //TMRom returns only the usual error codes, all <0 siResult:=TMStrongAccess(liSHandle,@baStateBuffer); DealWithErrorFlags(16); //TMStrongAccess only returns the usual error codes, plus "0" for //"Device not found" if siResult=0 then begin bWhichCall:=16; liDallErr:=siResult; end; if bWhichCall=0 //...i.e. no error seen yet, then do this block. //We could set up more blocks like this, to //save repeated checks of bWhichCall, but the code here //hasn't been made 100% "consistent" then begin //"1".. in this block is the main code for reading the DS2423 siResult:=TMTouchByte(liSHandle,$A5); (*'Read Memory+Counter' command*) DealWithErrorFlags(18); //TMTouchByte can return $FF if it sees a shorted MicroLan at //this point, but it seemed to me that it was also possible for //$FF to be returned due to other circumstances... I'm not sure //that could happen, i.e. is is possible that $FF could ONLY //mean there was a shorted MicroLan, but I passed up the chance //to raise an error here, just in case the "other" $FF CAN arise. //This comment applies to each instance of not checking if //siResult=$FF after a call of TMTouchByte //In the next statement, we are using the value passed to //cardCounterRegHolds to "switch" us between reading the //counter register for input A and reading the counter //register for input B..... if bWhich=1 //Start saying WHERE (address) the page you want to read is then siResult:=TMTouchByte(liSHandle,$C0)// no ; here. //Point to page 14 to read input A counter else siResult:=TMTouchByte(liSHandle,$E0); //Point to page 15 to read input B counter DealWithErrorFlags(20); //... now send the other half of the start address //of the page of data you want to read, the MSB siResult:=TMTouchByte(liSHandle,$01); DealWithErrorFlags(22); for bTmp:=0 to 31 //Discard bytes reporting data from the page do begin siResult:=TMTouchByte(liSHandle,$FF); DealWithErrorFlags(24); end; (*Now read and accumulate the four bytes of the counter. They are delivered LSB first, AFTER the 32 bytes of data that was in the page of RAM just read from the DS2423*) cardTmp:=TMTouchByte(liSHandle,$FF); DealWithErrorFlags(26); siResult:=TMTouchByte(liSHandle,$FF); DealWithErrorFlags(28); cardTmp:=siResult*256+cardTmp; siResult:=TMTouchByte(liSHandle,$FF); DealWithErrorFlags(30); cardTmp:=siResult*65536+cardTmp;(*65536=256*256 *) siResult:=TMTouchByte(liSHandle,$FF); DealWithErrorFlags(32); cardTmp:=siResult*16777216+cardTmp;(*16777216=256*256*256 *) //The value in cardTmp will be invalid, of course, if // an error occured during any of the TMTouchByte calls, // but the code can remain as above anyway. //Now read and discard the 4 bytes of zeros expected after the 4 bytes // of counter info. for bTmp:=0 to 3 do begin siResult:=TMTouchByte(liSHandle,$FF); DealWithErrorFlags(34); //If siResult is >0, it indicates that something was //read from the MicroLan without an "error" arising.... //but here, the DS2423 ought to be sending zeros. if siResult>0 then begin bWhichCall:=34; liDallErr:=siResult; end; end; //skip zeros //Pick up the two numbers which between them make up // the checksum. While they are returned to a Smallint // (16 bit signed) variable, unless an error code // is returned, the value returned will be 0-255 // in each case. siResult:=TMTouchByte(liSHandle,$FF); DealWithErrorFlags(36); wCS:=siResult;//wCS meaningless if bWhichCall>0, but it can have //siResult put in it. siResult:=TMTouchByte(liSHandle,$FF); DealWithErrorFlags(38); wCS:=(256*wCS)+siResult; //or the right way to combine them might be... // wCS:=wCS+(256*siResult);... //It will be one or the other! The result // may also need inverting. When someone // revises all this to locally calculate the // CRC 16 which the CRC passed by the last two // calls of TMTouchByte, then we can pin down // which formula should be used! end;// of then begin "1" //In any case, even if TMStrongAccess was //unsuccessful, or errors were seen, do.... siResult:=TMEndSession(liSHandle); DealWithErrorFlags(40); //... to free things up. result:=cardTmp;//Return answer to whatever called the routine. //Remember that values are also returned in the two //global variables, bWhichCall and liDallErr, to //tell the user whether all went well during the function. //(If bWhichCall is zero, no errors were detected.) end;//of cardCounterRegHolds
That has become very "cluttered" due to all of the provision for catching and reporting errors if they are returned by any of the TMxxx functions, but if you read through carefully, ignoring the error reporting, the underlying "shape" should become "visible" to you.
Each time you call cardCounterRegHolds, you start by establishing a TMEX "session". This is your "connection" to the TMEX drivers in your PC. You "talk" to them, they "talk" to the MicroLan. There's a lot more about the general theory of MicroLan work at my overview.
You then "reach out" to the MicroLan. First you "reset it", which gets all the chips "on page one". You then tell every chip but the one you are interested in to "go to sleep". You then sent it the "tell me" command (code $A5), and then sit back, harvest the answer. Once you have your answer, you then shut down the session which you had open. It may seem strange... it may not even be necessary to keep setting up, and then shutting down your TMEX session. I'm just more comfortable with doing things this way, so that I know I'm working with a "fresh" session each time.
I sometimes over-explain. If I'm guilty of that again here, I give up trying to be more "lean"!
Another resource...
Long ago, I addressed these issues once before. I don't think they are better than the above, and the associated general overview of doing programming for 1-Wire, but if you want to look at another DS2423 reading program, there's a zip with sourcecode for that in it.
Page WILL BE 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 .....