【i.MX6ULL】驅(qū)動(dòng)開發(fā)11——LCD驅(qū)動(dòng)實(shí)踐
之前在Linux系統(tǒng)移植時(shí)提到過(guò)LCD驅(qū)動(dòng),本篇來(lái)看下Linux設(shè)備樹如何配置LCD驅(qū)動(dòng)。
首先需要了解一個(gè)新的概念:Framebuffer
1.1 Framebuffer
Framebuffer直譯即幀緩沖,簡(jiǎn)稱 fb,它是Linux將系統(tǒng)中所有跟顯示有關(guān)的硬件以及軟件集合起來(lái),將底層的LCD虛擬抽象出一 個(gè)/dev/fbX設(shè)備,應(yīng)用程序可以通過(guò)操作/dev/fbX來(lái)實(shí)現(xiàn)對(duì)屏幕的顯示控制。
NXP官方Linux內(nèi)核已默認(rèn)開啟了LCD驅(qū)動(dòng),在dev/目錄下可以看到fb0這樣一個(gè)設(shè)備
Framebuffer在內(nèi)核中的表現(xiàn)就是fb_info結(jié)構(gòu)體:
完整的結(jié)構(gòu)體定義如下:
struct fb_info { atomic_t count; int node; int flags; struct mutex lock; /* Lock for open/release/ioctl funcs */ struct mutex mm_lock; /* Lock for fb_mmap and smem_* fields */ struct fb_var_screeninfo var; /* 當(dāng)前的可變參數(shù) */ struct fb_fix_screeninfo fix; /* 當(dāng)前的固定參數(shù) */ struct fb_monspecs monspecs; /* Current Monitor specs */ struct work_struct queue; /* Framebuffer event queue */ struct fb_pixmap pixmap; /* Image hardware mapper */ struct fb_pixmap sprite; /* Cursor hardware mapper */ struct fb_cmap cmap; /* Current cmap */ struct list_head modelist; /* mode list */ struct fb_videomode *mode; /* current mode */ #ifdef CONFIG_FB_BACKLIGHT /* assigned backlight device */ /* set before framebuffer registration, remove after unregister */ struct backlight_device *bl_dev; /* Backlight level curve */ struct mutex bl_curve_mutex; u8 bl_curve[FB_BACKLIGHT_LEVELS]; #endif #ifdef CONFIG_FB_DEFERRED_IO struct delayed_work deferred_work; struct fb_deferred_io *fbdefio; #endif struct fb_ops *fbops; /* 幀緩沖操作函數(shù)集 */ struct device *device; /* This is the parent */ struct device *dev; /* This is this fb device */ int class_flag; /* private sysfs flags */ #ifdef CONFIG_FB_TILEBLITTING struct fb_tile_ops *tileops; /* Tile Blitting */ #endif char __iomem *screen_base; /* 虛擬內(nèi)存基地址(屏幕顯存) */ unsigned long screen_size; /* 虛擬內(nèi)存大小(屏幕顯存大小) */ void *pseudo_palette; /* 偽16位調(diào)色板 */ #define FBINFO_STATE_RUNNING 0 #define FBINFO_STATE_SUSPENDED 1 u32 state; /* Hardware state i.e suspend */ void *fbcon_par; /* fbcon use-only private area */ /* From here on everything is device dependent */ void *par; /* we need the PCI or similar aperture base/size not smem_start/size as smem_start may just be an object allocated inside the aperture so may not actually overlap */ struct apertures_struct { unsigned int count; struct aperture { resource_size_t base; resource_size_t size; } ranges[0]; } *apertures; bool skip_vt_switch; /* no VT switch on suspend/resume required */ };
注意結(jié)構(gòu)體中的fb_fops這一項(xiàng),/dev/fb0 是個(gè)字符設(shè)備,fb_fops就是它的文件操作結(jié)構(gòu)體,它的file_operations操作集在drivers/video/fbdev/core/fbmem.c 文件中:
static const struct file_operations fb_fops = { .owner = THIS_MODULE, .read = fb_read, .write = fb_write, .unlocked_ioctl = fb_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = fb_compat_ioctl, #endif .mmap = fb_mmap, .open = fb_open, .release = fb_release, #ifdef HAVE_ARCH_FB_UNMAPPED_AREA .get_unmapped_area = get_fb_unmapped_area, #endif #ifdef CONFIG_FB_DEFERRED_IO .fsync = fb_deferred_io_fsync, #endif .llseek = default_llseek, };
可以看到有熟悉的open、release等函數(shù)接口。
因此,LCD驅(qū)動(dòng)的重點(diǎn)就是初始化fb_info里面的各個(gè)成員。
fb_info結(jié)構(gòu)體的成員變量很多,需要重點(diǎn)關(guān)注的是這幾個(gè):
var:當(dāng)前的可變參數(shù)
fix:當(dāng)前的固定參數(shù)
fbops:幀緩沖操作函數(shù)集
screen_base:虛擬內(nèi)存基地址(屏幕顯存)
screen_size:虛擬內(nèi)存大小(屏幕顯存大小)
pseudo_palette:偽16位調(diào)色板
初始化完成fb_info后,通過(guò)register_framebuffer函數(shù)向內(nèi)核注冊(cè)剛剛初始化的fb_info。
1.2 LCD驅(qū)動(dòng)文件mxsfb介紹
LCD的驅(qū)動(dòng)文件為mxsfb.c,這是一種platform驅(qū)動(dòng)框架,驅(qū)動(dòng)和設(shè)備匹配之后,mxsfb_probe函數(shù)就會(huì)執(zhí)行。
LCD的初始化通過(guò)mxsfb_probe函數(shù)來(lái)實(shí)現(xiàn),該函數(shù)的主要功能有:
申請(qǐng)fb_info
初始化fb_info結(jié)構(gòu)體中的各個(gè)成員變量
初始化eLCDIF控制器
使用register_framebuffer函數(shù)向Linux內(nèi)核注冊(cè)初始化好的fb_info
該函數(shù)位于:/drivers/video/fbdev/mxsfb.c中
該函數(shù)的實(shí)現(xiàn)如下:
static int mxsfb_probe(struct platform_device *pdev) { const struct of_device_id *of_id = of_match_device(mxsfb_dt_ids, &pdev->dev); struct resource *res; struct mxsfb_info *host; //<-----NXP的fb_info struct fb_info *fb_info; //<-----Linux的fb_info struct pinctrl *pinctrl; int irq = platform_get_irq(pdev, 0); int gpio, ret; if (of_id) pdev->id_entry = of_id->data; gpio = of_get_named_gpio(pdev->dev.of_node, "enable-gpio", 0); if (gpio == -EPROBE_DEFER) return -EPROBE_DEFER; //省略... fb_info = framebuffer_alloc(sizeof(struct fb_info), &pdev->dev);//<--------申請(qǐng)fb_info if (!fb_info) { dev_err(&pdev->dev, "Failed to allocate fbdev\n"); devm_kfree(&pdev->dev, host); return -ENOMEM; } host->fb_info = fb_info; //<---將mxsfb_info與fb_info聯(lián)系起來(lái) fb_info->par = host; //省略... ret = mxsfb_init_fbinfo(host); if (ret != 0) goto fb_pm_runtime_disable; mxsfb_dispdrv_init(pdev, fb_info); //省略... ret = register_framebuffer(fb_info); //<------------注冊(cè) if (ret != 0) { dev_err(&pdev->dev, "Failed to register framebuffer\n"); goto fb_destroy; } console_lock(); ret = fb_blank(fb_info, FB_BLANK_UNBLANK); console_unlock(); if (ret < 0) { dev_err(&pdev->dev, "Failed to unblank framebuffer\n"); goto fb_unregister; } dev_info(&pdev->dev, "initialized\n"); }
其中,register_framebuffer函數(shù)的原型如下:
函數(shù)參數(shù)和返回值含義:
fb_info:需上報(bào)的fb_info
返回值:0-成功,負(fù)值-失敗
1.3 LCD 驅(qū)動(dòng)程序編寫
6ULL的eLCDIF接口驅(qū)動(dòng)程序 NXP 已經(jīng)編 寫好了,因此 LCD 驅(qū)動(dòng)部分我們不需要去修改。我們需要做的就是按照所使用的 LCD 來(lái)修改設(shè)備樹。
1.3.1 查看設(shè)備樹
1.3 先來(lái)看一下NXP官方編寫的Linux下的 LCD 驅(qū)動(dòng)。打開 imx6ull.dtsi,然后找到 lcdif節(jié)點(diǎn)內(nèi)容:
lcdif: lcdif@021c8000 { compatible = "fsl,imx6ul-lcdif", "fsl,imx28-lcdif"; reg = <0x021c8000 0x4000>; interrupts =
其中021c8000 這個(gè)地址,可以從參考手冊(cè)中找到對(duì)應(yīng)的介紹:
1.3.2 屏幕IO配置
打開 imx6ull-myboard.dts 文件,在 iomuxc 節(jié)點(diǎn)中找到如下內(nèi)容:
具體為:
pinctrl_lcdif_dat: lcdifdatgrp { fsl,pins = < MX6UL_PAD_LCD_DATA00__LCDIF_DATA00 0x79 MX6UL_PAD_LCD_DATA01__LCDIF_DATA01 0x79 MX6UL_PAD_LCD_DATA02__LCDIF_DATA02 0x79 MX6UL_PAD_LCD_DATA03__LCDIF_DATA03 0x79 MX6UL_PAD_LCD_DATA04__LCDIF_DATA04 0x79 MX6UL_PAD_LCD_DATA05__LCDIF_DATA05 0x79 MX6UL_PAD_LCD_DATA06__LCDIF_DATA06 0x79 MX6UL_PAD_LCD_DATA07__LCDIF_DATA07 0x79 MX6UL_PAD_LCD_DATA08__LCDIF_DATA08 0x79 MX6UL_PAD_LCD_DATA09__LCDIF_DATA09 0x79 MX6UL_PAD_LCD_DATA10__LCDIF_DATA10 0x79 MX6UL_PAD_LCD_DATA11__LCDIF_DATA11 0x79 MX6UL_PAD_LCD_DATA12__LCDIF_DATA12 0x79 MX6UL_PAD_LCD_DATA13__LCDIF_DATA13 0x79 MX6UL_PAD_LCD_DATA14__LCDIF_DATA14 0x79 MX6UL_PAD_LCD_DATA15__LCDIF_DATA15 0x79 MX6UL_PAD_LCD_DATA16__LCDIF_DATA16 0x79 MX6UL_PAD_LCD_DATA17__LCDIF_DATA17 0x79 MX6UL_PAD_LCD_DATA18__LCDIF_DATA18 0x79 MX6UL_PAD_LCD_DATA19__LCDIF_DATA19 0x79 MX6UL_PAD_LCD_DATA20__LCDIF_DATA20 0x79 MX6UL_PAD_LCD_DATA21__LCDIF_DATA21 0x79 MX6UL_PAD_LCD_DATA22__LCDIF_DATA22 0x79 MX6UL_PAD_LCD_DATA23__LCDIF_DATA23 0x79 >; }; pinctrl_lcdif_ctrl: lcdifctrlgrp { fsl,pins = < MX6UL_PAD_LCD_CLK__LCDIF_CLK 0x79 MX6UL_PAD_LCD_ENABLE__LCDIF_ENABLE 0x79 MX6UL_PAD_LCD_HSYNC__LCDIF_HSYNC 0x79 MX6UL_PAD_LCD_VSYNC__LCDIF_VSYNC 0x79 >; }; pinctrl_pwm1: pwm1grp { fsl,pins = < MX6UL_PAD_GPIO1_IO08__PWM1_OUT 0x110b0 >; };
這里有3個(gè)節(jié)點(diǎn):
子節(jié)點(diǎn)pinctrl_lcdif_dat ,為 RGB LCD 的 24根數(shù)據(jù)線配置項(xiàng)
子節(jié)點(diǎn) pinctrl_lcdif_ctrl ,為RGB LCD 的 4根控制線配置項(xiàng),包括 CLK、ENABLE、VSYNC 和 HSYNC
子節(jié)點(diǎn) pinctrl_pwm1 ,為RGB LCD 的背光亮度配置項(xiàng)
1.3.3 屏幕參數(shù)配置
在imx6ull-myboard.dts 文件中找到lcdif 節(jié)點(diǎn),根據(jù)自己使用的LCD,修改為對(duì)應(yīng)的參數(shù)。
下面是NXP官方板子的參數(shù):
我用的野火7寸屏(GT911,800x480),其參數(shù)為:
修改后的lcdif 節(jié)點(diǎn)如下:
&lcdif { pinctrl-names = "default"; /* 使用到的 IO */ pinctrl-0 = <&pinctrl_lcdif_dat &pinctrl_lcdif_ctrl &pinctrl_lcdif_reset>; display = <&display0>; status = "okay"; display0: display { /* LCD 屬性信息 */ bits-per-pixel = <16>; /* 一個(gè)像素占用幾個(gè)bit */ bus-width = <24>; /* 總線寬度 */ display-timings { native-mode = <&timing0>; /* 時(shí)序信息 */ timing0: timing0 { clock-frequency = <9200000>; /* LCD像素時(shí)鐘,單位Hz */ hactive = <800>; /* LCD X軸像素個(gè)數(shù) */ vactive = <480>; /* LCD Y軸像素個(gè)數(shù) */ hfront-porch = <22>; /* LCD hfp 參數(shù) */ hback-porch = <46>; /* LCD hbp 參數(shù) */ hsync-len = <23>; /* LCD hspw 參數(shù) */ vback-porch = <22>; /* LCD vbp 參數(shù) */ vfront-porch = <4>; /* LCD vfp 參數(shù) */ vsync-len = <1>; /* LCD vspw 參數(shù) */ hsync-active = <0>; /* hsync 數(shù)據(jù)線極性 */ vsync-active = <0>; /* vsync 數(shù)據(jù)線極性 */ de-active = <1>; /* de 數(shù)據(jù)線極性 */ pixelclk-active = <0>; /* clk 數(shù)據(jù)線極性 */ }; }; }; };
1.3.4 屏幕背光配置
通過(guò)PWM信號(hào)來(lái)控制LCD屏幕背光的亮度
pinctrl_pwm1: pwm1grp { fsl,pins = < MX6UL_PAD_GPIO1_IO08__PWM1_OUT 0x110b0 >; };
LCD 背光要用到PWM1,因此也要設(shè)置 PWM1 節(jié)點(diǎn),在imx6ull.dtsi 文件中找到如下內(nèi)容:
這個(gè)節(jié)點(diǎn)信息不用修改,使用默認(rèn)的配置即可。如果要修改的話,也不要修改這里,可以通過(guò)imx6ull-myboard.dts文件中進(jìn)行修改。
imx6ull-myboard.dts中的pwm1節(jié)點(diǎn):
&pwm1 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_pwm1>; status = "okay"; };
imx6ull-myboard.dts中的backlight節(jié)點(diǎn):
backlight { compatible = "pwm-backlight"; pwms = <&pwm1 0 5000000>; brightness-levels = <0 4 8 16 32 64 128 255>; default-brightness-level = <6>; status = "okay"; };
2 實(shí)驗(yàn)測(cè)試
2.1使能Linux logo顯示
uboot啟動(dòng)的時(shí)候,LCD左上角上會(huì)顯示NXP的圖標(biāo),而Linux內(nèi)核啟動(dòng)的時(shí)候,LCD左上角上會(huì)顯示一個(gè)小企鵝。因此,可以通過(guò)小企鵝logo的顯示來(lái)驗(yàn)證LCD 驅(qū)動(dòng)是否正常。
默認(rèn)情況下是已經(jīng)開啟logo顯示的,可以再確認(rèn)一下。
在Linux內(nèi)核源碼目錄,輸入以下指令打開內(nèi)核的圖形化配置:
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
Linux內(nèi)核配置界面:
然后按下路徑找到對(duì)應(yīng)的配置項(xiàng):
-> Device Drivers -> Graphics support -> Bootup logo (LOGO [=y])
最終到達(dá)這個(gè)界面:
這三個(gè)選項(xiàng)分別對(duì)應(yīng)黑白、16 位、24 位色彩格式的 logo。
2.2 編譯設(shè)備樹
修改設(shè)備樹中的lcdif節(jié)點(diǎn)后(主要是修改屏幕的參數(shù)),在Linux內(nèi)核源碼目錄執(zhí)行下面的命令,重新編譯設(shè)備樹并拷貝到網(wǎng)絡(luò)啟動(dòng)位置。
make imx6ull-myboard.dtb cp arch/arm/boot/dts/imx6ull-myboard.dtb ~/myTest/tftpboot/nxp/
然后重啟開發(fā)板,就可以在Linux內(nèi)核驅(qū)動(dòng)的時(shí)候看到屏幕上的企鵝圖標(biāo)了:
2.3 設(shè)置LCD作為終端控制臺(tái)
之前一直使用串口來(lái)顯示板子的啟動(dòng)和調(diào)試信息,實(shí)際上可以設(shè)置 LCD 作為終端進(jìn)行同步顯示:
2.3.1 設(shè)置uboot的bootargs
重啟開發(fā)板,在倒計(jì)時(shí)時(shí)按回充進(jìn)入ubout,可以先看下之前的bootargs配置:
只需要在原來(lái)的基礎(chǔ)上再添加console=tty1即可:
setenv bootargs 'console=tty1 console=ttymxc0,115200 root=/dev/nfs nfsroot=192.168.5.104:/home/xxpcb/myTest/nfs/rootfs,proto=tcp,nfsvers=4 rw ip=192.168.5.102:192.168.5.104:192.168.5.1:255.255.255.0::eth1:off' saveenv
然后重啟開發(fā)板,在Linux內(nèi)核驅(qū)動(dòng)的時(shí)候就可以在屏幕上看到輸出信息了:
對(duì)比一下串口輸出的信息,可以看出屏幕輸出到Freeing unused kernel memory: 400K (8090e000 - 80972000)這句后就沒有了,沒有出現(xiàn)按下回車鍵繼續(xù)的提示,也沒有顯示開啟自啟動(dòng)的hello word測(cè)試程序的打印,這是因?yàn)槟承┰O(shè)置還未完成。
2.3.2 修改/etc/inittab文件
該修改用于設(shè)置屏幕作為終端進(jìn)行交互。
打開根文件系統(tǒng)中的/etc/inittab 文件,加入下面這一行:
tty1::askfirst:-/bin/sh
保存后重啟板子,并在板子的USB接口插上鍵盤,就可以通過(guò)鍵盤和板子交互了:
現(xiàn)在通過(guò)板子插入鍵盤,也可以在屏幕上操作板子了。
注意,之前設(shè)置的開機(jī)啟動(dòng)的hello word程序的打印沒有出現(xiàn)在屏幕上,是因?yàn)閜rintf的輸入沒有設(shè)置的LCD中,我們可以通過(guò)將輸出指向 /dev/tty1 來(lái)實(shí)現(xiàn)LCD屏幕的打印,比如測(cè)試屏幕輸出hello linux:
echo hello linux > /dev/tty1
2.4 其它問(wèn)題
2.4.1 自動(dòng)熄屏的問(wèn)題
當(dāng)沒有操作LCD屏幕一段時(shí)間后,屏幕會(huì)自動(dòng)黑屏,這時(shí)可以通過(guò)接入鍵盤按下回車鍵進(jìn)行喚醒(也可以通過(guò)板子的ON/OFF按鍵進(jìn)行喚醒,因?yàn)樵摪存I也被賦予了回車鍵的功能)。
這個(gè)時(shí)間是在Linux源碼的 drivers/tty/vt/vt.c中設(shè)置的,默認(rèn)是10分鐘(10*60秒)。
如果想讓屏幕一直亮著,可以將改值設(shè)為0,并重新編輯Linux內(nèi)核得到zImage,然后用新的zImage啟動(dòng)開發(fā)板。
如果不想修改zImage,另外一種方式可以創(chuàng)建一個(gè)開機(jī)啟動(dòng)的應(yīng)用程序來(lái)控制屏幕不熄滅, lcd_always_on.c的內(nèi)容為:
#include
在ubuntu中編譯該程序,然后將可執(zhí)行程序拷貝到板子的根文件系統(tǒng)中:
arm-linux-gnueabihf-gcc lcd_always_on.c -o lcd_always_on cp lcd_always_on ~/myTest/nfs/rootfs/usr/bin/
然后,/etc/init.d/rcS中設(shè)置該程序開機(jī)自啟動(dòng)即可。
保存后,重啟開發(fā)板,屏幕就不會(huì)自動(dòng)熄屏了。
2.4.2 屏幕亮度調(diào)節(jié)
屏幕的亮度也是可以調(diào)節(jié)的,設(shè)備樹中背光節(jié)點(diǎn)設(shè)置了8 個(gè)等級(jí),可以在 0~7范圍內(nèi)進(jìn)行亮度調(diào)節(jié),進(jìn)入下面的目錄,可以查看當(dāng)前屏幕的亮度:
/sys/devices/platform/backlight/backlight/backlight
通過(guò)下面的指令可以實(shí)時(shí)修改屏幕的亮度,比如修改亮度為1:
echo 1 > brightness
總結(jié)
本篇介紹了LCD屏幕驅(qū)動(dòng)相關(guān)知識(shí)并進(jìn)行了實(shí)驗(yàn),因?yàn)镹XP官方的板子和我這個(gè)板子的LCD引腳一樣,因此主要的修改就是將設(shè)備樹中的lcdif 節(jié)點(diǎn)的屏幕參數(shù)進(jìn)行修改即可。
通過(guò)實(shí)驗(yàn),可以將企鵝logo顯示出來(lái),并將板子的輸出信息定向到了LCD屏幕顯示,通過(guò)接入鍵盤可實(shí)現(xiàn)與Linux板子的交互。最后,還測(cè)試了屏幕熄屏和亮度調(diào)節(jié)功能。
Linux 嵌入式
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。