蜜蜂vp加速器七天試用杭州優(yōu)化公司在線留言
前言:
在 Linux 系統(tǒng)中通過 Framebuffer 驅(qū)動(dòng)程序來控制 LCD。Frame 是幀的意 思,buffer 是緩沖的意思,這意味著 Framebuffer 就是一塊內(nèi)存,里面保存著 一幀圖像。Framebuffer 中保存著一幀圖像的每一個(gè)像素顏色值,假設(shè) LCD 的 分辨率是 1024x768,每一個(gè)像素的顏色用 32 位來表示,那么 Framebuffer 的 大小就是:1024x768x32/8=3145728 字節(jié)。
目錄
一、LCD 操作原理
二、涉及的 API 函數(shù)
?1.open 函數(shù)
2.ioctl 函數(shù)
?3.mmap 函數(shù)
三、Framebuffer 程序分析?
1.Framebuffer源碼如下:
2. 打開設(shè)備:
3.獲取 LCD 參數(shù)
4.映射 Framebuffer
5.描點(diǎn)函數(shù)
6.隨便畫幾個(gè)點(diǎn)?
四、上機(jī)實(shí)驗(yàn)測試
一、LCD 操作原理
1.驅(qū)動(dòng)程序設(shè)置好 LCD 控制器:
???????? 根據(jù) LCD 的參數(shù)設(shè)置 LCD 控制器的時(shí)序、信號(hào)極性;
? ? ? ? ?根據(jù) LCD 分辨率、BPP 分配 Framebuffer。
2.APP 使用 ioctl 獲得 LCD 分辨率、BPP
3.APP 通過 mmap 映射 Framebuffer,在 Framebuffer 中寫入數(shù)據(jù)
????????假設(shè)需要設(shè)置 LCD 中坐標(biāo)(x,y)處像素的顏色,首要要找到這個(gè)像素對(duì)應(yīng)的 內(nèi)存,然后根據(jù)它的 BPP 值設(shè)置顏色。假設(shè) fb_base 是 APP 執(zhí)行 mmap 后得到 的 Framebuffer 地址,如圖
可以用以下公式算出(x,y)坐標(biāo)處像素對(duì)應(yīng)的 Framebuffer 地址:
(x,y)像素起始地址=fb_base+(xres*bpp/8)*y + x*bpp/8
????????最后一個(gè)要解決的問題就是像素的顏色怎么表示?它是用 RGB 三原色(紅、綠、 藍(lán))來表示的,在不同的 BPP 格式中,用不同的位來分別表示 R、G、B
? ? ? ? ?對(duì)于 32BPP,一般只設(shè)置其中的低 24 位,高 8 位表示透明度,一般的 LCD 都不支持。
???????? 對(duì)于 24BPP,硬件上為了方便處理,在 Framebuffer 中也是用 32 位來表 示,效果跟 32BPP 是一樣的。
????????對(duì)于 16BPP,常用的是 RGB565;很少的場合會(huì)用到 RGB555,這可以通過 ioctl 讀取驅(qū)動(dòng)程序中的 RGB 位偏移來確定使用哪一種格式。
二、涉及的 API 函數(shù)
目的是:打開 LCD 設(shè)備節(jié)點(diǎn),獲取分辨率等參數(shù),映射 Framebuffer,最后實(shí)現(xiàn)描點(diǎn)函數(shù)。
?1.open 函數(shù)
在 Ubuntu 中執(zhí)行“man 2 open”,可以看到 open 函數(shù)的說明:
頭文件:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
函數(shù)原型:
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
函數(shù)說明:
1.pathname 表示打開文件的路徑;
2.Flags 表示打開文件的方式,常用的有以下 6 種,
????????? O_RDWR 表示可讀可寫方式打開;
????????? O_RDONLY 表示只讀方式打開;
????????? O_WRONLY 表示只寫方式打開;
????????? O_APPEND 表示如果這個(gè)文件中本來是有內(nèi)容的,則新寫入的內(nèi)容會(huì)接續(xù)到原來內(nèi)容的后面;
????????? O_TRUNC 表示如果這個(gè)文件中本來是有內(nèi)容的,則原來的內(nèi)容會(huì)被丟棄,截?cái)?#xff1b; ????????? O_CREAT 表示當(dāng)前打開文件不存在,我們創(chuàng)建它并打開它,通常與 O_EXCL 結(jié)合使用,當(dāng)沒有文件時(shí)創(chuàng)建文件,有這個(gè)文件時(shí)會(huì)報(bào)錯(cuò)提醒我們;
3.Mode 表示創(chuàng)建文件的權(quán)限,只有在 flags 中使用了 O_CREAT 時(shí)才有效, 否則忽略。
4.返回值:打開成功返回文件描述符,失敗將返回-1。
2.ioctl 函數(shù)
在 Ubuntu 中執(zhí)行“man ioctl”,可以看到 ioctl 函數(shù)的說明:
頭文件:?
#include <sys/ioctl.h>
函數(shù)原型:
int ioctl(int fd, unsigned long request, ...);
1. fd 表示文件描述符;
2. request 表示與驅(qū)動(dòng)程序交互的命令,用不同的命令控制驅(qū)動(dòng)程序輸出我們 需要的數(shù)據(jù); 3. … 表示可變參數(shù) arg,根據(jù) request 命令,設(shè)備驅(qū)動(dòng)程序返回輸出的數(shù)據(jù)。
4.返回值:打開成功返回文件描述符,失敗將返回-1。
?????ioctl 的作用非常強(qiáng)大、靈活。不同的驅(qū)動(dòng)程序內(nèi)部會(huì)實(shí)現(xiàn)不同的 ioctl, APP 可以使用各種 ioctl 跟驅(qū)動(dòng)程序交互:可以傳數(shù)據(jù)給驅(qū)動(dòng)程序,也可以從驅(qū)動(dòng)程序中讀出數(shù)據(jù)。
?3.mmap 函數(shù)
在 Ubuntu 中執(zhí)行“man mmap”,可以看到 mmap 函數(shù)的說明:
頭文件:
#include <sys/mman.h>
函數(shù)原型:
void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
1.addr 表示指定映射的內(nèi)存起始地址,通常設(shè)為 NULL 表示讓系統(tǒng)自動(dòng)選定 地址,并在成功映射后返回該地址;
2.length 表示將文件中多大的內(nèi)容映射到內(nèi)存中;
3.prot 表示映射區(qū)域的保護(hù)方式,可以為以下 4 種方式的組合
????????? PROT_EXEC 映射區(qū)域可被執(zhí)行
????????? PROT_READ 映射區(qū)域可被讀出
????????? PROT_WRITE 映射區(qū)域可被寫入
????????? PROT_NONE 映射區(qū)域不能存取 Flags 表示影響映射區(qū)域的不同特性,常用的有以下兩種
????????? MAP_SHARED 表示對(duì)映射區(qū)域?qū)懭氲臄?shù)據(jù)會(huì)復(fù)制回文件內(nèi),原來的文件會(huì)改變。 ????????? MAP_PRIVATE 表示對(duì)映射區(qū)域的操作會(huì)產(chǎn)生一個(gè)映射文件的復(fù)制,對(duì)此區(qū)域的任何修改都不會(huì)寫回原來的文件內(nèi)容中。
5.返回值:若成功映射,將返回指向映射的區(qū)域的指針,失敗將返回-1。
三、Framebuffer 程序分析?
1.Framebuffer源碼如下:
/*********************************************************************** 函數(shù)名稱: lcd_put_pixel* 功能描述: 在LCD指定位置上輸出指定顏色(描點(diǎn))* 輸入?yún)?shù): x坐標(biāo),y坐標(biāo),顏色* 輸出參數(shù): 無* 返 回 值: 會(huì)***********************************************************************/
void lcd_put_pixel(int x, int y, unsigned int color)
{unsigned char *pen_8 = fb_base+y*line_width+x*pixel_width;unsigned short *pen_16;unsigned int *pen_32;unsigned int red, green, blue;pen_16 = (unsigned short *)pen_8;pen_32 = (unsigned int *)pen_8;switch (var.bits_per_pixel){case 8:{*pen_8 = color;break;}case 16:{/* 565 */red = (color >> 16) & 0xff;green = (color >> 8) & 0xff;blue = (color >> 0) & 0xff;color = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);*pen_16 = color;break;}case 32:{*pen_32 = color;break;}default:{printf("can't surport %dbpp\n", var.bits_per_pixel);break;}}
}int main(int argc, char **argv)
{int i;fd_fb = open("/dev/fb0", O_RDWR);if (fd_fb < 0){printf("can't open /dev/fb0\n");return -1;}if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var)){printf("can't get var\n");return -1;}line_width = var.xres * var.bits_per_pixel / 8;pixel_width = var.bits_per_pixel / 8;screen_size = var.xres * var.yres * var.bits_per_pixel / 8;fb_base = (unsigned char *)mmap(NULL , screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0);if (fb_base == (unsigned char *)-1){printf("can't mmap\n");return -1;}/* 清屏: 全部設(shè)為白色 */memset(fb_base, 0xff, screen_size);/* 隨便設(shè)置出100個(gè)為紅色 */for (i = 0; i < 100; i++)lcd_put_pixel(var.xres/2+i, var.yres/2, 0xFF0000);munmap(fb_base , screen_size);close(fd_fb);return 0;
}
2. 打開設(shè)備:
首先打開設(shè)備節(jié)點(diǎn):
fd_fb = open("/dev/fb0", O_RDWR);if (fd_fb < 0){printf("can't open /dev/fb0\n");return -1;}
3.獲取 LCD 參數(shù)
LCD 驅(qū)動(dòng)程序給 APP 提供 2 類參數(shù):
????????可變的參數(shù) fb_var_screeninfo
????????固 定的參數(shù) fb_fix_screeninfo
編寫應(yīng)用程序時(shí)主要關(guān)心可變參數(shù),它的結(jié)構(gòu) 體定義如下(#include <Linux/fd.h>):
可以使用以下代碼獲取 fb_var_screeninfo:
static struct fb_var_screeninfo var; /* Current var */......if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var))
{printf("can't get var\n");return -1;
}
????????注意到 ioctl 里用的參數(shù)是:FBIOGET_VSCREENINFO,它表示 get var screen info,獲得屏幕的可變信息;當(dāng)然也可以使用 FBIOPUT_VSCREENINFO 來調(diào)整這 些參數(shù),但是很少用到。
對(duì)于固定的參數(shù) fb_fix_screeninfo,在應(yīng)用編程中很少用到。它的結(jié)構(gòu) 體定義如下:
?可以使用 ioctl FBIOGET_FSCREENINFO 來讀出這些信息,但是很少用到。
4.映射 Framebuffer
????????要映射一塊內(nèi)存,需要知道它的地址──這由驅(qū)動(dòng)程序來設(shè)置,需要知道它 的大小──這由應(yīng)用程序決定。代碼如下:
line_width = var.xres * var.bits_per_pixel / 8;
pixel_width = var.bits_per_pixel / 8;
screen_size = var.xres * var.yres * var.bits_per_pixel / 8;
fb_base = (unsigned char *)mmap(NULL , screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0);
if (fb_base == (unsigned char *)-1)
{printf("can't mmap\n");return -1;
}
screen_size 是整個(gè) Framebuffer 的大小;PROT_READ | PROT_WRITE 表示該區(qū)域可讀、可寫;MAP_SHARED 表示該區(qū)域是共享的,APP 寫 入數(shù)據(jù)時(shí),會(huì)直達(dá)驅(qū)動(dòng)程序。
5.描點(diǎn)函數(shù)
能夠在 LCD 上描繪指定像素后,就可以寫字、畫圖,描點(diǎn)函數(shù)是基礎(chǔ)。代碼如下:
void lcd_put_pixel(int x, int y, unsigned int color)
{unsigned char *pen_8 = fb_base+y*line_width+x*pixel_width;unsigned short *pen_16;unsigned int *pen_32;unsigned int red, green, blue;pen_16 = (unsigned short *)pen_8;pen_32 = (unsigned int *)pen_8;switch (var.bits_per_pixel){case 8:{*pen_8 = color;break;}case 16:{/* 565 */red = (color >> 16) & 0xff;green = (color >> 8) & 0xff;blue = (color >> 0) & 0xff;color = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);*pen_16 = color;break;}case 32:{*pen_32 = color;break;}default:{printf("can't surport %dbpp\n", var.bits_per_pixel);break;}}
}
1.void lcd_put_pixel(int x, int y, unsigned int color)
傳入的 color 表示顏色,它的格式永遠(yuǎn)是 0x00RRGGBB,即 RGB888。 當(dāng) LCD 是16bpp 時(shí),要把 color 變量中的 R、G、B 抽出來再合并成 RGB565 格式。
2.unsigned char *pen_8 = fb_base+y*line_width+x*pixel_width;
計(jì)算(x,y)坐標(biāo)上像素對(duì)應(yīng)的 Framebuffer 地址。
3.*pen_8 = color;
對(duì)于 8bpp,color 就不再表示 RBG 三原色了,這涉及調(diào)色板的概念,color 是調(diào)色板的值。 第 49~51 行,先從 color 變量中把 R、G、B 抽出來。
4.? red = (color >> 16) & 0xff;
? ? ?green = (color >> 8) & 0xff;
? ? ?blue = (color >> 0) & 0xff;
把 red、green、blue 這三種 8 位顏色值,根據(jù) RGB565 的格式, 只保留 red 中的高 5 位、green 中的高 6 位、blue 中的高 5 位,組合成一個(gè)新 的 16 位顏色值。
5.color = ((red >> 3) > 2) > 3);
把新的 16 位顏色值寫入 Framebuffer。
6.*pen_32 = color;
對(duì)于 32bpp,顏色格式跟 color 參數(shù)一致,可以直接寫入 Framebuffer。
6.隨便畫幾個(gè)點(diǎn)?
本程序的 main 函數(shù),在最后只是簡單地畫了幾個(gè)點(diǎn):
/* 清屏: 全部設(shè)為白色 */memset(fb_base, 0xff, screen_size);/* 隨便設(shè)置出100個(gè)為紅色 */for (i = 0; i < 100; i++)lcd_put_pixel(var.xres/2+i, var.yres/2, 0xFF0000);
四、上機(jī)實(shí)驗(yàn)測試
在 Ubuntu 中編譯程序,先設(shè)置交叉編譯工具鏈,再執(zhí)行以下命令:
book@100ask:~/source/07_framebuffer$ arm-buildroot-linux-gnueabihf-gcc -o show_pixel show_pixel.c
book@100ask:~/source/07_framebuffer$ cp show_pixel ~/nfs_rootfs/
在開發(fā)板上掛載網(wǎng)絡(luò)文件系統(tǒng)
??????
?運(yùn)行程序
[root@100ask:~]# mount -t nfs -o nolock,vers=3 192.168.5.11:/home/book/nfs_rootfs /mnt
[root@100ask:~]# /mnt/show_pixel
?運(yùn)行效果: