initial commit
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
namespace RTCSync.Utils;
|
||||
|
||||
public class BinaryUtils
|
||||
{
|
||||
public static byte ToBcd(int value) => (byte)(((value / 10) << 4) | (value % 10));
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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 _);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user