源代碼下載:
0.png (62.07 KB, 下載次數: 138)
下載附件
2016-10-18 15:46 上傳
所有資料下載:
campic.zip
(188.26 KB, 下載次數: 174)
2016-10-18 15:47 上傳
點擊文件名下載附件
下載積分: 黑幣 -5
資料占用情況:Program Size: data=197.1 xdata=970 code=37223
實驗詳細說明:
所謂“數碼相機”,其實就是圖像采集+數據存儲+顯示,當然還要有一個CPU作核心。
DIY一個簡易的“數碼相機”的想法由來已久,我們不需要它有多么強的功能,只要實現基本的圖像采集與存儲功能即可。我一直在關注和研究OV的圖像傳感器(俗稱攝像頭芯片)OV7670,雖然已經比較了解它的驅動方法,但也是困難重重,“DIY數碼相機”實驗也就一直擱置。
這個東西為什么那么難搞?其原因主要一是速度問題,二是數據量比較大。
速度方面,說白了,就是攝像頭芯片輸出像素數據的速度太快了!我們來舉例說明:假設芯片每秒產生10個圖像,即它的幀頻為10幀/秒。如果圖像的分辨率為320*240(QVGA),那么它每秒就將產生768000個像素數據,每一個像素數據在攝像頭芯片數據輸出端口上維持的時間約為1000ns。我們如果用單片機去采集這個數據,就必須在這1000ns的時間內完成數據的讀入、存儲與處理。有人可能說:“1000ns的時間應該是比較富裕的,足夠單片機去完成這些操作了。”確實!但是這需要代碼的運行效率很高,也許要使用匯編來完成。另外,每一個像素數據并不單純是一個字節(jié),而可能是16位或24位的。這就會涉及到多次數據讀入等操作。我們拿AVR這種單指令周期的CPU來舉個例子:我們將AVR超頻到20MHz(它的可靠工作主頻最高為16MHz),那么它每執(zhí)行一條指令的時間為50ns,它要在1000ns的時間里完成像素數據的讀取等操作,最多只能用20條指令。是不是感覺有點玄乎?!再加之,像素數據在攝像頭芯片的數據輸出端口上產生也需要一個信號建立的過程,為了保證數據數據的穩(wěn)定性,實際上留給我們去讀取像素數據的時間也只有700ns左右。這顯然是非常緊張的!
其實,就算真的來得及去讀取和處理像素數據,數據量也是一個大問題。像素數據我們是首先暫存在RAM中的,前面說過,一個像素數據可能是16位或24位的,就拿16位來說,一幅QVGA的圖像數據量為150KB,有多少單片機,乃至于ARM或更高端的芯片會有這么多的RAM資源?
上面說的是“數碼相機”實驗中,攝像頭芯片方面的一此困難。另一方面的問題是:SD卡的文件操作和數據寫入速率問題。假設我們現在已經得到了一幅完整而正確的圖像數據,它就存放在RAM中。現在,我們要把這些數據以文件的形式存入到SD卡中,并作成BMP位圖圖片。這一過程,仍然是一個極大的挑戰(zhàn),挑戰(zhàn)并不在于SD卡的驅動或是BMP文件格式,這些都簡單!而在于文件系統(tǒng),尤其是文件數據寫入這方面!詳細解釋一下:我們知道,SD卡或U盤這類存儲設備,要存儲文件必須事先進行格式化。格式化的過程其實就是使存儲設備遵從某種文件系統(tǒng)協(xié)議或者格式,比如我們經常使用的FAT32。要用單片機或ARM等去操作SD卡上的文件,必須也要遵循FAT32的協(xié)議格式。振南長期以來所研究和編寫的znFAT就是在作這樣的事情,通過znFAT可以在SD卡等存儲設備上進行各種文件操作,如文件創(chuàng)建、數據讀寫等等。我們要將像素數據以BMP文件的形式寫入SD卡,就可以使用znFAT的文件創(chuàng)建和數據寫入功能來實現(znFAT是一個完備而龐雜的東西,詳細可以參考使用手冊)。對于像素數據的寫入,我們是要講求一些速度的。說白了,向SD卡上的文件中寫入這150KB的像素數據,如果用了半個小時,那這樣的“數碼相機”實驗還有什么樂趣和實用性可言,簡直變成了一種煎熬!我們要求每寫入一個圖片,這個寫入數據的過程所花費的時間最好控制在幾秒以內,最多不過10秒。znFAT的性能和執(zhí)行效率能有這么高嗎?這正是上面我們所說的的挑戰(zhàn)所在!
現在“數碼相機”實驗已經作成功了,發(fā)布在這里給大家作個參考。上面的諸多問題的解決方法:
1、速度太快的問題采用在攝像頭芯片的數據輸出端口上加一片FIFO芯片AL422來解決,同時因為AL422有300多KB的容量,所以數據量大的問題也隨之解決。150KB的像素數據進入到FIFO中后,單片機可以“小數據量,多次取出”的方式將數據取走,進而再存到SD卡的BMP文件中。當然,數據寫入文件的方式是“頻繁的小數據量寫入”,這種寫入方式其實是最考驗一個文件系統(tǒng)方案的效率的。我試過在不開啟znFAT的加速算法的時候,以這種方式寫完一個BMP文件,大約需要40s左右。然而在開啟加速算法之后,只需要10s(開啟與不開啟,在RAM資源的占用上僅僅多了幾十個字節(jié),這也正是znFAT所使用的加速算法的獨到之處)。
2、znFAT中采用了比較優(yōu)秀的加速算法(基于預建簇鏈與壓縮簇鏈緩沖思想),可以在占用極少的內存資源的前提下,極大的提高文件數據的寫入速率。因此,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卡的數碼相機實驗
- // 文件系統(tǒng) 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) //如果此時狀態(tài)為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(); //文件系統(tǒng)初始化
- UART_Send_Str("文件系統(tǒng)初始化完成.");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("圖像文件已創(chuàng)建成功.");UART_Send_Enter();
- znFAT_WriteData(&fileinfo,54,bmp_header);
- UART_Send_Str("圖像文件頭數據已寫入.");UART_Send_Enter();
- IT0=1; //下降沿觸發(fā)
- 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;
- }
復制代碼
|