This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

LMX1205: LMX1205: How to programmatically control via USB2ANY API (DLL or .NET library)?

Part Number: LMX1205
Other Parts Discussed in Thread: USB2ANY, LMX2594

Tool/software:

Hello TI team,

I’m currently using LMX1205 with TICS Pro and a USB2ANY adapter. Using the GUI works fine, but I need to programmatically control the LMX1205 registers (read/write) from my own software.

Here’s what I’m trying to achieve:

  • Send SPI frames (3-byte register writes like 0x04 0x00 0x2D) directly to the LMX1205 via the USB2ANY adapter.

  • Do this without using the TICS Pro GUI, but from a C#/.NET application (or Python if easier).

  • Ideally, call a function like Write_Block() or similar that directly sends the SPI command.

I have already installed:

  • Texas Instruments USB Serial Adapter API & Tools

  • I see TIDP.SAA.dll and TIDP.SAA2.dll in C:\Program Files (x86)\Texas Instruments USB Serial Adapter API\Bin

  • I also see USB2GPIOv2.dll which I assume is the low-level driver

I tried to reference TIDP.SAA.dll in a .NET 8.0 console app but the compiler could not find the TIUsbAdapters class (even though it’s mentioned in the API documentation).

So my questions are:

  1. Which DLL is the correct one for programmatic access to USB2ANY in C#/.NET?

    • Is it TIDP.SAA.dll or TIDP.SAA2.dll?

    • Does it include TIUsbAdapters, Write_Block(), etc.?

  2. Can you provide a minimal example (C#, C++, or Python) to:

    • Discover a USB2ANY adapter

    • Open it

    • Send a simple SPI write to the LMX1205 (e.g. write to R4 = 0x002D)

  3. If no official .NET API is available, is there a C API (header + DLL) we can call with DllImport to perform SPI writes?

  4. Is there any existing sample code for controlling LMX devices over USB2ANY programmatically (outside of TICS Pro)?

Ultimately, my goal is to write/read LMX1205 registers over SPI in my own automated software without using the TICS GUI.

Any guidance, documentation, or sample code would be greatly appreciated!

Thanks in advance.

  • I've documented this to some extent with python and the USB2ANY DLL provided with TICS Pro in this thread:  LMX2594EVM: Serial control through python

    You'll also want to refer to the SDK versions.

    SDK version 2.7.0.0: https://e2e.ti.com/support/amplifiers-group/amplifiers/f/amplifiers-forum/799340/usb2any-usb2any-use-usb2any-dll-in-own-software-gui-project/2957499#2957499

    SDK version 2.8.2.0: https://e2e.ti.com/support/sensors-group/sensors/f/sensors-forum/964057/tmp117evm-usb2any-sdk-required-to-safely-work-with-the-module/3562141#3562141

    The SDK includes an API manual that documents all functions in USB2ANY.dll, along with all the exported symbols and calling conventions, enums/typedefs for API arguments, and and a few code samples (though I didn't find the samples terribly useful for .NET users, since the projects only assume usage of binary release objects and the maintenance between .NET and C++ versions of the libraries diverged greatly over time).

    For your questions, line by line:

    1. The correct DLL is usually called USB2ANY.dll, but it may be renamed in the application. There's a separate tool called the USB2GPIO (and, confusingly, a v2 which is different hardware entirely) which are an earlier unrelated attempt to solve a similar problem. The files you referred to are not what you're looking for.
    2. See the thread above, a minimal python example is provided for LMX2594 (not LMX1205, but basically the same principles).
    3. I don't have an official .NET API available yet, but I can share a short snippet that shows how I handle the dll imports below. This is the code currently used in TICS Pro 2, which targets .NET 8; the code for TICS Pro 1.x is .NET Framework and it's virtually identical, just not necessarily following modern best practices - I could provide that on request, but it wouldn't provide you with too many surprises. Note that TICS Pro 2 is a 64-bit binary, and TICS Pro 1 is a 32-bit binary; I wrote the TICS Pro 2 wrapper code in such a way that it can load either the 32-bit or 64-bit DLL as needed based on the environment at a time when I thought the architecture might be 32-bit, and left it in after as a useful instructive example. I also included the USB2ANY-specific enums, and the generic reused enums across different interfaces.
    4. Again, see the thread above, a minimal python example is provided for LMX2594 (not LMX1205, but basically the same principles). I think you could also use the python code in the example above, combined with the wrapper code below, to arrive at a similar implementation in .NET.

    using System.Runtime.InteropServices;
    using System.Text;
    
    /* These Enums are available in USBIO namespace and are used across multiple interface types.
     * Enables, polarities, and endianness flips show up in other USB interfaces, or follow 
     * common conventions across SPI/I2C/etc.
    
    namespace USBIO
    {
        public enum IO_Enable
        {
            Disabled = 0,
            Enabled = 1,
            Ignored = 2,
        }
        
        public enum IO_Polarity
        {
            Active_High = 0,
            Active_Low = 1,
        }
        
        public enum IO_Endianness
        {
            LSB_First = 0,
            MSB_First = 1,
        }
    }
    */
    
    
    namespace USBIO.USB2ANY
    {
        #region USB2ANY-specific enums
        internal enum ErrorCode
        {
            ERR_OK = 0,
            ERR_COM_RX_OVERFLOW = -1,
            ERR_COM_RX_BUF_EMPTY = -2,
            ERR_COM_TX_BUF_FULL = -3,
            ERR_COM_TX_STALLED = -4,
            ERR_COM_TX_FAILED = -5,
            ERR_COM_OPEN_FAILED = -6,
            ERR_COM_PORT_NOT_OPEN = -7,
            ERR_COM_PORT_IS_OPEN = -8,
            ERR_COM_READ_TIMEOUT = -9,
            ERR_COM_READ_ERROR = -10,
            ERR_COM_WRITE_ERROR = -11,
            ERR_DEVICE_NOT_FOUND = -12,
    
            ERR_INVALID_PORT = -20,
            ERR_ADDRESS_OUT_OF_RANGE = -21,
            ERR_INVALID_FUNCTION_CODE = -22,
            ERR_BAD_PACKET_SIZE = -23,
            ERR_INVALID_HANDLE = -24,
            ERR_OPERATION_FAILED = -25,
            ERR_PARAM_OUT_OF_RANGE = -26,
            ERR_PACKET_OUT_OF_SEQUENCE = -27,
            ERR_INVALID_PACKET_HEADER = -28,
            ERR_UNIMPLEMENTED_FUNCTION = -29,
            ERR_TOO_MUCH_DATA = -30,
            ERR_INVALID_DEVICE = -31,
            ERR_UNSUPPORTED_FIRMWARE = -32,
    
            ERR_I2C_INIT_ERROR = -40,
            ERR_I2C_READ_ERROR = -41,
            ERR_I2C_WRITE_ERROR = -42,
            ERR_I2C_BUSY = -43,
            ERR_I2C_ADDR_NAK = -44,
            ERR_I2C_DATA_NAK = -45,
            ERR_I2C_READ_TIMEOUT = -46,
            ERR_I2C_READ_DATA_TIMEOUT = -47,
            ERR_I2C_READ_COMP_TIMEOUT = -48,
            ERR_I2C_WRITE_TIMEOUT = -49,
            ERR_I2C_WRITE_DATA_TIMEOUT = -50,
            ERR_I2C_WRITE_COMP_TIMEOUT = -51,
            ERR_I2C_NOT_MASTER = -52,
            ERR_I2C_ARBITRATION_LOST = -53,
            ERR_I2C_NO_PULLUP_POWER = -54,
    
            ERR_SPI_INIT_ERROR = -60,
            ERR_SPI_WRITE_READ_ERROR = -61,
        }
    
        internal enum SMBus_Flags
        {
            SMBus_PEC_Default = 0,
            SMBus_PEC_OFF = 1,
            SMBus_PEC_CRC8 = 2
        }
    
        internal enum LED
        {
            LED_OFF = 0,
            LED_ON = 1,
            LED_TOGGLE = 2
        }
    
        internal enum ADC_PinFunction
        {
            ADC_No_Change = 0,
            ADC_Analog_In = 1,
        }
    
        internal enum ADC_VREF
        {
            ADC_VREF_1V5 = 0,
            ADC_VREF_2V5 = 1,
            ADC_VREF_3V3 = 2,
            ADC_VREF_EXTERNAL = 3,
        }
        
        internal enum GPIO_OutPinState
        {
            GPIO_Out_No_Change = 0,
            GPIO_Out_Low = 1,
            GPIO_Out_High = 2,
        }
    
        internal enum GPIO_PinFunction
        {
            GPIO_No_Change = 0,
            GPIO_Output = 1,
            GPIO_Input_No_Resistor = 2,
            GPIO_Input_Pull_Up = 3,
            GPIO_Input_Pull_Down = 4
        }
        
        internal enum SPI_ClockPhase
        {
            SPI_Capture_On_Trailing_Edge = 0,
            SPI_Capture_On_Leading_Edge = 1,
        }
    
        internal enum SPI_CharacterLength
        {
            SPI_8_Bit = 0,
            SPI_7_Bit = 1,
        }
    
        internal enum SPI_CSType
        {
            SPI_With_Every_Byte = 0,
            SPI_With_Every_Packet = 1,
            SPI_With_Every_Word = 2,
            SPI_No_CS = 3,
            SPI_Every_Three = 6,
            SPI_Every_Four = 5,
            SPI_Pulse_After_Packet = 255
        }
    
        internal enum SPI_SlaveSelect
        {
            SPI_GPIO0 = 0,
            SPI_GPIO1 = 1,
            SPI_GPIO3 = 3,
            SPI_GPIO6 = 6,
            SPI_GPIO7 = 7,
            SPI_GPIO8 = 8,
            SPI_GPIO9 = 9,
            SPI_GPIO10 = 10,
            SPI_GPIO11 = 11,
            SPI_GPIO12 = 12,
            SPI_Default = SPI_GPIO6,
        }
        
        internal enum I2C_ClockRate
        {
            I2C_100kHz = 0,
            I2C_400kHz = 1,
            I2C_10kHz = 2
        }
    
        internal enum I2C_BusAddressLength
        {
            I2C_7Bits = 0,
            I2C_10Bits = 1
        }
    
        #endregion USB2ANY-specific enums
        
        internal static partial class U2A32
        {
            const string dllPath = "USB2ANY_x86.dll";
    
            #region USB2ANY_x86.dll external definitions
            // Utility Functions
            [DllImport(dllPath, EntryPoint = "_u2aGetSerialNumber@8", CharSet = CharSet.Unicode)]
            internal static extern int u2aGetSerialNumber(int index, StringBuilder SerialNumber);
            [LibraryImport(dllPath, EntryPoint = "_u2aFindControllers@0")]
            internal static partial int u2aFindControllers();
            [LibraryImport(dllPath, EntryPoint = "_u2aOpen@4", StringMarshalling = StringMarshalling.Utf8)]
            internal static partial int u2aOpen(string SerialNumber);
            [LibraryImport(dllPath, EntryPoint = "_u2aClose@4")]
            internal static partial int u2aClose(int handle);
            [LibraryImport(dllPath, EntryPoint = "_u2aLED_WriteControl@8")]
            internal static partial int u2aLED_WriteControl(int handle, LED LEDState);
            [LibraryImport(dllPath, EntryPoint = "_u2aPower_Enable@16")]
            internal static partial int u2aPower_Enable(int handle, IO_Enable power_3V3, IO_Enable power_5V0, IO_Enable power_Adj);
            [DllImport(dllPath, EntryPoint = "_u2aStatus_GetText@12", CharSet = CharSet.Unicode)]
            internal static extern IntPtr u2aStatus_GetText(int code, StringBuilder buffer, ushort length);
    
            // ADC
            [LibraryImport(dllPath, EntryPoint = "_u2aADC_Control@24")]
            internal static partial int u2aADC_Control(int handle, ADC_PinFunction ADC0, ADC_PinFunction ADC1, ADC_PinFunction ADC2, ADC_PinFunction ADC3, ADC_VREF VREF);
            [LibraryImport(dllPath, EntryPoint = "_u2aADC_ConvertAndRead@12")]
            internal static partial int u2aADC_ConvertAndRead(int handle, byte nBytes, [Out] byte[] data);
            [LibraryImport(dllPath, EntryPoint = "_u2aADC_Enable@12")]
            internal static partial int u2aADC_Enable(int handle, int nChannel, int nMode);
            [LibraryImport(dllPath, EntryPoint = "_u2aADC_SetReference@8")]
            internal static partial int u2aADC_SetReference(int handle, ADC_VREF VREF);
            [LibraryImport(dllPath, EntryPoint = "_u2aADC_Acquire@12")]
            internal static partial int u2aADC_Acquire(int handle, int nInterval, int nSamples);
            [LibraryImport(dllPath, EntryPoint = "_u2aADC_AcquireTriggered@16")]
            internal static partial int u2aADC_AcquireTriggered(int handle, int nInterval, int nSamples, int nTrigger);
            [LibraryImport(dllPath, EntryPoint = "_u2aADC_GetData@16")]
            internal static partial int u2aADC_GetData(int handle, ushort nOffset, ushort nDataPoints, [Out] ushort[] buffer);
            [LibraryImport(dllPath, EntryPoint = "_u2aADC_GetStatus@8")]
            internal static partial int u2aADC_GetStatus(int handle, [Out] byte[] buffer);
    
            // GPIO
            [LibraryImport(dllPath, EntryPoint = "_u2aGPIO_ReadPort@8")]
            internal static partial int u2aGPIO_ReadPort(int handle, byte GPIO_Port);
            [LibraryImport(dllPath, EntryPoint = "_u2aGPIO_ReadState@12")]
            internal static partial int u2aGPIO_ReadState(int handle, byte nBytes, [Out] byte[] data);
            [LibraryImport(dllPath, EntryPoint = "_u2aGPIO_WritePort@12")]
            internal static partial int u2aGPIO_WritePort(int handle, byte GPIO_Port, byte state);
            [LibraryImport(dllPath, EntryPoint = "_u2aGPIO_WriteState@56")]
            internal static partial int u2aGPIO_WriteState(int handle, GPIO_OutPinState GPIO0, GPIO_OutPinState GPIO1, GPIO_OutPinState GPIO2, GPIO_OutPinState GPIO3, GPIO_OutPinState GPIO4, GPIO_OutPinState GPIO5, GPIO_OutPinState GPIO6, GPIO_OutPinState GPIO7, GPIO_OutPinState GPIO8, GPIO_OutPinState GPIO9, GPIO_OutPinState GPIO10, GPIO_OutPinState GPIO11, GPIO_OutPinState GPIO12);
            [LibraryImport(dllPath, EntryPoint = "_u2aGPIO_WriteControl@56")]
            internal static partial int u2aGPIO_WriteControl(int handle, GPIO_PinFunction GPIO0, GPIO_PinFunction GPIO1, GPIO_PinFunction GPIO2, GPIO_PinFunction GPIO3, GPIO_PinFunction GPIO4, GPIO_PinFunction GPIO5, GPIO_PinFunction GPIO6, GPIO_PinFunction GPIO7, GPIO_PinFunction GPIO8, GPIO_PinFunction GPIO9, GPIO_PinFunction GPIO10, GPIO_PinFunction GPIO11, GPIO_PinFunction GPIO12);
            [LibraryImport(dllPath, EntryPoint = "_u2aGPIO_SetPort@12")]
            internal static partial int u2aGPIO_SetPort(int handle, byte GPIO_Port, GPIO_PinFunction func);
    
            // I2C
            [LibraryImport(dllPath, EntryPoint = "_u2aI2C_Control@16")]
            internal static partial int u2aI2C_Control(int handle, I2C_ClockRate Speed, I2C_BusAddressLength AddressLength, IO_Enable PullUps);
            [LibraryImport(dllPath, EntryPoint = "_u2aI2C_RawWrite@16")]
            internal static partial int u2aI2C_RawWrite(int handle, ushort I2C_Address, byte nBytes, [Out] byte[] Data);
            [LibraryImport(dllPath, EntryPoint = "_u2aI2C_RawRead@16")]
            internal static partial int u2aI2C_RawRead(int handle, ushort I2C_Address, byte nBytes, [Out] byte[] Data);
            [LibraryImport(dllPath, EntryPoint = "_u2aI2C_BlockWriteBlockRead@24")]
            internal static partial int u2aI2C_BlockWriteBlockRead(int handle, ushort I2C_Address, byte nWriteBytes, [Out] byte[] WriteData, byte nReadBytes, [Out] byte[] ReadData);
            [LibraryImport(dllPath, EntryPoint = "_u2aI2C_InternalRead@24")]
            internal static partial int u2aI2C_InternalRead(int handle, ushort I2C_Address, ushort InternalAddress, byte IntAddrSize, ushort nBytes, [Out] byte[] Data);
    
            // SPI
            [LibraryImport(dllPath, EntryPoint = "_u2aSPI_Control@36")]
            internal static partial int u2aSPI_Control(int handle, SPI_ClockPhase _SPI_ClockPhase, IO_Polarity _IO_Polarity, IO_Endianness _IO_Endianness, SPI_CharacterLength _SPI_CharacterLength, SPI_CSType _SPI_LatchType, IO_Polarity _SPI_LatchPolarity, int _DividerHigh, int _DividerLow);
            [LibraryImport(dllPath, EntryPoint = "_u2aSPI_WriteAndRead@12")]
            internal static partial int u2aSPI_WriteAndRead(int handle, byte nBytes, [Out] byte[] Data);
            [LibraryImport(dllPath, EntryPoint = "_u2aSPI_WriteAndReadEx@16")]
            internal static partial int u2aSPI_WriteAndReadEx(int handle, SPI_SlaveSelect _SPI_SlaveSelect, int nBytes, [Out] byte[] Data);
    
            //SMBusControl
            [LibraryImport(dllPath, EntryPoint = "_u2aSMBUS_Control@8")]
            internal static partial int u2aSMBUS_Control(int handle, SMBus_Flags SMBUSFlags);
            //SMBusWriteByte
            [LibraryImport(dllPath, EntryPoint = "_u2aSMBUS_WriteByte@20")]
            internal static partial int u2aSMBUS_WriteByte(int handle, ushort I2CSlaveAddress, byte CommandCode, byte data, SMBus_Flags SMBUSFlags);
            //SMBusReadByte
            [LibraryImport(dllPath, EntryPoint = "_u2aSMBUS_ReadByte@16")]
            internal static partial int u2aSMBUS_ReadByte(int handle, ushort I2CSlaveAddress, byte CommandCode, SMBus_Flags SMBUSFlags);
            //SMBusWriteBlock
            [LibraryImport(dllPath, EntryPoint = "_u2aSMBUS_WriteBlock@24")]
            internal static partial int u2aSMBUS_WriteBlock(int handle, ushort I2CSlaveAddress, byte CommandCode, byte nBytes, [Out] byte[] Data, SMBus_Flags SMBUSFlags);
            //SMBusReadBlock
            [LibraryImport(dllPath, EntryPoint = "_u2aSMBUS_ReadBlock@24")]
            internal static partial int u2aSMBUS_ReadBlock(int handle, ushort I2CSlaveAddress, byte CommandCode, byte nBytes, [Out] byte[] Data, SMBus_Flags SMBUSFlags);
            #endregion USB2ANY_x86.dll external definitions
        }
    
        internal static partial class U2A64
        {
            const string dllPath = "USB2ANY_x64.dll";
    
            #region USB2ANY_x64.dll external definitions
            // Utility Functions
            [DllImport(dllPath, EntryPoint = "u2aGetSerialNumber", CharSet = CharSet.Unicode)]
            internal static extern int u2aGetSerialNumber(int index, StringBuilder SerialNumber);
            [LibraryImport(dllPath, EntryPoint = "u2aFindControllers")]
            internal static partial int u2aFindControllers();
            [LibraryImport(dllPath, EntryPoint = "u2aOpen", StringMarshalling = StringMarshalling.Utf8)]
            internal static partial int u2aOpen(string SerialNumber);
            [LibraryImport(dllPath, EntryPoint = "u2aClose")]
            internal static partial int u2aClose(int handle);
            [LibraryImport(dllPath, EntryPoint = "u2aLED_WriteControl")]
            internal static partial int u2aLED_WriteControl(int handle, LED LEDState);
            [LibraryImport(dllPath, EntryPoint = "u2aPower_Enable")]
            internal static partial int u2aPower_Enable(int handle, IO_Enable power_3V3, IO_Enable power_5V0, IO_Enable power_Adj);
            [DllImport(dllPath, EntryPoint = "_u2aStatus_GetText", CharSet = CharSet.Unicode)]
            internal static extern IntPtr u2aStatus_GetText(int code, StringBuilder buffer, ushort length);
    
            // ADC
            [LibraryImport(dllPath, EntryPoint = "_u2aADC_Control")]
            internal static partial int u2aADC_Control(int handle, ADC_PinFunction ADC0, ADC_PinFunction ADC1, ADC_PinFunction ADC2, ADC_PinFunction ADC3, ADC_VREF VREF);
            [LibraryImport(dllPath, EntryPoint = "_u2aADC_ConvertAndRead")]
            internal static partial int u2aADC_ConvertAndRead(int handle, byte nBytes, [Out] byte[] data);
            [LibraryImport(dllPath, EntryPoint = "_u2aADC_Enable")]
            internal static partial int u2aADC_Enable(int handle, int nChannel, int nMode);
            [LibraryImport(dllPath, EntryPoint = "_u2aADC_SetReference")]
            internal static partial int u2aADC_SetReference(int handle, ADC_VREF VREF);
            [LibraryImport(dllPath, EntryPoint = "_u2aADC_Acquire")]
            internal static partial int u2aADC_Acquire(int handle, int nInterval, int nSamples);
            [LibraryImport(dllPath, EntryPoint = "_u2aADC_AcquireTriggered")]
            internal static partial int u2aADC_AcquireTriggered(int handle, int nInterval, int nSamples, int nTrigger);
            [LibraryImport(dllPath, EntryPoint = "_u2aADC_GetData")]
            internal static partial int u2aADC_GetData(int handle, ushort nOffset, ushort nDataPoints, [Out] ushort[] buffer);
            [LibraryImport(dllPath, EntryPoint = "_u2aADC_GetStatus")]
            internal static partial int u2aADC_GetStatus(int handle, [Out] byte[] buffer);
    
            // GPIO
            [LibraryImport(dllPath, EntryPoint = "u2aGPIO_ReadPort")]
            internal static partial int u2aGPIO_ReadPort(int handle, byte GPIO_Port);
            [LibraryImport(dllPath, EntryPoint = "u2aGPIO_ReadState")]
            internal static partial int u2aGPIO_ReadState(int handle, byte nBytes, [Out] byte[] data);
            [LibraryImport(dllPath, EntryPoint = "u2aGPIO_WritePort")]
            internal static partial int u2aGPIO_WritePort(int handle, byte GPIO_Port, byte state);
            [LibraryImport(dllPath, EntryPoint = "u2aGPIO_WriteState")]
            internal static partial int u2aGPIO_WriteState(int handle, GPIO_OutPinState GPIO0, GPIO_OutPinState GPIO1, GPIO_OutPinState GPIO2, GPIO_OutPinState GPIO3, GPIO_OutPinState GPIO4, GPIO_OutPinState GPIO5, GPIO_OutPinState GPIO6, GPIO_OutPinState GPIO7, GPIO_OutPinState GPIO8, GPIO_OutPinState GPIO9, GPIO_OutPinState GPIO10, GPIO_OutPinState GPIO11, GPIO_OutPinState GPIO12);
            [LibraryImport(dllPath, EntryPoint = "u2aGPIO_WriteControl")]
            internal static partial int u2aGPIO_WriteControl(int handle, GPIO_PinFunction GPIO0, GPIO_PinFunction GPIO1, GPIO_PinFunction GPIO2, GPIO_PinFunction GPIO3, GPIO_PinFunction GPIO4, GPIO_PinFunction GPIO5, GPIO_PinFunction GPIO6, GPIO_PinFunction GPIO7, GPIO_PinFunction GPIO8, GPIO_PinFunction GPIO9, GPIO_PinFunction GPIO10, GPIO_PinFunction GPIO11, GPIO_PinFunction GPIO12);
            [LibraryImport(dllPath, EntryPoint = "u2aGPIO_SetPort")]
            internal static partial int u2aGPIO_SetPort(int handle, byte GPIO_Port, GPIO_PinFunction func);
    
            // I2C
            [LibraryImport(dllPath, EntryPoint = "u2aI2C_Control")]
            internal static partial int u2aI2C_Control(int handle, I2C_ClockRate Speed, I2C_BusAddressLength AddressLength, IO_Enable PullUps);
            [LibraryImport(dllPath, EntryPoint = "u2aI2C_RawWrite")]
            internal static partial int u2aI2C_RawWrite(int handle, ushort I2C_Address, byte nBytes, [Out] byte[] Data);
            [LibraryImport(dllPath, EntryPoint = "u2aI2C_RawRead")]
            internal static partial int u2aI2C_RawRead(int handle, ushort I2C_Address, byte nBytes, [Out] byte[] Data);
            [LibraryImport(dllPath, EntryPoint = "u2aI2C_BlockWriteBlockRead")]
            internal static partial int u2aI2C_BlockWriteBlockRead(int handle, ushort I2C_Address, byte nWriteBytes, [Out] byte[] WriteData, byte nReadBytes, [Out] byte[] ReadData);
            [LibraryImport(dllPath, EntryPoint = "u2aI2C_InternalRead")]
            internal static partial int u2aI2C_InternalRead(int handle, ushort I2C_Address, ushort InternalAddress, byte IntAddrSize, ushort nBytes, [Out] byte[] Data);
    
            // SPI
            [LibraryImport(dllPath, EntryPoint = "u2aSPI_Control")]
            internal static partial int u2aSPI_Control(int handle, SPI_ClockPhase _SPI_ClockPhase, IO_Polarity _IO_Polarity, IO_Endianness _IO_Endianness, SPI_CharacterLength _SPI_CharacterLength, SPI_CSType _SPI_LatchType, IO_Polarity _SPI_LatchPolarity, int _DividerHigh, int _DividerLow);
            [LibraryImport(dllPath, EntryPoint = "u2aSPI_WriteAndRead")]
            internal static partial int u2aSPI_WriteAndRead(int handle, byte nBytes, [Out] byte[] Data);
            [LibraryImport(dllPath, EntryPoint = "u2aSPI_WriteAndReadEx")]
            internal static partial int u2aSPI_WriteAndReadEx(int handle, SPI_SlaveSelect _SPI_SlaveSelect, int nBytes, [Out] byte[] data);
    
            //SMBusControl
            [LibraryImport(dllPath, EntryPoint = "u2aSMBUS_Control")]
            internal static partial int u2aSMBUS_Control(int handle, SMBus_Flags SMBUSFlags);
            //SMBusWriteByte
            [LibraryImport(dllPath, EntryPoint = "u2aSMBUS_WriteByte")]
            internal static partial int u2aSMBUS_WriteByte(int handle, ushort I2CSlaveAddress, byte CommandCode, byte data, SMBus_Flags SMBUSFlags);
            //SMBusReadByte
            [LibraryImport(dllPath, EntryPoint = "u2aSMBUS_ReadByte")]
            internal static partial int u2aSMBUS_ReadByte(int handle, ushort I2CSlaveAddress, byte CommandCode, SMBus_Flags SMBUSFlags);
            //SMBusWriteBlock
            [LibraryImport(dllPath, EntryPoint = "u2aSMBUS_WriteBlock")]
            internal static partial int u2aSMBUS_WriteBlock(int handle, ushort I2CSlaveAddress, byte CommandCode, byte nBytes, [Out] byte[] data, SMBus_Flags SMBUSFlags);
            //SMBusReadBlock
            [LibraryImport(dllPath, EntryPoint = "u2aSMBUS_ReadBlock")]
            internal static partial int u2aSMBUS_ReadBlock(int handle, ushort I2CSlaveAddress, byte CommandCode, byte nBytes, [Out] byte[] data, SMBus_Flags SMBUSFlags);
            #endregion USB2ANY_x64.dll external definitions
        }
    }

    I should also point out that I've made some custom modifications to the USB2ANY firmware and DLL used by TICS Pro, of which only one is relevant to SPI usage: u2aSPI_Control CSType parameter was extended to allow 3-byte stride (CSType=6) and 4-byte stride (CSType=5) for better utilization of the buffer sent to the USB2ANY. If you follow the example posted above, you'll find that you can only read or write one register at a time because of the per-packet SPI toggles. If you instead specify three-byte stride (CSType=6), you can pack the buffer with multiple three-byte address/data elements, up to the limit of the HID packet size without overhead (54 bytes for u2aSPI_WriteAndRead, 53 bytes for u2aSPI_WriteAndReadEx). Since USB2ANY enumerates as an HID class device, and only uses control transfers with 64-byte packets over full-speed USB, and since the firmware adds about 10 bytes of overhead to each packet in the best case, the throughput is only one control transfer per 5-15ms; the 3-byte and 4-byte stride improve the throughput for SPI by a factor of 18 and 13 respectively. If you want to take advantage of these optimizations, use the 32-bit USB2ANY.dll (and firmware version 2.9.1.2) included in TICS Pro, or the 64-bit version (also firmware version 2.9.1.2) included in TICS Pro 2 (USB2ANY_x64.dll). It's a little tricky to break down a sequence of addresses and data into an optimally-packed byte buffer, but not terribly so compared to something like I2C with block reads and writes. I could provide more guidance here if needed - I tried putting together some simple python to explain the principle for the byte packing, which I think isn't too hard to understand:

    from typing import Iterator
    
    _U2A_MAX_BUF_LEN = 54
    
    def encode_LMX1205_buffers(
        addresses: list[int],
        data: list[int],
        isRead: bool = False
    ) -> Iterator[bytes]:
        buf = b''
        transferred = 0
        for a, v in zip(addresses, data):
            if isRead:
                packed = 0x800000 | ((a & 0x7f) << 16)
            else:
                packed = ((a & 0x7f) << 16) | ((v & 0xffff))
            buf += packed.to_bytes(length=3, byteorder='big')
            transferred += 3
            if transferred + 3 > _U2A_MAX_BUF_LEN:
                yield buf
                buf = b''
                transferred = 0
        if transferred > 0:
            yield buf
    
    def decode_LMX1205_readback(buf: bytes) -> list[int]:
        return [
            int.from_bytes(buf[i+1:i+3], byteorder='big')
            for i in range(0, len(buf), 3)
        ]
    

    Let me know if this is enough, or if you have additional questions.