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 DS2423 counter chip on a MicroLan

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....

screenshot of DD91

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!)

Screenshot of OneWireViewer

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.


   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.




Click here if you're feeling kind! (Promotes my site via "Top100Borland")
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 WILL BE 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 .....