OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 1909|回复: 0

小屏幕(SPI屏)ST77系列ili93系列NV3030等屏幕基于设备树的驱动

[复制链接]

1

主题

18

帖子

0

精华

初级会员

Rank: 2

积分
181
金钱
181
注册时间
2018-1-5
在线时间
44 小时
发表于 2023-7-7 17:21:07 | 显示全部楼层 |阅读模式
本帖最后由 li510746966 于 2023-7-7 17:23 编辑

这几天通过以前大神的帖子来移植spi类型的小屏幕到imx6ull上。大概调试学习了一个星期。现在已经移植成功手上的4块屏幕,并且使用的是设备树的形式进行的屏幕配置,这样的好处个人感觉要优于大神的方法,可以在只编译设备树的情况下来快速切换屏幕,下面就是这一个星期的学习总结与必坑。
首先先上大神的连接:干货来了!!如何让你的正点原子IMX6ULL开发板支持TFT系列小屏幕(SPI接口的ST7789为例)-OpenEdv-开源电子网
大神所指出的我就不在重复了 主要说一下重点。

这里大神是使用修改<fbtft_device.c>这个文件来对屏幕和驱动尽心匹配,但在后来发现 这种方法实在是效率太低不敢恭维,换个IO就傻掉了 需要重新编译内核。

如果大家是先看完整点的教程才来看帖子的话 就会很快的明白下面写的内容了~。

开始介绍整个小屏幕移植的方法:

我们先从移植一内核中已有屏幕的驱动来开始,这里使用的是中景园的ili9341屏分辨率240*320,就是下面这一款:
微信截图_20230707165958.png

谁家的屏无所谓提供驱动就好,用过他家的屏的人都知道他家提供驱动。
首先找到linux 小屏驱动所在位置:linux内核中drivers -> staging -> fbtft 在该文件夹下面就是一些小屏幕的驱动了
找到fb_ili9341.c复制一份到同级目录并更名为fb_zjy_tft240_8p_cs.c (当然这个名字自己随便取主要方便自己记忆就好)

这里直接先改写Kconfig和Makefile如下:
Kconfig 按照格式添加一个配置项
  1. config FB_TFT_ZJY_TFT240_8P_CS
  2.         tristate "FB driver for the ZJY_TFT240_8P_CS LCD Controller"
  3.         depends on FB_TFT
  4.         help
  5.           Generic Framebuffer support for ZJY_TFT240_8P_CS
复制代码
Makefile 加入需要被编译的文件
  1. obj-$(CONFIG_FB_TFT_ZJY_TFT240_8P_CS)      += fb_zjy_tft240_8p_cs.o
复制代码
添加后的效果如下:
微信截图_20230707170259.png

微信截图_20230707170315.png

这里有一点要注意:我们在Kconfig中定义的是 FB_TFT_ZJY_TFT240_8P_CS在 Makefile 中就要使用  CONFIG_FB_TFT_ZJY_TFT240_8P_CS     别光顾着复制忘了修改了!!

这样我们就可以通过menuconfig进行驱动的选择了。

但是有一点发现linux内核中带的驱动和屏厂家的驱动有所出入,还是相信屏幕厂家的 毕竟手上的屏幕都在M核上试验过 开始改驱动,先上一个我改好的驱动在一点点的说。
  1. /*
  2. * FB driver for the ILI9341 LCD display controller
  3. *
  4. * This display uses 9-bit SPI: Data/Command bit + 8 data bits
  5. * For platforms that doesn't support 9-bit, the driver is capable
  6. * of emulating this using 8-bit transfer.
  7. * This is done by transferring eight 9-bit words in 9 bytes.
  8. *
  9. * Copyright (C) 2013 Christian Vogelgsang
  10. * Based on adafruit22fb.c by Noralf Tronnes
  11. *
  12. * This program is free software; you can redistribute it and/or modify
  13. * it under the terms of the GNU General Public License as published by
  14. * the Free Software Foundation; either version 2 of the License, or
  15. * (at your option) any later version.
  16. *
  17. * This program is distributed in the hope that it will be useful,
  18. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  20. * GNU General Public License for more details.
  21. *
  22. * You should have received a copy of the GNU General Public License
  23. * along with this program; if not, write to the Free Software
  24. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  25. */

  26. #include <linux/module.h>
  27. #include <linux/kernel.h>
  28. #include <linux/init.h>
  29. #include <linux/delay.h>

  30. #include "fbtft.h"

  31. #define DRVNAME                "ZJY_TFT240_8P_CS"        //别重名了
  32. #define WIDTH                240
  33. #define HEIGHT                320
  34. #define TXBUFLEN        (50 * PAGE_SIZE)                /* 设置dma发送缓冲区, 240*320*2(按照字节来)大点也能接收(貌似设置超过100会报错error -12) */
  35. #define DEFAULT_GAMMA        "0F 22 1C 1B 08 0F 48 B8 34 05 0C 09 0F 07 00\n" \
  36.                                                 "00 23 24 07 10 07 38 47 4B 0A 13 06 30 38 0F"


  37. static int init_display(struct fbtft_par *par)
  38. {
  39.         /* 根据设备树配置的debug信息选择是否打印 */
  40.         fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
  41.         /* 先执行res引脚的复位动作 */
  42.         par->fbtftops.reset(par);
  43.         /* startup sequence for MI0283QT-9A */
  44.         write_reg(par, 0x01); /* software reset */
  45.         mdelay(5);
  46.         write_reg(par, 0x28); /* display off */
  47.         /* --------------------------------------------------------- */
  48.         write_reg(par, 0xCF, 0x00, 0xC1, 0x30);
  49.         write_reg(par, 0xED, 0x64, 0x03, 0x12, 0x81);
  50.         write_reg(par, 0xE8, 0x85, 0x01, 0x79);
  51.         write_reg(par, 0xCB, 0x39, 0X2C, 0x00, 0x34, 0x02);
  52.         write_reg(par, 0xF7, 0x20);
  53.         write_reg(par, 0xEA, 0x00, 0x00);
  54.         /* ------------power control-------------------------------- */
  55.         write_reg(par, 0xC0, 0x1D);
  56.         write_reg(par, 0xC1, 0x12);
  57.         /* ------------VCOM --------- */
  58.         write_reg(par, 0xC5, 0x35, 0x3F);
  59.         write_reg(par, 0xC7, 0x92);
  60.         /* ------------memory access control------------------------ */
  61.         write_reg(par, 0x3A, 0x55); /* 16bit pixel */
  62.         /* ------------frame rate----------------------------------- */
  63.         write_reg(par, 0xB1, 0x00, 0x12);
  64.         write_reg(par, 0xB6, 0x0A, 0xA2);
  65.         /* ------------Gamma---------------------------------------- */
  66.         /* write_reg(par, 0xF2, 0x08); */ /* Gamma Function Disable */
  67.         write_reg(par, 0x44, 0x02);
  68.         write_reg(par, 0xF2, 0x00);
  69.         /* ------------display-------------------------------------- */
  70.         write_reg(par, 0xB7, 0x07); /* entry mode set */
  71.         write_reg(par, 0xB6, 0x0A, 0x82, 0x27, 0x00);
  72.         write_reg(par, 0x11); /* sleep out */
  73.         mdelay(100);
  74.         write_reg(par, 0x29); /* display on */
  75.         mdelay(20);

  76.         return 0;
  77. }

  78. static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
  79. {
  80.         fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
  81.                 "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
  82.         /* Column address set */
  83.         write_reg(par, 0x2A,
  84.                 (xs >> 8) & 0xFF, xs & 0xFF, (xe >> 8) & 0xFF, xe & 0xFF);

  85.         /* Row address set */
  86.         write_reg(par, 0x2B,
  87.                 (ys >> 8) & 0xFF, ys & 0xFF, (ye >> 8) & 0xFF, ye & 0xFF);

  88.         /* Memory write */
  89.         write_reg(par, 0x2C);
  90. }

  91. #define MEM_Y   (7) /* MY row address order */
  92. #define MEM_X   (6) /* MX column address order */
  93. #define MEM_V   (5) /* MV row / column exchange */
  94. #define MEM_L   (4) /* ML vertical refresh order */
  95. #define MEM_H   (2) /* MH horizontal refresh order */
  96. #define MEM_BGR (3) /* RGB-BGR Order */
  97. static int set_var(struct fbtft_par *par)
  98. {
  99.         fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
  100.         switch (par->info->var.rotate) {
  101.         case 0:
  102.                 write_reg(par, 0x36, (1 << MEM_X) | (par->bgr << MEM_BGR));
  103.                 break;
  104.         case 270:
  105.                 write_reg(par, 0x36,
  106.                         (1<<MEM_V) | (1 << MEM_L) | (par->bgr << MEM_BGR));
  107.                 break;
  108.         case 180:
  109.                 write_reg(par, 0x36, (1 << MEM_Y) | (par->bgr << MEM_BGR));
  110.                 break;
  111.         case 90:
  112.                 write_reg(par, 0x36, (1 << MEM_Y) | (1 << MEM_X) |
  113.                                      (1 << MEM_V) | (par->bgr << MEM_BGR));
  114.                 break;
  115.         }

  116.         return 0;
  117. }

  118. /*
  119.   Gamma string format:
  120.     Positive: Par1 Par2 [...] Par15
  121.     Negative: Par1 Par2 [...] Par15
  122. */
  123. #define CURVE(num, idx)  curves[num*par->gamma.num_values + idx]
  124. static int set_gamma(struct fbtft_par *par, unsigned long *curves)
  125. {
  126.         int i;
  127.         fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);

  128.         for (i = 0; i < par->gamma.num_curves; i++)
  129.                 write_reg(par, 0xE0 + i,
  130.                         CURVE(i, 0), CURVE(i, 1), CURVE(i, 2),
  131.                         CURVE(i, 3), CURVE(i, 4), CURVE(i, 5),
  132.                         CURVE(i, 6), CURVE(i, 7), CURVE(i, 8),
  133.                         CURVE(i, 9), CURVE(i, 10), CURVE(i, 11),
  134.                         CURVE(i, 12), CURVE(i, 13), CURVE(i, 14));

  135.         return 0;
  136. }
  137. #undef CURVE


  138. static struct fbtft_display display = {
  139.         .regwidth = 8,
  140.         .width = WIDTH,
  141.         .height = HEIGHT,
  142.         .txbuflen = TXBUFLEN,
  143.         .gamma_num = 2,
  144.         .gamma_len = 15,
  145.         .gamma = DEFAULT_GAMMA,
  146.         .fbtftops = {
  147.                 .init_display = init_display,
  148.                 .set_addr_win = set_addr_win,
  149.                 .set_var = set_var,
  150.                 .set_gamma = set_gamma,
  151.         },
  152. };
  153. FBTFT_REGISTER_DRIVER(DRVNAME, "ZJY_TFT240_8P_CS", &display);

  154. MODULE_ALIAS("spi:" DRVNAME);
  155. MODULE_ALIAS("platform:" DRVNAME);
  156. MODULE_ALIAS("spi:ZJY_TFT240_8P_CS");
  157. MODULE_ALIAS("platform:ZJY_TFT240_8P_CS");

  158. MODULE_DESCRIPTION("FB driver for the ZJY_TFT240_8P_CS LCD display controller");
  159. MODULE_AUTHOR("LXL");
  160. 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(...)  保不齐有些奇葩屏需要数据偏移我是用下面的方法偏移的:
  1. static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
  2. {
  3.         /* 定义几个变量用来偏移 */
  4.         int _xs = xs;
  5.         int _ys = ys;
  6.         int _xe = xe;
  7.         int _ye = ye;               
  8.         /* 这个屏幕偏移比较怪需要根据方向调整 */
  9.         switch (par->info->var.rotate)
  10.         {
  11.                 case 0:
  12.                         _xs = xs+40;        _xe = xe+40;        _ys = ys+53;        _ye = ye+53;
  13.                         break;
  14.                 case 270:
  15.                         _xs = xs+53;        _xe = xe+53;        _ys = ys+40;        _ye = ye+40;
  16.                         break;
  17.                 case 180:
  18.                         _xs = xs+40;        _xe = xe+40;        _ys = ys+52;        _ye = ye+52;
  19.                         break;
  20.                 case 90:
  21.                         _xs = xs+52;        _xe = xe+52;        _ys = ys+40;        _ye = ye+40;
  22.                         break;
  23.         }

  24.         fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
  25.                 "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, _xs, _ys, _xe, _ye);
  26.         printk("****************set_addr_win\r\n");
  27.         /* 设置行列填充起始地址与结束地址 */
  28.         write_reg(par, 0x2A,(_xs>>8)&(0xFF),_xs&0xFF,(_xe>>8)&(0xFF),_xe&0xFF);
  29.     write_reg(par, 0x2B,(_ys>>8)&(0xFF),_ys&0xFF,(_ye>>8)&(0xFF),_ye&0xFF);

  30.         /* Memory write */
  31.         write_reg(par, 0x2C);
  32. }
复制代码
这样就给数据偏移了

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未被选中

111.png

配置小屏幕驱动
配置路径如下:
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


微信截图_20230707170734.png
我把自己写的都给开启了 这样以后就只需要编译一下设备树就可以无缝切换屏幕了 省的还需要编译内核

自此 可以拜托大神的去修改fbtft_device.c的困境

简单说两句fbtft_device.c文件中修改貌似是不使用设备树的情况才需要这样去改 在后来测试的时候已经不向该文件中添加结构了


这里可以使用一个简单的命令测试一下屏幕
cat /dev/urandom > /dev/fb0
我称之为雪花测试
效果如下:
微信截图_20230707170047.png 微信截图_20230707170035.png


有一点需要求助一下大家 根据数据手册上说spi可以跑到52M但是我实际的情况只能跑到大概27~30M左右是什么情况
微信截图_20230707170955.png
微信截图_20230707171010.png
# cat /proc/cpuinfo

BogoMIPS        : 12.00


# cat cpuinfo_cur_freq
792000


这是板子上打印的信息

还有那个小企鹅到底要如何显示??

有没有大神出手解答一下,内核是使用整点的内核,根文件系统是使用的buildroot

附件中的是我的设备树 和写好的一些驱动文件以供参考 file.zip (23.14 KB, 下载次数: 13)
微信截图_20230707170713.png
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-6-11 12:21

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表