当前位置: 首页 > news >正文

【嵌入式环境下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

相关文章:

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

目录 1、 Frmebuffer(帧缓冲&#xff09;操作介绍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.…...

自己动手写数据库系统:实现一个小型SQL解释器(中)

我们接上节内容继续完成SQL解释器的代码解析工作。下面我们实现对update语句的解析&#xff0c;其语法如下&#xff1a; UpdateCmd -> INSERT | DELETE | MODIFY | CREATE Create -> CreateTable | CreateView | CreateIndex Insert -> INSERT INTO ID LEFT_PARAS Fie…...

HTML 与 XHTML 二者有什么区别

HTML 与 XHTML 二者有什么区别&#xff0c;你觉得应该使用哪一个并说出理由。 HTML 与 XHTML 之间的差别&#xff0c;主要分为功能上的差别和书写习惯的差别两方面。 关于功能上的差别&#xff0c;主要是 XHTML 可兼容各大浏览器、手机以及 PDA&#xff0c;并且浏览器也能快速正…...

fiddler抓包问题记录,支持https、解决 tunnel to 443

fiddler下载安装步骤及基本配置 fiddler抓包教程&#xff0c;如何抓取HTTPS请求&#xff0c;详细教程 可能遇到的问题及解决方案 1. 不能正常访问页面&#xff08;所有https都无法访问&#xff09; 解决方案&#xff1a;查看下面配置是否正确 Rules-customization 找到 OnB…...

Kubesphere中DevOps流水线无法部署/部署失败

摘要 总算能让devops运行以后&#xff0c;流水线却卡在了deploy这一步。碰到了两个比较大的问题&#xff0c;一个是无法使用k8sp自带的kubeconfig认证去部署&#xff1b;一个是部署好了以后但是没有办法解析镜像名。 版本信息 k8s&#xff1a;v1.21.5 k8sp&#xff1a;v3.3.…...

使用Nginx解决跨域问题

前言&#xff1a; 项目是公司的老项目&#xff0c;只有部署在服务器上的时候&#xff0c;项目才可以正常运行&#xff08;接口是通的&#xff09;&#xff1b;现在需求&#xff1a;在现有的项目代码上进行修改&#xff0c;请求接口是第三方给的。接口是正常的&#xff0c;通过A…...

在 OpenCV 中使用深度学习进行年龄检测-附源码

文末附完整源码和模型文件下载链接 在本教程中,我们将了解使用 OpenCV 创建年龄预测器和性别分类器项目的整个过程。 年龄检测 我们的目标是创建一个程序,使用图像来预测人的性别和年龄。但预测年龄可能并不像你想象的那么简单,为什么呢?您可能会认为年龄预测是一个回归问…...

【BASH】回顾与知识点梳理(三十一)

【BASH】回顾与知识点梳理 三十一 三十一. 进程的管理31.1 给进程发送讯号kill -signal PIDlinux系统后台常驻进程killall -signal 指令名称 31.2 关于进程的执行顺序Priority 与 Nice 值nice &#xff1a;新执行的指令即给予新的 nice 值renice &#xff1a;已存在进程的 nice…...

Linux 终端命令之文件浏览(3) less

Linux 文件浏览命令 cat, more, less, head, tail&#xff0c;此五个文件浏览类的命令皆为外部命令。 hannHannYang:~$ which cat /usr/bin/cat hannHannYang:~$ which more /usr/bin/more hannHannYang:~$ which less /usr/bin/less hannHannYang:~$ which head /usr/bin/he…...

【精通性能优化:解锁JMH微基准测试】一基本用法

文章目录 1. 什么是JMH1.1 用JMH进行微基准测试1. JmhExample01.java2. 程序输出JmhExample01.java 2.2 JMH的基本用法2.1 Benchmark标记基准测试方法2.2 Warmup以及Measurement1. 设置全局的Warmup和Measurement&#xff08;一&#xff09;2. 设置全局的Warmup和Measurement&a…...

.Net程序调试时接受外部命令行参数方式

1.对项目右键&#xff0c;属性 2.在调试中打开常规&#xff0c;打开调试启动配置文件UI 3.输入需要的命令行参数...

Mariadb高可用MHA (四十二)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 前言 一、概述 1.1 概念 1.2 组成 1.3 特点 1.4 工作原理 二、构建MHA 2.1 ssh免密登录 2.2 主从复制 2.3 MHA安装 2.3.1所有节点安装perl环境 2.3..2 node 2.3.…...

Vue3 setup中使用$refs

在 Vue 3 中的 Composition API 中&#xff0c;$refs 并不直接可用于 setup 函数。这是因为 $refs 是 Vue 2 的实例属性&#xff0c;而在 Vue 3 中&#xff0c;setup 函数是与模板实例分离的&#xff0c;不再使用实例属性。 实际工作中确实有需求&#xff0c;在setup 函数使用…...

什么是React的上下文(Context)?如何使用和传递上下文信息?

1、什么是React的上下文(Context)&#xff1f;如何使用和传递上下文信息&#xff1f; React上下文(Context)是React提供的一种功能&#xff0c;允许你在组件之间传递数据和状态。通过使用上下文&#xff0c;你无需通过props一层一层地传递数据&#xff0c;从而减少了代码的复杂…...

CentOS Linux 78安全基线检查

阿里云标准-CentOS Linux 7/8安全基线检查 检查项类别描述加固建议等级密码复杂度检查身份鉴别检查密码长度和密码是否使用多种字符类型编辑/etc/security/pwquality.conf&#xff0c;把minlen(密码最小长度)设置为8-32位&#xff0c;把minclass(至少包含小写字母、大写字母、数…...

Java之SpringCloud Alibaba【四】【微服务 Sentinel服务熔断】

Java之SpringCloud Alibaba【四】【微服务 Sentinel服务熔断】 一、分布式系统遇到的问题1、服务挂掉的一些原因 二、解决方案三、Sentinel&#xff1a;分布式系统的流量防卫兵1、Sentinel是什么2、Sentinel和Hystrix对比3、Sentinel快速开发4、通过注解的方式来控流5、启动Sen…...

Kubernetes 企业级高可用部署

目录 1、Kubernetes高可用项目介绍 2、项目架构设计 2.1、项目主机信息 2.2、项目架构图 2.3、项目实施思路 3、项目实施过程 3.1、系统初始化 3.2、配置部署keepalived服务 3.3、配置部署haproxy服务 3.4、配置部署Docker服务 3.5、部署kubelet kubeadm kubectl工具…...

8.1 C++ STL 变易拷贝算法

C STL中的变易算法&#xff08;Modifying Algorithms&#xff09;是指那些能够修改容器内容的算法&#xff0c;主要用于修改容器中的数据&#xff0c;例如插入、删除、替换等操作。这些算法同样定义在头文件 <algorithm> 中&#xff0c;它们允许在容器之间进行元素的复制…...

攻击LNMP架构Web应用

环境配置(centos7) 1.php56 php56-fpm //配置epel yum install epel-release rpm -ivh http://rpms.famillecollet.com/enterprise/remi-release-7.rpm//安装php56&#xff0c;php56-fpm及其依赖 yum --enablereporemi install php56-php yum --enablereporemi install php…...

深度学习入门-3-计算机视觉-图像分类

1.概述 图像分类是根据图像的语义信息对不同类别图像进行区分&#xff0c;是计算机视觉的核心&#xff0c;是物体检测、图像分割、物体跟踪、行为分析、人脸识别等其他高层次视觉任务的基础。图像分类在许多领域都有着广泛的应用&#xff0c;如&#xff1a;安防领域的人脸识别…...

shopee运营新手入门教程!Shopee运营技巧!

​随着跨境电商行业的蓬勃发展&#xff0c;越来越多的人开始关注Shopee这个平台。短视频等渠道也成为了人们了解Shopee的途径。因此&#xff0c;对于许多新手来说&#xff0c;在Shopee上开店成为了一种吸引人的选择。为了帮助这些新手更好地入门&#xff0c;下面将介绍一下Shop…...

Python Web框架:Django、Flask和FastAPI巅峰对决

今天&#xff0c;我们将深入探讨Python Web框架的三巨头&#xff1a;Django、Flask和FastAPI。无论你是Python小白还是老司机&#xff0c;本文都会为你解惑&#xff0c;带你领略这三者的魅力。废话不多说&#xff0c;让我们开始这场终极对比&#xff01; Django&#xff1a;百…...

机器学习线性代数基础

本文是斯坦福大学CS 229机器学习课程的基础材料&#xff0c;原始文件下载 原文作者&#xff1a;Zico Kolter&#xff0c;修改&#xff1a;Chuong Do&#xff0c; Tengyu Ma 翻译&#xff1a;黄海广 备注&#xff1a;请关注github的更新&#xff0c;线性代数和概率论已经更新完毕…...

PyQt5组件之QLabel显示图像和视频

目录 一、显示图像和视频 1、显示图像 2、显示视频 二、QtDesigner 窗口简单介绍 三、相关函数 1、打开本地图片 2、保存图片到本地 3、打开文件夹 4、打开本地文本文件并显示 5、保存文本到本地 6、关联函数 7、图片 “.png” | “.jpn” Label 自适应显示 8、Q…...

微信程序 自定义遮罩层遮不住底部tabbar解决

一、先上效果 二 方法 1、自定义底部tabbar 实现&#xff1a; https://developers.weixin.qq.com/miniprogram/dev/framework/ability/custom-tabbar.html 官网去抄 简单写下&#xff1a;在代码根目录下添加入口文件 除了js 文件的list 需要调整 其他原封不动 代码&#xf…...

Python简易部署方法

一.安装Python解释器和vscode或者其他开发工具 下载地址&#xff1a; 1.下载vscode 链接: https://code.visualstudio.com/. 2.下载python解释器 链接: https://www.python.org/downloads/. 二.安装包 打开cmd&#xff0c;输入命令&#xff1a;pip install 包名 三.配置…...

Spring Boot单元测试与Mybatis单表增删改查

目录 1. Spring Boot单元测试 1.1 什么是单元测试? 1.2 单元测试有哪些好处? 1.3 Spring Boot 单元测试使用 单元测试的实现步骤 1. 生成单元测试类 2. 添加单元测试代码 简单的断言说明 2. Mybatis 单表增删改查 2.1 单表查询 2.2 参数占位符 ${} 和 #{} ${} 和 …...

机器学习样本数据划分的典型Python方法

机器学习样本数据划分的典型Python方法 DateAuthorVersionNote2023.08.16Dog TaoV1.0完成文档撰写。 文章目录 机器学习样本数据划分的典型Python方法样本数据的分类Training DataValidation DataTest Data numpy.ndarray类型数据直接划分交叉验证基于KFold基于RepeatedKFold基…...

重建与突破,探讨全链游戏的现在与未来

全链游戏&#xff08;On-Chain Game&#xff09;是指将游戏内资产通过虚拟货币或 NFT 形式记录上链的游戏类型。除此以外&#xff0c;游戏的状态存储、计算与执行等皆被部署在链上&#xff0c;目的是为用户打造沉浸式、全方位的游戏体验&#xff0c;超越传统游戏玩家被动控制的…...

[C++] 模板template

目录 1、函数模板 1.1 函数模板概念 1.2 函数模板格式 1.3 函数模板的原理 1.4 函数模板的实例化 1.4.1 隐式实例化 1.4.2 显式实例化 1.5 模板参数的匹配原则 2、类模板 2.1 类模板的定义格式 2.2 类模板的实例化 讲模板之前呢&#xff0c;我们先来谈谈泛型编程&am…...