1
0

usbasp.pas 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. unit USBasp;
  2. {
  3. This file is part of Nephelae's Object Pascal FPUSBasp.
  4. USBasp Class.
  5. Copyright (C) 2022 - 2025 Dimitrios Chr. Ioannidis.
  6. Nephelae - https://www.nephelae.eu
  7. https://www.nephelae.eu/
  8. Licensed under the MIT License (MIT).
  9. See licence file in root directory.
  10. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
  11. ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
  12. TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
  13. PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
  14. SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
  15. ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  16. ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  17. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  18. OTHER DEALINGS IN THE SOFTWARE.
  19. }
  20. {$mode objfpc}{$H+}
  21. interface
  22. uses
  23. Classes, SysUtils, syncobjs, hidapi,
  24. SPSCRingBuffer, USBasp_Threads;
  25. const
  26. USBASP_SHARED_VID = $16C0;
  27. USBASP_SHARED_PID = $05DC;
  28. USBASP_FUNC_GETCAPABILITIES = 127;
  29. USBASP_CAP_0_TPI = 1;
  30. USBASP_CAP_PDI = 16;
  31. USBASP_CAP_2_SNHIDUPDATE = 32;
  32. USBASP_CAP_6_UART = 64;
  33. USBASP_CAP_7_HID_UART = 128;
  34. USBASP_NO_CAPS = (-4);
  35. USBASP_CAP_12MHZ_CLOCK = 0;
  36. USBASP_CAP_16MHZ_CLOCK = 1;
  37. USBASP_CAP_18MHZ_CLOCK = 2;
  38. USBASP_CAP_20MHZ_CLOCK = 3;
  39. PROG_STATE_IDLE = 0;
  40. PROG_STATE_WRITEFLASH = 1;
  41. PROG_STATE_READFLASH = 2;
  42. PROG_STATE_READEEPROM = 3;
  43. PROG_STATE_WRITEEEPROM = 4;
  44. PROG_STATE_TPI_READ = 5;
  45. PROG_STATE_TPI_WRITE = 6;
  46. PROG_STATE_SET_REPORT = 7;
  47. UART_STATE_ENABLED = 16;
  48. UART_STATE_DISABLED = 0;
  49. // USBasp UART Extension
  50. // https://github.com/dioannidis/usbasp/blob/master/firmware/usbasp.h
  51. USBASP_UART_PARITY_MASK = %11;
  52. USBASP_UART_PARITY_NONE = %00;
  53. USBASP_UART_PARITY_EVEN = %01;
  54. USBASP_UART_PARITY_ODD = %10;
  55. USBASP_UART_STOP_MASK = %100;
  56. USBASP_UART_STOP_1BIT = %000;
  57. USBASP_UART_STOP_2BIT = %100;
  58. USBASP_UART_BYTES_MASK = %111000;
  59. USBASP_UART_BYTES_5B = %000000;
  60. USBASP_UART_BYTES_6B = %001000;
  61. USBASP_UART_BYTES_7B = %010000;
  62. USBASP_UART_BYTES_8B = %011000;
  63. USBASP_UART_BYTES_9B = %100000;
  64. TUARTBaudRate: array[0..13] of integer =
  65. (300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 28800, 31250,
  66. 38400, 57600, 74880, 115200);
  67. TUARTDataBits: array[0..4] of integer =
  68. (USBASP_UART_BYTES_5B, USBASP_UART_BYTES_6B, USBASP_UART_BYTES_7B,
  69. USBASP_UART_BYTES_8B, USBASP_UART_BYTES_9B);
  70. TUARTParity: array[0..2] of integer =
  71. (USBASP_UART_PARITY_NONE, USBASP_UART_PARITY_EVEN, USBASP_UART_PARITY_ODD);
  72. TUARTStopBit: array[0..1] of integer = (USBASP_UART_STOP_1BIT, USBASP_UART_STOP_2BIT);
  73. type
  74. { TUSBasp }
  75. TUSBasp = class(TObject)
  76. private
  77. FUART_HIDDevice, FStatus_HIDDevice: PHidDevice;
  78. FUART_HIDDevicePath, FStatus_HIDDevicePath, FSerial, FManufacturer,
  79. FProductName, FHidApiVersionString: string;
  80. FConnected, FHasHIDUart, FHasStatus, FHasPDI, FHasTPI, FHasSNWrite,
  81. FUARTOpened: boolean;
  82. FCrystalOSC: integer;
  83. FStatus_RxEvent, FStatus_TxEvent, FUART_RxEvent, FUART_TxEvent,
  84. FUnpluggedEvent: TEvent;
  85. FHidErrorStr: string;
  86. FUART_RxBuffer, FUART_TxBuffer, FStatus_RxBuffer,
  87. FStatus_TxBuffer: TSPSCRingBuffer;
  88. FUART_RxThread: TThread_HID_UART_Rx;
  89. FUART_TxThread: TThread_HID_UART_Tx;
  90. FStatus_RxThread: TThread_HID_Status_Rx;
  91. FStatus_TxThread: TThread_HID_Status_Tx;
  92. function HIDEnumerate(const ASerialNumber: string = ''): boolean;
  93. protected
  94. property ReceiveBuffer: TSPSCRingBuffer read FUART_RxBuffer;
  95. property ReceiveEvent: TEvent read FUART_RxEvent;
  96. property TransmitBuffer: TSPSCRingBuffer read FUART_TxBuffer;
  97. property TransmitEvent: TEvent read FUART_TxEvent;
  98. property MonitorBuffer: TSPSCRingBuffer read FStatus_RxBuffer;
  99. property MonitorEvent: TEvent read FStatus_RxEvent;
  100. property DisconnectEvent: TEvent read FUnpluggedEvent;
  101. public
  102. constructor Create;
  103. destructor Destroy; override;
  104. function Connect(const ASerialNumber: string = ''): boolean;
  105. function Disconnect: boolean;
  106. function UARTOpen(const ABaudRate, ADataBits, AParity, AStopBits: integer): boolean;
  107. function UARTClose: boolean;
  108. function ChangeSerialNumber(const ASerialNumber: string): integer;
  109. property Connected: boolean read FConnected;
  110. property UARTOpened: boolean read FUARTOpened;
  111. property HidApiVersion: string read FHidApiVersionString;
  112. end;
  113. implementation
  114. type
  115. TUSBPacketBuffer = array[0..8] of byte;
  116. { TUSBasp }
  117. constructor TUSBasp.Create;
  118. begin
  119. FConnected := False;
  120. FUARTOpened := False;
  121. FSerial := '';
  122. FUART_RxBuffer := TSPSCRingBuffer.Create(1024);
  123. FUART_TxBuffer := TSPSCRingBuffer.Create(1024);
  124. FStatus_RxBuffer := TSPSCRingBuffer.Create(1024);
  125. FStatus_TxBuffer := TSPSCRingBuffer.Create(1024);
  126. Randomize;
  127. FUART_RxEvent := TEvent.Create(nil, False, False, 'UARTRX_' + IntToStr(Random(999)));
  128. FUART_TxEvent := TEvent.Create(nil, True, False, 'UARTTX_' + IntToStr(Random(999)));
  129. FStatus_RxEvent := TEvent.Create(nil, False, False, 'STUSRX_' +
  130. IntToStr(Random(999)));
  131. FStatus_TxEvent := TEvent.Create(nil, True, False, 'STUSTX_' +
  132. IntToStr(Random(999)));
  133. FUnpluggedEvent := TEvent.Create(nil, False, False, 'UNPLD_' +
  134. IntToStr(Random(999)));
  135. HidInit();
  136. end;
  137. destructor TUSBasp.Destroy;
  138. begin
  139. Disconnect;
  140. FreeAndNil(FUART_RxBuffer);
  141. FreeAndNil(FUART_TxBuffer);
  142. FreeAndNil(FStatus_RxBuffer);
  143. FreeAndNil(FStatus_TxBuffer);
  144. FUART_RxEvent.Free;
  145. FUART_TxEvent.Free;
  146. FStatus_RxEvent.Free;
  147. FStatus_TxEvent.Free;
  148. FUnpluggedEvent.Free;
  149. HidExit();
  150. inherited Destroy;
  151. end;
  152. function TUSBasp.Connect(const ASerialNumber: string = ''): boolean;
  153. begin
  154. FConnected := False;
  155. if not FConnected then
  156. begin
  157. if not HIDEnumerate(ASerialNumber) then
  158. Exit;
  159. FUART_HIDDevice := THidDevice.OpenPath(FUART_HIDDevicePath);
  160. FHidErrorStr := FUART_HIDDevice^.GetError;
  161. if FHidErrorStr <> 'Success' then
  162. Exit;
  163. FUART_HIDDevice^.SetNonBlocking(0);
  164. FUART_RxThread := TThread_HID_UART_Rx.Create(
  165. FUART_HIDDevice, FUART_RxBuffer, FUART_RxEvent, FUnpluggedEvent);
  166. FUART_TxThread := TThread_HID_UART_Tx.Create(
  167. FUART_HIDDevice, FUART_TxBuffer, FUART_TxEvent, FUnpluggedEvent);
  168. if FHasStatus then
  169. begin
  170. FStatus_HIDDevice := THidDevice.OpenPath(FStatus_HIDDevicePath);
  171. FHidErrorStr := FStatus_HIDDevice^.GetError;
  172. if FHidErrorStr = 'Success' then
  173. begin
  174. FStatus_HIDDevice^.SetNonBlocking(0);
  175. FStatus_RxThread := TThread_HID_Status_Rx.Create(FStatus_HIDDevice,
  176. FStatus_RxBuffer, FStatus_RxEvent, FUnpluggedEvent);
  177. FStatus_TxThread := TThread_HID_Status_Tx.Create(FStatus_HIDDevice,
  178. FStatus_TxBuffer, FStatus_TxEvent, FUnpluggedEvent);
  179. end;
  180. end;
  181. FConnected := True;
  182. end;
  183. Result := FConnected;
  184. end;
  185. function TUSBasp.Disconnect: boolean;
  186. begin
  187. if FConnected then
  188. begin
  189. if FUARTOpened then
  190. UARTClose;
  191. FUART_RxThread.Terminate;
  192. FUART_RxThread.WaitFor;
  193. FreeAndNil(FUART_RxThread);
  194. FUART_TxThread.Terminate;
  195. FUART_TxEvent.SetEvent;
  196. FUART_TxThread.WaitFor;
  197. FreeAndNil(FUART_TxThread);
  198. FUART_HIDDevice^.Close;
  199. FUART_HIDDevice := nil;
  200. if FHasStatus then
  201. begin
  202. FStatus_RxThread.Terminate;
  203. FStatus_RxThread.WaitFor;
  204. FreeAndNil(FStatus_RxThread);
  205. FStatus_TxThread.Terminate;
  206. FStatus_TxEvent.SetEvent;
  207. FStatus_TxThread.WaitFor;
  208. FreeAndNil(FStatus_TxThread);
  209. FStatus_HIDDevice^.Close;
  210. FStatus_HIDDevice := nil;
  211. end;
  212. FConnected := False;
  213. end;
  214. Result := FConnected;
  215. end;
  216. function TUSBasp.UARTOpen(const ABaudRate, ADataBits, AParity,
  217. AStopBits: integer): boolean;
  218. var
  219. Buffer: TUSBPacketBuffer;
  220. Prescaler: word;
  221. begin
  222. if FConnected and not FUARTOpened then
  223. begin
  224. Prescaler := FCrystalOsc div 8 div ABaudRate - 1;
  225. Buffer[0] := 0;
  226. Buffer[1] := lo(Prescaler);
  227. Buffer[2] := hi(Prescaler);
  228. Buffer[3] := ADataBits or AStopBits or AParity;
  229. if FUART_HIDDevice^.SendFeatureReport(Buffer, 8 + 1) > 0 then
  230. FUARTOpened := True;
  231. end;
  232. Result := FUARTOpened;
  233. end;
  234. function TUSBasp.UARTClose: boolean;
  235. var
  236. Buffer: TUSBPacketBuffer;
  237. begin
  238. if FConnected and FUARTOpened then
  239. begin
  240. Buffer[0] := 0;
  241. Buffer[1] := 0;
  242. Buffer[2] := 0;
  243. Buffer[3] := 0;
  244. FUART_HIDDevice^.SendFeatureReport(Buffer, 8 + 1);
  245. FUARTOpened := False;
  246. end;
  247. Result := FUARTOpened;
  248. end;
  249. function TUSBasp.HIDEnumerate(const ASerialNumber: string): boolean;
  250. var
  251. HidItemRoot, HidItem: PHidDeviceInfo;
  252. HIDDeviceMatch: boolean;
  253. FeatureReportBuffer: TUSBPacketBuffer;
  254. begin
  255. Result := False;
  256. FUART_HIDDevicePath := '';
  257. FStatus_HIDDevicePath := '';
  258. FUART_HIDDevice := nil;
  259. FStatus_HIDDevice := nil;
  260. try
  261. HidItemRoot := THidDeviceInfo.Enumerate(USBASP_SHARED_VID, USBASP_SHARED_PID);
  262. HidItem := HidItemRoot;
  263. while Assigned(HidItem) do
  264. begin
  265. HIDDeviceMatch := True;
  266. if ASerialNumber <> '' then
  267. if ASerialNumber <> PCWCharToUnicodeString(HidItem^.SerialNumber) then
  268. HIDDeviceMatch := False;
  269. if HIDDeviceMatch then
  270. case HidItem^.InterfaceNumber of
  271. 1: begin
  272. FUART_HIDDevicePath := HidItem^.Path;
  273. Result := True;
  274. end;
  275. 2: begin
  276. FStatus_HIDDevicePath := HidItem^.Path;
  277. FHasStatus := True;
  278. end;
  279. end;
  280. HidItem := HidItem^.Next;
  281. end;
  282. finally
  283. HidItemRoot^.Free;
  284. end;
  285. if Result then
  286. begin
  287. FUART_HIDDevice := THidDevice.OpenPath(FUART_HIDDevicePath);
  288. FSerial := FUART_HIDDevice^.GetSerialNumberString;
  289. FManufacturer := FUART_HIDDevice^.GetManufacturerString;
  290. FProductName := FUART_HIDDevice^.GetProductString;
  291. if FUART_HIDDevice^.GetFeatureReport(FeatureReportBuffer, 8 + 1) > 0 then
  292. begin
  293. case FeatureReportBuffer[6] of
  294. USBASP_CAP_12MHZ_CLOCK: FCrystalOsc := 12000000;
  295. USBASP_CAP_16MHZ_CLOCK: FCrystalOsc := 16000000;
  296. USBASP_CAP_18MHZ_CLOCK: FCrystalOsc := 18000000;
  297. USBASP_CAP_20MHZ_CLOCK: FCrystalOsc := 20000000;
  298. end;
  299. FHasHIDUart := (FeatureReportBuffer[5] and USBASP_CAP_7_HID_UART) =
  300. USBASP_CAP_7_HID_UART;
  301. FHasPDI := (FeatureReportBuffer[5] and USBASP_CAP_PDI) = USBASP_CAP_PDI;
  302. FHasTPI := (FeatureReportBuffer[5] and USBASP_CAP_0_TPI) = USBASP_CAP_0_TPI;
  303. FHasSNWrite := (FeatureReportBuffer[5] and USBASP_CAP_2_SNHIDUPDATE) =
  304. USBASP_CAP_2_SNHIDUPDATE;
  305. end;
  306. FUART_HIDDevice^.Close;
  307. FUART_HIDDevice := nil;
  308. end;
  309. end;
  310. function TUSBasp.ChangeSerialNumber(const ASerialNumber: string): integer;
  311. var
  312. Buffer: TUSBPacketBuffer;
  313. ErrorCode: integer;
  314. SerNumValue: word;
  315. begin
  316. Result := 0;
  317. if FConnected and not FUARTOpened then
  318. begin
  319. Val(ASerialNumber, SerNumValue, ErrorCode);
  320. Buffer[0] := 0;
  321. Buffer[1] := lo(SerNumValue);
  322. Buffer[2] := Hi(SerNumValue);
  323. Buffer[4] := 1;
  324. FUART_HIDDevice^.SendFeatureReport(Buffer, 8 + 1);
  325. end;
  326. end;
  327. end.