李文睿,陳 新
(福州大學(xué) 物理與信息工程學(xué)院,福建 福州350108)
電子紙可以實(shí)現(xiàn)顯示內(nèi)容的重寫,具有對(duì)比度高、重量輕、可以適當(dāng)彎曲,且在斷電的情況下,能保持原有的顯示內(nèi)容等優(yōu)點(diǎn)。Linux是一種具有開放性、多用戶、多任務(wù)、設(shè)備獨(dú)立性、系統(tǒng)可靠安全、良好移植性等優(yōu)點(diǎn)的操作系統(tǒng)[1],其內(nèi)核可根據(jù)具體的運(yùn)行平臺(tái)進(jìn)行適當(dāng)?shù)牟眉?,這對(duì)于資源有限的嵌入式系統(tǒng)至關(guān)重要。因此,如何將電子紙顯示屏移植應(yīng)用到Linux操作系統(tǒng)的嵌入式平臺(tái)下,引起了業(yè)界的廣泛關(guān)注。
本文采用GD6210E作為電子紙顯示屏的控制芯片,在S3C2440處理器上使用GPIO口對(duì)GD6210E進(jìn)行擴(kuò)展。編寫、編譯基于Linux中framebuffer的電子紙顯示屏驅(qū)動(dòng)程序。
本文搭建的嵌入式系統(tǒng)采用Samsung公司推出的S3C2440芯片為處理器。S3C2440采用ARM920T內(nèi)核,擁有豐富的GPIO口,能夠很好地對(duì)電子紙顯示屏控制芯片實(shí)現(xiàn)擴(kuò)展;外圍設(shè)備有容量為128 MB的NAND Flash、64 MB的SDRAM、以太網(wǎng)以及擴(kuò)展GD6210E電子紙顯示屏控制芯片[2-3]等。嵌入式系統(tǒng)架構(gòu)框圖如圖1所示。
GD6210E芯片是Giga Device Semiconductor公司開發(fā)的電子紙控制芯片。該控制芯片提供多種顯示功能,如支持部分屏或整屏顯示、全局或局部刷新、支持圖像翻轉(zhuǎn)等,從而能夠減少CPU的運(yùn)算時(shí)間,并支持CPU用命令訪問內(nèi)部寄存器和存儲(chǔ)介質(zhì)。
在接口設(shè)計(jì)上,本文使用S3C2440的GPD0~GPD15作為數(shù)據(jù)/命令的輸入/輸出口;GPC15作為探測GD6210E是否準(zhǔn)備好下一次操作的引腳,若為1,說明在GD6210E上已完成一個(gè)操作,進(jìn)入等待接收下一個(gè)操作命令狀態(tài);利用S3C2440的CLKOUT0引腳為GD6210E提供時(shí)鐘源/S3C2440對(duì)GD6210E擴(kuò)展接口如圖2所示。
驅(qū)動(dòng)程序是應(yīng)用程序與硬件之間的一個(gè)中間軟件層,沒有main()函數(shù),其工作過程是通過使用宏module_init(初始化函數(shù)名)將初始化函數(shù)加入內(nèi)核全局初始化函數(shù)列表中,在內(nèi)核初始化時(shí)執(zhí)行驅(qū)動(dòng)的初始化函數(shù),從而完成驅(qū)動(dòng)的初始化和注冊(cè),之后驅(qū)動(dòng)便停止,等待被應(yīng)用程序調(diào)用。應(yīng)用程序通過調(diào)用設(shè)備驅(qū)動(dòng)程序中實(shí)現(xiàn)的接口函數(shù)(如 read()、ioctl()等)實(shí)現(xiàn)對(duì)硬件的操作。
本文設(shè)計(jì)的電子紙驅(qū)動(dòng)程序基于framebuffer,整體分為兩大部分:(1)GD6210E的驅(qū)動(dòng)程序,其任務(wù)是完成S3C2440 GPIO口的設(shè)置、GD6210E初始化并使其處在運(yùn)行狀態(tài);(2)完成framebuffer的填寫和內(nèi)核對(duì)顯示設(shè)備驅(qū)動(dòng)的注冊(cè)。
GD6210E是一個(gè)從設(shè)備,無法自主地工作,必須由外部的CPU對(duì)其發(fā)送命令,再把命令轉(zhuǎn)換成能使電子紙顯示屏做出相應(yīng)動(dòng)作的電平時(shí)序。GD6210E驅(qū)動(dòng)程序分為以下部分。
(1)初始化GD6210E。首先分別利用Linux中的s3c-2410_gpio_cfgpin()和 s3c2410_gpio_setpin()兩個(gè)函數(shù)設(shè)置復(fù)用GPIO口的引腳功能和引腳值。接口配置完后,再向GD6210E發(fā)出INIT_SYS_RUN命令,使芯片進(jìn)入等待初始化狀態(tài);然后初始化GD6210E中的display engine,填寫顯示時(shí)序寄存器,配置顯示時(shí)序。其中時(shí)序包括行數(shù)據(jù)輸出時(shí)間、行同步時(shí)間、幀數(shù)據(jù)輸出時(shí)間、幀同步時(shí)間;最后設(shè)置Image buffer和Updimage buffer在GD6210E中SDRAM的開始地址,至此初始化完成。GD6210E初始化流程如圖3所示。
(2)實(shí)現(xiàn)GD6210E對(duì)顯示數(shù)據(jù)的裝載和更新顯示屏。首先ARM9向GD6210E發(fā)送WAIT_DSPE_FREND命令,等待GD6210E當(dāng)前工作完成。若Ready引腳為1,ARM9發(fā)送 LD_IMG命令,再判斷 Ready是否為 1,若是,則開始向GD6210E內(nèi)部的存儲(chǔ)器件發(fā)送圖像數(shù)據(jù)。數(shù)據(jù)發(fā)送完畢,ARM9發(fā)送LD_IMG_END命令,表示數(shù)據(jù)發(fā)送完成;最后發(fā)送UPD_FULL命令,開始電子紙更新。GD6210E更新電子紙顯示內(nèi)容過程如圖4所示。
Linux操作系統(tǒng)為顯示設(shè)備提供專用接口即幀緩沖(framebuffer),它將顯示緩沖區(qū)進(jìn)行抽象,允許上層應(yīng)用程序在圖形模式下直接對(duì)顯示緩存區(qū)進(jìn)行讀寫操作[4]。在Linux系統(tǒng)中,每個(gè)顯示外設(shè)都與自己的一個(gè)fb_info結(jié)構(gòu)體相對(duì)應(yīng)。顯示驅(qū)動(dòng)的實(shí)現(xiàn)很大部分是在初始化fb_info各項(xiàng)。fb_info包含 fb_fix_screeninfo、fb_var_screeninfo、fb_cmap、fb_ops 4 個(gè)結(jié)構(gòu)體[5]。
(1)fb_fix_screeninfo結(jié)構(gòu)體主要設(shè)置:①顯示設(shè)備的id、顯示緩沖區(qū)的大小、顯示屏每行在緩沖區(qū)字節(jié)數(shù)。②設(shè)置顯示屏的行像素?cái)?shù)、列像素?cái)?shù)、像素深度、色彩模式等。這兩個(gè)結(jié)構(gòu)體的設(shè)置較為簡單,根據(jù)實(shí)際情況對(duì)顯示屏進(jìn)行具體設(shè)置即可。
(2)fb_info最關(guān)鍵的是要填寫 fb_ops。fb_ops要實(shí)現(xiàn)fb_mmap()和 fb_ioctl()兩個(gè)關(guān)鍵的函數(shù)。
①fb_mmap():在Linux系統(tǒng)中,應(yīng)用程序不可能直接訪問設(shè)備驅(qū)動(dòng)所在的內(nèi)核空間。但是,可通過調(diào)用mmap()函數(shù),使應(yīng)用程序能直接訪問設(shè)備所在的物理地址。mmap()將用戶空間的一段內(nèi)存與設(shè)備內(nèi)存進(jìn)行映射,當(dāng)用戶訪問用戶空間的這段地址范圍時(shí),實(shí)際上會(huì)轉(zhuǎn)化為對(duì)設(shè)備的訪問,但mmap()必須以 PAGE_SIZE為單位進(jìn)行映射。所以在fb_mmap()函數(shù)中,首先為應(yīng)用程序建立一個(gè)VMA(VMA描述的是一段連續(xù)的、具有相同訪問屬性的虛存空間)。建立VMA后只是說明進(jìn)程可以訪問這個(gè)虛存空間,但還沒有為其分配相應(yīng)的物理頁面,所以在訪問VMA時(shí),就會(huì)產(chǎn)生一個(gè)缺頁異常,系統(tǒng)將自動(dòng)調(diào)用VMA中的fault()函數(shù)。所以在填寫 fault()函數(shù)時(shí),首先調(diào)用vmalloc_to_page(*addr)找到電子紙顯示緩沖區(qū)所對(duì)應(yīng)的物理頁面;接著調(diào)用get_page(*struct page)函數(shù),獲得顯示緩沖區(qū)所在物理頁面的頁面描述符,再根據(jù)頁面描述符找到物理頁面地址,將物理頁面的地址填充到進(jìn)程的頁表中。這樣就能正常訪問VMA所描述的虛擬空間,從而完成電子紙的顯示緩沖區(qū)和用戶空間的映射。頁面映射的流程如圖5所示。
② fb_ioctl():fb_ioctl(fb_info*info、unsigned int cmd、unsigned long arg)函數(shù)最終實(shí)現(xiàn)用戶對(duì)I/O控制命令的執(zhí)行。其3個(gè)參數(shù)分別表示操作的fb_info對(duì)象、指令和指令所帶的參數(shù)。內(nèi)核中預(yù)定義了一些I/O控制命令,如framebuffer中的FBIOGET_VSCREENINFO、FBIOGET_FSCREENINFO分別為獲取可變的屏幕參數(shù)和固定屏幕參數(shù)。這些預(yù)定義的命令被內(nèi)核處理而不是被設(shè)備驅(qū)動(dòng)處理。若命令為用戶自定義的,則調(diào)用設(shè)備驅(qū)動(dòng)中的fb_ioctl()。本文自定義顯示更新命令為FBUPDATA,在fb_ioctl()中,若判斷出命令為FBUPDATA,則調(diào)用GD6210E驅(qū)動(dòng)中的更新函數(shù)。Framebuffer中fb_ioctl()調(diào)用流程如圖6所示。
完整的電子紙驅(qū)動(dòng)程序架構(gòu)如圖7所示。應(yīng)用程序首先打開顯示設(shè)備驅(qū)動(dòng),再調(diào)用framebuffer中fb_ops結(jié)構(gòu)體的fb_mmap()函數(shù),把驅(qū)動(dòng)中的顯示緩存區(qū)映射到用戶空間,并填充顯示數(shù)據(jù)。再向顯示設(shè)備驅(qū)動(dòng)發(fā)送cmd,fb_ioctl()接收 cmd,再根據(jù) cmd調(diào)用 GD6210E驅(qū)動(dòng)程序中相對(duì)應(yīng)的操作函數(shù)。如刷新顯示屏,則調(diào)用GD6210E驅(qū)動(dòng)程序中的刷新函數(shù)。
測試是在Fedroa 13 Linux操作系統(tǒng)下進(jìn)行,安裝arm-linux-gcc 4.4.3版本的交叉編譯工具。把電子紙驅(qū)動(dòng)程序放在Linux 2.6.32.2內(nèi)核/drivers/vedio目錄下,并修改該目錄下Makefile和Kconfig這兩個(gè)文件,目的是為了在配置內(nèi)核時(shí)能添加電子紙驅(qū)動(dòng)。運(yùn)行make menuconfig指令重新配置內(nèi)核,把電子紙顯示屏驅(qū)動(dòng)配置進(jìn)內(nèi)核。最后運(yùn)行make zImage指令,用交叉編譯工具重新編譯內(nèi)核,完成Linux內(nèi)核對(duì)電子紙驅(qū)動(dòng)的添加。
驅(qū)動(dòng)測試程序首先用open()打開電子紙驅(qū)動(dòng),再用fopen()打開一個(gè)8BBP的bmp圖片文件,跳過頭文件信息,提取每個(gè)像素點(diǎn)的灰階值。由于測試中采用的電子紙顯示屏是4BBP,所以要把8BBP的灰階值提取高4 bit轉(zhuǎn)成4BBP;接著調(diào)用mmap()把幀緩沖區(qū)間映射到用戶空間,對(duì)緩沖區(qū)填充每個(gè)像素點(diǎn)的灰階值;最后調(diào)用ioctl(),向顯示驅(qū)動(dòng)發(fā)送更新命令,更新電子紙顯示屏。
目前,Linux圖形用戶界面程序編譯工具大部分是基于 framebuffer(如Qt),而本文中的電子紙顯示屏驅(qū)動(dòng)也是基于framebuffer,所以圖形用戶界面程序的開發(fā)人員利用本文的方法就可以完全不用理會(huì)電子紙與其他顯示屏在硬件上的差異問題。從而可以大大縮短采用電子紙為顯示屏的電子產(chǎn)品的開發(fā)時(shí)間,更利于電子紙顯示屏的普及應(yīng)用。
[1]陳博,孫宏彬,於岳.Linux實(shí)用教程[M].北京:人民郵電出版社,2010.
[2]孫天澤,袁文菊.嵌入式設(shè)計(jì)及Linux驅(qū)動(dòng)開發(fā)指南—基于ARM9處理器[M].二版.北京:電子工業(yè)出版社,2007.
[3]Christopher Hallinan.嵌入式 Linux基礎(chǔ)教程[M].北京:人民郵電出版社,2009.
[4]宋寶華.Linux設(shè)備驅(qū)動(dòng)開發(fā)詳解[M].北京:人民郵電出版社,2008.
[5]商斌.Linux設(shè)備驅(qū)動(dòng)開發(fā)入門與編程實(shí)踐[M].北京:電子工業(yè)出版社,2009.