第一篇:LCD電子廣告屏
LCD電子廣告屏設計
一、設計目的為了進一步鞏固學習的理論知識,增強學生對所學知識的實際應用能力和運用所學的知識解決實際問題的能力,開始為期兩周的課程設計。通過設計使學生在鞏固所學知識的基礎之上具有初步的單片機系統設計與應用能力。
1、通過本設計,使學生綜合運用《單片機技術原理與應用》、《DSP原理與 用》《C語言程序設計》以及《數字電路》、《模擬電路》等課程的內容,為以后從事電子產品設計、軟件編程、系統控制等工作奠定一定的基礎。
2、學會使用KEIL C和PROTEUS等軟件,用C語言或匯編語言編寫一個較完 整的實用程序,并仿真運行,保證設計的正確性。
3、了解單片機接口應用開發的全過程:分析需求、設計原理圖、選用元器 件、布線、編程、調試、撰寫報告等。
二.設計內容
1、設計要求
單片機控制的LCD 1602的電子廣告牌
用單片機控制字符型LCD 1602顯示字符信息“Hello everyone!”和“Welcome to Harbin”。字符信息“Hello everyone!”、“Welcome to Harbin”分別從LCD 1602右側第一行、第一行滾動移入,然后再從左側滾動移出,循環顯示。
2、設計思路
液晶模塊用命令控制顯示由模塊本身已經給出,AVR、LCD 的VSS、VDD、VEE 不需連接,默認VSS=0V、VDD=5V、VEE=-5V、GND=0V,液晶模塊與單片機的連接不采用添加多個與非門的方式,而采用更直接、清晰的方式,使WR、RD直接與LCD的R/W、RS相連,使能端與P3^5相連,由軟件給出下降沿。
3、功能說明
1)給LCD寫命令、送數據等均需要進行選中寄存器、發送命令或數據代碼、使能端有效等過程,若逐一寫出會使程序混亂冗長,因此將寫命令、寫數據、初始化等分別封裝在子函數中,方便隨時調用。
2)為實現滾動顯示,使用命令打開整體顯示移動。
3)顯示字母只需將相應的ASCII代碼發送給LCM即可,將需要顯示的字符直 接設為數組中的元素,顯示時逐一寫入液晶。
4)若數組中只有相應的字符,顯示的末尾會出現預料之外的字符,因此加入足 夠的空格避免這種現象發生。
5)為實現循環不斷顯示的功能,只需將函數整體放入while(1)中即可,但應注 意下一次循環開始前將DDRAM的地址回復為第一行。
四.實驗原理圖
三、硬件電路元件介紹
1、AT89C51單片機:
AT89C51是一種低電壓、高性能CMOS 8位微處理器,它自帶4K字節閃存可編程可擦除只讀存儲器(FPEROM—Flash Programmable and Erasable Read Only Memory),俗稱單片機。單片機的可擦除只讀存儲器可以反復擦除1000次。該器件采用ATMEL高密度非易失存儲器制造技術制造,與工業標準的MCS-51指令集和輸出管腳相兼容。由于將多功能8位CPU和閃存存儲器組合在單個芯片中,ATMEL的AT89C51是一種高效微控制器。AT89C系列單片機為很多嵌入式控制系統提供了一種靈活性高且價廉的方案。它的部分引腳功能介紹如下。
P0口:P0口為一個8位漏級開路雙向I/O口。當P1口的管腳第一次寫1時,被定義為高阻輸入。P0口能夠用于外部程序數據存儲器,它可以被定義為數據/地址的第八位。在FIASH編程時,P0 口作為原碼輸入口,當FIASH進行校驗時,P0輸出原碼,此時P0外部必須被拉高。
P1口:P1口是一個內部提供上拉電阻的8位雙向I/O口。P1口管腳寫入1后,被內部上拉為高,可用作輸入,P1口被外部下拉為低電平時,將輸出電流,這是由于內部上拉的緣故。在FLASH編程和校驗時,P1口作為第八位地址接收。
P2口:P2口為一個內部上拉電阻的8位雙向I/O口,當P2口被寫“1”時,其管腳被內部上拉電阻拉高,且作為輸入。并因此作為輸入時,P2口的管腳被外部拉低,將輸出電流。這是由于內部上拉的緣故。P2口當用于外部程序存儲器或16位地址外部數據存儲器進行存取時,P2口輸出地址的高八位。在給出地址“1”時,它利用內部上拉優勢,當對外部八位地址數據存儲器進行讀寫時,P2口輸出其特殊功能寄存器的內容。P2口在FLASH編程和校驗時接收高八位地址信號和控制信號。
P3口:P3口管腳是8個帶內部上拉電阻的雙向I/O口。當P3口寫入“1”后,它們被內部上拉為高電平,并用作輸入。作為輸出,由于外部下拉為低電平,它將輸出電流。
RST:復位輸入。當振蕩器復位器件時,要保持RST腳兩個機器周期的高電平時間。
2、液晶模塊
液晶模塊簡單點說就是屏+背光板+PCB板+鐵框。
電力終端、儀器儀表等的顯示部件就是液晶模塊,其地位相當于CRT中的顯像管。
其它部分包括電源電路,信號處理電路等,當然還有外殼什么的。
模塊主要分為屏和背光燈組件。兩部分被組裝在一起,但工作的時候是相互獨立的(即電路不相關)。
液晶顯示的原理是背光燈組件發出均勻的面光,光通過液晶屏傳到我們的眼睛里。屏的作用就是按像素對這些光進行處理,以顯示圖像。
3、排阻
排阻,是一排電阻的簡稱,就是若干個參數完全相同的電阻,它們的一個引腳都連到一起,作為公共引腳,其余引腳正常引出。內存在處理、傳輸數據時會產生大小不一的工作電流。而在內存顆粒走線的必經之處安裝一排電阻,則能夠幫助內存起到穩壓作用,讓內存工作更穩定。從而提升內存的穩定性,增強內存使用壽命。
四、軟件部分的程序流程圖
五、源程序清單
C語言程序: #include
#define uchar unsigned char #define uint unsigned int #define LCDPORT P0 uchar code tab[]=“Hello everyone!”;
uchar code tab1[]=“Welcome to Harbin ”;//字符后面加若干個空格,防止滾動顯示時出現不該出現的字符
sbit LCDE=P3^5;sbit LCDRW=P3^6;sbit LCDRS=P3^7;
void WriteCMD(uchar);
//定義寫命令函數
void init();
//定義初始化函數
void WriteData(uchar);
//定義寫數據函數
void Delay();
//定義延遲函數
五、源程序清單
C語言程序:
#include
#define uchar unsigned char #define uint unsigned int
#define LCDPORT P0 uchar code tab[]=“Hello everyone!
”;
uchar code tab1[]=“Welcome to Harbin
”;//字符后面加若干個空格,防止滾動顯示時出現不該出現的字符
sbit LCDE=P3^5;sbit LCDRW=P3^6;sbit LCDRS=P3^7;void WriteCMD(uchar);
//定義寫命令函數
void init();
//定義初始化函數
void WriteData(uchar);
//定義寫數據函數
void Delay();
//定義延遲函數 void mydelay(int);
void main()
{ uint i;
//定義無符號整形變量
init();
//液晶模塊的初始化
while(1)
//使之一直進行滾動顯示程序,不斷循環
{ for(i=0;i<32;i++){
WriteData(tab[i]);
//顯示第一行字符
mydelay(50);
//每顯示一個字符后停留一會,越過液晶的“忙”狀態
}
mydelay(50);
WriteCMD(0x01);
//清屏,準備進行下一行顯示
WriteCMD(0x80+0x40+15);//改變DDRAM的地址,更改為第二行末尾,從此處開始進入字符
for(i=0;i<33;i++)
//開始輸入第二行字符
{ WriteData(tab1[i]);
mydelay(50);
}
mydelay(50);
WriteCMD(0x01);
WriteCMD(0x80+15);
//DDRAM的地址改回第一行,準備 進入下一次循環,顯示重新開始
} }
void Delay()
{
uint uiCount;
for(uiCount=0;uiCount<250;uiCount++);}
void WriteCMD(uchar Command)
//寫命令函數,用于修改液晶的狀態 寄存器
{ Delay();
//先延時,越過液晶“忙”狀態
LCDE=1;
//使能端先置1
LCDRS=0;
//設置RS為0,打開指令寄存器
LCDRW=0;
//設置為寫寄存器狀態
LCDPORT=Command;
//輸出命令
LCDE=0;
//使能端由1變0,出現下降沿有效,執行命令
}
void WriteData(uchar dat)
//寫數據函數,用于提供液晶的顯示 的字符
{
Delay();
LCDE=1;
//把LCD改為寫入數據狀態 LCDRS=1;
LCDRW=0;
LCDPORT=dat;
//再輸出數據
LCDE=0;
//使能端有效,顯示數據 }
void init()
//初始化程序,模塊化使程序更清晰
{
LCDRW=0;
LCDE=0;
WriteCMD(0x38);
//設置雙行顯示、8位數據接口
WriteCMD(0x0c);
//設置整體顯示開、光標關、字符不閃爍
WriteCMD(0x07);
//設置為增量方式,整體顯示移動開
WriteCMD(0x01);
//清屏
WriteCMD(0x80+15);
//設置字符進入屏幕的初始位置 } void mydelay(int x){
int i,j;
for(i=x;i>0;i--)
for(j=255;j>0;j--);}
六、仿真調試
1、軟件調試
借助 Keil 和 Proteus進行單片機系統軟硬件的開發、運行、仿真。由
Keil 軟件集成文件管理編譯環境,編譯 C 語言源程序,下載、運行、調試,連接和定位目標文件和庫,創建 HEX 文件,調試目標程序。利用Proteus 完成硬件設計仿真平臺,實現硬件原理圖的連接,并將hex文件與89c51同頻,下載運行,仿真實現。
2、仿真調試
在將電路按原理圖連接好之后,通過89c51將由Keil產生的hex文件下載到單片機內。運行調試,觀察液晶模塊上字幕的顯示,符合實驗設計要求。
3、難點與解決思路
設計過程中遇到程序設計問題,網上找到視頻教程學習程序的編寫,以及查閱C語言課本得以解決。調試過程中發現液晶不顯示,電路高低電平不符合規律現象,通過檢查和改變線路部分連接解決了問題。
七、課設體會
過本次實驗我們認識到了單片機的應用,并掌握了單片機的構造成分,液晶模塊的應用領域。而通過自己的實際動手操作,實際查書找資料提高了自己的動手實踐能力和知識的儲備和應用能力,認識自己的不足并加以改正,增加字自己的成就感,為以后的工作和學習打好堅實的基礎。
七、參考文獻
【1】《單片機原理及應用》
主編
李建忠
【2】高鋒《單片微型計算機原理與接口技術》
科學出版社
【3】朱清慧、張鳳蕊、翟天嵩、王志奎
《Proteus教程——電子線路設計、制板與仿真》,清華大學出版社出版
【4】李全釗、遲榮強,《單片機原理及接口技術》,高等教育出版社出版
【5】李學禮,《基于Proteus的80C51單片機實例教程》,電子工業出版社出版
【6】周向紅,《51系列單片機應用與實踐教程》,北京航空航天大學出版社出版 【7】《單片機應用及技術》,電子工業出版社
【8】Proteus
入門教程
第二篇:小議LCD顯示器優缺點
小議LCD顯示器優缺點
LCD顯示器發展至今,它有許多優缺點的,下面給大家分析一下LCD顯示器它的優缺點問題。
液晶顯示器的優勢
傳統CRT顯示器發展了幾十年,其技術結構原理限制了它的進一步發展。真空陰極射線管固有的幾個重大缺點導致CRT顯示器越來越難適應消費者對顯示器要求的進一步提高,這時候,平板顯示器件異軍突起,其中最有可能取代CRT顯示器在PC顯示終端壟斷的地位就是液晶顯示器。
液晶顯示器顯示原理與CRT顯示器迥然不同。相對CRT顯示器來說,液晶顯示器天生有擁有以下絕對優勢:
1、零輻射,低耗能,散熱小。液晶顯示器的顯示原理是通過扭轉液晶像素中的液晶分子偏轉角度來背景光而實現還原畫面的,其不存在象CRT那樣內部具有超高壓元器件,不至于出現由于高壓導致的x射線超標。而且機器結構電路簡單,模塊化以及芯片的高集成化足以把電路工作時候產生的電磁輻射降到最低。這樣的設計直接降低了電路的功耗,發熱量也非常小。液晶顯示器雖然在工作時候可能產生輕微的電磁輻射,但是很容易通過屏蔽電路解決。而CRT顯示器由于考慮到散熱,不得以在屏蔽罩上鉆孔導致輻射的泄露。
2、纖薄輕巧。正是液晶顯示器的出現,才令手提電腦的發明成為可能。同樣,桌式液晶顯示器雖然在體積以及重量上要比手提電腦的都要大一些,但是,相對那又笨又重的CRT顯示器來說就是小巫見大巫了。以15英寸的顯示器比較,CRT顯示器的深度一般接近50厘米,而大白鯊最新推出的液晶顯示器NF-1500MA的深度卻不到5厘米!隨著消費觀點以及居住環境的改變,人們對家用電器產品的體積以及重量要求越來越高。液晶顯示器以其纖薄輕巧的天生優勢成為最有可能打破CRT顯示器壟斷地位的顯示器件。
3、精確還原圖像。液晶顯示器采用的是直接數碼尋址的顯示方式,它能夠將顯卡輸出的視頻信號經過AD轉換之后,根據信號電平中的“地址”信號,直接將視頻信號一一對應的在屏幕上的液晶像素上顯示出來。而CRT顯示器是靠偏轉線圈產生電磁場來控制電子束在屏幕上周期性的掃描來達到顯示圖像的目的的。由于電子束的運動軌跡容易受到環境磁場或者地磁的影響,無法做到電子束在屏幕上的絕對定位。所以CRT顯示器容易出現畫面的幾何失真,線性失真等無法根本消除的現象。而液晶顯示器則不存在這一可能。液晶顯示器可以把畫面完美的在屏幕上呈現出來,而不會出現任何的幾何失真,線性失真。
4、顯示字符銳利。畫面穩定不閃爍。液晶顯示獨特的顯示原理決定了其屏幕上各個像素發光均勻,而且紅綠藍三基色像素緊密排列,視頻信號直接送到像素背后的以驅動像素發光,因此不會出現傳統的CRT顯示器固有的會聚以及聚焦不良的弊病。所以,液晶顯示器上的文本顯示效果與傳統的CRT顯示器相比有著天淵之別。液晶顯示器的字體非常銳利,完全沒有CRT顯示器顯示文本時候出現的字體模糊,字體泛色等現象。而且,由于液晶顯示器在通電之后就一直在發光,背光燈工作在高頻下,顯示畫面穩定而不閃爍,有利于長時間的使用電腦。而CRT顯示器是靠電子束重復撞擊到熒光粉來達到發光的,這樣會導致亮度周期性閃爍。長時間使用之后容易造成人眼的不適。
5、屏幕調節方便。液晶顯示器的直接尋址顯示方式,使得液晶顯示器的屏幕調節不需要太多的幾何調節和線性調節以及顯示內容的位置調節。液晶顯示器的可以很方便的通過芯片計算后自動把屏幕調節到最佳位置,這個步驟你只需要按一下“Auto”鍵就可以完成。省卻了CRT顯示器那而煩瑣的調節。你只需要手動調節一下屏幕的亮度和對比度就可以使機器工作在最佳狀態了。
液晶顯示器的具有這些天生優勢,已經對CRT顯示器構成足夠的威脅,唯一的遺憾就是由于液晶顯示器的制造成本所限,目前液晶顯示器的價格還是相對偏高。
液晶顯示器的缺點
現在市面上有很多低價的14、15英寸液晶顯示器出售,許多商家也將液晶吹捧到天上去了,誠然,液晶有不少非常明顯的有點,只是由于各種原因,目前出售的低價位液晶都只是屬于液晶產品里面的“低端”,本身有不少先天的缺點,下面我們來詳細分析一下到底這些液晶顯示器有什么缺點。
1、價格
雖然說是低價位,但是何CRT比較起來,液晶顯示器的價格在顯示器家族中可謂“貴族價格”,僅僅是15寸就3000元,而同尺寸的純平顯示器也不過千元左右。專家分析液晶顯示屏高居不下的主要原因是在制造過程中良品率很低,導致成本無法降低。目前,能夠生產液晶屏的只有日本和臺灣的部分廠商,技術沒有完全擴散,尚未形成大規模生產的競爭態勢,并且質量上也有很大差異。在國際市場上,不同品級之間的價差可達到數十到上百美元之多。
2、接口
液晶顯示器的數字接口高處不勝寒。從理論上說,液晶顯示器是純數字設備,與電腦主機的連接也應該是采用數字式接口,采用數字接口的優點是不言而喻的。首先可以減少在模數轉換過程中的信號損失和干擾;減少相應的轉化電路和元件;其次不需要進行時鐘頻率、向量的調整。
但是目前市場上出售的低價液晶顯示器,大部分都是采用模擬接口,存在著傳輸信號易受干擾、顯示器內部需要加入模數轉換電路、無法升級到數字接口等問題。并且,為了避免像素閃爍的出現,必須做到時鐘頻率、向量與模擬信號的完全一致。
此外,液晶顯示器的數字接口尚未形成統一標準,帶有數字輸出的顯示卡在市面上并不多見。這樣一來,液晶顯示器的關鍵性的優勢卻很難充分發揮。就目前而言,提前消費的結果就是花高價買個擺設。
3、可視角度小
早期的液晶顯示器可視偏轉角度只有90度,只能從正面觀看,從側面看就會出現較大的亮度和色彩失真?,F在市面上的液晶顯示器可視偏轉角度一般在140度左右,對于個人使用來說是夠了,但如果幾個人同時觀看,失真的問題就顯現出來了。
4、相應時間過慢
響應時間是液晶顯示器的一個特殊指標。液晶顯示器的響應時間指的是顯示器各像素點對輸入信號反應的速度,響應時間短,則顯示運動畫面時就不會產生影像拖尾的現象。這一點在玩游戲、看快速動作的影像時十分重要。足夠快的響應時間才能保證畫面的連貫。目前,市面上一般的液晶顯示器,響應時間與以前相比已經有了很大的突破,一般為40ms左右。但是仍舊無法滿足對3D游戲和高質量DVD電影播放的要求。
5、亮度和對比度低
要打電筒嗎?這句笑話說的是液晶顯示器的亮度和對比度。由于液晶分子不
能自己發光,所以,液晶顯示器需要靠外界光源輔助發光。一般來講140流明每平方米才夠。有些廠商的參數標準和實際標準還存在差距。這里要說明一下,就是一些小尺寸的液晶顯示器以往主要應用于筆記本電腦當中,采用兩燈調節,因此它們的亮度和對比度都不是很好。
6、維修問題
液晶“壞點”問題。液晶顯示屏的材料一般采用玻璃,很容易破碎,再加上每一個像素都十分細小,常常會造成個別的像素壞掉的現象,俗稱“壞點”,這是無法維修的,只有更換整個顯示屏,而更換的價格往往十分昂貴。
一種新品的推出,自有它優越于陳品的特色。LCD顯示器的優越之處就在于它的輕巧簡便和環保護眼。但是由于現階段的LCD產品同時存在著上述缺陷,尚不能滿足消費者的所有需求。在這樣的產品技術前提下,我們呼吁廣大消費者,不要盲目跟隨時尚風潮,而是要認清自己的需求和產品的特點,做出最為客觀的、實用的選擇。
網站:聯系:0755-29491359 *** QQ:649539816
第三篇:S3C2410 LCD驅動學習心得-4
3.BMP和JPEG圖形顯示程序
3.1 在LCD上顯示BMP或JPEG圖片的主流程圖
首先,在程序開始前。要在nfs/dev目錄下創建LCD的設備結點,設備名fb0,設備類型為字符設備,主設備號為29,次設備號為0。命令如下: mknod fb0 c 29 0 在LCD上顯示圖象的主流程圖如圖3.1所示。程序一開始要調用open函數打開設備,然后調用ioctl獲取設備相關信息,接下來就是讀取圖形文件數據,把圖象的RGB值映射到顯存中,這部分是圖象顯示的核心。對于JPEG格式的圖片,要先經過JPEG解碼才能得到RGB數據,本項目中直接才用現成的JPEG庫進行解碼。對于bmp格式的圖片,則可以直接從文件里面提取其RGB數據。要從一個bmp文件里面把圖片數據陣列提取出來,首先必須知道bmp文件的格式。下面來詳細介紹bmp文件的格式。
圖3.1
3.2 bmp位圖格式分析
位圖文件可看成由四個部分組成:位圖文件頭、位圖信息頭、彩色表和定義位圖的字節陣列。如圖3.2所示。
圖3.2 文件頭中各個段的地址及其內容如圖3.3。
圖3.3 位圖文件頭數據結構包含BMP圖象文件的類型,顯示內容等信息。它的數據結構如下定義: Typedef struct {
int bfType;//表明位圖文件的類型,必須為BM long bfSize;//表明位圖文件的大小,以字節為單位 int bfReserved1;//屬于保留字,必須為本0 int bfReserved2;//也是保留字,必須為本0 long bfOffBits;//位圖陣列的起始位置,以字節為單位 } BITMAPFILEHEADER;
圖3.4 位圖文件頭的數據結構
(2)信息頭中各個段的地址及其內容如圖3.5所示。
圖3.5 位圖信息頭的數據結構包含了有關BMP圖象的寬,高,壓縮方法等信息,它的C語言數據結構如圖3.6所示。Typedef struct { long biSize; //指出本數據結構所需要的字節數
long biWidth;//以象素為單位,給出BMP圖象的寬度 long biHeight;//以象素為單位,給出BMP圖象的高度 int
biPlanes;//輸出設備的位平面數,必須置為1 int
biBitCount;//給出每個象素的位數 long biCompress;//給出位圖的壓縮類型 long biSizeImage;//給出圖象字節數的多少 long biXPelsPerMeter;//圖像的水平分辨率 long biYPelsPerMeter;//圖象的垂直分辨率
long biClrUsed;//調色板中圖象實際使用的顏色素數 long biClrImportant;//給出重要顏色的索引值 } BITMAPINFOHEADER;
圖3.6 BITMAPINFOHEADER數據結構
(3)對于象素小于或等于16位的圖片,都有一個顏色表用來給圖象數據陣列提供顏色索引,其中的每塊數據都以B、G、R的順序排列,還有一個是reserved保留位。而在圖形數據區域存放的是各個象素點的索引值。它的C語言結構如圖3.7所示。
圖3.7 顏色表數據結構(4)對于24位和32位的圖片,沒有彩色表,他在圖象數據區里直接存放圖片的RGB數據,其中的每個象素點的數據都以B、G、R的順序排列。每個象素點的數據結構如圖3.8所示。
圖3.8 圖象數據陣列的數據結構(5)由于圖象數據陣列中的數據是從圖片的最后一行開始往上存放的,因此在顯示圖象時,是從圖象的左下角開始逐行掃描圖象,即從左到右,從下到上。
(6)對S3C2410或PXA255開發板上的LCD來說,他們每個象素點所占的位數為16位,這16位按B:G:R=5:6:5的方式分,其中B在最高位,R在最低位。而從bmp圖象得到的R、G、B數據則每個數據占8位,合起來一共24位,因此需要對該R、G、B數據進行移位組合成一個16位的數據。移位方法如下: b >>= 3;g >>= 2;r >>= 3;RGBValue =(r<<11 | g << 5 | b);基于以上分析,提取各種類型的bmp圖象的流程如圖3.9所示
圖 3.9
本篇文章來源于 Linux公社網站(本篇文章來源于 Linux公社網站(
第四篇:S3C2410 LCD驅動學習心得-6
三
實驗過程與結果 1.Linux 源代碼的修改
首先修改arch/arm/mach-smdk2410.c文件,加入以下代碼。static struct s3c2410fb_mach_info smdk2440_lcd_cfg __initdata = {.regs = {
.lcdcon1 = S3C2410_LCDCON1_TFT16BPP |
S3C2410_LCDCON1_TFT |
S3C2410_LCDCON1_CLKVAL(7),.lcdcon2 = S3C2410_LCDCON2_VBPD(4)|
S3C2410_LCDCON2_LINEVAL(319)|
S3C2410_LCDCON2_VFPD(1)|
S3C2410_LCDCON2_VSPW(1),.lcdcon3 = S3C2410_LCDCON3_HBPD(26)|
S3C2410_LCDCON3_HOZVAL(239)|
S3C2410_LCDCON3_HFPD(30),.lcdcon4 = S3C2410_LCDCON4_HSPW(13)|
S3C2410_LCDCON4_MVAL(13),.lcdcon5 = S3C2410_LCDCON5_FRM565 |
S3C2410_LCDCON5_INVVLINE |
S3C2410_LCDCON5_INVVFRAME |
S3C2410_LCDCON5_PWREN |
S3C2410_LCDCON5_HWSWP, },.lpcsel =((0xCE6)& ~7)| 1<<4,.width = 240,.height = 320,.xres = {
.min = 240,.max = 240,.defval = 240,},.yres = {
.min = 320,.max = 320,.defval = 320,},.bpp = {
.min = 16,.max = 16,.defval = 16,}, };在函數smdk2410_machine_init()函數中加入LCD的初始化代碼,見下 static void __init smdk2410_machine_init(void){ s3c24xx_fb_set_platdata(&smdk2440_lcd_cfg);smdk_machine_init();} 2.編譯內核,產生zImage文件,放入tftp目錄下。
3.在nfs的dev目錄下建立FrameBuffer的設備節點,使用命令:
mknod fb0 c 29 0 4.啟動開發板,加載內核和文件系統。5.編寫LCD的應用程序,程序見附錄。
6.采用arm-linux-gcc 編譯應用程序,產生可執行文件,放入nfs目錄中。7.在開發板上運行編譯好的可執行文件,便可。
8.下圖是BMP位圖顯示程序,在S3C2410上的運行結果。
四
實驗心得體會
1. LCD驅動的主要問題是沒有LCD屏的文檔,我們找不到它的那些參數值,后來只能參照Linux源碼里面的其他LCD屏的參數進行實驗。
2. 在驅動差錯的過程中,我們采用跟蹤打印的方法進行調試。剛開始的時候,內核打印出一行找不到LCD設備。我們定位到輸出這行提示的代碼處,進行反向跟蹤。發現傳給函數的設備指針為空,于是往上排查,終于發現源代碼中沒有定義LCD的設備信息。于是驅動問題也就順利解決了。
3. 原來一直以為,只要LCD驅動工作正常了,內核起來的時候,液晶屏會顯示出Logo。當時搞了很久一直沒有,還以為是驅動的問題。后來隨便寫了一個LCD應用程序,竟然能用。4. 在調試過程應用程序中發現,在讀取文件頭的時候,如果直接定義一個bitmapfileheader為它動態分配內存:
*bmph=(bitmapfileheader*)malloc(sizeof(bitmapfileheader));然后用fread((char*)bmph,sizeof(bitmapfileheader),1,f)把文件頭一次性讀出來,讀出來的文件頭是錯誤的,經過調試發現原因是bitmapfileheader這個結構體中的type屬性原本應該占2字節,但是被編譯器在分配內存的時候進行了內存對齊的優化,給他分配了4個字節的空間,造成讀文件的錯誤。因此在編程中要特別注意內存對齊的影響。typedef struct { WORD
type;(被優化)DWORD bfsize;DWORD reserved;DWORD offbits;} bitmapfileheader;5. 在嵌入式應用程序的移植過程中,我們原來認為ARM和PC機大小尾順序是不同的,因此在應用程序中,也對這個差別進行了處理。當時,在調試過程中發現,PC機程序可以直接移植到ARM上,不需要任何改動。但是我們的程序,的確存在會產生大小尾問題代碼(在使用fread()讀入時)。這究竟是為什么?有人說,ARM是可以設置大小尾順序的。后來這個問題也沒有深究下去。
五
參考文獻
(1)嵌入式系統設計與應用開發:鄭靈翔.北京:北京航天航空大學出版社 2006
本篇文章來源于 Linux公社網站(
第五篇:S3c2410 LCD驅動學習心得
S3c2410 LCD驅動學習心得
一 實驗內容簡要描述 1.實驗目的
學會驅動程序的編寫方法,配置S3C2410的LCD驅動,以及在LCD屏上顯示包括bmp和jpeg兩種格式的圖片 2.實驗內容
(1)分析S3c2410實驗箱LCD以及LCD控制器的硬件原理,據此找出相應的硬件設置參數,參考xcale實驗箱關于lcd的設置,完成s3c2410實驗箱LCD的設置
(2)在LCD上顯示一張BMP圖片或JPEG圖片 3.實驗條件(軟硬件環境)
PC機、S3C2410開發板、PXA255開發板 二 實驗原理
1.S3C2410內置LCD控制器分析 1.1 S3C2410 LCD控制器
一塊LCD屏顯示圖像,不但需要LCD驅動器,還需要有相應的LCD控制器。通常LCD驅動器會以COF/COG的形式與LCD 玻璃基板制作在一起,而LCD控制器則由外部電路來實現。而S3C2410內部已經集成了LCD控制器,因此可以很方便地去控制各種類型的LCD屏,例如:STN和TFT屏。S3C2410 LCD控制器的特性如下:(1)STN屏
支持3種掃描方式:4bit單掃、4位雙掃和8位單掃 支持單色、4級灰度和16級灰度屏
支持256色和4096色彩色STN屏(CSTN)
支持分辯率為640*480、320*240、160*160以及其它規格的多種LCD(2)TFT屏
支持單色、4級灰度、256色的調色板顯示模式 支持64K和16M色非調色板顯示模式
支持分辯率為640*480,320*240及其它多種規格的LCD 對于控制TFT屏來說,除了要給它送視頻資料(VD[23:0])以外,還有以下一些信號是必不可少的,分別是:
VSYNC(VFRAME):幀同步信號 HSYNC(VLINE):行同步信號 VCLK :像數時鐘信號
VDEN(VM):數據有效標志信號
由于本項目所用的S3C2410上的LCD是TFT屏,并且TFT屏將是今后應用的主流,因此接下來,重點圍繞TFT屏的控制來進行。
圖1.1是S3C2410內部的LCD控制器的邏輯示意圖:
圖1.1 REGBANK 是LCD控制器的寄存器組,用來對LCD控制器的各項參數進行設置。而 LCDCDMA 則是LCD控制器專用的DMA信道,負責將視頻資料從系統總線(System Bus)上取來,通過 VIDPRCS 從VD[23:0]發送給LCD屏。同時 TIMEGEN 和 LPC3600 負責產生 LCD屏所需要的控制時序,例如VSYNC、HSYNC、VCLK、VDEN,然后從 VIDEO MUX 送給LCD屏。
1.2 TFT屏時序分析
圖1.2是TFT屏的典型時序。其中VSYNC是幀同步信號,VSYNC每發出1個脈沖,都意味著新的1屏視頻資料開始發送。而HSYNC為行同步信號,每個HSYNC脈沖都表明新的1行視頻資料開始發送。而VDEN則用來標明視頻資料的有效,VCLK是用來鎖存視頻資料的像數時鐘。
并且在幀同步以及行同步的頭尾都必須留有回掃時間,例如對于VSYNC來說前回掃時間就是(VSPW+1)+(VBPD+1),后回掃時間就是(VFPD +1);HSYNC亦類同。這樣的時序要求是當初CRT顯示器由于電子槍偏轉需要時間,但后來成了實際上的工業標準,乃至于后來出現的TFT屏為了在時序上于CRT兼容,也采用了這樣的控制時序。
圖1.2 S3C2410實驗箱上的LCD是一款3.5寸TFT真彩LCD屏,分辯率為240*320,下圖為該屏的時序要求。
圖1.3 通過對比圖1.2和圖1.3,我們不難看出: VSPW+1=2-> VSPW=1 VBPD+1=2-> VBPD=1 LINVAL+1=320-> LINVAL=319 VFPD+1=3-> VFPD=2 HSPW+1=4-> HSPW=3 HBPD+1=7-> HBPW=6 HOZVAL+1=240-> HOZVAL=239 HFPD+1=31-> HFPD=30 以上各參數,除了LINVAL和HOZVAL直接和屏的分辯率有關,其它的參數在實際操作過程中應以上面的為參考,不應偏差太多。
1.3 LCD控制器主要寄存器功能詳解
圖1.4 LINECNT :當前行掃描計數器值,標明當前掃描到了多少行。
CLKVAL :決定VCLK的分頻比。LCD控制器輸出的VCLK是直接由系統總線(AHB)的工作頻率HCLK直接分頻得到的。做為240*320的TFT屏,應保證得出的VCLK在5~10MHz之間。MMODE :VM信號的觸發模式(僅對STN屏有效,對TFT屏無意義)。
PNRMODE :選擇當前的顯示模式,對于TFT屏而言,應選擇[11],即TFT LCD panel。BPPMODE :選擇色彩模式,對于真彩顯示而言,選擇16bpp(64K色)即可滿足要求。ENVID :使能LCD信號輸出。
圖1.5 VBPD,LINEVAL,VFPD,VSPW 的各項含義已經在前面的時序圖中得到體現。
圖1.6 HBPD,HOZVAL,HFPD 的各項含義已經在前面的時序圖中得到體現。
圖1.7 HSPW 的含義已經在前面的時序圖中得到體現。MVAL 只對 STN屏有效,對TFT屏無意義。
HSPW 的含義已經在前面的時序圖中得到體現,這里不再贅述。MVAL 只對 STN屏有效,對TFT屏無意義。
圖1.8 VSTATUS :當前VSYNC信號掃描狀態,指明當前VSYNC同步信號處于何種掃描階段。HSTATUS :當前HSYNC信號掃描狀態,指明當前HSYNC同步信號處于何種掃描階段。
BPP24BL :設定24bpp顯示模式時,視頻資料在顯示緩沖區中的排列順序(即低位有效還是高位有效)。對于16bpp的64K色顯示模式,該設置位無意義。
FRM565 :對于16bpp顯示模式,有2中形式,一種是RGB=5:5:5:1,另一種是5:6:5。后一種模式最為常用,它的含義是表示64K種色彩的16bit RGB資料中,紅色(R)占了5bit,綠色(G)占了6bit,蘭色(B)占了5bit INVVCLK,INVLINE,INVFRAME,INVVD :通過前面的時序圖,我們知道,CPU的LCD控制器輸出的時序默認是正脈沖,而LCD需要VSYNC(VFRAME)、VLINE(HSYNC)均為負脈沖,因此 INVLINE 和 INVFRAME 必須設為“1 ”,即選擇反相輸出。INVVDEN,INVPWREN,INVLEND 的功能同前面的類似。
PWREN 為LCD電源使能控制。在CPU LCD控制器的輸出信號中,有一個電源使能管腳LCD_PWREN,用來做為LCD屏電源的開關信號。
ENLEND 對普通的TFT屏無效,可以不考慮。
BSWP 和 HWSWP 為字節(Byte)或半字(Half-Word)交換使能。由于不同的GUI對FrameBuffer(顯示緩沖區)的管理不同,必要時需要通過調整 BSWP 和 HWSWP 來適應GUI。2.Linux 驅動 2.1 FrameBuffer Linux是工作在保護模式下,所以用戶態進程是無法像DOS那樣使用顯卡BIOS里提供的中斷調用來實現直接寫屏,Lin仿顯卡的功能,將顯ux抽象出FrameBuffer這個設備來供用戶態進程實現直接寫屏。Framebuffer機制??ㄓ布Y構抽象掉,可以通過Framebuffer的讀寫直接對顯存進行操作。用戶可以將Framebuffer看成是顯示內存的一個映像,將其映射到進程地址空間之后,就可以直接進行讀寫操作,而寫操作可以立即反應在屏幕上。這種操作是抽象的,統一的。用戶不必關心物理顯存的位置、換頁機制等等具體細節。這些都是由Framebuffer設備驅動來完成的。
在Linux系統下,FrameBuffer的主要的結構如圖所示。Linux為了開發FrameBuffer程序的方便,使用了分層結構。fbmem.c處于Framebuffer設備驅動技術的中心位置。它為上層應用程序提供系統調用,也為下一層的特定硬件驅動提供接口;那些底層硬件驅動需要用到這兒的接口來向系統內核注冊它們自己。
fbmem.c 為所有支持FrameBuffer的設備驅動提供了通用的接口,避免重復工作。下將介紹fbmem.c主要的一些數據結構。
2.2 數據結構
2.2.1 Linux FrameBuffer的數據結構
在FrameBuffer中,fb_info可以說是最重要的一個結構體,它是Linux為幀緩沖設備定義的驅動層接口。它不僅包含了底層函數,而且還有記錄設備狀態的數據。每個幀緩沖設備都與一個fb_info結構相對應。fb_info的主要成員如下 struct fb_info { int node;struct fb_var_screeninfo var;/* Current var */ struct fb_fix_screeninfo fix;/* Current fix */ struct fb_videomode *mode;/* current mode */
struct fb_ops *fbops;struct device *device;/* This is the parent */ struct device *dev;/* This is this fb device */
char __iomem *screen_base;/* Virtual address */ unsigned long screen_size;/* Amount of ioremapped VRAM or 0 */
………… };其中node成員域標示了特定的FrameBuffer,實際上也就是一個FrameBuffer設備的次設備號。fb_var_screeninfo結構體成員記錄用戶可修改的顯示控制器參數,包括屏幕分辨率和每個像素點的比特數。fb_var_screeninfo中的xres定義屏幕一行有多少個點, yres定義屏幕一列有多少個點, bits_per_pixel定義每個點用多少個字節表示。其他域見以下代碼注釋。struct fb_var_screeninfo { __u32 xres;/* visible resolution */ __u32 yres;__u32 xoffset;/* offset from virtual to visible */ __u32 yoffset;/* resolution */ __u32 bits_per_pixel;/* bits/pixel */ __u32 pixclock;/* pixel clock in ps(pico seconds)*/ __u32 left_margin;/* time from sync to picture */ __u32 right_margin;/* time from picture to sync */ __u32 hsync_len;/* length of horizontal sync */ __u32 vsync_len;/* length of vertical sync */ ………… };在fb_info結構體中,fb_fix_screeninfo中記錄用戶不能修改的顯示控制器的參數,如屏幕緩沖區的物理地址,長度。當對幀緩沖設備進行映射操作的時候,就是從fb_fix_screeninfo中取得緩沖區物理地址的。struct fb_fix_screeninfo { char id[16];/* identification string eg “TT Builtin” */ unsigned long smem_start;/* Start of frame buffer mem(physical address)*/ __u32 smem_len;/* Length of frame buffer mem */ unsigned long mmio_start;/* Start of Mem Mapped I/O(physical address)*/ __u32 mmio_len;/* Length of Memory Mapped I/O */ ………… };fb_info還有一個很重要的域就是fb_ops。它是提供給底層設備驅動的一個接口。通常我們編寫字符驅動的時候,要填寫一個file_operations結構體,并使用register_chrdev()注冊之,以告訴Linux如何操控驅動。當我們編寫一個FrameBuffer的時候,就要依照Linux FrameBuffer編程的套路,填寫fb_ops結構體。這個fb_ops也就相當于通常的file_operations結構體。struct fb_ops { int(*fb_open)(struct fb_info *info, int user);int(*fb_release)(struct fb_info *info, int user);ssize_t(*fb_read)(struct file *file, char __user *buf, size_t count, loff_t *ppos);ssize_t(*fb_write)(struct file *file, const char __user *buf, size_t count, loff_t *ppos);int(*fb_set_par)(struct fb_info *info);int(*fb_setcolreg)(unsigned regno, unsigned red, unsigned green, unsigned blue, unsigned transp, struct fb_info *info);int(*fb_setcmap)(struct fb_cmap *cmap, struct fb_info *info)int(*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma);…………… } 上面的結構體,根據函數的名字就可以看出它的作用,這里不在一一說明。下圖給出了Linux FrameBuffer的總體結構,作為這一部分的總結。圖2.2
2.2.2 S3C2410中LCD的數據結構
在S3C2410的LCD設備驅動中,定義了s3c2410fb_info來標識一個LCD設備,結構體如下: struct s3c2410fb_info { struct fb_info *fb;struct device *dev;struct s3c2410fb_mach_info *mach_info;struct s3c2410fb_hw regs;/* LCD Hardware Regs */ dma_addr_t map_dma;/* physical */ u_char * map_cpu;/* virtual */ u_int map_size;/* addresses of pieces placed in raw buffer */ u_char * screen_cpu;/* virtual address of buffer */ dma_addr_t screen_dma;/* physical address of buffer */ ………… };成員變量fb指向我們上面所說明的fb_info結構體,代表了一個FrameBuffer。dev則表示了這個LCD設備。map_dma,map_cpu,map_size這三個域向了開辟給LCD DMA使用的內存地址。screen_cpu,screen_dma指向了LCD控制器映射的內存地址。另外regs標識了LCD控制器的寄存器。struct s3c2410fb_hw { unsigned long lcdcon1;unsigned long lcdcon2;unsigned long lcdcon3;unsigned long lcdcon4;unsigned long lcdcon5;};這個寄存器和硬件的寄存器一一對應,主要作為實際寄存器的映像,以便程序使用。這個s3c2410fb_info中還有一個s3c2410fb_mach_info成員域。它存放了和體系結構相關的一些信息,如時鐘、LCD設備的GPIO口等等。這個結構體定義為 struct s3c2410fb_mach_info { unsigned char fixed_syncs;/* do not update sync/border */ int type;/* LCD types */ int width;/* Screen size */ int height;struct s3c2410fb_val xres;/* Screen info */ struct s3c2410fb_val yres;struct s3c2410fb_val bpp;struct s3c2410fb_hw regs;/* lcd configuration registers */ /* GPIOs */ unsigned long gpcup;unsigned long gpcup_mask;unsigned long gpccon;unsigned long gpccon_mask;………… };
圖2.3 上圖表示了S3C2410驅動的整體結構,反映了結構體之間的相互關系 2.3 主要代碼結構以及關鍵代碼分析 2.3.1 FrameBuffer驅動的統一管理
fbmem.c實現了Linux FrameBuffer的中間層,任何一個FrameBuffer驅動,在系統初始化時,必須向fbmem.c注冊,即需要調用register_framebuffer()函數,在這個過程中,設備驅動的信息將會存放入名稱為registered_fb數組中,這個數組定義為 struct fb_info *registered_fb[FB_MAX];int num_registered_fb;它是類型為fb_info的數組,另外num_register_fb則存放了注冊過的設備數量。
我們分析一下register_framebuffer的代碼。int register_framebuffer(struct fb_info *fb_info){ int i;struct fb_event event;struct fb_videomode mode;if(num_registered_fb == FB_MAX)return-ENXIO;/* 超過最大數量 */ num_registered_fb++;for(i = 0;i < FB_MAX;i++)if(!registered_fb[i])break;/* 找到空余的數組空間 */ fb_info->node = i;
fb_info->dev = device_create(fb_class, fb_info->device, MKDEV(FB_MAJOR, i), “fb%d”, i);/* 為設備建立設備節點 */ if(IS_ERR(fb_info->dev)){ ………… } else{ fb_init_device(fb_info);/* 初始化改設備 */ } ………… return 0;} 從上面的代碼可知,當FrameBuffer驅動進行注冊的時候,它將驅動的fb_info結構體記錄到全局數組registered_fb中,并動態建立設備節點,進行設備的初始化。注意,這里建立的設備節點的次設備號就是該驅動信息在registered_fb存放的位置,即數組下標i。在完成注冊之后,fbmem.c就記錄了驅動的fb_info。這樣我們就有可能實現fbmem.c對全部FrameBuffer驅動的統一處理。
2.3.2 實現消息的分派
fbmem.c實現了對系統全部FrameBuffer設備的統一管理。當用戶嘗試使用一個特定的FrameBuffer時,fbmem.c怎么知道該調用那個特定的設備驅動呢?
我們知道,Linux是通過主設備號和次設備號,對設備進行唯一標識。不同的FrameBuffer設備向fbmem.c注冊時,程序分配給它們的主設備號是一樣的,而次設備號是不一樣的。于是我們就可以通過用戶指明的次設備號,來覺得具體該調用哪一個FrameBuffer驅動。下面通過分析fbmem.c的fb_open()函數來說明。(注:一般我們寫FrameBuffer驅動不需要實現open函數,這里只是說明函數流程。)static int fb_open(struct inode *inode, struct file *file){ int fbidx = iminor(inode);struct fb_info *info;int res;/* 得到真正驅動的函數指針 */ if(!(info = registered_fb[fbidx]))return-ENODEV;if(info->fbops->fb_open){ res = info->fbops->fb_open(info,1);//調用驅動的open()if(res)module_put(info->fbops->owner);} return res;} 當用戶打開一個FrameBuffer設備的時,將調用這里的fb_open()函數。傳進來的inode就是欲打開設備的設備號,包括主設備和次設備號。fb_open函數首先通過iminor()函數取得次設備號,然后查全局數組registered_fb得到設備的fb_info信息,而這里面存放了設備的操作函數集fb_ops。這樣,我們就可以調用具體驅動的fb_open()函數,實現open的操作。下面給出了一個LCD驅動的open()函數的調用流程圖,用以說明上面的步驟。
圖2.4
2.3.3 開發板S3C2410 LCD驅動的流程
(1)在mach-smdk2410.c中,定義了初始的LCD參數。注意這是個全局變量。static struct s3c2410fb_mach_info smdk2410_lcd_cfg = {.regs= {.lcdcon1 = S3C2410_LCDCON1_TFT16BPP | S3C2410_LCDCON1_TFT| S3C2410_LCDCON1_CLKVAL(7),......},.width = 240,.height = 320,.xres = {.min = 240,.max= 240,.defval = 240},.bpp = {.min = 16,.max= 16,.defval = 16},......};(2)內核初始化時候調用s3c2410fb_probe函數。下面分析這個函數的做的工作。首先先動態分配s3c2410fb_info空間。
fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info),&pdev->dev);把域mach_info指向mach-smdk2410.c中的smdk2410_lcd_cfg。info->mach_info = pdev->dev.platform_data;設置fb_info域的fix,var,fops字段。
fbinfo->fix.type = FB_TYPE_PACKED_PIXELS;fbinfo->fix.type_aux = 0;fbinfo->fix.xpanstep = 0;
fbinfo->var.nonstd = 0;fbinfo->var.activate = FB_ACTIVATE_NOW;fbinfo->var.height = mach_info->height;fbinfo->var.width = mach_info->width;
fbinfo->fbops = &s3c2410fb_ops;……
該函數調用s3c2410fb_map_video_memory()申請DMA內存,即顯存。
fbi->map_size = PAGE_ALIGN(fbi->fb->fix.smem_len + PAGE_SIZE);fbi->map_cpu = dma_alloc_writecombine(fbi->dev, fbi->map_size, &fbi->map_dma, GFP_KERNEL);
fbi->map_size = fbi->fb->fix.smem_len;…….設置控制寄存器,設置硬件寄存器。
memcpy(&info->regs, &mach_info->regs,sizeof(info->regs));info->regs.lcdcon1 &= ~S3C2410_LCDCON1_ENVID;……….調用函數s3c2410fb_init_registers(),把初始值寫入寄存器。
writel(fbi->regs.lcdcon1, S3C2410_LCDCON1);writel(fbi->regs.lcdcon2, S3C2410_LCDCON2);
(3)當用戶調用mmap()映射內存的時候,Fbmem.c把剛才設置好的顯存區域映射給用戶。start = info->fix.smem_start;len = PAGE_ALIGN((start & ~PAGE_MASK)+ info->fix.smem_len);io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, vma->vm_end-vma->vm_start,vma->vm_page_prot);
……
這樣就完成了驅動初始化到用戶調用的整個過程。3.BMP和JPEG圖形顯示程序
3.1 在LCD上顯示BMP或JPEG圖片的主流程圖
首先,在程序開始前。要在nfs/dev目錄下創建LCD的設備結點,設備名fb0,設備類型為字符設備,主設備號為29,次設備號為0。命令如下: mknod fb0 c 29 0 在LCD上顯示圖象的主流程圖如圖3.1所示。程序一開始要調用open函數打開設備,然后調用ioctl獲取設備相關信息,接下來就是讀取圖形文件數據,把圖象的RGB值映射到顯存中,這部分是圖象顯示的核心。對于JPEG格式的圖片,要先經過JPEG解碼才能得到RGB數據,本項目中直接才用現成的JPEG庫進行解碼。對于bmp格式的圖片,則可以直接從文件里面提取其RGB數據。要從一個bmp文件里面把圖片數據陣列提取出來,首先必須知道bmp文件的格式。下面來詳細介紹bmp文件的格式。
圖3.1
3.2 bmp位圖格式分析
位圖文件可看成由四個部分組成:位圖文件頭、位圖信息頭、彩色表和定義位圖的字節陣列。如圖3.2所示。
圖3.2 文件頭中各個段的地址及其內容如圖3.3。
圖3.3 位圖文件頭數據結構包含BMP圖象文件的類型,顯示內容等信息。它的數據結構如下定義: Typedef struct {
int bfType;//表明位圖文件的類型,必須為BM long bfSize;//表明位圖文件的大小,以字節為單位 int bfReserved1;//屬于保留字,必須為本0 int bfReserved2;//也是保留字,必須為本0 long bfOffBits;//位圖陣列的起始位置,以字節為單位 } BITMAPFILEHEADER; 圖3.4 位圖文件頭的數據結構
(2)信息頭中各個段的地址及其內容如圖3.5所示。圖3.5 位圖信息頭的數據結構包含了有關BMP圖象的寬,高,壓縮方法等信息,它的C語言數據結構如圖3.6所示。Typedef struct { long biSize; //指出本數據結構所需要的字節數 long biWidth;//以象素為單位,給出BMP圖象的寬度 long biHeight;//以象素為單位,給出BMP圖象的高度 int biPlanes;//輸出設備的位平面數,必須置為1 int biBitCount;//給出每個象素的位數 long biCompress;//給出位圖的壓縮類型 long biSizeImage;//給出圖象字節數的多少 long biXPelsPerMeter;//圖像的水平分辨率 long biYPelsPerMeter;//圖象的垂直分辨率 long biClrUsed;//調色板中圖象實際使用的顏色素數 long biClrImportant;//給出重要顏色的索引值 } BITMAPINFOHEADER;
圖3.6 BITMAPINFOHEADER數據結構
(3)對于象素小于或等于16位的圖片,都有一個顏色表用來給圖象數據陣列提供顏色索引,其中的每塊數據都以B、G、R的順序排列,還有一個是reserved保留位。而在圖形數據區域存放的是各個象素點的索引值。它的C語言結構如圖3.7所示。
圖3.7 顏色表數據結構
(4)對于24位和32位的圖片,沒有彩色表,他在圖象數據區里直接存放圖片的RGB數據,其中的每個象素點的數據都以B、G、R的順序排列。每個象素點的數據結構如圖3.8所示。
圖3.8 圖象數據陣列的數據結構
(5)由于圖象數據陣列中的數據是從圖片的最后一行開始往上存放的,因此在顯示圖象時,是從圖象的左下角開始逐行掃描圖象,即從左到右,從下到上。
(6)對S3C2410或PXA255開發板上的LCD來說,他們每個象素點所占的位數為16位,這16位按B:G:R=5:6:5的方式分,其中B在最高位,R在最低位。而從bmp圖象得到的R、G、B數據則每個數據占8位,合起來一共24位,因此需要對該R、G、B數據進行移位組合成一個16位的數據。移位方法如下:
b >>= 3;g >>= 2;r >>= 3;RGBValue =(r<<11 | g << 5 | b);基于以上分析,提取各種類型的bmp圖象的流程如圖3.9所示
圖 3.9
3.3 實現顯示任意大小的圖片
開發板上的LCD屏的大小是固定的,S3C2410上的LCD為:240*320,PXA255上的為:640*480。比屏幕小的圖片在屏上顯示當然沒問題,但是如果圖片比屏幕大呢?這就要求我們通過某種算法對圖片進行縮放。
縮放的基本思想是將圖片分成若干個方塊,對每個方塊中的R、G、B數據進行取平均,得到一個新的R、G、B值,這個值就作為該方塊在LCD屏幕上的映射。縮放的算法描述如下:
(1)、計算圖片大小與LCD屏大小的比例,以及方塊的大小。為了適應各種屏幕大小,這里并不直接給lcd_width和lcd_height賦值為240和320。而是調用標準的接口來獲取有關屏幕的參數。具體如下:
// Get variable screen information if(ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo)){ printf(“Error reading variable information.”);exit(3);} unsigned int lcd_width=vinfo.xres;unsigned int lcd_height=vinfo.yres;
計算比例:
widthScale=bmpi->width/lcd_width;heightScale=bmpi->height/lcd_height;本程序中方塊的大小以如下的方式確定: unsigned int paneWidth= unsigned int paneHeight=;符號 代表向上取整。
(2)、從圖片的左上角開始,以(i* widthScale,j* heightScale)位起始點,以寬paneWidth 高paneHeight為一個小方塊,對該方塊的R、G、B數值分別取平均,得到映射點的R、G、B值,把該點作為要在LCD上顯示的第(i , j)點存儲起來。這部分的程序如下: //-------------取平均--------for(i=0;i
通過以上的分析,整個圖片數據提取及顯示的總流程如圖3.10 所示。圖 3.10 三 實驗過程與結果 1.Linux 源代碼的修改
首先修改arch/arm/mach-smdk2410.c文件,加入以下代碼。static struct s3c2410fb_mach_info smdk2440_lcd_cfg __initdata = {.regs = {.lcdcon1 = S3C2410_LCDCON1_TFT16BPP | S3C2410_LCDCON1_TFT | S3C2410_LCDCON1_CLKVAL(7),.lcdcon2 = S3C2410_LCDCON2_VBPD(4)| S3C2410_LCDCON2_LINEVAL(319)| S3C2410_LCDCON2_VFPD(1)| S3C2410_LCDCON2_VSPW(1),.lcdcon3 = S3C2410_LCDCON3_HBPD(26)| S3C2410_LCDCON3_HOZVAL(239)| S3C2410_LCDCON3_HFPD(30),.lcdcon4 = S3C2410_LCDCON4_HSPW(13)| S3C2410_LCDCON4_MVAL(13),.lcdcon5 = S3C2410_LCDCON5_FRM565 | S3C2410_LCDCON5_INVVLINE | S3C2410_LCDCON5_INVVFRAME | S3C2410_LCDCON5_PWREN | S3C2410_LCDCON5_HWSWP, },.lpcsel =((0xCE6)& ~7)| 1<<4,.width = 240,.height = 320,.xres = {.min = 240,.max = 240,.defval = 240, },.yres = {.min = 320,.max = 320,.defval = 320, },.bpp = {.min = 16,.max = 16,.defval = 16, }, };在函數smdk2410_machine_init()函數中加入LCD的初始化代碼,見下 static void __init smdk2410_machine_init(void){ s3c24xx_fb_set_platdata(&smdk2440_lcd_cfg);smdk_machine_init();} 2.編譯內核,產生zImage文件,放入tftp目錄下。
3.在nfs的dev目錄下建立FrameBuffer的設備節點,使用命令: mknod fb0 c 29 0 4.啟動開發板,加載內核和文件系統。5.編寫LCD的應用程序,程序見附錄。
6.采用arm-linux-gcc 編譯應用程序,產生可執行文件,放入nfs目錄中。7.在開發板上運行編譯好的可執行文件,便可。
8.下圖是BMP位圖顯示程序,在S3C2410上的運行結果。
四 實驗心得體會
1. LCD驅動的主要問題是沒有LCD屏的文檔,我們找不到它的那些參數值,后來只能參照Linux源碼里面的其他LCD屏的參數進行實驗。
2. 在驅動差錯的過程中,我們采用跟蹤打印的方法進行調試。剛開始的時候,內核打印出一行找不到LCD設備。我們定位到輸出這行提示的代碼處,進行反向跟蹤。發現傳給函數的設備指針為空,于是往上排查,終于發現源代碼中沒有定義LCD的設備信息。于是驅動問題也就順利解決了。
3. 原來一直以為,只要LCD驅動工作正常了,內核起來的時候,液晶屏會顯示出Logo。當時搞了很久一直沒有,還以為是驅動的問題。后來隨便寫了一個LCD應用程序,竟然能用。
4. 在調試過程應用程序中發現,在讀取文件頭的時候,如果直接定義一個bitmapfileheader為它動態分配內存:
*bmph=(bitmapfileheader*)malloc(sizeof(bitmapfileheader));然后用fread((char*)bmph,sizeof(bitmapfileheader),1,f)把文件頭一次性讀出來,讀出來的文件頭是錯誤的,經過調試發現原因是bitmapfileheader這個結構體中的type屬性原本應該占2字節,但是被編譯器在分配內存的時候進行了內存對齊的優化,給他分配了4個字節的空間,造成讀文件的錯誤。因此在編程中要特別注意內存對齊的影響。typedef struct { WORD type;(被優化)DWORD bfsize;DWORD reserved;DWORD offbits;} bitmapfileheader;5. 在嵌入式應用程序的移植過程中,我們原來認為ARM和PC機大小尾順序是不同的,因此在應用程序中,也對這個差別進行了處理。當時,在調試過程中發現,PC機程序可以直接移植到ARM上,不需要任何改動。但是我們的程序,的確存在會產生大小尾問題代碼(在使用fread()讀入時)。這究竟是為什么?有人說,ARM是可以設置大小尾順序的。后來這個問題也沒有深究下去。五 參考文獻
(1)嵌入式系統設計與應用開發:鄭靈翔.北京:北京航天航空大學出版社 2006