哈喽,老吴又来分享学习心得啦~
目标是关于你想要达到的结果,而体系是涉及导致这些结果的过程;
目标的意义在于确定大方向,但体系才能促进进步。完全忽略目标,只关注体系,仍然会成功。
结果并不是导致问题产生的根源。真正需要改变的是导致这些结果的体系。为了取得一劳永逸的成效,需要解决体系层面上的问题,修正输入端,输出端就会自行修正。
fbtft 的作用:
驱动各种 spi 接口的 TFT-LCD。
fbtft 的框架:
点击查看大图驱动一款 spi lcd 的大致步骤:
检查硬件连接;
配置 spi 控制器,包括引脚配置、时钟输出、cs 脚、相位和极性等;
配置 led/reset/dc gpio;
添加 spi lcd 初始化代码;
异常调试:
示波器;
sysfs 读写 TFT-LCD 驱动 IC的寄存器;
打开 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 为例;
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()。
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 调试节点;
初始化流程图:
点击查看大图3) fbtft_update_display(), 刷新屏幕
par->fbtftops.set_addr_win(),设置绘制窗口的坐标;
par->fbtftops.write_vmem(par, offset, len),将显存中的数据刷到屏幕上;
刷屏流程图:
点击查看大图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(...)
功能测试:
$ git clone https://github.com/ponty/fb-test-app
$ make
$ ./fb-test
效果如下:
点击查看大图性能测试:
在 dts 中设置 debug = 0x20,会打开 DEBUG_TIME_EACH_UPDATE 以打印 fps 的值:
spi0.0: Display update: 34100 kB/s, fps=40
要学习技术,更要学习如何生活。
你和我各有一个苹果,如果我们交换苹果的话,我们还是只有一个苹果。但当你和我各有一个想法,我们交换想法的话,我们就都有两个想法了。
对 嵌入式系统 (Linux、RTOS、OpenWrt、Android) 和 开源软件 感兴趣,关注公众号:嵌入式Hacker。
觉得文章对你有价值,不妨点个 在看和赞。