Linux驱动开发: FrameBuffe(LCD)驱动开发

一、FrameBuffer 帧缓冲设备的原理

1.1 概念

在linux系统中LCD这类设备称为帧缓冲设备,英文frameBuffer设备。


frameBuffer 是出现在2.2.xx 内核当中的一种驱动程序接口。


帧缓冲(framebuffer)是Linux 系统为显示设备提供的一个接口,它将显示缓冲区抽象,屏蔽图像硬件的底层差异,允许上层应用程序在图形模式下直接对显示缓冲区进行读写操作。用户不必关心物理显示缓冲区的具体位置及存放方式,这些都由帧缓冲设备驱动本身来完成。


framebuffer机制模仿显卡的功能,将显卡硬件结构抽象为一系列的数据结构,可以通过framebuffer的读写直接对显存进行操作。用户可以将framebuffer看成是显存的一个映像,将其映射到进程空间后,就可以直接进行读写操作,写操作会直接反映在屏幕上。


1.2 帧缓冲的理解

用户可以将Framebuffer看成是显示内存的一个映像,将其映射到进程地址空间之后,就可以直接进行读写操作,而写操作可以立即反应在屏幕上。


这种操作是抽象的、统一的。用户不必关心物理显存的位置、换页机制等等具体细节。


这些都是由Framebuffer设备驱动来完成的。


1.3 了解帧缓冲设备文件

framebuffer的设备文件一般是 /dev/fb0、/dev/fb1等,最多支持32个设备。


framebuffer是个字符设备,主设备号为29,对应于/dev/fb%d设备文件。


通常,使用如下方式(前面的数字表示次设备号):


(次设备号)0 = /dev/fb0 第一个fb设备


(次设备号)1 = /dev/fb1 第二个fb 设备。


fb 也是一种普通的内存设备,可以读写其内容。


例如,屏幕抓屏:cp /dev/fb0 myfilefb;


注意,这个命令保存下来的仅是rgb数据,并不是图片格式,Window下是打不开的。


(前提是framebuffer驱动里实现了read函数)


1.4 如何去操作这个设备文件

对程序员和Linux系统来说,framebuffer设备与其他的文件没有区别;可以通过配置对framebuffer设备文件完成对硬件的参数设置。


framebuffer映射操作:

Linux驱动开发: FrameBuffe(LCD)驱动开发

二、LCD知识介绍

2.1 了解LCD显示的色深

  色深(BPP):一个像素使用多少个二进制位表示它的颜色。


  1bpp :一个像素使用 1 个二进制位表示它的颜色,可以有两种色,这种屏单色屏。


  2bpp :一个像素使用 2 个二进制位表示它的颜色,可以有4种色,恢阶。


  4BPP :


  8BPP : 一个像素使用 8 个二进制位表示它的颜色,可以有256种色。


  16BPP: 一个像素使用 16 个二进制位表示它的颜色,可以有65536种色,伪彩色。


  stm32 这种等级 CPU一般使用这个级别的。


         RGB565--常用  RGB5551


  24BPP: 一个像素使用 24 个二进制位表示它的颜色,可以有16M种色,真彩色。


         RGB888


  32BPP: 一个像素使用 32 个二进制位表示它的颜色。 增加了透明度控制。


         ARGB888


  所有颜色都由RGB三原色组成。通过调节三者比例程现不同颜色。


2.2  LCD屏的时序

     要驱动一个TFT屏,首先来认识一下LCD工作时序图。在(芯片数据手册)Exynos 4412 SCP_Users Manual_Ver.0.10.00_Preliminary0.pdf的1816页,第41.3.11.2章节LCD RGB Interface Timing。


LCD的工作时序图如下:

Linux驱动开发: FrameBuffe(LCD)驱动开发

  可以把LCD看成一个二维数据。从左到右,从上到下,一个点一点描绘(逐行扫描)。当最后一个点描绘完成,循环扫描。所有显示器显示图像的原理都是从上到下,从左到右的。

那么这幅图在LCD上的显示原理就是:

Linux驱动开发: FrameBuffe(LCD)驱动开发

LCD提供的外部接口信号说明

(Synchronization:同步,vertical:垂直,horizontal:水平)

Linux驱动开发: FrameBuffe(LCD)驱动开发

上面时序图上各时钟延时参数的含义如下

(这些参数的值,LCD产生厂商会提供相应的数据手册)。

Linux驱动开发: FrameBuffe(LCD)驱动开发

它们的英文全称:


VBPD(vertical back porch) :垂直后肩


VFBD(vertical front porch) :垂直前肩


VSPW(vertical sync pulse width):垂直同步脉冲宽度


HBPD(horizontal back porch):水平前肩


HFPD(horizontal front porth):水平后肩


HSPW(horizontal sync pulse width):水平同步脉冲宽度


LINEVAL: 屏实际高度-1。


HOZVAL: 屏实际宽度-1。


像素时钟信号:一个像素时钟(时间)扫描一个点。


     帧 :由行组成


     行 :由像素组成


扫描过程:


     发起帧同步信号-->行同步信号-->像素时钟信号 (三种信号是同步出现)。


2.3  LCD时序参数的值

     以上参数都是由LCD屏厂家提供的。参考光盘资料:

Linux驱动开发: FrameBuffe(LCD)驱动开发

在以上文档的14页,我们一般用的是经典值。

Linux驱动开发: FrameBuffe(LCD)驱动开发

Linux驱动开发: FrameBuffe(LCD)驱动开发

 除了以上的时序参数外,还要注意LCD控制器的默认时序极性和LCD屏的时序极性是否相同,不同则配置为相同。(VSYNC,HSYNC,DEN,像素采样边沿)。


三、应用层FrameBuffer 帧缓冲设备编程(LCD屏编程)

   在Linux 系统中LCD的应用程序编程是有特定编写模板的。


   下面我们就一步一步的来编写linux下的lcd应用程序。


3.1 编程步骤

(1)  打开/dev/fbX

Linux驱动开发: FrameBuffe(LCD)驱动开发

  Open的第一个参数:/dev/fbx,打开lcd设备文件,lcd设备文件在dev目录下都是以fb*存在的。


    得到一个可代表这个设备文件的文件描述符。


(2) 获取可变参数、固定参数

    如:要获取可变参数


    那么就定义一个变量来存放这个可变参数:

Linux驱动开发: FrameBuffe(LCD)驱动开发

   然后用ioctl接口实现数据的传递:

Linux驱动开发: FrameBuffe(LCD)驱动开发

     既然我们要获取参数,那么ioctl方向肯定是从内核到应用空间;


    根据FBIOGET_VSCREENINFO命令,从fbX设备文件中获取对应的数据,存放在vinfo指向的结构中。


   具体是什么结构的数据就看从内核传递上来是什么数据了。


   FBIOGET_VSCREENINFO命令命名方式可以分解如下:

Linux驱动开发: FrameBuffe(LCD)驱动开发

这样就可以直观的看出来,是获取屏幕的可变信息。

如:要获取固定参数

同样也定义一个变量来存放这个固定参数:

Linux驱动开发: FrameBuffe(LCD)驱动开发

通过lcd驱动提供的iotcl接口获取:

Linux驱动开发: FrameBuffe(LCD)驱动开发

(3) 计算屏幕大小(字节为单位)

计算屏幕大小是为了后面的mmap函数使用,这个屏幕大小是根据可变参数来计算的,公式如下:

Linux驱动开发: FrameBuffe(LCD)驱动开发

屏幕的一个点就是x方向的32位数据和y方向的32数据构成。

其中Bpp/8意义是每一个像素占用了多少个字节。即32/8=4byte。

我们可以看出来,屏幕内存大小是以字节为单位的。

如:

Linux驱动开发: FrameBuffe(LCD)驱动开发

这个screensize有何用处呢,就是给mmap函数去映射空间时使用的。


(4) 内存映射(mmap函数)

Linux下一切都是文件,我们在对Lcd设备文件操作就是对lcd屏进行了操作。


我们一般想到对屏的操作方法:


我们一般想到对屏的操作方法:


1- LCD操作可以像其他字符设备一样可以调用read,write,对lcd屏进行操作;


               但是出于效率考虑,一般不这样操作。


               例如: 我要让屏幕实现黑白切换。 (驱动层需要实现read和write函数)

While(1){
        Memset(buf,0,screensize);
        Write(fp,buf,screensize);
        Msleep(1);
        Memset(buf,0,screensize);
        Write(fp,buf,screensize);
        Msleep(1);
    }   

    分析这里为什么会出现效率低的问题:


                       我要把数据给内核,write就会调用copy_from_user传递数据;


                       如果屏容量1024*768*4 约有3M左右的数据,我在屏上刷一幅图片要传递3M左右            的数据,1S要刷50次呢,就要在1s内传递150M左右的数据;


                       memset函数也传递了一次数据,一个2次数据传递,150*2=300M。


                       那么主频低cpu在这样短时间里处理就会卡顿等。


               不用read,write原因:


                       我们在调用memset的时候,传递了一次数据给缓冲区;然后再使用write的copy_fr/


      om_user又传递了一次数据,一共就传递数据了2次。


                       那么是mmap又是怎么样子的呢?


                       只要值映射的时候传递了一次数据,在效率上提高了一倍。


2- 内核一般对LCD屏操作是通过内存映射(mmap)方式来实现。


              这是一种常见的文件操作方法,系统编程课程会有提到。


我们需要知道lcd编程的概念:


把lcd看成是一块内存,使用mmap函数把它的缓冲区映射到进程空间中,然后通过映射后的地址直接操作驱动中的显示缓冲区,往这块缓冲写数据,lcd就会按数值转换成相应颜色显示在LCD屏上。


那么就需要把lcd设备文件映射到进程的地址空间中。


首先来介绍mmap函数:(man 2 mmap可以查看该函数的用法)

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

功能:把文件的内存映射到进程的地址空间中。


参数:


addr: 设置一个可用的地址,一般情况你不知道哪个地址是可用的。


      所以,给0表示由系统自动分配。


length:映射的长度。单位是字节


prot:属性。如PROT_READ(可读),PROT_WRITE(可写)等。


        PROT_EXEC         映射后的地址空间可以执行代码.


        PROT_READ       映射后可以读.


        PROT_WRITE      映射后可以写


        PROT_NONE        映射后不能访问  


flags:保护标志。常用的是共享方式 MAP_SHARED。


fd :open返回的文件描述符。


offset:是从文件的哪个地方开始映射。  


返回值:映射后的首地址指针。


如下:

fbp =(unsigned char *) mmap (0,                     //表示系统自动分别可以映射地址
                         screensize,            //映射大小(字节)
                         PROT_READ | PROT_WRITE, //映射得到的空间属性
                         MAP_SHARED,            // 保护标志
                         fp,                        //要映射的文件描述符
                         0);                        //从文件的那个地方开始映射

把lcd设备文件映射到进程的地址空间,从映射开始0开始、大小为 screensize 的内容,得到一个指向这块空间首地址的指针fbp。


(5) 使用映射后的地址对屏进行操作

  使用上面得到的 fbp 指针,我们就可以来操作lcd的显示缓冲区了。


  示例1:要在(100,200) 处画一个点。


1- 计算偏移(固定的)地址:


      off = (800*200+100) * 4;  


屏幕上的点是呈(x,y)坐标形式,而进程地址空间呈一块连续的地址内存。所以要计算由屏上的点对于进程空间的地址。


写成通用的表示:


      Off = (xres*y + x) * bpp/8;


2- 写入颜色数据(是一个32位的数据):

*(unsigned int *)(fbp+off) = 0x00ff0000;//低地址存低字节(小端格式)
或:
* (fbp+off)   = 0x00;  //B(蓝色)
* (fbp+off+1) = 0x00;   //G(绿色)
* (fbp+off+2) = 0xff;   //R(红色)
* (fbp+off+3) = 0x00; //透明色

*(fbp+off)就是我们要写入颜色值的地方。

Fbp就是屏的起始点地址指针,fbp+off是指定点的地址指针。

这里的颜色表示是32bpp色深形式,rgb+透明色形式。

从低地址到高地址为B(蓝色)、G(绿色)、透明色。

我们可以把以上两个步骤封装成一个函数:

void show_pixle(int x,int y,unsigned int c)
{
    location = x * (vinfo.bits_per_pixel / 8) + y * finfo.line_length;
    *(unsigned int*)(fbp + location) = c;    
}  

参数:

X:要画的点(x,y)的x坐标

Y:要画的点(x,y)的y坐标

C:要写入的颜色值

(6) 释放映射

知道映射的首地址指针,映射的大小就可以把之前映射的进程地址空间释放,还给系统作其他用途。

munmap (fbp, screensize);

(7) 关闭文件

然后就是把你打的设备文件关闭了。

close (fp);

OK,到此,你对这个文件的操作就结束了。

3.2 示例代码: 获取屏幕信息

Linux驱动开发: FrameBuffe(LCD)驱动开发

输出的结果:

Linux驱动开发: FrameBuffe(LCD)驱动开发

3.3 示例代码: 显示一个中文字符

在LCD屏指定位置显示一个中文字符。

#include <linux/fb.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <sys/ioctl.h>

#include <stdio.h>

#include <sys/mman.h>

#include <string.h>

unsigned char font[]={/*--  文字:  国  --*/

/*--  宋体42;  此字体下对应的点阵为:宽x高=56x56   --*/

0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

0x00,0x00,0x00,0x00,0x00,0x01,0x80,0x00,0x00,0x00,0x03,0x00,0x01,0xE0,0x00,0x00,

0x00,0x07,0x80,0x01,0xFF,0xFF,0xFF,0xFF,0xFF,0xE0,0x01,0xF0,0x00,0x00,0x00,0x07,

0xC0,0x01,0xF0,0x00,0x00,0x00,0x07,0x80,0x01,0xF0,0x00,0x00,0x00,0x07,0x80,0x01,

0xF0,0x00,0x00,0x01,0x87,0x80,0x01,0xF0,0x00,0x00,0x03,0xC7,0x80,0x01,0xF6,0x00,

0x00,0x07,0xE7,0x80,0x01,0xF7,0xFF,0xFF,0xFF,0xF7,0x80,0x01,0xF1,0x80,0x7C,0x00,

0x07,0x80,0x01,0xF0,0x00,0x7C,0x00,0x07,0x80,0x01,0xF0,0x00,0x7C,0x00,0x07,0x80,

0x01,0xF0,0x00,0x7C,0x00,0x07,0x80,0x01,0xF0,0x00,0x7C,0x00,0x07,0x80,0x01,0xF0,

0x00,0x7C,0x00,0x07,0x80,0x01,0xF0,0x00,0x7C,0x00,0x07,0x80,0x01,0xF0,0x00,0x7C,

0x00,0x07,0x80,0x01,0xF0,0x00,0x7C,0x06,0x07,0x80,0x01,0xF0,0x00,0x7C,0x0F,0x07,

0x80,0x01,0xF1,0x80,0x7C,0x1F,0x87,0x80,0x01,0xF1,0xFF,0xFF,0xFF,0xC7,0x80,0x01,

0xF0,0xE0,0x7C,0x00,0x07,0x80,0x01,0xF0,0x00,0x7C,0x00,0x07,0x80,0x01,0xF0,0x00,

0x7F,0xC0,0x07,0x80,0x01,0xF0,0x00,0x7D,0xF0,0x07,0x80,0x01,0xF0,0x00,0x7C,0xFC,

0x07,0x80,0x01,0xF0,0x00,0x7C,0x7E,0x07,0x80,0x01,0xF0,0x00,0x7C,0x3F,0x07,0x80,

0x01,0xF0,0x00,0x7C,0x3F,0x07,0x80,0x01,0xF0,0x00,0x7C,0x1F,0x07,0x80,0x01,0xF0,

0x00,0x7C,0x0F,0x07,0x80,0x01,0xF0,0x00,0x7C,0x0E,0x07,0x80,0x01,0xF0,0x00,0x7C,

0x06,0x07,0x80,0x01,0xF0,0x00,0x7C,0x03,0x87,0x80,0x01,0xF0,0x00,0x7C,0x07,0xC7,

0x80,0x01,0xFC,0x00,0x7C,0x0F,0xE7,0x80,0x01,0xFF,0xFF,0xFF,0xFF,0xFF,0x80,0x01,

0xF7,0x00,0x00,0x00,0x07,0x80,0x01,0xF0,0x00,0x00,0x00,0x07,0x80,0x01,0xF0,0x00,

0x00,0x00,0x07,0x80,0x01,0xF0,0x00,0x00,0x00,0x07,0x80,0x01,0xF0,0x00,0x00,0x00,

0x07,0x80,0x01,0xF0,0x00,0x00,0x00,0x07,0x80,0x01,0xFF,0xFF,0xFF,0xFF,0xFF,0x80,

0x01,0xF0,0x00,0x00,0x00,0x07,0x80,0x01,0xF0,0x00,0x00,0x00,0x07,0x80,0x01,0xF0,

0x00,0x00,0x00,0x07,0x80,0x01,0xF0,0x00,0x00,0x00,0x06,0x00,0x01,0xC0,0x00,0x00,

0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,};

unsigned char *fbmem=NULL;

struct fb_var_screeninfo var;  // 可变参数--

struct fb_fix_screeninfo fix;  // 固定参数--

/*画点*/

void show_pixel(int x,int y,int color)

{

        unsigned long  *show32 = NULL;

        /* 定位到LCD屏上的位置*/

        show32  =(unsigned long  *)(fbmem + y*var.xres*var.bits_per_pixel/8 + x*var.bits_per_pixel/8);

        *show32 =color;  /*向指向的LCD地址赋数据*/

}

/*

函数功能:在LCD屏上显示字体

*/

void LCD_ShowFont(unsigned int x,unsigned int y,unsigned int w,unsigned int h,unsigned char *p)

{undefined

        unsigned int i,j;

        unsigned char data;

        unsigned int x0=x;  //保存X坐标

        for(i=0;i<w/8*h;i++)

        {undefined

                 data=p[i];

                 for(j=0;j<8;j++)

                 {undefined

                         if(data&0x80)show_pixel(x,y,0xF800); //字体颜色画红色

                         else show_pixel(x,y,0x0000);       //背景颜色画白色

                         x++;             //画完一个点,x坐标要向后移动

                         data<<=1;   //依次判断

                 }

                 if(x-x0==w)  //判断是否需要换新行

                 {undefined

                         x=x0; //横坐标归位

                         y++;  //换新的一行

                 }

        }

}

int main(int argc,char **argv)

{undefined

        int fd;

        /* 1、打开/dev/fb0 */

        fd = open("/dev/fb0",2);

        if(fd <= 0)

        {undefined

                 printf("open is error!!\n");

                 return -1;

        }

       

        /* 2、获取可变参数,固定参数 */

        /* 2.2、FBIOGET_VSCREENINFO获取可变参数:x,y,bpp */

        ioctl(fd,FBIOGET_VSCREENINFO,&var);

        printf("横坐标=%d\n",var.xres);

        printf("纵坐标=%d\n",var.yres);

        printf("一个像素点的位数=%dbit\n",var.bits_per_pixel);

        printf("横坐标*纵坐标*一个像素点的位数/8=Framebuffer的大小=%d字节\n",var.xres*var.yres*var.bits_per_pixel/8);

        /* 2.2、FBIOGET_FSCREENINFO获取固定参数:显存大小 */

        ioctl(fd,FBIOGET_FSCREENINFO,&fix);

        printf("屏幕显示缓冲区大小=%d字节\n",fix.smem_len); //FD缓冲区长度

        printf("虚拟横坐标=%d字节\n",var.xres_virtual);

        printf("虚拟纵坐标=%d字节\n",var.yres_virtual);

        /* 3、获取显存虚拟起始地址 */

        /*

         * start:虚拟起始地址 null 自动分配

         * length: 映射的大小

         * prot :权限 PROT_READ | PROT_WRITE

         * flags : MAP_SHARED

         * fd:文件描述符

         */

        fbmem =(unsigned char *)mmap(NULL,fix.smem_len,PROT_READ|PROT_WRITE,MAP_SHARED,fd, 0);

        printf("LCD 映射地址:%p\n",fbmem);

        if(fbmem == (unsigned char *)-1)

        {undefined

                 printf("mmap error!!!\n");

                 munmap(fbmem,fix.smem_len);   //取消映射

                 return -1;

        }

       

        /* 4、清屏函数----把fbmem空间覆盖成0x0    ,清屏成黑色*/

        memset(fbmem,0x00,fix.smem_len);

        LCD_ShowFont(50,150,56,56,font); //显示汉字

        close(fd);

        return 0;

}

四、MMAP驱动实现

4.1 mmap简介

mmap函数用于将一个文件或者其它对象映射进内存,通过对这段内存的读取和修改,来实现对文件的读取和修改,而不需要再调用read,write等操作。

头文件:<sys/mman.h>

函数原型:

Linux驱动开发: FrameBuffe(LCD)驱动开发

4.2 mmap系统调用接口参数说明

映射函数

Linux驱动开发: FrameBuffe(LCD)驱动开发

addr: 指定映射的起始地址,通常设为NULL,由系统指定。


length:映射到内存的文件长度。


prot:  映射的保护方式,可以是:


PROT_EXEC:映射区可被执行


PROT_READ:映射区可被读取


PROT_WRITE:映射区可被写入


PROT_NONE:映射区不能存取


Flags:  映射区的特性,可以是:


MAP_SHARED:写入映射区的数据会复制回文件,且允许其他映射该文件的进程共享。


MAP_PRIVATE:对映射区的写入操作会产生一个映射区的复制(copy_on_write),对此区域所做的修改不会写回原文件。


fd:由open返回的文件描述符,代表要映射的文件。


offset:以文件开始处的偏移量,必须是分页大小的整数倍,通常为0,表示从文件头开始映射。


解除映射

Linux驱动开发: FrameBuffe(LCD)驱动开发

功能:取消参数start所指向的映射内存,参数length表示欲取消的内存大小。


返回值:解除成功返回0,否则返回-1


4.3 Linux内核的mmap接口

Linux内核中使用结构体vm_area_struct来描述虚拟内存的区域,其中几个主要成员如下:


unsigned long vm_start   虚拟内存区域起始地址


unsigned long vm_end   虚拟内存区域结束地址


unsigned long vm_flags  该区域的标志


该结构体定义在<linux/mm_types.h>头文件中。


该结构体的vm_flags成员赋值的标志为:VM_IO和VM_RESERVED。


其中:VM_IO表示对设备IO空间的映射,M_RESERVED表示该内存区不能被换出,在设备驱动中虚拟页和物理页的关系应该是长期的,应该保留起来,不能随便被别的虚拟页换出(取消)。


mmap操作接口


在字符设备的文件操作集合(struct file_operations)中有mmap函数的接口。原型如下:

Linux驱动开发: FrameBuffe(LCD)驱动开发

其中第二个参数struct vm_area_struct *相当于内核找到的,可以拿来用的虚拟内存区间。mmap内部可以完成页表的建立。


4.5 实现mmap映射

      映射一个设备是指把用户空间的一段地址关联到设备内存上,当程序读写这段用户空间的地址时,它实际上是在访问设备。这里需要做的两个操作:


1. 找到可以用来关联的虚拟地址区间。


2.  实现关联操作。


mmap设备操作实例如下:

Linux驱动开发: FrameBuffe(LCD)驱动开发

其中的buf就是在内核中申请的一段空间。使用kmalloc函数实现。

代码如下:

Linux驱动开发: FrameBuffe(LCD)驱动开发

4.6 remap_pfn_range函数

remap_pfn_range函数用于一次建立所有页表。函数原型如下:

Linux驱动开发: FrameBuffe(LCD)驱动开发

      其中vma是内核为我们找到的虚拟地址空间,addr要关联的是虚拟地址,pfn是要关联的物理地址,size是关联的长度是多少。


   ioremap与phys_to_virt、virt_to_phys的区别:


ioremap是用来为IO内存建立映射的, 它为IO内存分配了虚拟地址,这样驱动程序才可以访问这块内存。


phys_to_virt只是计算出某个已知物理地址所对应的虚拟地址。


virt_to_phys :物理地址


4.7 示例代码

(1) 驱动代码示例


Linux驱动开发: FrameBuffe(LCD)驱动开发

Linux驱动开发: FrameBuffe(LCD)驱动开发

(2) 应用层代码示例

Linux驱动开发: FrameBuffe(LCD)驱动开发

五、FrameBuffer帧缓冲驱动框架

帧缓冲设备相关的数据结构存放在: fb.h头文件里

使用帧缓冲相关函数与数据结构需要添加头文件: #include <linux/fb.h>

Linux驱动开发: FrameBuffe(LCD)驱动开发

5.1  帧缓冲注册与注销函数

1. FrameBuffer注册函数

Linux驱动开发: FrameBuffe(LCD)驱动开发

函数功能介绍: 注册帧缓冲设备

函数参数介绍:

@ fb_info  :帧缓冲(LCD)设备信息。

说明: 注册成功之后,会在/dev目录下生成fb开头的设备文件。

2. FrameBuffer注销函数

Linux驱动开发: FrameBuffe(LCD)驱动开发

函数功能介绍: 注销帧缓冲设备

函数参数介绍:

@ fb_info  :注册时填入的帧缓冲(LCD)设备信息。

5.2 struct fb_info结构介绍

以下列出了struct fb_info常用的几个成员:

Linux驱动开发: FrameBuffe(LCD)驱动开发

LCD屏专属的文件操作接口,在fb.h定义,以下列出了常用的几个函数接口:

Linux驱动开发: FrameBuffe(LCD)驱动开发

可变形参常用的结构成员:

Linux驱动开发: FrameBuffe(LCD)驱动开发

固定参数常用的结构体成员:

Linux驱动开发: FrameBuffe(LCD)驱动开发

填充示例:

Linux驱动开发: FrameBuffe(LCD)驱动开发

5.3 分配显存地址

Linux驱动开发: FrameBuffe(LCD)驱动开发

功能介绍:分配一块物理地址连续的内存, 供后续 DMA 传输使用。


函数参数:


struct device *dev : 传入设备指针, 没有填 NULL。


size_t size : 分配的空间大小。


dma_addr_t *dma_handle :存放分配之后的物理地址。


gfp_t flag :分配的属性。 常用: GFP_KERNEL


返回值: 物理地址对应的虚拟地址, 内核代码本身只能操作虚拟地址。


示例:

Linux驱动开发: FrameBuffe(LCD)驱动开发

5.4  帧缓冲框架注册示例代码

以下演示了帧缓冲的驱动框架,剩下事情可以直接加入硬件代码。

1. 帧缓冲驱动代码

Linux驱动开发: FrameBuffe(LCD)驱动开发

Linux驱动开发: FrameBuffe(LCD)驱动开发

2. 应用层代码

Linux驱动开发: FrameBuffe(LCD)驱动开发

Linux驱动开发: FrameBuffe(LCD)驱动开发

运行结果:

Linux驱动开发: FrameBuffe(LCD)驱动开发

六、OLED显示屏驱动+帧缓冲驱动模板

6.1 OLED简介

OLED,即有机发光二极管( Organic Light Emitting Diode)。 OLED 由于同时具备自发光,不需背光源、对比度高、厚度薄、视角广、反应速度快、可用于挠曲性面板、使用温度范围广、构造及制程较简单等优异之特性,被认为是下一代的平面显示器新兴应用技术。


LCD 都需要背光,而 OLED 不需要,因为它是自发光的。这样同样的显示 OLED 效果要来得好一些。以目前的技术,OLED 的尺寸还难以大型化,但是分辨率确可以做到很高。在此我们使用的是中景园电子的 0.96 寸 OLED 显示屏,该屏有以下特点:


1)0.96 寸 OLED 有黄蓝,白,蓝三种颜色可选;其中黄蓝是屏上 1/4 部分为黄光,下 3/4 为蓝;而且是固定区域显示固定颜色,颜色和显示区域均不能修改;白光则为纯白,也就是黑底白字;


蓝色则为纯蓝,也就是黑底蓝字。


2)分辨率为 128*64


3)多种接口方式;OLED 裸屏总共种接口包括:6800、8080 两种并行接口方式、3 线或 4 线的串行 SPI 接口方式、 IIC 接口方式(只需要 2 根线就可以控制 OLED 了!),这五种接口是通过屏上的 BS0~BS2 来配置的。


4)OLED屏开发了两种接口的 Demo 板,接口分别为七针的 SPI/IIC 兼容模块,四针的IIC 模块。


0.96 寸 OLED屏外观

Linux驱动开发: FrameBuffe(LCD)驱动开发Linux驱动开发: FrameBuffe(LCD)驱动开发

6.2 OLED驱动代码示例

Linux驱动开发: FrameBuffe(LCD)驱动开发

Linux驱动开发: FrameBuffe(LCD)驱动开发

Linux驱动开发: FrameBuffe(LCD)驱动开发

Linux驱动开发: FrameBuffe(LCD)驱动开发

Linux驱动开发: FrameBuffe(LCD)驱动开发

Linux驱动开发: FrameBuffe(LCD)驱动开发

Linux驱动开发: FrameBuffe(LCD)驱动开发

6.3 应用层测试代码示例

#include <stdio.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <sys/ioctl.h>

#include <linux/fb.h>

#include <sys/ioctl.h>

#include <sys/mman.h>

#include <string.h>

#define OLED_RefreshGRAM 0x12345 /*将应用层的数据刷新到OLED屏幕上*/

#define OLED_ClearGRAM 0x45678 /*将应用层的数据刷新到OLED屏幕上*/

struct fb_var_screeninfo var;  //可变参数

struct fb_fix_screeninfo fix;   //固定参数

unsigned char *fb_mem=NULL; //LCD屏的首地址

extern unsigned char font1[];

extern unsigned char font2[];

/*

函数功能: 画点

*/

void Show_Pixel(int x,int y,unsigned char color)

{undefined

        unsigned char *lcd=(unsigned char *)(fb_mem+y*var.xres*var.bits_per_pixel/8+x*var.bits_per_pixel/8);

        *lcd=color; //颜色赋值

}

/*

函数功能: 显示中文

说明:  取模的字体是按照横向取模进行取点阵码。

       取模的字体宽度是8的倍数。

*/

void ShowFont(int x,int y,int size,unsigned char *data)

{undefined

        int i,j,x0=x;

        unsigned char tmp;

        for(i=0;i<size/8*size;i++)

        {undefined

                 tmp=data[i];

                 for(j=0;j<8;j++)

                 {undefined

                         if(tmp&0x80)Show_Pixel(x0,y,0xF);

                         //else 画背景色

                         x0++;

                         tmp<<=1;

                 }

                 if(x0-x==size)

                 {undefined

                         y++;

                         x0=x;

                 }

        }

}

int main(int argc,char **argv)

{undefined

        if(argc!=2)

        {undefined

                 printf("./app /dev/fbX\n");

                 return 0;

        }

        int fd=open(argv[1],O_RDWR);

        if(fd<0)

        {undefined

                 perror("设备文件打开失败");

                 return 0;

        }

       

        /*1. 获取LCD屏的可变形参*/

        ioctl(fd,FBIOGET_VSCREENINFO,&var);

        printf("分辨率:%d*%d\n",var.xres,var.yres);

        printf("像素点位数:%d\n",var.bits_per_pixel);

       

        /*2. 获取LCD屏的固定形参*/

        ioctl(fd,FBIOGET_FSCREENINFO,&fix);

        printf("映射的长度:%d\n",fix.smem_len);

        printf("一行的字节数:%d\n",fix.line_length);

       

        /*3. 映射LCD缓冲区地址到进程空间*/

        fb_mem=mmap(NULL,fix.smem_len,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);

        if(fb_mem==NULL)

        {undefined

                 perror("空间映射失败!\n");

                 return 0;

        }

       

       

        /*4. 控制显示屏*/

        //OLED清屏

        ioctl(fd,OLED_ClearGRAM);

        memset(fb_mem,0,fix.smem_len);

       

        /*显示中文*/

        ShowFont(0,0,56,font1);

        ShowFont(56,0,56,font2);

        ioctl(fd,OLED_RefreshGRAM);

                

        munmap(fb_mem,fix.smem_len);

        close(fd);

        return 0;

}

unsigned char font2[]=

{undefined

        /*--  文字:  入  --*/

/*--  幼圆42;  此字体下对应的点阵为:宽x高=56x56   --*/

0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0xC0,0x00,0x00,

0x00,0x00,0x00,0x0F,0xE0,0x00,0x00,0x00,0x00,0x00,0x07,0xF0,0x00,0x00,0x00,0x00,

0x00,0x01,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0xFC,0x00,0x00,0x00,0x00,0x00,0x00,

0x7C,0x00,0x00,0x00,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x00,

0x00,0x00,0x00,0x00,0x00,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,

0x00,0x00,0x00,0x3F,0x80,0x00,0x00,0x00,0x00,0x00,0x3F,0x80,0x00,0x00,0x00,0x00,

0x00,0x7F,0xC0,0x00,0x00,0x00,0x00,0x00,0x7F,0xC0,0x00,0x00,0x00,0x00,0x00,0x7B,

0xC0,0x00,0x00,0x00,0x00,0x00,0xFB,0xE0,0x00,0x00,0x00,0x00,0x00,0xF1,0xE0,0x00,

0x00,0x00,0x00,0x00,0xF1,0xE0,0x00,0x00,0x00,0x00,0x01,0xF1,0xF0,0x00,0x00,0x00,

0x00,0x01,0xE0,0xF0,0x00,0x00,0x00,0x00,0x03,0xE0,0xF8,0x00,0x00,0x00,0x00,0x03,

0xC0,0xF8,0x00,0x00,0x00,0x00,0x07,0xC0,0x7C,0x00,0x00,0x00,0x00,0x07,0x80,0x7C,

0x00,0x00,0x00,0x00,0x0F,0x80,0x3C,0x00,0x00,0x00,0x00,0x0F,0x00,0x3E,0x00,0x00,

0x00,0x00,0x1F,0x00,0x1E,0x00,0x00,0x00,0x00,0x1E,0x00,0x1F,0x00,0x00,0x00,0x00,

0x3E,0x00,0x0F,0x00,0x00,0x00,0x00,0x7C,0x00,0x0F,0x80,0x00,0x00,0x00,0x7C,0x00,

0x07,0xC0,0x00,0x00,0x00,0xF8,0x00,0x07,0xC0,0x00,0x00,0x01,0xF0,0x00,0x03,0xE0,

0x00,0x00,0x01,0xE0,0x00,0x01,0xE0,0x00,0x00,0x03,0xE0,0x00,0x01,0xF0,0x00,0x00,

0x07,0xC0,0x00,0x00,0xF8,0x00,0x00,0x0F,0x80,0x00,0x00,0xF8,0x00,0x00,0x1F,0x00,

0x00,0x00,0x7C,0x00,0x00,0x1F,0x00,0x00,0x00,0x3E,0x00,0x00,0x3E,0x00,0x00,0x00,

0x3E,0x00,0x00,0x7C,0x00,0x00,0x00,0x1F,0x00,0x00,0xF8,0x00,0x00,0x00,0x0F,0x80,

0x01,0xF0,0x00,0x00,0x00,0x07,0xC0,0x03,0xE0,0x00,0x00,0x00,0x03,0xE0,0x07,0xC0,

0x00,0x00,0x00,0x03,0xF0,0x0F,0x80,0x00,0x00,0x00,0x01,0xF8,0x1F,0x00,0x00,0x00,

0x00,0x00,0xFC,0x1E,0x00,0x00,0x00,0x00,0x00,0x7C,0x1C,0x00,0x00,0x00,0x00,0x00,

0x3C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

};

unsigned char font1[]=

{undefined

/*--  文字:  嵌  --*/

/*--  幼圆42;  此字体下对应的点阵为:宽x高=56x56   --*/

0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x38,

0x00,0x00,0x00,0x03,0x80,0x00,0x7C,0x00,0x03,0x80,0x07,0xC0,0x00,0x7C,0x00,0x03,

0xC0,0x07,0xC0,0x00,0x7C,0x00,0x03,0xC0,0x07,0xC0,0x00,0x7C,0x00,0x03,0xC0,0x07,

0xC0,0x00,0x7C,0x00,0x03,0xC0,0x07,0xC0,0x00,0x7C,0x00,0x03,0xC0,0x07,0xC0,0x00,

0x7C,0x00,0x03,0xC0,0x07,0xFF,0xFF,0xFF,0xFF,0xFF,0xC0,0x03,0xFF,0xFF,0xFF,0xFF,

0xFF,0x80,0x01,0xFF,0xFF,0xFF,0xFF,0xFF,0x80,0x00,0xFF,0xFF,0xFF,0xFF,0xFC,0x00,

0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0xE0,0x07,0x00,0xF0,0x00,0x00,0x01,0xE0,

0x0F,0x01,0xF0,0x00,0x00,0x01,0xE0,0x0F,0x01,0xF0,0x00,0x00,0x01,0xE0,0x0F,0x01,

0xE0,0x00,0x00,0x01,0xE0,0x0F,0x01,0xE0,0x00,0x00,0x1F,0xFF,0xFF,0xF3,0xFF,0xFF,

0xF0,0x3F,0xFF,0xFF,0xFB,0xFF,0xFF,0xF8,0x3F,0xFF,0xFF,0xFB,0xFF,0xFF,0xF8,0x1F,

0xFF,0xFF,0xE7,0xC3,0x80,0x7C,0x01,0xE0,0x0F,0x07,0x87,0x80,0x7C,0x01,0xE0,0x0F,

0x0F,0x87,0x80,0x7C,0x01,0xE0,0x0F,0x0F,0x87,0x80,0x78,0x01,0xE0,0x0F,0x1F,0x07,

0x80,0x78,0x01,0xE0,0x0F,0x1F,0x07,0x80,0xF8,0x01,0xE0,0x0F,0x3E,0x07,0x80,0xF8,

0x01,0xE0,0x0F,0x3E,0x07,0x80,0xF0,0x01,0xE0,0x0F,0x3C,0x07,0x81,0xF0,0x01,0xE0,

0x0F,0x3C,0x07,0x81,0xE0,0x01,0xE0,0x0F,0x00,0x07,0x83,0xE0,0x01,0xFF,0xFF,0x00,

0x07,0xC3,0xC0,0x01,0xFF,0xFF,0x00,0x07,0xC0,0x00,0x01,0xFF,0xFF,0x00,0x0F,0xE0,

0x00,0x01,0xFF,0xFF,0x00,0x0F,0xE0,0x00,0x01,0xE0,0x0F,0x00,0x0F,0xF0,0x00,0x01,

0xE0,0x0F,0x00,0x1E,0xF0,0x00,0x01,0xE0,0x0F,0x00,0x1E,0xF8,0x00,0x01,0xE0,0x0F,

0x00,0x3E,0x78,0x00,0x01,0xE0,0x0F,0x00,0x7C,0x7C,0x00,0x01,0xE0,0x0F,0x00,0x78,

0x3E,0x00,0x01,0xE0,0x0F,0x00,0xF8,0x1E,0x00,0x01,0xE0,0x0F,0x01,0xF0,0x1F,0x00,

0x01,0xE0,0x0F,0x03,0xE0,0x0F,0x80,0x01,0xE0,0x0F,0x07,0xE0,0x07,0xC0,0x01,0xE0,

0x0F,0x0F,0xC0,0x03,0xE0,0x01,0xE0,0x0F,0x1F,0x80,0x01,0xF0,0x01,0xFF,0xFF,0x3F,

0x00,0x01,0xF8,0x00,0xFF,0xFF,0x7E,0x00,0x00,0xFC,0x00,0x7F,0xFE,0x78,0x00,0x00,

0x3C,0x00,0x00,0x00,0x70,0x00,0x00,0x18,

};

6.4 OLED显示效果图

Linux驱动开发: FrameBuffe(LCD)驱动开发

七、LCD驱动编写

7.1 编写S70屏幕驱动

     如果自己编写了LCD驱动(S720屏幕),测试LCD驱动之前,先去除内核自带的LCD驱动,编译烧写内核:

Linux驱动开发: FrameBuffe(LCD)驱动开发

Linux驱动开发: FrameBuffe(LCD)驱动开发

群创S70驱动代码:

#include <linux/module.h>

#include <linux/kernel.h>

#include <linux/errno.h>

#include <linux/string.h>

#include <linux/mm.h>

#include <linux/slab.h>

#include <linux/delay.h>

#include <linux/fb.h>

#include <linux/init.h>

#include <linux/dma-mapping.h>

#include <linux/interrupt.h>

#include <linux/platform_device.h>

#include <linux/clk.h>

#include <linux/cpufreq.h>

#include <asm/io.h>

#include <asm/div64.h>

#include <asm/mach/map.h>

#include <mach/regs-gpio.h>

#define MHZ (1000*1000)

#define PRINT_MHZ(m)                       ((m) / MHZ), ((m / 1000) % 1000)

/* LCD参数 */

#define VSPW       0 

#define VBPD       22

#define LINEVAL    479

#define VFPD       21

#define HSPW       0

#define HBPD       45

#define HOZVAL     799

#define HFPD       209

#define LeftTopX     0

#define LeftTopY     0

#define RightBotX   799

#define RightBotY   479

/* LCD控制寄存器 */

 static unsigned long *vidcon0;     /*  Configures video output format and displays enable/disable. */                         

 static unsigned long *vidcon1;     /*  Specifies RGB I/F control signal */                          

 static unsigned long *vidcon2;     /* Specifies output data format control. */                          

static unsigned long *vidtcon0; /* video time control 0 */                  

static unsigned long *vidtcon1; /* video time control 1 */                  

static unsigned long *vidtcon2; /* video time control 2 */

static unsigned long *wincon0;      /* window control 0 */                        

static unsigned long *vidosd0a;     /* video window 0 position control */       

static unsigned long *vidosd0b;     /* video window 0 position control1 */      

static unsigned long *vidosd0c;     /* video window 0 position control */

static unsigned long *vidw00add0b0; /* window 0 buffer start address, buffer 0 */

static unsigned long *vidw00add1b0; /* window 0 buffer end address, buffer 0 */ 

static unsigned long *vidw00add2;       /* window 0 buffer size */   

static unsigned long *wpalcon;

static unsigned long *shadowcon;

static unsigned long *winchmap2;

/* 用于LCD的GPIO */

static unsigned long *gpf0con = NULL;

static unsigned long *gpf1con = NULL;

static unsigned long *gpf2con = NULL;

static unsigned long *gpf3con = NULL;

static unsigned long *gpf0pud = NULL;

static unsigned long *gpf1pud = NULL;

static unsigned long *gpf2pud = NULL;

static unsigned long *gpf3pud = NULL;

static unsigned long *gpf0drv = NULL;

static unsigned long *gpf1drv = NULL;

static unsigned long *gpf2drv = NULL;

static unsigned long *gpf3drv = NULL;

/* 用于背光 */

static unsigned long *gpd0con = NULL;

static unsigned long *gpd0dat = NULL;

static unsigned long *lcdblk_cfg = NULL;

static unsigned long *lcdblk_cfg2 = NULL;

static struct fb_info *mylcd;

static u32 pseudo_pal[16];

static struct device dev =

{undefined

        .init_name = "exynos4-fb.0",

};

static inline unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf)

{undefined

        chan &= 0xffff;

        chan >>= 16 - bf->length;

        return chan << bf->offset;

}

static int mylcdfb_setcolreg(unsigned int regno, unsigned int red,

                              unsigned int green, unsigned int blue,

                              unsigned int transp, struct fb_info *info)

{undefined

        unsigned int val;

       

        if (regno > 16)

                 return 1;

        /* 用red,green,blue三原色构造出val */

        val  = chan_to_field(red,      &info->var.red);

        val |= chan_to_field(green, &info->var.green);

        val |= chan_to_field(blue,       &info->var.blue);

       

        //((u32 *)(info->pseudo_palette))[regno] = val;

        pseudo_pal[regno] = val;

        return 0;

}

static struct fb_ops mylcdfb_ops = {undefined

        .owner              = THIS_MODULE,

        .fb_setcolreg   = mylcdfb_setcolreg,

        .fb_fillrect       = cfb_fillrect,

        .fb_copyarea   = cfb_copyarea,

        .fb_imageblit   = cfb_imageblit,

};

static void init_fimd(void)

{undefined

        gpf0con = ioremap(0x11400180,16);

        gpf0pud = gpf0con + 2;

        gpf0drv = gpf0con + 3;

        gpf1con = ioremap(0x114001A0,16);

        gpf1pud = gpf1con + 2;

        gpf1drv = gpf1con + 3;

        gpf2con = ioremap(0x114001C0,16);

        gpf2pud = gpf2con + 2;

        gpf2drv = gpf2con + 3;

        gpf3con = ioremap(0x114001E0,16);

        gpf3pud = gpf3con + 2;

        gpf3drv = gpf3con + 3;

        /* 设置管脚为LCD接口功能 */

        *gpf0con        = 0x22222222;      

        *gpf1con        = 0x22222222;               

        *gpf2con        = 0x22222222;               

        *gpf3con        = 0x22222222;               

        /* 设置为上拉 */

        *gpf0pud        = 0x0000FFFF;

        *gpf1pud        = 0x0000FFFF;

        *gpf2pud        = 0x0000FFFF;

        *gpf3pud        = 0x00000FFF;

        /* 设置为最高驱动能力 */

        *gpf0drv         = 0x0000FFFF;

        *gpf1drv         = 0x0000FFFF;

        *gpf2drv         = 0x0000FFFF;

        *gpf3drv         = 0x00000FFF;

}

static void init_blanklight(void)

{undefined

        gpd0con = ioremap(0x114000a0,4);

        gpd0dat = ioremap(0x114000a4,4);

        *gpd0con |= 1<<4;

        *gpd0dat |= 1<<1;

}

static void init_lcd_regs(void)

{undefined

        lcdblk_cfg = ioremap(0x10010210,4);

        lcdblk_cfg2 = lcdblk_cfg + 1;

        vidcon0           = ioremap(0x11C00000,4);

        vidcon1           = ioremap(0x11C00004,4);

        wincon0          = ioremap(0x11C00020,4);

        vidosd0a          = ioremap(0x11C00040,4);

        vidosd0b                 = ioremap(0x11C00044,4);

        vidosd0c                 = ioremap(0x11C00048,4);

        vidw00add0b0 = ioremap(0x11C000A0,4);

        vidw00add1b0 = ioremap(0x11C000D0,4);

        vidw00add2  = ioremap(0x11C00100,4);

        vidtcon0                 = ioremap(0x11C00010,4);

        vidtcon1                 = ioremap(0x11C00014,4);

        vidtcon2                 = ioremap(0x11C00018,4);

        wpalcon                 = ioremap(0x11C001A0,4);

        shadowcon             = ioremap(0x11C00034,4);

        winchmap2              = ioremap(0x11C0003c,4);

}

static void rm_all_regs(void)

{undefined

        iounmap(gpf0con);

        iounmap(gpf1con);

        iounmap(gpf2con);

        iounmap(gpf3con);

        iounmap(gpd0con);

        iounmap(gpd0dat);

        iounmap(lcdblk_cfg);

        iounmap(vidcon0);

        iounmap(vidcon1);

        iounmap(vidtcon2);

        iounmap(wincon0);

        iounmap(vidosd0a);

        iounmap(vidosd0b);

        iounmap(vidosd0c);

        iounmap(vidw00add0b0);

        iounmap(vidw00add1b0);

        iounmap(vidw00add2);

        iounmap(vidtcon0);

        iounmap(vidtcon1);

        iounmap(shadowcon);

        iounmap(winchmap2);

}

/* 入口函数 */

static int mylcd_init(void)

{undefined

        struct clk          *bus;

        struct clk         *lcd_clk;

        /* 1. 分配一个fb_info */

        mylcd = framebuffer_alloc(0, NULL);

        /* 2. 设置 */

        /* 2.1 设置固定的参数 */

        strcpy(mylcd->fix.id, "mylcd");

        //mylcd->fix.smem_start = ;  //显存的物理起始地址

        mylcd->fix.smem_len = 800*480*4;

        mylcd->fix.type = FB_TYPE_PACKED_PIXELS;

        mylcd->fix.visual = FB_VISUAL_TRUECOLOR;

        mylcd->fix.line_length = 800*4;

        /* 2.2 设置可变的参数 */

        mylcd->var.xres             = 800;

        mylcd->var.yres             = 480;

        mylcd->var.xres_virtual  = 800;

        mylcd->var.yres_virtual  = 480;

        mylcd->var.bits_per_pixel = 32;

        /*RGB = 8:8:8*/

        mylcd->var.red.offset          = 16;

        mylcd->var.red.length         = 8;

        mylcd->var.green.offset       = 8;

        mylcd->var.green.length      = 8;

        mylcd->var.blue.offset         = 0;

        mylcd->var.blue.length        = 8;

        mylcd->var.activate             = FB_ACTIVATE_NOW;

       

        /* 2.3 设置操作函数 */

        mylcd->fbops = &mylcdfb_ops;

        /* 2.4 其他的设置 */

        //mylcd->screen_base = ; /*显存的虚拟起始地址*/

        mylcd->screen_size = 800*480*4;

        mylcd->pseudo_palette = pseudo_pal;

        /* 3. 硬件相关的操作 */

        /* 3.1 配置GPIO用于LCD */

        init_fimd();

        /* 3.2 使能时钟 */

        bus = clk_get(&dev, "lcd");

        if (IS_ERR(bus))

        {undefined

                 printk(KERN_INFO "failed to get lcd clock source\n");

                

        }

        clk_enable(bus);

        printk("bus clock = %lu\n",clk_get_rate(bus));

        lcd_clk = clk_get(&dev, "sclk_fimd");

        if (IS_ERR(lcd_clk))

        {undefined

                 printk(KERN_INFO "failed to get lcd clock source\n");

        }

        clk_enable(lcd_clk);

        printk("lcd clock = %lu\n",clk_get_rate(lcd_clk));

        /* LCD控制器初始化 */

        init_lcd_regs();

        /*

         * LCDBLK_CFG

         * [11:10] :Video Type Selection

                         00 = RGB Interface

         */

        *lcdblk_cfg &= ~(0x3<<10);

        *lcdblk_cfg |= 1 << 1;

    *lcdblk_cfg2 |= 1;

        /*

         *VIDCON0:

         *     [13:6]: CLKVAL_F     //设置LCD时钟分频系数

         *

         *  VCLK == 33.3Mhz  

         *  VCLK = FIMD * SCLK/(CLKVAL+1)

         *  VCLK =  800000000 / (CLKVAL + 1)  

         *  33300000 = 800000000 /(CLKVAL + 1)

         *  CLKVAL + 1 = 24.02

         *  CLKVAL = 23

         *

         */

        *vidcon0 &= ~((0xff<<6)|(0x7<<26)|(1<<18));

        *vidcon0 |= 23<<6;

        /* 设置极性(要修改)

         *VIDTCON1:

         * [5]:IVSYNC  ===> 1 : Inverted(反转)

         * [6]:IHSYNC  ===> 1 : Inverted(反转)

         * [7]:IVCLK   ===> 1 : Fetches video data at VCLK rising edge (下降沿触发)

         * [10:9]:FIXVCLK  ====> 01 : VCLK running

         *

         */

        *vidcon1 = (1 << 9) | (1 << 7) | (1 << 5) | (1 << 6);

       

        /* 设置时序(需要修改) */

        *vidtcon0 = (VBPD << 16) | (VFPD << 8) | (VSPW << 0);

        *vidtcon1 = (HBPD << 16) | (HFPD << 8) | (HSPW << 0);

        /* 设置屏幕的大小

         * LINEVAL[21:11]:多少行   = 480

         * HOZVAL [10:0] :水平大小 = 800

         */

        *vidtcon2 = (LINEVAL << 11) | (HOZVAL << 0);

        /*

         *

         *WINCON0:

         *  [5:2]: 选择窗口的像素 1011 ===> 24BPP

         *  [1]:  使能或者禁止窗口输出  1 = Enables

         *  [15] : 大小端 :1=Enable ;

         *

         */

        *wincon0 &= ~(0xf << 2);

        *wincon0 |= (0xB<<2)|(1<<15);

       

        /* 窗口0,左上角的位置(0,0) */

        /* 窗口0,右下角的位置(0,0) */

        *vidosd0a = (LeftTopX<<11) | (LeftTopY << 0);

        *vidosd0b = (RightBotX<<11) | (RightBotY << 0);

        /* 大小 */

        *vidosd0c = (LINEVAL + 1) * (HOZVAL + 1);

        /* 分配显存 */

        mylcd->screen_base = dma_alloc_writecombine(NULL, mylcd->fix.smem_len, &mylcd->fix.smem_start, GFP_KERNEL);

        *vidw00add0b0 = mylcd->fix.smem_start; //显存的物理起始地址

        *vidw00add1b0 = mylcd->fix.smem_start + mylcd->fix.smem_len; //显存的物理结束地址

       

        /* C0_EN_F  0  Enables Channel 0.

         * 0 = Disables 1 = Enables

         */

        *shadowcon |= 0x1;

        /* LCD控制器开启 */

        *vidcon0 |= 0x3; /* 开启总控制器 */

        *wincon0 |= 1;     /* 开启窗口0 */

        /*4.注册*/

        register_framebuffer(mylcd);

        return 0;

}

static void mylcd_exit(void)

{undefined

        unregister_framebuffer(mylcd);

        dma_free_writecombine(NULL, mylcd->fix.smem_len, &(mylcd->fix.smem_start), GFP_KERNEL);

        rm_all_regs();

        framebuffer_release(mylcd);

}

module_init(mylcd_init);

module_exit(mylcd_exit);

MODULE_LICENSE("GPL");

7.2 增加自己的LCD驱动

下面步骤演示,在内核自带的LCD驱动框架上增加自己的LCD信息。

打开tiny4412-lcds.c文件增加LCD信息列表,增加之后再编译内核烧写

Linux驱动开发: FrameBuffe(LCD)驱动开发

Linux驱动开发: FrameBuffe(LCD)驱动开发

LCD_wbyq结构体信息如下(拷贝S70屏幕的信息):

Linux驱动开发: FrameBuffe(LCD)驱动开发

修改UBOOT启动代码传入的LCD参数,将参数改为自己的LCD名字

Linux驱动开发: FrameBuffe(LCD)驱动开发


上一篇:Ubuntu安装Docker引擎和支持HTTPS的docker-registry服务


下一篇:乌海市大数据产业园建设正酣