Linux驱动开发 / fbtft源码速读

哈喽,老吴又来分享学习心得啦~

一、目标与体系

  • 目标是关于你想要达到的结果,而体系是涉及导致这些结果的过程;

  • 目标的意义在于确定大方向,但体系才能促进进步。完全忽略目标,只关注体系,仍然会成功。

  • 结果并不是导致问题产生的根源。真正需要改变的是导致这些结果的体系。为了取得一劳永逸的成效,需要解决体系层面上的问题,修正输入端,输出端就会自行修正。

二、Linux驱动开发 / fbtft源码速读

1. fbtft 简介

fbtft 的作用:

  • 驱动各种 spi 接口的 TFT-LCD。

fbtft 的框架:

Linux驱动开发 / fbtft源码速读_第1张图片

点击查看大图

驱动一款 spi lcd 的大致步骤:

  • 检查硬件连接;

  • 配置 spi 控制器,包括引脚配置、时钟输出、cs 脚、相位和极性等;

  • 配置 led/reset/dc gpio;

  • 添加 spi lcd 初始化代码;

  • 异常调试:

    • 示波器;

    • sysfs 读写 TFT-LCD 驱动 IC的寄存器;

    • 打开 fbtft 调试信息;

2. 快速阅读 fbtft 源码

环境:

  • Linux-5.4。

dts 大致如下:

&spi0 {
	pitft: pitft@0{
		compatible = "sitronix,st7735r";
		reg = <0>;
		pinctrl-names = "default";
		pinctrl-0 = <&tft13_gpio>;
		status = "okay";

		spi-max-frequency = <25000000>;
		fps = <30>;
		buswidth = <8>;
		dc-gpios = <&gpio1 PA5 GPIO_ACTIVE_HIGH>;
		reset-gpios = <&gpio1 PA7 GPIO_ACTIVE_LOW>;
		led-gpios = <&gpio1 PC7 GPIO_ACTIVE_LOW>;
		debug = <0x0>;
	};
};
  • compatible = "sitronix,st7735r" 指定了 TFT-LCD 驱动 IC 的驱动名称;

  • spi-max-frequency 指定 spi clock 的频率;

  • fps 指定你想用的帧数,最大值和 spi clock 挂钩;

  • dc-gpios / reset-gpios/ lcd-gpios 这些引脚的功能请自行查看 TFT-LCD 芯片手册;

  • debug 用于指定输出哪些调试信息;

几个重点源码文件:

  • fbtft-core.c,核心层,实现了一个 frambuffer 设备驱动;

  • fbtft-bus.c,提供读写寄存器 / 显存的功能;

  • fbtft-io.c,提供最底层的 spi 读写功能;

  • fbtft-sysfs.c,导出一些调试接口;

  • 具体的 TFT-LCD 芯片驱动,下面以 fb_st7735r.c 为例;

分析 fb_st7735r.c

1) 构造 struct fbtft_display:

static struct fbtft_display display = {
 .regwidth = 8,
 .width = 128,
 .height = 160,
 .init_sequence = default_init_sequence,
 .gamma_num = 2,
 .gamma_len = 16,
 .gamma = DEFAULT_GAMMA,
 .fbtftops = {
  //.init_display = init_display,
  .set_addr_win = set_addr_win,
  .set_var = set_var,
  .set_gamma = set_gamma,
 },
};

struct fbtft_display 用于描述一款 TFT-LCD,包括硬件参数+硬件访问操作函数,需要根据 LCD 驱动 IC 的手册进行填写:

  • regwidth: LCD 驱动 IC 寄存器的位宽;

  • width / height: LCD 的分辨率;

  • init_sequence:初始化序列;

  • fbtftops: LCD 操作函数集,init_display 和 init_sequence 一般只需要设置其中一项就行了;

2) 定义 platform_driver:

FBTFT_REGISTER_DRIVER(DRVNAME, "sitronix,st7735r", &display);

这是一个宏,它的关键内容是定义了一个 platform_driver。

  • platform_driver 会和设备树里的带有 "sitronix,st7735r" 属性的节点匹配上,触发 fbtft-core.c / fbtft_probe_common()。

分析 fbtft-core.c 里的几个重点函数

1) fbtft_probe_common(): 分配/设置/注册 framebuffer

  • fbtft_framebuffer_alloc(),定义并初始化 fb_info

  • 解析设备树里的 "sitronix,st7735r" 节点:fbtft_probe_dt(dev);

  • 使用 struct fbtft_display 来 填充 struct fbtft_par,fbtft_par 是 Main FBTFT data structure,负责保存 fbtft 所有的软硬件信息;

  • fbtft_register_framebuffer(info),注册 fb_info;

2) fbtft_register_framebuffer(): 注册 tft 设备的 framebuffer

  • par->fbtftops.init_display(par),执行 tft-lcd 的初始化操作

  • register_framebuffer(fb_info);

  • fbtft_sysfs_init(par),注册一些 sysfs 调试节点;

初始化流程图:

Linux驱动开发 / fbtft源码速读_第2张图片

点击查看大图

3) fbtft_update_display(), 刷新屏幕

  • par->fbtftops.set_addr_win(),设置绘制窗口的坐标;

  • par->fbtftops.write_vmem(par, offset, len),将显存中的数据刷到屏幕上;

刷屏流程图:

Linux驱动开发 / fbtft源码速读_第3张图片

点击查看大图

简单看下 fbtft-bus.c 和 fbtft-io.c

fbtft-bus.c:

// 写显存
int fbtft_write_vmem16_bus8(...) 
int fbtft_write_vmem16_bus9(...) 
int fbtft_write_vmem8_bus8(...) 
int fbtft_write_vmem16_bus16(...)

// 写 tft-lcd 的寄存器
void fbtft_write_reg8_bus8(...)
void fbtft_write_reg16_bus8(...)
void fbtft_write_reg16_bus16(...)

fbtft-io.c:

int fbtft_write_spi(...) 
int fbtft_read_spi(...) 

// 支持用 gpio 来模拟 spio 通讯
int fbtft_write_spi_emulate_9(...) 
int fbtft_write_gpio8_wr(...) 
int fbtft_write_gpio16_wr(...) 
int fbtft_write_gpio16_wr_latched(...)

3. 测试方法

功能测试:

$ git clone https://github.com/ponty/fb-test-app
$ make
$ ./fb-test

效果如下:

Linux驱动开发 / fbtft源码速读_第4张图片

点击查看大图

性能测试:

在 dts 中设置 debug = 0x20,会打开 DEBUG_TIME_EACH_UPDATE 以打印 fps 的值:

spi0.0: Display update: 34100 kB/s, fps=40

三、思考技术,也思考人生

要学习技术,更要学习如何生活

你和我各有一个苹果,如果我们交换苹果的话,我们还是只有一个苹果。但当你和我各有一个想法,我们交换想法的话,我们就都有两个想法了。

嵌入式系统 (Linux、RTOS、OpenWrt、Android) 和 开源软件 感兴趣,关注公众号:嵌入式Hacker

觉得文章对你有价值,不妨点个 在看和赞

你可能感兴趣的:(Linux,驱动开发,java,python,编程语言,linux,嵌入式)