FPGA_WebLab/server/src/Peripherals/CameraClient.cs

894 lines
33 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Net;
using DotNext;
namespace Peripherals.CameraClient;
static class CameraAddr
{
public const UInt32 BASE = 0x7000_0000;
public const UInt32 STORE_ADDR = BASE + 0x12;
public const UInt32 STORE_NUM = BASE + 0x13;
public const UInt32 EXPECTED_VH = BASE + 0x14;
public const UInt32 CAPTURE_ON = BASE + 0x15;
}
class Camera
{
private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
readonly int timeout = 2000;
readonly int taskID;
readonly int port;
readonly string address;
private IPEndPoint ep;
const uint CAM_I2C_ADDR = 0x3C;
const Peripherals.I2cClient.I2cProtocol CAM_PROTO = Peripherals.I2cClient.I2cProtocol.SCCB;
const byte PLL_MUX = 10;
const UInt32 FrameAddr = 0x00;
// 动态分辨率参数
private UInt16 _currentWidth = 640;
private UInt16 _currentHeight = 480;
private UInt32 _currentFrameLength = 640 * 480 * 2 / 4; // RGB565格式2字节/像素按4字节对齐
/// <summary>
/// 初始化摄像头客户端
/// </summary>
/// <param name="address">摄像头设备IP地址</param>
/// <param name="port">摄像头设备端口</param>
/// <param name="timeout">超时时间(毫秒)</param>
public Camera(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;
}
public async ValueTask<Result<bool>> Init()
{
// 步骤1: 复位
var resetResult = await Reset();
if (!resetResult.IsSuccessful) return resetResult;
// 步骤2: 休眠
var sleepResult = await Sleep();
if (!sleepResult.IsSuccessful) return sleepResult;
// 步骤3: 配置基础寄存器
var basicResult = await ConfigureBasicRegisters();
if (!basicResult.IsSuccessful) return basicResult;
// 步骤4: 配置传感器控制
var sensorResult = await ConfigureSensorControl();
if (!sensorResult.IsSuccessful) return sensorResult;
// 步骤5: 配置模拟控制
var analogResult = await ConfigureAnalogControl();
if (!analogResult.IsSuccessful) return analogResult;
// 步骤6: 配置时钟控制
var clockResult = await ConfigureClockControl();
if (!clockResult.IsSuccessful) return clockResult;
// 步骤7: 配置PSRAM控制
var psramResult = await ConfigurePSRAMControl();
if (!psramResult.IsSuccessful) return psramResult;
// 步骤8: 配置DVP时序
var dvpResult = await ConfigureDVPTiming();
if (!dvpResult.IsSuccessful) return dvpResult;
// 步骤9: 配置基础控制
var baseResult = await ConfigureBaseControl();
if (!baseResult.IsSuccessful) return baseResult;
// 步骤10: 配置图像格式
var formatResult = await ConfigureImageFormat();
if (!formatResult.IsSuccessful) return formatResult;
// 步骤11: 配置ISP控制
var ispResult = await ConfigureISPControl();
if (!ispResult.IsSuccessful) return ispResult;
// 步骤12: 配置AEC
var aecResult = await ConfigureAEC();
if (!aecResult.IsSuccessful) return aecResult;
// 步骤13: 配置LENC
var lencResult = await ConfigureLENC();
if (!lencResult.IsSuccessful) return lencResult;
// 步骤14: 配置AWB
var awbResult = await ConfigureAWB();
if (!awbResult.IsSuccessful) return awbResult;
// 步骤15: 配置Gamma
var gammaResult = await ConfigureGamma();
if (!gammaResult.IsSuccessful) return gammaResult;
// 步骤16: 配置CMX
var cmxResult = await ConfigureCMX();
if (!cmxResult.IsSuccessful) return cmxResult;
// 步骤17: 配置SDE
var sdeResult = await ConfigureSDE();
if (!sdeResult.IsSuccessful) return sdeResult;
// 步骤18: 配置CIP
var cipResult = await ConfigureCIP();
if (!cipResult.IsSuccessful) return cipResult;
// 步骤19: 配置时序控制
var timingResult = await ConfigureTimingControl();
if (!timingResult.IsSuccessful) return timingResult;
// 步骤20: 配置测试和闪光灯
var testResult = await ConfigureTestAndFlash();
if (!testResult.IsSuccessful) return testResult;
// 步骤21: 配置分辨率默认640x480
// var resolutionResult = await ConfigureResolution640x480();
var resolutionResult = await ConfigureResolution1280x720();
if (!resolutionResult.IsSuccessful) return resolutionResult;
// 步骤22: 开始流
var startResult = await StartStreaming();
if (!startResult.IsSuccessful) return startResult;
return true;
}
public async ValueTask<Result<bool>> EnableCamera(bool isEnable)
{
var ret = await UDPClientPool.WriteAddr(this.ep, this.taskID, CameraAddr.CAPTURE_ON, Convert.ToUInt32(isEnable));
if (!ret.IsSuccessful)
{
logger.Error($"Failed to write CAPTURE_ON to camera at {this.address}:{this.port}, error: {ret.Error}");
return new(ret.Error);
}
if (!ret.Value)
{
logger.Error($"CAPTURE_ON write returned false for camera at {this.address}:{this.port}");
return false;
}
return true;
}
/// <summary>
/// 读取一帧图像数据
/// </summary>
/// <returns>包含图像数据的字节数组</returns>
public async ValueTask<Result<byte[]>> ReadFrame()
{
// 只在第一次或出错时清除UDP缓冲区避免每帧都清除造成延迟
// await MsgBus.UDPServer.ClearUDPData(this.address, this.taskID);
logger.Trace($"Reading frame from camera {this.address}");
// 使用UDPClientPool读取图像帧数据
var result = await UDPClientPool.ReadAddr4Bytes(
this.ep,
this.taskID, // taskID
FrameAddr,
(int)(_currentWidth * _currentHeight * 2), // 使用当前分辨率的动态大小
this.timeout);
if (!result.IsSuccessful)
{
logger.Error($"Failed to read frame from camera {this.address}:{this.port}, error: {result.Error}");
// 读取失败时清除缓冲区,为下次读取做准备
try
{
await MsgBus.UDPServer.ClearUDPData(this.address, this.taskID);
}
catch (Exception ex)
{
logger.Warn($"Failed to clear UDP data after read error: {ex.Message}");
}
return new(result.Error);
}
logger.Trace($"Successfully read frame from camera {this.address}:{this.port}, data length: {result.Value.Length} bytes");
return result.Value;
}
/// <summary>
/// 批量配置I2C寄存器
/// </summary>
/// <param name="registerTable">寄存器配置表每个元素包含3个字节[地址高位, 地址低位, 数据]</param>
/// <param name="customDelayMs">自定义延时时间毫秒如果为null则使用默认延时逻辑</param>
/// <returns>配置结果</returns>
public async ValueTask<Result<bool>> ConfigureRegisters(byte[][] registerTable, int? customDelayMs = null)
{
var i2c = new Peripherals.I2cClient.I2c(this.address, this.port, this.taskID, this.timeout);
foreach (var cmd in registerTable)
{
if (cmd.Length != 3)
{
logger.Error($"Invalid register command length: {cmd.Length}, expected 3 bytes");
return new(new ArgumentException($"Invalid register command length: {cmd.Length}, expected 3 bytes"));
}
var ret = await i2c.WriteData(CAM_I2C_ADDR, cmd, CAM_PROTO);
if (!ret.IsSuccessful)
{
logger.Error($"I2C write 0x{CAM_I2C_ADDR.ToString("X")} failed: {BitConverter.ToString(cmd)} error: {ret.Error}");
return new(ret.Error);
}
if (!ret.Value)
{
logger.Error($"I2C write 0x{CAM_I2C_ADDR.ToString("X")} returned false: {BitConverter.ToString(cmd)}");
return false;
}
// 处理延时逻辑
if (customDelayMs.HasValue)
{
await Task.Delay(customDelayMs.Value);
}
else
{
// 使用默认延时逻辑
if (cmd[0] == 0x30 && cmd[1] == 0x08 && cmd[2] == 0x82)
{
// 复位命令等待5MS
await Task.Delay(5);
}
else
{
await Task.Delay(3); // 其他命令延时3ms
}
}
}
return true;
}
/// <summary>
/// 配置摄像头分辨率和相关参数
/// </summary>
/// <param name="hStart">水平起始位置</param>
/// <param name="vStart">垂直起始位置</param>
/// <param name="dvpHo">输出水平像素数</param>
/// <param name="dvpVo">输出垂直像素数</param>
/// <param name="hts">水平总像素数</param>
/// <param name="vts">垂直总像素数</param>
/// <param name="hOffset">水平偏移</param>
/// <param name="vOffset">垂直偏移</param>
/// <param name="hWindow">水平窗口大小默认1500</param>
/// <param name="vWindow">垂直窗口大小默认1300</param>
/// <returns>配置结果</returns>
public async ValueTask<Result<bool>> ConfigureResolution(
UInt16 hStart, UInt16 vStart,
UInt16 dvpHo, UInt16 dvpVo,
UInt16 hts, UInt16 vts,
UInt16 hOffset, UInt16 vOffset,
UInt16 hWindow = 1500, UInt16 vWindow = 1300)
{
// 计算结束位置
UInt16 hEnd = (UInt16)(hStart + hWindow - 1);
UInt16 vEnd = (UInt16)(vStart + vWindow - 1);
// 计算帧长度
UInt32 frameLength = (UInt32)(dvpHo * dvpVo * 16 / 32);
// 1. 配置UDP相关寄存器
{
var ret = await UDPClientPool.WriteAddr(this.ep, this.taskID, CameraAddr.STORE_ADDR, FrameAddr);
if (!ret.IsSuccessful)
{
logger.Error($"Failed to write STORE_ADDR: {ret.Error}");
return new(ret.Error);
}
if (!ret.Value)
{
logger.Error("STORE_ADDR write returned false");
return new(new Exception("STORE_ADDR write returned false"));
}
}
{
var ret = await UDPClientPool.WriteAddr(this.ep, this.taskID, CameraAddr.STORE_NUM, frameLength);
if (!ret.IsSuccessful)
{
logger.Error($"Failed to write STORE_NUM: {ret.Error}");
return new(ret.Error);
}
if (!ret.Value)
{
logger.Error("STORE_NUM write returned false");
return new(new Exception("STORE_NUM write returned false"));
}
}
{
var ret = await UDPClientPool.WriteAddr(this.ep, this.taskID, CameraAddr.EXPECTED_VH, ((uint)dvpVo) << 16 | ((uint)dvpHo * 2));
if (!ret.IsSuccessful)
{
logger.Error($"Failed to write EXPECTED_VH: {ret.Error}");
return new(ret.Error);
}
if (!ret.Value)
{
logger.Error("EXPECTED_VH write returned false");
return new(new Exception("EXPECTED_VH write returned false"));
}
}
// 2. 配置I2C寄存器
var resolutionRegisters = new byte[][]
{
// H_OFFSET/V_OFFSET
new byte[] { 0x38, 0x10, unchecked((byte)((hOffset >> 8) & 0xFF)) },
new byte[] { 0x38, 0x11, unchecked((byte)(hOffset & 0xFF)) },
new byte[] { 0x38, 0x12, unchecked((byte)((vOffset >> 8) & 0xFF)) },
// H_START/V_START
new byte[] { 0x38, 0x00, unchecked((byte)((hStart >> 8) & 0xFF)) },
new byte[] { 0x38, 0x01, unchecked((byte)(hStart & 0xFF)) },
new byte[] { 0x38, 0x02, unchecked((byte)((vStart >> 8) & 0xFF)) },
new byte[] { 0x38, 0x03, unchecked((byte)(vStart & 0xFF)) },
// H_END/V_END
new byte[] { 0x38, 0x04, unchecked((byte)((hEnd >> 8) & 0xFF)) },
new byte[] { 0x38, 0x05, unchecked((byte)(hEnd & 0xFF)) },
new byte[] { 0x38, 0x06, unchecked((byte)((vEnd >> 8) & 0xFF)) },
new byte[] { 0x38, 0x07, unchecked((byte)(vEnd & 0xFF)) },
// 输出像素个数
new byte[] { 0x38, 0x08, unchecked((byte)((dvpHo >> 8) & 0xFF)) },
new byte[] { 0x38, 0x09, unchecked((byte)(dvpHo & 0xFF)) },
new byte[] { 0x38, 0x0A, unchecked((byte)((dvpVo >> 8) & 0xFF)) },
new byte[] { 0x38, 0x0B, unchecked((byte)(dvpVo & 0xFF)) },
// 总像素
new byte[] { 0x38, 0x0C, unchecked((byte)((hts >> 8) & 0xFF)) },
new byte[] { 0x38, 0x0D, unchecked((byte)(hts & 0xFF)) },
new byte[] { 0x38, 0x0E, unchecked((byte)((vts >> 8) & 0xFF)) },
new byte[] { 0x38, 0x0F, unchecked((byte)(vts & 0xFF)) },
// Timing Voffset
new byte[] { 0x38, 0x13, unchecked((byte)(vOffset & 0xFF)) }
};
var configResult = await ConfigureRegisters(resolutionRegisters, customDelayMs: 1);
if (!configResult.IsSuccessful)
{
logger.Error($"Failed to configure resolution registers: {configResult.Error}");
return configResult;
}
logger.Info($"Successfully configured resolution: {dvpHo}x{dvpVo}, HTS={hts}, VTS={vts}");
return true;
}
/// <summary>
/// 配置为640x480分辨率默认配置
/// </summary>
/// <returns>配置结果</returns>
public async ValueTask<Result<bool>> ConfigureResolution640x480()
{
return await ConfigureResolution(
hStart: 0, vStart: 0,
dvpHo: 640, dvpVo: 480,
hts: 1700, vts: 1500,
hOffset: 16, vOffset: 4
);
}
/// <summary>
/// 配置为320x240分辨率
/// </summary>
/// <returns>配置结果</returns>
public async ValueTask<Result<bool>> ConfigureResolution320x240()
{
return await ConfigureResolution(
hStart: 0, vStart: 0,
dvpHo: 320, dvpVo: 240,
hts: 850, vts: 750,
hOffset: 16, vOffset: 4,
hWindow: 750, vWindow: 650
);
}
/// <summary>
/// 配置为1280x720分辨率
/// </summary>
/// <returns>配置结果</returns>
public async ValueTask<Result<bool>> ConfigureResolution1280x720()
{
return await ConfigureResolution(
hStart: 0, vStart: 0,
dvpHo: 1280, dvpVo: 720,
hts: 2844, vts: 1968,
hOffset: 16, vOffset: 4,
hWindow: 2592, vWindow: 1944
);
}
/// <summary>
/// 切换摄像头分辨率
/// </summary>
/// <param name="width">宽度</param>
/// <param name="height">高度</param>
/// <returns>配置结果</returns>
public async ValueTask<Result<bool>> ChangeResolution(int width, int height)
{
try
{
logger.Info($"正在切换摄像头分辨率到 {width}x{height}");
Result<bool> result;
switch ($"{width}x{height}")
{
case "640x480":
result = await ConfigureResolution640x480();
break;
case "1280x720":
result = await ConfigureResolution1280x720();
break;
default:
logger.Error($"不支持的分辨率: {width}x{height}");
return new(new ArgumentException($"不支持的分辨率: {width}x{height}"));
}
if (result.IsSuccessful)
{
_currentWidth = (UInt16)width;
_currentHeight = (UInt16)height;
_currentFrameLength = (UInt32)(width * height * 2 / 4); // RGB565格式按4字节对齐
logger.Info($"摄像头分辨率已切换到 {width}x{height}");
}
return result;
}
catch (Exception ex)
{
logger.Error(ex, $"切换分辨率到 {width}x{height} 时发生错误");
return new(ex);
}
}
/// <summary>
/// 获取当前分辨率
/// </summary>
/// <returns>当前分辨率(宽度, 高度)</returns>
public (int Width, int Height) GetCurrentResolution()
{
return (_currentWidth, _currentHeight);
}
/// <summary>
/// 获取当前帧长度
/// </summary>
/// <returns>当前帧长度</returns>
public UInt32 GetCurrentFrameLength()
{
return _currentFrameLength;
}
/// <summary>
/// 复位摄像头
/// </summary>
/// <returns>配置结果</returns>
public async ValueTask<Result<bool>> Reset()
{
var resetRegisters = new byte[][]
{
new byte[] { 0x30, 0x08, 0x82 } // 复位命令
};
return await ConfigureRegisters(resetRegisters, customDelayMs: 5); // 复位后等待5ms
}
/// <summary>
/// 设置摄像头为休眠模式
/// </summary>
/// <returns>配置结果</returns>
public async ValueTask<Result<bool>> Sleep()
{
var sleepRegisters = new byte[][]
{
new byte[] { 0x30, 0x08, 0x42 } // 休眠命令
};
return await ConfigureRegisters(sleepRegisters);
}
/// <summary>
/// 配置基础寄存器
/// </summary>
/// <returns>配置结果</returns>
public async ValueTask<Result<bool>> ConfigureBasicRegisters()
{
var basicRegisters = new byte[][]
{
new byte[] { 0x31, 0x03, 0x02 },
new byte[] { 0x30, 0x17, 0xff },
new byte[] { 0x30, 0x18, 0xff },
new byte[] { 0x30, 0x37, 0x13 },
new byte[] { 0x31, 0x08, 0x01 }
};
return await ConfigureRegisters(basicRegisters);
}
/// <summary>
/// 配置传感器控制寄存器
/// </summary>
/// <returns>配置结果</returns>
public async ValueTask<Result<bool>> ConfigureSensorControl()
{
var sensorRegisters = new byte[][]
{
new byte[] { 0x36, 0x30, 0x36 },
new byte[] { 0x36, 0x31, 0x0e },
new byte[] { 0x36, 0x32, 0xe2 },
new byte[] { 0x36, 0x33, 0x12 },
new byte[] { 0x36, 0x21, 0xe0 }
};
return await ConfigureRegisters(sensorRegisters);
}
/// <summary>
/// 配置模拟控制寄存器
/// </summary>
/// <returns>配置结果</returns>
public async ValueTask<Result<bool>> ConfigureAnalogControl()
{
var analogRegisters = new byte[][]
{
new byte[] { 0x37, 0x04, 0xa0 },
new byte[] { 0x37, 0x03, 0x5a },
new byte[] { 0x37, 0x15, 0x78 },
new byte[] { 0x37, 0x17, 0x01 },
new byte[] { 0x37, 0x0b, 0x60 },
new byte[] { 0x37, 0x05, 0x1a }
};
return await ConfigureRegisters(analogRegisters);
}
/// <summary>
/// 配置时钟控制寄存器
/// </summary>
/// <returns>配置结果</returns>
public async ValueTask<Result<bool>> ConfigureClockControl()
{
var clockRegisters = new byte[][]
{
new byte[] { 0x39, 0x05, 0x02 },
new byte[] { 0x39, 0x06, 0x10 },
new byte[] { 0x39, 0x01, 0x0a },
new byte[] { 0x30, 0x35, 0x11 }, // 30fps
new byte[] { 0x30, 0x36, PLL_MUX } // PLL倍频
};
return await ConfigureRegisters(clockRegisters);
}
/// <summary>
/// 配置PSRAM控制寄存器
/// </summary>
/// <returns>配置结果</returns>
public async ValueTask<Result<bool>> ConfigurePSRAMControl()
{
var psramRegisters = new byte[][]
{
new byte[] { 0x37, 0x31, 0x12 },
new byte[] { 0x36, 0x00, 0x08 },
new byte[] { 0x36, 0x01, 0x33 },
new byte[] { 0x30, 0x2d, 0x60 },
new byte[] { 0x36, 0x20, 0x52 },
new byte[] { 0x37, 0x1b, 0x20 },
new byte[] { 0x47, 0x1c, 0x50 }
};
return await ConfigureRegisters(psramRegisters);
}
/// <summary>
/// 配置DVP时序寄存器
/// </summary>
/// <returns>配置结果</returns>
public async ValueTask<Result<bool>> ConfigureDVPTiming()
{
var dvpRegisters = new byte[][]
{
new byte[] { 0x3a, 0x13, 0x43 },
new byte[] { 0x3a, 0x18, 0x00 },
new byte[] { 0x3a, 0x19, 0xf8 },
new byte[] { 0x36, 0x35, 0x13 },
new byte[] { 0x36, 0x36, 0x03 },
new byte[] { 0x36, 0x34, 0x40 },
new byte[] { 0x36, 0x22, 0x01 }
};
return await ConfigureRegisters(dvpRegisters);
}
/// <summary>
/// 配置基础控制寄存器
/// </summary>
/// <returns>配置结果</returns>
public async ValueTask<Result<bool>> ConfigureBaseControl()
{
var baseRegisters = new byte[][]
{
new byte[] { 0x3c, 0x01, 0x34 },
new byte[] { 0x3c, 0x04, 0x28 },
new byte[] { 0x3c, 0x05, 0x98 },
new byte[] { 0x3c, 0x06, 0x00 },
new byte[] { 0x3c, 0x07, 0x08 },
new byte[] { 0x3c, 0x08, 0x00 },
new byte[] { 0x3c, 0x09, 0x1c },
new byte[] { 0x3c, 0x0a, 0x9c },
new byte[] { 0x3c, 0x0b, 0x40 },
new byte[] { 0x37, 0x08, 0x64 }
};
return await ConfigureRegisters(baseRegisters);
}
/// <summary>
/// 配置图像格式寄存器
/// </summary>
/// <returns>配置结果</returns>
public async ValueTask<Result<bool>> ConfigureImageFormat()
{
var formatRegisters = new byte[][]
{
new byte[] { 0x40, 0x01, 0x02 },
new byte[] { 0x40, 0x05, 0x1a },
new byte[] { 0x30, 0x00, 0x00 },
new byte[] { 0x30, 0x04, 0xff },
new byte[] { 0x43, 0x00, 0x6F }, // RGB565:first byte:{g[2:0],b[4:0]],second byte:{r[4:0],g[5:3]}
new byte[] { 0x50, 0x1f, 0x01 }, // Format: ISP RGB
new byte[] { 0x44, 0x0e, 0x00 }
};
return await ConfigureRegisters(formatRegisters);
}
/// <summary>
/// 配置ISP控制寄存器
/// </summary>
/// <returns>配置结果</returns>
public async ValueTask<Result<bool>> ConfigureISPControl()
{
var ispRegisters = new byte[][]
{
new byte[] { 0x50, 0x00, 0xA7 }, // ISP控制
new byte[] { 0x50, 0x01, 0xA3 } // ISP控制
};
return await ConfigureRegisters(ispRegisters);
}
/// <summary>
/// 配置AEC自动曝光控制寄存器
/// </summary>
/// <returns>配置结果</returns>
public async ValueTask<Result<bool>> ConfigureAEC()
{
var aecRegisters = new byte[][]
{
new byte[] { 0x3a, 0x0f, 0x30 }, // AEC控制;stable range in high
new byte[] { 0x3a, 0x10, 0x28 }, // AEC控制;stable range in low
new byte[] { 0x3a, 0x1b, 0x30 }, // AEC控制;stable range out high
new byte[] { 0x3a, 0x1e, 0x26 }, // AEC控制;stable range out low
new byte[] { 0x3a, 0x11, 0x60 }, // AEC控制; fast zone high
new byte[] { 0x3a, 0x1f, 0x14 }, // AEC控制; fast zone low
new byte[] { 0x3a, 0x02, 0x17 }, // 60Hz max exposure
new byte[] { 0x3a, 0x03, 0x10 }, // 60Hz max exposure
new byte[] { 0x3a, 0x14, 0x17 }, // 50Hz max exposure
new byte[] { 0x3a, 0x15, 0x10 }, // 50Hz max exposure
new byte[] { 0x3b, 0x07, 0x0a } // 帧曝光模式
};
return await ConfigureRegisters(aecRegisters);
}
/// <summary>
/// 配置LENC镜头校正寄存器
/// </summary>
/// <returns>配置结果</returns>
public async ValueTask<Result<bool>> ConfigureLENC()
{
var lencRegisters = new byte[][]
{
new byte[] { 0x58, 0x00, 0x23 }, new byte[] { 0x58, 0x01, 0x14 }, new byte[] { 0x58, 0x02, 0x0f },
new byte[] { 0x58, 0x03, 0x0f }, new byte[] { 0x58, 0x04, 0x12 }, new byte[] { 0x58, 0x05, 0x26 },
new byte[] { 0x58, 0x06, 0x0c }, new byte[] { 0x58, 0x07, 0x08 }, new byte[] { 0x58, 0x08, 0x05 },
new byte[] { 0x58, 0x09, 0x05 }, new byte[] { 0x58, 0x0a, 0x08 }, new byte[] { 0x58, 0x0b, 0x0d },
new byte[] { 0x58, 0x0c, 0x08 }, new byte[] { 0x58, 0x0d, 0x03 }, new byte[] { 0x58, 0x0e, 0x00 },
new byte[] { 0x58, 0x0f, 0x00 }, new byte[] { 0x58, 0x10, 0x03 }, new byte[] { 0x58, 0x11, 0x09 },
new byte[] { 0x58, 0x12, 0x07 }, new byte[] { 0x58, 0x13, 0x03 }, new byte[] { 0x58, 0x14, 0x00 },
new byte[] { 0x58, 0x15, 0x01 }, new byte[] { 0x58, 0x16, 0x03 }, new byte[] { 0x58, 0x17, 0x08 },
new byte[] { 0x58, 0x18, 0x0d }, new byte[] { 0x58, 0x19, 0x08 }, new byte[] { 0x58, 0x1a, 0x05 },
new byte[] { 0x58, 0x1b, 0x06 }, new byte[] { 0x58, 0x1c, 0x08 }, new byte[] { 0x58, 0x1d, 0x0e },
new byte[] { 0x58, 0x1e, 0x29 }, new byte[] { 0x58, 0x1f, 0x17 }, new byte[] { 0x58, 0x20, 0x11 },
new byte[] { 0x58, 0x21, 0x11 }, new byte[] { 0x58, 0x22, 0x15 }, new byte[] { 0x58, 0x23, 0x28 },
new byte[] { 0x58, 0x24, 0x46 }, new byte[] { 0x58, 0x25, 0x26 }, new byte[] { 0x58, 0x26, 0x08 },
new byte[] { 0x58, 0x27, 0x26 }, new byte[] { 0x58, 0x28, 0x64 }, new byte[] { 0x58, 0x29, 0x26 },
new byte[] { 0x58, 0x2a, 0x24 }, new byte[] { 0x58, 0x2b, 0x22 }, new byte[] { 0x58, 0x2c, 0x24 },
new byte[] { 0x58, 0x2d, 0x24 }, new byte[] { 0x58, 0x2e, 0x06 }, new byte[] { 0x58, 0x2f, 0x22 },
new byte[] { 0x58, 0x30, 0x40 }, new byte[] { 0x58, 0x31, 0x42 }, new byte[] { 0x58, 0x32, 0x24 },
new byte[] { 0x58, 0x33, 0x26 }, new byte[] { 0x58, 0x34, 0x24 }, new byte[] { 0x58, 0x35, 0x22 },
new byte[] { 0x58, 0x36, 0x22 }, new byte[] { 0x58, 0x37, 0x26 }, new byte[] { 0x58, 0x38, 0x44 },
new byte[] { 0x58, 0x39, 0x24 }, new byte[] { 0x58, 0x3a, 0x26 }, new byte[] { 0x58, 0x3b, 0x28 },
new byte[] { 0x58, 0x3c, 0x42 }, new byte[] { 0x58, 0x3d, 0xce }
};
return await ConfigureRegisters(lencRegisters);
}
/// <summary>
/// 配置摄像头AWB自动白平衡寄存器
/// </summary>
/// <returns>配置结果</returns>
public async ValueTask<Result<bool>> ConfigureAWB()
{
var awbRegisters = new byte[][]
{
new byte[] { 0x51, 0x80, 0xff }, new byte[] { 0x51, 0x81, 0xf2 }, new byte[] { 0x51, 0x82, 0x00 },
new byte[] { 0x51, 0x83, 0x14 }, new byte[] { 0x51, 0x84, 0x25 }, new byte[] { 0x51, 0x85, 0x24 },
new byte[] { 0x51, 0x86, 0x09 }, new byte[] { 0x51, 0x87, 0x09 }, new byte[] { 0x51, 0x88, 0x09 },
new byte[] { 0x51, 0x89, 0x75 }, new byte[] { 0x51, 0x8a, 0x54 }, new byte[] { 0x51, 0x8b, 0xe0 },
new byte[] { 0x51, 0x8c, 0xb2 }, new byte[] { 0x51, 0x8d, 0x42 }, new byte[] { 0x51, 0x8e, 0x3d },
new byte[] { 0x51, 0x8f, 0x56 }, new byte[] { 0x51, 0x90, 0x46 }, new byte[] { 0x51, 0x91, 0xf8 },
new byte[] { 0x51, 0x92, 0x04 }, new byte[] { 0x51, 0x93, 0x70 }, new byte[] { 0x51, 0x94, 0xf0 },
new byte[] { 0x51, 0x95, 0xf0 }, new byte[] { 0x51, 0x96, 0x03 }, new byte[] { 0x51, 0x97, 0x01 },
new byte[] { 0x51, 0x98, 0x04 }, new byte[] { 0x51, 0x99, 0x12 }, new byte[] { 0x51, 0x9a, 0x04 },
new byte[] { 0x51, 0x9b, 0x00 }, new byte[] { 0x51, 0x9c, 0x06 }, new byte[] { 0x51, 0x9d, 0x82 },
new byte[] { 0x51, 0x9e, 0x38 }
};
return await ConfigureRegisters(awbRegisters, customDelayMs: 2);
}
/// <summary>
/// 配置摄像头Gamma校正寄存器
/// </summary>
/// <returns>配置结果</returns>
public async ValueTask<Result<bool>> ConfigureGamma()
{
var gammaRegisters = new byte[][]
{
new byte[] { 0x54, 0x80, 0x01 }, new byte[] { 0x54, 0x81, 0x08 }, new byte[] { 0x54, 0x82, 0x14 },
new byte[] { 0x54, 0x83, 0x28 }, new byte[] { 0x54, 0x84, 0x51 }, new byte[] { 0x54, 0x85, 0x65 },
new byte[] { 0x54, 0x86, 0x71 }, new byte[] { 0x54, 0x87, 0x7d }, new byte[] { 0x54, 0x88, 0x87 },
new byte[] { 0x54, 0x89, 0x91 }, new byte[] { 0x54, 0x8a, 0x9a }, new byte[] { 0x54, 0x8b, 0xaa },
new byte[] { 0x54, 0x8c, 0xb8 }, new byte[] { 0x54, 0x8d, 0xcd }, new byte[] { 0x54, 0x8e, 0xdd },
new byte[] { 0x54, 0x8f, 0xea }, new byte[] { 0x54, 0x90, 0x1d }
};
return await ConfigureRegisters(gammaRegisters);
}
/// <summary>
/// 配置CMX色彩矩阵寄存器
/// </summary>
/// <returns>配置结果</returns>
public async ValueTask<Result<bool>> ConfigureCMX()
{
var cmxRegisters = new byte[][]
{
new byte[] { 0x53, 0x81, 0x1e }, new byte[] { 0x53, 0x82, 0x5b }, new byte[] { 0x53, 0x83, 0x08 },
new byte[] { 0x53, 0x84, 0x0a }, new byte[] { 0x53, 0x85, 0x7e }, new byte[] { 0x53, 0x86, 0x88 },
new byte[] { 0x53, 0x87, 0x7c }, new byte[] { 0x53, 0x88, 0x6c }, new byte[] { 0x53, 0x89, 0x10 },
new byte[] { 0x53, 0x8a, 0x01 }, new byte[] { 0x53, 0x8b, 0x98 }
};
return await ConfigureRegisters(cmxRegisters);
}
/// <summary>
/// 配置SDE特殊数字效果寄存器
/// </summary>
/// <returns>配置结果</returns>
public async ValueTask<Result<bool>> ConfigureSDE()
{
var sdeRegisters = new byte[][]
{
new byte[] { 0x55, 0x80, 0x06 }, new byte[] { 0x55, 0x83, 0x40 }, new byte[] { 0x55, 0x84, 0x10 },
new byte[] { 0x55, 0x89, 0x10 }, new byte[] { 0x55, 0x8a, 0x00 }, new byte[] { 0x55, 0x8b, 0xf8 },
new byte[] { 0x50, 0x1d, 0x40 }
};
return await ConfigureRegisters(sdeRegisters);
}
/// <summary>
/// 配置CIP颜色插值处理寄存器
/// </summary>
/// <returns>配置结果</returns>
public async ValueTask<Result<bool>> ConfigureCIP()
{
var cipRegisters = new byte[][]
{
new byte[] { 0x53, 0x00, 0x08 }, new byte[] { 0x53, 0x01, 0x30 }, new byte[] { 0x53, 0x02, 0x10 },
new byte[] { 0x53, 0x03, 0x00 }, new byte[] { 0x53, 0x04, 0x08 }, new byte[] { 0x53, 0x05, 0x30 },
new byte[] { 0x53, 0x06, 0x08 }, new byte[] { 0x53, 0x07, 0x16 }, new byte[] { 0x53, 0x09, 0x08 },
new byte[] { 0x53, 0x0a, 0x30 }, new byte[] { 0x53, 0x0b, 0x04 }, new byte[] { 0x53, 0x0c, 0x06 },
new byte[] { 0x50, 0x25, 0x00 }
};
return await ConfigureRegisters(cipRegisters);
}
/// <summary>
/// 配置时序控制寄存器
/// </summary>
/// <returns>配置结果</returns>
public async ValueTask<Result<bool>> ConfigureTimingControl()
{
var timingRegisters = new byte[][]
{
new byte[] { 0x38, 0x20, 0x46 }, // vflip
new byte[] { 0x38, 0x21, 0x01 }, // mirror
new byte[] { 0x38, 0x14, 0x31 }, // timing X inc
new byte[] { 0x38, 0x15, 0x31 }, // timing Y inc
new byte[] { 0x36, 0x18, 0x00 },
new byte[] { 0x36, 0x12, 0x29 },
new byte[] { 0x37, 0x09, 0x52 },
new byte[] { 0x37, 0x0c, 0x03 }
};
return await ConfigureRegisters(timingRegisters);
}
/// <summary>
/// 配置测试模式和闪光灯寄存器
/// </summary>
/// <returns>配置结果</returns>
public async ValueTask<Result<bool>> ConfigureTestAndFlash()
{
var testRegisters = new byte[][]
{
new byte[] { 0x40, 0x04, 0x02 }, // BLC(背光) 2 lines
new byte[] { 0x47, 0x13, 0x03 }, // JPEG mode 3
new byte[] { 0x44, 0x07, 0x04 }, // 量化标度
new byte[] { 0x46, 0x0c, 0x20 },
new byte[] { 0x48, 0x37, 0x22 }, // DVP CLK divider
new byte[] { 0x38, 0x24, 0x02 }, // DVP CLK divider
// 彩条测试禁用
new byte[] { 0x50, 0x3d, 0x00 },
new byte[] { 0x47, 0x41, 0x00 },
// 闪光灯配置
new byte[] { 0x30, 0x16, 0x02 },
new byte[] { 0x30, 0x1c, 0x02 },
new byte[] { 0x30, 0x19, 0x02 }, // 开启闪光灯
new byte[] { 0x30, 0x19, 0x00 } // 关闭闪光灯
};
return await ConfigureRegisters(testRegisters);
}
/// <summary>
/// 开始流媒体传输
/// </summary>
/// <returns>配置结果</returns>
public async ValueTask<Result<bool>> StartStreaming()
{
var startRegisters = new byte[][]
{
new byte[] { 0x30, 0x08, 0x02 } // 开始流
};
return await ConfigureRegisters(startRegisters);
}
}