[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;//DMLGet1820TtureOnce 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;//DMLEstablishSessionIn 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 .....