Skip to content

Instantly share code, notes, and snippets.

@saper-2
Created February 22, 2026 00:45
Show Gist options
  • Select an option

  • Save saper-2/e94b64253ba223302dea6c257263ddd0 to your computer and use it in GitHub Desktop.

Select an option

Save saper-2/e94b64253ba223302dea6c257263ddd0 to your computer and use it in GitHub Desktop.
cp2112-cs
using System;
using System.Runtime.InteropServices;
using System.Text;
namespace SLABCP2112;
/// <summary>
/// Główna klasa importująca funkcje z SLABHIDtoSMBus.dll
/// Używa LibraryImport dla .NET 9 (source generator)
/// </summary>
internal static partial class HidSmbus
{
// =========================================================================
// Funkcje zarządzania urządzeniem i enumeracji
// =========================================================================
[DllImport("SLABHIDtoSMBus.dll", CallingConvention = CallingConvention.StdCall)]
private static extern int HidSmbus_Open(
out IntPtr device,
uint deviceNum, // const DWORD deviceNum
ushort vid, // const WORD vid
ushort pid); // const WORD pid
[DllImport("SLABHIDtoSMBus.dll", CallingConvention = CallingConvention.StdCall)]
private static extern int HidSmbus_GetNumDevices(
out uint numDevices, // const DWORD deviceNum - UWAGA! może być uint
ushort vid, // const WORD vid
ushort pid); // const WORD pid
[DllImport("SLABHIDtoSMBus.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
private static extern int HidSmbus_OpenBySerial(
out IntPtr device,
ushort vid,
ushort pid,
string serial);
[DllImport("SLABHIDtoSMBus.dll", CallingConvention = CallingConvention.StdCall)]
private static extern int HidSmbus_Close(IntPtr device);
/*[LibraryImport("SLABHIDtoSMBus.dll")]
private static partial int HidSmbus_IsOpened(
IntPtr device,
out bool opened);*/
[DllImport("SLABHIDtoSMBus.dll", CallingConvention = CallingConvention.StdCall)]
private static extern int HidSmbus_IsOpened(
IntPtr device,
out byte opened);
[DllImport("SLABHIDtoSMBus.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
private static extern int HidSmbus_GetString(
uint deviceNum, // const DWORD deviceNum
ushort vid, // const WORD vid
ushort pid, // const WORD pid
byte[] deviceString, // char* deviceString
int options); // const HID_SMBUS_GETSTRING options
/// <summary>
/// h: HidSmbus_GetOpenedString(_In_ _Pre_defensive_ const HID_SMBUS_DEVICE device, char* deviceString, _In_ _Pre_defensive_ const HID_SMBUS_GETSTRING options);
/// </summary>
/// <param name="device"></param>
/// <param name="deviceString"></param>
/// <param name="getStringOption"></param>
/// <returns></returns>
[DllImport("SLABHIDtoSMBus.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
private static extern int HidSmbus_GetOpenedString(
IntPtr device,
byte[] deviceString,
int getStringOption);
// =========================================================================
// Funkcje konfiguracyjne SMBus
// =========================================================================
[DllImport("SLABHIDtoSMBus.dll", CallingConvention = CallingConvention.StdCall)]
private static extern int HidSmbus_GetSmbusConfig(
IntPtr device,
out int bitRate,
out int address,
out int autoReadRespond,
out int writeTimeout,
out int readTimeout,
out byte sclLowTimeout,
out byte transferRetries);
[DllImport("SLABHIDtoSMBus.dll", CallingConvention = CallingConvention.StdCall)]
private static extern int HidSmbus_SetSmbusConfig(
IntPtr device,
int bitRate,
int address,
int autoReadRespond,
int writeTimeout,
int readTimeout,
byte sclLowTimeout,
byte transferRetries);
// =========================================================================
// Funkcje transferu danych SMBus
// =========================================================================
[DllImport("SLABHIDtoSMBus.dll", CallingConvention = CallingConvention.StdCall)]
private static extern int HidSmbus_WriteRequest(
IntPtr device,
byte targetAddress,
byte[] data,
int numBytesToWrite);
[DllImport("SLABHIDtoSMBus.dll", CallingConvention = CallingConvention.StdCall)]
private static extern int HidSmbus_ReadRequest(
IntPtr device,
byte targetAddress,
int numBytesToRead);
[DllImport("SLABHIDtoSMBus.dll", CallingConvention = CallingConvention.StdCall)]
private static extern int HidSmbus_TransferStatusRequest(
IntPtr device,
out byte status,
out byte detail);
[DllImport("SLABHIDtoSMBus.dll", CallingConvention = CallingConvention.StdCall)]
private static extern int HidSmbus_ReadResponse(
IntPtr device,
byte[] buffer,
out int numBytesRead);
[DllImport("SLABHIDtoSMBus.dll", CallingConvention = CallingConvention.StdCall)]
private static extern int HidSmbus_TransferAbort(
IntPtr device);
[DllImport("SLABHIDtoSMBus.dll", CallingConvention = CallingConvention.StdCall)]
private static extern int HidSmbus_Reset(
IntPtr device);
// =========================================================================
// Funkcje do zarządzania GPIO
// =========================================================================
[DllImport("SLABHIDtoSMBus.dll", CallingConvention = CallingConvention.StdCall)]
private static extern int HidSmbus_GetGpioConfig(
IntPtr device,
out byte direction,
out byte mode,
out byte function,
out byte clkDivider);
[DllImport("SLABHIDtoSMBus.dll", CallingConvention = CallingConvention.StdCall)]
private static extern int HidSmbus_SetGpioConfig(
IntPtr device,
byte direction,
byte mode,
byte function,
byte clkDivider);
[DllImport("SLABHIDtoSMBus.dll", CallingConvention = CallingConvention.StdCall)]
private static extern int HidSmbus_GetGpio(
IntPtr device,
out byte gpioValue);
[DllImport("SLABHIDtoSMBus.dll", CallingConvention = CallingConvention.StdCall)]
private static extern int HidSmbus_SetGpio(
IntPtr device,
byte gpioValue);
// =========================================================================
// Publiczne metody wrappera - one wywołują prywatne importy
// =========================================================================
public static int GetNumDevices(out int numDevices, int vid, int pid)
{
uint devices = 0;
int result = HidSmbus_GetNumDevices(out devices, (ushort)vid, (ushort)pid);
numDevices = (int)devices;
return result;
}
public static int Open(out IntPtr device, int vid, int pid, int deviceIndex)
{
// POPRAWNA KOLEJNOŚĆ: deviceNum, vid, pid
return HidSmbus_Open(out device, (uint)deviceIndex, (ushort)vid, (ushort)pid);
}
public static int OpenBySerial(out IntPtr device, int vid, int pid, string serial)
{
return HidSmbus_OpenBySerial(out device, (ushort)vid, (ushort)pid, serial);
}
public static int Close(IntPtr device)
{
return HidSmbus_Close(device);
}
public static int GetString(int deviceNum, int vid, int pid, byte[] deviceString, int options)
{
return HidSmbus_GetString((uint)deviceNum, (ushort)vid, (ushort)pid, deviceString, options);
}
public static int GetOpenedString(IntPtr device, byte[] deviceString, int options)
{
return HidSmbus_GetOpenedString(device, deviceString, options);
}
/*public static int IsOpened(IntPtr device, out bool opened)
{
return HidSmbus_IsOpened(device, out opened);
}*/
public static int IsOpened(IntPtr device, out bool opened)
{
int result = HidSmbus_IsOpened(device, out byte openedByte);
opened = openedByte != 0;
return result;
}
public static int GetSmbusConfig(IntPtr device, out int bitRate, out int address, out int autoReadRespond,
out int writeTimeout, out int readTimeout, out byte sclLowTimeout, out byte transferRetries)
{
return HidSmbus_GetSmbusConfig(device, out bitRate, out address, out autoReadRespond,
out writeTimeout, out readTimeout, out sclLowTimeout, out transferRetries);
}
public static int SetSmbusConfig(IntPtr device, int bitRate, int address, int autoReadRespond,
int writeTimeout, int readTimeout, byte sclLowTimeout, byte transferRetries)
{
return HidSmbus_SetSmbusConfig(device, bitRate, address, autoReadRespond,
writeTimeout, readTimeout, sclLowTimeout, transferRetries);
}
public static int WriteRequest(IntPtr device, byte targetAddress, byte[] data, int numBytesToWrite)
{
return HidSmbus_WriteRequest(device, targetAddress, data, numBytesToWrite);
}
public static int ReadRequest(IntPtr device, byte targetAddress, int numBytesToRead)
{
return HidSmbus_ReadRequest(device, targetAddress, numBytesToRead);
}
public static int TransferStatusRequest(IntPtr device, out byte status, out byte detail)
{
return HidSmbus_TransferStatusRequest(device, out status, out detail);
}
public static int ReadResponse(IntPtr device, byte[] buffer, out int numBytesRead)
{
return HidSmbus_ReadResponse(device, buffer, out numBytesRead);
}
public static int TransferAbort(IntPtr device)
{
return HidSmbus_TransferAbort(device);
}
public static int Reset(IntPtr device)
{
return HidSmbus_Reset(device);
}
public static int GetGpioConfig(IntPtr device, out byte direction, out byte mode, out byte function, out byte clkDivider)
{
return HidSmbus_GetGpioConfig(device, out direction, out mode, out function, out clkDivider);
}
public static int SetGpioConfig(IntPtr device, byte direction, byte mode, byte function, byte clkDivider)
{
return HidSmbus_SetGpioConfig(device, direction, mode, function, clkDivider);
}
public static int GetGpio(IntPtr device, out byte gpioValue)
{
return HidSmbus_GetGpio(device, out gpioValue);
}
public static int SetGpio(IntPtr device, byte gpioValue)
{
return HidSmbus_SetGpio(device, gpioValue);
}
}
/// <summary>
/// Wrapper dla urządzenia CP2112 - uproszczenie użycia
/// </summary>
public class Cp2112Device : IDisposable
{
private IntPtr _deviceHandle;
private bool _disposed;
public const int HID_SMBUS_GET_VID_STR = 0x01;
public const int HID_SMBUS_GET_PID_STR = 0x02;
public const int HID_SMBUS_GET_PATH_STR = 0x03;
public const int HID_SMBUS_GET_SERIAL_STR = 0x04;
public const int HID_SMBUS_GET_MANUFACTURER_STR = 0x05;
public const int HID_SMBUS_GET_PRODUCT_STR = 0x06;
public const byte HID_SMBUS_S0_IDLE = 0x00;
public const byte HID_SMBUS_S0_BUSY = 0x01;
public const byte HID_SMBUS_S0_COMPLETE = 0x02;
public const byte HID_SMBUS_S0_ERROR = 0x03;
public const int SILICON_LABS_VID = 0x10C4;
public const int CP2112_PID = 0xEA90;
public IntPtr GetHandle() => _deviceHandle;
public static Cp2112Device? OpenFirst(int vid = SILICON_LABS_VID, int pid = CP2112_PID)
{
var result = HidSmbus.GetNumDevices(out int numDevices, vid, pid);
if (result != 0 || numDevices == 0)
return null;
var device = new Cp2112Device();
result = HidSmbus.Open(out device._deviceHandle, vid, pid, 0);
return result == 0 ? device : null;
}
// Dodatkowa metoda do testowania
/*public static void ListAllDevices()
{
//Console.WriteLine("=== LISTA WSZYSTKICH URZĄDZEŃ CP2112 ===\n");
int result = HidSmbus.GetNumDevices(out int numDevices, SILICON_LABS_VID, CP2112_PID);
if (result != 0 || numDevices == 0)
{
//Console.WriteLine("Nie znaleziono żadnych urządzeń CP2112.");
return;
}
//Console.WriteLine($"Znaleziono {numDevices} urządzeń CP2112:\n");
for (int i = 0; i < numDevices; i++)
{
//Console.WriteLine($"--- URZĄDZENIE #{i} ---");
using var device = OpenByIndex(i, SILICON_LABS_VID, CP2112_PID);
if (device != null)
{
//Console.WriteLine($" Handle: {device.GetHandle()}");
//Console.WriteLine($" Serial: {device.GetSerialString()}");
//Console.WriteLine($" Manufacturer: {device.GetString(HID_SMBUS_GET_MANUFACTURER_STR)}");
//Console.WriteLine($" Product: {device.GetString(HID_SMBUS_GET_PRODUCT_STR)}");
//Console.WriteLine();
}
else
{
//Console.WriteLine($" Nie można otworzyć urządzenia #{i}");
}
}
}*/
public static Cp2112Device? OpenBySerial(string serial, int vid = SILICON_LABS_VID, int pid = CP2112_PID)
{
var device = new Cp2112Device();
var result = HidSmbus.OpenBySerial(out device._deviceHandle, vid, pid, serial);
return result == 0 ? device : null;
}
/// <summary>
/// Otwiera urządzenie o podanym indeksie
/// </summary>
public static Cp2112Device? OpenByIndex(int index, int vid = SILICON_LABS_VID, int pid = CP2112_PID)
{
var device = new Cp2112Device();
int result = HidSmbus.Open(out device._deviceHandle, vid, pid, index);
return result == 0 ? device : null;
}
public string? GetSerialString()
{
return GetString(HID_SMBUS_GET_SERIAL_STR);
}
public string? GetString(int stringType)
{
byte[] buffer = new byte[260];
var result = HidSmbus.GetOpenedString(_deviceHandle, buffer, stringType);
if (result != 0) return null;
int length = Array.IndexOf(buffer, (byte)0);
return System.Text.Encoding.UTF8.GetString(buffer, 0, length >= 0 ? length : buffer.Length);
}
public int ConfigureSmbus(int bitRate, int address, int writeTimeoutMs, int readTimeoutMs, byte retries = 0)
{
return HidSmbus.SetSmbusConfig(
_deviceHandle,
bitRate,
address,
1, // autoReadRespond = true
writeTimeoutMs,
readTimeoutMs,
1, // sclLowTimeout = true
retries);
}
public int Write(byte targetAddress, byte[] data)
{
return HidSmbus.WriteRequest(_deviceHandle, targetAddress, data, data.Length);
}
public int Read(byte targetAddress, int bytesToRead, out byte[] data)
{
data = Array.Empty<byte>();
var result = HidSmbus.ReadRequest(_deviceHandle, targetAddress, bytesToRead);
if (result != 0) return result;
byte status, detail;
do
{
Thread.Sleep(1);
result = HidSmbus.TransferStatusRequest(_deviceHandle, out status, out detail);
if (result != 0) return result;
} while (status == HID_SMBUS_S0_BUSY);
if (status != HID_SMBUS_S0_COMPLETE)
return -1;
data = new byte[61];
result = HidSmbus.ReadResponse(_deviceHandle, data, out int bytesRead);
if (bytesRead < data.Length)
Array.Resize(ref data, bytesRead);
return result;
}
public int SetGpio(byte gpioValue)
{
return HidSmbus.SetGpio(_deviceHandle, gpioValue);
}
public int GetGpio(out byte gpioValue)
{
return HidSmbus.GetGpio(_deviceHandle, out gpioValue);
}
public int Reset()
{
return HidSmbus.Reset(_deviceHandle);
}
public void Dispose()
{
if (!_disposed && _deviceHandle != IntPtr.Zero)
{
HidSmbus.Close(_deviceHandle);
_deviceHandle = IntPtr.Zero;
}
_disposed = true;
GC.SuppressFinalize(this);
}
/// <summary>
/// Zwraca listę wszystkich podłączonych urządzeń CP2112 bez ich otwierania
/// </summary>
public static List<Cp2112DeviceInfo> EnumerateAll(int vid = SILICON_LABS_VID, int pid = CP2112_PID)
{
var devices = new List<Cp2112DeviceInfo>();
// Sprawdź liczbę urządzeń
int result = HidSmbus.GetNumDevices(out int numDevices, vid, pid);
if (result != 0 || numDevices == 0)
return devices;
// Dla każdego urządzenia pobierz podstawowe informacje
for (int i = 0; i < numDevices; i++)
{
var deviceInfo = new Cp2112DeviceInfo
{
DeviceIndex = i,
Vid = vid,
Pid = pid
};
// Próba otwarcia urządzenia (tymczasowo)
IntPtr tempHandle = IntPtr.Zero;
try
{
result = HidSmbus.Open(out tempHandle, vid, pid, i);
if (result == 0 && tempHandle != IntPtr.Zero)
{
// Pobierz stringi
deviceInfo.ManufacturerString = GetStringFromDevice(tempHandle, HID_SMBUS_GET_MANUFACTURER_STR);
deviceInfo.ProductString = GetStringFromDevice(tempHandle, HID_SMBUS_GET_PRODUCT_STR);
deviceInfo.SerialString = GetStringFromDevice(tempHandle, HID_SMBUS_GET_SERIAL_STR);
deviceInfo.Path = GetStringFromDevice(tempHandle, HID_SMBUS_GET_PATH_STR);
}
}
catch
{
// Ignoruj błędy przy pojedynczym urządzeniu
}
finally
{
if (tempHandle != IntPtr.Zero)
HidSmbus.Close(tempHandle);
}
devices.Add(deviceInfo);
}
return devices;
}
/// <summary>
/// Pomocnicza metoda do pobierania stringów z urządzenia
/// </summary>
private static string? GetStringFromDevice(IntPtr deviceHandle, int stringType)
{
byte[] buffer = new byte[260];
int result = HidSmbus.GetOpenedString(deviceHandle, buffer, stringType);
if (result != 0) return null;
int length = Array.IndexOf(buffer, (byte)0);
if (length <= 0) return null;
return Encoding.UTF8.GetString(buffer, 0, length);
}
/// <summary>
/// Skanuje magistralę I2C w poszukiwaniu urządzeń slave.
/// </summary>
/// <param name="startAddress">Początkowy adres (domyślnie 0x02)</param>
/// <param name="endAddress">Końcowy adres (domyślnie 0x7F)</param>
/// <param name="timeoutMs">Timeout dla każdej próby w ms</param>
/// <returns>Lista znalezionych adresów</returns>
public List<byte> ScanI2CBus(byte startAddress = 0x02, byte endAddress = 0x7F, int timeoutMs = 10)
{
var foundDevices = new List<byte>();
if (_deviceHandle == IntPtr.Zero)
throw new InvalidOperationException("Urządzenie nie jest otwarte");
// Zapisz oryginalną konfigurację
HidSmbus.GetSmbusConfig(_deviceHandle,
out int originalBitRate,
out int originalAddress,
out int originalAutoRespond,
out int originalWriteTimeout,
out int originalReadTimeout,
out byte originalSclTimeout,
out byte originalRetries);
try
{
// Optymalizacja dla skanowania: ustaw małą liczbę retransmisji i krótki timeout
// Ustawienie transferRetries=1 oznacza tylko jedną próbę, bez ponowień przy błędzie [citation:1]
HidSmbus.SetSmbusConfig(
_deviceHandle,
originalBitRate,
originalAddress,
0, // autoReadRespond = false (ważne dla skanowania)
timeoutMs,
timeoutMs,
1, // sclLowTimeout = true
1 // transferRetries = 1 (tylko jedna próba)
);
//Console.WriteLine($"Skanowanie adresów od 0x{startAddress:X2} do 0x{endAddress:X2}...");
// Przeskanuj wszystkie adresy
for (byte address = startAddress; address <= endAddress; address++)
{
// Pomijamy adresy nieparzyste? Dla SMBus adresy są parzyste (7-bitowe)
// CP2112 obsługuje adresy od 0x02 do 0xFE z krokiem 2 [citation:1]
// Próba zapisu 1 bajtu (lub odczytu) bez faktycznego przesyłania danych
byte[] dummyData = { 0x00 }; // Jeden bajt dummy data
// Opcja 1: Próba zapisu (nie zmienia stanu urządzenia jeśli wyślemy 0x00)
int result = HidSmbus.WriteRequest(_deviceHandle, address, dummyData, 1);
// Opcja 2: Alternatywnie możesz użyć ReadRequest (bezpieczniejsze, nie modyfikuje)
// int result = HidSmbus.ReadRequest(_deviceHandle, address, 1);
if (result == 0) // HID_SMBUS_SUCCESS
{
// Sprawdź status transferu
byte status, detail;
Thread.Sleep(1); // Krótkie opóźnienie
result = HidSmbus.TransferStatusRequest(_deviceHandle, out status, out detail);
if (result == 0)
{
// Jeśli status COMPLETE - urządzenie odpowiedziało ACK
if (status == HID_SMBUS_S0_COMPLETE)
{
//Console.WriteLine($" Znaleziono urządzenie na adresie 0x{address:X2}");
foundDevices.Add(address);
}
}
}
// Krótkie opóźnienie między skanowaniem
Thread.Sleep(1);
}
}
finally
{
// Przywróć oryginalną konfigurację
HidSmbus.SetSmbusConfig(
_deviceHandle,
originalBitRate,
originalAddress,
originalAutoRespond,
originalWriteTimeout,
originalReadTimeout,
originalSclTimeout,
originalRetries);
}
return foundDevices;
}
/// <summary>
/// Skanuje magistralę I2C używając ReadRequest (nie modyfikuje urządzeń)
/// </summary>
public List<byte> ScanI2CBusSafe(byte startAddress = 0x02, byte endAddress = 0x7F, int timeoutMs = 10)
{
var foundDevices = new List<byte>();
if (_deviceHandle == IntPtr.Zero)
throw new InvalidOperationException("Urządzenie nie jest otwarte");
// Zapisz oryginalną konfigurację
HidSmbus.GetSmbusConfig(_deviceHandle,
out int originalBitRate,
out int originalAddress,
out int originalAutoRespond,
out int originalWriteTimeout,
out int originalReadTimeout,
out byte originalSclTimeout,
out byte originalRetries);
try
{
// Konfiguracja dla skanowania
HidSmbus.SetSmbusConfig(
_deviceHandle,
originalBitRate,
originalAddress,
0, // autoReadRespond = false
timeoutMs,
timeoutMs,
1,
1);
//Console.WriteLine($"Skanowanie adresów od 0x{startAddress:X2} do 0x{endAddress:X2}...");
//Console.WriteLine("Oczekiwanie na stabilizację magistrali...");
Thread.Sleep(100);
for (byte address = startAddress; address <= endAddress; address++)
{
// Użyj ReadRequest zamiast WriteRequest - to bezpieczniejsze
// Nie wysyłamy żadnych danych, tylko sprawdzamy czy slave odpowiada
int result = HidSmbus.ReadRequest(_deviceHandle, address, 1);
if (result == 0)
{
// Daj czas na odpowiedź
Thread.Sleep(2);
// Sprawdź status transferu
byte status, detail;
result = HidSmbus.TransferStatusRequest(_deviceHandle, out status, out detail);
if (result == 0)
{
if (status == HID_SMBUS_S0_COMPLETE)
{
//Console.WriteLine($" [OK] Adres 0x{address:X2} - odpowiedź ACK");
foundDevices.Add(address);
}
else if (status == HID_SMBUS_S0_ERROR)
{
// Brak urządzenia - NACK, ignorujemy
// Console.WriteLine($" Adres 0x{address:X2} - NACK");
}
}
}
// Małe opóźnienie między próbami
Thread.Sleep(1);
}
}
finally
{
// Przywróć oryginalną konfigurację
HidSmbus.SetSmbusConfig(
_deviceHandle,
originalBitRate,
originalAddress,
originalAutoRespond,
originalWriteTimeout,
originalReadTimeout,
originalSclTimeout,
originalRetries);
}
//Console.WriteLine($"Skanowanie zakończone. Znaleziono {foundDevices.Count} urządzeń.");
return foundDevices;
}
}
public class I2CScanResult
{
public byte Address { get; set; }
public bool HasDevice { get; set; }
public string? DeviceType { get; set; }
public int ResponseTimeMs { get; set; }
public override string ToString()
{
return $"0x{Address:X2} ({(Address % 2 == 0 ? "even" : "odd")}): {(HasDevice ? "OK" : "---")}";
}
}
/// <summary>
/// Klasa reprezentująca informacje o urządzeniu bez otwierania go
/// </summary>
public class Cp2112DeviceInfo
{
public int DeviceIndex { get; set; }
public int Vid { get; set; }
public int Pid { get; set; }
public string? ManufacturerString { get; set; }
public string? ProductString { get; set; }
public string? SerialString { get; set; }
public string? Path { get; set; }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment