深入理解FBTFT--使用篇

深入理解FBTFT--使用篇

  • 文章背景
  • 文章目标
  • 正文
    • 材料:
    • 编辑Device
    • 添加Driver
    • 可能遇到的问题
    • 总结

文章背景

最近扣扣群里老是有人问fbtft该怎么用,为什么显示屏驱动不了,之前一直忙着学习Linux内核,国庆到了,该放假了(总结一波之前的学习成果了,顺便填坑),所以该系列是填坑系列。
链接: 挖坑处:FBTFT源码逻辑分析
没有看过的可以看一下,不看也没关系。总之虽然fbtft的代码年代久远已经停止维护被移在staging中,但是fbtft完全可以作为初学者第一个用于理解内核的模块。
那么在本系列中,将会以连载的形式(国庆期间),使用fbtft,向fbtft中加入自己的屏幕,以及复刻fbtft(改造fbtft)。

文章目标

  • 成功使用fbtft点亮自己的屏幕(添加自己的屏幕)
  • 复刻FBTFT(SPI,I2C,MIPI,模拟GPIO口接入的FBTFT )

正文

材料:

编译平台:Win10 Wsl2(Ubuntu 20)
目标平台:全志H3 香橙派(Machine U-boot+Mainline Kernel+自己做的根文件系统)
以上相关教程,看我空间。
显示屏:ST7789 240*240 IPS屏。(淘宝中景园有卖)
少量杜邦线。
CH340USB转串口。

编辑Device

(先提一句:主线内核里是有fbtft的源码的,在编译内核时,fbtft模块已经被编译好了,不用到处找在哪,就在你跟文件系统里,源码就在你内核里。)
根据Linux统一设备模型思想:Device提供有什么用的硬件信息,Driver提供使用硬件信息的方法。
我们打开 /kernel/driver/staging/fbtft/fbtft_device.c文件

static struct fbtft_device_display displays[] = {
	{
		.name = "adafruit18",
		.spi = &(struct spi_board_info) {
			.modalias = "fb_st7735r",
			.max_speed_hz = 32000000,
			.mode = SPI_MODE_0,
			.platform_data = &(struct fbtft_platform_data) {
				.display = {
					.buswidth = 8,
					.backlight = 1,
				},
				.gamma = ADAFRUIT18_GAMMA,
			}
		}
	}, {
		.name = "adafruit18_green",
		.spi = &(struct spi_board_info) {
			.modalias = "fb_st7735r",
			.max_speed_hz = 4000000,
			.mode = SPI_MODE_0,
			.platform_data = &(struct fbtft_platform_data) {
				.display = {
					.buswidth = 8,
					.backlight = 1,
					.fbtftops.set_addr_win =
					    adafruit18_green_tab_set_addr_win,
				},
				.bgr = true,
				.gamma = ADAFRUIT18_GAMMA,
			}
		}
	}, {
	......................省略

该结构体就是描述了大量的显示屏信息,我们在最后面仿造前面的,加上

{
		.name = "bosstft",//你自己取
		.spi = &(struct spi_board_info) {
			.modalias = "fb_st7789s",//匹配显示屏的名字
			.max_speed_hz = 96000000,//最大SPI速率
			.mode = SPI_MODE_0,//spi的模式
			.bus_num=0,//使用SPI0
			.chip_select=0,//使用SPI0的CS0管脚
			.platform_data = &(struct fbtft_platform_data) {
				.display = {
					.buswidth = 8, //8为总线
					.backlight = 1, //有背光
				},
				.gpios = (const struct fbtft_gpio []) {
					{ "reset", 20 },//reset占用的管脚号
					{ "dc", 10 },//dc占用的管脚号
					{ "led", 9 },//背光占用的管脚号
				},
			}
		}
	}

我都写注释了,不会还有人看不懂吧?这些变量的值可以自己根据实际情况添加,fbtft_device文件的修改完成。

添加Driver

接着我们修改位于统一设备模型中的driver
在/kernel/driver/staging/fbtft/新建文件st7789s.c
大概的步骤:

  1. 再次设置像素宽高
  2. 提供显示屏初始化代码
  3. 提供显示屏写入像素的代码。
  4. 注册驱动
#include 
#include 
#include 
#include 

#include "fbtft.h"
#define DRVNAME		"fb_st7789s"
#define WIDTH		240
#define HEIGHT		240
#define DEFAULT_GAMMA	"1F 1A 18 0A 0F 06 45 87 32 0A 07 02 07 05 00\n" \
			"00 25 27 05 10 09 3A 78 4D 05 18 0D 38 3A 1F"
//GAMMA应该是用来矫正颜色的,专业人士懂的改,不懂的默认。
static int init_display(struct fbtft_par *par)
{//显示屏驱动IC一般需要前面一段初始化程序,这个就是
    fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
    par->fbtftops.reset(par);//显示屏初始化前先reset,防止存有之前的配置
    write_reg(par, 0x11);//寄存器写入
    mdelay(120);
    write_reg(par, 0x36,0x00);
    write_reg(par, 0x3A,0x05);
    write_reg(par, 0xB2,0x0C,0x0C,0x00,0x33,0x33);
    write_reg(par, 0xB7,0x35);
    write_reg(par, 0xBB,0x32);
    write_reg(par, 0xC2,0x01);
    write_reg(par, 0xC3,0x15);
    write_reg(par, 0xC4,0x20);
    write_reg(par, 0xC6,0x0F);
    write_reg(par, 0xD0,0xA4,0xA1);
    write_reg(par, 0xE0,0xD0,0x08,0x0E,0x09,0x09,0x05,0x31,0x33,0x48,0x17,0x14,0x15,0x31,0x34);
    write_reg(par, 0xE1,0xD0,0x08,0x0E,0x09,0x09,0x15,0x31,0x33,0x48,0x17,0x14,0x15,0x31,0x34);
    write_reg(par, 0x21);
    write_reg(par, 0x29);
  	return 0;

}

static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
{//设置写的范围
	fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
		"%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
	//打印必要信息
    write_reg(par, 0x2A,(xs>>8)&(0xFF),xs&0xFF,(xe>>8)&(0xFF),xe&0xFF);
    write_reg(par, 0x2B,(ys>>8)&(0xFF),ys&0xFF,(ye>>8)&(0xFF),ye&0xFF);
	/* Memory write */
	write_reg(par, 0x2C);
}

static struct fbtft_display display = {
	.regwidth = 8,//总线尾数
	.width = WIDTH, //显示屏像素宽
	.height = HEIGHT,//显示屏像素高
	.gamma_num = 2,//默认
	.gamma_len = 15,//默认
	.gamma = DEFAULT_GAMMA,//默认
	.fbtftops = {
		.init_display = init_display,//初始化回调函数
		.set_addr_win = set_addr_win,//设置写入范围回调函数
	},
};


FBTFT_REGISTER_DRIVER(DRVNAME, "boss,st7789s", &display);//注册驱动

MODULE_ALIAS("spi:" DRVNAME);
MODULE_ALIAS("platform:" DRVNAME);
MODULE_ALIAS("spi:st7789s");
MODULE_ALIAS("platform:st7789s");

MODULE_DESCRIPTION("FB driver for the ST7789S LCD display controller");
MODULE_AUTHOR("Boss");
MODULE_LICENSE("GPL");

说明write_reg(par, 0x36,0x00);是宏定义,第一个0x36是作为寄存器地址,此时DC线被拉低,第二个第三个等是数据,此时DC被拉高。
如果要移植其他显示屏,我们一般只需要重写init_display,和set_addr_win,分别对于初始化和设置写入范围。
好,弄完了!很有精神!现在开始编译。
编译完丢进根文件系统里。
然后输入:

modprobe fbtft_device name=bosstft busnum=0 cs=0 speed=50000000 debug=0

name:fbtft_device里你自己设置的
busnum:哪个SPI
CS:哪个SPI的哪个CS片选线
speed:时钟速度(会自动往接近的频率设置,100000000实际应该为96000000,没测试过但是应该是这样)
debug:数字越大,调试的时候打印的信息越多,我debug的时候设置为7.

现在FB应该挂上去了,启动桌面吧。

附图:

深入理解FBTFT--使用篇_第1张图片

可能遇到的问题

1.FB成功挂上去了,屏幕不亮或者乱闪。
小伙子,测测SPI数据线的电压,检查一下设备树里是否配置上拉。
2.模块挂不上去
modprobe检查依赖是依靠文件的,单独放新模块进去当然不行,得整个编译后的模块文件夹重新放进根文件系统,或者用kmod。
3.编译失败
啊这,留言吧。

总结

十一前晚有空写的,根据之前回忆写的,没有具体测试,应该能用,毕竟我的屏幕已经亮了,可能还有错误,遇到再改吧(反正一般人不会看到这篇的)。
使用篇比较水,确实没啥写的,FBTFT原理很简单(至少对我来说是),有问题留言就是了,之后会连载复刻篇

另外:屏幕240X240已经很清楚了,那如果是相同大小的400X400呢?
请期待另外一篇:《C端嵌入式显示屏痛点解决——低成本超高清嵌入式屏幕的调通》挖坑…

你可能感兴趣的:(Linux设备驱动模型,全志H3踩坑之旅,嵌入式,linux,内核)