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的画面“正常且好看”
1、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文件
2、代码修改
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; // 微秒 usint 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] = '';SensorInfo->bayer_pattern = info.bayer_pattern;SensorInfo->color_depth = info.color_depth; // 10SensorInfo->width = info.width; // 2592SensorInfo->height = info.height; // 1944SensorInfo->gain_min = info.gain_min; // mdb 单位SensorInfo->gain_max = info.gain_max;SensorInfo->exposure_min = info.exposure_min; // usSensorInfo->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 PIPEif (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处理算法。
3、小结
至此,我们终于完成了从“原始 Bayer 数据”到“可观看彩色图像”的完整链路:IMX335 传感器 → DCMIPP 数据接收 → ISP 中间件处理 → LCD 显示输出。
通过对接 IMX335 的控制接口与 ST 提供的 ISP 中间件,我们实现了传感器信息获取、增益/曝光的动态设置,并在每一帧到来时通过 VSYNC 中断可靠地更新帧计数和采集统计数据。这一系列工作,让画面从最初的“绿油油”“偏粉”“噪点严重”逐步走向了“能看”“基本正常”的阶段。
当然,目前的画面效果仍然主要依赖于 ST 官方示例中的默认IQ参数,距离“专业级”“讨喜”“适合实际应用”的水准还有一定距离。真正的画质飞跃,通常需要在 STM32_ISP_IQTune 软件中进行针对性的调参:调整黑电平、去马赛克算法、白平衡参考点、gamma 曲线、降噪强度、锐化程度等,并反复对比实拍效果,才能让 IMX335 在不同光照、不同场景下都呈现出令人满意的表现。
但无论如何,走到这一步已经是一个非常重要的里程碑——我们不再只是“把数据搬运到屏幕上”,而是真正拥有了对图像信号处理链路的控制能力。这为后续更高级的功能奠定了基础。
301