賈天有,任獲榮,李兆剛
(1.西安電子科技大學通信工程學院;2.西安電子科技大學機電工程學院,陜西西安 710000;3.浙江萬勝智能科技股份有限公司,浙江 杭州 310012)
在嵌入式主流LCD 屏中,主要包括兩大類硬件接口,一種是常見的RGB 接口,另一種是MCU 接口[1]。RGBLCD 一般連接在處理器集成的LCD 控制器上,顯存由系統內存充當,常用于顯示視頻或動畫;MCU-LCD 針對早期內存較小、資源受限的單片機而設計,一般通過并口、SPI 等方式與處理器連接。MCU-LCD 顯存內置在LCD 模組內部,常用于顯示靜止圖片[2]。MCU-LCD 由于其價格低的優點,在嵌入式設備中仍然大量使用。范林濤等[3]提出SPI 接口的MCU-LCD 與Framebuffer 結合的LCD 驅動開發方法,該方法占用引腳資源較少,但也存在一些缺點:傳輸距離較近、抗干擾能力較差且不能在應用層控制LCD 以刷新。本文針對這些問題,提出基于并口的MCU-LCD 驅動程序,該驅動程序提供定時刷新幀緩沖區和應用層控制刷新幀緩沖區兩種方式,可以通過應用層控制方式快速將信息顯示在電子屏上,而對于非緊急數據的顯示則采用定時器定時刷新方式,提升了驅動靈活性。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(數據/命令選擇,1:數據,0:命令)、CSB 片選。數據和命令都通過D[7:0](雙向數據線)傳輸,通過A0 的高低電平判斷傳輸的是數據還是控制命令,A0 為低電平表明傳輸的是控制命令,A0 為高電平表明傳輸的是數據。
SCM601 芯片是智芯微發布的一款以ARM926EJ-S[5]為內核的工業級SOC,最高主頻300MHz,SOC 集成了EBI(外部總線,External Bus Interface)接口[6],SCM601 外部總線接口支持訪問外部并行接口設備,如SRAM 和外部I/O設備,支持8 bit/16 bit 數據總線寬度,支持8080 和6800 模式的接口信號,支持最高5 SRAM 和外部I/O 設備,支持可編程訪問周期。本文通過EBI 接口與ST75161 液晶模組連接。配置EBI 接口相關寄存器的值,使EBI 工作于并行8080 模式,調整EBI 總線讀/寫時序,匹配外接的ST75161液晶時序規格。為操作外接的LCD 模塊,將EBI 的A0 地址線與LCD 側的A0(數據/命令選擇,1:數據,0:命令)相連,這樣LCD 成為只有兩個可訪問地址的外部設備,訪問兩個地址中的低地址表示MCU 和LCD 之間傳輸的是命令,訪問兩個地址中的高地址表示MCU 和LCD 之間傳輸的是數據。通過以上配置,便可以通過EBI 接口向LCD 發送控制命令或數據以實現對LCD 的操作。采用并行8080接口時接線方式如圖1 所示。

Fig.1 Parallel 8080 interface wiring method圖1 并行8080 接口接線方式
Framebuffer 是出現在Linux2.2.xx 內核中的一種驅動程序接口[7]。Linux 抽象出Framebuffer 設備供用戶態進程實現直接寫屏,用戶可以將Framebuffer 看成是顯示內存的一個映像,將其映射到用戶地址空間后,就可以直接進行讀寫操作,而寫操作可以直接反應在屏幕上。這種操作是抽象的、統一的,用戶不必關心物理顯存的位置、換頁機制等具體細節,這些都由Framebuffer 設備驅動完成[8]。
fb_info 結構體:幀緩沖設備最關鍵的一個數據結構是fb_info 結構體,其中包含了關于幀緩沖設備屬性和操作的完整描述[9],結構體定義如下:

本處只列出部分較為重要的成員,其中fb_var_screen?info 記錄用戶可以修改的顯示器控制器參數,包括屏幕分辨率和每個像素的比特數等;fb_fix_screeninfo 記錄用戶不能修改的顯示控制器參數;fb_ops 為幀緩沖操作函數集;screen_base 為幀緩沖的虛擬地址[10]。
fb_var_screeninfo 結構體:用于記錄幀緩沖設備的可修改記錄[11]。結構體定義如下:

其中,xres 為屏幕的水平像素數,yres 為屏幕的垂直像素數,bits_per_pixel 定義每個像素由多少個bit 表示[12]。
fb_fix_screeninfo 結構體:記錄了幀緩沖設備不可修改的記錄[13]。結構體定義如下:


其中,id 是字符串標識,smem_start 是幀緩沖內存的開始地址,type 標識LCD 設備類型,line_length 指一行的長度(以字節為單位)。
基于Framebuffer 的LCD 驅動是字符設備,主設備號固定為29[14],次設備號從0 到31,符合Linux 驅動框架,驅動可獨立編寫為一個模塊,能夠動態加載到內核或動態從內核卸載,Linux 中通過module_init()和module_exit()分別聲明驅動的入口和出口函數st75161_i80_init()、st75161_i80_exit()。

入口函數st75161_i80_init()實現以下功能:①分配一個fb_info 結構體;②初始化該結構體中的固定和可變參數,即根據 ST75161 液晶模組的硬件特性填充fb_var_screeninfo 結構體和fb_fix_screeninfo 結構體,初始化fb_ops 結構體。調用dma_alloc_writecombine()申請一段連續的物理內存區域作為顯存空間,函數返回值為申請的幀緩存內存起始位置的虛擬地址[15];③參考SCM601 芯片手冊,配置EBI 相關寄存器使EBI 接口工作于并行8080模式并設置讀寫時序;④背光引腳設置,初始化st75161 液晶;⑤向內核注冊fb_info 結構體[16];⑥MCU-LCD 不同于RGB-LCD,不具備自動刷新幀緩沖區的功能,因此需要手動刷新,將顯示緩沖區中的數據顯示在屏幕上,本文通過在內核中添加定時器周期性刷新和在應用層通過ioctl()系統調用函數控制刷新兩種方式相結合,共同控制幀緩沖區的刷新。
部分關鍵代碼如下:


其中,initialization_ST75161()為ST75161 液晶模組初始化函數,該函數根據液晶模組官方手冊中的初始化時序編寫。
出口函數st75161_i80_exit()實現以下功能:①注銷fb_info 結構體;②關閉st75161 液晶顯示及背光;③注銷用于刷新幀緩沖區的定時器;④釋放硬件資源。
部分關鍵代碼如下:

編寫Makefile 文件,將編寫的驅動編譯成模塊。Make?file 文件如下:

執行make 命令,將生成驅動模塊ST75161_lcd.ko。
將編譯生成的ST75161_lcd.ko 模塊,使用insmod 命令動態加載進Linux 內核,在文件系統中生成一個設備文件/dev/fb0,在應用程序中,操作/dev/fb0 表現為對LCD 設備的操作。
對于應用程序而言,framebuffer 驅動和其它設備并無區別,用戶可以將它看成是一塊內存,可以向這塊內存讀寫數據。Framebuffer 的顯示緩沖區位于內核空間,應用程序可以將此空間映射到應用空間,在應用程序進行操作[18]。
應用層操作/dev/fb0 一般步驟如下:①使用系統調用函數open()打開/dev/fb0 設備文件;②使用系統調用函數ioctl()取得LCD 屏的重要參數,如屏幕分辨率、每個像素點的比特數,并根據這些參數計算屏幕緩沖區大小;③使用系統調用函數mmap()將屏幕緩沖區映射到用戶空間;④讀寫映射后的空間,進行繪圖、顯示等操作;⑤釋放資源,關閉設備文件。
應用程序操作/dev/fb0 流程如圖2 所示。

Fig.2 Process of application operation/dev/fb0圖2 應用程序操作/dev/fb0 流程
分別編寫基本圖形繪制函數、漢字顯示函數和位圖顯示函數對驅動程序進行測試。
(1)繪制基本圖形。畫點函數是圖形繪制的基礎,將液晶屏左上角定義為坐標原點,橫向為x 軸,縱向為y 軸,根據具體像素點的(x,y)坐標向幀緩沖區寫入數據,將(x,y)位置畫點,矩形和圓形繪制依據相關算法調用畫點函數依次繪制。
(2)位圖顯示。BMP 位圖文件可分為文件頭、信息頭和圖像數據3 個部分,其中文件頭占14 字節,信息頭占40字節[19],分別用兩個結構體表示文件頭和信息頭:


打開圖片文件后,先根據文件頭中的文件類型判斷是否為BMP 圖片,如果是,則讀取信息頭和文件頭獲取圖片的其它信息,最后根據BMP 圖片寬度和高度將位圖信息復制到幀緩沖區,位圖信息即可顯示。
(3)ASCII 字符和漢字顯示。簡體中文漢字編碼常采用GB2312 編碼,一個漢字使用兩個字節編碼,高字節稱為區碼,低字節稱為位碼[20]。本文使用16×16 格式漢字庫和8×16 格式字符庫,使用以下方法即可獲得具體漢字的字模:
ADD=[(區碼-0xa1)x 0x5e+(位碼-0xa1)]x 0x20;
從字符庫中獲取字模較簡單,8×16 字符庫中,每個字符占用16 字節,具體字符的字模信息在字符庫中的偏移量等于字符對應的ASCII 值與每個字符占用字節數的乘積[21]。根據字符和漢字的字模信息,即可將具體字符和漢字加以顯示。
測試程序在基于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 驅動程序,并編寫測試程序以驗證其功能。基于這種方式開發的驅動很好地解決了Linux 內核對MCU-LCD 的支持,為移植嵌入式中流行的Qt 和MINGUI 提供了基礎。但該方法控制幀緩沖區刷新頻率的定時器周期為500ms,該時間可能不是幀緩沖區刷新最優時間值,需進一步調試修改。