源代码下载:
所有资料下载:
campic.zip
(188.26 KB, 下载次数: 174)
资料占用情况:Program Size: data=197.1 xdata=970 code=37223
实验详细说明:
所谓“数码相机”,其实就是图像采集+数据存储+显示,当然还要有一个CPU作核心。
DIY一个简易的“数码相机”的想法由来已久,我们不需要它有多么强的功能,只要实现基本的图像采集与存储功能即可。我一直在关注和研究OV的图像传感器(俗称摄像头芯片)OV7670,虽然已经比较了解它的驱动方法,但也是困难重重,“DIY数码相机”实验也就一直搁置。
这个东西为什么那么难搞?其原因主要一是速度问题,二是数据量比较大。
速度方面,说白了,就是摄像头芯片输出像素数据的速度太快了!我们来举例说明:假设芯片每秒产生10个图像,即它的帧频为10帧/秒。如果图像的分辨率为320*240(QVGA),那么它每秒就将产生768000个像素数据,每一个像素数据在摄像头芯片数据输出端口上维持的时间约为1000ns。我们如果用单片机去采集这个数据,就必须在这1000ns的时间内完成数据的读入、存储与处理。有人可能说:“1000ns的时间应该是比较富裕的,足够单片机去完成这些操作了。”确实!但是这需要代码的运行效率很高,也许要使用汇编来完成。另外,每一个像素数据并不单纯是一个字节,而可能是16位或24位的。这就会涉及到多次数据读入等操作。我们拿AVR这种单指令周期的CPU来举个例子:我们将AVR超频到20MHz(它的可靠工作主频最高为16MHz),那么它每执行一条指令的时间为50ns,它要在1000ns的时间里完成像素数据的读取等操作,最多只能用20条指令。是不是感觉有点玄乎?!再加之,像素数据在摄像头芯片的数据输出端口上产生也需要一个信号建立的过程,为了保证数据数据的稳定性,实际上留给我们去读取像素数据的时间也只有700ns左右。这显然是非常紧张的!
其实,就算真的来得及去读取和处理像素数据,数据量也是一个大问题。像素数据我们是首先暂存在RAM中的,前面说过,一个像素数据可能是16位或24位的,就拿16位来说,一幅QVGA的图像数据量为150KB,有多少单片机,乃至于ARM或更高端的芯片会有这么多的RAM资源?
上面说的是“数码相机”实验中,摄像头芯片方面的一此困难。另一方面的问题是:SD卡的文件操作和数据写入速率问题。假设我们现在已经得到了一幅完整而正确的图像数据,它就存放在RAM中。现在,我们要把这些数据以文件的形式存入到SD卡中,并作成BMP位图图片。这一过程,仍然是一个极大的挑战,挑战并不在于SD卡的驱动或是BMP文件格式,这些都简单!而在于文件系统,尤其是文件数据写入这方面!详细解释一下:我们知道,SD卡或U盘这类存储设备,要存储文件必须事先进行格式化。格式化的过程其实就是使存储设备遵从某种文件系统协议或者格式,比如我们经常使用的FAT32。要用单片机或ARM等去操作SD卡上的文件,必须也要遵循FAT32的协议格式。振南长期以来所研究和编写的znFAT就是在作这样的事情,通过znFAT可以在SD卡等存储设备上进行各种文件操作,如文件创建、数据读写等等。我们要将像素数据以BMP文件的形式写入SD卡,就可以使用znFAT的文件创建和数据写入功能来实现(znFAT是一个完备而庞杂的东西,详细可以参考使用手册)。对于像素数据的写入,我们是要讲求一些速度的。说白了,向SD卡上的文件中写入这150KB的像素数据,如果用了半个小时,那这样的“数码相机”实验还有什么乐趣和实用性可言,简直变成了一种煎熬!我们要求每写入一个图片,这个写入数据的过程所花费的时间最好控制在几秒以内,最多不过10秒。znFAT的性能和执行效率能有这么高吗?这正是上面我们所说的的挑战所在!
现在“数码相机”实验已经作成功了,发布在这里给大家作个参考。上面的诸多问题的解决方法:
1、速度太快的问题采用在摄像头芯片的数据输出端口上加一片FIFO芯片AL422来解决,同时因为AL422有300多KB的容量,所以数据量大的问题也随之解决。150KB的像素数据进入到FIFO中后,单片机可以“小数据量,多次取出”的方式将数据取走,进而再存到SD卡的BMP文件中。当然,数据写入文件的方式是“频繁的小数据量写入”,这种写入方式其实是最考验一个文件系统方案的效率的。我试过在不开启znFAT的加速算法的时候,以这种方式写完一个BMP文件,大约需要40s左右。然而在开启加速算法之后,只需要10s(开启与不开启,在RAM资源的占用上仅仅多了几十个字节,这也正是znFAT所使用的加速算法的独到之处)。
2、znFAT中采用了比较优秀的加速算法(基于预建簇链与压缩簇链缓冲思想),可以在占用极少的内存资源的前提下,极大的提高文件数据的写入速率。因此,BMP文件数据的写入速率问题也解决了。
- #include "stdio.h"
- #include "znfat/znFAT.h"
- #include "sdx.h"
- #include "uart.h"
- #include "myfun.h"
- #include "sensor.h"
- #include "fifo.h"
- //===========================================
- // 此程序用于完成OV7670+SD卡的数码相机实验
- // 文件系统 znFAT
- // 于振南 QQ 987582714
- //===========================================
- sfr P1M0 = 0x92;
- sfr P1M1 = 0x91;
- struct znFAT_Init_Args idata Init_Args; //初始化参数集合
- struct FileInfo idata fileinfo; //文件信息集合
- struct DateTime dt; //日期时间结构体变量
- unsigned char cur_status=0;
- unsigned char buf[192];
- unsigned char code bmp_header[54]=
- {
- 0x42,0x4D,0x38,0x58,0x02,0x00,0x00,0x00,0x00,0x00,0x36,0x00,0x00,0x00,0x28,0x00,
- 0x00,0x00,0x40,0x01,0x00,0x00,0xF0,0x00,0x00,0x00,0x01,0x00,0x10,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x0B,0x00,0x00,0x12,0x0B,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00
- };
- //==================================================================
- void int0(void) interrupt 0
- {
- EX0=0; //关闭中断
- if(cur_status==0) //如果此时状态为0,则说明是一个图像的开始,开始向FIFO罐入数据
- {
- FIFO_WEN=1;
- cur_status=1; //标记为1
-
- EX0=1; //打开中断
- }
- else
- if(cur_status==1) //说明此处为图像的结束,亦即下一图像的开始
- {
- FIFO_WEN=0;
- cur_status=2;
- }
- }
- int main()
- {
- unsigned char idata i=0;
- FIFO_OE=0;
- P1M1=0x40; //P16高阻输入
- P1M0=0xa8; //如果使用51单片机来调试SD卡模块,打开推挽,增强IO驱动能力
- UART_Init();
- UART_Send_Str("串口初始化完成.");UART_Send_Enter();
- znFAT_Device_Init(); //存储设备初始化
- UART_Send_Str("存储设备初始化完成.");UART_Send_Enter();
- znFAT_Select_Device(0,&Init_Args); //选择设备
- znFAT_Init(); //文件系统初始化
- UART_Send_Str("文件系统初始化完成.");UART_Send_Enter();
- while(!Sensor_init());
- UART_Send_Str("摄像头芯片初始化完成.");UART_Send_Enter();
- //==============================================================================
-
- dt.date.year=2012; dt.date.month=12; dt.date.day=21;
- dt.time.hour=15; dt.time.min=14; dt.time.sec=35;
-
- znFAT_Create_File(&fileinfo,"/znmcu6.bmp",&dt);
- UART_Send_Str("图像文件已创建成功.");UART_Send_Enter();
- znFAT_WriteData(&fileinfo,54,bmp_header);
- UART_Send_Str("图像文件头数据已写入.");UART_Send_Enter();
- IT0=1; //下降沿触发
- EX0=1; //打开外部中断0
- EA=1; //打开总中断
- UART_Send_Str("外部中断已开启,等待图像数据...");UART_Send_Enter();
- while(1)
- {
- if(cur_status==2) //越级FIFO中已经罐入一个完整的图像
- {
- cur_status=0;
- UART_Send_Str("图像已获取.");UART_Send_Enter();
- FIFO_Reset_Read_Addr(); //把FIFO的读指针指向0地址
- UART_Send_Str("开始将图像写入文件.");UART_Send_Enter();
- for(i=0;i<200;i++)
- {
- FIFO_Read_Words(96,buf); //从FIFO读取数据
- znFAT_WriteData(&fileinfo,192,buf); //将数据写入文件
- FIFO_Read_Words(96,buf); //从FIFO读取数据
- znFAT_WriteData(&fileinfo,192,buf); //将数据写入文件
- FIFO_Read_Words(96,buf); //从FIFO读取数据
- znFAT_WriteData(&fileinfo,192,buf); //将数据写入文件
- FIFO_Read_Words(96,buf); //从FIFO读取数据
- znFAT_WriteData(&fileinfo,192,buf); //将数据写入文件
- }
- UART_Send_Str("图像写入完成.");UART_Send_Enter();
- znFAT_Close_File(&fileinfo);
- znFAT_Flush_FS();
- }
- }
- while(1);
- return 0;
- }
复制代码
|