1 | unit UDallasProgrammer;
|
---|
2 |
|
---|
3 | {$mode delphi}
|
---|
4 |
|
---|
5 | interface
|
---|
6 |
|
---|
7 | uses
|
---|
8 | Classes, SysUtils, USerialPort, UCommSerialPort, UCommPin, UCommMark,
|
---|
9 | UJobProgressView, SyncObjs, DateUtils, Dialogs, URegistry,
|
---|
10 | Forms, UISPProgrammer, Registry, UBinarySerializer, SpecializedList,
|
---|
11 | UCommTelnet, UCommTelnetComPortOption, UCommConnector;
|
---|
12 |
|
---|
13 | const
|
---|
14 | Mark = #13#10;
|
---|
15 |
|
---|
16 | type
|
---|
17 | ETimeout = class(Exception);
|
---|
18 |
|
---|
19 | { TDallasProgrammer }
|
---|
20 |
|
---|
21 | TDallasProgrammer = class(TISPProgrammer)
|
---|
22 | private
|
---|
23 | FOnLogData: TOnLogDataEvent;
|
---|
24 | Pin: TCommPin;
|
---|
25 | CommMark: TCommMark;
|
---|
26 | ResponseQueue: TListObject;
|
---|
27 | ResponseLock: TCriticalSection;
|
---|
28 | ResponseTemp: TBinarySerializer;
|
---|
29 | ConnectorBackup: TDeviceConnector;
|
---|
30 | ConnectorBackupPin: TCommPin;
|
---|
31 | HexData: TStringList;
|
---|
32 | Request: TBinarySerializer;
|
---|
33 | Mark: TListByte;
|
---|
34 | procedure ReceiveData(Sender: TCommPin; Stream: TListByte);
|
---|
35 | function ReadResponse(Count: Integer = 0): string;
|
---|
36 | function ResponseCount: Integer;
|
---|
37 | procedure ResponseClear;
|
---|
38 | procedure CheckWriteErrorCode(Value: string);
|
---|
39 | procedure CheckResponseErrorCode(Value: string);
|
---|
40 | protected
|
---|
41 | procedure SetActive(AValue: Boolean); override;
|
---|
42 | public
|
---|
43 | Timeout: TDateTime;
|
---|
44 | Identification: string;
|
---|
45 | BaudRate: Integer;
|
---|
46 | Connector: TDeviceConnector;
|
---|
47 | procedure LoadFromRegistry(Root: HKEY; Key: string); override;
|
---|
48 | procedure SaveToRegistry(Root: HKEY; Key: string); override;
|
---|
49 | procedure Read(Job: TJob); override;
|
---|
50 | procedure Write(Job: TJob); override;
|
---|
51 | procedure Erase; override;
|
---|
52 | procedure Verify(Job: TJob); override;
|
---|
53 | procedure Reset; override;
|
---|
54 | function ReadIdentification: string; override;
|
---|
55 | constructor Create; override;
|
---|
56 | destructor Destroy; override;
|
---|
57 | property OnLogData: TOnLogDataEvent read FOnLogData write FOnLogData;
|
---|
58 | end;
|
---|
59 |
|
---|
60 | implementation
|
---|
61 |
|
---|
62 | resourcestring
|
---|
63 | STimeout = 'Timeout';
|
---|
64 | //SEmptyBuffer = 'Empty buffer';
|
---|
65 | SInvalidHexFormat = 'Invalid Intel Hex record format';
|
---|
66 | SFlashControllerError = 'Flash controller error';
|
---|
67 | SInvalidRecordAddress = 'Invalid address in Intel Hex record';
|
---|
68 | SInvalidRecordLength = 'Invalid Intel Hex record length';
|
---|
69 | SWriteFailure = 'Failure to write 1s to 0s during programming';
|
---|
70 | SInvalidRecordType = 'Invalid Intel Hex record type';
|
---|
71 | SInvalidRecordChecksum = 'Invalid checksum in Intel Hex record';
|
---|
72 | SVerifyError = 'Verify Error';
|
---|
73 | SInvalidResponse = 'Invalid response';
|
---|
74 | SUnknownProgrammerResponse = 'Unknown flash programmer response "%s"';
|
---|
75 | SIdentification = 'Device identification';
|
---|
76 |
|
---|
77 | { TDallasProgrammer }
|
---|
78 |
|
---|
79 | procedure TDallasProgrammer.ReceiveData(Sender: TCommPin; Stream: TListByte);
|
---|
80 | var
|
---|
81 | NewList: TListByte;
|
---|
82 | begin
|
---|
83 | try
|
---|
84 | ResponseLock.Acquire;
|
---|
85 | NewList := TListByte.Create;
|
---|
86 | NewList.Assign(Stream);
|
---|
87 | ResponseQueue.Add(NewList);
|
---|
88 | finally
|
---|
89 | ResponseLock.Release;
|
---|
90 | end;
|
---|
91 | end;
|
---|
92 |
|
---|
93 | function TDallasProgrammer.ReadResponse(Count: Integer = 0): string;
|
---|
94 | var
|
---|
95 | Serializer: TBinarySerializer;
|
---|
96 | StartTime: TDateTime;
|
---|
97 | ElapsedTime: TDateTime;
|
---|
98 | begin
|
---|
99 | StartTime := Now;
|
---|
100 | repeat
|
---|
101 | if Count = 0 then begin
|
---|
102 | if ResponseCount > 0 then Break;
|
---|
103 | end else begin
|
---|
104 | if ResponseCount >= Count then Break;
|
---|
105 | end;
|
---|
106 | Sleep(1);
|
---|
107 | ElapsedTime := Now - StartTime;
|
---|
108 | until (ElapsedTime > Timeout);
|
---|
109 | if ElapsedTime > Timeout then
|
---|
110 | raise Exception.Create(STimeout);
|
---|
111 | try
|
---|
112 | ResponseLock.Acquire;
|
---|
113 | Serializer := TBinarySerializer.Create;
|
---|
114 | Serializer.List := TListByte(ResponseQueue.First);
|
---|
115 | if Count = 0 then
|
---|
116 | Result := Serializer.ReadString(Serializer.List.Count)
|
---|
117 | else Result := Serializer.ReadString(Count);
|
---|
118 | ResponseQueue.Delete(0);
|
---|
119 | finally
|
---|
120 | Serializer.Free;
|
---|
121 | ResponseLock.Release;
|
---|
122 | end;
|
---|
123 | end;
|
---|
124 |
|
---|
125 | function TDallasProgrammer.ResponseCount: Integer;
|
---|
126 | begin
|
---|
127 | try
|
---|
128 | ResponseLock.Acquire;
|
---|
129 | Result := ResponseQueue.Count;
|
---|
130 | finally
|
---|
131 | ResponseLock.Release;
|
---|
132 | end;
|
---|
133 | end;
|
---|
134 |
|
---|
135 | procedure TDallasProgrammer.ResponseClear;
|
---|
136 | begin
|
---|
137 | try
|
---|
138 | ResponseLock.Acquire;
|
---|
139 | ResponseQueue.Clear;
|
---|
140 | finally
|
---|
141 | ResponseLock.Release;
|
---|
142 | end;
|
---|
143 | end;
|
---|
144 |
|
---|
145 | procedure TDallasProgrammer.SetActive(AValue: Boolean);
|
---|
146 | var
|
---|
147 | SerialPort: TCommSerialPort;
|
---|
148 | TelnetOption: TTelnetOptionComPort;
|
---|
149 | begin
|
---|
150 | if Active = AValue then Exit;
|
---|
151 | inherited;
|
---|
152 | if AValue then begin
|
---|
153 | Request := TBinarySerializer.Create;
|
---|
154 | Request.List := TListByte.Create;
|
---|
155 | Request.OwnsList := True;
|
---|
156 | HexData := TStringList.Create;
|
---|
157 |
|
---|
158 | Connector.Active := False;
|
---|
159 | ConnectorBackup.Assign(Connector);
|
---|
160 | ConnectorBackupPin := Connector.Pin.RemotePin;
|
---|
161 | Connector.Pin.Disconnect;
|
---|
162 | if Connector.ConnectionType = ctSerialPort then begin
|
---|
163 | //SerialPort.Name := SerialPort.Name;
|
---|
164 | Connector.CommSerial.SerialPort.FlowControl := fcNone;
|
---|
165 | Connector.CommSerial.SerialPort.BaudRate := BaudRate;
|
---|
166 | Connector.CommSerial.SerialPort.DTR := True;
|
---|
167 | Connector.CommSerial.SerialPort.Flush;
|
---|
168 | end else
|
---|
169 | if Connector.ConnectionType = ctNetworkClient then begin
|
---|
170 | TelnetOption := TTelnetOptionComPort(Connector.CommTelnet.SearchOption(tmComPortControlOption));
|
---|
171 | TelnetOption.FlowControl := fcNone;
|
---|
172 | TelnetOption.BaudRate := BaudRate;
|
---|
173 | TelnetOption.DTR := True;
|
---|
174 | end;
|
---|
175 | Connector.CommSerial.SerialPort.Purge;
|
---|
176 | Connector.CommTelnet.Purge;
|
---|
177 | Connector.Pin.Connect(CommMark.PinRaw);
|
---|
178 | if Assigned(FOnLogData) then
|
---|
179 | Pin.OnLogData := FOnLogData;
|
---|
180 | CommMark.Active := True;
|
---|
181 | Connector.Active := True;
|
---|
182 | ResponseClear;
|
---|
183 | Sleep(1000);
|
---|
184 | ReadIdentification;
|
---|
185 | end else begin
|
---|
186 | CommMark.Active := False;
|
---|
187 | Connector.Active := False;
|
---|
188 | Connector.Assign(ConnectorBackup);
|
---|
189 | Connector.Pin.Connect(ConnectorBackupPin);
|
---|
190 | Connector.Active := True;
|
---|
191 | HexData.Free;
|
---|
192 | Request.Free;
|
---|
193 | end;
|
---|
194 | end;
|
---|
195 |
|
---|
196 | procedure TDallasProgrammer.CheckWriteErrorCode(Value: string);
|
---|
197 | begin
|
---|
198 | if Value = 'H' then raise Exception.Create(SInvalidHexFormat)
|
---|
199 | else if Value = 'F' then raise Exception.Create(SFlashControllerError)
|
---|
200 | else if Value = 'A' then raise Exception.Create(SInvalidRecordAddress)
|
---|
201 | else if Value = 'L' then raise Exception.Create(SInvalidRecordLength)
|
---|
202 | else if Value = 'P' then raise Exception.Create(SWriteFailure)
|
---|
203 | else if Value = 'R' then raise Exception.Create(SInvalidRecordType)
|
---|
204 | else if Value = 'S' then raise Exception.Create(SInvalidRecordChecksum)
|
---|
205 | else if Value = 'V' then raise Exception.Create(SVerifyError)
|
---|
206 | else if Value = '' then raise Exception.Create(SInvalidResponse)
|
---|
207 | else if Value <> 'G' then raise Exception.Create(Format(SUnknownProgrammerResponse, [Value]));
|
---|
208 | end;
|
---|
209 |
|
---|
210 | procedure TDallasProgrammer.CheckResponseErrorCode(Value: string);
|
---|
211 | begin
|
---|
212 | if Value <> '' then begin
|
---|
213 | if Value[1] = 'E' then
|
---|
214 | raise Exception.Create('Dallas error: ' + Value);
|
---|
215 | end;
|
---|
216 | end;
|
---|
217 |
|
---|
218 | procedure TDallasProgrammer.LoadFromRegistry(Root: HKEY; Key: string);
|
---|
219 | begin
|
---|
220 | with TRegistryEx.Create do
|
---|
221 | try
|
---|
222 | RootKey := Root;
|
---|
223 | OpenKey(Key + '\ISPProgrammer\Dallas', True);
|
---|
224 | BaudRate := ReadIntegerWithDefault('FirmwareBaudRateNumeric', 57600);
|
---|
225 | finally
|
---|
226 | Free;
|
---|
227 | end;
|
---|
228 | end;
|
---|
229 |
|
---|
230 | procedure TDallasProgrammer.SaveToRegistry(Root: HKEY; Key: string);
|
---|
231 | begin
|
---|
232 | with TRegistryEx.Create do
|
---|
233 | try
|
---|
234 | RootKey := Root;
|
---|
235 | OpenKey(Key + '\ISPProgrammer\Dallas', True);
|
---|
236 | WriteInteger('FirmwareBaudRateNumeric', Integer(BaudRate));
|
---|
237 | finally
|
---|
238 | Free;
|
---|
239 | end;
|
---|
240 | end;
|
---|
241 |
|
---|
242 | procedure TDallasProgrammer.Read(Job: TJob);
|
---|
243 | var
|
---|
244 | Value: string;
|
---|
245 | I: Integer;
|
---|
246 | begin
|
---|
247 | Active := True;
|
---|
248 |
|
---|
249 | Request.List.Count := 0;
|
---|
250 | ResponseClear;
|
---|
251 | Request.WriteByte(Ord('D'));
|
---|
252 | Pin.Send(Request.List);
|
---|
253 | Value := ReadResponse;
|
---|
254 | Value := ReadResponse; // Empty line
|
---|
255 | CheckResponseErrorCode(Value);
|
---|
256 |
|
---|
257 | //HexFile.SaveToStringList(HexData);
|
---|
258 | Job.Progress.Max := 65535 div 32;
|
---|
259 | //Request.Size := 0;
|
---|
260 | //ResponseClear;
|
---|
261 | I := 0;
|
---|
262 | repeat
|
---|
263 | Value := ReadResponse;
|
---|
264 | if Value <> '' then begin
|
---|
265 | //Log(Value);
|
---|
266 | HexData.Add(Value);
|
---|
267 | if Value = ':00000001FF' then Break;
|
---|
268 | Inc(I);
|
---|
269 | end;
|
---|
270 | Job.Progress.Value := I;
|
---|
271 | if Job.Terminate then Break;
|
---|
272 | until False;
|
---|
273 | //for I := 0 to 10 do //HexData.Count - 1 do
|
---|
274 | // Log(HexData[I]);
|
---|
275 | if not Job.Terminate then
|
---|
276 | HexFile.LoadFromStringList(HexData);
|
---|
277 | end;
|
---|
278 |
|
---|
279 | procedure TDallasProgrammer.Verify(Job: TJob);
|
---|
280 | var
|
---|
281 | Value: string;
|
---|
282 | I: Integer;
|
---|
283 | begin
|
---|
284 | Active := True;
|
---|
285 |
|
---|
286 | Request.List.Count := 0;
|
---|
287 | ResponseClear;
|
---|
288 | Request.WriteByte(Ord('V'));
|
---|
289 | Pin.Send(Request.List);
|
---|
290 | ReadResponse;
|
---|
291 |
|
---|
292 | try
|
---|
293 | CommMark.Mark.Clear;
|
---|
294 | HexFile.SaveToStringList(HexData);
|
---|
295 | Job.Progress.Max := HexData.Count;
|
---|
296 | for I := 0 to HexData.Count - 1 do begin
|
---|
297 | Request.Clear;
|
---|
298 | ResponseClear;
|
---|
299 | Request.WriteString(HexData[I]);
|
---|
300 | Request.WriteList(Mark, 0, Mark.Count);
|
---|
301 | Pin.Send(Request.List);
|
---|
302 | Value := ReadResponse;
|
---|
303 | CheckWriteErrorCode(Value);
|
---|
304 | Job.Progress.Value := I;
|
---|
305 | if Job.Terminate then Break;
|
---|
306 | end;
|
---|
307 | finally
|
---|
308 | CommMark.Mark.Assign(Mark);
|
---|
309 | end;
|
---|
310 | end;
|
---|
311 |
|
---|
312 | procedure TDallasProgrammer.Write(Job: TJob);
|
---|
313 | var
|
---|
314 | Value: string;
|
---|
315 | I: Integer;
|
---|
316 | begin
|
---|
317 | Active := True;
|
---|
318 | Request.Clear;
|
---|
319 | ResponseClear;
|
---|
320 | Request.WriteByte(Ord('L'));
|
---|
321 | Pin.Send(Request.List);
|
---|
322 | Value := ReadResponse;
|
---|
323 |
|
---|
324 | try
|
---|
325 | CommMark.Mark.Clear;
|
---|
326 | HexFile.SaveToStringList(HexData);
|
---|
327 | Job.Progress.Max := HexData.Count;
|
---|
328 | // Delayed response check mechanism,
|
---|
329 | // to avoid thread sleep(1) to context switch after 16 ms
|
---|
330 | // During 16 ms delay: 57600 / 10 * 0.016 = 97 bytes can be transfered
|
---|
331 | // One line of hex file is 74 bytes long
|
---|
332 | // Then two during context switching next line should be written to output buffer
|
---|
333 | // On I = 0 do not check response
|
---|
334 | // On I = HexData.Count do not send data, only check response
|
---|
335 | ResponseClear;
|
---|
336 | for I := 0 to HexData.Count do begin
|
---|
337 | if I < HexData.Count then begin
|
---|
338 | Request.Clear;
|
---|
339 | Request.WriteString(HexData[I]);
|
---|
340 | Request.WriteList(Mark, 0, Mark.Count);
|
---|
341 | Pin.Send(Request.List);
|
---|
342 | end;
|
---|
343 | if I > 0 then begin
|
---|
344 | Value := ReadResponse;
|
---|
345 | CheckWriteErrorCode(Value);
|
---|
346 | end;
|
---|
347 | Job.Progress.Value := I;
|
---|
348 | if Job.Terminate then Break;
|
---|
349 | end;
|
---|
350 | finally
|
---|
351 | CommMark.Mark.Assign(Mark);
|
---|
352 | end;
|
---|
353 | end;
|
---|
354 |
|
---|
355 | procedure TDallasProgrammer.Erase;
|
---|
356 | begin
|
---|
357 | Active := True;
|
---|
358 | Request.Clear;
|
---|
359 | ResponseClear;
|
---|
360 | Request.WriteByte(Ord('K'));
|
---|
361 | Pin.Send(Request.List);
|
---|
362 | CheckResponseErrorCode(ReadResponse);
|
---|
363 | end;
|
---|
364 |
|
---|
365 | procedure TDallasProgrammer.Reset;
|
---|
366 | begin
|
---|
367 | end;
|
---|
368 |
|
---|
369 | function TDallasProgrammer.ReadIdentification: string;
|
---|
370 | var
|
---|
371 | Value: string;
|
---|
372 | begin
|
---|
373 | Result := '';
|
---|
374 | Active := True;
|
---|
375 |
|
---|
376 | ResponseClear;
|
---|
377 | Request.Clear;
|
---|
378 | Pin.Send(Request.List);
|
---|
379 | Value := ReadResponse; // Empty line
|
---|
380 | Identification := ReadResponse;
|
---|
381 |
|
---|
382 | // Make one empty command to clear possible previous data
|
---|
383 | ResponseClear;
|
---|
384 | Request.Clear;
|
---|
385 | Pin.Send(Request.List);
|
---|
386 | Value := ReadResponse; // Empty line
|
---|
387 |
|
---|
388 | Log(SIdentification + ': ' + Identification);
|
---|
389 | end;
|
---|
390 |
|
---|
391 | constructor TDallasProgrammer.Create;
|
---|
392 | begin
|
---|
393 | inherited;
|
---|
394 | Capabilities := [ipcErase, ipcRead, ipcWrite, ipcReset];
|
---|
395 | Timeout := 3000 * OneMillisecond;
|
---|
396 | ResponseQueue := TListObject.Create;
|
---|
397 | ResponseLock := TCriticalSection.Create;
|
---|
398 | ResponseTemp := TBinarySerializer.Create;
|
---|
399 | ResponseTemp.List := TListByte.Create;
|
---|
400 | ResponseTemp.OwnsList := True;
|
---|
401 | Pin := TCommPin.Create;
|
---|
402 | Pin.OnReceive := ReceiveData;
|
---|
403 | Mark := TListByte.Create;
|
---|
404 | Mark.SetArray([13, 10]);
|
---|
405 | CommMark := TCommMark.Create(nil);
|
---|
406 | CommMark.Mark.Assign(Mark);
|
---|
407 | CommMark.PinFrame.Connect(Pin);
|
---|
408 | BaudRate := 9600;
|
---|
409 | ConnectorBackup := TDeviceConnector.Create;
|
---|
410 | end;
|
---|
411 |
|
---|
412 | destructor TDallasProgrammer.Destroy;
|
---|
413 | begin
|
---|
414 | Active := False;
|
---|
415 | FreeAndNil(Mark);
|
---|
416 | FreeAndNil(CommMark);
|
---|
417 | FreeAndNil(ConnectorBackup);
|
---|
418 | FreeAndNil(Pin);
|
---|
419 | FreeAndNil(ResponseQueue);
|
---|
420 | FreeAndNil(ResponseLock);
|
---|
421 | FreeAndNil(ResponseTemp);
|
---|
422 | inherited;
|
---|
423 | end;
|
---|
424 |
|
---|
425 | end.
|
---|
426 |
|
---|