本帖最后由 FuShenxiao 于 2024-10-7 11:55 编辑
转接板设计
观察开发板原理图摄像头接口可以发现,接口居然有30pin,这和软排线连接的OV5640(24pin)或者OV5640模块(2*9pin)显然是不相同的。
恰好我手头有一块正点原子OV5640摄像头模块,于是我就针对它完成转接板的设计。
观察正点原子OV5640摄像头模块原理图可以轻易地与STM32H7S78-DK的摄像头接口相对应。
I2C1_SCL ---> OV_SCL
I2C1_SDA ---> OV_SDA
DCMI_D0 ~ DMCI_D7 ---> OV_D0 ~ OV_D7
RSTI ---> OV_RESET
PWR_EN ---> OV_PWDN
DCMI_VSYNC ---> OV_VSYNC
DCMI_HSYNC ---> OV_HREF
DCMI_PIXCLK ---> OV_PCLK
在嘉立创中绘制原理图如下
绘制PCB如下。为了保证相关电学特性稳定,需要在OV5640供电端加入100nF的去耦电容,在SCL和SDA两个IIC接口上加入4.7kΩ的上拉电阻。
最终得到PCB正反面如下图所示
具体工程文件参见STM32H7S78-DK摄像头转接板 - 立创开源硬件平台 (oshwhub.com)
引脚配置
首先在CubeMX中配置时钟(这里不再赘述)
接着分别配置DCMIPP(摄像头接口)和LTDC(显示屏接口)
配置两个指示灯
完成以上操作后,生成代码
代码编写
OV5640初始化代码,需要注意的是,OV5640默认原始图像是上下翻转的,所以要使用OV5640_FLIP将图像翻转
static uint32_t OV5640_Config(uint32_t Resolution, uint32_t PixelFormat)
{
OV5640_IO_t IOCtx;
uint32_t id;
uint32_t ret = OV5640_OK;
static OV5640_Object_t OV5640Obj;
/* Configure the Camera driver */
IOCtx.Address = CAMERA_OV5640_ADDRESS;
IOCtx.Init = BSP_I2C1_Init;
IOCtx.DeInit = BSP_I2C1_DeInit;
IOCtx.ReadReg = BSP_I2C1_ReadReg16;
IOCtx.WriteReg = BSP_I2C1_WriteReg16;
IOCtx.GetTick = BSP_GetTick;
/* Register Bus IO */
if(OV5640_RegisterBusIO (&OV5640Obj, &IOCtx) != OV5640_OK)
{
ret = OV5640_ERROR;
}
/* Read ID */
if(OV5640_ReadID(&OV5640Obj, &id) != OV5640_OK)
{
ret = OV5640_ERROR;
}
if(id == OV5640_ID)
{
/* Initialize the camera Module */
Camera_Drv = (CAMERA_Drv_t *) &OV5640_CAMERA_Driver;
OV5640_DeInit(&OV5640Obj);
if(Camera_Drv->Init(&OV5640Obj, Resolution, PixelFormat) != OV5640_OK)
{
ret = OV5640_ERROR;
}
else if(Camera_Drv->MirrorFlipConfig(&OV5640Obj, OV5640_FLIP) != OV5640_OK)
{
ret = OV5640_ERROR;
}
}
return ret;
}
在主函数中使用如下代码完成OV5640初始化
if(OV5640_Config(OV5640_R480x272, OV5640_RGB565) != OV5640_OK)
{
/* Camera Module Config KO */
Error_Handler();
}
DCMIPP接口获取数据信息
HAL_StatusTypeDef HAL_DCMIPP_PIPE_Start(DCMIPP_HandleTypeDef *hdcmipp, uint32_t Pipe, uint32_t DstAddress,
uint32_t CaptureMode)
{
assert_param(IS_DCMIPP_PIPE(Pipe));
assert_param(IS_DCMIPP_CAPTURE_MODE(CaptureMode));
/* Check pointer validity */
if ((hdcmipp == NULL) || ((DstAddress & 0xFU) != 0U))
{
return HAL_ERROR;
}
/* Check DCMIPP pipe state */
if (hdcmipp->PipeState[Pipe] != HAL_DCMIPP_PIPE_STATE_READY)
{
return HAL_ERROR;
}
/* Set Capture Mode and Destination address for the selected pipe */
DCMIPP_SetConfig(hdcmipp, Pipe, DstAddress, CaptureMode);
/* Enable Capture for the selected Pipe */
DCMIPP_EnableCapture(hdcmipp, Pipe);
return HAL_OK;
}
在主函数中用如下代码完成DCMIPP接口数据接收,将接收到的图像信息传到CAMERA_FRAME_BUFFER中。CAMERA_FRAME_BUFFER后面的传参为当前运行模式,除了可以选择DCMIPP_MODE_CONTINUOUS,还可以使用DCMIPP_MODE_SNAPSHOT
if(HAL_DCMIPP_PIPE_Start(&phdcmipp, DCMIPP_PIPE0, (uint32_t)CAMERA_FRAME_BUFFER , DCMIPP_MODE_CONTINUOUS) != HAL_OK)
{
Error_Handler();
}
也可以使用双缓冲模式,代码如下
HAL_StatusTypeDef HAL_DCMIPP_PIPE_DoubleBufferStart(DCMIPP_HandleTypeDef *hdcmipp, uint32_t Pipe, uint32_t DstAddress0,
uint32_t DstAddress1, uint32_t CaptureMode)
{
assert_param(IS_DCMIPP_PIPE(Pipe));
assert_param(IS_DCMIPP_CAPTURE_MODE(CaptureMode));
/* Check pointer validity */
if ((hdcmipp == NULL) || ((DstAddress0 & 0xFU) != 0U) || ((DstAddress1 & 0xFU) != 0U))
{
return HAL_ERROR;
}
/* Check DCMIPP pipe state */
if (hdcmipp->PipeState[Pipe] != HAL_DCMIPP_PIPE_STATE_READY)
{
return HAL_ERROR;
}
/* Set Capture Mode and Destination addresses for the selected pipe */
DCMIPP_SetDBMConfig(hdcmipp, Pipe, DstAddress0, DstAddress1, CaptureMode);
/* Enable Capture for the selected Pipe */
DCMIPP_EnableCapture(hdcmipp, Pipe);
return HAL_OK;
}
最后在显示屏上显示图像,在这一步中,需要将CAMERA_FRAME_BUFFER的数据显示到屏幕的指定区域内
static void MX_LTDC_Init(void)
{
/* USER CODE BEGIN LTDC_Init 0 */
/* USER CODE END LTDC_Init 0 */
LTDC_LayerCfgTypeDef pLayerCfg = {0};
/* USER CODE BEGIN LTDC_Init 1 */
/* USER CODE END LTDC_Init 1 */
hltdc.Instance = LTDC;
hltdc.Init.HSPolarity = LTDC_HSPOLARITY_AL;
hltdc.Init.VSPolarity = LTDC_VSPOLARITY_AL;
hltdc.Init.DEPolarity = LTDC_DEPOLARITY_AL;
hltdc.Init.PCPolarity = LTDC_PCPOLARITY_IPC;
hltdc.Init.HorizontalSync = 3;
hltdc.Init.VerticalSync = 3;
hltdc.Init.AccumulatedHBP = 11;
hltdc.Init.AccumulatedVBP = 11;
hltdc.Init.AccumulatedActiveW = 811;
hltdc.Init.AccumulatedActiveH = 491;
hltdc.Init.TotalWidth = 819;
hltdc.Init.TotalHeigh = 499;
hltdc.Init.Backcolor.Blue = 0;
hltdc.Init.Backcolor.Green = 0;
hltdc.Init.Backcolor.Red = 0;
if (HAL_LTDC_Init(&hltdc) != HAL_OK)
{
Error_Handler();
}
pLayerCfg.WindowX0 = xpos;
pLayerCfg.WindowX1 = xpos + ImageWidth;
pLayerCfg.WindowY0 = ypos;
pLayerCfg.WindowY1 = ypos + ImageHeight;
pLayerCfg.PixelFormat = LTDC_PIXEL_FORMAT_RGB565;
pLayerCfg.Alpha = 255;
pLayerCfg.Alpha0 = 0;
pLayerCfg.BlendingFactor1 = LTDC_BLENDING_FACTOR1_PAxCA;
pLayerCfg.BlendingFactor2 = LTDC_BLENDING_FACTOR2_PAxCA;
pLayerCfg.FBStartAdress = (uint32_t)CAMERA_FRAME_BUFFER;
pLayerCfg.ImageWidth = ImageWidth;
pLayerCfg.ImageHeight = ImageHeight;
pLayerCfg.Backcolor.Blue = 0;
pLayerCfg.Backcolor.Green = 0;
pLayerCfg.Backcolor.Red = 0;
if (HAL_LTDC_ConfigLayer(&hltdc, &pLayerCfg, 0) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN LTDC_Init 2 */
/* USER CODE END LTDC_Init 2 */
}
结果展示
当程序正常运行时,LD1会持续闪烁,OV5640中的内容也会实时传输到屏幕上,传输显示的过程还是较为流畅的。
不过噪声的问题似乎有些严重,面对比较白和亮的地方会产生一些紫色的点点,但是对着暗色调的就不会产生这个问题(比如我的书包,桌子在椅背的阴影部分)。考虑PCB绘制部分,网上对OV5640的建议是信号线之间的线长差距小于100mil,这个我符合要求。再考虑软排线的长度,10cm在诸多网友看来似乎确实有些长了,下一步尝试换短一点的软排线再试试。
不过让我费解的是,官方的板子既然做了这个接口,为什么不顺便再做一个适配的摄像头呢,就像STM32MP135F-DK开发板还配套摄像头模块。而且这个30pin的接口根本无法适配OV5640,还得自己画转接板。
E60298581D98084D62290AA6065AE7CA
程序源码:
DCMIPP_ContinuousMode.rar
(503.26 KB, 下载次数: 2)