S3C2410 LCD 驅動程式移植及GUI程式編寫
1. 為了不讓大家覺枯燥,讓朋友們更好的了解,我以一個執行個體來叙述 S3C2410 下一個驅動程式的編寫(本文的初始化源碼以華恒公司提供的 s3c2410fb.c 為基礎)及簡單的 GUI程式的編寫。
2. 拿到一塊 LCD,首先要将 LCD的各個控制線與 S3C2410 的 LCD控制信号相接,當然,電源也一定要接入了,否則不亮可别找我。另外需要注意以下幾點:
1) 背光:對于大部分的彩色 LCD一定要接背光,我們才能看到屏上的内容;
2) 控制信号:不同的 LCD 廠商對于控制信号有不同的叫法,S3C2410 晶片手冊也給出了一個信号的多個名稱(圖一),這就要看你們硬體工程師的功底了,
圖一 S3C2410 手冊上給出的控制信号的名稱及解釋
這裡我做一個簡單的介紹:
VFRAME:LCD 控制器和 LCD 驅動器之間的幀同步信号。該信号告訴 LCD屏的新的一幀開始了。LCD 控制器在一個完整幀顯示完成後立即插入一個VFRAME 信号,開始新一幀的顯示;
VLINE:LCD控制器和 LCD驅動器之間的線同步脈沖信号,該信号用于 LCD驅動器将水準線(行)移位寄存器的内容傳送給 LCD 屏顯示。LCD 控制器在整個水準線(整行)資料移入 LCD驅動器後,插入一個 VLINE 信号;
VCLK:LCD控制器和 LCD驅動器之間的像素時鐘信号,由 LCD控制器送出的資料在 VCLK的上升沿處送出,在 VCLK的下降沿處被 LCD驅動器采樣;
VM:LCD驅動器的 AC 信号。VM 信号被 LCD驅動器用于改變行和列的電壓極性,進而控制像素點的顯示或熄滅。VM 信号可以與每個幀同步,也可以與可變數量的 VLINE 信号同步。
3) 資料線:也就是我們說的 RGB 信号線,S3C2410 晶片手冊上都有詳細的說明,由于篇幅關系,在此不一一摘錄,不過需要與硬體工程是配合的是他采用了哪種接線方法,24 位 16 位或其它。對于 16 位 TFT 屏又有兩種方式,在寫驅動前你要清楚是 5:6:5還是 5:5:5:I,這些與驅動的編寫都有關系
4) 要注意一下 LCD 的電源電壓,對于手持裝置來說一般都為 5V 或 3.3V,或同時支援 5V和 3.3V,如果 LCD的需要的電源電壓是 5V,那就要注意了,S3C2410 的邏輯輸出電壓隻有 3.3V,此時一定要讓你們的硬體工程師幫忙把 S3C2410 的邏輯輸出電壓提高到 5V,否則你可能能将屏點亮,但顯示的圖像要等到太陽從西邊出來的那一天才能正常,呵呵,我可吃過苦頭的哦!
5) 3.3V邏輯電壓轉變成 5V邏輯電壓電路圖(此圖由華恒公司提供)
6) 最後還有一個問題,有些 LCD 屏還需要一顆伴侶晶片,就是 S3C2410 手冊中的那顆 LPC3600。這可能在 LCD 的手冊中都有論述吧,我沒有遇到過這樣的屏,是以也不是很清楚。那麼是不是所有的屏與 S3C2410相接都需要那個讨厭的家夥呢?這是好多人(包括我)在最開始都會有的疑問,不過現在的大部分 LCD 屏應該都不需要這個讨厭的家夥了,屏的控制信号直接與 S3C2410 的控制信号相接就可以了,至少我還沒有遇到過。
7) 還得提醒大家一下,S3C2410到 LCD屏的連線千萬千萬别超過 0.5 米,否則會給你帶來麻煩,我也是吃過苦頭的,LCD屏上面的部分顯示任何資訊都是正确的,而隻有屏的底部會有時正确有時錯誤,折騰了好一陣,才知道是連線太長的緣故!
3. 好了,在硬體工程師的幫助下,硬體接好了,那就該我們做軟體的幹活了,編寫驅動吧
1) 讓我們首先看一下 RGB資料結構的定義
在 s3c2410fb.c 中找到如下資訊
static struct s3c2410fb_rgb xxx_tft_rgb_16 = {
red: {offset:11, length:5,},
green: {offset:5, length:6,},
blue: {offset:0, length:5,},
transp: {offset:0, length:0,},
};
這是對 16 位色的 RGB 顔色進行定義,R:G:B:I = 5:6:5:0,即我們常說的565 顯示方式。呵呵,為了讓有些朋友更好的了解,我多羅嗦幾句,我們随便寫一個 16 位資料的顔色資料(為了分析的友善,我把它寫成二進制)
RGB = 10101101 10111001
根據上面的結構定義我們來分析一下 RGB 各是多少(因為沒有透明色,我們不去分析)
a) blue: {offset: 0, length: 5} 偏移量為 0,長度為 5,我們從那個 RGB 中提取出來便是“11001”
b) green:{offset: 5, length: 6} 偏移量為 5,長度為 6,我們從那個 RGB 中提取出來便是 101 101
c) red: {offset: 11, length: 5 } 偏移量為 11,長度為 5,我們從那個 RGB 中提取出來便是 10101
d) 我們得到了一個 RGB 值為 13:45:200,就是這個顔色
e) 那麼反過來,有了 RGB的值我們該如何,因為 RGB 的有效位數都不足一個位元組(8 位),那我們隻能忍痛割愛了,舍棄掉低位資料,代碼如下
r=(rDat&0xF8);
g=(gDat&0xFC);
b=(bDat&0xF8);
hight=r|(g>>5);
low=(g<<3)|(b>>3);
color=(hight<<8)|low;
記住,這段代碼在 GUI 程式中是有用的
2) 對于 8 位色(256 色)的資料結構定義
static struct s3c2410fb_rgb rgb_8 = {
red: {offset:0, length:4,},
green: {offset:0, length:4,},
blue: {offset:0, length:4,},
transp: {offset:0, length:0,},
};
這是原程式中給出的定義,我感覺有些錯誤,我認為應該為 R:G:B = 3:3:2
static struct s3c2410fb_rgb rgb_8 = {
red: {offset:5, length:3,},
green: {offset:2, length:3,},
blue: {offset:0, length:2,},
transp: {offset:0, length:0,},
};
因為沒有親自去調試,是以沒有什麼發言權,希望做過這方面的朋友給我一個答案。
3) 對于 CSTN 屏,一般都能達到 12 位色(4096 色)的,S3C2410 這顆晶片也是支援的,但是在軟體方面要做的工作比較大,因為從原有的代碼,我們找不到任何 12位色顯示的迹象,另外 Linux 本身好像也不支援 12 位色的,如果你要作的事情比較簡單,那你就自己寫代碼吧。我在此給出 12位色的資料結構定義
static struct s3c2410fb_rgb xxx_stn_rgb_12 = {
red: {offset:8, length:4,},
green: {offset:4, length:4,},
blue: {offset:0, length:4,},
transp: {offset:0, length:0,},
};
但是要完成 12 位色 CSTN 屏驅動程式的編寫還有一些工作要做,稍後我會适當的向大家介紹。
4) 接着看下面的代碼,其中要修改的部分已經用綠色标出,下面分别進行介紹。
a) 顔色位數
bpp:16
如果你的 LCD 屏是 TFT 的,那一般都可以達到 16 位色或 24 位色,這也要看硬體怎麼連接配接了,根據情況進行設定即可;
如果你的 LCD屏是 CSTN的,按照正常 LCD手冊的介紹,一般都可以支援到8 位色(256色),而實際的 CSTN屏的顯示效果都可以達到 12 位色(4096色),那可有很大的差別的,如果你要選擇便宜的屏又要豐富的顔色,那就費點勁,完成 12 位色的驅動。
b) LCD屏的寬度和高度
xres: 240
yres: 320
這個就不用多說了,你的屏的分辨率是多少就設定成多少呗。
c) 寄存器的設定,這些也不困難。下面就讓我們一起一口一口的将 S3C2410 的LCD寄存器統統吃掉! 首先介紹一下我這塊屏,這是日立的一塊 TFT 屏,大小為 640X240,可以支援到 16位色。 與驅動有關的一張表
圖二 LCD屏資料
有了這些資訊,讓我們看一下 LCD寄存器的設定。
LCD控制器1
LINECNT --- 這是一個隻讀的資料,我們當然沒有必要理它
CLKVAL --- 這可是一個很有用的參數,其實沒必要管它後面的計算,我們可以通過實際的測試來得出一個有效的值,對于320x240 的屏一般設定為 7 就可以了,而對于 640x480 的屏,該值可以小一點。對于後面的計算公式及注釋(STN: CLKVAL >= 2,TFT: CLKVAL >= 0),我不知道該如何去了解,因為在實際的應用中我點了一塊 640X240 的CSTN 屏,當我的 CLKVAL = 1 時才達到了一個最佳的效果,這似乎與說明書相違背,我也解釋不清為什麼?!
PNRMODE --- 這個應該不用多做解釋,大家一看都明白了,對于 TFT 屏,隻能設定成 11,而對于 CSTN 屏,可能需要根據實際屏的資訊去設定,我遇到的屏都設定成 10,即 8bit 單掃描模式。對于4bit單掃描、4bit 雙掃描、8bit 單掃描的說明在 s3c2410 的手冊中有詳細的介紹,大家可以去參考一下。
BPPMODE --- 這個參數更不用多說了吧,就是設定屏的顔色位數喽。
這些參數的設定都很簡單,我給出我這塊屏的定義:
lcdcon1: LCD1_BPP_16T | LCD1_PNR_TFT | LCD1_CLKVAL(1),
同時,我也給出一塊 CSTN 屏的寄存器參數資訊
lcdcon1: LCD1_BPP_12S | LCD1_PNR_8S | LCD1_CLKVAL(9),
LCD控制器2
對于 TFT 屏必須要填,至于什麼意思怎麼翻譯,相信大家都比我的水準強,自己翻譯吧。我隻說明從 LCD中如何将這個值“扣”出來。
很容易,看一下圖二 LCD屏資料,對比一下得出如下資訊:
LCD2_VBPD:
Vertical back porch 典型值為 7
LCD2_VFPD:
Vertical front porch 典型值為 4
LCD2_VSPW:
Vsync Valid width 典型值為 2
關于 LINEVAL 在程式的後面将會提到,此處不必理會。
經過分析,我們知道了如何設定 LCD2:
lcdcon2: LCD2_VBPD(7) | LCD2_VFPD(4) | LCD2_VSPW(2),
對于 STN(CSTN)屏,這個寄存器的設定最簡單,将 VBPD、VFPD、VSPW 都設定成 Zero 就可以了。即
lcdcon2: LCD2_VBPD(0) | LCD2_VFPD(0) | LCD2_VSPW(0),
LCD控制器3
對于 TFT 屏,很容易将 HBPD 和 HFPD 找出來,如下
LCD3_HBPD:
Horizontal back porch 典型值為 37
LCD3_HFBD:
Horizontal back porch 典型值為 32
對于 HOZVAL 同樣會在後面提到,此處暫時不管
經過分析,我們知道了如何設定 LCD3:
lcdcon3: LCD3_HBPD(37) | LCD3_HFPD(32) ,
對于(STN)CSTN屏,我沒有很好的了解 WDLY 和 LINEBLANK 的真正涵義,通過改變這兩個參數的值,我也沒有得到特别明顯的差異,我一般設定為:
lcdcon3: LCD3_WDLY_16 | 0x10 ,
LCD控制器4
對于 TFT 屏,需要設定 HSPW 的值,這個在 LCD 手冊上也很容易得到
LCD4_HSPW:
Hsync Valid width 典型值為 5
至于 MVAL,我不知道是什麼意思,有什麼作用,我從來不動它,隻取它最初的那個值 13
經過分析,我們知道了如何設定 LCD4:
lcdcon4: LCD4_HSPW(5) | LCD4_MVAL(13) ,
對于 STN(CSTN)屏,像 WDLY 一樣,我通常不改變,因為改變了沒有發現有什麼作用,這是我驅動中的代碼,好幾塊屏都一樣的:
lcdcon4: LCD4_WLH(0) | LCD4_MVAL(13) ,
LCD控制器5
這個寄存器的看起來比較複雜,但是無外乎這幾類:
隻讀資訊:VSTATUS和 HSTATUS
隻讀的東東,設定它也沒用,不必理會。
TFT 屏的顔色資訊:BPP24BL、FRM565
TFT 屏的顔色資訊,這個我們在 LCD的硬體連接配接時已經提到了,根據具體的接線方式,設定資訊。
控制信号的極性
TFT/STN 屏控制信号的極性:INVVCLK、INVVLINE、INVVFRAME、INVVD、INVPWREN、PWREN
TFT 屏特有的控制信号的極性:INVVDEN、INVLEND、ENLEND
這些資訊主要是使S3C2410的信号輸出極性與LCD屏的輸入極性的問題,需要根據具體的硬體進行設定,較為常見的是vline/hsync 、VFRAME/VSYNC脈沖的極性。
顔色資訊的位元組交換控制位:BSWP、HWSWP
這兩位用來控制位元組交換和半字交換,主要用來大小頭的問題,如果輸出到屏上的漢字左右互換了,或者輸出到屏上的圖花屏了,可以更改這個選項。具體涵義在 S3C2410晶片手冊上有詳細的說明。
我的這塊 TFT 的資訊設定如下:
lcdcon5: LCD5_FRM565 | LCD5_HWSWP | LCD5_PWREN ,
一塊 CSTN屏的資訊
lcdcon5: LCD5_BSWP | LCD5_PWREN ,
FrameBuffer 起始寄存器 1
z
這個寄存器的設定沒有必要去修改(TFT/STN),都使用預設的代碼即可:
FrameBuffer 起始寄存器 2 和 FrameBuffer 起始寄存器 3
這兩個寄存器的設定比較重要,在此我給出 12 位色 CSTN 屏和 16 位色TFT 的設定代碼:
前面提到的 LINEVAL 和 HOZVAL 以源碼的形式給出,其中 CSTN 8 位色沒有經過測試。
RGB Loopup Table Register
這三個寄存器的在驅動 256 色 CSTN 屏的時候需要使用,我在别的晶片上使用過,因為這顆晶片支援 12 位色,是以沒有去調試,我給
出兩組可能的值:
S3C44B0 上的
rREDLUT = 0xFCA86420;
rGREENLUT = 0xFCA86420;
rBLUELUT = 0xFFFFFA50;
Jupiter 上的
rREDLUT = 0xFEC85310
rGREENLUT = 0xFEC85310
rBLUELUT = 0xFB40
5) 好了,各個寄存器的設定完成了,最後在驅動 CSTN屏的時候需要提醒大家一句,CSTN的信号引腳中有一個叫VM/DISP的信号線,這個信号線的作用就是打開LCD的顯示開關,讓其進行顯示,它可以接到任何一個 GPIO 口上。S3C2410 中提供了一個 VM 信号,可以将 LCD的這個信号與 S3C2410 的 VM 信号相接即可,然後在驅動中一定要加上如下語句(藍色選中部分):
否則你的 LCD可能沒有任何顯示哦(對于 TFT 屏不需要這個語句)
6) 關于 12 位色的 CSTN屏的驅動還需要做一些工作,我在這裡簡單介紹一下:
a) 首先要完成一個 fbcon-cfb12.c和 fbcon-cfb12.h 的編寫,這兩個檔案很簡單,在armLinux 中不是提供了 fbcon-cfb16.c 和 fbcon-cfb12.h 嗎?簡單修改一下就可以了;
b) 将 fbcon-cfb12.c 的編譯加入 Config.in 中(不會的話去 google 搜一下,或者看一下我的另一篇文章《JFFS2 在 HHARM2410 上的實作》,裡邊有一些說明),并定義一個 FBCON_HAS_CFB12 參數(模仿 FBCON_HAS_CFB16 呗);
c) 另外,需要在 s3c2410fb.c 中的相應部分加上對 12位色的支援即可。呵,說起來簡單,但實際做起來可能會有一些問題,給大家一個竅門:在程式中找到#ifdef FBCON_HAS_CFB16 之類的代碼,簡單了解一下加上對 12 位色的支援;
d) 我隻給出函數 s3c2410fb_set_var中的改動,其他的應該都不是很困難,相信朋友們都能搞定。
e) 不要跟我要源碼哦,否則老闆會不高興哦 。
4. 驅動寫好了,重新 Make,下載下傳就可以了。如果一切順利,在 TFT 屏或 256 色的 CSTN屏上會有一個漂亮的小蜻蜓(應該是蜻蜓吧)出現。注意,并不是蜻蜓出現了就代表你的驅動 OK了,還要用 GUI 程式做進一步的測試,因為某一個或幾個參數雖然不正确,但是仍然能夠看到小蜻蜓的,但顯示圖形的時候就有問題了。另外,在驅動 CSTN到 12位色的時候,我們在屏上看不到小蜻蜓(我的 N塊 CSTN屏上都沒見到小蜻蜓),我想,可能是 armLinux 本身不支援 12 位色顯示,或者我們某些地方沒搞對的原因吧,但這不代表你的驅動有問題,用 GUI 程式寫 FrameBuffer,看看能否的到正确的結果。
5. GUI 程式的編寫
FrameBuffer 驅動寫好了,那麼怎麼去使用,怎麼在 LCD 上顯示圖像呢?這就是 GUI程式的任務了,其實要在 LCD 上顯示圖像,說白了就是把資料(包含顔色)寫到FrameBuffer 中對應的位置就可以了。如果你使用如 Microwinow、MiniGui、Qt 之類的GUI,則沒有必要關心 FrameBuffer與 LCD屏上的點如何進行映射了,但如果你在使用了 CSTN 屏,并且要顯示效果好的照片,選擇了 CSTN 的 12 位色(4096色 ),那你就要自己寫 GUI 程式了,因為好像 armLinux(Linux)本身都不支援 12 位色的,聽說 MiniGui支援 12 位色,但我在工作中的要求隻是顯示圖形而已,沒有去深入研究 MiniGui,是以自己寫了。
另外請朋友們見諒的是我不能給出全部的源代碼,因為我畢竟受雇于人,有些東東是可以 GPL 的,而有些東東暫時是不可以 GPL 的。
下面給出我的程式的部分代碼,希望對朋友們有所幫助。
1) 全局變量的定義:
定義幾個全局變量,用起來友善。
2) 初始化圖形顯示引擎,将 fb0與 GUI 的 buffer做個映射
用mmap函數使使用者空間的一段位址關聯到裝置記憶體(FrameBuffer)上。無論何時,隻要程式在配置設定的位址範圍内進行讀取或者寫入,實際上就是對裝置的通路,使用 mmap 可以既快速又簡單地通路顯示卡的記憶體。對于象這樣的性能要求比較嚴格的應用來說,直接通路能給我們提供很大不同。 不過我曾将幫一個網友調試了一個 S3C44B0 上的 GUI 程式,在他的 GUI 中 mmap 函數總會出錯,因為沒有拿到他的硬體和驅動源碼,沒有分析出其中的原因,是以隻得用 write函數,直接向 fb0 寫入資料,奇怪的是隻寫入一部分資料好像都不起任何作用,隻得整屏資料寫入才搞定了。這可就比較痛苦了,不過好在他隻是寫入的黑白資料,資料量還不是很大,要是彩色的那可真的痛苦了 。
另外,我還想多啰嗦兩句,FrameBuffer的像素點與LCD屏上的像素點的對應關系 ,深入了解一下對程式的了解可能會更清楚一點。我們知道黑白(2 色)顔色用 0 和 1 就可以表示了,也就是 1 位資料就可以了,那 1 個位元組就可以表示 8 位資料,假如這個位元組是10101010,FrameBuffer 的偏移位址為 0,則在 LCD 屏上便會顯示出 4 個黑點,黑點中間會有 4 個白點出現(假如 1 是黑色);對于 4 色則用 00、01、10、11 就可以表示出四種顔色,即用兩位資料可以表示一位資料,那同樣是 10101010,則對應于 LCD 屏上則顯示的
是顔色值為10,長度為4(8/2)的一條直線;同理,對于8位色(256色),則8位資料才能表示出一個點的顔色值,10101010在LCD屏上就隻能顯示為顔色值為10101010的點了。
有了上面的基礎我們就可以很好的了解這個語句了:
screensize = vinfo.xres*vinfo.yres*vinfo.bits_per_pixel/8;
即FrameBuffer 的大小=LCD屏的寬度 * LCD屏的高度 * 每像素的位數 / 每位元組的位數
例如,一個320*240的黑白平,FrameBuffer的大小為
320 * 240 * 1 / 8 = 9600 (位元組)
而一個320 * 240的16位色LCD的 FrameBuffer的大小則為
320 * 240 * 16 / 8 = 153600(位元組)
3) TFT 屏 16 位色的畫點函數
有了畫點函數,你還愁什麼?圖形漢字都可以搞定了吧!
4) CSTN屏 12位色的畫點函數
注意,為了更便于代碼書寫,我在這個函數中将 fbp 定義為 static char * fbp,而在TFT 屏 16 位色的畫點函數中 fbp 的定義為 U16 * fbp,你可以根據需要進行修改。
5) TFT 屏 16 位色下顯示 24色位圖函數
Bmp檔案的格式可以參考網上的一些資料,如果需要也可以直接找我要。
6) CSTN屏 12位色下顯示 24 色位圖函數
7) 呵呵,别忘了關閉裝置哦
void closegraph()
{
munmap(fbp,screensize);
close(fb);
}
8) 另外關于如何顯示漢字的 C 語言代碼,在我的網站上有現成的源碼《點陣字庫西那時程式》,在TC2.0 下都是可以跑的,她可以支援 12X12 點陣、14X14 點陣、16X16點陣漢字及 ASC 碼的顯示,将畫點函數和與裝置相關的函數簡單替換就可以了,移植起來應該不困難。對于有更高需求的朋友,如果 GB 庫還不夠,我也提供了 GBK支援 2 萬多個漢字的顯示,隻可惜隻有 16X16點陣的字庫,沒有小字型。顯示方法我也提供了源碼哦(因為網上沒有找到相關資料,再加上老劉我比較笨,我研究了好長時間才成功,就讓需要的朋友一起分享吧!)