• STM32内存受限情况下摄像头驱动方式与图像裁剪的选择


    1、STM32图像接收接口

    使用stm32芯片,128kB RAM,512kB Rom,资源有限,接摄像头采集图像,这种情况下,内存利用制约程序设计。

    STM32使用DCMI接口读取摄像头,协议如下。行同步信号指示了一行数据完成,场同步信号指示了一帧图像传输完成。所以出现了两种典型的数据接收方式,按照行信号一行一行处理,按照场信号一次接收一副图像。

    2、按行读取

    以网络上流行的野火的demo为例,使用行中断,用DMA来读取一行数据。

    //记录传输了多少行
    static uint16_t line_num =0;
    //DMA传输完成中断服务函数
    void DMA2_Stream1_IRQHandler(void)
    {
      if ( DMA_GetITStatus(DMA2_Stream1,DMA_IT_TCIF1) == SET )
      {
       /*行计数*/
      line_num++;
      if (line_num==img_height)
      {
      /*传输完一帧,计数复位*/
      line_num=0;
      }
      /*DMA 一行一行传输*/
      OV2640_DMA_Config(FSMC_LCD_ADDRESS+(lcd_width*2*(lcd_height-line_num-1)),img_width*2/4);
      DMA_ClearITPendingBit(DMA2_Stream1,DMA_IT_TCIF1);
      }
    }
    
     //帧中断服务函数,使用帧中断重置line_num,可防止有时掉数据的时候DMA传送行数出现偏移
    void DCMI_IRQHandler(void)
    {
      if ( DCMI_GetITStatus (DCMI_IT_FRAME) == SET )
      {
      /*传输完一帧,计数复位*/
      line_num=0;
      DCMI_ClearITPendingBit(DCMI_IT_FRAME);
      }
    }

    DMA中断服务函数中主要是使用了一个静态变量line_num来记录已传输了多少行数据,每进一次DMA中断时自加1,由于进入一次中断就代表传输完一行数据,所以line_num的值等于lcd_height时(摄像头输出的数据行数),表示传输完一帧图像,line_num复位为0,开始另一帧数据的传输。line_num计数完毕后利用前面定义的OV2640_DMA_Config函数配置新的一行DMA数据传输,它利用line_num变量计算显存地址的行偏移,控制DCMI数据被传送到正确的位置,每次传输的都是一行像素的数据量。

    当DCMI接口检测到摄像头传输的帧同步信号时,会进入DCMI_IRQHandler中断服务函数,在这个函数中不管line_num原来的值是什么,它都把line_num直接复位为0,这样下次再进入DMA中断服务函数的时候,它会开始新一帧数据的传输。这样可以利用DCMI的硬件同步信号,而不只是依靠DMA自己的传输计数,这样可以避免有时STM32内部DMA传输受到阻塞而跟不上外部摄像头信号导致的数据错误。

    图像按帧读取比按行读取效率更高,那么为什么要按行读取呢?上面的例子是把图像送到LCD,如果是送到内存,按帧读取就需要芯片有很大的内存空间。以752*480的分辨率为例,需要360kB的RAM空间,远远超出了芯片RAM的大小。部分应用不需要摄像头全尺寸的图像,只需要中心区域,比如为了避免畸变影响一般只用图像中间的部分,那么按行读取就有一个好处,读到一行后,可以把不需要的丢弃,只保留中间部分的图像像素。

    那么问题来了?为什么不直接配置摄像头的属性,来实现只读取图像的中间部分呢,全部读取出来然后在arm的内存中裁剪丢弃不要的像素,第一浪费了读取时间,第二浪费了读取的空间。更优的做法是直接配置摄像头sensor,使用sensor的裁剪功能输出需要的像素区域。

    3、图像裁剪--使用STM32 crop功能裁剪

    STM32F4系列的DCMI接口支持裁剪功能,对摄像头输出的像素点进行截取,不需要的像素部分不被DCMI传入内存,从硬件接口一侧就丢弃了。

    HAL_DCMI_EnableCrop(&hdcmi);
    HAL_DCMI_ConfigCrop(&hdcmi, CAM_ROW_OFFSET, CAM_COL_OFFSET, IMG_ROW-1, IMG_COL-1);

    裁剪的本质如下所述,从接收到的数据里选择需要的矩形区域。所以STM32 DCMI裁剪功能可以完成节约内存,只选取需要的图像存入内存的作用。

    此方法相比于一次读一行,然后丢弃首尾部分后把需要的区域图像像素存入buffer后再读下一行,避免了时序错误,代码简洁了,DCMI硬件计数丢掉不要的像素,也提高了程序可靠性、可读性。

    成也萧何败也萧何,如上面所述,STM32的crop完成了选取特定区域图像的功能,那么也要付出代价,它是从接收到的图像数据里进行选择的,这意味着那些不需要的数据依然会传输到MCU一侧,只不过MCU的DCMI对数据进行计数是忽略了它而已,那么问题就来了,哪些不需要的数据的传输会带来什么问题呢?

    有图为证,下图是使用了STM32 crop裁剪的时序图,通道1启动采集IO置高,frame中断里拉低,由于使用dma传输,那么被crop裁剪后dma计数的数据量变少,所以DCMI frame中断能在行数据传输完成前到达,通道1高电平部分就代表一有效分辨率的帧的采集时间。通道2 曝光信号管脚,通道3是行扫描信号。其中通道1下降沿到通道3下降沿4.5ms。代表单片机已经收到crop指定尺寸的图像,采集有效区域(crop区域)的图像完成,但是line信号没有结束还有很多行没传输,即CMOS和DCMI接口要传输752*480图像还没完成。

     举例说明,如果使用752*480分辨率采集图像,你只取中间的360*360视野,有效分辨率是360*360,但是总线上的数据依然是752*480,所以帧率无法提高,多余的数据按说就不应该传输出来,如何破解,问题追到这里,STM32芯片已经无能为力了,接下来需要在CMOS一侧发力了。

    4、图像裁剪--配置CMOS寄存器裁剪

    下图是MT9V034 摄像头芯片的寄存器手册,Reg1--4配置CMOS的行列起点和宽度高度。

    修改寄存器后,摄像头CMOS就不再向外传输多余的数据,被裁剪丢弃的数据也不会反应在接口上,所以STM32 DCMI接收到的数据都是需要保留的有效区数据,极大地减少了数据输出,提高了传输效率。本人也在STM324芯片上,实现了220*220分辨率120帧的连续采集。

    下面是序图,通道1高电平代表开始采集和一帧结束,不同于使用STM32 的crop裁剪,使用CMOS寄存器裁剪有效窗口,使得帧结束时行信号也同时结束,后续没有任何需要传输的行数据。

    5、一帧数据一次性传输

    一帧数据一次全部读入到MCU的方式,其实是最简单的驱动编写方式,缺点就是太占内存,但是对于没有压缩功能的cmos芯片来说,一般都无力实现。对部分有jpg压缩功能的cmos芯片而言,比如OV2640可以使用这种方式,一次性读出一帧图像。

    __align(4) u32 jpeg_buf[jpeg_buf_size];    //JPEG buffer
    //JPEG 格式
    const u16 jpeg_img_size_tbl[][2]=
    {
        176,144,    //QCIF
        160,120,    //QQVGA
        352,288,    //CIF
        320,240,    //QVGA
        640,480,    //VGA
        800,600,    //SVGA
        1024,768,    //XGA
        1280,1024,    //SXGA
        1600,1200,    //UXGA
    }; 

    //DCMI 接收数据
    void DCMI_IRQHandler(void)
    {
      if(DCMI_GetITStatus(DCMI_IT_FRAME)==SET)// 一帧数据
      {
        jpeg_data_process();  
        DCMI_ClearITPendingBit(DCMI_IT_FRAME); 
      }
    }

  • 相关阅读:
    异步解决方案----Promise与Await
    多页应用 Webpack4 配置优化与踩坑记录
    左侧固定,右侧自适应的布局方式(新增评论区大佬教的方法)
    精读《Epitath 源码
    如何编写 Typescript 声明文件
    状态码具体解释
    LINQ体验(2)——C# 3.0新语言特性和改进(上篇)
    kafka教程
    double x = 10 ,y = 0;y = x % 2; 这个表达式正确吗?
    mongodb mapreduce使用总结
  • 原文地址:https://www.cnblogs.com/pingwen/p/13197711.html
Copyright © 2020-2023  润新知