本帖最后由 li510746966 于 2023-7-7 17:23 编辑
这几天通过以前大神的帖子来移植spi类型的小屏幕到imx6ull上。大概调试学习了一个星期。现在已经移植成功手上的4块屏幕,并且使用的是设备树的形式进行的屏幕配置,这样的好处个人感觉要优于大神的方法,可以在只编译设备树的情况下来快速切换屏幕,下面就是这一个星期的学习总结与必坑。
首先先上大神的连接:干货来了!!如何让你的正点原子IMX6ULL开发板支持TFT系列小屏幕(SPI接口的ST7789为例)-OpenEdv-开源电子网
大神所指出的我就不在重复了 主要说一下重点。
这里大神是使用修改<fbtft_device.c>这个文件来对屏幕和驱动尽心匹配,但在后来发现 这种方法实在是效率太低不敢恭维,换个IO就傻掉了 需要重新编译内核。
如果大家是先看完整点的教程才来看帖子的话 就会很快的明白下面写的内容了~。
开始介绍整个小屏幕移植的方法:
我们先从移植一内核中已有屏幕的驱动来开始,这里使用的是中景园的ili9341屏分辨率240*320,就是下面这一款:
谁家的屏无所谓提供驱动就好,用过他家的屏的人都知道他家提供驱动。
首先找到linux 小屏驱动所在位置:linux内核中drivers -> staging -> fbtft 在该文件夹下面就是一些小屏幕的驱动了
找到fb_ili9341.c复制一份到同级目录并更名为fb_zjy_tft240_8p_cs.c (当然这个名字自己随便取主要方便自己记忆就好)
这里直接先改写Kconfig和Makefile如下:
Kconfig 按照格式添加一个配置项
- config FB_TFT_ZJY_TFT240_8P_CS
- tristate "FB driver for the ZJY_TFT240_8P_CS LCD Controller"
- depends on FB_TFT
- help
- Generic Framebuffer support for ZJY_TFT240_8P_CS
复制代码 Makefile 加入需要被编译的文件
- obj-$(CONFIG_FB_TFT_ZJY_TFT240_8P_CS) += fb_zjy_tft240_8p_cs.o
复制代码 添加后的效果如下:
这里有一点要注意:我们在Kconfig中定义的是 FB_TFT_ZJY_TFT240_8P_CS在 Makefile 中就要使用 CONFIG_FB_TFT_ZJY_TFT240_8P_CS 别光顾着复制忘了修改了!!
这样我们就可以通过menuconfig进行驱动的选择了。
但是有一点发现linux内核中带的驱动和屏厂家的驱动有所出入,还是相信屏幕厂家的 毕竟手上的屏幕都在M核上试验过 开始改驱动,先上一个我改好的驱动在一点点的说。
- /*
- * FB driver for the ILI9341 LCD display controller
- *
- * This display uses 9-bit SPI: Data/Command bit + 8 data bits
- * For platforms that doesn't support 9-bit, the driver is capable
- * of emulating this using 8-bit transfer.
- * This is done by transferring eight 9-bit words in 9 bytes.
- *
- * Copyright (C) 2013 Christian Vogelgsang
- * Based on adafruit22fb.c by Noralf Tronnes
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <linux/init.h>
- #include <linux/delay.h>
- #include "fbtft.h"
- #define DRVNAME "ZJY_TFT240_8P_CS" //别重名了
- #define WIDTH 240
- #define HEIGHT 320
- #define TXBUFLEN (50 * PAGE_SIZE) /* 设置dma发送缓冲区, 240*320*2(按照字节来)大点也能接收(貌似设置超过100会报错error -12) */
- #define DEFAULT_GAMMA "0F 22 1C 1B 08 0F 48 B8 34 05 0C 09 0F 07 00\n" \
- "00 23 24 07 10 07 38 47 4B 0A 13 06 30 38 0F"
- static int init_display(struct fbtft_par *par)
- {
- /* 根据设备树配置的debug信息选择是否打印 */
- fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
- /* 先执行res引脚的复位动作 */
- par->fbtftops.reset(par);
- /* startup sequence for MI0283QT-9A */
- write_reg(par, 0x01); /* software reset */
- mdelay(5);
- write_reg(par, 0x28); /* display off */
- /* --------------------------------------------------------- */
- write_reg(par, 0xCF, 0x00, 0xC1, 0x30);
- write_reg(par, 0xED, 0x64, 0x03, 0x12, 0x81);
- write_reg(par, 0xE8, 0x85, 0x01, 0x79);
- write_reg(par, 0xCB, 0x39, 0X2C, 0x00, 0x34, 0x02);
- write_reg(par, 0xF7, 0x20);
- write_reg(par, 0xEA, 0x00, 0x00);
- /* ------------power control-------------------------------- */
- write_reg(par, 0xC0, 0x1D);
- write_reg(par, 0xC1, 0x12);
- /* ------------VCOM --------- */
- write_reg(par, 0xC5, 0x35, 0x3F);
- write_reg(par, 0xC7, 0x92);
- /* ------------memory access control------------------------ */
- write_reg(par, 0x3A, 0x55); /* 16bit pixel */
- /* ------------frame rate----------------------------------- */
- write_reg(par, 0xB1, 0x00, 0x12);
- write_reg(par, 0xB6, 0x0A, 0xA2);
- /* ------------Gamma---------------------------------------- */
- /* write_reg(par, 0xF2, 0x08); */ /* Gamma Function Disable */
- write_reg(par, 0x44, 0x02);
- write_reg(par, 0xF2, 0x00);
- /* ------------display-------------------------------------- */
- write_reg(par, 0xB7, 0x07); /* entry mode set */
- write_reg(par, 0xB6, 0x0A, 0x82, 0x27, 0x00);
- write_reg(par, 0x11); /* sleep out */
- mdelay(100);
- write_reg(par, 0x29); /* display on */
- mdelay(20);
- 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);
- /* Column address set */
- write_reg(par, 0x2A,
- (xs >> 8) & 0xFF, xs & 0xFF, (xe >> 8) & 0xFF, xe & 0xFF);
- /* Row address set */
- write_reg(par, 0x2B,
- (ys >> 8) & 0xFF, ys & 0xFF, (ye >> 8) & 0xFF, ye & 0xFF);
- /* Memory write */
- write_reg(par, 0x2C);
- }
- #define MEM_Y (7) /* MY row address order */
- #define MEM_X (6) /* MX column address order */
- #define MEM_V (5) /* MV row / column exchange */
- #define MEM_L (4) /* ML vertical refresh order */
- #define MEM_H (2) /* MH horizontal refresh order */
- #define MEM_BGR (3) /* RGB-BGR Order */
- static int set_var(struct fbtft_par *par)
- {
- fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
- switch (par->info->var.rotate) {
- case 0:
- write_reg(par, 0x36, (1 << MEM_X) | (par->bgr << MEM_BGR));
- break;
- case 270:
- write_reg(par, 0x36,
- (1<<MEM_V) | (1 << MEM_L) | (par->bgr << MEM_BGR));
- break;
- case 180:
- write_reg(par, 0x36, (1 << MEM_Y) | (par->bgr << MEM_BGR));
- break;
- case 90:
- write_reg(par, 0x36, (1 << MEM_Y) | (1 << MEM_X) |
- (1 << MEM_V) | (par->bgr << MEM_BGR));
- break;
- }
- return 0;
- }
- /*
- Gamma string format:
- Positive: Par1 Par2 [...] Par15
- Negative: Par1 Par2 [...] Par15
- */
- #define CURVE(num, idx) curves[num*par->gamma.num_values + idx]
- static int set_gamma(struct fbtft_par *par, unsigned long *curves)
- {
- int i;
- fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
- for (i = 0; i < par->gamma.num_curves; i++)
- write_reg(par, 0xE0 + i,
- CURVE(i, 0), CURVE(i, 1), CURVE(i, 2),
- CURVE(i, 3), CURVE(i, 4), CURVE(i, 5),
- CURVE(i, 6), CURVE(i, 7), CURVE(i, 8),
- CURVE(i, 9), CURVE(i, 10), CURVE(i, 11),
- CURVE(i, 12), CURVE(i, 13), CURVE(i, 14));
- return 0;
- }
- #undef CURVE
- static struct fbtft_display display = {
- .regwidth = 8,
- .width = WIDTH,
- .height = HEIGHT,
- .txbuflen = TXBUFLEN,
- .gamma_num = 2,
- .gamma_len = 15,
- .gamma = DEFAULT_GAMMA,
- .fbtftops = {
- .init_display = init_display,
- .set_addr_win = set_addr_win,
- .set_var = set_var,
- .set_gamma = set_gamma,
- },
- };
- FBTFT_REGISTER_DRIVER(DRVNAME, "ZJY_TFT240_8P_CS", &display);
- MODULE_ALIAS("spi:" DRVNAME);
- MODULE_ALIAS("platform:" DRVNAME);
- MODULE_ALIAS("spi:ZJY_TFT240_8P_CS");
- MODULE_ALIAS("platform:ZJY_TFT240_8P_CS");
- MODULE_DESCRIPTION("FB driver for the ZJY_TFT240_8P_CS LCD display controller");
- MODULE_AUTHOR("LXL");
- MODULE_LICENSE("GPL");
复制代码
这里说明一下上面这个文件中的坑:
1. #define DRVNAME "ZJY_TFT240_8P_CS" //别重名了 这个名称只要不重名就没有问题
2. #define WIDTH 240
#define HEIGHT 320 //分辨率这一块在我测试st7789的时候发现不允许小于200 小于后会导致内核崩溃 直接不执行了
3. #define DEFAULT_GAMMA "0F 22 1C 1B 08 0F 48 B8 34 05 0C 09 0F 07 00\n" \
"00 23 24 07 10 07 38 47 4B 0A 13 06 30 38 0F" //这个值来源厂家提供的函数中,中景园的是在void LCD_Init(void)中 一般都会以e0 和e1作为寄存器地址 数量长短不定
4.write_reg(par, 0xCF, 0x00, 0xC1, 0x30); //这个是向spi写数据的语句 其中0xCF 是寄存器地址 后面可以带数据也可以不带
5.设置坐标函数 static void set_addr_win(...) 保不齐有些奇葩屏需要数据偏移我是用下面的方法偏移的:
- static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
- {
- /* 定义几个变量用来偏移 */
- int _xs = xs;
- int _ys = ys;
- int _xe = xe;
- int _ye = ye;
- /* 这个屏幕偏移比较怪需要根据方向调整 */
- switch (par->info->var.rotate)
- {
- case 0:
- _xs = xs+40; _xe = xe+40; _ys = ys+53; _ye = ye+53;
- break;
- case 270:
- _xs = xs+53; _xe = xe+53; _ys = ys+40; _ye = ye+40;
- break;
- case 180:
- _xs = xs+40; _xe = xe+40; _ys = ys+52; _ye = ye+52;
- break;
- case 90:
- _xs = xs+52; _xe = xe+52; _ys = ys+40; _ye = ye+40;
- break;
- }
- fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
- "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, _xs, _ys, _xe, _ye);
- printk("****************set_addr_win\r\n");
- /* 设置行列填充起始地址与结束地址 */
- 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);
- }
复制代码 这样就给数据偏移了
6.设备描述结构体 static struct fbtft_display display = { 里面写的比较详细 如果你上面的gamma 设置的是14个 这里的 .gamma_len 就设置为14!!
7. 这里比较关键的是驱动名称这一块
FBTFT_REGISTER_DRIVER(DRVNAME, "ZJY_TFT240_8P_CS", &display);
这里的 ZJY_TFT240_8P_CS 就是要在设备树中设置的啦,设备树详细的在下面说。
这里需要关注的就是这些
其中大神提到过需要改 fbtft-io.c 这个文件在我使用ST77系列里也碰到了这个事情,按照大神的改即可 这里就不在赘述了。
到了设备树的环节了:
在那个节点改就不用多说了吧,上代码
&ecspi3 {
fsl,spi-num-chipselects = <1>; /* 有一个子设备 */
dc-gpio = <&gpio1 2 GPIO_ACTIVE_HIGH>; /* spiTFT dc数据/命令控制线 */
res-gpio= <&gpio1 1 GPIO_ACTIVE_HIGH>; /* spiTFT 复位线 */
cs-gpio = <&gpio1 20 GPIO_ACTIVE_LOW>; /* 片选引脚 */
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_ecspi3>;
status = "okay";
matrix: matrix@0 {
compatible = "ZJY_TFT240_8P_CS"; /* ZJY_TFT240_8P_CS fa,st7789v fa,st7789s fa,nv3030b ZJY_350_S11_TG21*/
reg = <0>;
status = "okay";
spi-max-frequency = <50000000>; /* 50M全速 */
fps = <90>; /* 设置FPS */
rotate = <90>; /* 设置显示方向 */
buswidth = <8>; /* 总线数据宽度 */
bgr = <0>; /* 颜色不对设置这里,是使用RGB还是GBR模式 */
dc-gpios = <&gpio1 2 GPIO_ACTIVE_HIGH>; /* spiTFT dc数据/命令控制线 */
reset-gpios = <&gpio1 1 GPIO_ACTIVE_HIGH>; /* spiTFT 复位线 */
cs-gpios = <&gpio1 20 GPIO_ACTIVE_HIGH>; /* spiTFT 片选 */
debug = <0x0>; /* 设置调试信息等级 最大可以开到0xFFFFFFFF*/
};
};
应该所有的说明都写上去了
有几个需要注意的地方
compatible = "ZJY_TFT240_8P_CS"; //这个需要需要和驱动里面的完全对应上,就会加载我们使用的驱动啦
fps 给设置到90 本来想迫使spi快点跑 没有效果~~
rotate 仔细看代码的人应该会发现 这个在驱动中有所体现 0 90 180 270 这几个值用来旋转屏幕使用
bgr 如果你本来应该显示蓝色的区域变成了 红色恭喜你 改一下它就好了 本身是bool型的参数哦
gpio名称别乱改 接口就自己配置就好
这里面的debug 在调试的时候很有用 在fbtft.h中385行有所定义,有的时候偷懒直接开成0xFFFFFFFF 全都打印出来
pinctrl_ecspi3: ecspi3grp {
fsl,pins = <
MX6UL_PAD_UART2_RTS_B__ECSPI3_MISO 0x100b1 /* MISO*/
MX6UL_PAD_UART2_CTS_B__ECSPI3_MOSI 0x100b1 /* MOSI*/
MX6UL_PAD_UART2_RX_DATA__ECSPI3_SCLK 0x100b1 /* CLK*/
MX6UL_PAD_UART2_TX_DATA__GPIO1_IO20 0x100b0 /* CS*/
MX6UL_PAD_GPIO1_IO02__GPIO1_IO02 0x100B0 /* 复位(RES) */
MX6UL_PAD_GPIO1_IO01__GPIO1_IO01 0x100B0 /* 数据/指令(DC) */
>;
};
这个也粘贴出来吧
下面这是我的接线顺序
屏幕 -> 底板 Scl -> U2-RX Sda -> us_cts Res -> gpio1 Dc -> gpio2 Cs -> U2-TX Blk -> vcc3.3
至此 整个屏幕的设置就结束了 剩下的就剩编译了
首先先关闭正点原子默认的屏幕输出在menuconfig中检查 Device Drivers ---> Graphics support ---> Frame buffer Devices ---> < > MXS LCD framebuffer support 保证MXS LCD framebuffer support未被选中
配置小屏幕驱动配置路径如下: Device Drivers ---> Staging drivers ---> <*> Support for small TFT LCD display modules ---> <*> FB driver for the ILI9341 LCD Controller <*> Generic FB driver for TFT LCD displays <*> Module to for adding FBTFT devices
我把自己写的都给开启了 这样以后就只需要编译一下设备树就可以无缝切换屏幕了 省的还需要编译内核
自此 可以拜托大神的去修改fbtft_device.c的困境
简单说两句fbtft_device.c文件中修改貌似是不使用设备树的情况才需要这样去改 在后来测试的时候已经不向该文件中添加结构了

这里可以使用一个简单的命令测试一下屏幕
cat /dev/urandom > /dev/fb0 我称之为雪花测试 效果如下:
有一点需要求助一下大家 根据数据手册上说spi可以跑到52M但是我实际的情况只能跑到大概27~30M左右是什么情况
# cat /proc/cpuinfo
BogoMIPS : 12.00
# cat cpuinfo_cur_freq
792000
这是板子上打印的信息
还有那个小企鹅到底要如何显示??
有没有大神出手解答一下,内核是使用整点的内核,根文件系统是使用的buildroot
附件中的是我的设备树 和写好的一些驱动文件以供参考
file.zip
(23.14 KB, 下载次数: 20)
|