=============================================
一、framebuffer子系统(帧缓冲):lcd屏
1,了解lcd屏的基础知识
2,framebuffer子系统框架
3,lcd驱动移植
4,linux启动logo的制作
5,framebuffer框架代码跟读
6,应用程序如何向lcd上显示数据
==================================================
一,了解lcd屏的基础知识
//参考:E:\peter\2010\高级驱动\5day\framebuffer研究.xls
二,framebuffer子系统框架
---------------------------------------------------------
fb通用层--------//drivers/video/fbmem.c
作用:
1,与用户交互
2,实现fops中的接口---mmap
--------------------------------------------
lcd控制器层 ------//drivers/video/s3c-fb.c
作用:
1,初始化lcd控制器
2,分配显存空间---dma
三,lcd驱动移植
1,lcd硬件连接
//参考:E:\peter\2010\高级驱动\5day\5day_photo\3_lcd硬件连接.tif
2,lcd时序分析 ---------Documentation/fb/framebuffer.txt
soc: lcd屏 驱动:
垂直方向:
VSPW tvpw 1 < y < 20 y = 10 vsync_len
VBPD tvb -tvpw 23 -y = 13 upper_margin
LNEVAL tvd 480 yres
VFPD tvfp 22 lower_margin
水平方向:
HSPW thpw 1 < x < 40 x = 20 hsync_len
HBPD thb -thpw 46 -x = 26 left_margin
HOZVAL thd 800 xres
HFPD thfp 210 right_margin
3,lcd参数设置 ------- //硬件差异化数据:pdev
arch/arm/mach-s5pv210/mach-smdkv210.c
|
static void __init smdkv210_machine_init(void)
|
s3c_fb_set_platdata(&smdkv210_lcd0_pdata); //设置fb的自定义数据
|
static struct s3c_fb_platdata smdkv210_lcd0_pdata __initdata = {
.win[0] = &smdkv210_fb_win0, //lcd参数
|
static struct s3c_fb_pd_win smdkv210_fb_win0 = {
.win_mode = {
.left_margin = 13,
.right_margin = 8,
.upper_margin = 7,
.lower_margin = 5,
.hsync_len = 3,
.vsync_len = 1,
.xres = 800,
.yres = 480,
},
.max_bpp = 32, //像素最大的颜色空间
.default_bpp = 24, //缺省颜色空间值
};
.vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB,
.vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC, //电平翻转
.setup_gpio = s5pv210_fb_gpio_setup_24bpp,
};
4,移植lcd驱动-----//修改自定义数据
arch/arm/mach-s5pv210/mach-smdkv210.c
|
static void __init smdkv210_machine_init(void)
|
s3c_fb_set_platdata(&smdkv210_lcd0_pdata); //设置fb的自定义数据
|
static struct s3c_fb_platdata smdkv210_lcd0_pdata __initdata = {
.win[0] = &smdkv210_fb_win0, //lcd参数
|
static struct s3c_fb_pd_win smdkv210_fb_win0 = {
.win_mode = {
.left_margin = 26,
.right_margin = 210,
.upper_margin = 13,
.lower_margin = 22,
.hsync_len = 20,
.vsync_len = 10,
.xres = 800,
.yres = 480,
},
.max_bpp = 32, //像素最大的颜色空间
.default_bpp = 16, //缺省颜色空间值
};
5,确保fb子系统两层相关代码被编译进内核镜像
make menuconfig
Device Drivers --->
Graphics support --->
<*> Support for frame buffer devices --->
<*> Samsung S3C framebuffer support
Console display driver support --->
<*> Framebuffer Console support
[*] Bootup logo --->
[*] Standard 224-color Linux logo (NEW)
6,打开lcd的背光
背光:gpd0_0
arch/arm/mach-s5pv210/mach-smdkv210.c
|
static void __init smdkv210_machine_init(void)
//打开lcd的背光
gpio_request(S5PV210_GPD0(0), “lcd_gpd0_0”);
gpio_direction_output(S5PV210_GPD0(0), 1);
gpio_free(S5PV210_GPD0(0));
7,编译内核,并更新
make -j2 zImage
cp arch/arm/boot/zImage /tftpboot/
8,重启开发板,观察结果:
在lcd屏的左上角可以看到一个小企鹅的图标,说明驱动移植OK
四,linux启动logo的制作
1,准备一张需要做成logo的图片,并将图片转换为ppm格式
1》在Windows中安装格式转换工具gimp
1)安装:
gtk+-2.10.13-setup.exe
2)安装:
gimp-2.2.17-i586-setup.exe
2》打开gimp工具,在gimp中打开要制作的图片,然后修改格式
1)修改图片的大小
--->图像
---->缩放图像
---->图像大小: 800 * 480
2)修改颜色空间
--->图像
---->模式
----->索引
---->最大颜色数量:224
3)图片另存为,同时修改格式
--->文件
---->另存为
----->选择文件类型: ppm ,点击保存
------->数据格式:ascii
2,将图片编译进内核中
1》将制作好的图片拷贝到:drivers/video/logo/ 并修改图片的名称,如下:
logo_fegnj_clut224.ppm
2》修改drivers/video/logo/中的Makefile文件,
添加一行:
7 obj-$(CONFIG_LOGO_FENGJ_CLUT224) += logo_fengj_clut224.o
3》修改drivers/video/logo/中Kconfig文件
添加下面几行:
30 config LOGO_FENGJ_CLUT224
31 bool "test Linux logo for fs210"
32 help
33 test lcd logo
4》执行make menuconfig
make menuconfig
Device Drivers --->
Graphics support --->
[*] Bootup logo --->
[*] test Linux logo for fs210
5》编译内核:
farsight@ubuntu:~/fs210/linux-3.0.8$ make -j2 zImage
scripts/kconfig/conf --silentoldconfig Kconfig
CHK include/linux/version.h
CHK include/generated/utsrelease.h
make[1]: `include/generated/mach-types.h' is up to date.
CALL scripts/checksyscalls.sh
CHK include/generated/compile.h
LOGO drivers/video/logo/logo_fengj_clut224.c //生成图片对应的.c文件
CC drivers/video/logo/logo_fengj_clut224.o //编译.c文件生成.o
LD drivers/video/logo/built-in.o
//logo_fengj_clut224.c中会创建一个图片对应的结构体:
const struct linux_logo logo_fengj_clut224 __initconst = {
.type = LINUX_LOGO_CLUT224,
.width = 800,
.height = 480,
.clutsize = 224,
.clut = logo_fengj_clut224_clut,
.data = logo_fengj_clut224_data
};
3,在内核中指定启动时,加载哪个图片作为logo
1》修改 drivers/video/logo/logo.c
增加下面几行:
70 #ifdef CONFIG_LOGO_FENGJ_CLUT224
71 /* test logo */
72 logo = &logo_fengj_clut224;
73 #endif
2》修改 include/linux/linux_logo.h
增加下面一行:
38 extern const struct linux_logo logo_fengj_clut224;
3》重新编译内核,并更新
make -j2 zImage
cp arch/arm/boot/zImage /tftpboot/
4》重启开发板,观察结果:
lcd屏整个屏幕上会显示出新的图片
五,framebuffer框架代码跟读
fb通用层:drivers/video/fbmem.c
module_init(fbmem_init);
|
static int __init fbmem_init(void)
{
1,在/proc目录中创建驱动相关的文件
proc_create("fb", 0, NULL, &fb_proc_fops);
2,申请设备号,主:29
if (register_chrdev(FB_MAJOR,"fb",&fb_fops))
|
static const struct file_operations fb_fops = {
.mmap = fb_mmap,
|
static int fb_mmap(struct file *file, struct vm_area_struct * vma)
{
unsigned long start;
//1,获取显存的物理地址
start = info->fix.smem_start;
//2,初始化vma
vma->vm_pgoff = off >> PAGE_SHIFT;
/* This is an IO map - tell maydump to skip this VMA */
vma->vm_flags |= VM_IO | VM_RESERVED;
vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
fb_pgprotect(file, vma, off);
//3,将显存空间映射到用户的虚拟空间
if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
vma->vm_end - vma->vm_start, vma->vm_page_prot))
}
}
3,创建类
fb_class = class_create(THIS_MODULE, "graphics");
}
----------------------------------------------------------------------------
lcd控制器层 : drivers/video/s3c-fb.c
module_init(s3c_fb_init);
|
static int __init s3c_fb_init(void)
|
return platform_driver_register(&s3c_fb_driver);
|
static struct platform_driver s3c_fb_driver = {
.probe = s3c_fb_probe,
|
static int __devinit s3c_fb_probe(struct platform_device *pdev)
{
// 1,获取平台数据
pd = pdev->dev.platform_data;
// 2,分配struct s3c_fb对象空间
struct s3c_fb *sfb = kzalloc(sizeof(struct s3c_fb), GFP_KERNEL);
// 3,时钟使能
sfb->bus_clk = clk_get(dev, "lcd");
clk_enable(sfb->bus_clk);
// 4,获取平台地址资源,并映射地址
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
sfb->regs = ioremap(res->start, resource_size(res));
// 5,获取中断资源,申请中断
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
sfb->irq_no = res->start;
ret = request_irq(sfb->irq_no, s3c_fb_irq, 0, "s3c_fb", sfb);
// 6,设置lcd相关的gpio
pd->setup_gpio();
// 7,翻转电平
writel(pd->vidcon1, sfb->regs + VIDCON1);
// 8,初始化lcd控制器
ret = s3c_fb_probe_win(sfb, win, fbdrv->win[win],&sfb->windows[win]);
|
1,初始化等待队列头
init_waitqueue_head(&sfb->vsync_info.wait);
2,实例化struct fb_info对象
struct fb_info *fbinfo = framebuffer_alloc(sizeof(struct s3c_fb_win) +palette_size * sizeof(u32), sfb->dev);
struct fb_info {
struct fb_var_screeninfo var; /* Current var ----可变参数,与lcd屏相关 */
struct fb_fix_screeninfo fix; /* Current fix ----固定参数,与soc相关*/
}
3,分配显存空间,并初始化dma
ret = s3c_fb_alloc_memory(sfb, win);
|
// 1,计算显存大小
size = (real_size > virt_size) ? real_size : virt_size;
size *= (windata->max_bpp > 16) ? 32 : windata->max_bpp;
size /= 8;
fbi->fix.smem_len = size;
size = PAGE_ALIGN(size);
// 2,分配显存空间:返回显存映射到内核中的虚拟地址,同时返回显存的物理地址给map_dma
fbi->screen_base = dma_alloc_writecombine(sfb->dev, size, &map_dma, GFP_KERNEL);
// 3,初始化显存空间
memset(fbi->screen_base, 0x0, size);
// 4, 将显存的物理地址给:fbi->fix.smem_start
fbi->fix.smem_start = map_dma;
4,设置可变参数和固定参数
fb_videomode_to_var(&fbinfo->var, initmode);
|
var->xres = mode->xres;
var->yres = mode->yres;
var->xres_virtual = mode->xres;
var->yres_virtual = mode->yres;
var->xoffset = 0;
var->yoffset = 0;
var->pixclock = mode->pixclock;
var->left_margin = mode->left_margin;
var->right_margin = mode->right_margin;
var->upper_margin = mode->upper_margin;
var->lower_margin = mode->lower_margin;
var->hsync_len = mode->hsync_len;
var->vsync_len = mode->vsync_len;
var->sync = mode->sync;
var->vmode = mode->vmode & FB_VMODE_MASK;
fbinfo->fix.type = FB_TYPE_PACKED_PIXELS;
fbinfo->fix.accel = FB_ACCEL_NONE;
fbinfo->var.activate = FB_ACTIVATE_NOW;
fbinfo->var.vmode = FB_VMODE_NONINTERLACED;
fbinfo->var.bits_per_pixel = windata->default_bpp;
fbinfo->fbops = &s3c_fb_ops;
fbinfo->flags = FBINFO_FLAG_DEFAULT;
fbinfo->pseudo_palette = &win->pseudo_palette;
5,初始化lcd控制器
s3c_fb_set_par(fbinfo);
|
data |= VIDCON0_ENVID | VIDCON0_ENVID_F;
writel(data, regs + VIDCON0);
data = VIDTCON0_VBPD(var->upper_margin - 1) |
VIDTCON0_VFPD(var->lower_margin - 1) |
VIDTCON0_VSPW(var->vsync_len - 1);
writel(data, regs + sfb->variant.vidtcon);
data = VIDTCON1_HBPD(var->left_margin - 1) |
VIDTCON1_HFPD(var->right_margin - 1) |
VIDTCON1_HSPW(var->hsync_len - 1);
/* VIDTCON1 */
writel(data, regs + sfb->variant.vidtcon + 4);
data = VIDTCON2_LINEVAL(var->yres - 1) |
VIDTCON2_HOZVAL(var->xres - 1);
writel(data, regs + sfb->variant.vidtcon + 8);
6,注册struct fb_info对象
ret = register_framebuffer(fbinfo);
|
ret = do_register_framebuffer(fb_info);
{
// 1,在数组registered_fb找到一个空的位置
for (i = 0 ; i < FB_MAX; i++)
if (!registered_fb[i])
break;
// 2,创建设备节点
fb_info->dev = device_create(fb_class, fb_info->device, MKDEV(FB_MAJOR, i), NULL, "fb%d", i);
// 3,将fbinfo对象保存到数组:registered_fb
registered_fb[i] = fb_info;
}
}
};
六,应用程序如何向lcd上显示数据
/*1.打开一副图片*/
pic_fd =open(argv[2],O_RDWR);
printf("pic_fd=%d\n",pic_fd);
/*2.获取图片大小*/
len =lseek(pic_fd, 0, SEEK_END);
printf("len =%ld\n",len);
/*分配空间保存图片的数据*/
buffer =(char *)malloc(len);
lseek(pic_fd, 0, SEEK_SET);
/*3.读取图片数据*/
read(pic_fd,buffer,len);
/*4.打开帧缓冲设备*/
framebuffer_fd =open(dev_name,O_RDWR);
/*5.获取固定参数*/
if(ioctl(framebuffer_fd,FBIOGET_FSCREENINFO,&finfo))
{
printf("Error:failed get the framebuffer device`s fix informations!\n");
return -1;
}
/*6.获取可变参数*/
if(ioctl(framebuffer_fd,FBIOGET_VSCREENINFO,&vinfo))
{
printf("Error:failed get the framebuffer device`s var informations!\n");
return -1;
}
printf("xres =%ld\n",vinfo.xres);
printf("yres =%ld\n",vinfo.yres);
/*7,计算一帧数据的长度(byte)*/
screensize =(vinfo.xres *vinfo.yres*vinfo.bits_per_pixel/8);
printf("screensize =%ld\n",screensize);
/*8.映射*/
framebuffer_ptr =(char *)mmap( NULL,//如果此值为NULL,则表示用内核来自动给你分配一块虚拟空间
screensize, //空间大小
PROT_READ|PROT_WRITE,//权限
MAP_SHARED, //是否可以共享
framebuffer_fd, //文件描述符
0); //从哪个地方开始
/*9.清空映射的空间*/
memset(framebuffer_ptr,0,screensize);
/*10.向lcd屏上显示图片或者文字*/
while(1)
{
printf("选择要显示图片还是文字:1-图片,2-文字");
scanf("%d",&key);
switch(key)
{
case 1:
/*2.7 显示图片*/
printf("test picture!\n");
printf("loadjpeg00000!!!!\n");
draw_bmp(buffer,(unsigned short *) framebuffer_ptr);
break;
case 2:
/*2.8 显示字符*/
length1 =sizeof(string1);
length2 =sizeof(string2);
printf("length1 =%d\n",length1);
Display_character(140,100,length1,string1);
Display_character(140,120,length2,string2);
break;
default:
break;
}
}