1. 硬件连接与基础环境搭建
第一次用STM32F429驱动OV7725摄像头时,我对着开发板和摄像头模块的引脚发呆了半小时。后来发现,只要理清三类信号线,连接其实很简单。电源部分需要给OV7725提供3.3V供电,注意正负极别接反。我用的是带LDO稳压的摄像头模块,直接连开发板的3.3V输出就行。
数据接口是重点,OV7725的8位数据线(D0-D7)要对应连接到开发板的DCMI数据引脚。这里有个坑:不同型号开发板的DCMI引脚可能不同,我用的F429IGT6板子,数据线对应PE0-PE7这组GPIO。同步信号线中,PCLK接PE8,HREF接PE9,VSYNC接PE11。记得查清楚自己板子的引脚定义,有次我把VSYNC接到PE10导致一直收不到帧同步信号。
I2C配置接口比较简单,SCL和SDA分别接PB6和PB7,这是STM32的硬件I2C1默认引脚。不过实际调试时我发现硬件I2C经常卡在BUSY状态,后来改用软件模拟I2C反而更稳定。复位信号线建议接个GPIO方便硬复位摄像头,我用的PE12。
LTDC接口连接TFT屏时要注意电平匹配。我的4.3寸屏需要RGB565模式,所以只接了R0-R5、G0-G5、B0-B5这16根数据线。同步信号中,HSYNC接PI12,VSYNC接PI13,DE接PI14,时钟接PI15。第一次接线时把DE信号漏接了,导致屏幕一直花屏,排查了好久才发现问题。
2. CubeMX关键配置详解
在CubeMX里配置DCMI时,Mode选择让我纠结了很久。OV7725采用8位并行数据+独立同步信号,所以应该选"Slave 8 bits External Synchro"。有次手误选了Embedded模式,结果DMA收到的全是乱码。同步极性配置更要小心,根据OV7725的时序图,PCLK是上升沿有效,VSYNC低电平期间传输数据,HREF高电平有效。对应DCMI配置应该是:PCLK选Rising edge,VSYNC选Active high,HREF选Active low。
DMA配置建议用Circular模式实现连续传输。数据宽度选Word,因为DCMI会攒够4个字节才触发一次DMA传输。我一开始选Half-word导致图像错位。FIFO可以开启,Threshold设为1/2就行。有个隐藏坑点:DMA内存地址必须4字节对齐,我在定义缓冲区时加了__attribute__((aligned(4)))才解决偶尔花屏的问题。
LTDC配置更考验耐心。我的800x480屏幕参数如下:水平同步宽度30,后沿16,有效宽度480,前沿14;垂直同步宽度13,后沿10,有效宽度272,前沿2。这些参数必须严格按屏幕手册填写,有次把水平后沿填成160导致图像右移。时钟配置要注意,我最终采用25MHz像素时钟,通过PLLSAI分频实现。图层配置选择RGB565格式,记得开启图层并使能dithering。
时钟树配置是另一个容易翻车的地方。DCMI需要至少8MHz时钟,我通过PLL配置系统时钟180MHz,然后分频给DCMI。OV7725的XCLK我用了MCO输出24MHz,后来发现摄像头实际只需要12MHz,又加了分频器。建议先用示波器测量时钟信号,有次因为时钟不稳导致图像出现横纹。
3. OV7725寄存器调优实战
OV7725的寄存器配置简直是个迷宫,官方手册列出的上百个寄存器让我头皮发麻。经过多次试验,我总结出几个关键配置点。首先是输出格式寄存器COM7,设为0x46选择QVGA RGB565输出。窗口寄存器组中,HSTART=0x3F,HSIZE=0x50,VSTRT=0x03,VSIZE=0x78对应320x240的有效区域。
帧率控制很关键,CLKRC寄存器设为0x01实现12MHz输入时钟二分频。COM4=0x41开启PLL四倍频,配合COM5=0xF5的自动帧率控制,在光线不足时会自动降低帧率提升亮度。有次晚上调试发现帧率骤降,原来是这个功能在起作用。
图像质量调节更有意思。AWB_Ctrl0=0xF0设置白平衡,EDGE1=0x08增强锐度,UVADJ0=0x81调节色度。我最满意的画质参数是:BRIGHT=0x08提升亮度,CNST=0x20增加对比度,USAT=VSAT=0x65调整饱和度。这些值需要根据实际环境微调,实验室里完美的参数到窗边就可能过曝。
特殊场景还要特殊处理。逆光环境下,我把COM8设为0xFF开启强光抑制;夜间模式时,TGT_B=0x80提升暗部细节。最折腾的是色彩矩阵,MTX1-MTX6那组寄存器我调了整整两天才找到合适的肤色表现。建议先用默认值,等图像能正常显示了再慢慢优化。
4. 零缓存传输的核心代码实现
实现无缓存传输的关键在于精准的时序控制。我在DCMI的VSYNC中断中切换LTDC图层地址:
void HAL_DCMI_VsyncEventCallback(DCMI_HandleTypeDef *hdcmi) { static uint32_t toggle = 0; LTDC_LayerCfgTypeDef pLayerCfg; if(toggle){ pLayerCfg.FBStartAdress = (uint32_t)frame_buffer1; toggle = 0; }else{ pLayerCfg.FBStartAdress = (uint32_t)frame_buffer0; toggle = 1; } HAL_LTDC_ConfigLayer(&hltdc, &pLayerCfg, 0); }这里用了双缓冲机制,一个缓冲区正在采集时,另一个用于显示。注意帧缓冲区要定义在CCM内存或者DTCM区域,我有次把缓冲区放在普通SRAM导致DMA传输速度跟不上。
DMA配置有个重要细节:传输长度应该设为图像宽x高/2,因为每次传输32位数据包含2个像素。我的QVGA图像设置是320x240x2/4=38400。中断处理中还要注意临界区保护,我在切换缓冲区时短暂关闭中断,避免DMA传输被打断。
LTDC的图层配置也有技巧。除了设置地址,还要配置像素格式为RGB565,窗口尺寸匹配图像分辨率。我遇到过图像错位问题,最后发现是图层行长度寄存器(LTDC_LxWHPCR)没正确设置。Alpha值设为0xFF关闭混合,颜色键控也要禁用。
性能优化方面,开启D-Cache后要特别注意缓存一致性。我在DMA接收完成中断里调用SCB_CleanDCache_by_Addr清理缓存。有段时间图像偶尔出现撕裂,就是因为缓存数据没及时更新。另外,将LTDC和DCMI的DMA通道分配到不同流,可以避免总线冲突。