博主主要在WX写作,C站消息不能及时看见,如有需要联系请关注:《实在太懒于是不想取名》获取联系方式。
STM32N6作为意法半导体推出的首款集成自研神经处理单元的STM32产品以"MCU+NPU"的异构架构重新定义了边缘AI的算力边界,是意法半导体的MCU最前沿技术栈,不过由于其高难度技术应用以及需要的极其深厚的STM32使用经验以及神经网络基础概念,因此上手难度非常的高。

自从STM32N6发布以来,博主有幸获得一块STM32N6570-DK开发板,闲暇之余陆陆续续折腾如何开发。因此将会陆陆续续发表一些使用STM32N6的使用笔记,以供将来的使用者参考。
回顾学习历程,踩了很多很多的坑,在后续使用STM32N6的文章中也会向大家陆续介绍这些点。
上一期我们介绍了如何在 STM32N6 上使用 DCMIPP 读取 IMX335 摄像头的数据,并通过 LCD 显示屏实时显示摄像头图像。这已经是一个非常重要的基础步骤,让我们成功地把IMX335的原始Bayer数据流送进了MCU并显示了出来。

但是,正如大家所知,IMX335是一款专业级5MP CMOS图像传感器,它直接输出的RAW Bayer数据如果不经过适当的图像信号处理(ISP),画面通常会呈现出严重的偏色、对比度不足、噪声明显、动态范围有限等问题。直接显示的画面往往是"绿绿的"或"偏粉",这正是典型的未经过demosaic、白平衡、gamma校正等处理的 RAW 图表现。
要让IMX335真正发挥出专业水准的画质,我们必须对图像进行完整的ISP(Image Signal Processing)处理链路,包括但不限于:黑电平校正(Black Level Compensation)、坏点校正(Defective Pixel Correction)、去马赛克(Demosaicing)------从Bayer RGGB格局还原RGB、自动白平衡(AWB)、色彩校正(Color Correction Matrix, CCM)、伽马校正(Gamma Correction)、色彩增强/饱和度调整、降噪(Noise Reduction)、锐化(Sharpening)、镜头畸变校正(Lens Shading Correction / 周边光量补偿)、自动曝光(AE)与自动增益(AGC)控制
幸运的是,STM32N6系列集成了功能强大的硬件ISP模块,并且ST官方提供了配套的STM32 ISP中间件(STM32-MW-ISP),可以帮助我们快速实现上述大部分处理功能,而无需从零编写复杂的ISP算法。
本期重点:让IMX335的画面"正常且好看"
STM32_ISP_IQTune
ST提供了专业的ISP软件:STM32_ISP_IQTune来帮我我们设置CMOS相机参数:


我们可以从ST官网找到STM32_IQTune的Windows版本和用于STM32N6的程序。

利用STM32CubeProgrammer将ISP程序烧入到STM32N6570-DK开发板中,将开发板的USB线连接到PC上。


接着我们打开STM32_ISP_IQTune软件,可以连接到STM32N6570-DK开发板上:


进入配置界面,我们可以读取到STM32N6570-DK官方推荐的ISP参数,这里我们可以点击左边的Start tuning from scratch重置参数(非专业不推荐)。

设置完各个参数之后,调节到我们满意的图像界面,接着我们就可以导出我们的参数配置:

导出成.h版本后,就可以获得参数.h文件
代码修改
int32_tIMX335_Init(I2C_HandleTypeDef *hi2c, uint16_t DevAddr);
int32_tIMX335_ReadID(I2C_HandleTypeDef *hi2c, uint16_t DevAddr, uint32_t *Id);
int32_tIMX335_SetGain(I2C_HandleTypeDef *hi2c, uint16_t DevAddr, int32_t gain);
int32_tIMX335_SetExposure(I2C_HandleTypeDef *hi2c, uint16_t DevAddr, int32_t exposure);
int32_tIMX335_SetFrequency(I2C_HandleTypeDef *hi2c, uint16_t DevAddr, int32_t frequency);
int32_tIMX335_SetFramerate(I2C_HandleTypeDef *hi2c, uint16_t DevAddr, int32_t framerate);
int32_tIMX335_MirrorFlipConfig(I2C_HandleTypeDef *hi2c, uint16_t DevAddr, uint32_t Config);
int32_tIMX335_GetSensorInfo(IMX335_SensorInfo_t *Info);
int32_tIMX335_SetTestPattern(I2C_HandleTypeDef *hi2c, uint16_t DevAddr, int32_t mode);
上一期我们实现了IMX335的功能函数,包括传感器的基本初始化、ID 读取、增益/曝光控制、时钟频率与帧率设置、镜像翻转、测试图案输出,以及获取传感器基本信息等操作。

接着我们使用ST提供的ISP库,将库文件(.c/.h/.a)添加到我们的工程中:



ISP的API对接
#define IMX335_I2C_ADDRESS (0x1A << 1)
staticint32_t isp_gain = 0; // mdb 单位 (×1000)
staticint32_t isp_exposure = 0; // 微秒 us
int NbMainFrames = 0;
ISP_HandleTypeDef hcamera_isp;
/**
* @brief ISP Middleware helper: 获取传感器信息
*/
ISP_StatusTypeDef GetSensorInfoHelper(uint32_t Instance, ISP_SensorInfoTypeDef *SensorInfo)
{
(void)Instance; // 未使用参数
IMX335_SensorInfo_t info = {0};
if (IMX335_GetSensorInfo(&info) != IMX335_OK)
{
return ISP_ERR_SENSORINFO;
}
strncpy(SensorInfo->name, info.name, ISP_SENSOR_INFO_MAX_LENGTH - 1);
SensorInfo->name[ISP_SENSOR_INFO_MAX_LENGTH - 1] = '\0';
SensorInfo->bayer_pattern = info.bayer_pattern;
SensorInfo->color_depth = info.color_depth; // 10
SensorInfo->width = info.width; // 2592
SensorInfo->height = info.height; // 1944
SensorInfo->gain_min = info.gain_min; // mdb 单位
SensorInfo->gain_max = info.gain_max;
SensorInfo->exposure_min = info.exposure_min; // us
SensorInfo->exposure_max = info.exposure_max;
return ISP_OK;
}
/**
* @brief ISP Middleware helper: 设置传感器模拟增益
*/
ISP_StatusTypeDef SetSensorGainHelper(uint32_t Instance, int32_t Gain)
{
(void)Instance;
isp_gain = Gain;
if (IMX335_SetGain(&hi2c1, IMX335_I2C_ADDRESS, Gain) != IMX335_OK)
{
return ISP_ERR_SENSORGAIN;
}
return ISP_OK;
}
/**
* @brief ISP Middleware helper: 获取当前增益值(软件缓存)
*/
ISP_StatusTypeDef GetSensorGainHelper(uint32_t Instance, int32_t *Gain)
{
(void)Instance;
*Gain = isp_gain;
return ISP_OK;
}
/**
* @brief ISP Middleware helper: 设置传感器曝光时间(单位:微秒)
*/
ISP_StatusTypeDef SetSensorExposureHelper(uint32_t Instance, int32_t Exposure)
{
(void)Instance;
isp_exposure = Exposure;
if (IMX335_SetExposure(&hi2c1, IMX335_I2C_ADDRESS, Exposure) != IMX335_OK)
{
return ISP_ERR_SENSOREXPOSURE;
}
return ISP_OK;
}
/*
* @brief ISP Middleware helper: 获取当前曝光值(软件缓存)
*/
ISP_StatusTypeDef GetSensorExposureHelper(uint32_t Instance, int32_t *Exposure)
{
(void)Instance;
*Exposure = isp_exposure;
return ISP_OK;
}
接着将ISP的API和我们的IMX335的API对接,包括获取各类信息,设置各类参数的函数。
voidHAL_DCMIPP_PIPE_VsyncEventCallback(DCMIPP_HandleTypeDef *hdcmipp, uint32_t Pipe)
{
UNUSED(hdcmipp);
/* Update the frame counter and call the ISP statistics handler */
switch (Pipe)
{
case DCMIPP_PIPE0 :
ISP_IncDumpFrameId(&hcamera_isp);
break;
case DCMIPP_PIPE1 :
ISP_IncMainFrameId(&hcamera_isp);
ISP_GatherStatistics(&hcamera_isp);
NbMainFrames++;
break;
case DCMIPP_PIPE2 :
ISP_IncAncillaryFrameId(&hcamera_isp);
break;
}
}
添加HAL_DCMIPP_PIPE_VsyncEventCallback函数用来为DCMIPP实现VSYNC中断,在每帧画面接收完成后就会调用这个中断,更新帧计数、采集 ISP 统计数据、为自动曝光/白平衡等算法提供实时输入。
NbMainFrames用于帧计数。
voidDCMIPP_ISP_Init(void)
{
ISP_AppliHelpersTypeDef appliHelpers = {0};
ISP_StatAreaTypeDef statArea = {0};
// 填充 ISP middleware 需要的回调函数
appliHelpers.GetSensorInfo = GetSensorInfoHelper;
appliHelpers.SetSensorGain = SetSensorGainHelper;
appliHelpers.GetSensorGain = GetSensorGainHelper;
appliHelpers.SetSensorExposure = SetSensorExposureHelper;
appliHelpers.GetSensorExposure = GetSensorExposureHelper;
// 统计区域(全画幅)
statArea.X0 = 0;
statArea.Y0 = 0;
statArea.XSize = 2592;
statArea.YSize = 1944;
// 初始化 ISP 中间件
if (ISP_Init(&hcamera_isp, &hdcmipp, 0, &appliHelpers, &statArea, ISP_IQParamCacheInit[0]) != ISP_OK)
{
Error_Handler();
}
// 启动 DCMIPP CSI PIPE
if (HAL_DCMIPP_CSI_PIPE_Start(&hdcmipp, DCMIPP_PIPE1, DCMIPP_VIRTUAL_CHANNEL0,
(uint8_t *)lcd_bg_buffer, DCMIPP_MODE_CONTINUOUS) != HAL_OK)
{
Error_Handler();
}
// 启动 ISP 处理
if (ISP_Start(&hcamera_isp) != ISP_OK)
{
Error_Handler();
}
while (NbMainFrames < 60)
{
if (ISP_BackgroundProcess(&hcamera_isp) != ISP_OK)
{
HAL_GPIO_TogglePin(GPIOG, GPIO_PIN_10);
}
}
}
接着开始STM32的ISP处理,统计60帧的数据送入ISP处理算法。
至此,我们终于完成了从"原始 Bayer 数据"到"可观看彩色图像"的完整链路:IMX335 传感器 → DCMIPP 数据接收 → ISP 中间件处理 → LCD 显示输出。
通过对接 IMX335 的控制接口与 ST 提供的 ISP 中间件,我们实现了传感器信息获取、增益/曝光的动态设置,并在每一帧到来时通过 VSYNC 中断可靠地更新帧计数和采集统计数据。这一系列工作,让画面从最初的"绿油油""偏粉""噪点严重"逐步走向了"能看""基本正常"的阶段。
当然,目前的画面效果仍然主要依赖于 ST 官方示例中的默认IQ参数,距离"专业级""讨喜""适合实际应用"的水准还有一定距离。真正的画质飞跃,通常需要在 STM32_ISP_IQTune 软件中进行针对性的调参:调整黑电平、去马赛克算法、白平衡参考点、gamma 曲线、降噪强度、锐化程度等,并反复对比实拍效果,才能让 IMX335 在不同光照、不同场景下都呈现出令人满意的表现。
但无论如何,走到这一步已经是一个非常重要的里程碑------我们不再只是"把数据搬运到屏幕上",而是真正拥有了对图像信号处理链路的控制能力。这为后续更高级的功能奠定了基础。