fix: 跑通摄像头640x480配置

This commit is contained in:
alivender 2025-07-12 18:24:25 +08:00
parent f253a33c83
commit e25f08739a
4 changed files with 120 additions and 98 deletions

View File

@ -72,7 +72,7 @@ public class Image
var b8 = (byte)((b5 * 255) / 31); // 5位扩展到8位
// 存储到 RGB24 数组
var rgb24Index = i * 3;
var rgb24Index = (i%2 == 0)?((i+1) * 3):((i-1) * 3);
rgb24Data[rgb24Index] = r8; // R
rgb24Data[rgb24Index + 1] = g8; // G
rgb24Data[rgb24Index + 2] = b8; // B

View File

@ -9,7 +9,8 @@ static class CameraAddr
public const UInt32 STORE_ADDR = BASE + 0x12;
public const UInt32 STORE_NUM = BASE + 0x13;
public const UInt32 CAPTURE_ON = BASE + 0x14;
public const UInt32 EXPECTED_VH = BASE + 0x14;
public const UInt32 CAPTURE_ON = BASE + 0x15;
}
class Camera
@ -17,7 +18,7 @@ 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;
@ -25,27 +26,25 @@ class Camera
const uint CAM_I2C_ADDR = 0x3C;
const Peripherals.I2cClient.I2cProtocol CAM_PROTO = Peripherals.I2cClient.I2cProtocol.SCCB;
const UInt16 ISP_INPUT_SIZE_X = 2624;
const UInt16 ISP_INPUT_SIZE_Y = 1952;
const UInt16 X_OFFSET = 16;
const UInt16 Y_OFFSET = 4;
const UInt16 X_OUTPUT_SIZE = 640;
const UInt16 Y_OUTPUT_SIZE = 480;
const UInt16 X_TOTAL_OUTPUT_SIZE = 2844;
const UInt16 Y_TOTAL_OUTPUT_SIZE = 1968;
const UInt16 PISYCAL_PIXEL_START_X = 336;
const UInt16 PISYCAL_PIXEL_START_Y = 434;
const UInt16 PISYCAL_PIXEL_END_X = PISYCAL_PIXEL_START_X + ISP_INPUT_SIZE_X - 1;
const UInt16 PISYCAL_PIXEL_END_Y = PISYCAL_PIXEL_START_Y + ISP_INPUT_SIZE_Y - 1;
const byte PLL_MUX = 105;
const UInt16 H_START = 0; //default: 0
const UInt16 V_START = 0; //default: 0
const UInt16 DVPHO = 640; //default: 2592 (0xA20)
const UInt16 DVPVO = 480; //default: 1944 (0x798)
const UInt16 H_END = H_START + 1500 - 1; //default: 2624-1 (0xA3F)
const UInt16 V_END = V_START + 1300 - 1; //default: 1951-1 (0x79F)
const UInt16 HTS = 1700; //default: 2844 (0xB1C)
const UInt16 VTS = 1500; //default: 1968 (0x7B0)
const UInt16 H_OFFSET = 16; //default: 16 (0x10)
const UInt16 V_OFFSET = 4; //default: 4 (0x04)
const byte PLL_MUX = 10;
const UInt32 FrameAddr = 0x00;
const UInt32 FrameLength = X_OUTPUT_SIZE * Y_OUTPUT_SIZE * 16 / 32;
const UInt32 FrameLength = DVPHO * DVPVO * 16 / 32;
static byte[][] InitCmdData = new byte[][] {
// Stop streaming
new byte[] { 0x30, 0x08, 0x42 },
new byte[] { 0x30, 0x08, 0x82 }, //82是复位
// delay 5ms
new byte[] { 0x30, 0x08, 0x42 }, //42是休眠
new byte[] { 0x31, 0x03, 0x02 },
new byte[] { 0x30, 0x17, 0xff },
new byte[] { 0x30, 0x18, 0xff },
@ -83,30 +82,30 @@ class Camera
new byte[] { 0x3c, 0x04, 0x28 },
new byte[] { 0x3c, 0x05, 0x98 },
new byte[] { 0x3c, 0x06, 0x00 },
new byte[] { 0x3c, 0x07, 0x07 },
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 },
// X_OFFSET/Y_OFFSET
new byte[] { 0x38, 0x10, unchecked((byte)((X_OFFSET >> 8) & 0xFF)) },
new byte[] { 0x38, 0x11, unchecked((byte)(X_OFFSET & 0xFF)) },
new byte[] { 0x38, 0x12, unchecked((byte)((Y_OFFSET >> 8) & 0xFF)) },
// H_OFFSET/V_OFFSET
new byte[] { 0x38, 0x10, unchecked((byte)((H_OFFSET >> 8) & 0xFF)) },
new byte[] { 0x38, 0x11, unchecked((byte)(H_OFFSET & 0xFF)) }, //0010
new byte[] { 0x38, 0x12, unchecked((byte)((V_OFFSET >> 8) & 0xFF)) },
new byte[] { 0x37, 0x08, 0x64 },
new byte[] { 0x40, 0x01, 0x02 },
new byte[] { 0x40, 0x05, 0x1a },
new byte[] { 0x30, 0x00, 0x00 },
new byte[] { 0x30, 0x04, 0xff },
new byte[] { 0x43, 0x00, 0x00 }, // RAW: BGGR
new byte[] { 0x50, 0x1f, 0x03 }, // ISP RAW DPC
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 },
new byte[] { 0x50, 0x00, 0x06 },
new byte[] { 0x3a, 0x0f, 0x78 },
new byte[] { 0x3a, 0x10, 0x68 },
new byte[] { 0x3a, 0x1b, 0x78 },
new byte[] { 0x3a, 0x1e, 0x68 },
new byte[] { 0x3a, 0x11, 0xD0 },
new byte[] { 0x3a, 0x1f, 0x40 },
new byte[] { 0x50, 0x00, 0xA7 }, //ISP控制
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
// LENC
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 },
@ -164,65 +163,66 @@ class Camera
new byte[] { 0x53, 0x0a, 0x30 }, new byte[] { 0x53, 0x0b, 0x04 }, new byte[] { 0x53, 0x0c, 0x06 },
new byte[] { 0x50, 0x25, 0x00 },
// 系统时钟分频
new byte[] { 0x30, 0x35, 0x21 }, // 30fps
new byte[] { 0x30, 0x35, 0x11 }, // 30fps
new byte[] { 0x30, 0x36, PLL_MUX }, // PLL倍频
new byte[] { 0x3c, 0x07, 0x08 },
// 时序控制
new byte[] { 0x38, 0x20, 0x41 }, // vflip
new byte[] { 0x38, 0x21, 0x00 }, // mirror
new byte[] { 0x38, 0x14, 0x11 }, // timing X inc
new byte[] { 0x38, 0x15, 0x11 }, // timing Y inc
// PISYCAL_PIXEL_START_X/Y
new byte[] { 0x38, 0x00, unchecked((byte)((PISYCAL_PIXEL_START_X >> 8) & 0xFF)) },
new byte[] { 0x38, 0x01, unchecked((byte)(PISYCAL_PIXEL_START_X & 0xFF)) },
new byte[] { 0x38, 0x02, unchecked((byte)((PISYCAL_PIXEL_START_Y >> 8) & 0xFF)) },
new byte[] { 0x38, 0x03, unchecked((byte)(PISYCAL_PIXEL_START_Y & 0xFF)) },
// PISYCAL_PIXEL_END_X/Y
new byte[] { 0x38, 0x04, unchecked((byte)((PISYCAL_PIXEL_END_X >> 8) & 0xFF)) },
new byte[] { 0x38, 0x05, unchecked((byte)(PISYCAL_PIXEL_END_X & 0xFF)) },
new byte[] { 0x38, 0x06, unchecked((byte)((PISYCAL_PIXEL_END_Y >> 8) & 0xFF)) },
new byte[] { 0x38, 0x07, unchecked((byte)(PISYCAL_PIXEL_END_Y & 0xFF)) },
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
// H_START/Y
new byte[] { 0x38, 0x00, unchecked((byte)((H_START >> 8) & 0xFF)) },
new byte[] { 0x38, 0x01, unchecked((byte)(H_START & 0xFF)) },
new byte[] { 0x38, 0x02, unchecked((byte)((V_START >> 8) & 0xFF)) },
new byte[] { 0x38, 0x03, unchecked((byte)(V_START & 0xFF)) },
// H_END/Y
new byte[] { 0x38, 0x04, unchecked((byte)((H_END >> 8) & 0xFF)) },
new byte[] { 0x38, 0x05, unchecked((byte)(H_END & 0xFF)) },
new byte[] { 0x38, 0x06, unchecked((byte)((V_END >> 8) & 0xFF)) },
new byte[] { 0x38, 0x07, unchecked((byte)(V_END & 0xFF)) },
// 输出像素个数
new byte[] { 0x38, 0x08, unchecked((byte)((X_OUTPUT_SIZE >> 8) & 0xFF)) },
new byte[] { 0x38, 0x09, unchecked((byte)(X_OUTPUT_SIZE & 0xFF)) },
new byte[] { 0x38, 0x0A, unchecked((byte)((Y_OUTPUT_SIZE >> 8) & 0xFF)) },
new byte[] { 0x38, 0x0B, unchecked((byte)(Y_OUTPUT_SIZE & 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)((X_TOTAL_OUTPUT_SIZE >> 8) & 0xFF)) },
new byte[] { 0x38, 0x0D, unchecked((byte)(X_TOTAL_OUTPUT_SIZE & 0xFF)) },
new byte[] { 0x38, 0x0E, unchecked((byte)((Y_TOTAL_OUTPUT_SIZE >> 8) & 0xFF)) },
new byte[] { 0x38, 0x0F, unchecked((byte)(Y_TOTAL_OUTPUT_SIZE & 0xFF)) },
new byte[] { 0x38, 0x13, unchecked((byte)(Y_OFFSET & 0xFF)) }, // Timing Voffset
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)) },
new byte[] { 0x38, 0x13, unchecked((byte)(V_OFFSET & 0xFF)) }, // Timing Voffset
new byte[] { 0x36, 0x18, 0x00 },
new byte[] { 0x36, 0x12, 0x29 },
new byte[] { 0x37, 0x09, 0x52 },
new byte[] { 0x37, 0x0c, 0x03 },
new byte[] { 0x3a, 0x02, 0x17 },
new byte[] { 0x3a, 0x03, 0x10 },
new byte[] { 0x3a, 0x14, 0x17 },
new byte[] { 0x3a, 0x15, 0x10 },
new byte[] { 0x40, 0x04, 0x02 },
new byte[] { 0x47, 0x13, 0x00 },
new byte[] { 0x44, 0x07, 0x04 },
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[] { 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 },
new byte[] { 0x38, 0x24, 0x02 },
new byte[] { 0x50, 0x01, 0x80 }, // 关闭缩放
new byte[] { 0x3b, 0x07, 0x0a },
new byte[] { 0x48, 0x37, 0x22 }, //DVP CLK divider
new byte[] { 0x38, 0x24, 0x02 }, //DVP CLK divider
new byte[] { 0x50, 0x01, 0xA3 }, //ISP 控制
new byte[] { 0x3b, 0x07, 0x0a }, //帧曝光模式
// 彩条测试使能
new byte[] { 0x50, 0x3d, 0x00 },
new byte[] { 0x47, 0x41, 0x00 },
// 闪光灯禁用
new byte[] { 0x30, 0x16, 0x00 },
new byte[] { 0x30, 0x1c, 0x00 },
new byte[] { 0x30, 0x19, 0x00 },
new byte[] { 0x30, 0x31, 0x08 },
new byte[] { 0x30, 0x2c, 0xC2 },
new byte[] { 0x46, 0x00, 0xA0 },
new byte[] { 0x30, 0x16, 0x02 },
new byte[] { 0x30, 0x1c, 0x02 },
new byte[] { 0x30, 0x19, 0x02 }, //开启闪光灯
new byte[] { 0x30, 0x19, 0x00 }, //关闭闪光灯
// new byte[] { 0x30, 0x2c, 0xC2 }, //控制驱动能力的
// new byte[] { 0x46, 0x00, 0xA0 },
// for subsample=OFF
new byte[] { 0x36, 0x18, 0x04 },
new byte[] { 0x36, 0x12, 0x2b },
new byte[] { 0x37, 0x09, 0x12 },
new byte[] { 0x37, 0x0c, 0x00 },
// new byte[] { 0x36, 0x18, 0x04 },
// new byte[] { 0x36, 0x12, 0x2b },
// new byte[] { 0x37, 0x09, 0x12 },
// new byte[] { 0x37, 0x0c, 0x00 },
// Start streaming
new byte[] { 0x30, 0x08, 0x02 }
};
@ -247,7 +247,7 @@ class Camera
public async ValueTask<Result<bool>> Init()
{
{
var ret = await UDPClientPool.WriteAddr(this.ep, 2, CameraAddr.STORE_ADDR, FrameAddr);
var ret = await UDPClientPool.WriteAddr(this.ep, this.taskID, CameraAddr.STORE_ADDR, FrameAddr);
if (!ret.IsSuccessful)
{
logger.Error($"Failed to write STORE_ADDR to camera at {this.address}:{this.port}, error: {ret.Error}");
@ -261,7 +261,7 @@ class Camera
}
{
var ret = await UDPClientPool.WriteAddr(this.ep, 2, CameraAddr.STORE_NUM, FrameLength);
var ret = await UDPClientPool.WriteAddr(this.ep, this.taskID, CameraAddr.STORE_NUM, FrameLength);
if (!ret.IsSuccessful)
{
logger.Error($"Failed to write STORE_NUM to camera at {this.address}:{this.port}, error: {ret.Error}");
@ -274,7 +274,21 @@ class Camera
}
}
var i2c = new Peripherals.I2cClient.I2c(this.address, this.port, this.timeout);
{
var ret = await UDPClientPool.WriteAddr(this.ep, this.taskID, CameraAddr.EXPECTED_VH, (DVPVO)<<16 | (DVPHO*2));
if (!ret.IsSuccessful)
{
logger.Error($"Failed to write EXPECTED_VH to camera at {this.address}:{this.port}, error: {ret.Error}");
return new(ret.Error);
}
if (!ret.Value)
{
logger.Error($"EXPECTED_VH write returned false for camera at {this.address}:{this.port}");
return new(new Exception($"EXPECTED_VH write returned false for camera at {this.address}:{this.port}"));
}
}
var i2c = new Peripherals.I2cClient.I2c(this.address, this.port, this.taskID, this.timeout);
foreach (var cmd in InitCmdData)
{
@ -291,6 +305,12 @@ class Camera
logger.Error($"I2C write 0x{CAM_I2C_ADDR.ToString("X")} returned false: {BitConverter.ToString(cmd)}");
return false;
}
if (cmd[0] == 0x30 && cmd[1] == 0x08 && cmd[2] == 0x82)
{
// 复位命令等待5MS
await Task.Delay(5);
}
else await Task.Delay(5); // 其他命令延时1ms
}
return true;
@ -298,7 +318,7 @@ class Camera
public async ValueTask<Result<bool>> EnableCamera(bool isEnable)
{
var ret = await UDPClientPool.WriteAddr(this.ep, 2, CameraAddr.CAPTURE_ON, Convert.ToUInt32(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}");
@ -319,14 +339,14 @@ class Camera
public async ValueTask<Result<byte[]>> ReadFrame()
{
// 清除UDP服务器接收缓冲区
await MsgBus.UDPServer.ClearUDPData(this.address, 2);
await MsgBus.UDPServer.ClearUDPData(this.address, this.taskID);
logger.Trace($"Clear up udp server {this.address} receive data");
// 使用UDPClientPool读取图像帧数据
var result = await UDPClientPool.ReadAddr4Bytes(
this.ep,
2, // taskID
this.taskID, // taskID
FrameAddr,
((int)FrameLength),
this.timeout);

View File

@ -69,7 +69,7 @@ public class I2c
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;
@ -79,13 +79,15 @@ public class I2c
/// </summary>
/// <param name="address">[TODO:parameter]</param>
/// <param name="port">[TODO:parameter]</param>
/// <param name="taskID">[TODO:parameter]</param>
/// <param name="timeout">[TODO:parameter]</param>
/// <returns>[TODO:return]</returns>
public I2c(string address, int port, int timeout = 2000)
public I2c(string address, int port, int taskID,int timeout = 2000)
{
if (timeout < 0)
throw new ArgumentException("Timeout couldn't be negative", nameof(timeout));
this.address = address;
this.taskID = taskID;
this.port = port;
this.ep = new IPEndPoint(IPAddress.Parse(address), port);
this.timeout = timeout;
@ -107,7 +109,7 @@ public class I2c
}
// 清除UDP服务器接收缓冲区
await MsgBus.UDPServer.ClearUDPData(this.address, 2);
await MsgBus.UDPServer.ClearUDPData(this.address, this.taskID);
logger.Trace($"Clear up udp server {this.address} receive data");
@ -123,7 +125,7 @@ public class I2c
i2cData[i++] = item;
}
var ret = await UDPClientPool.WriteAddr(this.ep, 2, I2cAddr.Write, i2cData);
var ret = await UDPClientPool.WriteAddr(this.ep, this.taskID, I2cAddr.Write, i2cData);
if (!ret.IsSuccessful)
{
logger.Error($"Failed to write data to I2C FIFO: {ret.Error}");
@ -139,7 +141,7 @@ public class I2c
// 配置本次传输数据量
{
var ret = await UDPClientPool.WriteAddr(this.ep, 2, I2cAddr.TranConfig, ((uint)(data.Length - 1)));
var ret = await UDPClientPool.WriteAddr(this.ep, this.taskID, I2cAddr.TranConfig, ((uint)(data.Length - 1)));
if (!ret.IsSuccessful)
{
logger.Error($"Failed to configure transfer length: {ret.Error}");
@ -156,7 +158,7 @@ public class I2c
// 配置I2C地址、协议及启动传输
{
var ret = await UDPClientPool.WriteAddr(
this.ep, 2, I2cAddr.BaseConfig, (devAddr) | (((uint)proto) << 16) | (1 << 24));
this.ep, this.taskID, I2cAddr.BaseConfig, (devAddr) | (((uint)proto) << 16) | (1 << 24));
if (!ret.IsSuccessful)
{
logger.Error($"Failed to configure I2C address/protocol/start: {ret.Error}");
@ -172,7 +174,7 @@ public class I2c
// 等待I2C命令完成
{
var ret = await UDPClientPool.ReadAddrWithWait(this.ep, 2, I2cAddr.Flag, 0x0000_0001, 0xFFFF_FFFF);
var ret = await UDPClientPool.ReadAddrWithWait(this.ep, this.taskID, I2cAddr.Flag, 0x0000_0001, 0xFFFF_FFFF);
if (!ret.IsSuccessful)
{
logger.Error($"Failed to wait for I2C command completion: {ret.Error}");
@ -205,13 +207,13 @@ public class I2c
}
// 清除UDP服务器接收缓冲区
await MsgBus.UDPServer.ClearUDPData(this.address, 2);
await MsgBus.UDPServer.ClearUDPData(this.address, this.taskID);
logger.Trace($"Clear up udp server {this.address} receive data");
// 配置本次传输数据量
{
var ret = await UDPClientPool.WriteAddr(this.ep, 2, I2cAddr.TranConfig, ((uint)(length - 1)));
var ret = await UDPClientPool.WriteAddr(this.ep, this.taskID, I2cAddr.TranConfig, ((uint)(length - 1)));
if (!ret.IsSuccessful)
{
logger.Error($"Failed to configure transfer length: {ret.Error}");
@ -228,7 +230,7 @@ public class I2c
// 配置I2C地址、协议及启动传输读操作
{
var ret = await UDPClientPool.WriteAddr(
this.ep, 2, I2cAddr.BaseConfig, (devAddr) | (1 << 8) | (((uint)proto) << 16) | (1 << 24));
this.ep, this.taskID, I2cAddr.BaseConfig, (devAddr) | (1 << 8) | (((uint)proto) << 16) | (1 << 24));
if (!ret.IsSuccessful)
{
logger.Error($"Failed to configure I2C address/protocol/start: {ret.Error}");
@ -244,7 +246,7 @@ public class I2c
// 等待I2C命令完成
{
var ret = await UDPClientPool.ReadAddrWithWait(this.ep, 2, I2cAddr.Flag, 0x0000_0001, 0xFFFF_FFFF);
var ret = await UDPClientPool.ReadAddrWithWait(this.ep, this.taskID, I2cAddr.Flag, 0x0000_0001, 0xFFFF_FFFF);
if (!ret.IsSuccessful)
{
logger.Error($"Failed to wait for I2C command completion: {ret.Error}");
@ -260,7 +262,7 @@ public class I2c
// 读取数据
{
var ret = await UDPClientPool.ReadAddr(this.ep, 2, I2cAddr.Read, length);
var ret = await UDPClientPool.ReadAddr(this.ep, this.taskID, I2cAddr.Read, length);
if (!ret.IsSuccessful)
{
logger.Error($"Failed to read data from I2C FIFO: {ret.Error}");

View File

@ -627,7 +627,7 @@ public class HttpVideoStreamService : BackgroundService
}
// 将 RGB565 转换为 RGB24
var rgb24Result = Common.Image.ConvertRGB565ToRGB24(rgb565Data, _frameWidth, _frameHeight);
var rgb24Result = Common.Image.ConvertRGB565ToRGB24(rgb565Data, _frameWidth, _frameHeight, isLittleEndian: false);
if (!rgb24Result.IsSuccessful)
{
logger.Error("RGB565转RGB24失败: {Error}", rgb24Result.Error);