[Adapter] type=5 port=1 [Chips] 0=firstchip 1=secondchipWe're going to "use" that ini file very crudely, at first, just to be sure it is working. (You may need different numbers for the keys in the "Adapter" section, and you will need to replace "firstchip" and "secondchip". More on that later.)
sIniFileName:string;...into the private declarations for the TDS042f1 class, and in the FormCreate procedure, add...
sIniFileName:=ExtractFileDir(application.exename); sIniFileName:=sIniFileName+'\ds042ini.txt';... in order to set up the ini file name.
IniFiles... to the program's "uses" clause, and add...
dfIniFile:TIniFile;to the private declarations for the TDS042f1 class, and- for the moment- in the FormCreate procedure, add...
dfIniFile:=TIniFile.create(sIniFileName);
laTture0.caption:=dfIniFile.ReadString('adapter','type','xx');
dfIniFile.free;
This rather crude code goes to the ini file, picks up what's in the Adapter/Type line, and puts it (just to see it works) in the laTture0 label. If there is no Adapter/Type line, 'xx' will be put in the laTture0 label.
procedure TDS042f1.FormCreate(Sender: TObject);
procedure CreateIniFile;
begin
dfIniFile:=TIniFile.create(sIniFileName);
dfIniFile.WriteString('adapter','type','you must put number here');
dfIniFile.free;
end;
begin
DS042f1.caption:='DS042'+ver;
sIniFileName:=ExtractFileDir(application.exename);
sIniFileName:=sIniFileName+'\xxds042ini.txt';
if not fileexists(sIniFileName) then begin
CreateIniFile;
end//no ; here
else begin
dfIniFile:=TIniFile.create(sIniFileName);
laTture0.caption:=dfIniFile.ReadString('adapter','type','xx');
dfIniFile.free;
end;//else
end;
Next, we're going to create global variable for the adapter type, adapter port, and the chip IDs. The chip ids are going to be in an array, and we're going to have a constant telling us the maximum valid value for a chip array element index....
saDMLRomID:array [0..kDMLRomIndexMax] of string[16];
(*While this example accesses only two chips, it is
written so that extending it to more is easy.
saDMLRomID[0] would hold 'A20000000547FB10' if
the first chip's id were $A20000000547FB10. The
address is given in the form returned by the
iButtonViewer (software included in SDK), i.e.
the right hand byte is the device ID byte.*)
baDMLRomIDTable:array [0..kDMLRomIndexMax,0..7] of byte;
(*baDMLRomIDTable[2,0] holds chip 2's first id byte*)
baDMLRomIDofOne:array [0..7] of byte;
(*A "temporary" variable, for passing ID of a ROM to
external routine.
baDMLRomIDofOne[0] holds a chip's first id byte*)
sIniFileName:string;
dfIniFile:TIniFile;
iDMLPortNum,iDMLAdapterType,iDMLTmpResult:integer;
(*iDMLTmpResult is a 'scratch' variable, never used for
long-term storage of any value.*)
liDMLSHandle:longint;
sTmp:string;
iErr:integer;
//boTmp:boolean;
bTmp:byte;
=====laTture0.caption:=dfIniFile.ReadString('adapter','type','xx');
... to....
sTmp:=dfIniFile.ReadString('adapter','type','xx');
val(sTmp,iDMLAdapterType,iErr);
if iErr<>0 then begin
showmessage('The adapter type must be a number');
application.terminate;
end;
(The problems this addresses could be handled more elegantly, but this will do.)
dfIniFile.WriteString('adapter','port','you must put number here');
dfIniFile.WriteString('chips','0','you must put 16 hex characters here');
dfIniFile.WriteString('chips','1','you must put 16 hex characters here');
You will assign the value of the adapter port to iDMLAdapterPort, and the two chip ID strings to saDMLRomID[0] and saDMLRomID[1]. There is some error checking you may want to look at in the source code, which comes with this tutorial.
unit PSD016eu; interface const version='23 Aug 06'; function ver:string; implementation function ver:string; begin result:=version; end; end.To the uses clause of DS042, add 'PSD016eu'.
laTture1.caption:=psd016eu.ver;Run the program, click on the Tture1 button. You should get 23 Aug 06 in the Tture1 label.
procedure DMLGet1820Tture(
var bTtureHi,bTtureLo,bTKBErr:byte;
var iDalErr:integer;
liHandle:longint;
baRomID:array of byte);
And just before the unit's final "end.", add the following initial test of some aspects of the code. This won't read the chip!....
procedure DMLGet1820Tture(
var bTtureHi,bTtureLo,bTKBErr:byte;
var iDalErr:integer;
liHandle:longint;
baRomID:array of byte);
begin
bTtureHi:=250;
bTtureLo:=251;
bTKBErr:=0;
iDalErr:=0;
end;//DMLGet1820Tture
Once that's done, over in DS042, make buTture0's handler be....
procedure TDS042f1.buReadTture0Click(Sender: TObject);
var c1,bTmp1,bTmp2,bTmp3:byte;
iTmp:integer;
begin
bTmp1:=10;//tmp for devel of DMLGet1820...
bTmp2:=10;//tmp for devel of DMLGet1820...
bTmp3:=10;//tmp for devel of DMLGet1820...
iTmp:=10;//tmp for devel of DMLGet1820...
DMLGet1820Tture(bTmp1,bTmp2,bTmp3,iTmp,liDMLSHandle,baDMLRomIDofOne);
laTture0.caption:=inttostr(bTmp1)+' '+
inttostr(bTmp2)+' '+
inttostr(bTmp3)+' '+
inttostr(iTmp);
end;
Run that. Study it to grasp all that you can about what's going on. Not the "var" in the declaration of the procedure... that's why values get passed back in the calling parameters.
for c1:=0 to kDMLRomIndexMax do begin
sTmp:=saDMLRomID[c1];
baDMLRomIDTable[c1,7]:=strtoint('$'+copy(sTmp,1,2));
baDMLRomIDTable[c1,6]:=strtoint('$'+copy(sTmp,3,2));
baDMLRomIDTable[c1,5]:=strtoint('$'+copy(sTmp,5,2));
baDMLRomIDTable[c1,4]:=strtoint('$'+copy(sTmp,7,2));
baDMLRomIDTable[c1,3]:=strtoint('$'+copy(sTmp,9,2));
baDMLRomIDTable[c1,2]:=strtoint('$'+copy(sTmp,11,2));
baDMLRomIDTable[c1,1]:=strtoint('$'+copy(sTmp,13,2));
baDMLRomIDTable[c1,0]:=strtoint('$'+copy(sTmp,15,2));
end;(*For c1:=...*)
We also need some other things before we can build the "innards" of DMLGet1820Tture. We will put some of them in PSD016 where they will be available for when we next need them.
procedure DMLEstablishSession(iPortNum,iAdapterType:longint;
var bTKBErr:byte; var liHandle:longint);
You need to put that in the interface section of PSD016eu, and, in the implementation section....
procedure DMLEstablishSession(iPortNum,iAdapterType:longint;
var bTKBErr:byte; var liHandle:longint);
(*On Exit...
if bTKBErr=0 then all was well, and a valid session handle is
established in liHandle
if bTKBErr=1 then TMExtendedStartSession failed, and error
code from Dallas returned in liHandle
if bTKBErr=2 then TMSetUp failed, and error
code from Dallas returned in liHandle*)
var iDMLTmpResult:longint;
begin
bTKBErr:=0;
liHandle:=TMExtendedStartSession(iPortNum,iAdapterType,nil);
if liHandle<0 then bTKBErr:=1;
if bTKBErr=0 then begin
iDMLTmpResult:=TMSetUp(liHandle);
if iDMLTmpResult<>1 then begin
bTKBErr:=2;
liHandle:=iDMLTmpResult;
end;(*iDMLTmpResult<>0*)
end;(*bTKBErr=0*)
end;//DMLEstablishSession
In order to use DMLEstablishSession, we need to add iBTMEXPW to the "uses" clause of DS043 and to create a "uses" clause with it in PSD016eu. The "uses" clause goes just after "interface". We also need to put a copy of IBTMEXPW.PAS, which Dallas supplies, into the project's folder. We also need "liDMLSHandle:longint;", which I "snuck in" along with all the other global variables a while back. liDMLSHandle won't be directly accessed by the underlying code of DMLEstablishSession, but it will be one of the variables we fill when we call DMLEstablishSession.
procedure TDS042f1.buReadTture0Click(Sender: TObject);
var c1,bTmp1,bTmp2,bTmp3:byte;
iTmp:integer;
begin
laTture0.caption:='One moment...';
//Execution of DMLGet1820... does take enough time that you
// Really Should add this "frill".
application.processmessages;
//without this, you don't see the caption change,
// (And even with it, you don't always see it!)
// because
// (at 24aug06) DMLGet18200... has a bad, Windows hogging,
// loop as part of WaitABit. Hey! This works.... even though
// it shouldn't. I'm not proud. If you can show me a better
// way within Delphi 2, I'll be delighted!
// ---------------------
//Needed over and over...
DMLEstablishSession(iDMLPortNum,iDMLAdapterType,bTmp,liDMLSHandle);
if bTmp<>0 then begin
showmessage('DMLEstablishSession reported an error. Try again. '+
'It may simply be that MicroLan was busy.');
end// no ; here
else begin
//First, put the ID of one chip (from table) into array for passing to Get1820...
//The first index of baDMLRomIDTable determines which chip will be read.
//For less flexibility, more speed, variables
// baDMLRomIDofChip0, baDMLRomIDofChip1, type array[0..7] of byte
// could be set up and used. This would save having to fill and
// refill the "scratch" variable baDMLRomIDofOne.
for c1:=0 to 7 do
baDMLRomIDofOne[c1]:=baDMLRomIDTable[0,c1];
DMLGet1820Tture(bTmp1,bTmp2,bTmp3,iTmp,liDMLSHandle,baDMLRomIDofOne);
laTture0.caption:=inttostr(bTmp1)+' '+
inttostr(bTmp2)+' '+
inttostr(bTmp3)+' '+
inttostr(iTmp);
//At a room temperature, the result of this may be 0 50 0 xx,
// where xx is a number. (It means nothing, if the third number is 0.
// Warm the device with your fingers, and the 50 will rise.)
//If third number not a zero, it means there was an error.
end;//else
end;//buReadTture0Click
=====var siaDMLRom:array[0..8] of smallint;(*Yes: 9 bytes.
the last isn't used every time, but I think
it IS used sometimes.
A curiosity: The type MUST(?) BE SMALLINT...
I accidentally had this as byte and got
GPFaults during TMRom call. (Problem
stuffing $FF into an element?)*)
baDMLStateBuffer:array[0..15361] of byte;
(*15360 might do, but for one byte, I covered
the possibility that I was misunderstanding
something in the Dallas docs*)
liHandle:longint;
... and replace the current DMLGet1820Tture code with the following. I'm sorry it is such a mess... but that is the glory of units... any calling program doesn't know what's in the mess, and as it is cleaned up, all calling programs will benefit.
procedure DMLGet1820Tture(
var bTtureHi,bTtureLo,bTKBErr:byte;
var iDalErr:integer;
liHandle:longint;
baRomID:array of byte);
// Here begins....
// A LONG bit of code!...
// AND BAD if remmed out "application.processmessages" in WaitAWhile
// not fixed or replaced yet.
//This code "ain't pretty", but I believe that the header is a
// basis which can be built on. It is about my third attempt...
//The code is suitable for chips with family codes $10 and $28.
var iDMLTmpResult:integer;
//boSetup:boolean;
(*This DMLGet1820Tture derived from
DMLGet1820TtureJly06, which had significant changes from
the older DMLGet1820Tture, even though it started from there.
AT PRESENT (12Jly06), it SHOULD work with ds1820's which are
supplied with 5v non-parasitically. It will even work with
parasitically powered 1820s, sometimes, but to make it work
reliably using only parasitic power, the software is supposed
to provide strong pull up...
which not all(?) adapters can do...*)
(*KEEP THIS AND dmlGet18B20Tture TUTORIAL/DS code IN STEP
THIS (in PSD016) modified from
"THIS" (in DS025) modified 10jly06, but it should still have the
"new at 10jly06" feature that even if tkbErr<>0,
procedure still releases session at end of call.
Calls TMEndSession. (line is remmed.)
ALSO added a TMReset block
Other old (at 7/06) "read Dallas chip" routines probably
need similar.
In the Dallas "read a temperature" demo code, there
seems to be a number of places where the code says
"Couldn't do that first time? Try a few times more"
In this code, we work through the stages of reading
a temperature, and if we get a snag at any point,
we give up. It is up to the calling program to
re-call this as often as it deems worthwhile.
N.B. This code does not check that the device it has
been sent to read is in fact a DS1820 (family code $10,
or compatible device). That check should be made before
this routine is called.*)
procedure WaitAWhile;
(*NOT a good idea, on several fronts.. but it works, and
the program has been constructed so that a better
solution can be implemented with few side effects.
Error codes may be confused, and non-unique, but working
towards....
nnnn.1110: A CheckSession failure
... otherwise, MSB carries info from last stages of routine,
LSB info from earlier parts
*)
var lwTmp:cardinal;//used longword in Delphi 7
begin
//Add 'WINDOWS' to the unit's USES clause in order to use "GetTickCount"
//This routine was once in the main unit of an application. Since
// the time it was moved into a subordinate unit,
// application.processmessages;
// has not been possible.
//Originally there was one loop....
// lwTmp:=GetTickCount+1800;(*lwTmp:=lwTmp+500; here causes a 500ms delay*)
// repeat
// application.processmessages;
// until GetTickCount>lwTmp;
//The delay is now made up of several shorter loops in
// hopes of not crashing Windows through
// excessive non-release.
//Not the best thing to do, even the old way, but
// the best I've found so far to meet the needs
// of the application!
//Doing 4x400 plus 1x200, total 1800, 1.8 seconds delay
lwTmp:=GetTickCount+400;(*lwTmp:=lwTmp+500; here causes a 500ms delay*)
repeat
until GetTickCount>lwTmp;
lwTmp:=GetTickCount+400;
repeat
until GetTickCount>lwTmp;
lwTmp:=GetTickCount+400;
repeat
until GetTickCount>lwTmp;
lwTmp:=GetTickCount+400;
repeat
until GetTickCount>lwTmp;
lwTmp:=GetTickCount+200;
repeat
until GetTickCount>lwTmp;
end;
function FillBufferNResetNAccess:byte;
var c1,bTmpErrLToFillNAcc:byte;
sChipId:string;
//generalize this for big DML library...
//BEFORE calling it, TMExtendedSession and TMSetup need
// to have executed okay.
//BEFORE calling, baRomID must hold chip's ID. As long as
// FillBuff... is inside DMLGet1820Tture this will be okay.
//It takes care of moving Dallas ID spec to buffer inside TMEX driver,
// resetting the MicroLan (part of TMAccess),
// and then accessing that chip, i.e. shutting down all the rest.
// TMAccess does not guarantee that a chip of the id you looked for was
// present, only that SOME chip was present.
// See... I think... StrongAccess if you need to KNOW chip present.
// (Different from Strong Pull Up.)
//Returns 0 if all went well, error code otherwise.
begin //FillBufferNAccess
bTmpErrLToFillNAcc:=0;//ERRor-flagLocalTo-Fill...
for c1:=0 to 7 do siaDMLRom[c1]:=baRomID[c1];
(*Prepare to pass chip ID to a buffer in TMEX driver.
It MAY not always be necessary to repeatedly refill
this buffer, but doing so makes this code more generally
useful.*)
try (*You could introduce other try... except... end blocks.
I only put them in where I needed them to work past
various errors in my code, errors which I hope are
now gone!*)
iDMLTmpResult:=TMRom(liHandle,@baDMLStateBuffer,@siaDMLRom);
//previous returns 1 when all well.
except
{ on EGPFault do begin
bTmpErrLToFillNAcc:=31;
iDalErr:=0;
iDMLTmpResult:=1;
end;}
end;//except
if iDMLTmpResult<>1 then begin
bTmpErrLToFillNAcc:=3;
iDalErr:=iDMLTmpResult;
end;
if bTmpErrLToFillNAcc=0 then begin //x1
iDMLTmpResult:=TMAccess(liHandle,@baDMLStateBuffer);
//TMAccess does MicroLan reset prior to shutting down other chips.
//Returns 1 is all well. (Can be fooled if chip of spec'd ID not on MivroLan)
if iDMLTmpResult<>1 then begin
bTmpErrLToFillNAcc:=4;
iDalErr:=iDMLTmpResult;
end //no ; here
else bTmpErrLToFillNAcc:=0;//Turn TMEX "no problem" into TKB "no problem"
end;//x1
result:=bTmpErrLToFillNAcc;
end;//FillBufferNAccess
begin (*DMLGet1820TtureJly06*)
bTKBErr:=0;
bTtureHi:=0;
bTtureLo:=0;
bTKBErr:=FillBufferNResetNAccess;//rework error codes and how things pass
if bTKBErr=0 (*CSess*) then begin
iDMLTmpResult:=TMValidSession(liHandle);
(*Check to see that liHandle, the session handle, is still
valid*)
if iDMLTmpResult<>1 then begin
bTKBErr:=14;// 0000.1110
iDalErr:=iDMLTmpResult;
end;
end;
(*Note "22Jun02": See DD17dU1 for possible enhancing code
involving invoking strong pull up here.. the other half
of this idea is flagged below with a ref to 22Jun03
7/06: Hardware probably NEEDS strong pull up here,
if DS1820's may be parasitically powered....
(8/06)...but they WILL work sometimes, even
parasitically powered.
7/06... this note may now be in wrong place*)
{if bTKBErr=0 (*CSess*) then begin
iDMLTmpResult:=TMValidSession(liHandle);
if iDMLTmpResult<>1 then begin
bTKBErr:=16+14;//MSB says WHICH CSess, LSB of 1110 says "A CSess failed"
iDalErr:=iDMLTmpResult;
end;
end;
}
// HERE IS where "go into strong pull-up after next op" should(?) be (7/06 note)
(*Now send message to chip telling it to take a reading*)
if bTKBErr=0 then begin
iDMLTmpResult:=TMTouchByte(liHandle,$44);//ConvertT cmnd
if iDMLTmpResult<0 then begin
bTKBErr:=5;
iDalErr:=iDMLTmpResult;(*-1 flags shorted ML*)
end;
end;
if bTKBErr=0 then WaitAWhile;
(*The above causes a delay so that the chip can complete
the process of taking a tture reading. There are better
ways to make a delay, and even better, there's a way
to ask the chip if the conversion is completed. Hey! This
is still worth what you paid me for it!
How long is long enough? That depends... It's probably worth
using the iButtonViewer (s/w supplied with SDK) to see
what sort of delays your chip is needing.
There is much talk in the literature about the "need" for
the master to put the ML into a string pull up state
during the conversion.... but the hardware I had (DS9097E+
DS1820) worked, even though I don't think I was able to have
a string pull up from the DS9097E. Go figger.
In a related, I suspect, vein: If in doubt, if possible, avoid
parasitic powering of devices. What's an extra wire for something
that works?! "One-Wire" was already two, with the ground wire.
After conversion, the tture should be in bytes 0 and 1 of
the scratchpad, lsb/msb respectively.*)
if bTKBErr=0 (*CSess*) then begin
iDMLTmpResult:=TMValidSession(liHandle);
if iDMLTmpResult<>1 then begin
bTKBErr:=32+14;
iDalErr:=iDMLTmpResult;
end;
end;
//HERE insert "leave strong pull up state" (7/06 note)
(*See note 22Jun03 above re...
See DD017d for the other half of the strong pull up stuff.*)
{reserved for cancel strong pull up...
if bTKBErr=0 then begin
err:=60
end;
if bTKBErr=0 (*CSess*) then begin
iDMLTmpResult:=TMValidSession(liHandle);
if iDMLTmpResult then begin
bTKBErr:=62;
iDalErr:=iDMLTmpResult;
end;
end; }
(*Now we start near the top of the sequence, this time
to access the tture reading...*)
if bTKBErr=0 then begin
bTKBErr:=FillBufferNResetNAccess;//need to refine error messaging
end;
if bTKBErr=0 (*CSess*) then begin
iDMLTmpResult:=TMValidSession(liHandle);
if iDMLTmpResult<>1 then begin
bTKBErr:=32+16+14;
iDalErr:=iDMLTmpResult;
end;
end;
if bTKBErr=0 then begin
iDMLTmpResult:=TMTouchByte(liHandle,$BE);//Send Read Scratchpad cmnd
if iDMLTmpResult<0 then begin
bTKBErr:=8;//(7 may still be avail... jumped to 8 from 6)
iDalErr:=iDMLTmpResult;
end;
end;
{No Check Session in DD17d... Would adding one wreck things?
if bTKBErr=0 (*CSess*) then begin
iDMLTmpResult:=TMValidSession(liHandle);
if iDMLTmpResult<>1 then begin
bTKBErr:=82;
iDalErr:=iDMLTmpResult;
end;
end; }
//Start reading 9 bytes from DS1820... 1st holds low byte of TTure
if bTKBErr=0 then begin
iDMLTmpResult:=TMTouchByte(liHandle,$FF);
bTtureLo:=iDMLTmpResult and $FF;
if iDMLTmpResult<0 then begin
bTKBErr:=8+1;//error codes get hairy if you go for remaining 7 data
iDalErr:=iDMLTmpResult;
end;
end;
if bTKBErr=0 (*CSess*) then begin
iDMLTmpResult:=TMValidSession(liHandle);
if iDMLTmpResult<>1 then begin
bTKBErr:=64+14;
iDalErr:=iDMLTmpResult;
end;
end;
//Read 2nd byte of datastream from 1820- high byte of TTure
if bTKBErr=0 then begin
iDMLTmpResult:=TMTouchByte(liHandle,$FF);
bTtureHi:=iDMLTmpResult and $FF;
if iDMLTmpResult<0 then begin
bTKBErr:=8+2;
iDalErr:=iDMLTmpResult;
end;
end;
//That's the tture data read. For a more robust application,
//read the following 7 bytes, too, as the last holds a CRC
//for testing whether data valid.
{DD17d had no CSess here, based, I believe, on Dallas sample code
would one hurt? matter?
if bTKBErr=0 (*CSess*) then begin
iDMLTmpResult:=TMValidSession(liHandle);
if iDMLTmpResult<>1 then begin
bTKBErr:=102;
iDalErr:=iDMLTmpResult;
end;
end; }
(*Following is NEW (in DS025) 10Jly06, and if successful needs
adding to Tut versions, etc*)
(*Even if bTKBErr<>0....
Reset perhaps not strictly necessary, as any access should START with
a reset. Waste of cycles? Tidy?*)
{ 12Jly06: IT MIGHT SEEM LIKE A GOOD IDEA TO RESET THE MICROLAN,
AND THE DEVICES ON IT, READY FOR NEXT READ CYCLE... BUT IF
YOU DO, ALL SORTS OF PROBLEMS ARISE FOR SUBSEQUENT TMSETUPs.
TIME NEEDED AFTER TMTOUCHRESET BEFORE A TMSETUP CAN BE ISSUED?
WHATEVER.... REMOVING THIS TMTOUCHRESET MADE A WHOLE BUNCH
OF PROBLEMS GO AWAY!!
if boSetup then begin //reset Microlan
iDMLTmpResult:=TMTouchReset(liHandle);//Done. Reset devices on the
//Microlan so they are ready for whatever comes next.
if (iDMLTmpResult<>1)and(iDMLTmpResult<>2) then begin
ReportError('Problem in DMLGet1820TtureJly06 10jly06b.');
bTKBErr:=64+bTKBErr;
iDalErr:=iDMLTmpResult;
end;
end;// of "if boSetup..."
}
{EVEN if bTKBErr<>0 then.... <-mod of 10jly06... and what follows expanded...}
iDMLTmpResult:=TMEndSession(liHandle);
if iDMLTmpResult<>1 then begin
//replace somehow ReportError('Problem in DMLGet1820TtureJly06 10jly06a.');
bTKBErr:=128+bTkbErr;//64 bit may already be set.
//needed???boErrorReported:=true;
iDalErr:=iDMLTmpResult;
end;
(*I suppose that in some circumstances, leaving the
session running continuously might work. I opted
for the simpler and more general answer: Start it
just before each use, shut it down as soon as not
needed immediately. There is an entry in the TMEX
help file suggesting that leaving a session
running is a bad idea.
Puzzle....
Is there a way to do TMExtendedStartSession
and TMSetUp just once, and then access the API
multiple times, each with an TMEndSession??
One attempt at that failed miserably, but I
think I am doing just that in some other program.
Maybe something that only works if only one
running program is accessing the API?*)
end;(*DMLGet1820Tture*)
======End DMLGet1820Tture code ====
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 .....