OV2640摄像头显示方式探究
国庆节快乐~点击上方文字关注我们哦
GD32F450I开发板上配了一个OV2640摄像头,其最大像素尺寸可设置为1600*1200,板子上的RGB-LCD液晶屏的尺寸为480*272,本篇来测试摄像头在整个屏幕上的显示效果。
1
显存大小分析
本次测试中:
摄像头的图像输出尺寸设置为480*272,图像格式为RGB565,即一个像素需要2个字节的空间(Red占5位,Green占6位,Blue占5位),所以一帧图像需要的内存为:
480*272*2/1024=255K字节
因为GD32F450自带的256K内存,虽然大于255K,但程序其它地方也需要内存,所以,摄像头全屏显示,需要用到外部SDRAM来扩展内存。
2
SDRAM搭配DMA进行图像显示
SDRAM扩展内存,就是将SDRAM作为RGB液晶屏的显存,关于RGB-LCD的使用方法,可以先查阅之前的文章:RGB-LCD液晶屏层叠显示测试
另外,为加快数据传输,使用DMA的方式,直接将摄像头采集的数据(地址为0x50050028)转移到显存(地址为0xc0000000,即SDRAM的首地址),减少CPU的干预。只要将图像数据传输至LCD显存,图像就自动显示在液晶屏上了。
图像数据传递基本原理如下图所示。
使用这种方式,显示结果如下,可以发现显示的图像为横向显示(注:这个屏幕的原点是在左下角,因为摄像头的参数设置中设置了镜像显示,所以不要误以为原点在右上角)
3
图像旋转
如果想要图像竖直显示,一种方式是:首先将摄像头的输出由480*272修改为272*480,然后,手动重新排列数据用于LCD的显示,就是将摄像头的行数据转换为LCD的列数据。
当然,这种方式就需要再使用一块内存,用于数据转换。这种方式,摄像头采集的图像,DMA传输时,从摄像头传输到的第一个地址(0XC0000000)不再作为LCD的显存地址,手动将图像旋转90度进行数据转换后,将转换后的数据传送到第二个地址(0XC0400000),用这个地址作为LCD的显存地址。
这种方式的数据传递关系可用下图表示:
图像旋转的代码实现:
void DMA1_Channel7_IRQHandler(void)
{
int i=0, x=0, y=0;
if(dma_interrupt_flag_get(DMA1,DMA_CH7,DMA_INTF_FTFIF))
{
//将0xC0000000的内容,旋转一下方向,放到0xC0400000
for(x=0;x<272;x++)
{
for(y=0;y<480;y++)
{
*(uint16_t *)(0XC0400000+2*i)=*(uint16_t *)(0xC0000000+2*(272*y+x));
i++;
}
}
dma_interrupt_flag_clear(DMA1,DMA_CH7,DMA_INTC_FTFIFC);
}
}
这种方式的显示效果如下,可以看到变为了竖直方向显示。
4
传输方式探究
修改为全屏显示,不能只是把摄像头和液晶屏的显示尺寸修改一下就完事了,因为尺寸变大以后,还要考虑DMA是否可以正常传输的问题。
DMA每次传输也有最大的限制,为:2的16次方,即65536。
上面的方式,DMA每次传输的大小为一帧图像,且DMA的传输数据位宽为32位(4字节),则一帧的数据量为:480*272*2/4=65280,小于65536,刚刚够,所以图像看起来显示正常(但实测,图像画面变化较大时,会出现局部图像滞后显示的现象)。
对于尺寸更大的屏幕,DMA就不能一次传输一帧图像了,可以考虑每次传输一行,每传输一行后,修改DMA的地址,传输下一行,直至一幅画面传输完。另外,利用摄像头的帧中断,强制从第1行重新开始传输,防止摄像头与DMA的速率不一致导致图像错位。
这种方式,中断函数可修改为如下:
static uint16_t line_num =0;//记录传输了多少行
void DMA1_Channel7_IRQHandler(void)
{
int i=0;
int last_line=0;
if(dma_interrupt_flag_get(DMA1,DMA_CH7,DMA_INTF_FTFIF))
{
/*行计数*/
line_num++;
if(line_num==480)
{
/*传输完一帧,计数复位*/
line_num=0;
}
dma_channel_disable(DMA1, DMA_CH7);
dci_dma_config(0xC0000000+(272*2*line_num), 272*2/4);//修改DMA的接收地址,每次传输一行
//将0xC0000000的内容,旋转一下方向,放到0xC0400000
//last_line = (0 == line_num)? 480 : (line_num-1);
last_line = line_num;
for(i=0;i<272;i++)
{
*(uint16_t *)(0XC0400000+2*(480*i+last_line))=*(uint16_t *)(0xC0000000+2*(272*last_line+i));
}
dma_interrupt_flag_clear(DMA1,DMA_CH7,DMA_INTC_FTFIFC);
dma_interrupt_enable(DMA1, DMA_CH7,DMA_CHXCTL_FTFIE);
dma_channel_enable(DMA1, DMA_CH7);
}
}
//使用帧中断重置line_num,可防止有时掉数据的时候DMA传送行数出现偏移
void DCI_IRQHandler(void)
{
if( dci_interrupt_flag_get (DCI_INT_EF) == SET )
{
/*传输完一帧,计数复位*/
line_num=0;
dci_interrupt_clear(DCI_INT_EF);
dci_interrupt_enable(DCI_INT_EF);
}
}
上面的DMA中断函数,每传输完一行数据执行一次,将DMA的接收地址修改为下一行,并记录当前的行数,同时对该行数据进行旋转。
- ubuntu13.04环境hadoop1.2.1单机模式安装
- silverlight:telerik RadControls中RadGridView的一个Bug及解决办法
- scope引起的问题
- JS正则表达式常用函数汇总
- 修改(同步)linux时间
- “爬虫”程序吃掉低价机票 高科技“黄牛”怎么破?
- javascript:json数据的页面绑定
- “刷脸”就可购物 全国首家人脸智慧时尚店亮相深圳
- testng-result中文乱码问题
- mac下环境变量、maven3.1.1 及 jdk1.7.0.45配置
- 高通获准在加州测试无人驾驶技术:车上要有司机
- 机器人产业链分析-机器人基本知识
- postman也可以使用F12功能
- 掌握机器学习数学基础之概率统计(二)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 测试面试题集-MySQL数据库灵魂拷问
- Linux提权姿势一:滥用SUDO提权
- Linux提权姿势二:利用NFS提权
- 统计回归拟合方程参数
- biomaRt包实现不同物种之间同源基因转换
- 使用OpenCV和Python标记超像素色彩
- 听说,你的Loki还是单体?(上篇)
- 加一
- APP自动化测试系列之Appium介绍及运行原理
- 没想到吧,Markdown 还能这么玩!
- 致敬Vue3: 1.1万字从零解读Vue3.0源码响应式系统
- APP自动化测试系列之Desired Capabilities详解
- Kafka分区分配策略(Partition Assignment Strategy)
- 内网渗透-代理篇(一)
- java学习应用篇|逃不掉的HelloWorld