STM32H7S78-DK测评(三)——OV5640摄像头测试
<div class='showpostmsg'> 本帖最后由 FuShenxiao 于 2024-10-7 11:55 编辑<p><span style="font-size:18px;"><strong>转接板设计</strong></span></p>
<p>观察开发板原理图摄像头接口可以发现,接口居然有30pin,这和软排线连接的OV5640(24pin)或者OV5640模块(2*9pin)显然是不相同的。</p>
<p>恰好我手头有一块正点原子OV5640摄像头模块,于是我就针对它完成转接板的设计。</p>
<p style="text-align: center;"></p>
<p>观察正点原子OV5640摄像头模块原理图可以轻易地与STM32H7S78-DK的摄像头接口相对应。</p>
<p>I2C1_SCL ---> OV_SCL</p>
<p>I2C1_SDA ---> OV_SDA</p>
<p>DCMI_D0 ~ DMCI_D7 ---> OV_D0 ~ OV_D7</p>
<p>RSTI ---> OV_RESET</p>
<p>PWR_EN ---> OV_PWDN</p>
<p>DCMI_VSYNC ---> OV_VSYNC</p>
<p>DCMI_HSYNC ---> OV_HREF</p>
<p>DCMI_PIXCLK ---> OV_PCLK </p>
<p style="text-align: center;"></p>
<p>在嘉立创中绘制原理图如下</p>
<p style="text-align: center;"></p>
<p>绘制PCB如下。为了保证相关电学特性稳定,需要在OV5640供电端加入100nF的去耦电容,在SCL和SDA两个IIC接口上加入4.7kΩ的上拉电阻。</p>
<p style="text-align: center;"></p>
<p>最终得到PCB正反面如下图所示</p>
<p style="text-align: center;"></p>
<p style="text-align: center;"> </p>
<p style="text-align: center;"> </p>
<p>具体工程文件参见<a href="https://oshwhub.com/fushenxiao/stm32h7s78-dk-she-xiang-tou-zhuan-jie-ban">STM32H7S78-DK摄像头转接板 - 立创开源硬件平台 (oshwhub.com)</a></p>
<p><span style="font-size:18px;"><strong>引脚配置</strong></span></p>
<p>首先在CubeMX中配置时钟(这里不再赘述)</p>
<p>接着分别配置DCMIPP(摄像头接口)和LTDC(显示屏接口)</p>
<p style="text-align: center;"></p>
<p style="text-align: center;"> </p>
<p style="text-align: center;"></p>
<p style="text-align: justify;">配置两个指示灯</p>
<p style="text-align: center;"></p>
<p>完成以上操作后,生成代码</p>
<p><span style="font-size:18px;"><strong>代码编写</strong></span></p>
<p>OV5640初始化代码,需要注意的是,OV5640默认原始图像是上下翻转的,所以要使用OV5640_FLIP将图像翻转</p>
<pre>
<code>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;
}</code></pre>
<p>在主函数中使用如下代码完成OV5640初始化</p>
<pre>
<code>if(OV5640_Config(OV5640_R480x272, OV5640_RGB565) != OV5640_OK)
{
/* Camera Module Config KO */
Error_Handler();
}</code></pre>
<p>DCMIPP接口获取数据信息</p>
<pre>
<code>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!= 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;
}</code></pre>
<p>在主函数中用如下代码完成DCMIPP接口数据接收,将接收到的图像信息传到CAMERA_FRAME_BUFFER中。CAMERA_FRAME_BUFFER后面的传参为当前运行模式,除了可以选择DCMIPP_MODE_CONTINUOUS,还可以使用DCMIPP_MODE_SNAPSHOT</p>
<pre>
<code>if(HAL_DCMIPP_PIPE_Start(&phdcmipp, DCMIPP_PIPE0, (uint32_t)CAMERA_FRAME_BUFFER , DCMIPP_MODE_CONTINUOUS) != HAL_OK)
{
Error_Handler();
}</code></pre>
<p>也可以使用双缓冲模式,代码如下</p>
<pre>
<code>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!= 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;
}</code></pre>
<p>最后在显示屏上显示图像,在这一步中,需要将CAMERA_FRAME_BUFFER的数据显示到屏幕的指定区域内</p>
<pre>
<code>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 */
}</code></pre>
<p><strong><span style="font-size:18px;">结果展示</span></strong></p>
<p>当程序正常运行时,LD1会持续闪烁,OV5640中的内容也会实时传输到屏幕上,传输显示的过程还是较为流畅的。</p>
<p>不过噪声的问题似乎有些严重,面对比较白和亮的地方会产生一些紫色的点点,但是对着暗色调的就不会产生这个问题(比如我的书包,桌子在椅背的阴影部分)。考虑PCB绘制部分,网上对OV5640的建议是信号线之间的线长差距小于100mil,这个我符合要求。再考虑软排线的长度,10cm在诸多网友看来似乎确实有些长了,下一步尝试换短一点的软排线再试试。</p>
<p>不过让我费解的是,官方的板子既然做了这个接口,为什么不顺便再做一个适配的摄像头呢,就像STM32MP135F-DK开发板还配套摄像头模块。而且这个30pin的接口根本无法适配OV5640,还得自己画转接板。</p>
<p style="text-align: center;"></p>
<p style="text-align: center;"> </p>
<p style="text-align: center;"> </p>
<p style="text-align: center;"> </p>
<p style="text-align: justify;">6ff2fc34925b327c32ce635fc53741fe<br />
程序源码:</p>
<p style="text-align: justify;"></p>
</div><script> var loginstr = '<div class="locked">查看本帖全部内容,请<a href="javascript:;" style="color:#e60000" class="loginf">登录</a>或者<a href="https://bbs.eeworld.com.cn/member.php?mod=register_eeworld.php&action=wechat" style="color:#e60000" target="_blank">注册</a></div>';
if(parseInt(discuz_uid)==0){
(function($){
var postHeight = getTextHeight(400);
$(".showpostmsg").html($(".showpostmsg").html());
$(".showpostmsg").after(loginstr);
$(".showpostmsg").css({height:postHeight,overflow:"hidden"});
})(jQuery);
} </script><script type="text/javascript">(function(d,c){var a=d.createElement("script"),m=d.getElementsByTagName("script"),eewurl="//counter.eeworld.com.cn/pv/count/";a.src=eewurl+c;m.parentNode.insertBefore(a,m)})(document,523)</script> <p></p>
<p>楼主。ST官方手册有说的,配套摄像头模块型号是MB1683,Mouser上售价400+元人民币。</p>
cc1989summer 发表于 2024-10-8 14:00
楼主。ST官方手册有说的,配套摄像头模块型号是MB1683,Mouser上售价400+元人民币。
<p>抱歉我没仔细读文档,不过这个摄像头也是基于OV5640的,我看e络盟要将近300块,但是感觉都挺贵的,还是自己画个转接板划算</p>
<p style="text-align:center"> </p>
<p>很不错,买了几块开发板,还没有实际操作下手,学习动起来。</p>
<p>楼主是太历害了呀,可以分享开发板的设计吗?</p>
lugl4313820 发表于 2024-10-13 19:41
楼主是太历害了呀,可以分享开发板的设计吗?
<p>这块开发板是ST官方的开发板,ST官网上能找到开发板资料的。如果你说的是OV5640转接板,我已经在嘉立创开源了。</p>
页:
[1]