diff --git a/server/src/Peripherals/CameraClient.cs b/server/src/Peripherals/CameraClient.cs index dc63b4a..89bfc9b 100644 --- a/server/src/Peripherals/CameraClient.cs +++ b/server/src/Peripherals/CameraClient.cs @@ -35,6 +35,25 @@ class Camera this.timeout = timeout; } + public async ValueTask> Init() + { + var i2c = new Peripherals.I2cClient.I2c(this.address, this.port, this.timeout); + var ret = await i2c.WriteData(0x78, new byte[] { 0x08, 0x30, 0x02 }, Peripherals.I2cClient.I2cProtocol.I2c); + if (!ret.IsSuccessful) + { + logger.Error($"I2C write failed during camera initialization for {this.address}:{this.port}, error: {ret.Error}"); + return new(ret.Error); + } + + if (!ret.Value) + { + logger.Error($"I2C write returned unsuccessful result during camera initialization for {this.address}:{this.port}"); + return false; + } + + return true; + } + /// /// 读取一帧图像数据 /// @@ -47,7 +66,7 @@ class Camera logger.Trace($"Clear up udp server {this.address} receive data"); // 使用UDPClientPool读取图像帧数据 - var result = await UDPClientPool.ReadAddrBytes( + var result = await UDPClientPool.ReadAddr4Bytes( this.ep, 2, // taskID CameraAddr.Base, diff --git a/server/src/Peripherals/I2cClient.cs b/server/src/Peripherals/I2cClient.cs new file mode 100644 index 0000000..50fd083 --- /dev/null +++ b/server/src/Peripherals/I2cClient.cs @@ -0,0 +1,269 @@ +using System.Net; +using DotNext; + +namespace Peripherals.I2cClient; + +static class I2cAddr +{ + + const UInt32 Base = 0x6000_0000; + + /// + /// 0x0000_0000: + /// [7:0] 本次传输的i2c地址(最高位总为0); + /// [8] 1为读,0为写; + /// [16] 1为SCCB协议,0为I2C协议; + /// [24] 1为开启本次传输,自动置零 + /// + public const UInt32 BaseConfig = Base + 0x0000_0000; + + /// + /// 0x0000_0001: + /// [15:0] 本次传输的数据量(以字节为单位,0为传1个字节); + /// [31:16] 若本次传输为读的DUMMY数据量(字节为单位,0为传1个字节) + /// + public const UInt32 TranConfig = Base + 0x0000_0001; + + /// + /// 0x0000_0002: [0] cmd_done; [8] cmd_error; + /// + public const UInt32 Flag = Base + 0x0000_0002; + + /// + /// 0x0000_0003: FIFO写入口,仅低8位有效,只写 + /// + public const UInt32 Write = Base + 0x0000_0003; + + /// + /// 0x0000_0004: FIFO读出口,仅低8位有效,只读 + /// + public const UInt32 Read = Base + 0x0000_0003; + + /// + /// 0x0000_0005: [0] FIFO写入口清空;[8] FIFO读出口清空; + /// + public const UInt32 Clear = Base + 0x0000_0003; +} + +/// +/// [TODO:Enum] +/// +public enum I2cProtocol +{ + /// + /// [TODO:Enum] + /// + I2c = 0, + + /// + /// [TODO:Enum] + /// + SCCB = 1 +} + +/// +/// [TODO:description] +/// +public class I2c +{ + private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); + + readonly int timeout = 2000; + + readonly int port; + readonly string address; + private IPEndPoint ep; + + /// + /// [TODO:description] + /// + /// [TODO:parameter] + /// [TODO:parameter] + /// [TODO:parameter] + /// [TODO:return] + public I2c(string address, int port, int timeout = 2000) + { + if (timeout < 0) + throw new ArgumentException("Timeout couldn't be negative", nameof(timeout)); + this.address = address; + this.port = port; + this.ep = new IPEndPoint(IPAddress.Parse(address), port); + this.timeout = timeout; + } + + /// + /// 向指定I2C设备写入数据 + /// + /// I2C设备地址 + /// 要写入的数据 + /// I2C协议类型 + /// 操作结果,成功返回true,否则返回异常信息 + public async ValueTask> WriteData(UInt32 devAddr, byte[] data, I2cProtocol proto) + { + if (data.Length > 0x0000_FFFF) + { + logger.Error($"Data length {data.Length} exceeds maximum allowed 0x0000_FFFF"); + return new(new ArgumentException($"Data length {data.Length} exceeds maximum allowed 0x0000_FFFF")); + } + + // 清除UDP服务器接收缓冲区 + await MsgBus.UDPServer.ClearUDPData(this.address, 2); + + logger.Trace($"Clear up udp server {this.address} receive data"); + + // 写入数据到I2C FIFO写入口 + { + var ret = await UDPClientPool.WriteAddr(this.ep, 2, I2cAddr.Write, data); + if (!ret.IsSuccessful) + { + logger.Error($"Failed to write data to I2C FIFO: {ret.Error}"); + return new(ret.Error); + } + + if (!ret.Value) + { + logger.Error("WriteAddr to I2C FIFO returned false"); + return new(new Exception("Failed to write data to I2C FIFO")); + } + } + + // 配置本次传输数据量 + { + var ret = await UDPClientPool.WriteAddr(this.ep, 2, I2cAddr.TranConfig, ((uint)(data.Length - 1))); + if (!ret.IsSuccessful) + { + logger.Error($"Failed to configure transfer length: {ret.Error}"); + return new(ret.Error); + } + + if (!ret.Value) + { + logger.Error("WriteAddr to TranConfig returned false"); + return new(new Exception("Failed to configure transfer length")); + } + } + + // 配置I2C地址、协议及启动传输 + { + var ret = await UDPClientPool.WriteAddr( + this.ep, 2, I2cAddr.BaseConfig, (devAddr << 1) | (((uint)proto) << 16) | (1 << 24)); + if (!ret.IsSuccessful) + { + logger.Error($"Failed to configure I2C address/protocol/start: {ret.Error}"); + return new(ret.Error); + } + + if (!ret.Value) + { + logger.Error("WriteAddr to BaseConfig returned false"); + return new(new Exception("Failed to configure I2C address/protocol/start")); + } + } + + // 等待I2C命令完成 + { + var ret = await UDPClientPool.ReadAddrWithWait(this.ep, 2, I2cAddr.Flag, 0x0000_0001, 0xFFFF_FFFF); + if (!ret.IsSuccessful) + { + logger.Error($"Failed to wait for I2C command completion: {ret.Error}"); + return new(ret.Error); + } + + if (!ret.Value) + { + logger.Error("ReadAddrWithWait for I2C command completion returned false"); + return new(new Exception("I2C command did not complete successfully")); + } + } + + return true; + } + + /// + /// 从指定I2C设备读取数据 + /// + /// I2C设备地址 + /// 要读取的数据长度 + /// I2C协议类型 + /// 操作结果,成功返回读取到的数据,否则返回异常信息 + public async ValueTask> ReadData(UInt32 devAddr, int length, I2cProtocol proto) + { + if (length <= 0 || length > 0x0000_FFFF) + { + logger.Error($"Read length {length} is invalid or exceeds maximum allowed 0x0000_FFFF"); + return new(new ArgumentException($"Read length {length} is invalid or exceeds maximum allowed 0x0000_FFFF")); + } + + // 清除UDP服务器接收缓冲区 + await MsgBus.UDPServer.ClearUDPData(this.address, 2); + + logger.Trace($"Clear up udp server {this.address} receive data"); + + // 配置本次传输数据量 + { + var ret = await UDPClientPool.WriteAddr(this.ep, 2, I2cAddr.TranConfig, ((uint)(length - 1))); + if (!ret.IsSuccessful) + { + logger.Error($"Failed to configure transfer length: {ret.Error}"); + return new(ret.Error); + } + + if (!ret.Value) + { + logger.Error("WriteAddr to TranConfig returned false"); + return new(new Exception("Failed to configure transfer length")); + } + } + + // 配置I2C地址、协议及启动传输(读操作) + { + var ret = await UDPClientPool.WriteAddr( + this.ep, 2, I2cAddr.BaseConfig, (devAddr << 1) | (1 << 8) | (((uint)proto) << 16) | (1 << 24)); + if (!ret.IsSuccessful) + { + logger.Error($"Failed to configure I2C address/protocol/start: {ret.Error}"); + return new(ret.Error); + } + + if (!ret.Value) + { + logger.Error("WriteAddr to BaseConfig returned false"); + return new(new Exception("Failed to configure I2C address/protocol/start")); + } + } + + // 等待I2C命令完成 + { + var ret = await UDPClientPool.ReadAddrWithWait(this.ep, 2, I2cAddr.Flag, 0x0000_0001, 0xFFFF_FFFF); + if (!ret.IsSuccessful) + { + logger.Error($"Failed to wait for I2C command completion: {ret.Error}"); + return new(ret.Error); + } + + if (!ret.Value) + { + logger.Error("ReadAddrWithWait for I2C command completion returned false"); + return new(new Exception("I2C command did not complete successfully")); + } + } + + // 读取数据 + { + var ret = await UDPClientPool.ReadAddr(this.ep, 2, I2cAddr.Read, length); + if (!ret.IsSuccessful) + { + logger.Error($"Failed to read data from I2C FIFO: {ret.Error}"); + return new(ret.Error); + } + + if (ret.Value.Options.Data == null || ret.Value.Options.Data.Length != length) + { + logger.Error($"ReadAddr returned unexpected data length: {ret.Value.Options.Data?.Length ?? 0}"); + return new(new Exception("Failed to read expected amount of data from I2C FIFO")); + } + + return ret.Value.Options.Data; + } + } +} diff --git a/server/src/Peripherals/JtagClient.cs b/server/src/Peripherals/JtagClient.cs index 14217b0..813455e 100644 --- a/server/src/Peripherals/JtagClient.cs +++ b/server/src/Peripherals/JtagClient.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Net; using DotNext; using Newtonsoft.Json; +using server; using WebProtocol; namespace Peripherals.JtagClient; diff --git a/server/src/Peripherals/OscilloscopeClient.cs b/server/src/Peripherals/OscilloscopeClient.cs new file mode 100644 index 0000000..3eeff22 --- /dev/null +++ b/server/src/Peripherals/OscilloscopeClient.cs @@ -0,0 +1,36 @@ +using System.Net; +using DotNext; + +namespace Peripherals.OscilloscopeClient; + +static class OscilloscopeAddr +{ + public const UInt32 Base = 0x0000_0000; +} + +class Oscilloscope +{ + private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); + + readonly int timeout = 2000; + + readonly int port; + readonly string address; + private IPEndPoint ep; + + /// + /// 初始化示波器客户端 + /// + /// 示波器设备IP地址 + /// 示波器设备端口 + /// 超时时间(毫秒) + public Oscilloscope(string address, int port, int timeout = 2000) + { + if (timeout < 0) + throw new ArgumentException("Timeout couldn't be negative", nameof(timeout)); + this.address = address; + this.port = port; + this.ep = new IPEndPoint(IPAddress.Parse(address), port); + this.timeout = timeout; + } +}