|
|
@@ -0,0 +1,413 @@
|
|
|
+unit USBasp;
|
|
|
+
|
|
|
+{
|
|
|
+
|
|
|
+ This file is part of Nephelae's Object Pascal FPUSBasp.
|
|
|
+
|
|
|
+ USBasp Class.
|
|
|
+
|
|
|
+ Copyright (C) 2022 - 2025 Dimitrios Chr. Ioannidis.
|
|
|
+ Nephelae - https://www.nephelae.eu
|
|
|
+
|
|
|
+ https://www.nephelae.eu/
|
|
|
+
|
|
|
+ Licensed under the MIT License (MIT).
|
|
|
+ See licence file in root directory.
|
|
|
+
|
|
|
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
|
|
+ ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
|
|
+ TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
|
+ PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
|
|
+ SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
|
|
+ ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
|
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
|
+ OTHER DEALINGS IN THE SOFTWARE.
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+{$mode objfpc}{$H+}
|
|
|
+
|
|
|
+interface
|
|
|
+
|
|
|
+uses
|
|
|
+ Classes, SysUtils, syncobjs, hidapi,
|
|
|
+ SPSCRingBuffer, USBasp_Threads;
|
|
|
+
|
|
|
+const
|
|
|
+ USBASP_SHARED_VID = $16C0;
|
|
|
+ USBASP_SHARED_PID = $05DC;
|
|
|
+
|
|
|
+ USBASP_FUNC_GETCAPABILITIES = 127;
|
|
|
+
|
|
|
+ USBASP_CAP_0_TPI = 1;
|
|
|
+ USBASP_CAP_PDI = 16;
|
|
|
+ USBASP_CAP_2_SNHIDUPDATE = 32;
|
|
|
+ USBASP_CAP_6_UART = 64;
|
|
|
+ USBASP_CAP_7_HID_UART = 128;
|
|
|
+
|
|
|
+ USBASP_NO_CAPS = (-4);
|
|
|
+
|
|
|
+ USBASP_CAP_12MHZ_CLOCK = 0;
|
|
|
+ USBASP_CAP_16MHZ_CLOCK = 1;
|
|
|
+ USBASP_CAP_18MHZ_CLOCK = 2;
|
|
|
+ USBASP_CAP_20MHZ_CLOCK = 3;
|
|
|
+
|
|
|
+ PROG_STATE_IDLE = 0;
|
|
|
+ PROG_STATE_WRITEFLASH = 1;
|
|
|
+ PROG_STATE_READFLASH = 2;
|
|
|
+ PROG_STATE_READEEPROM = 3;
|
|
|
+ PROG_STATE_WRITEEEPROM = 4;
|
|
|
+ PROG_STATE_TPI_READ = 5;
|
|
|
+ PROG_STATE_TPI_WRITE = 6;
|
|
|
+ PROG_STATE_SET_REPORT = 7;
|
|
|
+
|
|
|
+ UART_STATE_ENABLED = 16;
|
|
|
+ UART_STATE_DISABLED = 0;
|
|
|
+
|
|
|
+ // USBasp UART Extension
|
|
|
+ // https://github.com/dioannidis/usbasp/blob/master/firmware/usbasp.h
|
|
|
+
|
|
|
+ USBASP_UART_PARITY_MASK = %11;
|
|
|
+ USBASP_UART_PARITY_NONE = %00;
|
|
|
+ USBASP_UART_PARITY_EVEN = %01;
|
|
|
+ USBASP_UART_PARITY_ODD = %10;
|
|
|
+
|
|
|
+ USBASP_UART_STOP_MASK = %100;
|
|
|
+ USBASP_UART_STOP_1BIT = %000;
|
|
|
+ USBASP_UART_STOP_2BIT = %100;
|
|
|
+
|
|
|
+ USBASP_UART_BYTES_MASK = %111000;
|
|
|
+ USBASP_UART_BYTES_5B = %000000;
|
|
|
+ USBASP_UART_BYTES_6B = %001000;
|
|
|
+ USBASP_UART_BYTES_7B = %010000;
|
|
|
+ USBASP_UART_BYTES_8B = %011000;
|
|
|
+ USBASP_UART_BYTES_9B = %100000;
|
|
|
+
|
|
|
+ TUARTBaudRate: array[0..13] of integer =
|
|
|
+ (300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 28800, 31250,
|
|
|
+ 38400, 57600, 74880, 115200);
|
|
|
+
|
|
|
+ TUARTDataBits: array[0..4] of integer =
|
|
|
+ (USBASP_UART_BYTES_5B, USBASP_UART_BYTES_6B, USBASP_UART_BYTES_7B,
|
|
|
+ USBASP_UART_BYTES_8B, USBASP_UART_BYTES_9B);
|
|
|
+
|
|
|
+ TUARTParity: array[0..2] of integer =
|
|
|
+ (USBASP_UART_PARITY_NONE, USBASP_UART_PARITY_EVEN, USBASP_UART_PARITY_ODD);
|
|
|
+
|
|
|
+ TUARTStopBit: array[0..1] of integer = (USBASP_UART_STOP_1BIT, USBASP_UART_STOP_2BIT);
|
|
|
+
|
|
|
+type
|
|
|
+
|
|
|
+ { TUSBasp }
|
|
|
+
|
|
|
+ TUSBasp = class(TObject)
|
|
|
+ private
|
|
|
+ FUART_HIDDevice, FStatus_HIDDevice: PHidDevice;
|
|
|
+
|
|
|
+ FUART_HIDDevicePath, FStatus_HIDDevicePath, FSerial, FManufacturer,
|
|
|
+ FProductName, FHidApiVersionString: string;
|
|
|
+
|
|
|
+ FConnected, FHasHIDUart, FHasStatus, FHasPDI, FHasTPI, FHasSNWrite,
|
|
|
+ FUARTOpened: boolean;
|
|
|
+ FCrystalOSC: integer;
|
|
|
+
|
|
|
+ FStatus_RxEvent, FStatus_TxEvent, FUART_RxEvent, FUART_TxEvent,
|
|
|
+ FUnpluggedEvent: TEvent;
|
|
|
+
|
|
|
+ //FLastUsbError: integer;
|
|
|
+
|
|
|
+ FUART_RxBuffer, FUART_TxBuffer, FStatus_RxBuffer,
|
|
|
+ FStatus_TxBuffer: TSPSCRingBuffer;
|
|
|
+
|
|
|
+ FUART_RxThread: TThread_HID_UART_Rx;
|
|
|
+ FUART_TxThread: TThread_HID_UART_Tx;
|
|
|
+ FStatus_RxThread: TThread_HID_Status_Rx;
|
|
|
+ FStatus_TxThread: TThread_HID_Status_Tx;
|
|
|
+
|
|
|
+ function HIDEnumerate(const ASerialNumber: string = ''): boolean;
|
|
|
+ protected
|
|
|
+ property ReceiveBuffer: TSPSCRingBuffer read FUART_RxBuffer;
|
|
|
+ property ReceiveEvent: TEvent read FUART_RxEvent;
|
|
|
+ property TransmitBuffer: TSPSCRingBuffer read FUART_TxBuffer;
|
|
|
+ property TransmitEvent: TEvent read FUART_TxEvent;
|
|
|
+ property MonitorBuffer: TSPSCRingBuffer read FStatus_RxBuffer;
|
|
|
+ property MonitorEvent: TEvent read FStatus_RxEvent;
|
|
|
+ property DisconnectEvent: TEvent read FUnpluggedEvent;
|
|
|
+ public
|
|
|
+ constructor Create;
|
|
|
+ destructor Destroy; override;
|
|
|
+
|
|
|
+ function Connect(const ASerialNumber: string = ''): boolean;
|
|
|
+ function Disconnect: boolean;
|
|
|
+ function UARTOpen(const ABaudRate, ADataBits, AParity, AStopBits: integer): boolean;
|
|
|
+ function UARTClose: boolean;
|
|
|
+ function ChangeSerialNumber(const ASerialNumber: string): integer;
|
|
|
+
|
|
|
+ property Connected: boolean read FConnected;
|
|
|
+ property UARTOpened: boolean read FUARTOpened;
|
|
|
+ property HidApiVersion: string read FHidApiVersionString;
|
|
|
+ end;
|
|
|
+
|
|
|
+implementation
|
|
|
+
|
|
|
+{ TUSBasp }
|
|
|
+
|
|
|
+constructor TUSBasp.Create;
|
|
|
+begin
|
|
|
+ FConnected := False;
|
|
|
+ FUARTOpened := False;
|
|
|
+ FSerial := '';
|
|
|
+
|
|
|
+ FUART_RxBuffer := TSPSCRingBuffer.Create(1024);
|
|
|
+ FUART_TxBuffer := TSPSCRingBuffer.Create(1024);
|
|
|
+ FStatus_RxBuffer := TSPSCRingBuffer.Create(1024);
|
|
|
+ FStatus_TxBuffer := TSPSCRingBuffer.Create(1024);
|
|
|
+
|
|
|
+ Randomize;
|
|
|
+ FUART_RxEvent := TEvent.Create(nil, False, False, 'UARTRX_' + IntToStr(Random(999)));
|
|
|
+ FUART_TxEvent := TEvent.Create(nil, True, False, 'UARTTX_' + IntToStr(Random(999)));
|
|
|
+
|
|
|
+ FStatus_RxEvent := TEvent.Create(nil, False, False, 'STUSRX_' +
|
|
|
+ IntToStr(Random(999)));
|
|
|
+ FStatus_TxEvent := TEvent.Create(nil, True, False, 'STUSTX_' +
|
|
|
+ IntToStr(Random(999)));
|
|
|
+
|
|
|
+ FUnpluggedEvent := TEvent.Create(nil, False, False, 'UNPLD_' +
|
|
|
+ IntToStr(Random(999)));
|
|
|
+
|
|
|
+ if HidInit = 0 then
|
|
|
+ HIDEnumerate;
|
|
|
+
|
|
|
+end;
|
|
|
+
|
|
|
+destructor TUSBasp.Destroy;
|
|
|
+begin
|
|
|
+ Disconnect;
|
|
|
+
|
|
|
+ FreeAndNil(FUART_RxBuffer);
|
|
|
+ FreeAndNil(FUART_TxBuffer);
|
|
|
+ FreeAndNil(FStatus_RxBuffer);
|
|
|
+ FreeAndNil(FStatus_TxBuffer);
|
|
|
+
|
|
|
+ FUART_RxEvent.Free;
|
|
|
+ FUART_TxEvent.Free;
|
|
|
+ FStatus_RxEvent.Free;
|
|
|
+ FStatus_TxEvent.Free;
|
|
|
+ FUnpluggedEvent.Free;
|
|
|
+
|
|
|
+ HidExit();
|
|
|
+
|
|
|
+ inherited Destroy;
|
|
|
+end;
|
|
|
+
|
|
|
+function TUSBasp.Connect(const ASerialNumber: string = ''): boolean;
|
|
|
+begin
|
|
|
+ FConnected := False;
|
|
|
+ if not FConnected then
|
|
|
+ begin
|
|
|
+
|
|
|
+ FUART_HIDDevice := THidDevice.OpenPath(FUART_HIDDevicePath);
|
|
|
+ FUART_HIDDevice^.SetNonBlocking(0);
|
|
|
+
|
|
|
+ FUART_RxThread := TThread_HID_UART_Rx.Create(
|
|
|
+ FUART_HIDDevice, FUART_RxBuffer, FUART_RxEvent, FUnpluggedEvent);
|
|
|
+ FUART_TxThread := TThread_HID_UART_Tx.Create(
|
|
|
+ FUART_HIDDevice, FUART_TxBuffer, FUART_TxEvent, FUnpluggedEvent);
|
|
|
+
|
|
|
+ if FHasStatus then
|
|
|
+ begin
|
|
|
+ FStatus_HIDDevice := THidDevice.OpenPath(FStatus_HIDDevicePath);
|
|
|
+ FStatus_HIDDevice^.SetNonBlocking(0);
|
|
|
+
|
|
|
+ FStatus_RxThread := TThread_HID_Status_Rx.Create(FStatus_HIDDevice,
|
|
|
+ FStatus_RxBuffer, FStatus_RxEvent, FUnpluggedEvent);
|
|
|
+ FStatus_TxThread := TThread_HID_Status_Tx.Create(FStatus_HIDDevice,
|
|
|
+ FStatus_TxBuffer, FStatus_TxEvent, FUnpluggedEvent);
|
|
|
+ end;
|
|
|
+
|
|
|
+ FConnected := True;
|
|
|
+ end;
|
|
|
+ Result := FConnected;
|
|
|
+end;
|
|
|
+
|
|
|
+function TUSBasp.Disconnect: boolean;
|
|
|
+begin
|
|
|
+ if FConnected then
|
|
|
+ begin
|
|
|
+ if FUARTOpened then
|
|
|
+ UARTClose;
|
|
|
+
|
|
|
+ FUART_RxThread.Terminate;
|
|
|
+ FUART_RxThread.WaitFor;
|
|
|
+ FreeAndNil(FUART_RxThread);
|
|
|
+
|
|
|
+ FUART_TxThread.Terminate;
|
|
|
+ FUART_TxEvent.SetEvent;
|
|
|
+ FUART_TxThread.WaitFor;
|
|
|
+ FreeAndNil(FUART_TxThread);
|
|
|
+
|
|
|
+ FUART_HIDDevice^.Close;
|
|
|
+ FUART_HIDDevice := nil;
|
|
|
+
|
|
|
+ if FHasStatus then
|
|
|
+ begin
|
|
|
+ FStatus_RxThread.Terminate;
|
|
|
+ FStatus_RxThread.WaitFor;
|
|
|
+ FreeAndNil(FStatus_RxThread);
|
|
|
+
|
|
|
+ FStatus_TxThread.Terminate;
|
|
|
+ FStatus_TxEvent.SetEvent;
|
|
|
+ FStatus_TxThread.WaitFor;
|
|
|
+ FreeAndNil(FStatus_TxThread);
|
|
|
+
|
|
|
+ FStatus_HIDDevice^.Close;
|
|
|
+ FStatus_HIDDevice := nil;
|
|
|
+ end;
|
|
|
+
|
|
|
+ FConnected := False;
|
|
|
+ end;
|
|
|
+ Result := FConnected;
|
|
|
+end;
|
|
|
+
|
|
|
+function TUSBasp.UARTOpen(const ABaudRate, ADataBits, AParity,
|
|
|
+ AStopBits: integer): boolean;
|
|
|
+var
|
|
|
+ Buffer: array[0..8] of byte = (0, 0, 0, 0, 0, 0, 0, 0, 0);
|
|
|
+ Prescaler: word;
|
|
|
+begin
|
|
|
+ if FConnected and not FUARTOpened then
|
|
|
+ begin
|
|
|
+ Prescaler := FCrystalOsc div 8 div ABaudRate - 1;
|
|
|
+
|
|
|
+ Buffer[0] := 0;
|
|
|
+ Buffer[1] := lo(Prescaler);
|
|
|
+ Buffer[2] := hi(Prescaler);
|
|
|
+ Buffer[3] := ADataBits or AStopBits or AParity;
|
|
|
+
|
|
|
+ if FUART_HIDDevice^.SendFeatureReport(Buffer, 8 + 1) > 0 then
|
|
|
+ FUARTOpened := True;
|
|
|
+ end;
|
|
|
+
|
|
|
+ Result := FUARTOpened;
|
|
|
+end;
|
|
|
+
|
|
|
+function TUSBasp.UARTClose: boolean;
|
|
|
+var
|
|
|
+ Buffer: array[0..8] of byte = (0, 0, 0, 0, 0, 0, 0, 0, 0);
|
|
|
+begin
|
|
|
+ if FConnected and FUARTOpened then
|
|
|
+ begin
|
|
|
+
|
|
|
+ Buffer[0] := 0;
|
|
|
+ Buffer[1] := 0;
|
|
|
+ Buffer[2] := 0;
|
|
|
+ Buffer[3] := 0;
|
|
|
+
|
|
|
+ FUART_HIDDevice^.SendFeatureReport(Buffer, 8 + 1);
|
|
|
+
|
|
|
+ FUARTOpened := False;
|
|
|
+ end;
|
|
|
+
|
|
|
+ Result := FUARTOpened;
|
|
|
+end;
|
|
|
+
|
|
|
+function TUSBasp.HIDEnumerate(const ASerialNumber: string): boolean;
|
|
|
+var
|
|
|
+ HidItemRoot, HidItem: PHidDeviceInfo;
|
|
|
+ HIDDeviceMatch: boolean;
|
|
|
+ FeatureReportBuffer: array[0..8] of byte = (0, 0, 0, 0, 0, 0, 0, 0, 0);
|
|
|
+begin
|
|
|
+ Result := False;
|
|
|
+
|
|
|
+ FUART_HIDDevicePath := '';
|
|
|
+ FStatus_HIDDevicePath := '';
|
|
|
+
|
|
|
+ FUART_HIDDevice := nil;
|
|
|
+ FStatus_HIDDevice := nil;
|
|
|
+
|
|
|
+ try
|
|
|
+ HidItemRoot := THidDeviceInfo.Enumerate(USBASP_SHARED_VID, USBASP_SHARED_PID);
|
|
|
+ HidItem := HidItemRoot;
|
|
|
+
|
|
|
+ while Assigned(HidItem) do
|
|
|
+ begin
|
|
|
+ HIDDeviceMatch := True;
|
|
|
+
|
|
|
+ if ASerialNumber <> '' then
|
|
|
+ if ASerialNumber <> PCWCharToUnicodeString(HidItem^.SerialNumber) then
|
|
|
+ HIDDeviceMatch := False;
|
|
|
+
|
|
|
+ if HIDDeviceMatch then
|
|
|
+ case HidItem^.InterfaceNumber of
|
|
|
+ 1: begin
|
|
|
+ FUART_HIDDevicePath := HidItem^.Path;
|
|
|
+ Result := True;
|
|
|
+ end;
|
|
|
+ 2: begin
|
|
|
+ FStatus_HIDDevicePath := HidItem^.Path;
|
|
|
+ FHasStatus := True;
|
|
|
+ Break;
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+
|
|
|
+ HidItem := HidItem^.Next;
|
|
|
+ end;
|
|
|
+ finally
|
|
|
+ HidItemRoot^.Free;
|
|
|
+ end;
|
|
|
+
|
|
|
+ if Result then
|
|
|
+ begin
|
|
|
+ FUART_HIDDevice := THidDevice.OpenPath(FUART_HIDDevicePath);
|
|
|
+
|
|
|
+ FSerial := FUART_HIDDevice^.GetSerialNumberString;
|
|
|
+ FManufacturer := FUART_HIDDevice^.GetManufacturerString;
|
|
|
+ FProductName := FUART_HIDDevice^.GetProductString;
|
|
|
+
|
|
|
+ if FUART_HIDDevice^.GetFeatureReport(FeatureReportBuffer, 8 + 1) > 0 then
|
|
|
+ begin
|
|
|
+
|
|
|
+ case FeatureReportBuffer[6] of
|
|
|
+ USBASP_CAP_12MHZ_CLOCK: FCrystalOsc := 12000000;
|
|
|
+ USBASP_CAP_16MHZ_CLOCK: FCrystalOsc := 16000000;
|
|
|
+ USBASP_CAP_18MHZ_CLOCK: FCrystalOsc := 18000000;
|
|
|
+ USBASP_CAP_20MHZ_CLOCK: FCrystalOsc := 20000000;
|
|
|
+ end;
|
|
|
+
|
|
|
+ FHasHIDUart := (FeatureReportBuffer[5] and USBASP_CAP_7_HID_UART) =
|
|
|
+ USBASP_CAP_7_HID_UART;
|
|
|
+ FHasPDI := (FeatureReportBuffer[5] and USBASP_CAP_PDI) = USBASP_CAP_PDI;
|
|
|
+ FHasTPI := (FeatureReportBuffer[5] and USBASP_CAP_0_TPI) = USBASP_CAP_0_TPI;
|
|
|
+ FHasSNWrite := (FeatureReportBuffer[5] and USBASP_CAP_2_SNHIDUPDATE) =
|
|
|
+ USBASP_CAP_2_SNHIDUPDATE;
|
|
|
+
|
|
|
+ end;
|
|
|
+
|
|
|
+ FUART_HIDDevice^.Close;
|
|
|
+ FUART_HIDDevice := nil;
|
|
|
+ end;
|
|
|
+end;
|
|
|
+
|
|
|
+function TUSBasp.ChangeSerialNumber(const ASerialNumber: string): integer;
|
|
|
+var
|
|
|
+ Buffer: array[0..8] of byte = (0, 0, 0, 0, 0, 0, 0, 0, 0);
|
|
|
+ ErrorCode: integer;
|
|
|
+ SerNumValue: word;
|
|
|
+begin
|
|
|
+ Result := 0;
|
|
|
+ if FConnected and not FUARTOpened then
|
|
|
+ begin
|
|
|
+ Val(ASerialNumber, SerNumValue, ErrorCode);
|
|
|
+
|
|
|
+ Buffer[0] := 0;
|
|
|
+
|
|
|
+ Buffer[1] := lo(SerNumValue);
|
|
|
+ Buffer[2] := Hi(SerNumValue);
|
|
|
+ Buffer[4] := 1;
|
|
|
+
|
|
|
+ FUART_HIDDevice^.SendFeatureReport(Buffer, 8 + 1);
|
|
|
+ end;
|
|
|
+end;
|
|
|
+
|
|
|
+end.
|