using LibUsbDotNet.LibUsb; using LibUsbDotNet.Main; namespace RTCSync.Utils; public static class I2CUtils { private const int VID = 0x1A86; private const int PID = 0x5512; // CH341T // Команды CH341 для I2C private const byte CH341_CMD_I2C_STREAM = 0xAA; private const byte CH341_CMD_I2C_STM_STA = 0x74; // START private const byte CH341_CMD_I2C_STM_STO = 0x75; // STOP private const byte CH341_CMD_I2C_STM_OUT = 0x80; // write byte (len в младших битах) private const byte CH341_CMD_I2C_STM_IN = 0xC0; // read byte private const byte CH341_CMD_I2C_STM_END = 0x00; // конец стрима public static void ScanDevice(IUsbDevice device) { var writer = device.OpenEndpointWriter(WriteEndpointID.Ep02); var reader = device.OpenEndpointReader(ReadEndpointID.Ep02); Console.WriteLine("Сканирование I2C шины (0x03 - 0x77)..."); for (byte addr = 0x03; addr < 0x78; addr++) { // Пробуем послать START + адрес + STOP // Если устройство есть — придёт ACK, если нет — NACK var cmd = new byte[] { CH341_CMD_I2C_STREAM, // CH341_CMD_I2C_STREAM CH341_CMD_I2C_STM_STA, // START CH341_CMD_I2C_STM_OUT | 1, // OUT, 1 байт (byte)(addr << 1), // адрес + write bit CH341_CMD_I2C_STM_STO, // STOP CH341_CMD_I2C_STM_END // END }; writer.Write(cmd, 500, out int written); var buf = new byte[4]; reader.Read(buf, 200, out int read); // CH341 возвращает статус: 0x00 = ACK получен (устройство есть) // Первый байт ответа — статус последней I2C операции if (read > 0 && buf[0] == 0x00) { Console.WriteLine($" Найдено устройство: 0x{addr:X2}"); } } Console.WriteLine("Сканирование завершено."); } public static void CH341Init(IUsbDevice device) { Console.WriteLine("Пытаемся инициализировать устройство"); var writer = device.OpenEndpointWriter(WriteEndpointID.Ep02); // Установка скорости I2C: 0x00=20kHz, 0x01=100kHz, 0x02=400kHz, 0x03=750kHz var cmd = new byte[] { CH341_CMD_I2C_STREAM, 0x60 | 0x01, // CH341_CMD_I2C_STM_SET | 100kHz CH341_CMD_I2C_STM_END }; writer.Write(cmd, 1000, out _); } public static byte[] Read(IUsbDevice device, byte address, byte reg, int length) { // Формируем пакет: START → write addr+reg → RESTART → read → STOP var cmd = new byte[] { CH341_CMD_I2C_STREAM, CH341_CMD_I2C_STM_STA, CH341_CMD_I2C_STM_OUT | 2, // 2 байта: адрес + регистр (byte)(address << 1), // write reg, CH341_CMD_I2C_STM_STA, // repeated START CH341_CMD_I2C_STM_OUT | 1, (byte)((address << 1) | 1), // read (byte)(CH341_CMD_I2C_STM_IN | (length - 1)), CH341_CMD_I2C_STM_STO, CH341_CMD_I2C_STM_END }; var writer = device.OpenEndpointWriter(WriteEndpointID.Ep02); var reader = device.OpenEndpointReader(ReadEndpointID.Ep02); writer.Write(cmd, 1000, out _); var buf = new byte[length]; reader.Read(buf, 1000, out var transferred); return buf; } public static byte[] ReadWithRetry(IUsbDevice device, byte address, byte reg, int length) { for (var attempt = 0; attempt < 100; attempt++) { var result = Read(device, address, reg, length); if (result[0] != 0x03) return result; Thread.Sleep(10); } throw new Exception("Ошибка чтения CH431 после 100 попыток"); } public static void Write(IUsbDevice device, byte address, byte reg, byte[] data) { // START → write addr → write reg → write data[0..n] → STOP // CH341_CMD_I2C_STM_OUT | N означает "записать N байт" // N = 1 (адрес) + 1 (регистр) + data.Length var cmd = new byte[5 + data.Length + 2]; int i = 0; cmd[i++] = CH341_CMD_I2C_STREAM; cmd[i++] = CH341_CMD_I2C_STM_STA; cmd[i++] = (byte)(CH341_CMD_I2C_STM_OUT | (2 + data.Length)); // адрес + регистр + данные cmd[i++] = (byte)(address << 1); // write bit = 0 cmd[i++] = reg; foreach (var b in data) cmd[i++] = b; cmd[i++] = CH341_CMD_I2C_STM_STO; cmd[i] = CH341_CMD_I2C_STM_END; var writer = device.OpenEndpointWriter(WriteEndpointID.Ep02); writer.Write(cmd, 1000, out _); } }