usbasp.pas 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  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. //FLastUsbError: integer;
  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. { TUSBasp }
  115. constructor TUSBasp.Create;
  116. begin
  117. FConnected := False;
  118. FUARTOpened := False;
  119. FSerial := '';
  120. FUART_RxBuffer := TSPSCRingBuffer.Create(1024);
  121. FUART_TxBuffer := TSPSCRingBuffer.Create(1024);
  122. FStatus_RxBuffer := TSPSCRingBuffer.Create(1024);
  123. FStatus_TxBuffer := TSPSCRingBuffer.Create(1024);
  124. Randomize;
  125. FUART_RxEvent := TEvent.Create(nil, False, False, 'UARTRX_' + IntToStr(Random(999)));
  126. FUART_TxEvent := TEvent.Create(nil, True, False, 'UARTTX_' + IntToStr(Random(999)));
  127. FStatus_RxEvent := TEvent.Create(nil, False, False, 'STUSRX_' +
  128. IntToStr(Random(999)));
  129. FStatus_TxEvent := TEvent.Create(nil, True, False, 'STUSTX_' +
  130. IntToStr(Random(999)));
  131. FUnpluggedEvent := TEvent.Create(nil, False, False, 'UNPLD_' +
  132. IntToStr(Random(999)));
  133. if HidInit = 0 then
  134. HIDEnumerate;
  135. end;
  136. destructor TUSBasp.Destroy;
  137. begin
  138. Disconnect;
  139. FreeAndNil(FUART_RxBuffer);
  140. FreeAndNil(FUART_TxBuffer);
  141. FreeAndNil(FStatus_RxBuffer);
  142. FreeAndNil(FStatus_TxBuffer);
  143. FUART_RxEvent.Free;
  144. FUART_TxEvent.Free;
  145. FStatus_RxEvent.Free;
  146. FStatus_TxEvent.Free;
  147. FUnpluggedEvent.Free;
  148. HidExit();
  149. inherited Destroy;
  150. end;
  151. function TUSBasp.Connect(const ASerialNumber: string = ''): boolean;
  152. begin
  153. FConnected := False;
  154. if not FConnected then
  155. begin
  156. FUART_HIDDevice := THidDevice.OpenPath(FUART_HIDDevicePath);
  157. FUART_HIDDevice^.SetNonBlocking(0);
  158. FUART_RxThread := TThread_HID_UART_Rx.Create(
  159. FUART_HIDDevice, FUART_RxBuffer, FUART_RxEvent, FUnpluggedEvent);
  160. FUART_TxThread := TThread_HID_UART_Tx.Create(
  161. FUART_HIDDevice, FUART_TxBuffer, FUART_TxEvent, FUnpluggedEvent);
  162. if FHasStatus then
  163. begin
  164. FStatus_HIDDevice := THidDevice.OpenPath(FStatus_HIDDevicePath);
  165. FStatus_HIDDevice^.SetNonBlocking(0);
  166. FStatus_RxThread := TThread_HID_Status_Rx.Create(FStatus_HIDDevice,
  167. FStatus_RxBuffer, FStatus_RxEvent, FUnpluggedEvent);
  168. FStatus_TxThread := TThread_HID_Status_Tx.Create(FStatus_HIDDevice,
  169. FStatus_TxBuffer, FStatus_TxEvent, FUnpluggedEvent);
  170. end;
  171. FConnected := True;
  172. end;
  173. Result := FConnected;
  174. end;
  175. function TUSBasp.Disconnect: boolean;
  176. begin
  177. if FConnected then
  178. begin
  179. if FUARTOpened then
  180. UARTClose;
  181. FUART_RxThread.Terminate;
  182. FUART_RxThread.WaitFor;
  183. FreeAndNil(FUART_RxThread);
  184. FUART_TxThread.Terminate;
  185. FUART_TxEvent.SetEvent;
  186. FUART_TxThread.WaitFor;
  187. FreeAndNil(FUART_TxThread);
  188. FUART_HIDDevice^.Close;
  189. FUART_HIDDevice := nil;
  190. if FHasStatus then
  191. begin
  192. FStatus_RxThread.Terminate;
  193. FStatus_RxThread.WaitFor;
  194. FreeAndNil(FStatus_RxThread);
  195. FStatus_TxThread.Terminate;
  196. FStatus_TxEvent.SetEvent;
  197. FStatus_TxThread.WaitFor;
  198. FreeAndNil(FStatus_TxThread);
  199. FStatus_HIDDevice^.Close;
  200. FStatus_HIDDevice := nil;
  201. end;
  202. FConnected := False;
  203. end;
  204. Result := FConnected;
  205. end;
  206. function TUSBasp.UARTOpen(const ABaudRate, ADataBits, AParity,
  207. AStopBits: integer): boolean;
  208. var
  209. Buffer: array[0..8] of byte = (0, 0, 0, 0, 0, 0, 0, 0, 0);
  210. Prescaler: word;
  211. begin
  212. if FConnected and not FUARTOpened then
  213. begin
  214. Prescaler := FCrystalOsc div 8 div ABaudRate - 1;
  215. Buffer[0] := 0;
  216. Buffer[1] := lo(Prescaler);
  217. Buffer[2] := hi(Prescaler);
  218. Buffer[3] := ADataBits or AStopBits or AParity;
  219. if FUART_HIDDevice^.SendFeatureReport(Buffer, 8 + 1) > 0 then
  220. FUARTOpened := True;
  221. end;
  222. Result := FUARTOpened;
  223. end;
  224. function TUSBasp.UARTClose: boolean;
  225. var
  226. Buffer: array[0..8] of byte = (0, 0, 0, 0, 0, 0, 0, 0, 0);
  227. begin
  228. if FConnected and FUARTOpened then
  229. begin
  230. Buffer[0] := 0;
  231. Buffer[1] := 0;
  232. Buffer[2] := 0;
  233. Buffer[3] := 0;
  234. FUART_HIDDevice^.SendFeatureReport(Buffer, 8 + 1);
  235. FUARTOpened := False;
  236. end;
  237. Result := FUARTOpened;
  238. end;
  239. function TUSBasp.HIDEnumerate(const ASerialNumber: string): boolean;
  240. var
  241. HidItemRoot, HidItem: PHidDeviceInfo;
  242. HIDDeviceMatch: boolean;
  243. FeatureReportBuffer: array[0..8] of byte = (0, 0, 0, 0, 0, 0, 0, 0, 0);
  244. begin
  245. Result := False;
  246. FUART_HIDDevicePath := '';
  247. FStatus_HIDDevicePath := '';
  248. FUART_HIDDevice := nil;
  249. FStatus_HIDDevice := nil;
  250. try
  251. HidItemRoot := THidDeviceInfo.Enumerate(USBASP_SHARED_VID, USBASP_SHARED_PID);
  252. HidItem := HidItemRoot;
  253. while Assigned(HidItem) do
  254. begin
  255. HIDDeviceMatch := True;
  256. if ASerialNumber <> '' then
  257. if ASerialNumber <> PCWCharToUnicodeString(HidItem^.SerialNumber) then
  258. HIDDeviceMatch := False;
  259. if HIDDeviceMatch then
  260. case HidItem^.InterfaceNumber of
  261. 1: begin
  262. FUART_HIDDevicePath := HidItem^.Path;
  263. Result := True;
  264. end;
  265. 2: begin
  266. FStatus_HIDDevicePath := HidItem^.Path;
  267. FHasStatus := True;
  268. Break;
  269. end;
  270. end;
  271. HidItem := HidItem^.Next;
  272. end;
  273. finally
  274. HidItemRoot^.Free;
  275. end;
  276. if Result then
  277. begin
  278. FUART_HIDDevice := THidDevice.OpenPath(FUART_HIDDevicePath);
  279. FSerial := FUART_HIDDevice^.GetSerialNumberString;
  280. FManufacturer := FUART_HIDDevice^.GetManufacturerString;
  281. FProductName := FUART_HIDDevice^.GetProductString;
  282. if FUART_HIDDevice^.GetFeatureReport(FeatureReportBuffer, 8 + 1) > 0 then
  283. begin
  284. case FeatureReportBuffer[6] of
  285. USBASP_CAP_12MHZ_CLOCK: FCrystalOsc := 12000000;
  286. USBASP_CAP_16MHZ_CLOCK: FCrystalOsc := 16000000;
  287. USBASP_CAP_18MHZ_CLOCK: FCrystalOsc := 18000000;
  288. USBASP_CAP_20MHZ_CLOCK: FCrystalOsc := 20000000;
  289. end;
  290. FHasHIDUart := (FeatureReportBuffer[5] and USBASP_CAP_7_HID_UART) =
  291. USBASP_CAP_7_HID_UART;
  292. FHasPDI := (FeatureReportBuffer[5] and USBASP_CAP_PDI) = USBASP_CAP_PDI;
  293. FHasTPI := (FeatureReportBuffer[5] and USBASP_CAP_0_TPI) = USBASP_CAP_0_TPI;
  294. FHasSNWrite := (FeatureReportBuffer[5] and USBASP_CAP_2_SNHIDUPDATE) =
  295. USBASP_CAP_2_SNHIDUPDATE;
  296. end;
  297. FUART_HIDDevice^.Close;
  298. FUART_HIDDevice := nil;
  299. end;
  300. end;
  301. function TUSBasp.ChangeSerialNumber(const ASerialNumber: string): integer;
  302. var
  303. Buffer: array[0..8] of byte = (0, 0, 0, 0, 0, 0, 0, 0, 0);
  304. ErrorCode: integer;
  305. SerNumValue: word;
  306. begin
  307. Result := 0;
  308. if FConnected and not FUARTOpened then
  309. begin
  310. Val(ASerialNumber, SerNumValue, ErrorCode);
  311. Buffer[0] := 0;
  312. Buffer[1] := lo(SerNumValue);
  313. Buffer[2] := Hi(SerNumValue);
  314. Buffer[4] := 1;
  315. FUART_HIDDevice^.SendFeatureReport(Buffer, 8 + 1);
  316. end;
  317. end;
  318. end.