LCD屏幕显示

LCD 显示驱动

一、LCD屏幕参数

LCD由一个一个像素组成:每行有xres个像素,有yres行,它的分辨率是:xres * yres。
只要我们能控制任意一个像素的颜色,就可以在LCD上绘制文字、图片。
在这里插入图片描述

1. 像素的颜色怎么表示

用红绿蓝三颜色来表示,可以用24位数据来表示红绿蓝,也可以用16位等等格式,比如:

  • bpp:bits per pixel,每个像素用多少位来表示
  • 24bpp:实际上会用到32位,其中8位未使用,其余24位中分别用8位表示红®、绿(G)、蓝(B)
  • 16bpp:有rbg565,rgb555
    • rgb565:用5位表示红、6位表示绿、5位表示蓝
    • rgb555:16位数据中用5位表示红、5位表示绿、5位表示蓝,浪费一位
      在这里插入图片描述

2. 怎么把颜色发给LCD

假设每个像素的颜色用16位来表示,那么一个LCD的所有像素点假设有xres * y res个,
需要的内存为:xres * yres * 16(or 32)/ 8(字节大小),也就是要设置所有像素的颜色,需要这么大小的内存。
这块内存就被称为Framebuffer,整个FrameBuffer就称为显示缓冲区,即显存

  • Framebuffer中每块数据对应一个像素
  • 每块数据的大小可能是16位、32位,这跟LCD上像素的颜色格式有关
  • 设置好LCD硬件后,只需要把颜色数据写入Framebuffer即可
    在这里插入图片描述

3. 应用工程师需要做什么?

只需通过修改fb中特定位置的像素来改变颜色,所以需要掌握:

  1. fb中每个像素用多少位表示?颜色格式是怎样的?
  2. fb显存的基地址是多少?
  3. 屏幕的分辨率?

4. 驱动工程师需要做什么?

  1. fb显存的位置是在哪里?
  2. LCD控制从fb中读取数据用来更新,那谁来传输这个数据?

二、统一的LCD硬件模型

在这里插入图片描述

1. LCD屏幕主要分为三种:

传统的MCU单片机跟SRAM(用于fb的内存)之间需要有至少五根数据线:读数据使能RD、写数据使能WR、像素地址Address Bus、数据线Data Bus、片选信号CS(可以连接多个SRAM)
在这里插入图片描述
这种情况下,需要的数据线较多,通过下面这种方法可以减少地址线的使用。

  • MIPI-DBI(Display Bus Interface) ,MCU常用的8080接口LCD模组。
    • 既然是Bus(总线),就是既能发送数据,也能发送命令,常用的8080接口就属于DBI接口。
    • Type B (i-80 system), 8-/9-/16-/18-/24-bit bus
    • Type C (Serial data transfer interface, 3/4-line SPI)

以STM32F103为例,由于芯片性能较弱且接口较少,所以fb+LCD控制器+LCD屏幕是集成在一起为LCM,通过一个8080接口连接,fb+LCD控制器即为该LCD屏幕的芯片,导致fb需要专用显存,价格较贵,该类屏幕的分辨率一般不高;F103通过与该芯片连线实现控制LCD屏幕。
这时出去CS、RD、WR必须的连接线外,地址线和数据传输线合并,通过Data/CMD使能线来控制,这样就可以减少数据位的使用。
在这里插入图片描述
在这里插入图片描述

  • MIPI-DPI (Display Pixel Interface) ,即搭载MPU的Linux开发板使用的(TFT RGB接口)。
    • Pixel(像素),强调的是操作单个像素,在MPU上的LCD控制器就是这种接口
    • Supports 24 bit/pixel (R: 8-bit, G: 8-bit, B: 8-bit)
    • Supports 18 bit/pixel (R: 6-bit, G: 6-bit, B: 6-bit)
    • Supports 16 bit/pixel (R: 5-bit, G: 6-bit, B: 5-bit)

以imx6ull为例,LCD控制器集成在ARM芯片中,再通过LCD控制器与LCD屏幕连接;fb可以在外挂的DDR或SDRAM中,价格较为便宜,可以用来实现高分辨率屏幕。

在这里插入图片描述

MIPI-DSI (Display Serial Interface)

  • Serial,相比于DBI、DPI需要使用很多接口线,DSI需要的接口线大为减少
  • Supports one data lane/maximum speed 500Mbps
  • Supports DSI version 1.01
  • Supports D-PHY version 1.00
    在这里插入图片描述

三、Frambuffer驱动框架

1. 怎么编写字符设备驱动程序

  • 驱动主设备号
  • 构造file_operations结构体,填充open/read/write等成员函数
  • 注册驱动:register_chrdev(major, name, &fops)
  • 入口函数
  • 出口函数

在这里插入图片描述

2. Framebuffer驱动程序框架

分为上下两层:

  • fbmem.c:承上启下
    • 在字符设备驱动框架基础上
    • 实现、注册file_operations结构体
    • 把APP的调用向下转发到具体的硬件驱动程序
  • xxx_fb.c:硬件相关的驱动程序
    • 实现、注册fb_info结构体
    • 实现硬件操作

核心:

在这里插入图片描述

  • 分配fb_info
    • framebuffer_alloc
  • 设置fb_info
    • var,屏幕分辨率,颜色格式
    • fix,屏幕物理地址,虚拟地址,需要开辟空间大小
    • fbops
    • 硬件相关操作
      • 引脚设置,pinctrl子系统
      • 时钟设置
      • LCD控制器设置
  • 注册fb_info
    • register_framebuffer

Framebuffer驱动和底层显示驱动之间的数据传输是一个关键过程,它涉及将图像数据从内存(或称为帧缓冲区)传输到显示设备上。这个过程在Linux内核中得到了良好的抽象和封装,以下是详细的解释:

3. Framebuffer驱动的作用

Framebuffer(帧缓冲)驱动是Linux内核中用于模拟显存的一个设备驱动。它为显示设备提供了一个统一的接口,屏蔽了不同硬件底层的差异,使得上层应用可以在图形模式下直接对帧缓冲区进行读写操作。Framebuffer驱动并不提供任何图形API,它仅负责将显示缓冲的数据显示在LCD等显示设备上。
Framebuffer驱动和底层显示驱动之间的数据传输是通过mmap机制实现的,上层应用通过mmap映射的虚拟地址写入帧缓冲区,底层显示驱动则通过DMA等机制将帧缓冲区中的数据传输到显示设备的SRAM中,并最终由LCD控制器驱动LCD屏幕显示图像。这个过程在Linux内核中得到了良好的封装和抽象,使得上层应用可以方便地进行图形界面的开发。

4. 数据传输机制

4.1 帧缓冲区的创建与映射

  • 创建帧缓冲区:Framebuffer驱动在内核中申请一块显存,用于存放将要显示的图像数据。
  • 映射帧缓冲区:通过mmap(内存映射)机制,将这块显存映射到用户空间的虚拟地址空间中,使得应用层可以直接访问和操作这块显存。

4.2 数据的写入与显示

  • 数据写入:上层应用通过mmap映射的虚拟地址,将图像数据写入帧缓冲区。
  • 显示驱动:底层显示驱动负责监控帧缓冲区的变化。当帧缓冲区中的数据更新时,显示驱动会将新的数据从帧缓冲区传输到显示设备的SRAM(静态随机存取存储器)中。
  • LCD控制器:LCD控制器负责将SRAM中的数据转换成LCD屏幕可以识别的信号,从而驱动LCD屏幕显示图像。

4.3 传输的具体实现

  • DMA(直接内存访问):在很多情况下,CPU并不直接参与帧缓冲区到显示设备SRAM的数据传输过程。相反,它使用DMA控制器来完成这一任务。DMA控制器可以独立于CPU运行,直接从帧缓冲区读取数据并写入到显示设备的SRAM中,从而大大提高了数据传输的效率。
  • 寄存器配置:底层显示驱动还需要配置LCD控制器的相关寄存器,以确保数据能够正确地从帧缓冲区传输到显示设备,并在LCD屏幕上正确显示。

四、Framebuffer应用编程

在应用程序中,操作/dev/fbX 的一般步骤如下:

  1. 首先打开/dev/fbX 设备文件。
  2. 使用 ioctl()函数获取到当前显示设备的参数信息,譬如屏幕的分辨率大小、像素格式,根据屏幕参
    数计算显示缓冲区的大小。
  3. 通过存储映射 I/O 方式将屏幕的显示缓冲区映射到用户空间(mmap)。
  4. 映射成功后就可以直接读写屏幕的显示缓冲区,进行绘图或图片显示等操作了。
  5. 完成显示后,调用 munmap()取消映射、并调用 close()关闭设备文件。

fb显示之刷背景和划线

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <sys/ioctl.h>
#include <sys/mman.h>

// 宏定义
#define FBDEVICE	"/dev/fb0"
#define WIDTH		1024	
#define HEIGHT		600

#define WHITE		0xffffffff			// test ok
#define BLACK		0x00000000
#define RED			0xffff0000
#define GREEN		0xff00ff00			// test ok
#define BLUE		0xff0000ff			
#define GREENP		0x0000ff00			// 一样,说明前2个ff透明位不起作用

// 函数声明
void draw_back(unsigned int width, unsigned int height, unsigned int color);
void draw_line(unsigned int color);

// 全局变量
unsigned int *pfb = NULL;


int main(void)
{
	int fd = -1, ret = -1;
	
	
	struct fb_fix_screeninfo finfo = {0};
	struct fb_var_screeninfo vinfo = {0};
	
	// 第1步:打开设备
	fd = open(FBDEVICE, O_RDWR);
	if (fd < 0)
	{
		perror("open");
		return -1;
	}
	printf("open %s success.\n", FBDEVICE);
	
	// 第2步:获取设备的硬件信息
	ret = ioctl(fd, FBIOGET_FSCREENINFO, &finfo);
	if (ret < 0)
	{
		perror("ioctl");
		return -1;
	}
	printf("smem_start = 0x%x, smem_len = %u.\n", finfo.smem_start, finfo.smem_len);
	
	ret = ioctl(fd, FBIOGET_VSCREENINFO, &vinfo);
	if (ret < 0)
	{
		perror("ioctl");
		return -1;
	}
	printf("xres = %u, yres = %u.\n", vinfo.xres, vinfo.yres);
	printf("xres_virtual = %u, yres_virtual = %u.\n", vinfo.xres_virtual, vinfo.yres_virtual);
	printf("bpp = %u.\n", vinfo.bits_per_pixel);

	
	// 第3步:进行mmap
	unsigned long len = vinfo.xres_virtual * vinfo.yres_virtual * vinfo.bits_per_pixel / 8;
	printf("len = %ld\n", len);
	pfb = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
	if (NULL == pfb)
	{
		perror("mmap");
		return -1;
	}
	printf("pfb = %p.\n", pfb);
	
	draw_back(WIDTH, HEIGHT, WHITE);
	draw_line(RED);
	

	close(fd);
	
	return 0;
}


//刷背景函数
void draw_back(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 color)
{
	unsigned int x, y;
	
	for (x=50; x<600; x++)
	{
		*(pfb + 200 * WIDTH + x) = color;
	}
}


  • 8
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值