initial commit

This commit is contained in:
2026-06-25 12:19:29 +03:00
commit 1160477945
19 changed files with 898 additions and 0 deletions
+7
View File
@@ -0,0 +1,7 @@
namespace RTCSync.Utils;
public class BinaryUtils
{
public static byte ToBcd(int value) => (byte)(((value / 10) << 4) | (value % 10));
}
+67
View File
@@ -0,0 +1,67 @@
using System;
using System.Diagnostics;
namespace RTCSync.Utils;
public readonly struct BitIndex
{
private readonly ushort _byteIdx;
private readonly byte _startBitIdx0;
private readonly byte _endBitIdx0;
// порядок указывания байт в структуре
// сначала старший, потом младше
// 1<-------------------------------41
// порядок указывания бит в структуре
// сначала младший, потом старше
// 7<------0
// таким образом все биты вписываются по порядку справа налево
// BitIndex[new(3, 0..7), new(2, 0..7), new(1, 0..6)]
public BitIndex(int byteIdx1, Range bits)
{
_byteIdx = (ushort)(byteIdx1 - 1);
Debug.Assert(!bits.Start.IsFromEnd);
Debug.Assert(!bits.End.IsFromEnd);
_startBitIdx0 = (byte)bits.Start.Value;
_endBitIdx0 = (byte)bits.End.Value;
}
public int Length => _endBitIdx0 - _startBitIdx0 + 1;
public int Take(ReadOnlySpan<byte> bytes)
{
// example: Len=3, 0b00001000 - 1 = 0b00000111
var mask = (1U << (_endBitIdx0 - _startBitIdx0 + 1)) - 1U;
return (int)((bytes[_byteIdx] >> _startBitIdx0) & mask);
}
public static int Take(BitIndex[] bis, ReadOnlySpan<byte> bytes)
{
var shift = 0;
var result = 0;
foreach (var bi in bis)
{
result |= (bi.Take(bytes) << shift);
shift += bi.Length;
}
return result;
}
}
public static class BitIndexV2Extensions
{
public static int Take(this BitIndex[] bis, ReadOnlySpan<byte> bytes)
{
var shift = 0;
var result = 0;
foreach (var bi in bis)
{
result |= (bi.Take(bytes) << shift);
shift += bi.Length;
}
return result;
}
}
+134
View File
@@ -0,0 +1,134 @@
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 _);
}
}
+26
View File
@@ -0,0 +1,26 @@
using System.Runtime.InteropServices;
namespace RTCSync.Utils;
public abstract class WindowsTimeUtils
{
// c# datetime set
[DllImport("kernel32.dll")]
public static extern bool SetSystemTime(ref SystemTime time);
[DllImport("kernel32.dll")]
public static extern void GetSystemTime(ref SystemTime time);
[StructLayout(LayoutKind.Sequential)]
public struct SystemTime(DateTime dt)
{
public ushort Year = (ushort)dt.Year;
public ushort Month = (ushort)dt.Month;
public ushort DayOfWeek = (ushort)dt.DayOfWeek;
public ushort Day = (ushort)dt.Day;
public ushort Hour = (ushort)dt.Hour;
public ushort Minute = (ushort)dt.Minute;
public ushort Second = (ushort)dt.Second;
public ushort Milliseconds = (ushort)dt.Millisecond;
}
}