| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428 |
- 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;
- FHidErrorStr: string;
- 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
- type
- TUSBPacketBuffer = array[0..8] of byte;
- { 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)));
- HidInit();
- 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
- if not HIDEnumerate(ASerialNumber) then
- Exit;
- FUART_HIDDevice := THidDevice.OpenPath(FUART_HIDDevicePath);
- FHidErrorStr := FUART_HIDDevice^.GetError;
- if FHidErrorStr <> 'Success' then
- Exit;
- 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);
- FHidErrorStr := FStatus_HIDDevice^.GetError;
- if FHidErrorStr = 'Success' then
- begin
- 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;
- 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: TUSBPacketBuffer;
- 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: TUSBPacketBuffer;
- 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: TUSBPacketBuffer;
- 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;
- 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: TUSBPacketBuffer;
- 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.
|