1.LCD 操作原理
在 Linux 系統(tǒng)中通過(guò) Framebuffer 驅(qū)動(dòng)程序來(lái)控制 LCD。Frame 是幀的意思,buffer 是緩沖的意思,這意味著 Framebuffer 就是一塊內(nèi)存,里面保存著一幀圖像。Framebuffer 中保存著一幀圖像的每一個(gè)像素顏色值,假設(shè) LCD 的分辨率是 1024x768,每一個(gè)像素的顏色用 32 位來(lái)表示,那么 Framebuffer 的大小就是:1024x768x32/8=3145728 字節(jié)。LCD的操作原理:
-
驅(qū)動(dòng)程序設(shè)置好 LCD 控制器:根據(jù) LCD 的參數(shù)設(shè)置 LCD 控制器的時(shí)序、信號(hào)極性;根據(jù) LCD 分辨率、 BPP 分配 Framebuffer 。
-
APP 使用 ioctl 獲得 LCD 分辨率、 BPP
-
APP 通過(guò) mmap 映射 Framebuffer ,在 Framebuffer 中寫入數(shù)據(jù)

(x,y)像素起始地址=fb_base+(xres*bpp/8)*y + x*bpp/8

2.涉及的 API 函數(shù)
2.1 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ù)說(shuō)明:
-
pathname 表示打開文件的路徑;
-
Flags 表示打開文件的方式,常用的有以下 6 種,O_RDWR 表示可讀可寫方式打開 ;O_RDONLY 表示只讀方式打開 ;O_WRONLY 表示只寫方式打開 ;O_APPEND 表示如果這個(gè)文件中本來(lái)是有內(nèi)容的,則新寫入的內(nèi)容會(huì)接續(xù)到原來(lái)內(nèi)容的后面;O_TRUNC 表示如果這個(gè)文件中本來(lái)是有內(nèi)容的,則原來(lái)的內(nèi)容會(huì)被丟棄,截?cái)啵?O_CREAT 表示當(dāng)前打開文件不存在,我們創(chuàng)建它并打開它,通常與 O_EXCL 結(jié)合使用,當(dāng)沒(méi)有文件時(shí)創(chuàng)建文件,有這個(gè)文件時(shí)會(huì)報(bào)錯(cuò)提醒我們;
-
Mode 表示創(chuàng)建文件的權(quán)限,只有在 flags 中使用了 O_CREAT 時(shí)才有效,否則忽略。
-
返回值:打開成功返回文件描述符,失敗將返回 -1 。
2.2?ioctl 函數(shù)
#include <sys/ioctl.h>
? ? ? ? 函數(shù)原型:
int ioctl(int fd, unsigned long request, ...);
????????函數(shù)說(shuō)明:
-
fd 表示文件描述符;
-
request 表示與驅(qū)動(dòng)程序交互的命令,用不同的命令控制驅(qū)動(dòng)程序輸出我們需要的數(shù)據(jù);
-
… 表示可變參數(shù) arg ,根據(jù) request 命令,設(shè)備驅(qū)動(dòng)程序返回輸出的數(shù)據(jù)。
-
返回值:打開成功返回文件描述符,失敗將返回 -1 。
2.3?mmap 函數(shù)
#include <sys/mman.h>
? ? ? ? 函數(shù)原型:
void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
????????函數(shù)說(shuō)明: 文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-737662.html
-
addr 表示指定映射的內(nèi)存起始地址,通常設(shè)為 NULL 表示讓系統(tǒng)自動(dòng)選定地址,并在成功映射后返回該地址;
-
length 表示將文件中多大的內(nèi)容映射到內(nèi)存中;
-
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),原來(lái)的文件會(huì)改變。MAP_PRIVATE 表示對(duì)映射區(qū)域的操作會(huì)產(chǎn)生一個(gè)映射文件的復(fù)制,對(duì)此區(qū)域的任何修改都不會(huì)寫回原來(lái)的文件內(nèi)容中。
-
返回值:若成功映射,將返回指向映射的區(qū)域的指針,失敗將返回 -1 。
3.Framebuffer 程序分析
3.1 打開設(shè)備節(jié)點(diǎn)
fd_fb = open("/dev/fb0", O_RDWR);//打開設(shè)備節(jié)點(diǎn)
if (fd_fb < 0)
{
printf("can't open /dev/fb0\n");
return -1;
}
3.2 獲取 LCD 參數(shù)
文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-737662.html
static struct fb_var_screeninfo var; /* Current var */
if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var))
{
printf("can't get var\n");
return -1;
}

3.3?映射 Framebuffer
line_width = var.xres * var.bits_per_pixel / 8;//屏幕寬度大?。ǘ嗌賯€(gè)字節(jié))
pixel_width = var.bits_per_pixel / 8;//一個(gè)像素大小(多少個(gè)字節(jié))
screen_size = var.xres * var.yres * var.bits_per_pixel / 8; //屏幕大?。ǘ嗌賯€(gè)字節(jié))
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;
}
3.4 描點(diǎn)函數(shù)
//傳入的 color 表示顏色,它的格式永遠(yuǎn)是 0x00RRGGBB,即 RGB888。當(dāng) LCD 是 16bpp 時(shí),要把 color 變量中的 R、G、B 抽出來(lái)再合并成 RGB565 格式
void lcd_put_pixel(int x, int y, unsigned int color)
{
unsigned char *pen_8 = fb_base+y*line_width+x*pixel_width;//計(jì)算要顯示的坐標(biāo)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;//對(duì)于 8bpp,color 就不再表示 RBG 三原色了,這涉及調(diào)色板的概念,color 是調(diào)色板的值。
break;
}
case 16:
{
/* 565 */
red = (color >> 16) & 0xff; //32位red數(shù)據(jù)是:16~24 24~32沒(méi)用
green = (color >> 8) & 0xff; //32位green數(shù)據(jù)是:8~16
blue = (color >> 0) & 0xff; //32位blue數(shù)據(jù)是:0~8
//總共16位:紅保留高5位放在11~16,綠保留高6位放在5~11,藍(lán)保留高5位放在0~5
color = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);
*pen_16 = color;//將計(jì)算的位置坐標(biāo)顯示該顏色
break;
}
case 32:
{
*pen_32 = color;//對(duì)于 32bpp,顏色格式跟 color 參數(shù)一致,可以直接寫入Framebuffer。
break;
}
default:
{
printf("can't surport %dbpp\n", var.bits_per_pixel);
break;
}
}
}
3.5?隨便畫幾個(gè)點(diǎn)的完整程序
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/fb.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
static int fd_fb;
static struct fb_var_screeninfo var; /* Current var */
static int screen_size;
static unsigned char *fb_base;
static unsigned int line_width;
static unsigned int pixel_width;
//傳入的 color 表示顏色,它的格式永遠(yuǎn)是 0x00RRGGBB,即 RGB888。當(dāng) LCD 是 16bpp 時(shí),要把 color 變量中的 R、G、B 抽出來(lái)再合并成 RGB565 格式
void lcd_put_pixel(int x, int y, unsigned int color)
{
unsigned char *pen_8 = fb_base+y*line_width+x*pixel_width;//計(jì)算要顯示的坐標(biāo)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;//對(duì)于 8bpp,color 就不再表示 RBG 三原色了,這涉及調(diào)色板的概念,color 是調(diào)色板的值。
break;
}
case 16:
{
/* 565 */
red = (color >> 16) & 0xff; //32位red數(shù)據(jù)是:16~24 24~32沒(méi)用
green = (color >> 8) & 0xff; //32位green數(shù)據(jù)是:8~16
blue = (color >> 0) & 0xff; //32位blue數(shù)據(jù)是:0~8
//總共16位:紅保留高5位放在11~16,綠保留高6位放在5~11,藍(lán)保留高5位放在0~5
color = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);
*pen_16 = color;//將計(jì)算的位置坐標(biāo)顯示該顏色
break;
}
case 32:
{
*pen_32 = color;//對(duì)于 32bpp,顏色格式跟 color 參數(shù)一致,可以直接寫入Framebuffer。
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);//打開設(shè)備節(jié)點(diǎn)
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;//屏幕寬度大?。ǘ嗌賯€(gè)字節(jié))
pixel_width = var.bits_per_pixel / 8;//一個(gè)像素大?。ǘ嗌賯€(gè)字節(jié))
screen_size = var.xres * var.yres * var.bits_per_pixel / 8; //屏幕大?。ǘ嗌賯€(gè)字節(jié))
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;
}
到了這里,關(guān)于LCD驅(qū)動(dòng)程序——Framebuffer應(yīng)用編程的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!