原文地址:U-boot中通過ENV設(shè)置顯示設(shè)備(如LCD)參數(shù)的方法與格式 作者:tekkamanninja
對于一個移植比較完善的U-boot來說,,顯示設(shè)備一般也是可以使用的,。在嵌入式中的LCD液晶屏一般由芯片的內(nèi)置的LCD控制器或者VPSS(視頻處理子系統(tǒng))來控制。在U-boot中已經(jīng)實現(xiàn)了類似framebuffer的機制,只要在移植的時候完成對LCD控制等顯示設(shè)備的初始化,,并將framebuffer的對應(yīng)的內(nèi)存地址空間通過結(jié)構(gòu)體傳遞給u-boot的中控制臺實現(xiàn)部分,,就可以在u-boot的啟動時從LCD上看到u-boot的控制臺終端信息,,并且可以在LCD的左上角看到uboot的logo,。對于顯示部分的初始化代碼根據(jù)CPU芯片的不同而異,但是大體結(jié)構(gòu)都是一樣的,。 在大部分液晶控制器的初始化中都需要以下參數(shù):
- /******************************************************************
- * 解析結(jié)構(gòu)體
- ******************************************************************/
- struct ctfb_res_modes {
- int xres; /* 可見分辨率 */
- int yres;
- /* 時序: 所有值都以像素時鐘為單位(當(dāng)然除了像素時鐘本身) */
- int pixclock; /* 像素時鐘(單位:微秒) */
- int left_margin; /* 從行同步到圖像左邊沿的像素時鐘數(shù) */
- int right_margin; /* 從行同步到圖像右邊沿的像素時鐘數(shù) */
- int upper_margin; /* 從場同步到圖像上邊沿的行數(shù) */
- int lower_margin; /* 從場同步到圖像下邊沿的行數(shù) */
- int hsync_len; /* 行同步時間長度(像素時鐘數(shù)) */
- int vsync_len; /* 場同步時間長度(行數(shù)) */
- int sync; /* see FB_SYNC_* */
- int vmode; /* see FB_VMODE_* */
- };
而這些參數(shù)將隨著所使用的LCD液晶屏不同而不同,。如果將這些參數(shù)寫死在代碼中,這樣換一個不同參數(shù)的液晶屏就要重新給參數(shù),,重新編譯一次uboot,,這么做當(dāng)然比較不合適。動態(tài)配置這些參數(shù)對于U-boot來說,,當(dāng)然是從自身ENV(環(huán)境變量)中獲取,。兩年前我在移植MINI6410的時候,,就對液晶屏的顯示做了支持,,并參考uboot中別的液晶驅(qū)動做了動態(tài)參數(shù)配置的支持,但是當(dāng)時寫完代碼之后沒有仔細(xì)去測試和研究其配置的方法和格式,。這些研究顯示設(shè)備參數(shù)的傳遞的時候才重新回來驗證一下原來的代碼,,發(fā)現(xiàn)原來我在MINI6410上的代碼是完全OK的,并支持3種不同的配置方法,。關(guān)鍵的參數(shù)獲取代碼如下: - got_mode = 0;
- videomode = CONFIG_SYS_DEFAULT_VIDEO_MODE;
- /* get video mode via environment */
- if (((penv = getenv ("videomode")) != NULL) &&
- (penv[0] <= '9')) {
- videomode = (int) simple_strtoul (penv, NULL, 16);
- /* parameter are vesa modes */
- /* search params */
- for (i = 0; i < VESA_MODES_COUNT; i++) {
- if (vesa_modes[i].vesanr == videomode)
- break;
- }
- if (i == VESA_MODES_COUNT) {
- printf ("no VESA Mode found, switching to mode 0x%x ",
- CONFIG_SYS_DEFAULT_VIDEO_MODE);
- i = VESA_MODES_COUNT - 2;
- }
- res_mode = (struct ctfb_res_modes *) &res_mode_init[vesa_modes[i].resindex];
- bits_per_pixel = vesa_modes[i].bits_per_pixel;
- } else {
- /*init to default params*/
- res_mode = (struct ctfb_res_modes *) &res_mode_init[vesa_modes[VESA_MODES_COUNT - 2].resindex];
- bits_per_pixel = vesa_modes[VESA_MODES_COUNT - 2].bits_per_pixel;
- if ((penv = getenv ("video-mode")) != NULL) {
- ret = video_get_video_mode(&var_mode.xres, &var_mode.yres,
- &bits_per_pixel, &dumpfreq, &p);
- while ((i = video_get_param_len (p, ',')) != 0) {
- GET_OPTION ("le:", var_mode.left_margin)
- GET_OPTION ("ri:", var_mode.right_margin)
- GET_OPTION ("up:", var_mode.upper_margin)
- GET_OPTION ("lo:", var_mode.lower_margin)
- GET_OPTION ("hs:", var_mode.hsync_len)
- GET_OPTION ("vs:", var_mode.vsync_len)
- GET_OPTION ("pclk:", var_mode.pixclock)
- p += i;
- if (*p != 0)
- p++; /* skip ',' */
- }
- res_mode = (struct ctfb_res_modes *) &var_mode;
- } else {
- printf ("no Video params found, try bootargs~~ ");
- res_mode = (struct ctfb_res_modes *) &var_mode;
- bits_per_pixel = video_get_params (res_mode, "bootargs");
- if((bits_per_pixel != 8) &&
- (bits_per_pixel != 15) &&
- (bits_per_pixel != 16) &&
- (bits_per_pixel != 24)) {
- printf ("Get params error, set to default!");
- res_mode = (struct ctfb_res_modes *) &res_mode_init[vesa_modes[VESA_MODES_COUNT - 2].resindex];
- bits_per_pixel = vesa_modes[VESA_MODES_COUNT - 2].bits_per_pixel;
- }
- }
- }
上面的代碼根據(jù)先后順序會從3個不同的環(huán)境變量中獲取所連接的液晶屏參數(shù):
- 一,、選擇預(yù)先編譯進uboot的某個顯示器參數(shù)(videomode=)
這種形式的參數(shù)配置適合預(yù)先知道你要用的幾個液晶屏的參數(shù),并將其寫在driver/video/videomodes.c中,。比如我在MINI6410的移植中添加了3.5寸,、 4.3寸和7寸屏的參數(shù):
- const struct ctfb_vesa_modes vesa_modes[VESA_MODES_COUNT] = {
- ......
- {0x211, RES_MODE_240x320, 16},
- {0x212, RES_MODE_480x272, 16},
- {0x213, RES_MODE_800x480, 16},
- };
- const struct ctfb_res_modes res_mode_init[RES_MODES_COUNT] = {
- /* x y pixclk le ri up lo hs vs s vmode */
- ......
- {240, 320, 90000, 1, 4, 1, 1, 30, 4, 0, FB_VMODE_NONINTERLACED},
- {480, 272, 75000, 2, 3, 1, 1, 40, 1, 0, FB_VMODE_NONINTERLACED},
- {800, 480, 50000, 2, 2, 2, 2, 41, 4, 0, FB_VMODE_NONINTERLACED},
- };
而對于這三種屏,只需要在環(huán)境變量中配置: - 3.5寸 :videomode=0x211
- 4.3寸 :videomode=0x212
- 7 寸 :videomode=0x213
即,, - 3.5寸 : setenv videomode 0x211 ;saveenv
- 4.3寸 : setenv videomode 0x212 ;saveenv
- 7 寸 : setenv videomode 0x213 ;saveenv
- 二,、通過環(huán)境變量(video-mode=)獲取參數(shù)
如果在環(huán)境變量中沒有videomode,則uboot會繼續(xù)查找是否有video-mode變量,。如果有,,代碼會從這個環(huán)境變量中直接解析出所需要的參數(shù),這個環(huán)境變量的格式應(yīng)為: - video-mode=ctfb:800x480-16@60,le:2,ri:2,up:2,lo:2,hs:41,vs:4,pclk:50000
其中ctfb 有沒有都無所謂,但是后面的 :一定要有 藍(lán)色的符合只要不是數(shù)字就好,,寫成這個形式比較標(biāo)準(zhǔn)罷了,。這個看了代碼就明白了。
這種情況比較適用于僅uboot需要這些顯示參數(shù),,并需要靈活配置的情況,。這些參數(shù)沒有主動傳遞給內(nèi)核。 - 三,、從將要傳遞給內(nèi)核的cmdline(bootargs=)獲取參數(shù)
如果通過上面兩步,,在環(huán)境變量中沒有正確地解析出參數(shù),代碼會到bootargs(也就是傳遞給內(nèi)核的cmdline)中找相關(guān)參數(shù),,在bootargs中的格式必須是: - video=ctfb:x:800,y:480,depth:16,le:2,ri:2,up:2,lo:2,hs:41,vs:4,pclk:50000,vmode:0,sync:0
紅色的字體是必須的,,這個看了 video_get_params 函數(shù)你就明白了。 這種情況可以靈活配置,,并且這些參數(shù)會通過tagged-list--->cmdline主動傳遞給內(nèi)核,,適用于uboot和內(nèi)核共享ENV區(qū)參數(shù)的情況。
而對于在上面我在mini6410中實現(xiàn)的代碼,,其實可以移植到別的芯片啟動中去,,只要將參數(shù)解析到struct ctfb_res_modes 中去,其中的數(shù)據(jù)就可以用于初始化LCD控制器等初始化代碼中了
|