STM32内存受限情况下摄像头驱动方式与图像裁剪的选择
1、STM32图像接收接口
使用stm32芯片,128kB RAM,512kB Rom,资源有限,接摄像头采集图像,这种情况下,内存利用制约程序设计。
STM32使用DCMI接口读取摄像头,协议如下。行同步信号指示了一行数据完成,场同步信号指示了一帧图像传输完成。所以出现了两种典型的数据接收方式,按照行信号一行一行处理,按照场信号一次接收一副图像。

2、按行读取
以网络上流行的野火的demo为例,使用行中断,用DMA来读取一行数据。
//记录传输了多少行
static uint16_t line_num =;
//DMA传输完成中断服务函数
void DMA2_Stream1_IRQHandler(void)
{
if ( DMA_GetITStatus(DMA2_Stream1,DMA_IT_TCIF1) == SET )
{
/*行计数*/
line_num++;
if (line_num==img_height)
{
/*传输完一帧,计数复位*/
line_num=;
}
/*DMA 一行一行传输*/
OV2640_DMA_Config(FSMC_LCD_ADDRESS+(lcd_width**(lcd_height-line_num-)),img_width*/);
DMA_ClearITPendingBit(DMA2_Stream1,DMA_IT_TCIF1);
}
} //帧中断服务函数,使用帧中断重置line_num,可防止有时掉数据的时候DMA传送行数出现偏移
void DCMI_IRQHandler(void)
{
if ( DCMI_GetITStatus (DCMI_IT_FRAME) == SET )
{
/*传输完一帧,计数复位*/
line_num=;
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-, IMG_COL-);
裁剪的本质如下所述,从接收到的数据里选择需要的矩形区域。所以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、图像裁剪--配置COMOS寄存器裁剪
下图是MT9V034 摄像头芯片的寄存器手册,Reg1--4配置CMOS的行列起点和宽度高度。

修改寄存器后,摄像头CMOS就不再向外传输多余的数据,被裁剪丢弃的数据也不会反应在接口上,所以STM32 DCMI接收到的数据都是需要保留的有效区数据,极大地减少了数据输出,提高了传输效率。本人也在STM324芯片上,实现了220*220分辨率120帧的连续采集。
下面是序图,通道1高电平代表开始采集和一帧结束,不同于使用STM32 的crop裁剪,使用CMOS寄存器裁剪有效窗口,使得帧结束时行信号也同时结束,后续没有任何需要传输的行数据。

5、一帧数据一次性传输
一帧数据一次全部读入到MCU的方式,其实是最简单的驱动编写方式,但是对于没有压缩功能的cmos芯片来说,一般都无力实现。对部分有jpg压缩功能的cmos芯片而言,比如OV2640可以使用这种方式,一次性读出一帧图像。
__align() u32 jpeg_buf[jpeg_buf_size]; //JPEG buffer
//JPEG 格式
const u16 jpeg_img_size_tbl[][]=
{
,, //QCIF
,, //QQVGA
,, //CIF
,, //QVGA
,, //VGA
,, //SVGA
,, //XGA
,, //SXGA
,, //UXGA
};
//DCMI 接收数据
void DCMI_IRQHandler(void)
{
if(DCMI_GetITStatus(DCMI_IT_FRAME)==SET)// 一帧数据
{
jpeg_data_process();
DCMI_ClearITPendingBit(DCMI_IT_FRAME);
}
}
STM32内存受限情况下摄像头驱动方式与图像裁剪的选择的更多相关文章
- 模拟APP存储空间、内存不足情况下软件正常运行
1.进行临界测试,手机盘空间存满的条件下应用会有何表现: 方法一:adb shell dd if=/dev/zero of=/mnt/sdcard/bigfile 方法二:哆啦A工具生成文件 2.内存 ...
- git 提交各种情况下的处理方式
自己总结: 01.若在提交过程中有冲突,解决冲突后,git add . git rebase —continue git push for 02.git rebase vs git merge g ...
- 【转载】为什么不建议<=3G的情况下使用CMS GC
之前曾经有讲过在heap size<=3G的情况下完全不要考虑CMS GC,在heap size>3G的情况下也优先选择ParallelOldGC,而不是CMS GC,只有在暂停时间无法接 ...
- JVM自动内存管理机制——Java内存区域(下)
一.虚拟机参数配置 在上一篇<Java自动内存管理机制——Java内存区域(上)>中介绍了有关的基础知识,这一篇主要是通过一些示例来了解有关虚拟机参数的配置. 1.Java堆参数设置 a) ...
- Linux下通过二进制方式安装mysql5.7版本和系统优化
本文主要介绍MySQL二进制软件包的安装/启动/关闭过程. 也许有人要问为什么要选择二进制的安装方式呢? 其实答案很简单,官方版本中已经把所有功能都配置好了,我们可以很方便地拿来使用. 官方MySQL ...
- DNS分别在什么情况下使用UDP和TCP
DNS同时占用UDP和TCP端口53是公认的,这种单个应用协议同时使用两种传输协议的情况在TCP/IP栈也算是个另类.但很少有人知道DNS分别在什么情况下使用这两种协议. 如果用wiresha ...
- 一步步学Mybatis-实现单表情况下的CRUD操作 (3)
今天这一章要紧接上一讲中的东西,本章中创建基于单表操作的CRUD与GetList操作,此示例中以Visitor表为范例,为了创建一点测试数据我们先弄个Add方法吧 继续在上次的IVisitorOper ...
- MySQL分页优化中的“INNER JOIN方式优化分页算法”到底在什么情况下会生效?
本文出处:http://www.cnblogs.com/wy123/p/7003157.html 最近无意间看到一个MySQL分页优化的测试案例,并没有非常具体地说明测试场景的情况下,给出了一种经典的 ...
- phpExcel导入大数据量情况下内存溢出解决方案
PHPExcel版本:1.7.6+ 在不进行特殊设置的情况下,phpExcel将读取的单元格信息保存在内存中,我们可以通过 PHPExcel_Settings::setCacheStorageMeth ...
随机推荐
- zabbix通过IPMI监控服务器传感器参数
一.需求:机房dell服务器和IBM服务器皆有主板管理接口iDRAC和iMM,上周已为服务器管理接口配置了ip地址,考虑通过zabbix实现对服务器传感器参数的实时监控.使用DELL-DL1300服务 ...
- Java分层经验
在学习和使用Java的过程中,我们时常要用到各种工具与技术,它们在某些时候可以大幅度地简化编程,利用好它们,可以让代码更强壮.下面的表格是我总结的关于java开发可能会用到的工具与它们在项目中扮演的角 ...
- Verilog缺少一个复合数据类型,如C语言中的结构体
https://mp.weixin.qq.com/s/_9UsgUQv-MfLe8nS938cfQ Verilog中的数据类型(Data Type)是分散的,缺少一个复合数据类型:把多个wire, r ...
- Linux(二) 系统远程访问
个人博客网:https://wushaopei.github.io/ (你想要这里多有) 1. Why?为什么需要远程访问? 人和人之间对话有两种方式,一种是面对面直接交谈,另一种是打电话. 我 ...
- Linux (一)概述
认识操作系统 操作系统的作用 把计算机系统中对硬件设备的操作封装起来,供应用软件调用. 2. 常见操作系统 1.2.1 PC端OS 1.2.2 移动端OS 1.2.3 服 ...
- Java实现 删数字
删数字 时间限制: 1 Sec 内存限制: 128 MB 题目描述 奶牛在数学课上学习了整除的概念.农夫Farmer John为了考验它的数学水平,于是在白纸上写了正整数T.由于农夫不喜欢数字0,所以 ...
- Java实现 LeetCode 605 种花问题(边界问题)
605. 种花问题 假设你有一个很长的花坛,一部分地块种植了花,另一部分却没有.可是,花卉不能种植在相邻的地块上,它们会争夺水源,两者都会死去. 给定一个花坛(表示为一个数组包含0和1,其中0表示没种 ...
- Java实现 蓝桥杯VIP 算法提高 阮小二买彩票
算法提高 阮小二买彩票 时间限制:1.0s 内存限制:512.0MB 问题描述 在同学们的帮助下,阮小二是变的越来越懒了,连算账都不愿意自己亲自动手了,每天的工作就是坐在电脑前看自己的银行账户的钱是否 ...
- python3 驱动自动安装脚本
from pywinauto.application import Applicationimport osimport timeos.system('start C:/Users/Administr ...
- spring Cloud服务注册中心Eureka集群
spring Cloud服务注册中心Eureka集群配置: 在application.yml文件加以下配置: server: port: 8761 tomcat: uri-encoding: UTF- ...