【嵌入式环境下linux内核及驱动学习笔记-(19)LCD驱动框架2-FrameBuffer】

目录

  • 1、 Frmebuffer(帧缓冲)操作介绍
    • 1.1 显示设备的抽象
    • 1.2 内存映像
    • 1.3 输出画面数据
    • 1.4 用户态下操作屏显
      • 1.4.1 用文件I / O 操作屏显
      • 1.4.2 mmap() 函数
      • 1.4.3 ioctl()函数
      • 1.4.5 用命令操作屏
      • 1.4.6 测试程序
  • 2、Framebuffer总体框架
    • 2.1 框架要点
    • 2.2 fbmem.c分析
      • 2.2.1 fbmem的入口分析
      • 2.2.2 fbmem的接口功能
        • 2.2.2.1 向上的接口
        • 2.2.2.2 向下的接口
      • 2.2.3 相关函数详解
        • 2.2.3.1 proc_create()函数
        • 2.2.3.2 fb_proc_fops结构体变量
        • 2.2.3.3. register_chrdev()函数
        • 2.2.3.4 class_create()函数
        • 2.2.3.5 register_framebuffer()函数
    • 2.3 LCD驱动分析(设备树匹配的platform平台驱动)
      • 2.3.1 设备树相关内容
      • 2.3.2 数据结构及函数
        • struct fb_info
        • struct fb_var_screeninfo 和 struct fb_fix_screeninfo
        • struct fb_var_screeninfo
        • struct fb_fix_screeninfo
        • struct fb_videomode结构体
        • fb_var_to_videomode()与 fb_videomode_to_var()函数
        • struct fb_ops
        • 宏 module_platform_driver
      • 2.3.3 LCD驱动的结构与步骤
  • 3、驱动程序实例
  • 4、测试LCD显示的应用程序
  • 写在结尾
  • 参考

1、 Frmebuffer(帧缓冲)操作介绍

1.1 显示设备的抽象

\qquad Linux是工作在保护模式下,Linux抽象出FrameBuffer这个设备来供用户态进程实现直接写屏。

\qquad Framebuffer机制模仿显卡的功能,将显卡硬件结构抽象掉,可以通过Framebuffer的读写直接对显存进行操作。用户可以将Framebuffer看成是显示内存的一个映像,将其映射到进程地址空间之后,就可以直接进行读写操作,而写操作可以立即反应在屏幕上。这种操作是抽象的,统一的。用户不必关心物理显存的位置、换页机制等等具体细节。这些都是由Framebuffer设备驱动来完成的。

1.2 内存映像

\qquad 显示画面的输出,实际是通过往显存里面写像素数据来实现的。由于显存实际是处于内核态的物理内存,所以下一步要把这块物理内存映射到用户态,这样应用程序就可以直接操作这块物理内存了。
\qquad 内存的映像是在使用时,由使用者通过mmap命令实现的。

1.3 输出画面数据

\qquad 我们有了显存之后,要如何才能将画面数据写入显存了?
\qquad 假设我们当前的环境, xres_virtual、yres_virtual分别为800,960;bpp(像素深度)为32位;所以每个像素用一个int来表示,虚拟屏幕尺寸为800*960像素。
\qquad 显存中,数据排布的顺序就是按照虚拟屏幕中像素数据从上到下,从左到右的数据来排布。而每一个像素数据则按照A(透明度)、R(红)、G(绿)、B(蓝)的顺序排布的。
在这里插入图片描述

1.4 用户态下操作屏显

1.4.1 用文件I / O 操作屏显

在Linux环境下,可以通过framebuffer设备文件(/dev/fb0等)来操作LCD屏幕。具体步骤如下:

  1. 打开framebuffer设备文件:
int fbfd = open("/dev/fb0", O_RDWR);
  1. 通过ioctl()来获取framebuffer参数,如屏分辨率,像素格式等:
struct fb_var_screeninfo vinfo;
ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo);
  1. 通过mmap()来映射LCD屏幕的显存到用户空间,得到一个指针fbp:
void *fbp = mmap(0 , vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8 , PROT_READ | PROT_WRIT E, MAP_SHARED, fbfd, 0);
  1. 可以通过fbp指针来直接操作显存,实现画点,画线,填充等功能。
  2. 通过ioctl(FBIOPUT_VSCREENINFO)来修改vinfo参数,实现Resolution修改,色深变化等功能。
  3. unmap显存:
munmap(fbp, vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8);
  1. 关闭framebuffer设备文件:
close(fbfd);

总之,通过framebuffer设备,我们可以获得屏幕信息,映射显存,直接操作显存来刷屏,这就是不使用GUI的原生LCD屏幕操作方法。

1.4.2 mmap() 函数

\qquad mmap是Linux系统调用,用于映射设备(如文件)的权限到进程的地址空间。对于framebuffer设备,我们可以通过mmap来映射LCD屏幕的显存到进程的用户空间,然后就可以直接操作显存来刷新屏幕。
mmap的原型如下:

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

参数说明:

  • addr: 要映射的内存块的起始地址,一般设为NULL让系统选择地址
  • length: 要映射的内存块的大小
  • prot: 设定内存块的访问权限,如PROT_READ、PROT_WRITE、PROT_EXEC等
  • flags: 设定映射类型,如MAP_SHARED、MAP_PRIVATE等
  • fd: 要映射的文件描述符
  • offset: 文件映射的偏移量

对于framebuffer,主要步骤如下:

  1. 打开/dev/fb0获取文件描述符fd
  2. 通过ioctl获取屏参数,如分辨率、色深等,计算映射大小length
  3. mmap映射:
void *fbp = mmap(0, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 
  • 这里我们要读写显存,所以权限为PROT_READ | PROT_WRITE
  • 映射类型为MAP_SHARED,因为多个进程可能要同时访问屏幕
  • 偏移量offset为0,从文件开始映射
  1. fbp就是映射到用户空间的显存指针,可以直接操作它来刷屏
  2. 用munmap释放映射:
munmap(fbp, length);

mmap的优点是可以直接操作物理内存,速度快;优点是会占用内存空间,并且映射和unmap也需要时间。
所以简单来说,mmap实现的是文件(物理内存)到进程虚拟地址空间的映射,我们可以通过虚拟地址直接操作文件(物理内存)。

1.4.3 ioctl()函数

\qquad ioctl()系统调用用于在用户空间和驱动空间之间传递信息。对于framebuffer设备,我们可以通过ioctl来获取和修改LCD屏的相关参数。主要的ioctl命令如下:

  • 1、 FBIOGET_VSCREENINFO: 获取可变屏幕参数,如分辨率、色彩模式等.
    从字面上可以理解为“fb ioctl, get variable screen info”:获取应用程序可改变的参数(如设定的分辨率)

用法:

#include <linux/fb.h>struct fb_var_screeninfo vinfo;
ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo);

用法:

ioctl(fbfd, FBIOPUT_VSCREENINFO, &vinfo);
  • 3、 FBIOGET_FSCREENINFO:获取固定屏参数,对应的数据结构是fb_fix_screeninfo,包括帧缓冲区大小等信息。
    从字面上理解“fb ioctl, get fixed screen info”:获取固定的参数(如屏幕的分辨率)
    用法:
struct fb_fix_screeninfo finfo;  
ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo);
  • 4、 FBIOBLANK: 设置屏幕的blanking参数,用于开启或关闭屏幕显示。
    用法:
int blank_mode = FB_BLANK_UNBLANK;   //开屏
ioctl(fbfd, FBIOBLANK, blank_mode);
  • 5、 FBIOGETCMAP: 获取颜色映射表
    用法:
struct fb_cmap cmap;
ioctl(fbfd, FBIOGETCMAP, &cmap); 
  • 6、 FBIOPUTCMAP: 设置颜色映射表
    **用法:**同 FBIOGETCMAP

  • 7、 FBIOGET_CON2FBMAP: 获取虚拟控制台到framebuffer的映射
    用法:

__u32 console_fb_map[MAX_NR_CONSOLES];
ioctl(fbfd, FBIOGET_CON2FBMAP, console_fb_map);
  • 8、 FBIOPUT_CON2FBMAP: 设置虚拟控制台到framebuffer的映射
    **用法:**同FBIOGET_CON2FBMAP

  • 9、. FBIOGET_VBLANK: 获取垂直空白中断相关参数
    用法:

struct fb_vblank vblank;
ioctl(fbfd, FBIOGET_VBLANK, &vblank);  

1.4.5 用命令操作屏

framebuffer的设备文件一般是/dev/fb0、/dev/fb1等等。

可以用命令: dd if=/dev/zero of=/dev/fb 清空屏幕.

如果显示模式是1024x768-8 位色,用命令:
dd if=/dev/zero of=/dev/fb0 bs=1024 count=768 清空屏幕;

用命令: dd if=/dev/fb0 of=fbfile 可以将fb中的内容保存下来;

可以重新写回屏幕: dd if=fbfile of=/dev/fb

在使用Framebuffer时,Linux是将显卡置于图形模式下的。

1.4.6 测试程序

在测试前,可以先在系统中查看lcd参数

输入 cat /sys/class/graphics/fb0/modes即可查看分辨率
输入cat /dev/urandom > /dev/fb0即可知晓fb是否能正常工作

fb_test.c
该程序在用户态打开fb0,然后,用ioctl函数读出fb可变参数与固定参数。然后用两个函数在framebuff上绘制背影和线条

编译正确后,需要切换到tty模式运行测试程序:
1、按ctrl+alt+F1 进入tty模式
2、登录,进入工作目录内
3、运行./fb_test.efl
4、运行结束后,按ctrl+alt+F7退出,回到x server状态


2、Framebuffer总体框架

2.1 框架要点

  • Framebuffer(帧缓冲)是Linux系统为显示设备提供的一个接口,它将显示缓冲区抽象,并屏蔽图像硬件的底层差异,允许上层应用程序在图形模式下直接对显示缓冲区进行读写操作。
    • framebuffer帧缓冲(简称fb)是一个platform类型设备,设备文件位于/dev/fbn。(n或以是0,1,…)
    • 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

在这里插入图片描述

\qquad 上图是Linux帧缓冲设备驱动结构是一个四层结构。从下到上分别为硬件(LCD控制器)、设备驱动(需要使用者开发)、fbmem层(系统已完成)、应用层(需要使用者开发应用程序)。而与驱动框架密切相关的就是中间这两层fbmem层和设备驱动层。
\qquad fb的结构由内核中的fb框架实现一部分(上图中的fbmem.c),然后再由设备驱动本身实现一部分(图中的xxxfb.c)。设备驱动本身就是一个普通的platform总线驱动 。

  • f b m e m 层 \color{red}{fbmem层} fbmem主要完成了“上传下达”的任务:
    • 1、向下获buffer地址。
    • 2、创建设备类(graphics)。
    • 3、注册字符设备主设备号。
    • 4、向上提供操作函数接口。
  • 驱动设备层主要完成了“驱动”的任务:
    • 1、创建buffer。
    • 2、构建设备模型(完成数据结构fb_info填写)
    • 3、设置LCD控制器的寄存器。
    • 4、实现设备的操作接口fb_ops。

下面这图是另一个角度来描述分层的概念。更宏观一点,有助于参考:

在这里插入图片描述

2.2 fbmem.c分析

\qquad fbmem.c实质也是一个驱动模块,是在linux系统启动时自动加载的。对于驱动模块,我们分为两个方面来分析,一是驱动的启动入口__init函数,启动时做了什么。二是fbmem提供了什么接口和能力,为LCD的驱动模块提供了哪些能力。

2.2.1 fbmem的入口分析

对于驱动程序首先从其入口的__init 函数分析起,这个__init 入口函数是在该驱动被加载时首先运行的。源码如下:

在这里插入图片描述

上图可以看出,fbmem启动时只做了几个简单的工作:

  • 1、创建了/proc/fb文件,并关联了struct file_operations fb_proc_fops结构体,用于绑定/proc中操作fb文件的相关函数。
  • 2、创建了一个主设备号为29的主设备(注意,这里只是建立了主设备,没有具体的fb设备),并关联了fb_fops,用于向用户层使用/dev/fbx设备提供接口。
  • 3、创建了一个设备类,生成一个类文件/sys/class/graphics
    (具体的上面的三个函数 可以看下面的4.2.3节。)

\qquad fbmem在启动初始化阶段只做了一些很简单的共性的工作。重要的是创建了主设备号29,并向用户层提供了两个操作接口。

2.2.2 fbmem的接口功能

fbmem的接口,有对上(用户层)的接口,也有对(LCD控制器)向下的接口。

2.2.2.1 向上的接口

提供了用户层操作的接口函数:
向上的接口有两个,一个是与用户操作相关的fb_fops这个结构体变量。提供了默认的open,read,ioctrl等操作接口。因此下面主要讨论的是这个接口。另一个向上接口是对内存文件系统/proc中的fb进行操作的接口。

在fbmem.c中,fb_fops和fb_proc_fops是两个不同的结构体变量,分别用于不同的用途,源码中定义如下:
在这里插入图片描述

fb_fops作用fb_proc_fops作用
\qquad fb_fops是用于定义fb设备在文件系统中的普通文件操作的结构体变量,它定义了打开、读取、写入、寻址等操作的回调函数。这个结构体变量被用于处理用户对fb设备文件的操作,例如通过open()函数打开设备文件、通过read()函数读取设备文件的内容等。 \qquad 而fb_proc_fops是用于定义fb设备在/proc文件系统中的文件操作的结构体变量。在Linux内核中,/proc文件系统是一种特殊的文件系统,用于提供内核和进程的运行时信息。/proc/fb是一个特殊的文件,它提供了有关帧缓冲设备的一些信息,如设备名称、设备类型、设备大小等。

\qquad 
可以看到,fb_fops和fb_proc_fops是用于不同的文件系统和不同的操作类型的结构体变量。它们分别定义了对应于不同文件系统中的fb设备的文件操作。这样设计的目的是为了能够根据不同的文件系统和操作类型,提供不同的文件操作处理方式,以满足不同的需求和场景。

提供了用户层操作的数据接口:

struct fb_info *registered_fb[FB_MAX] 

\qquad struct fb_info *registered_fb[FB_MAX]是一个全局数组,用于存储已注册的帧缓冲设备的帧缓冲信息结构体。
\qquad 在Linux的帧缓冲设备框架中,struct fb_info结构体代表了一个帧缓冲设备的信息。该结构体包含了帧缓冲设备的各种属性和状态,如设备名称、设备类型、设备大小、显存地址、像素格式等。
\qquad registered_fb数组的作用是用于管理已注册的帧缓冲设备。当一个帧缓冲设备被成功注册时(在帧缓冲设备框架中,通过register_framebuffer()函数将一个帧缓冲设备注册到内核中,并将其struct fb_info结构体存储在registered_fb数组中的一个位置上。),内核就可以通过访问registered_fb数组来获取已注册的帧缓冲设备的信息。
 当需要访问已注册的帧缓冲设备时,可以通过遍历registered_fb数组来获取相应的帧缓冲信息。

\qquad registered_fb数组的大小是由宏FB_MAX定义的,它表示系统中最大支持的帧缓冲设备数量。通过限制registered_fb数组的大小,可以限制系统中可以注册的帧缓冲设备的数量。
\qquad 在实际过程中,不同的帧缓冲设备的fb_info结构体是存入以次设备号做为下标的registered_fb数组中。例如/dev/fb0的fb_info信息是存在registered_fb[0]中。

2.2.2.2 向下的接口

fbmem.c向lcd的驱动层提供了一个重要的注册接口register_framebuffer(),该函数主要功能是将其参数info指向的帧缓冲设备信息添加到内核的帧缓冲设备列表中并进行注册。

\qquad 具体来说,register_framebuffer()函数执行以下操作:

  • 1.检查帧缓冲设备信息的有效性,比如检查是否提供了必需的回调函数(如刷新屏幕函数fb_ops->fb_pan_display、设置显示模式函数fb_ops->fb_set_par等)。
  • 2.为帧缓冲设备信息分配内存,并将设备信息的指针存储在内核中的帧缓冲设备列表(registered_fb[])的一个位置上。
  • 3.调用设备驱动程序的初始化函数(如果存在的话),完成设备的初始化工作。
  • 4.根据设备信息中指定的显存地址和大小等信息,为帧缓冲设备分配显存空间。
  • 5.注册帧缓冲设备到内核的帧缓冲子系统,使得应用程序可以使用对应设备的帧缓冲操作接口。
通过registered_fb[]数组,内核可以追踪已注册的帧缓冲设备,并提供对它们进行管理、控制和访问的功能。
需要注意的是,注册帧缓冲设备需要具有相应的权限,通常需要在内核初始化期间或者使用特权用户执行才能成功注册。

2.2.3 相关函数详解

2.2.3.1 proc_create()函数

\qquad 在Linux内核中,函数proc_create()是用于创建/proc这个内存文件系统中的文件的函数。它的声明位于<linux/proc_fs.h>头文件中。以下是proc_create()函数的声明:

struct proc_dir_entry *proc_create(const char *name, umode_t mode, struct proc_dir_entry *parent, const struct file_operations *proc_fops);

函数的参数

- 
1. name:要创建的文件的名称。这应该是一个唯一的字符串,作为文件在/proc目录下的名称。


- 2. mode:文件的访问权限模式(例如,S_IRUGO | S_IWUGO表示可读写文件)。这些模式定义在<linux/stat.h>头文件中。

- 
3. parent:指向父目录的指针,即/proc中包含新文件的目录。

- 
4. proc_fops:指向struct file_operations结构体的指针,它包含文件在被读/写时将调用的回调函数。该结构体包含了一个文件的各种操作函数,例如读取(read())、写入(write())和关闭(release())。
函数的功能:
\qquad 创建一个新的文件,并将其添加到/proc文件系统中。创建的文件将在通过parent参数指定的目录中可见。通过设置适当的回调函数来响应文件的读取和写入操作。
通过proc_create()函数创建的文件将在/proc中出现为一个虚拟的文件,并且可以通过相应的读写操作进行处理。这样,用户空间程序可以通过读取/proc文件系统来获取内核状态和信息。
需要注意的是,proc_create()函数已经被标记为弃用,并不推荐在新的内核代码中使用。代替的推荐方法是使用proc_create_data()函数或更高级的proc_create_single_data()函数,它们提供了更丰富的功能和更好的安全性。

2.2.3.2 fb_proc_fops结构体变量

原型:

static const struct file_operations fb_proc_fops = {.owner		= THIS_MODULE,.open		= proc_fb_open,.read		= seq_read,.llseek		= seq_lseek,.release	= seq_release,
};

\qquad 在Linux内核版本3.14中,fbmem.c文件中定义了一个名为fb_proc_fops的结构体变量,其类型为struct file_operations。
\qquad 这个结构体变量用于定义对应于在/proc文件系统中的fb设备请求的文件操作。

\qquad 在这个特定的结构体变量中,定义了以下几个函数指针:

  1. .owner:指向fb设备对应的内核模块的指针。
  2. .open:打开设备的回调函数,用于处理打开设备的操作。
  3. .read:读取设备的回调函数,用于处理从设备中读取数据的操作。
  4. .llseek:寻址设备的回调函数,用于在设备中寻址位置的操作。
  5. .release:释放设备的回调函数,用于处理释放设备的操作。

    \qquad 其中,THIS_MODULE宏作为.owner参数,表示当前fb设备模块是这个文件操作的所有者。其他的回调函数则是指向相应的处理函数。

    \qquad 这个结构体变量fb_proc_fops在fbmem.c文件中的作用是定义了对应于fb设备在/proc文件系统中的操作。当用户在/proc文件系统中访问fb设备时,内核将按照这个结构体变量中指定的函数来处理相应的操作,例如打开、读取、寻址和释放等。
通过这个结构体变量,fb设备模块可以响应相关的文件操作,以对用户的请求做出适当的响应。这种机制允许用户通过文件系统的方式与设备进行交互,提供了一种统一的接口来管理和控制fb设备。

2.2.3.3. register_chrdev()函数

\qquad 在Linux内核3.14中,函数register_chrdev()用于注册字符设备驱动程序。它的声明位于<linux/fs.h>头文件中。以下是register_chrdev()函数的声明:

int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops);

参数解释如下:

- 
1. major:设备的主设备号。如果指定为0,则会由内核动态分配可用的主设备号。

- 
2. name:设备的名称。这个名称将作为设备的标识,可以在/dev目录下找到相应的设备文件。

- 
3. fops:指向struct file_operations结构体的指针,其中包含了为设备定义的各种操作函数。

返回值
\qquad 表示注册结果,返回一个负值表示注册失败,返回一个非负值表示注册成功,并返回已分配或动态分配的主设备号。

功能:
\qquad register_chrdev()函数的功能是向内核注册字符设备驱动程序。注册后,操作系统将分配一个设备文件并将其与驱动程序关联起来,这将使其可以与用户程序进行通信。
字符设备驱动程序通常涉及到文件操作,例如打开、关闭、读取和写入。通过提供适当的回调函数,例如open()、release()、read()和write(),驱动程序可以响应这些操作。


需要注意
\qquad register_chrdev()函数已经被标记为弃用,并不推荐在新的内核代码中使用。代替的推荐方法是使用register_chrdev_region()函数或更高级的alloc_chrdev_region()函数,它们提供更灵活的设备号管理方式。此外,还可以使用字符设备框架中的设备模型进行设备注册和管理。

2.2.3.4 class_create()函数

在Linux内核3.14中,函数class_create()用于在/sys/class目录下创建和注册一个新的设备类。它的声明位于<linux/device.h>头文件中。以下是class_create()函数的声明:

struct class *class_create(struct module *owner, const char *name);

参数解释如下:


 1、owner:指向拥有该类的内核模块的指针。通常使用THIS_MODULE宏作为参数,表示当前模块是该类的所有。


 2、name:设备类的名称。这个名称将作为设备类的标识,会出现在/sys/class目录下。

返回值:
\qquad 是一个指向struct class结构体的指针,代表创建的设备类。如果创建失败,将返回一个错误指针。

函数的功能:
\qquad 是创建并注册一个新的设备类。设备类是用于管理一组相关设备的集合。通过创建设备类,可以将一组具有相似功能或属性的设备进行分组,并在/sys/class目录下创建相应的子目录。
创建设备类后,可以使用device_create()函数在设备类下创建具体的设备实例,并将其与相应的设备文件进行关联。
需要注意的是,class_create()函数在创建并注册设备类时,还会自动在/sys/class目录下创建与设备类同名的子目录,用于存放该类的具体设备实例。同时,该函数会创建和注册相关的属性文件,用于获取和设置设备类的属性。
对于设备驱动开发者来说,使用设备类是一种将相关设备进行组织和分类的有效方式,可以更好地管理和控制设备。

2.2.3.5 register_framebuffer()函数

在Linux 3.14中,register_framebuffer()函数用于注册一个帧缓冲设备,并将其添加到内核的帧缓冲设备列表中。

函数原型:

int register_framebuffer(struct fb_info *info);

参数:

  • info:指向帧缓冲设备信息的指针,类型为struct fb_info。帧缓冲设备信息结构体struct fb_info包含了与该设备相关的各种信息,例如显存地址、显存大小、像素格式、分辨率等。

返回值:

  • 成功时,函数返回值为0,表示设备注册成功。
  • 失败时,函数返回值为负数,表示设备注册失败。


功能:
\qquad register_framebuffer()函数的主要功能是将其参数info指向的帧缓冲设备信息添加到内核的帧缓冲设备列表中并进行注册。

2.3 LCD驱动分析(设备树匹配的platform平台驱动)

\qquad LCD驱动是platform平台驱动。框架难度不大,麻烦的是LCD有大量的硬件配置需要设置。
\qquad LCD驱动的核心能力在lcd_probe()函数,而初始化的 lcd_init()函数则是简单调用platform_driver_register()注册平台设备。本驱动采用platform的设备树匹配模式。驱动模块在切尔西成功则去调用lcd_probe()函数,完成驱动的主要能力。

这里主要参考了这编文章来详细说明LCD 驱动,由于都是用了exynos4412内核,因此参数的设置有很多相同,同时更正了文章中的一些错误以及增加了fs4412主板上的相应适配的内容。

2.3.1 设备树相关内容

\qquad 由于驱动采用的是设备树匹配。因此,驱动的第一步是正确设置设备树相关的节点。

由于exynos4412的设备树节点分布在几个不同的dtsi文件中,显得比较分散,这里一一列出,阅读时,注意节点的层次。

在这里插入图片描述

以上的树节点需要一一对照,没有的要一一补上。这里不再细说。因为以上内容,要细说,可以单独再开一篇了。

2.3.2 数据结构及函数

struct fb_info

这个结构体fb_info定义了Linux内核中关于帧缓冲设备(Framebuffer)的所有信息。
头文件/include/linux/fb.h

struct fb_info {atomic_t count;  这个原子计数器记录了当前打开此帧缓冲设备的进程数int node;  numenode节点编号。对于支持NUMA的系统来说,这个值定义了帧缓冲设备所在的节点。int flags;   一些标志位,定义此帧缓冲设备的一些属性int fbcon_rotate_hint;   一个提示值,默认情况下为-1,由驱动器设置为FB_ROTATE_*值,如果它知道lcd没有垂直安装,fbcon应该旋转进行补偿。struct mutex lock;		      两个互斥锁,lock用于open/release/ioctl操作 struct mutex mm_lock;		  mm_lock用于fb_mmap和smem_*字段的访问。struct fb_var_screeninfo var;	 可变参数结构体struct fb_fix_screeninfo fix;	 LCD固定参数结构体struct fb_monspecs monspecs;	 LCD显示器规格描述了当前显示器的规格信息,如制造商、型号等。struct work_struct queue;	  帧缓冲事件队列一个工作队列,用于在中断上下文中排队和调度非中断上下文的Framebuffer事件。struct fb_pixmap pixmap;	  图像硬件mapper,  pixmap用于硬件上映射的图像,struct fb_pixmap sprite;	  光标硬件mapper ,sprite用于光标的硬件映射。struct fb_cmap cmap;		  当前的颜色表cmap用于描述帧缓冲设备的当前色彩映射表。struct list_head modelist;      /* mode list */struct fb_videomode *mode;	  当前的显示模式*/#if IS_ENABLED(CONFIG_FB_BACKLIGHT)struct backlight_device *bl_dev ;   帧缓冲区注册前设置的指定背光设备,注销后删除struct mutex bl_curve_mutex;	    背光水平曲线u8 bl_curve[FB_BACKLIGHT_LEVELS];   背光调整
#endif#ifdef CONFIG_FB_DEFERRED_IOstruct delayed_work deferred_work;struct fb_deferred_io *fbdefio;
#endifstruct fb_ops *fbops;        对底层硬件操作的函数指针struct device *device;		 父设备指针struct device *dev;		     本fb设备int class_flag;              /* private sysfs flags */
#ifdef CONFIG_FB_TILEBLITTINGstruct fb_tile_ops *tileops;    图块Blitting(位图)
#endifunion {char __iomem *screen_base;	  虚拟基地址 char *screen_buffer;          “联合体,这两个互相覆盖”};unsigned long screen_size;	   LCD IO映射的虚拟内存大小,重新映射的VRAM数量或0void *pseudo_palette;16色颜色表
#define FBINFO_STATE_RUNNING	0   
#define FBINFO_STATE_SUSPENDED	1u32 state;			  LCD的挂起或恢复状态,值为上面这两个宏之一void *fbcon_par;                /* fbcon use-only private area */从这里开始,往下,一切都依赖于设备void *par;我们需要PCI或类似的光圈基础/大小,而不是smem_start/size,因为smem_start可能只是分配在光圈内部的对象,因此实际上可能不会重叠struct apertures_struct {unsigned int count;struct aperture {resource_size_t base;resource_size_t size;} ranges[0];} *apertures;bool skip_vt_switch; /* no VT switch on suspend/resume required */
};

在fb_info结构体中,flags字段是一个整数,它包含一些标志位,用于表示Framebuffer设备的某些属性。
标志位定义在<linux/fb.h>头文件中,如:

#define FBINFO_MODULE		0x0001  /* Low-level driver is a module *//*低级驱动器是一个模块*/
#define FBINFO_HWACCEL_DISABLED	0x0002    /*设置FBINFO_HWACCEL_DISABLED时:*硬件加速被关闭。所需函数(copyrea()、fillrect()和imageblit())的软件实现 取而代之;加速发动机应处于静止状态*/#define FBINFO_VIRTFB		0x0004        /*FB是系统RAM,而不是设备*/
#define FBINFO_PARTIAL_PAN_OK	0x0040    /*otw只使用pan进行双重缓冲*/
#define FBINFO_READS_FAST	0x0080  /* soft-copy 比渲染快  /*硬件支持的操作*/
/*语义:当设置了一个位时,表示硬件加速了操作。即使未设置位,所需的功能仍将工作。*如果没有设置标志位,则可选功能甚至可能不存在。*/#define FBINFO_HWACCEL_NONE		0x0000     表示帧缓冲设备不支持硬件加速。
#define FBINFO_HWACCEL_COPYAREA		0x0100  表示帧缓冲设备支持copyarea硬件加速,用于高效地复制区域。
#define FBINFO_HWACCEL_FILLRECT		0x0200  表示帧缓冲设备支持fillrect硬件加速,用于高效地填充矩形区域。

#define FBINFO_HWACCEL_IMAGEBLIT	0x0400  表示帧缓冲设备支持imageblit硬件加速,用于高效地传输图像数据。

#define FBINFO_HWACCEL_ROTATE		0x0800  表示帧缓冲设备支持旋转操作的硬件加速。这是可选的功能。

#define FBINFO_HWACCEL_XPAN		0x1000 /* optional */表示帧缓冲设备支持水平平移的硬件加速。这是可选的功能。

#define FBINFO_HWACCEL_YPAN		0x2000 /* optional */表示帧缓冲设备支持垂直平移的硬件加速。这是可选的功能。

#define FBINFO_HWACCEL_YWRAP		0x4000 /* optional */表示帧缓冲设备支持垂直循环滚动的硬件加速。这是可选的功能。

#define FBINFO_MISC_USEREVENT          0x10000 /* event request来自用户空间*/表示帧缓冲设备接收到的事件请求来自用户空间。

#define FBINFO_MISC_TILEBLITTING       0x20000 /* use tile blitting */表示帧缓冲设备使用瓦片传输进行加速。
#define FBINFO_MISC_ALWAYS_SETPAR   0x40000    此标志用于指示在每次切换控制台时都调用set_par函数。这可以确保在依赖于正确的硬件状态或更改该状态的任何函数之前,set_par函数始终被调用。但如果set_par函数执行较慢,会导致控制台切换的延迟增加。

#define FBINFO_MISC_FIRMWARE        0x80000     /*其中fb是一个固件驱动程序,可以用合适的驱动程序替换*/该标志表示帧缓冲驱动程序是一个固件驱动程序,并可被适当的驱动程序替换。

*/
#define FBINFO_FOREIGN_ENDIAN	0x100000  /*主机和GPU端序不同。*/表示主机和GPU的字节序不同。

#define FBINFO_BE_MATH  0x100000  大端序。这与上面的标志相同,但含义不同,由fb子系统根据FOREIGN_ENDIAN标志和主机端序设置。驱动不应使用此标志。
#define FBINFO_CAN_FORCE_OUTPUT     0x200000     向VT层报告此fb驱动程序可以接受像oopes一样的强制控制台输出

struct fb_var_screeninfo 和 struct fb_fix_screeninfo

在头文件include/uapi/linux/fb.h 中定义

struct fb_var_screeninfo

fb_var_screeninfo结构体主要记录用户可以修改的控制器的参数,比如屏幕的分辨率和每个像素的比特数等,该结构体定义如下:

#include <uapi/linux/fb.h>
struct fb_var_screeninfo {__u32 xres;		        可见屏幕一行有多少个像素点visible resolution__u32 yres;		        可见屏幕一列有多少个像素点__u32 xres_virtual;		虚拟屏幕一行有多少个像素点。虚拟屏幕是在硬件上模拟的屏幕,可以比可见屏幕更大。
	__u32 yres_virtual;		虚拟屏幕一列有多少个像素点。和xres_virtual一样,这个成员存储虚拟屏幕的垂直分辨率。__u32 xoffset;			    虚拟屏幕到可见屏幕之间的行偏移。当虚拟屏幕大于可见屏幕时,这个成员指定了虚拟屏幕相对于可见屏幕的水平偏移。__u32 yoffset;			    虚拟屏幕到可见屏幕之间的列偏移。和xoffset一样,这个成员指定了虚拟屏幕相对于可见屏幕的垂直偏移。		__u32 bits_per_pixel;		每个像素的位数,即BPP(Bits Per Pixel)。这个成员存储屏幕中每个像素的位数,用于确定图像的颜色深度。			__u32 grayscale;0时,表示图像为灰度图像。当grayscale为0时,表示图像为彩色图像。   		struct fb_bitfield red;		 表示帧缓冲中的红、绿、蓝位域。这些位域用于存储真彩色图像的每个像素的颜色值。struct fb_bitfield green;	 struct fb_bitfield blue;	 struct fb_bitfield transp;	  透明度位域。如果图像支持透明度,这个位域用于存储每个像素的透明度值。			__u32 nonstd;0时,表示非标准像素格式。这个成员用于区分非标准的像素格式。__u32 activate;			    用于指定激活显示的方式,可以是FB_ACTIVATE_NOW(立即激活)或FB_ACTIVATE_FORCE(强制激活)。看fb.h中的 FB_ACTIVATE_*宏		__u32 height;			 图像的高度,以毫米为单位。这个成员用于描述图像在屏幕上的实际高度。__u32 width;			 图像的宽度,以毫米为单位。和height一样,这个成员用于描述图像在屏幕上的实际宽度。__u32 accel_flags;		/* (OBSOLETE) see fb_info.flags *//* Timing: All values in pixclocks, except pixclock (of course) */定时:除了pixclock本身外,其他的都以像素时钟为单位__u32 pixclock;			 像素时钟,这个成员存储像素时钟的频率,用于计算图像的时序信息。__u32 left_margin;		 行切换,从同步到绘图之间的延迟 time from sync to picture	__u32 right_margin;		 行切换,从绘图到同步之间的延迟 time from picture to sync	__u32 upper_margin;		 帧切换,从同步到绘图之间的延迟 time from sync to picture	__u32 lower_margin;      帧切换,从绘图到同步之间的延迟__u32 hsync_len;		 水平同步的长度。它们表示同步信号的持续时间。__u32 vsync_len;		 垂直同步的长度。它们表示同步信号的持续时间。__u32 sync;			         示同步的方式,可以是FB_SYNC_HOR_HIGH_ACT(水平同步信号为高电平活动)或FB_SYNC_VERT_HIGH_ACT(垂直同步信号为高电平活动)等。看fb.h中的 FB_SYNC_*宏__u32 vmode;			    显示模式。它指定了显示器的模式,可以是FB_VMODE_NONINTERLACED(非隔行模式)或FB_VMODE_INTERLACED(隔行模式)等。参考fb.h中的 FB_VMODE_*宏		__u32 rotate;			以逆时针方向旋转的角度。它表示图像在屏幕上显示时的旋转角度。__u32 colorspace;		基于FOURCC的模式的颜色空间。它指定了基于FOURCC的模式的颜色空间,例如RGB或YUV。__u32 reserved[4];		保留Reserved for future compatibility 
};

**比较重要的可变参数有: **

  • xres、yres:可视画面的x、y轴分辨率(应用层改不了)
  • xres_virtual、yres_virtual:虚拟画面(即fb)x、y轴分辨率
  • xoffset、yoffset:可视画面相对于虚拟画面的x、y轴偏移量
  • bits_per_pixel:像素深度,每个像数的bit位数。
    在这里插入图片描述

虚拟画面的尺寸
虚拟画面一般可设为可视画面的两倍,这种结构被称之为“双缓冲机制”,这样做的好处是可以一边显示,一边缓冲下一幅画面 。

  • activate: 激活设定参数的方式。在fb.h中定义的宏。这些宏在设置帧缓冲设备的属性时,用于激活或改变属性的不同标志。下面是每个宏的具体含义:
    • FB_ACTIVATE_NOW(0):立即设置属性的值(或在垂直空白期设置)。
    • FB_ACTIVATE_NXTOPEN(1):在下一次打开设备时激活属性。
    • FB_ACTIVATE_TEST(2):不设置属性的值,将不可能的值四舍五入。
    • FB_ACTIVATE_MASK(15):用于掩码操作的值。
    • FB_ACTIVATE_VBL(16):在下一个垂直空白期激活属性的值。
    • FB_CHANGE_CMAP_VBL(32):在下一个垂直空白期更改颜色映射表。
    • FB_ACTIVATE_ALL(64):更改此帧缓冲设备上的所有虚拟控制台。
    • FB_ACTIVATE_FORCE(128):即使没有更改,也强制应用属性的设置。
    • FB_ACTIVATE_INV_MODE(256):使当前视频模式无效。
    • 这些标志可以用来指定在设置帧缓冲设备属性时应采取的行动。可以根据需要组合使用这些标志。

struct fb_fix_screeninfo

而fb_fix_screeninfo结构体又主要记录用户不可以修改的控制器的参数,比如屏幕缓冲区的物理地址和长度等,该结构体的定义如下:

struct fb_fix_screeninfo {char id[16];			 字符串形式的标示符  identification string eg "TT Builtin" unsigned long smem_start;	fb缓存的开始位置  Start of frame buffer mem  (physical address) __u32 smem_len;			    fb缓存的长度  Length of frame buffer mem __u32 type;			        看FB_TYPE_*	__u32 type_aux;			    分界 Interleave for interleaved Planes __u32 visual;			    看 FB_VISUAL_*		__u16 xpanstep;			    如果没有硬件panning就赋值为0 zero if no hardware panning  __u16 ypanstep;			    如果没有硬件panning就赋值为0  zero if no hardware panning  __u16 ywrapstep;		    如果没有硬件ywrap就赋值为0  zero if no hardware ywrap    __u32 line_length;		    一行的字节数  length of a line in bytes    unsigned long mmio_start;	内存映射IO的开始位置 /* Start of Memory Mapped I/O   *//* (physical address) */__u32 mmio_len;			内存映射IO的长度/* Length of Memory Mapped I/O  */__u32 accel;			/* Indicate to driver which	*//*  specific chip/card we have	*/__u16 capabilities;		/* see FB_CAP_*			*/__u16 reserved[2];		/* Reserved for future compatibility */
};

\qquad 这里比较常用的是:

  • smem_len : framebuff的长度
  • type : 类型,在fb.h中有如下的宏定义。
    • #define FB_TYPE_PACKED_PIXELS 0 /* Packed Pixels 表示使用“Packed Pixels”格式的像素数据。在此格式中,像素按照紧密排列的方式存储,每个像素占用固定数量的位或字节。这是一种常见的视频和图形数据存储格式。*/
    • #define FB_TYPE_PLANES 1 /* Non interleaved planes 表示使用“非交错平面”格式的像素数据。在此格式中,像素数据被分为多个独立的平面,每个平面存储一种颜色分量。这种像素排列方式在某些情况下可以提供更高的灵活性和图像质量。*/
    • #define FB_TYPE_INTERLEAVED_PLANES 2 /* Interleaved planes 表示使用“交错平面”格式的像素数据。在此格式中,像素数据以交错方式存储在不同的平面中。这种像素排列方式可以提供更高的内存访问效率。*/
    • #define FB_TYPE_TEXT 3 /* Text/attributes 表示使用“文本/属性”格式的像素数据。在此格式中,像素数据用于显示文本字符和相关属性(如前景色、背景色、样式等)。
*/
    • #define FB_TYPE_VGA_PLANES 4 /* EGA/VGA planes 表示使用“EGA/VGA平面”格式的像素数据。这种格式与 FB_TYPE_PLANES 类似,但特定于 VGA 图形适配器。
*/
    • #define FB_TYPE_FOURCC 5 /* Type identified by a V4L2 FOURCC 表示使用由 V4L2(Video for Linux 2)定义的 FOURCC(Four-Character Code)标识的像素数据类型。这种类型标识可以用于表示各种特定格式和压缩算法的像素数据。*/
  • visual : 可视化模式或像素模式,在fb.h中有如下的宏定义
    • #define FB_VISUAL_MONO01 0 /* Monochr. 1=Black 0=White 表示单色模式,像素值为0表示白色,像素值为1表示黑色。
*/
    • #define FB_VISUAL_MONO10 1 /* Monochr. 1=White 0=Black 表示单色模式,像素值为0表示黑色,像素值为1表示白色。
*/
    • #define FB_VISUAL_TRUECOLOR 2 /* True color 表示真彩色模式, 可以支持高分辨率图像显示。在此模式中,每个像素由红、绿、蓝三个颜色分量组成,可以使用更广泛的颜色范围,以呈现更真实的图像。*/
    • #define FB_VISUAL_PSEUDOCOLOR 3 /* Pseudo color (like atari) 表示伪彩色模式,类似于Atari ST的显示模式。在此模式中,每个像素的颜色值是通过颜色映射表(调色板)查找来确定的,显卡上的颜色映射表支持有限的颜色范围。*/
    • #define FB_VISUAL_DIRECTCOLOR 4 /* Direct color 表示直接彩色模式,像素的颜色将由红、绿、蓝三个颜色分量组成,但与真彩色不同,颜色分量的数量可能不同,且混合方式可能不同。此模式在显示彩色图像时提供了更高的位深度和精度。
*/
    • #define FB_VISUAL_STATIC_PSEUDOCOLOR 5 /* Pseudo color readonly 表示静态伪彩色模式,类似于伪彩色模式,但只读,即无法更改像素的颜色映射表。
*/
    • #define FB_VISUAL_FOURCC 6 /* Visual identified by a V4L2 FOURCC 表示被V4L2(Video for Linux 2)标识的特定像素格式。它允许使用V4L2 FOURCC(四字符码)来标识特定的像素编解码器和压缩算法,以支持多种图像格式。 */

struct fb_videomode结构体

(详见2.6.1)

fb_var_to_videomode()与 fb_videomode_to_var()函数

void fb_var_to_videomode(struct fb_videomode *mode,const struct fb_var_screeninfo *var)
{u32 pixclock, hfreq, htotal, vtotal;mode->name = NULL;mode->xres = var->xres;mode->yres = var->yres;mode->pixclock = var->pixclock;mode->hsync_len = var->hsync_len;mode->vsync_len = var->vsync_len;mode->left_margin = var->left_margin;mode->right_margin = var->right_margin;mode->upper_margin = var->upper_margin;mode->lower_margin = var->lower_margin;mode->sync = var->sync;mode->vmode = var->vmode & FB_VMODE_MASK;mode->flag = FB_MODE_IS_FROM_VAR;mode->refresh = 0;if (!var->pixclock)return;pixclock = PICOS2KHZ(var->pixclock) * 1000;htotal = var->xres + var->right_margin + var->hsync_len +var->left_margin;vtotal = var->yres + var->lower_margin + var->vsync_len +var->upper_margin;if (var->vmode & FB_VMODE_INTERLACED)vtotal /= 2;if (var->vmode & FB_VMODE_DOUBLE)vtotal *= 2;hfreq = pixclock/htotal;mode->refresh = hfreq/vtotal;
}
void fb_videomode_to_var(struct fb_var_screeninfo *var,const struct fb_videomode *mode)
{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;
}

struct fb_ops

fb_ops结构体是对底层硬件操作的函数指针,该结构体中定义了对硬件的操作有:

#include <linux/fb.h>
struct fb_ops {/* open/release and usage marking */struct module *owner;int (*fb_open)(struct fb_info *info, int user);int (*fb_release)(struct fb_info *info, int user);对于具有奇怪非线性布局或不适用于正常内存映射访问的帧缓冲区ssize_t (*fb_read)(struct fb_info *info, char __user *buf,size_t count, loff_t *ppos);ssize_t (*fb_write)(struct fb_info *info, const char __user *buf,size_t count, loff_t *ppos);检查可变参数并进行设置/* checks var and eventually tweaks it to something supported,DO NOT MODIFY PAR */int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info);根据设置的值进行更新,使之有效/* set the video mode according to info->var */int (*fb_set_par)(struct fb_info *info);设置颜色寄存器/* set color register */int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green,unsigned blue, unsigned transp, struct fb_info *info);/* set color registers in batch */int (*fb_setcmap)(struct fb_cmap *cmap, struct fb_info *info);显示空白/* blank display */int (*fb_blank)(int blank, struct fb_info *info);/* pan display */int (*fb_pan_display)(struct fb_var_screeninfo *var, struct fb_info *info);矩形填充/* Draws a rectangle */void (*fb_fillrect) (struct fb_info *info, const struct fb_fillrect *rect);复制数据/* Copy data from area to another */void (*fb_copyarea) (struct fb_info *info, const struct fb_copyarea *region);图形填充/* Draws a image to the display */void (*fb_imageblit) (struct fb_info *info, const struct fb_image *image);/* Draws cursor */int (*fb_cursor) (struct fb_info *info, struct fb_cursor *cursor);/* Rotates the display */void (*fb_rotate)(struct fb_info *info, int angle);/* wait for blit idle, optional */int (*fb_sync)(struct fb_info *info);/* perform fb specific ioctl (optional) */int (*fb_ioctl)(struct fb_info *info, unsigned int cmd,unsigned long arg);/* Handle 32bit compat ioctl (optional) */int (*fb_compat_ioctl)(struct fb_info *info, unsigned cmd,unsigned long arg);/* perform fb specific mmap */int (*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma);/* get capability given var */void (*fb_get_caps)(struct fb_info *info, struct fb_blit_caps *caps,struct fb_var_screeninfo *var);/* teardown any resources to do with this framebuffer */void (*fb_destroy)(struct fb_info *info);/* called at KDB enter and leave time to prepare the console */int (*fb_debug_enter)(struct fb_info *info);int (*fb_debug_leave)(struct fb_info *info);
};

宏 module_platform_driver

#include <linux/platform_device.h>
#define module_platform_driver(__platform_driver) \module_driver(__platform_driver, platform_driver_register, \platform_driver_unregister)
#include <linux/device.h>#define module_driver(__driver, __register, __unregister, ...) \
static int __init __driver##_init(void) \
{ \return __register(&(__driver) , ##__VA_ARGS__); \
} \
module_init(__driver##_init); \
static void __exit __driver##_exit(void) \
{ \__unregister(&(__driver) , ##__VA_ARGS__); \
} \
module_exit(__driver##_exit);

实例:
module_platform_driver(s3c_fb_driver)

#define module_driver(__driver, __register, __unregister, ...) \
static int __init s3c_fb_driver_init(void) \
{ \return platform_driver_register(&(s3c_fb_driver) , ##__VA_ARGS__); \
} \
module_init(s3c_fb_driver_init); \
static void __exit s3c_fb_driver_exit(void) \
{ \platform_driver_unregister(&(s3c_fb_driver) , ##__VA_ARGS__); \
} \
module_exit(s3c_fb_driver_exit);

2.3.3 LCD驱动的结构与步骤

在FrameBuffer框架下,LCD驱动的编写也是程式化了。
\qquad 首先,LCD驱动是一个标准的platform平台总线驱动,因此其驱动的总体结构就确定下来了,而匹配模式可以采用的是多种匹配方式,本文采用的是设备树匹配。
\qquad 其次,其probe函数是最重要的初始化函数,其具本步骤如下:

  • 1、分配一个fb_info
  • 2、设置fb_info数据结构
    • 2.1 设置 fix 固定的参数
    • 2.2 设置 var 可变的参数
    • 2.3 设置操作函数
    • 2.4 其他的设置
  • 3、 硬件相关的操作
    • 3.1 配置GPIO用于LCD
    • 3.2 根据LCD手册设置LCD控制器
    • 3.3 分配显存(framebuffer), 并把地址告诉LCD控制器
  • 4、注册

这里先给个初步的概念,关键是直接对照去阅读调试后的源码,里面已按上述步骤,又做了相对较细的注释。

3、驱动程序实例

//{% codeblock lang:c [lcd_drv.c] https://github.com/hceng/learn/blob/master/tiny4412/02_lcd_drv/lcd_drv.c %}
#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/workqueue.h>
#include <linux/wait.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/div64.h>
#include <asm/mach/map.h>
#include <linux/fb.h>
#include <asm/types.h>#define VIDCON0 0x00
#define VIDCON1 0x04
#define VIDTCON0 0x10
#define VIDTCON1 0x14
#define VIDTCON2 0x18
#define WINCON0 0x20
#define VIDOSD0C 0x48
#define SHADOWCON 0x34
#define WINCHMAP2 0x3c
#define VIDOSD0A 0x40
#define VIDOSD0B 0x44
#define VIDW00ADD0B0 0xA0
#define VIDW00ADD1B0 0xD0
#define CLK_SRC_LCD0 0x234
#define CLK_SRC_MASK_LCD 0x334
#define CLK_DIV_LCD 0x534
#define CLK_GATE_IP_LCD 0x934
#define LCDBLK_CFG 0x00
#define LCDBLK_CFG2 0x04
#define LCD_LENTH 1024   //800
#define LCD_WIDTH 600    //480
#define BITS_PER_PIXEL 32/**********调试用到的变量****/
int j;
static struct resource *res_debug;
/**********/static struct fb_info *fs4412_lcd;
static volatile void __iomem *lcd_regs_base;
static volatile void __iomem *lcdblk_regs_base;
static volatile void __iomem *lcd0_configuration;//Configures power mode of LCD0.0x10020000+0x3C80
static volatile void __iomem *clk_regs_base;static u32 pseudo_palette[16];
static struct resource *res0, *res1, *res2, *res3;/* from pxafb.c */
static inline unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf){chan &= 0xFFFF;//保留低16位chan >>= 16 - bf->length;//保留高bf->length位return chan << bf->offset;//返回保留的位,且在原位置
}static int cfb_setcolreg(unsigned int regno,   unsigned int red,    unsigned int green,   unsigned int blue,    unsigned int transp,     struct fb_info *info){unsigned int color = 0;uint32_t *p;color  = chan_to_field(red,   &info->var.red);color |= chan_to_field(green, &info->var.green);color |= chan_to_field(blue,  &info->var.blue);p = info->pseudo_palette;  p[regno] = color;return 0;
}static struct fb_ops fs4412_lcdfb_ops = {.owner = THIS_MODULE,.fb_setcolreg = cfb_setcolreg, //设置调色板,实现伪颜色表.fb_fillrect = cfb_fillrect, //填充矩形.fb_copyarea = cfb_copyarea, //数据复制.fb_imageblit = cfb_imageblit, //图形填充
};static int lcd_probe(struct platform_device *pdev){int ret;unsigned int temp;/* 1. 分配一个fb_info */fs4412_lcd = framebuffer_alloc(0, NULL);                        //不要额外空间设置私有数据if(!fs4412_lcd) {return  -ENOMEM;}/* 2. 设置 *//* 2.1 设置 fix 固定的参数 */strcpy(fs4412_lcd->fix.id, "s702");                              //设置fix名称fs4412_lcd->fix.smem_len = LCD_LENTH*LCD_WIDTH*BITS_PER_PIXEL/8; //显存的长度=分辨率*每象素字节数fs4412_lcd->fix.type     = FB_TYPE_PACKED_PIXELS;                //类型:填充式像素(常用在TFT屏幕)fs4412_lcd->fix.visual   = FB_VISUAL_TRUECOLOR;                  //TFT 真彩色fs4412_lcd->fix.line_length = LCD_LENTH*BITS_PER_PIXEL/8;        //每行的长度,以字节为单位/* 2.2 设置 var 可变的参数 */fs4412_lcd->var.xres           = LCD_LENTH;                      //x方向分辨率fs4412_lcd->var.yres           = LCD_WIDTH;                      //y方向分辨率fs4412_lcd->var.xres_virtual   = LCD_LENTH;                      //x方向虚拟分辨率fs4412_lcd->var.yres_virtual   = LCD_WIDTH;                      //y方向虚拟分辨率fs4412_lcd->var.xoffset        = 0 ;                   //x方向真实值和虚拟值得差值0fs4412_lcd->var.yoffset        = 0;                     //y方向真实值和虚拟值得差值fs4412_lcd->var.bits_per_pixel = BITS_PER_PIXEL;                 //每个像素占多少位RGB:888fs4412_lcd->var.red.length     = 8;fs4412_lcd->var.red.offset     = 16;   //红fs4412_lcd->var.green.length   = 8;fs4412_lcd->var.green.offset   = 8;    //绿fs4412_lcd->var.blue.length    = 8;fs4412_lcd->var.blue.offset    = 0;    //蓝fs4412_lcd->var.pixclock = 65000000;  //65MHZfs4412_lcd->var.left_margin = 140; //HBPfs4412_lcd->var.right_margin = 160; //HFPfs4412_lcd->var.upper_margin = 20;  //VBPfs4412_lcd->var.lower_margin = 12;  //VFPfs4412_lcd->var.hsync_len = 20;  fs4412_lcd->var.vsync_len = 3;fs4412_lcd->var.sync = ~FB_SYNC_HOR_HIGH_ACT | ~FB_SYNC_VERT_HIGH_ACT;fs4412_lcd->var.vmode = FB_VMODE_NONINTERLACED;fs4412_lcd->var.activate       = FB_ACTIVATE_NOW;      //使设置的值立即生效/* 2.3 设置操作函数 */fs4412_lcd->fbops              = &fs4412_lcdfb_ops;  //绑定操作函数/* 2.4 其他的设置 */fs4412_lcd->pseudo_palette     = pseudo_palette;       //存放调色板所调颜色的数组fs4412_lcd->screen_size        = LCD_LENTH * LCD_WIDTH * BITS_PER_PIXEL / 8;   //显存大小/* 3. 硬件相关的操作 *//* 3.1 配置GPIO用于LCD *///在设备树中,将 GPF0_0-GPF0_7、GPF1_0-GPF1_7、GPF2_0-GPF2_7、GPF3_0-GPF3_3//配置为了复用第二功能(LCD),禁止内部上拉,驱动强度配置设置为0;/* 3.2 根据LCD手册设置LCD控制器, 比如VCLK的频率等 *///寄存器映射/**********打印出所有resource*/for (j=0; j<pdev->num_resources;j++){res_debug = pdev->resource+j;printk("debug: resournces[%d],start:[%X], end:[%X],name:[%s],flags:[%X],parent[%p],sibling[%p],child[%p]\n",j,res_debug->start,res_debug->end,res_debug->name,(unsigned int)res_debug->flags,res_debug->parent,res_debug->sibling,res_debug->child);}/**********/res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0);if (res0 == NULL){printk("debug: lcd_driver.c->lcd_probe()  platform_get_resource A  error.\n");return -EINVAL;}lcd_regs_base = devm_ioremap_resource(&pdev->dev, res0);if (lcd_regs_base == NULL){printk("debug : lcd_driver.c -> lcd_probe() devm_ioremap_resource  A error.\n");return -EINVAL;}res1 = platform_get_resource(pdev, IORESOURCE_MEM, 1);if (res1 == NULL){printk("debug: lcd_driver.c->lcd_probe()  platform_get_resource B  error.\n");return -EINVAL;}lcdblk_regs_base = devm_ioremap_resource(&pdev->dev, res1);if (lcdblk_regs_base == NULL){printk("debug : lcd_driver.c -> lcd_probe() devm_ioremap_resource  B error.\n");return -EINVAL;}res2 = platform_get_resource(pdev, IORESOURCE_MEM, 2);if (res2 == NULL){printk("debug: lcd_driver.c->lcd_probe()  platform_get_resource C  error.\n");return -EINVAL;}/*devm_ioremap()和devm_ioremap_resource()区别:devm_ioremap()可以重复map相同的地址空间,devm_ioremap_resource()不可以。一般SoC的中,各个硬件模块各自的memory region都有严格的划分(比如说USB host的地址空间绝对不会和flash host冲突),所以一般的driver使用devm_ioremap()和devm_ioremap_resource()都行。但这里,应该系统已经映射过一次了,所以使用devm_ioremap_resource()会报错。*/lcd0_configuration = devm_ioremap(&pdev->dev, res2->start, resource_size(res2));  if (lcd0_configuration == NULL){printk("debug : lcd_driver.c -> lcd_probe() devm_ioremap_resource  C  error.\n");return -EINVAL;}*(unsigned long *)lcd0_configuration = 7; //Reset Value = 0x00000007 power on res3 = platform_get_resource(pdev, IORESOURCE_MEM, 3);if (res3 == NULL){printk("debug: lcd_driver.c->lcd_probe()  platform_get_resource D  error.\n");return -EINVAL;}//clk_regs_base = devm_ioremap_resource(&pdev->dev, res3);clk_regs_base = devm_ioremap(&pdev->dev, res3->start, resource_size(res3));  if (clk_regs_base == NULL){printk("debug : lcd_driver.c -> lcd_probe() devm_ioremap_resource  D  error.\n");return -EINVAL;}//时钟源选择\使能时钟//Selects clock source for LCD_BLK//FIMD0_SEL:bit[3:0]=0110=SCLKMPLL_USER_T=800Mtemp = readl(clk_regs_base + CLK_SRC_LCD0);temp &= ~(0x0F<<0);temp |= (0x3<<1);writel(temp, clk_regs_base + CLK_SRC_LCD0);//Clock source mask for LCD_BLK//FIMD0_MASK:Mask output clock of MUXFIMD0 (1=Unmask)temp = readl(clk_regs_base + CLK_SRC_MASK_LCD);temp |= (0x01<<0);writel(temp, clk_regs_base + CLK_SRC_MASK_LCD);//设置LCD_BLK的时钟分频    //SCLK_FIMD0 = MOUTFIMD0/(FIMD0_RATIO + 1),分频比 1/1temp = readl(clk_regs_base + CLK_DIV_LCD);temp &= ~(0x0F<<0);writel(temp, clk_regs_base + CLK_DIV_LCD);//Controls IP clock gating for LCD_BLK 时钟使能  //CLK_FIMD0:Gating all clocks for FIMD0 (1=Pass)temp = readl(clk_regs_base + CLK_GATE_IP_LCD);temp |= (0x01<<0);writel(temp, clk_regs_base + CLK_GATE_IP_LCD);//背光控制//FIMDBYPASS_LBLK0:FIMD of LBLK0 Bypass Selection (1=FIMD Bypass)temp = readl(lcdblk_regs_base + LCDBLK_CFG);temp |= (0x01<<1);writel(temp, lcdblk_regs_base + LCDBLK_CFG);//PWM设置//MIE0_DISPON:MIE0_DISPON: PWM output control (1=PWM outpupt enable)temp = readl(lcdblk_regs_base + LCDBLK_CFG2);temp |= (0x01<<0);writel(temp, lcdblk_regs_base + LCDBLK_CFG2);mdelay(1000);//VIDCON0的VCLK时钟设置//LCD时钟:  VCLK=FIMD*SCLK/(CLKVAL+1), where CLKVAL>=1//800/(19+1) == 40M<80Mtemp = readl(lcd_regs_base + VIDCON0);temp |= (19<<6);//temp |= (3<<6);writel(temp, lcd_regs_base + VIDCON0);/** * VIDCON1:* [5]:IVSYNC  ===> 1 : Inverted(反转)* [6]:IHSYNC  ===> 1 : Inverted(反转)* [7]:IVCLK   ===> 1 : Fetches video data at VCLK rising edge (上降沿触发)* [10:9]:FIXVCLK  ====> 01 : VCLK running* */temp = readl(lcd_regs_base + VIDCON1);temp |= (1 << 9) | (1 << 7) | (1 << 5) | (1 << 6);writel(temp, lcd_regs_base + VIDCON1);/** * VIDTCON0:* * [23:16]:  VBPD+1=tvb-tvpw=23-11=12 --> VBPD=11* * [15:8] :  VFPD+1=tvfp=22 --> VFPD=21* * [7:0]  :  VSPW+1=tvpw=1~20(暂取11) --> VSPW=10* */temp = readl(lcd_regs_base + VIDTCON0);//temp |= (11 << 16) | (21 << 8) | (10 << 0);temp |= (20 << 16) | (12 << 8) | (3 << 0);writel(temp, lcd_regs_base + VIDTCON0);/** VIDTCON1:* * [23:16]:  HBPD+1=thb-hpw=46-21=25 --> HBPD=24* * [15:8] :  HFPD+1=thfp=210 --> HFPD=209* * [7:0]  :  HSPW+1=hpw=1~40(暂取21) --> HSPW=20* */temp = readl(lcd_regs_base + VIDTCON1);//temp |= (24 << 16) | (209 << 8)  | (20 << 0);temp |= (140 << 16) | (160 << 8)  | (20 << 0);writel(temp, lcd_regs_base + VIDTCON1);/**VIDTCON2 * HOZVAL = (Horizontal display size) - 1 * LINEVAL = (Vertical display size) - 1.* * Horizontal(水平) display size : 800* * Vertical(垂直) display size : 480*/temp = ((LCD_WIDTH-1) << 11) | (LCD_LENTH << 0);writel(temp, lcd_regs_base + VIDTCON2);/**   * WINCON0:* * [15]:Specifies Word swap control bit.  1 = Enables swap 低位像素存放在低字节* * [5:2]: Selects Bits Per Pixel (BPP) mode for Window image : 1101 ===> Unpacked 25 BPP (non-palletized A:1-R:8-G:8-B:8)* * [0]:Enables/disables video output   1 = Enables* */temp = readl(lcd_regs_base + WINCON0);temp &= ~(0x0F << 2);temp |= (0X01 << 15) | (0x0D << 2) | (0x01<<0);writel(temp, lcd_regs_base + WINCON0);//SHADOWCON//Enables Channel 0.temp = readl(lcd_regs_base + SHADOWCON);writel(temp | 0x01, lcd_regs_base + SHADOWCON);//WINCHMAP2//Selects Channel 0temp = readl(lcd_regs_base + WINCHMAP2);temp &= ~(7 << 16);temp |= (0x01 << 16);//CH0FISEL:Selects Channel 0's channel.001 = Window 0temp &= ~(7 << 0);temp |= (0x01 << 0);//W0FISEL:Selects Window 0's channel.001 = Channel 0writel(temp, lcd_regs_base + WINCHMAP2);//VIDOSD0A VIDOSD0B VIDOSD0C//设置OSD显示大小//Window Size For example. Height *  Width (number of word)temp = (LCD_LENTH * LCD_WIDTH) >> 1;writel(temp, lcd_regs_base + VIDOSD0C);/** bit0-10 : 指定OSD图像左上像素的垂直屏幕坐标* * bit11-21: 指定OSD图像左上像素的水平屏幕坐标*/writel(0, lcd_regs_base + VIDOSD0A);/** bit0-10 : 指定OSD图像右下像素的垂直屏幕坐标* * bit11-21: 指定OSD图像右下像素的水平屏幕坐标*/writel(((LCD_LENTH-1) << 11) | (LCD_WIDTH-1), lcd_regs_base + VIDOSD0B);//VIDCON0//Display On: ENVID and ENVID_F are set to "1".temp = readl(lcd_regs_base + VIDCON0);writel(temp | (0x01<<1) | (0x01<<0), lcd_regs_base + VIDCON0);/* 3.3 分配显存(framebuffer), 并把地址告诉LCD控制器 */// fs4412_lcd->screen_base         显存虚拟地址// fs4412_lcd->fix.smem_len        显存大小,前面计算的// fs4412_lcd->fix.smem_start      显存物理地址fs4412_lcd->screen_base = dma_alloc_writecombine(NULL, fs4412_lcd->fix.smem_len, (dma_addr_t *)&fs4412_lcd->fix.smem_start, GFP_KERNEL);//显存起始地址writel(fs4412_lcd->fix.smem_start, lcd_regs_base + VIDW00ADD0B0);//显存结束地址writel(fs4412_lcd->fix.smem_start + fs4412_lcd->fix.smem_len, lcd_regs_base + VIDW00ADD1B0);/* 4. 注册 */ret = register_framebuffer(fs4412_lcd);return ret;
}static int lcd_remove(struct platform_device *pdev){//Direct Off: ENVID and ENVID_F are set to “0” simultaneously.unsigned int temp;temp = readl(lcd_regs_base + VIDCON0);temp &= ~(0x01<<1 | 0x01<<0);writel(temp, lcd_regs_base + VIDCON0);unregister_framebuffer(fs4412_lcd);dma_free_writecombine(NULL, fs4412_lcd->fix.smem_len, fs4412_lcd->screen_base, fs4412_lcd->fix.smem_start);framebuffer_release(fs4412_lcd);return 0;
}static const struct of_device_id lcd_dt_ids[] = {{.compatible = "samsung,exynos4210-fimd"},{},
};MODULE_DEVICE_TABLE(of, lcd_dt_ids);
static struct platform_driver lcd_driver = {.driver={.name = "mylcd",.of_match_table = of_match_ptr(lcd_dt_ids),},.probe = lcd_probe,.remove = lcd_remove,
};static int lcd_init(void) {int ret;ret = platform_driver_register(&lcd_driver);return ret;
}static void lcd_exit(void){printk("enter %s\n", __func__);platform_driver_unregister(&lcd_driver);
}module_init(lcd_init);
module_exit(lcd_exit);MODULE_LICENSE("GPL");

\qquad 以上内容。细节很多,特别是在probe函数中的寄存器赋值部份还有相当多的细节,但在这里是讲述驱动框架的,因此另开一篇来讲解exynos4412的LCD控制器的寄存器操作。

4、测试LCD显示的应用程序

#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <sys/mman.h>
#include <unistd.h>#define FBDEVICE "/dev/fb0"
void draw_back(unsigned int *pfb, unsigned int width, unsigned int height, unsigned int color);void draw_line(unsigned int *pfb, unsigned int width, unsigned int height);int main(void)
{int fd = -1;int ret = -1;unsigned int *pfb = NULL;struct fb_fix_screeninfo finfo;struct fb_var_screeninfo vinfo;fd = open(FBDEVICE, O_RDWR);if (fd < 0){perror("open");return -1;}printf("open %s success \n", FBDEVICE);/*获取fb信息*/ret = ioctl(fd, FBIOGET_FSCREENINFO, &finfo);if (ret < 0){perror("ioctl");return -1;}ret = ioctl(fd, FBIOGET_VSCREENINFO, &vinfo);if (ret < 0){perror("ioctl");return -1;}/*建立mmap映射*/pfb = mmap(NULL, finfo.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);if (NULL == pfb){perror("mmap");return -1;}printf("pfb :0x%x \n",(unsigned int ) pfb);draw_back(pfb, vinfo.xres_virtual, vinfo.yres_virtual, 0xffff0000);draw_line(pfb, vinfo.xres_virtual, vinfo.yres_virtual);close(fd);return 0;
}void draw_back(unsigned int *pfb, unsigned int width, unsigned int height, unsigned int color)
{unsigned int x, y;for (y = 0; y < height; y++){for (x = 0; x < width; x++){*(pfb + y * width + x) = color;}}
}void draw_line(unsigned int *pfb, unsigned int width, unsigned int height)
{unsigned int x, y;for (x = 50; x < width - 50; x++){*(pfb + 50 * width + x) = 0xffffff00;}for (y = 50; y < height -50; y++){*(pfb + y * width + 50) = 0xffffff00;}
}

写在结尾

\qquad 本篇与上一篇是linux下的LCD驱动框架-FrameBuffer框架的完整笔记,框架本身是简单的,但由于涉及到大量LCD的显示原理,LCD控制器的配置,寄存器的配置,时序的分辨等。而这些又与大量的数据结构相对应。在理清上述内容后,又需要在开发板上进行验证,因此这两篇实际写了一个多月。
\qquad 内容又多又杂,难免有诸多遗漏与不足,因此,在以后如有发现缺漏,我将会随时进行修改。

参考

https://blog.csdn.net/qq_28992301/article/details/52727050
https://www.cnblogs.com/armlinux/archive/2011/01/14/2396864.html
https://zhuanlan.zhihu.com/p/598132318
http://www.51hei.com/bbs/dpj-43162-1.html
https://www.ngui.cc/zz/1632478.html?action=onClick

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/94316.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring 框架入门介绍及IoC的三种注入方式

目录 一、Spring 简介 1. 简介 2. spring 的核心模块 ⭐ 二、IoC 的概念 2.1 IoC 详解 2.2 IoC的好处 2.3 谈谈你对IoC的理解 三、IoC的三种注入方式 3.1 构造方法注入 3.2 setter方法注入 3.3 接口注入&#xff08;自动分配&#xff09; 3.4 spring上下文与tomcat整…

RTC实验

一、RTC简介 RTC(Real Time Clock)即实时时钟&#xff0c;它是一个可以为系统提供精确的时间基准的元器件&#xff0c;RTC一般采用精度较高的晶振作为时钟源&#xff0c;有些RTC为了在主电源掉电时还可以工作&#xff0c;需要外加电池供电BCD码&#xff0c;四位二进制表示一位…

Linux Vm上部署Docker

创建ubutu虚拟机并远程连接&#xff0c; 参考 https://blog.csdn.net/m0_48468018/article/details/132267096 在终端中切换到root用户&#xff0c;并安装docker服务 2.1 切换到root用户 sudo su2.2 安装docker服务 , 参考 https://docs.docker.com/engine/install/ubuntu/ …

cesium学习记录09-turf.js的使用(画矩形结合地形生成三角网)

上一篇是绘制多边形&#xff0c;这一篇来说绘制矩形&#xff0c;但又因为只说绘制矩形太短了&#xff0c;所以就结合一下turf.js&#xff0c;生成一下地形三角网 Turf.js中文网 最终效果&#xff1a; 一、引入Turf.js 1&#xff0c;下载 npm install turf/turf2&#xff0c;…

使用maven打包时如何跳过test,有三种方式

方式一 针对spring项目&#xff1a; <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <configuration> <skipTests>true</skipTests> </configuration> …

影响力再度提升,Smartbi多次蝉联Gartner、IDC等权威认可

近期&#xff0c;思迈特软件捷报频传&#xff0c;Smartbi凭借技术创新实力和产品能力&#xff0c;成功入选Gartner中国增强数据分析代表厂商及自助分析代表厂商&#xff0c;同时&#xff0c;连续三年蝉联“IDC中国FinTech 50”榜单。 Part.1 再次被Gartner提名 Smartbi深度融…

如何在控制台查看excel内容

背景 最近发现打开电脑的excel很慢&#xff0c;而且使用到的场景很少&#xff0c;也因为mac自带了预览的功能。但是shigen就是闲不住&#xff0c;想自己搞一个excel预览软件&#xff0c;于是在一番技术选型之后&#xff0c;我决定使用python在控制台显示excel的内容。 具体的需…

【数据库系统】-- 【1】DBMS概述

1.DBMS概述 01数据库系统概述02数据库技术发展概述03关系数据库概述04数据库基准测试 01数据库系统概述 几个基本概念 为什么使用数据库系统 数据库发展的辉煌历程 02数据库技术发展概述 数据模型 应用领域 ● OLTP ● OLAP ● HTAP ● GIS OLTP与OLAP 与其他技术相…

V2board缓存投毒漏洞复现

1.什么是缓存投毒 缓存投毒&#xff08;Cache poisoning&#xff09;&#xff0c;通常也称为域名系统投毒&#xff08;domain name system poisoning&#xff09;&#xff0c;或DNS缓存投毒&#xff08;DNS cache poisoning&#xff09;。它是利用虚假Internet地址替换掉域名系…

PHP-MD5注入

0x00 前言 有些零散的知识未曾关注过&#xff0c;偶然捡起反而更加欢喜。 0x01 md5 注入绕过 md5函数有两个参数&#xff0c;第一个参数是要进行md5的值&#xff0c;第二个值默认为false&#xff0c;如果为true则返回16位原始二进制格式的字符串。意思就是会将md5后的结果当…

使用qsqlmysql操作mysql提示Driver not loaded

环境: win10 IDE: qt creator 编译器: mingw32 这里简单的记录下。我遇到的情况是在IDE使用debug和release程序都是运行正常&#xff0c;但是当我编译成发布版本之后。老是提示Driver not load。 这就很奇诡了。 回顾了下编译的时候是需要在使用qt先编译下libqsqlmysql.dll的…

神经网络基础-神经网络补充概念-08-逻辑回归中的梯度下降算法

概念 逻辑回归是一种用于分类问题的机器学习算法&#xff0c;而梯度下降是优化算法&#xff0c;用于更新模型参数以最小化损失函数。在逻辑回归中&#xff0c;我们使用梯度下降算法来找到最优的模型参数&#xff0c;使得逻辑回归模型能够更好地拟合训练数据。 逻辑回归中的梯…

python selenium如何保存网站的cookie用于下次自动登录

## 一、python selenium如何保存网站的cookie 使用Selenium保存网站的Cookie非常简单。下面是一个示例&#xff0c;展示了如何使用Selenium打开网站&#xff0c;然后保存获取到的Cookie&#xff1a; from selenium import webdriver# 初始化浏览器 browser webdriver.Chrome…

正则表达式 —— Awk

Awk awk&#xff1a;文本三剑客之一&#xff0c;是功能最强大的文本工具 awk也是按行来进行操作&#xff0c;对行操作完之后&#xff0c;可以根据指定命令来对行取列 awk的分隔符&#xff0c;默认分隔符是空格或tab键&#xff0c;多个空格会压缩成一个 awk的用法 awk的格式…

【经典排序】—— “希尔排序”

插入排序希尔排序插入排序VS希尔排序 测试 希尔排序是在插入排序的基础上进行改进优化&#xff0c;所以学习希尔排序之前需要先了解插入排序。 插入排序 像玩扑克牌摸牌时一样&#xff0c;一张一张摸&#xff0c;每摸到一张插入到对应的位置&#xff0c;插入排序就是从第一个位…

大模型技术实践(一)|ChatGLM2-6B基于UCloud UK8S的创新应用

近半年来&#xff0c;通过对多款主流大语言模型进行了调研&#xff0c;我们针对其训练方法和模型特点进行逐一分析&#xff0c;方便大家更加深入了解和使用大模型。本文将重点分享ChatGLM2-6B基于UCloud云平台的UK8S实践应用。 01各模型结构及特点 自从2017年6月谷歌推出Transf…

NFTScan NFT API 在 DID Protocol 开发中的应用

自互联网发展以来&#xff0c;Web2.0 时代产生了网络社会&#xff0c;社会已经不再局限于地理边界&#xff0c;而 Web 3.0 引入了去中心化的理念&#xff0c;强调个体数据隐私和可信互操作性。在这个新的时代中&#xff0c;去中心化身份&#xff08;Decentralized Identifier 即…

vue3+vite配置vantUI主题

❓在项目中统一配置UI主题色&#xff0c;各个组件配色统一修改 vantUI按需安装 参考vantUI文档 创建vantVar.less文件夹进行样式编写 vantVar.less :root:root{//导航--van-nav-bar-height: 44px;//按钮--van-button-primary-color: #ffffff;--van-button-primary-backgr…

【Rust】Rust学习 第十一章编写自动化测试

Rust 是一个相当注重正确性的编程语言&#xff0c;不过正确性是一个难以证明的复杂主题。Rust 的类型系统在此问题上下了很大的功夫&#xff0c;不过它不可能捕获所有种类的错误。为此&#xff0c;Rust 也在语言本身包含了编写软件测试的支持。 编写一个叫做 add_two 的将传递…

FFmpeg 硬编码VideoToolBox流程

介绍 FFmpeg已经提供对 VideoToolBox 的编解码支持&#xff1b;主要涉及到的文件有videotoolbox.c、videotoolbox.h、videotoolboxenc.c、ffmepg_videotoolbox.c。在编译 FFmpeg 源码时&#xff0c;想要支持VideoToolBox&#xff0c;在 configure 时&#xff0c;需要–enable-…