基于framebuffer(fb)的驱动分析

来源:互联网 发布:linux怎么重启网卡 编辑:程序博客网 时间:2024/05/16 05:23

基于framebuffer的驱动分析

framebuffer帧缓冲(简称fb)是linux内核中用代码虚拟出的一个设备,是一个platform类型设备,设备文件位于/dev/fb*

  • 在嵌入式系统中一般没有专门的显存,而仅仅是从RAM(SDRAM)空间中分配一段显示缓冲区
  • framebuffer的作用是:向应用层提供一个统一标准接口的显示设备。不论最终输出是通过hdmi还是lcd控制器,可以认为所有的GUI都是向fb输出画面的
  • 实际上是frambuffer就是linux内核驱动申请的一片内存空间,然后lcd内有一片sram,cpu内部有个lcd控制器,它有个单独的dma用来将frambuffer中的数据拷贝到lcd的sram中去,拷贝到lcd的sram中的数据就会显示在lcd上,具体数据的内容是由应用程序控制的。
  • LCD驱动和framebuffer驱动没有必然的联系,它只是驱动LCD正常工作的,比如有信号传过来,那么LCD驱动负责把信号转成显示屏上的内容,至于什么内容,怎么显示,它根本不关心也不知道。
  • 对于现代LCD,有一种“多屏叠加”的机制,即一个LCD设备可以有多个独立虚拟屏幕,以达到画面叠加的效果。所以fb与LCD不是一对一的关系,在常见的情况下,一个LCD对应了fb0~fb4。像QT这种GUI会默认把画面输出到fb0

1.画面输出原理

  • 如何输出画面?应用程序通过往显存中写数据,LCD控制器将自动把显存中的数据映射到lcd屏幕。映射是全自动的,应用层负责往显存里写数据,而驱动要做的仅仅是配置LCD控制器、创建显存罢了
  • 由于显存实际是处于内核态的物理内存,所以要把这块物理内存映射到用户态,所谓“映射”就可以理解为建立了一个“符号链接”,这样应用程序就可以直接操作这块物理内存了
  • 关于LCD的硬件原理详见LCD简介
  • 关于如何在应用层测试fb,详见framebuffer的使用与测试

2.framebuffer驱动结构



fb的结构和misc极为类似,由内核中的fb框架实现一部分,然后再由设备驱动本身实现一部分。设备驱动本身就是一个普通的platform总线驱动 
虽然每家原厂写的fb设备驱动可能有些差异,但是基本的套路还是相同的 
这里写图片描述 
这里写图片描述

  • 在内核fb框架中,所有的fb设备公用一个主设备号(都是29),它们之间以次设备号互相区分。所以在框架中使用register_chrdev注册了一个主设备号为29的设备,而在驱动中device_create创建设备文件主设备号都为29,次设备号不同
  • 由上图可以看出,内核提供了fb框架,原厂提供了fb的platform设备和驱动;不论有多少LCD屏幕,用的都是这一套platform驱动,它们的操作方式都是固定的,唯一的区别就在platform_data里的硬件参数。而我们驱动工程师重点关注的就是该硬件参数

3.修改LCD的硬件参数(2.6版本内核)

当我们板子上的LCD需要更换时,驱动中也需要进行相应的修改

  • 具体的思路,是去修改platform_device中的platform_data,LCD的硬件参数都在里面,但是怎么找到这个platform_data是一门学问,因为原厂写的代码是比较复杂的。。。。具体方法详见基于platform总线的驱动分析 的文末
  • 不难找到设置platform_data的地方,如图 
    这里写图片描述 
    那么目前导入的platform_data是哪一个呢?根据menuconfig和xxxxdefconfig,分析可知是ek070tn93_fb_data
static struct s3c_platform_fb ek070tn93_fb_data __initdata = {    .hw_ver = 0x62,    .nr_wins = 5,    .default_win = CONFIG_FB_S3C_DEFAULT_WINDOW,    .swap = FB_SWAP_WORD | FB_SWAP_HWORD,    .lcd = &ek070tn93,    .cfg_gpio   = ek070tn93_cfg_gpio,    .backlight_on   = ek070tn93_backlight_on,    .backlight_onoff    = ek070tn93_backlight_off,    .reset_lcd  = ek070tn93_reset_lcd,};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 里面最关键的是.lcd 这个成员,即结构体ek070tn93,查看发现里面有时序、分辨率等各种参数,这样我们就可以随便修改参数了
static struct s3cfb_lcd ek070tn93 = {    .width = S5PV210_LCD_WIDTH,    .height = S5PV210_LCD_HEIGHT,    .bpp = 32,    .freq = 60,    .timing = {        .h_fp   = 210,        .h_bp   = 38,        .h_sw   = 10,        .v_fp   = 22,        .v_fpe  = 1,        .v_bp   = 18,        .v_bpe  = 1,        .v_sw   = 7,    },    .polarity = {        .rise_vclk = 0,        .inv_hsync = 1,        .inv_vsync = 1,        .inv_vden = 0,    },};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

4.修改LCD的硬件参数(3.0+版本内核)

对于新的内核,platformdata都包含在了dts中,所以需要在dts中修改LCD的硬件参数。有关设备树详见设备树详解

  • 首先进入我们项目的dts以及包含的dtsi,寻找合适的地方安放我们新增的时序,一般某个dtsi里有一个叫display-timings的节点,里面会放时序。其实说实话时序放在哪里根本就无所谓,因为lcd/ldb节点是通过标号来访问具体的时序节点的,我们之所以选择放在display-timings里,仅仅是为了规范一点
    display-timings {        lq4851lg03:lvds_1280x480_53M{            clock-frequency = <53172000>;            hactive = <1280>;            vactive = <480>;            hback-porch = <268>;            hfront-porch = <70>;            vback-porch = <10>;            vfront-porch = <10>;            hsync-len = <70>;            vsync-len = <25>;            pixelclk-active = <0>;        };        ak070tn93:ttl_1280x480_45M{            clock-frequency = <45000000>;            hactive = <1280>;            vactive = <480>;            hback-porch = <40>;            hfront-porch = <73>;            vback-porch = <20>;            vfront-porch = <23>;            hsync-len = <20>;            vsync-len = <10>;        };        xxxxx:xxxxx{        /*需要添加的参数*/        };    };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 那么我们有两种方案,第一种是直接在默认的时序上修改,第二种添加一个时序,并在项目的dts中使用该时序。在此,我们选择第二种更优越的方法
  • 首先在已有的时序后面添加一个时序,具体参数照抄datasheet即可,唯一要注意的是某几个参数的名字可能和datasheet上不同:hsync-len对应datasheet上的Horizontal pulse width;vsync-len 对应datasheet上的Vertical pulse width。那么上面代码中的pixelclk-active = <0>;意味着什么呢?这个元素代表时钟的极性,如果显示图片清晰度不足时,可以尝试加入该元素
  • 打开我们项目的dts,然后可以做如下的修改
&mxcfb1 {    disp_dev = "lcd";};&ldb {    status = "disabled";};&lcd {    status = "okay";    native-mode = <&xxxx>;};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 可以看到有三项,mxcfb1、ldb、lcd。mxcfb1里面的disp_dev决定了图像通过什么方式输出,显然这里有两种方式,ldb和lcd,即lvds输出或ttl输出RGB信号。这里我们选择了lcd(即ttl输出RGB信号),那么ldb显然是要被”disabled”了,而lcd显然是”okay”,并且lcd的native-mode选择了我们刚刚添加的时序
  • 有时,除了时序之外,显示的模式可能也需要设置,比如某个屏幕需要jeida的data-mapping模式,而我们项目中的dtsi中并未设置过:
ldb: ldb@020e0008 {    #address-cells = <1>;    #size-cells = <0>;    gpr = <&gpr>;    status = "disabled";    lvds-channel@0 {        reg = <0>;        status = "disabled";    };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 那么在我们项目的dts中就要对其进行引用,并设置(类似于重写)
&ldb {    status = "okay";    lvds-channel@0 {        native-mode = <&SHARP_LQ123B5LW>;        fsl,data-mapping = "jeida";        status = "okay";    };};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

5.修改logo显示(2.6版本内核)

注意:本段关于logo显示的ne 
当kernel启动,在probe函数运行时,一般会往fb中输出一个小企鹅logo(开发板厂商可能会改成其他的)。很多时候产品是不需要这个企鹅logo的,我们要学会去修改它

  • 在probe函数中,我们可以发现调用了显示logo的相关代码,下面是三星写的,其他厂家应该也不会有很大的区别:
#if !defined(CONFIG_FRAMEBUFFER_CONSOLE) && defined(CONFIG_LOGO)    if (fb_prepare_logo( fbdev->fb[pdata->default_win], FB_ROTATE_UR)) {        printk("Start display and show logo\n");        /* Start display and show logo on boot */        fb_set_cmap(&fbdev->fb[pdata->default_win]->cmap, fbdev->fb[pdata->default_win]);        fb_show_logo(fbdev->fb[pdata->default_win], FB_ROTATE_UR);    }#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 不难发现如果要让logo消失,只需要让CONFIG_LOGO不被定义即可,即在menuconfig中配置,或者直接修改xxxdefconfig
  • 那么如果我们想要添加另外的logo呢?linux的启动logo全部放在drivers/video/logo/下,而且都是以一种专门的格式存在的,称之为ppm格式。我们可以使用专门的工具把png格式图片转成ppm格式,ubuntu里也有这个工具,网上教程很多
  • 假设添加完了,我们要选择该logo,怎么选择?很简单,这些logo也是由kconfig管理的,我们只要在drivers/video/logo/下的kconfig和makefile中照葫芦画瓢,添加我们的logo,然后就能在menuconfig中选择了!
  • 有时,我们做了一个很小的logo,比屏幕像素小得多,它会被系统放到屏幕左上角。那么如何把它放到屏幕正中央呢?有两种思路,第一种方法是把logo的整个画面做的和屏幕分辨率一样大小,这样自然就对齐到中间了;第二种方法是修改显示logo函数的参数。首先找到probe函数中的fb_show_logo,在进去一层,找到fb_show_logo_line,查看其定义,发现里面有两行:
image.dx = 0;image.dy = y;
  • 1
  • 2
  • 3

这便是logo图像的坐标偏移量,只需改成恰当的值即可让logo显示到中央了


/*********************************************************************************************/

b_fix_screeninfo 和 fb_var_screeninfo



fb_fix_screeninfo 和 fb_var_screeninfo 都和 frame buffer 有关。

结构体的成员变量

[cpp] view plain copy
 print?
  1. struct fb_fix_screeninfo {  
  2.     char id[16];            /* identification string eg "TT Builtin" */  
  3.     unsigned long smem_start;   /* Start of frame buffer mem */  
  4.                     /* (physical address) */  
  5.     __u32 smem_len;         /* Length of frame buffer mem */  
  6.     __u32 type;         /* see FB_TYPE_*        */  
  7.     __u32 type_aux;         /* Interleave for interleaved Planes */  
  8.     __u32 visual;           /* see FB_VISUAL_*      */   
  9.     __u16 xpanstep;         /* zero if no hardware panning  */  
  10.     __u16 ypanstep;         /* zero if no hardware panning  */  
  11.     __u16 ywrapstep;        /* zero if no hardware ywrap    */  
  12.     __u32 line_length;      /* length of a line in bytes    */  
  13.     unsigned long mmio_start;   /* Start of Memory Mapped I/O   */  
  14.                     /* (physical address) */  
  15.     __u32 mmio_len;         /* Length of Memory Mapped I/O  */  
  16.     __u32 accel;            /* Indicate to driver which */  
  17.                     /*  specific chip/card we have  */  
  18.     __u16 reserved[3];      /* Reserved for future compatibility */  
  19. };  

结构体的成员变量

[cpp] view plain copy
 print?
  1. struct fb_var_screeninfo {  
  2.     __u32 xres;         /* visible resolution       */  
  3.     __u32 yres;  
  4.     __u32 xres_virtual;     /* virtual resolution       */  
  5.     __u32 yres_virtual;  
  6.     __u32 xoffset;          /* offset from virtual to visible */  
  7.     __u32 yoffset;          /* resolution           */  
  8.   
  9.     __u32 bits_per_pixel;       /* guess what           */  
  10.     __u32 grayscale;        /* != 0 Graylevels instead of colors */  
  11.   
  12.     struct fb_bitfield red;     /* bitfield in fb mem if true color, */  
  13.     struct fb_bitfield green;   /* else only length is significant */  
  14.     struct fb_bitfield blue;  
  15.     struct fb_bitfield transp;  /* transparency         */    
  16.   
  17.     __u32 nonstd;           /* != 0 Non standard pixel format */  
  18.   
  19.     __u32 activate;         /* see FB_ACTIVATE_*        */  
  20.   
  21.     __u32 height;           /* height of picture in mm    */  
  22.     __u32 width;            /* width of picture in mm     */  
  23.   
  24.     __u32 accel_flags;      /* (OBSOLETE) see fb_info.flags */  
  25.   
  26.     /* Timing: All values in pixclocks, except pixclock (of course) */  
  27.     __u32 pixclock;         /* pixel clock in ps (pico seconds) */  
  28.     __u32 left_margin;      /* time from sync to picture    */  
  29.     __u32 right_margin;     /* time from picture to sync    */  
  30.     __u32 upper_margin;     /* time from sync to picture    */  
  31.     __u32 lower_margin;  
  32.     __u32 hsync_len;        /* length of horizontal sync    */  
  33.     __u32 vsync_len;        /* length of vertical sync  */  
  34.     __u32 sync;         /* see FB_SYNC_*        */  
  35.     __u32 vmode;            /* see FB_VMODE_*       */  
  36.     __u32 rotate;           /* angle we rotate counter clockwise */  
  37.     __u32 reserved[5];      /* Reserved for future compatibility */  
  38. };  

fb_fix_screeninfo 的 line_length 成员,含义是一行的 size,以字节数表示,就是屏幕的宽度。

结 构fb_var_screeninfo定义了视频硬件一些可变的特性。这些特性在程序运行期间可以由应用程序动态改变


6.在LCD上划线

***************************************************************************************************************************************
[cpp] view plain copy
 print?
  1. #include <stdlib.h>  
  2. #include <unistd.h>  
  3. #include <stdio.h>  
  4. #include <fcntl.h>  
  5. #include <linux/fb.h>  
  6. #include <linux/kd.h>  
  7. #include <sys/mman.h>  
  8. #include <sys/ioctl.h>  
  9. #include <sys/time.h>  
  10. #include <string.h>  
  11. #include <errno.h>  
  12. struct fb_var_screeninfo vinfo;  
  13. struct fb_fix_screeninfo finfo;  
  14. char *frameBuffer = 0;  
  15.    
  16. //打印fb驱动中fix结构信息,注:在fb驱动加载后,fix结构不可被修改。  
  17. void printFixedInfo ()  
  18. {  
  19.    printf ("Fixed screen info:\n"  
  20.                         "\tid: %s\n"  
  21.                         "\tsmem_start:0x%lx\n"  
  22.                         "\tsmem_len:%d\n"  
  23.                         "\ttype:%d\n"  
  24.                         "\ttype_aux:%d\n"  
  25.                         "\tvisual:%d\n"  
  26.                         "\txpanstep:%d\n"  
  27.                         "\typanstep:%d\n"  
  28.                         "\tywrapstep:%d\n"  
  29.                         "\tline_length: %d\n"  
  30.                         "\tmmio_start:0x%lx\n"  
  31.                         "\tmmio_len:%d\n"  
  32.                         "\taccel:%d\n"  
  33.            "\n",  
  34.            finfo.id, finfo.smem_start, finfo.smem_len, finfo.type,  
  35.            finfo.type_aux, finfo.visual, finfo.xpanstep, finfo.ypanstep,  
  36.            finfo.ywrapstep, finfo.line_length, finfo.mmio_start,  
  37.            finfo.mmio_len, finfo.accel);  
  38. }  
  39.    
  40. //打印fb驱动中var结构信息,注:fb驱动加载后,var结构可根据实际需要被重置  
  41. void printVariableInfo ()  
  42. {  
  43.    printf ("Variable screen info:\n"  
  44.                         "\txres:%d\n"  
  45.                         "\tyres:%d\n"  
  46.                         "\txres_virtual:%d\n"  
  47.                         "\tyres_virtual:%d\n"  
  48.                         "\tyoffset:%d\n"  
  49.                         "\txoffset:%d\n"  
  50.                         "\tbits_per_pixel:%d\n"  
  51.                         "\tgrayscale:%d\n"  
  52.                         "\tred: offset:%2d, length: %2d, msb_right: %2d\n"  
  53.                         "\tgreen: offset:%2d, length: %2d, msb_right: %2d\n"  
  54.                         "\tblue: offset:%2d, length: %2d, msb_right: %2d\n"  
  55.                         "\ttransp: offset:%2d, length: %2d, msb_right: %2d\n"  
  56.                         "\tnonstd:%d\n"  
  57.                         "\tactivate:%d\n"  
  58.                         "\theight:%d\n"  
  59.                         "\twidth:%d\n"  
  60.                         "\taccel_flags:0x%x\n"  
  61.                         "\tpixclock:%d\n"  
  62.                         "\tleft_margin:%d\n"  
  63.                         "\tright_margin: %d\n"  
  64.                         "\tupper_margin:%d\n"  
  65.                         "\tlower_margin:%d\n"  
  66.                         "\thsync_len:%d\n"  
  67.                         "\tvsync_len:%d\n"  
  68.                         "\tsync:%d\n"  
  69.                        "\tvmode:%d\n"  
  70.            "\n",  
  71.            vinfo.xres, vinfo.yres, vinfo.xres_virtual, vinfo.yres_virtual,  
  72.            vinfo.xoffset, vinfo.yoffset, vinfo.bits_per_pixel,  
  73.            vinfo.grayscale, vinfo.red.offset, vinfo.red.length,  
  74.             vinfo.red.msb_right,vinfo.green.offset, vinfo.green.length,  
  75.            vinfo.green.msb_right, vinfo.blue.offset, vinfo.blue.length,  
  76.            vinfo.blue.msb_right, vinfo.transp.offset, vinfo.transp.length,  
  77.            vinfo.transp.msb_right, vinfo.nonstd, vinfo.activate,  
  78.            vinfo.height, vinfo.width, vinfo.accel_flags, vinfo.pixclock,  
  79.            vinfo.left_margin, vinfo.right_margin, vinfo.upper_margin,  
  80.            vinfo.lower_margin, vinfo.hsync_len, vinfo.vsync_len,  
  81.            vinfo.sync, vinfo.vmode);  
  82. }  
下面才是我们的重点,这个代码是我自己参考别人画矩形的代码改过来的
[cpp] view plain copy
 print?
  1. //画一条直线  
  2. void drawline_rgb16 (int x0,int y0, int width,int height, int color,int flag0)  
  3. {  
  4.    const int bytesPerPixel = 2;//因为是rgb16,用16位来描述色深,所以2个字节  
  5.    const int stride = finfo.line_length / bytesPerPixel;,一行有多少个点  
  6.    const int red = (color & 0xff0000) >> (16 + 3);//下面是颜色的操作,我目前还没弄明白  
  7.    const int green = (color & 0xff00) >> (8 + 2);  
  8.    const int blue = (color & 0xff) >> 3;  
  9.    const short color16 = blue | (green << 5) | (red << (5 +6));  
  10.    int flag=flag0;//这里我为了图个方便就用一个flag来区分是画横线还是竖线,0表示横线,1表示竖线。  
  11.   
  12.    
  13.    short *dest = (short *) (frameBuffer)+ (y0 + vinfo.yoffset) * stride + (x0 +vinfo.xoffset);//这个就是我们画点的起始位置,其+stride就是换行(这个是我个人通过代码测试得出来的结论)  
  14.     
  15.    int x=0,y=0;  
  16.    if(flag==0)  
  17.    {       
  18.    for (x = 0; x < width; ++x)//width就是我们x方向的终点  
  19.        {  
  20.            dest[x] = color16;  
  21.        }  
  22.    }  
  23.    else if(flag==1)  
  24.    {  
  25.        for(y=0;y<height;y++)//height就是我们y方向的终点  
  26.        {  
  27.            dest[x]=color16;//这里x始终为0,和下面一句结合起来就是每一行就画一个点,一共画height行,不就是一条竖线了么,这里我还思考了很久。  
  28.            
  29.            dest +=stride;  
  30.        }  
  31.    }  
  32. }  
解释:我的屏的lcd分辨率是480*272,分辨率的意思是一行有480个点,一共有272行,其实屏蔽上都是一个个点组成的,在上面画线的意思并不是真正意思上的拿一支笔画线。打个比方来说你你把一行中80-180个点都改成红色(我们屏蔽不是黑色么),改完你就可以看见一条红线了,感觉就是画了一条红色的直线对不对?

而且“上色”是从左到右一个点一个点,一行一行“上色”的,屏幕的坐标系如下图所示:

[cpp] view plain copy
 print?
  1. short *dest = (short *) (frameBuffer)+ (y0 + vinfo.yoffset) * stride + (x0 +vinfo.xoffset);  
上面这一行代码的具体意思就是定位到(x0,y0)这个坐标,也就是我们要画的其实位置

可以下面这个代码画一个矩形。
[cpp] view plain copy
 print?
  1. //画大小为width*height的同色矩阵,5reds+6greens+5blues  
  2. void drawRect_rgb16 (int x0, int y0, int width,int height, int color)  
  3. {  
  4.    const int bytesPerPixel = 2;  
  5.    const int stride = finfo.line_length / bytesPerPixel;  
  6.    const int red = (color & 0xff0000) >> (16 + 3);  
  7.    const int green = (color & 0xff00) >> (8 + 2);  
  8.    const int blue = (color & 0xff) >> 3;  
  9.    const short color16 = blue | (green << 5) | (red << (5 +6));  
  10.    
  11.    short *dest = (short *) (frameBuffer)+ (y0 + vinfo.yoffset) * stride + (x0 +vinfo.xoffset);  
  12.    
  13.    int x, y;  
  14.    for (y = 0; y < height; ++y)  
  15.     {  
  16.        for (x = 0; x < width; ++x)  
  17.        {  
  18.            dest[x] = color16;  
  19.        }  
  20.        dest += stride;  
  21.     }  
  22. }  
下面是main函数:

[cpp] view plain copy
 print?
  1. int main (int argc, char **argv)  
  2. {  
  3.    const char *devfile = "/dev/fb0";  
  4.    long int screensize = 0;  
  5.    int fbFd = 0;  
  6.   
  7.   
  8.    
  9.    /* Open the file for reading and writing */  
  10.    fbFd = open (devfile, O_RDWR);  
  11.    if (fbFd == -1)  
  12.     {  
  13.        perror ("Error: cannot open framebuffer device");  
  14.        exit (1);  
  15.     }  
  16.    
  17.    //获取finfo信息并显示  
  18.    if (ioctl (fbFd, FBIOGET_FSCREENINFO, &finfo) == -1)  
  19.     {  
  20.        perror ("Error reading fixed information");  
  21.        exit (2);  
  22.     }  
  23.    printFixedInfo ();  
  24.    //获取vinfo信息并显示  
  25.    if (ioctl (fbFd, FBIOGET_VSCREENINFO, &vinfo) == -1)  
  26.     {  
  27.        perror ("Error reading variable information");  
  28.        exit (3);  
  29.     }  
  30.    printVariableInfo ();  
  31.    
  32.    /* Figure out the size of the screen in bytes */  
  33.    screensize = finfo.smem_len;//fb的缓存长度  
  34.    
  35.    /* Map the device to memory */  
  36.     frameBuffer =(char *) mmap (0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED,fbFd, 0);  
  37.     if (frameBuffer == MAP_FAILED)  
  38.        {  
  39.            perror ("Error: Failed to map framebuffer device to memory");  
  40.            exit (4);  
  41.        }  
  42.     
  43.        //drawRect_rgb16 (vinfo.xres *3 / 8, vinfo.yres * 3 / 8,vinfo.xres / 4, vinfo.yres / 4,0xff00ff00);//实现画矩形  
  44.   
  45.        drawline_rgb16(50,80,260,0,0xffff0000,0);  
  46.          
  47.        drawline_rgb16(160,10,0,180,0xff00ff00,1);//可以画出一个交叉的十字,坐标都是自己设的。  
  48.        sleep (2);  
  49.        printf (" Done.\n");  
  50.    
  51.        munmap (frameBuffer, screensize);   //解除内存映射,与mmap对应  
  52.    
  53.        close (fbFd);  
  54.        return 0;  
  55. }  






用一个流程图还说明一下mian函数吧
*****************************************************************************************************************************************************************************************
****************************************************************************************************************************************************************************************
这里最重要的就是mmap这个函数了

void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset);

addr指定文件应被映射到进程空间的起始地址,一般被指定一个空指针,此时选择起始地址的任务留给内核来完成。函数的返回值为最后文件映射到进程空间的地址,进程可直接操作起始地址为该值的有效地址。
    len是映射到调用进程地址空间的字节数,它从被映射文件开头offset个字节开始算起。
    prot参数指定共享内存的访问权限。可取如下几个值的或:PROT_READ(可读),PROT_WRITE(可写),PROT_EXEC(可执行),PROT_NONE(不可访问)。
   flags由以下几个常值指定:MAP_SHARED, MAP_PRIVATE, MAP_FIXED。其中,MAP_SHARED,MAP_PRIVATE必选其一,而MAP_FIXED则不推荐使用。
 如果指定为MAP_SHARED,则对映射的内存所做的修改同样影响到文件。如果是MAP_PRIVATE,则对映射的内存所做的修改仅对该进程可见,对文件没有影响。
    offset参数一般设为0,表示从文件头开始映射。

mmap使得进程之间通过映射同一个普通文件实现共享内存。普通文件被映射到进程地址空间后,进程可以像访问普通内存一样对文件进行访问,不必再调用read(),write()等操作。


 再来看看我们代码中的mmap的实例
frameBuffer =(char *) mmap (0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED,fbFd, 0);

前面我们不是说了frambuffer就是linux内核驱动申请的一片内存空间,lcd驱动将frambuffer的地址通过mmap()将这片内存映射到应用程序空间,这样我们写入到fb的数据就写入到内核驱动里的frambuffer中去了,而lcd 的dma就将这些数据写入到lcd的sram中,从而显示在lcd上.


原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 孕中期胎儿偏小怎么办 怀孕34周偏小2周怎么办 胎盘低怎么办6个月了 怀孕四个月胎盘低怎么办 怀孕五个月胎盘低怎么办 怀孕3个月胎盘低怎么办 怀孕三个月了胎盘低怎么办 新房交房开发商拿不出证件怎么办? 新干式变压器未送电进水怎么办 三相380转单相220怎么办 外国人一直找你出去怎么办 美团众包跑腿单物品太重怎么办 美团退款成功后 物品怎么办 win10电脑没有网络图标怎么办 电脑开机桌面什么都没有怎么办 xp桌面什么都没有了怎么办 手机下滑通知栏不见了怎么办 跑800米赶上月经怎么办 强迫症吃药没效怎么办 复读生档案丢了怎么办 大学退学重新高考学籍怎么办 玻尿酸鼻子宽了怎么办 被昆山市圆通快递公司骗怎么办 借壳上市后壳公司怎么办 盐城国有民办学校倒闭教师怎么办 联想复印机显示更换墨粉盒怎么办 襄垣县共公自行车卡怎么办 转南京江宁户口怎么办儿童医保 剑与家园鬼服怎么办 花生苗出现虫洞怎么办 被蝎子草扎到手怎么办 被蝎子草蛰了怎么办 螫麻子草扎了怎么办 苹果app没删除干净怎么办 字写的太潦草看不懂怎么办 肝内胆管结石疼怎么办 肝内胆管有结石怎么办 肝里胆管有结石怎么办 被信任的人算计你怎么办 卡罗拉1.8油耗高怎么办 请问09年途锐柴油版怎么办