賈天有,任獲榮,李兆剛
(1.西安電子科技大學通信工程學院;2.西安電子科技大學機電工程學院,陜西西安 710000;3.浙江萬勝智能科技股份有限公司,浙江 杭州 310012)
在嵌入式主流LCD 屏中,主要包括兩大類硬件接口,一種是常見的RGB 接口,另一種是MCU 接口[1]。RGBLCD 一般連接在處理器集成的LCD 控制器上,顯存由系統(tǒng)內(nèi)存充當,常用于顯示視頻或動畫;MCU-LCD 針對早期內(nèi)存較小、資源受限的單片機而設計,一般通過并口、SPI 等方式與處理器連接。MCU-LCD 顯存內(nèi)置在LCD 模組內(nèi)部,常用于顯示靜止圖片[2]。MCU-LCD 由于其價格低的優(yōu)點,在嵌入式設備中仍然大量使用。范林濤等[3]提出SPI 接口的MCU-LCD 與Framebuffer 結(jié)合的LCD 驅(qū)動開發(fā)方法,該方法占用引腳資源較少,但也存在一些缺點:傳輸距離較近、抗干擾能力較差且不能在應用層控制LCD 以刷新。本文針對這些問題,提出基于并口的MCU-LCD 驅(qū)動程序,該驅(qū)動程序提供定時刷新幀緩沖區(qū)和應用層控制刷新幀緩沖區(qū)兩種方式,可以通過應用層控制方式快速將信息顯示在電子屏上,而對于非緊急數(shù)據(jù)的顯示則采用定時器定時刷新方式,提升了驅(qū)動靈活性。MCU-LCD 和RGB-LCD 對比如表1 所示。
Table 1 Comparison of MCU-LCD and RGB-LCD表1 MCU-LCD 與RGB-LCD 對比
ST75161 是點陣型液晶模組,分辨率大小為160×160像素,可配置為單色或者4 級灰度兩種工作模式,該芯片可以通過8 位并行接口(8080 系列或6800 系列)、SPI 或IIC 接口直接連接到微處理器。本文針對并行8080 接口作介紹,I8080 總線是Intel 提出的一種總線[4],Intel 總線的控制線有4 根:RD 讀使能、WR 寫使能、A0(數(shù)據(jù)/命令選擇,1:數(shù)據(jù),0:命令)、CSB 片選。數(shù)據(jù)和命令都通過D[7:0](雙向數(shù)據(jù)線)傳輸,通過A0 的高低電平判斷傳輸?shù)氖菙?shù)據(jù)還是控制命令,A0 為低電平表明傳輸?shù)氖强刂泼睿珹0 為高電平表明傳輸?shù)氖菙?shù)據(jù)。
SCM601 芯片是智芯微發(fā)布的一款以ARM926EJ-S[5]為內(nèi)核的工業(yè)級SOC,最高主頻300MHz,SOC 集成了EBI(外部總線,External Bus Interface)接口[6],SCM601 外部總線接口支持訪問外部并行接口設備,如SRAM 和外部I/O設備,支持8 bit/16 bit 數(shù)據(jù)總線寬度,支持8080 和6800 模式的接口信號,支持最高5 SRAM 和外部I/O 設備,支持可編程訪問周期。本文通過EBI 接口與ST75161 液晶模組連接。配置EBI 接口相關寄存器的值,使EBI 工作于并行8080 模式,調(diào)整EBI 總線讀/寫時序,匹配外接的ST75161液晶時序規(guī)格。為操作外接的LCD 模塊,將EBI 的A0 地址線與LCD 側(cè)的A0(數(shù)據(jù)/命令選擇,1:數(shù)據(jù),0:命令)相連,這樣LCD 成為只有兩個可訪問地址的外部設備,訪問兩個地址中的低地址表示MCU 和LCD 之間傳輸?shù)氖敲?,訪問兩個地址中的高地址表示MCU 和LCD 之間傳輸?shù)氖菙?shù)據(jù)。通過以上配置,便可以通過EBI 接口向LCD 發(fā)送控制命令或數(shù)據(jù)以實現(xiàn)對LCD 的操作。采用并行8080接口時接線方式如圖1 所示。
Fig.1 Parallel 8080 interface wiring method圖1 并行8080 接口接線方式
Framebuffer 是出現(xiàn)在Linux2.2.xx 內(nèi)核中的一種驅(qū)動程序接口[7]。Linux 抽象出Framebuffer 設備供用戶態(tài)進程實現(xiàn)直接寫屏,用戶可以將Framebuffer 看成是顯示內(nèi)存的一個映像,將其映射到用戶地址空間后,就可以直接進行讀寫操作,而寫操作可以直接反應在屏幕上。這種操作是抽象的、統(tǒng)一的,用戶不必關心物理顯存的位置、換頁機制等具體細節(jié),這些都由Framebuffer 設備驅(qū)動完成[8]。
fb_info 結(jié)構(gòu)體:幀緩沖設備最關鍵的一個數(shù)據(jù)結(jié)構(gòu)是fb_info 結(jié)構(gòu)體,其中包含了關于幀緩沖設備屬性和操作的完整描述[9],結(jié)構(gòu)體定義如下:
本處只列出部分較為重要的成員,其中fb_var_screen?info 記錄用戶可以修改的顯示器控制器參數(shù),包括屏幕分辨率和每個像素的比特數(shù)等;fb_fix_screeninfo 記錄用戶不能修改的顯示控制器參數(shù);fb_ops 為幀緩沖操作函數(shù)集;screen_base 為幀緩沖的虛擬地址[10]。
fb_var_screeninfo 結(jié)構(gòu)體:用于記錄幀緩沖設備的可修改記錄[11]。結(jié)構(gòu)體定義如下:
其中,xres 為屏幕的水平像素數(shù),yres 為屏幕的垂直像素數(shù),bits_per_pixel 定義每個像素由多少個bit 表示[12]。
fb_fix_screeninfo 結(jié)構(gòu)體:記錄了幀緩沖設備不可修改的記錄[13]。結(jié)構(gòu)體定義如下:
其中,id 是字符串標識,smem_start 是幀緩沖內(nèi)存的開始地址,type 標識LCD 設備類型,line_length 指一行的長度(以字節(jié)為單位)。
基于Framebuffer 的LCD 驅(qū)動是字符設備,主設備號固定為29[14],次設備號從0 到31,符合Linux 驅(qū)動框架,驅(qū)動可獨立編寫為一個模塊,能夠動態(tài)加載到內(nèi)核或動態(tài)從內(nèi)核卸載,Linux 中通過module_init()和module_exit()分別聲明驅(qū)動的入口和出口函數(shù)st75161_i80_init()、st75161_i80_exit()。
入口函數(shù)st75161_i80_init()實現(xiàn)以下功能:①分配一個fb_info 結(jié)構(gòu)體;②初始化該結(jié)構(gòu)體中的固定和可變參數(shù),即根據(jù) ST75161 液晶模組的硬件特性填充fb_var_screeninfo 結(jié)構(gòu)體和fb_fix_screeninfo 結(jié)構(gòu)體,初始化fb_ops 結(jié)構(gòu)體。調(diào)用dma_alloc_writecombine()申請一段連續(xù)的物理內(nèi)存區(qū)域作為顯存空間,函數(shù)返回值為申請的幀緩存內(nèi)存起始位置的虛擬地址[15];③參考SCM601 芯片手冊,配置EBI 相關寄存器使EBI 接口工作于并行8080模式并設置讀寫時序;④背光引腳設置,初始化st75161 液晶;⑤向內(nèi)核注冊fb_info 結(jié)構(gòu)體[16];⑥MCU-LCD 不同于RGB-LCD,不具備自動刷新幀緩沖區(qū)的功能,因此需要手動刷新,將顯示緩沖區(qū)中的數(shù)據(jù)顯示在屏幕上,本文通過在內(nèi)核中添加定時器周期性刷新和在應用層通過ioctl()系統(tǒng)調(diào)用函數(shù)控制刷新兩種方式相結(jié)合,共同控制幀緩沖區(qū)的刷新。
部分關鍵代碼如下:
其中,initialization_ST75161()為ST75161 液晶模組初始化函數(shù),該函數(shù)根據(jù)液晶模組官方手冊中的初始化時序編寫。
出口函數(shù)st75161_i80_exit()實現(xiàn)以下功能:①注銷fb_info 結(jié)構(gòu)體;②關閉st75161 液晶顯示及背光;③注銷用于刷新幀緩沖區(qū)的定時器;④釋放硬件資源。
部分關鍵代碼如下:
編寫Makefile 文件,將編寫的驅(qū)動編譯成模塊。Make?file 文件如下:
執(zhí)行make 命令,將生成驅(qū)動模塊ST75161_lcd.ko。
將編譯生成的ST75161_lcd.ko 模塊,使用insmod 命令動態(tài)加載進Linux 內(nèi)核,在文件系統(tǒng)中生成一個設備文件/dev/fb0,在應用程序中,操作/dev/fb0 表現(xiàn)為對LCD 設備的操作。
對于應用程序而言,framebuffer 驅(qū)動和其它設備并無區(qū)別,用戶可以將它看成是一塊內(nèi)存,可以向這塊內(nèi)存讀寫數(shù)據(jù)。Framebuffer 的顯示緩沖區(qū)位于內(nèi)核空間,應用程序可以將此空間映射到應用空間,在應用程序進行操作[18]。
應用層操作/dev/fb0 一般步驟如下:①使用系統(tǒng)調(diào)用函數(shù)open()打開/dev/fb0 設備文件;②使用系統(tǒng)調(diào)用函數(shù)ioctl()取得LCD 屏的重要參數(shù),如屏幕分辨率、每個像素點的比特數(shù),并根據(jù)這些參數(shù)計算屏幕緩沖區(qū)大??;③使用系統(tǒng)調(diào)用函數(shù)mmap()將屏幕緩沖區(qū)映射到用戶空間;④讀寫映射后的空間,進行繪圖、顯示等操作;⑤釋放資源,關閉設備文件。
應用程序操作/dev/fb0 流程如圖2 所示。
Fig.2 Process of application operation/dev/fb0圖2 應用程序操作/dev/fb0 流程
分別編寫基本圖形繪制函數(shù)、漢字顯示函數(shù)和位圖顯示函數(shù)對驅(qū)動程序進行測試。
(1)繪制基本圖形。畫點函數(shù)是圖形繪制的基礎,將液晶屏左上角定義為坐標原點,橫向為x 軸,縱向為y 軸,根據(jù)具體像素點的(x,y)坐標向幀緩沖區(qū)寫入數(shù)據(jù),將(x,y)位置畫點,矩形和圓形繪制依據(jù)相關算法調(diào)用畫點函數(shù)依次繪制。
(2)位圖顯示。BMP 位圖文件可分為文件頭、信息頭和圖像數(shù)據(jù)3 個部分,其中文件頭占14 字節(jié),信息頭占40字節(jié)[19],分別用兩個結(jié)構(gòu)體表示文件頭和信息頭:
打開圖片文件后,先根據(jù)文件頭中的文件類型判斷是否為BMP 圖片,如果是,則讀取信息頭和文件頭獲取圖片的其它信息,最后根據(jù)BMP 圖片寬度和高度將位圖信息復制到幀緩沖區(qū),位圖信息即可顯示。
(3)ASCII 字符和漢字顯示。簡體中文漢字編碼常采用GB2312 編碼,一個漢字使用兩個字節(jié)編碼,高字節(jié)稱為區(qū)碼,低字節(jié)稱為位碼[20]。本文使用16×16 格式漢字庫和8×16 格式字符庫,使用以下方法即可獲得具體漢字的字模:
ADD=[(區(qū)碼-0xa1)x 0x5e+(位碼-0xa1)]x 0x20;
從字符庫中獲取字模較簡單,8×16 字符庫中,每個字符占用16 字節(jié),具體字符的字模信息在字符庫中的偏移量等于字符對應的ASCII 值與每個字符占用字節(jié)數(shù)的乘積[21]。根據(jù)字符和漢字的字模信息,即可將具體字符和漢字加以顯示。
測試程序在基于SCM601 的集中器平臺上運行,在屏幕上繪制矩形和圓形,同時對BMP 格式單色圖片進行顯示和漢字顯示。測試效果如圖3—圖5 所示。
Fig.3 Geometry drawing圖3 幾何圖形繪制
Fig.4 Graphic display圖4 圖形顯示
Fig.5 Character and text display圖5 字符和文字顯示
本文基于Framebuffer 機制設計MCU-LCD Linux 驅(qū)動程序,并編寫測試程序以驗證其功能?;谶@種方式開發(fā)的驅(qū)動很好地解決了Linux 內(nèi)核對MCU-LCD 的支持,為移植嵌入式中流行的Qt 和MINGUI 提供了基礎。但該方法控制幀緩沖區(qū)刷新頻率的定時器周期為500ms,該時間可能不是幀緩沖區(qū)刷新最優(yōu)時間值,需進一步調(diào)試修改。