使用STM32和LVGL在界面内显示DCMI摄像头视频流

系统环境

  • 芯片:STM32F429IGT6
  • CubeMX版本:6.9.2
  • Keil版本:5.36
  • LTDC接口16位颜色
  • LVGL 8.3.10
  • 一、理论

    此篇暂不讨论如何配置LTDC屏幕,LVGL等内容,若如下前置功能还存在问题请先解决

    1.确保前置功能调通

    1. 不带LVGL能否在显示屏上显示视频流
    2. 不考虑摄像头,LVGL能否成功显示在显示屏上

    2.基础知识回顾

    1.STM32 DCMI

    该外设使用比较简单,在CubeMX中配置好像素,垂直同步,水平同步极性和跳帧功能,DMA通道等功能后编写程序软件初始化OV5640,配置OV5640为RGB565模式即可。

    使用如下API开始摄像头视频流

    /**
      * @brief  Enables DCMI DMA request and enables DCMI capture
      * @param  hdcmi     pointer to a DCMI_HandleTypeDef structure that contains
      *                    the configuration information for DCMI.
      * @param  DCMI_Mode DCMI capture mode snapshot or continuous grab.
      * @param  pData     The destination memory Buffer address (LCD Frame buffer).
      * @param  Length    The length of capture to be transferred.
      * @retval HAL status
      */
    HAL_StatusTypeDef HAL_DCMI_Start_DMA(DCMI_HandleTypeDef* hdcmi, uint32_t DCMI_Mode, uint32_t pData, uint32_t Length)
    

    记住 pData 这个数据地址,接着看lvgl canvas控件。

    2.LVGL Canvas控件

    画布需要一个缓冲区来存储绘制的图像。要为画布分配一个缓冲区,请使用

    lv_canvas_set_buffer(canvas, buffer, width, height, LV_IMG_CF_...)
    

    其中buffer是一个静态缓冲区(不仅仅是一个局部变量),用于保存画布的图像。例如:

    static lv_color_t buffer[LV_CANVAS_BUF_SIZE_TRUE_COLOR(width, height)]
    

    LV_CANVAS_BUF_SIZE_...宏帮助确定不同颜色格式的缓冲区大小。

    画布支持所有内置颜色格式,如LV_IMG_CF_TRUE_COLORLV_IMG_CF_INDEXED_2BIT。在颜色格式部分查看完整列表。

    实现LVGL内显示摄像头画面的核心就是这个缓冲区

    二、实操

    为此我们需要解决两个问题

    1. 设置lvgl canvas对象缓冲区地址为dcmi dma通道地址
    2. 摄像头画面不断更新需要频繁刷新canvas对象从而显示视频流

    1. 配置OV5640输出尺寸

    /**
     * @brief 设置图像输出大小
     *			OV5640输出图像的大小(分辨率),完全由该函数确定
     *			offx,offy,为输出图像在OV5640_ImageWin_Set设定窗口(假设长宽为xsize和ysize)上的偏移
     *			由于开启了scale功能,用于输出的图像窗口为:xsize-2*offx,ysize-2*offy
     *			width,height:实际输出图像的宽度和高度
     *			实际输出(width,height),是在xsize-2*offx,ysize-2*offy的基础上进行缩放处理.
     *			一般设置offx和offy的值为16和4,更小也是可以,不过默认是16和4 
     * @ret	0,设置成功
     *		其他,设置失败
    */
    uint8_t OV5640_OutSize_Set(uint16_t offx,uint16_t offy,uint16_t width,uint16_t height)
    

    💡 该API同时设置输出大小和缩放功能,可以实现数码变焦功能,后期讲解如何等比缩放图像大小

    // 拉伸
    OV5640_OutSize_Set(300,0,240,290);
    

    💡 offx为300是因为我需要将横屏尺寸的原始图片缩放到竖屏下显示所添加的缩放参数,若你的屏幕为正常横屏显示则仅需微调该值保持等比即可

    2.开启DCMI DMA传输

    uint16_t dcmi_framebuf[240][290];
    
    HAL_DCMI_Start_DMA(&hdcmi, DCMI_MODE_CONTINUOUS,(uint32_t)dcmi_framebuf,240*290*2/4);
    

    首先定义缓冲区,这个变量需要定义为全局,因为你还需要让lvgl canvas对象使用,如果单片机内部没有这么大的内存可以将其指定到外部SDRAM内

    💡 注意函数的最后一个参数 Length ,示例中为2402902/4,其中2402902指一次传输240290个像素2个字节(RGB565格式16位一个像素2字节)。除4 是因为我在cubemx中配置的dcmi dma通道数据宽度为word(4字节)

    3.创建lvgl canvas对象并设置其缓冲地址

    // 创建对象
    ui->Main_canvas_cam = lv_canvas_create(ui->Main);
    // 设置buffer地址
    lv_canvas_set_buffer(ui->Main_canvas_cam, dcmi_framebuf, 240, 290, LV_IMG_CF_TRUE_COLOR);
    // 填充canvas背景
    lv_canvas_fill_bg(ui->Main_canvas_cam, lv_color_hex(0xffffff), 255);
    // 设置canvas位置
    lv_obj_set_pos(ui->Main_canvas_cam, 0, 30);
    // 设置canvas大小
    lv_obj_set_size(ui->Main_canvas_cam, 240, 290);
    // 设置canvas滚动模式
    lv_obj_set_scrollbar_mode(ui->Main_canvas_cam, LV_SCROLLBAR_MODE_OFF);
    

    ⚠️ 需要注意由于lvgl canvas对象和dcmi使用同一块缓冲区,canvas对象大小和dcmi缓冲区大小必须匹配,相应的也必须和OV5640输出尺寸匹配

    4.刷新lvgl canvas对象

    DCMI摄像头垂直同步中断服务例程即如下回调函数

    HAL_DCMI_VsyncEventCallback(DCMI_HandleTypeDef *hdcmi)
    {
    	/// @brief 告知LVGL需要重绘对象
    	/// @bug	!!!潜在BUG!!!
    	///			LVGL线程不安全,中断会频繁抢占lvgl线程
    	///     当lvgl正在刷新屏幕时被抢占调用该函数后LVGL内部会抛出Error
    	///			实际测试该问题应该不会造成内存泄露或者系统死机问题
    	lv_obj_invalidate(ui.Main_canvas_cam);
    }
    

    此回调函数即用于通知DCMI一帧数据传输完成,此时告知LVGL canvas数据已经无效了,立即重绘

    三、问题

    1.摄像头帧率&显示屏帧率&LVGL显示帧率 问题

    这三者帧率关系应该为 摄像头帧率≤LVGL显示帧率≤显示屏帧率

    另外实际表现性能非常重要,并非显示屏可以跑60帧你就必须把LVGL和摄像头帧率都拉到60帧,你的MCU没有这么强的性能

    我实测条件是LVGL全尺寸双缓冲30fps ,屏幕的像素时钟配置也为30fps,摄像头原始尺寸1280*800@15fps

    最终是顶层GUI底层摄像头画面的情况下可以稳30fps,CPU占用80-90%,此时也仅仅是可用的状态,大幅晃动摄像头会导致图像撕裂问题

    四、参考链接

    Canvas (lv_canvas) — LVGL documentation

    文章原始发布于https://blog.awolon.fun/archives/stm32-show-video-in-lvgl.html

    作者:四文鱼Max

    物联沃分享整理
    物联沃-IOTWORD物联网 » 使用STM32和LVGL在界面内显示DCMI摄像头视频流

    发表回复