zoukankan      html  css  js  c++  java
  • 小白自制Linux开发板 六. SPI TFT屏幕修改与移植

    本文章参考:
    https://www.bilibili.com/read/cv9947785?spm_id_from=333.999.0.0

    本篇通过SPI接口,使用ST7789V TFT焊接屏(13pin)为我们的小开发板进行显示加持,废话不多说了,直接开搞。

    1. 硬件设置

    我们在第四篇中使用了F1C200s的SPI0通信接口连接了ESP8266作为无线网卡使用,这一篇我们将使用SPI1作为我们的显示接口

     在F1C200s,我们用到了SPI1中的CLK、MOSI、CS三个接口,因为不需要从屏幕返回数据,所以不需要接MISO,另外我们配置PE4作为重置、PE5为DC,如上图。

    需要注意的是,在有些原理图中SPI中的CS是直接接地的,这种处理方式并不好,而且还要看硬件是否支持,墨云就在这里踩过坑。

    对于屏幕端,接线相对简单,SDA(MOSI)、SCL(CS) ,除了要接线,还需要拉高;VCC为供电,并且需要接一个4.7uf或是10uf的滤波电容;

    LEDA(12pin)引脚是控制显示屏灯光的的引脚,如果有必要可以接到一个控制IO上面,这样就可以自定义控制显示屏亮灭了,这里为了图省事,就直接接了3.3v,也就是上电直接亮屏,如下图所示。

     

    2. 软件编写

    在Linux内核中是带了ST7789V驱动的,但是因为Linux内核一直在不断升级改进,比如一些申请接口的方式在不断的变化,而对应的驱动代码却没有同步更新,所以造成了很多驱动不兼容问题,所以我们还需要修改ST7789V的驱动才能让屏幕工作起来。

    在Linux内核目录drivers/staging/fbtft中可以看到有st7789v的驱动代码,

    2.1 修改初始化参数

    现在打开fb_st7789v.c文件,然后找到屏幕初始化函数,修改如下:

     1 static int init_display(struct fbtft_par *par)
     2 {
     3     par->fbtftops.reset(par);
     4     mdelay(50);
     5     write_reg(par,0x11);//Sleep exit
     6     mdelay(12);
     7     write_reg(par,0x11);
     8     mdelay(10);
     9     write_reg(par,0x3A,0x05); //65k mode
    10     write_reg(par,0xc5,0x1a);
    11     write_reg(par,0x36,0x70); // 屏幕显示方向设置
    12 //-------------ST7789V Frame rate setting-----------//
    13     write_reg(par,0xb2,0x05,0x05,0x00,0x33,0x33);
    14     write_reg(par,0xb7,0x35);
    15 //--------------ST7789V Power setting---------------//
    16     write_reg(par,0xbb,0x3f);
    17     write_reg(par,0xc0,0x2c);
    18     write_reg(par,0xc2,0x01);
    19     write_reg(par,0xc3,0x0f);
    20     write_reg(par,0xc4,0x20);
    21     write_reg(par,0xc6,0x11);
    22     write_reg(par,0xd0,0xa4,0xa1);
    23     write_reg(par,0xe8,0x03);
    24     write_reg(par,0xe9,0x09,0x09,0x08);
    25     write_reg(par,0xe0,0xd0,0x05,0x09,0x09,0x08,0x14,0x28,0x33,0x3f,0x07,0x13,0x14,0x28,0x30);
    26     write_reg(par,0xe1,0xd0,0x05,0x09,0x09,0x08,0x03,0x24,0x32,0x32,0x3b,0x14,0x13,0x28,0x2f);
    27     write_reg(par,0x21);
    28     write_reg(par,0x11);
    29     mdelay(120);      //Delay 120ms
    30     write_reg(par,0x29);
    31     mdelay(200);
    32     return 0;
    33 }

    2.2 修改分辨率

    接下来要修改屏幕分辨率,这里我使用的是1.14寸135*240的液晶屏,找到fbtft_display display结构体,然后修改widthheight

     2.3 修改显示核心代码

    然后修改fbtft-core.c文件,

    先添加两个头文件:

    #include "linux/gpio.h"
    #include "linux/of_gpio.h"

    添加头文件的目的是后面需要用到申请gpio函数。

    然后找到fbtft_request_one_gpiofbtft_request_gpios函数,并且修改:

    修改fbtft_request_one_gpio,修改gpio申请函数

     1 static int fbtft_request_one_gpio(struct fbtft_par *par,
     2                   const char *name, int index,
     3                   struct gpio_desc **gpiop)
     4 {
     5     struct device *dev = par->info->device;
     6     struct device_node *node = dev->of_node;
     7     int gpio, flags, ret = 0;
     8     enum of_gpio_flags of_flags;
     9     if (of_find_property(node, name, NULL)) {
    10         gpio = of_get_named_gpio_flags(node, name, index, &of_flags);
    11         if (gpio == -ENOENT)
    12             return 0;
    13         if (gpio == -EPROBE_DEFER)
    14             return gpio;
    15         if (gpio < 0) {
    16             dev_err(dev,
    17                 "failed to get '%s' from DT
    ", name);
    18             return gpio;
    19         }
    20          //active low translates to initially low
    21         flags = (of_flags & OF_GPIO_ACTIVE_LOW) ? GPIOF_OUT_INIT_LOW :
    22                             GPIOF_OUT_INIT_HIGH;
    23         ret = devm_gpio_request_one(dev, gpio, flags,
    24                         dev->driver->name);
    25         if (ret) {
    26             dev_err(dev,
    27                 "gpio_request_one('%s'=%d) failed with %d
    ",
    28                 name, gpio, ret);
    29             return ret;
    30         }
    31 
    32         *gpiop = gpio_to_desc(gpio);
    33         fbtft_par_dbg(DEBUG_REQUEST_GPIOS, par, "%s: '%s' = GPIO%d
    ",
    34                             __func__, name, gpio);
    35     }
    36 
    37     return ret;
    38 } 

    修改fbtft_request_gpios,修改设备树匹配字符串

    static int fbtft_request_gpios(struct fbtft_par *par)
    {
        int i;
        int ret;
    
        ret = fbtft_request_one_gpio(par, "reset-gpios", 0, &par->gpio.reset);
        if (ret)
            return ret;
        ret = fbtft_request_one_gpio(par, "dc-gpios", 0, &par->gpio.dc);
        if (ret)
            return ret;
        ret = fbtft_request_one_gpio(par, "rd-gpios", 0, &par->gpio.rd);
        if (ret)
            return ret;
        ret = fbtft_request_one_gpio(par, "wr-gpios", 0, &par->gpio.wr);
        if (ret)
            return ret;
        ret = fbtft_request_one_gpio(par, "cs-gpios", 0, &par->gpio.cs);
        if (ret)
            return ret;
        ret = fbtft_request_one_gpio(par, "latch-gpios", 0, &par->gpio.latch);
        if (ret)
            return ret;
        for (i = 0; i < 16; i++) {
            ret = fbtft_request_one_gpio(par, "db-gpios", i,
                             &par->gpio.db[i]);
            if (ret)
                return ret;
            ret = fbtft_request_one_gpio(par, "led-gpios", i,
                             &par->gpio.led[i]);
            if (ret)
                return ret;
            ret = fbtft_request_one_gpio(par, "aux-gpios", i,
                             &par->gpio.aux[i]);
            if (ret)
                return ret;
        }
    
        return 0;
    } 

    修改gpio申请函数的原因在于这里一个不同版本之间的不兼容问题,因为内核版本移植在更新,但是有些驱动却没有即使更新,这就出现了一些内核接口已经更新了,而驱动却还在使用旧的方式,导致即使可以注册成功,但并不能对其操作。

    然后修改fbtft复位函数,如下:

    static void fbtft_reset(struct fbtft_par *par)
    {
        if (!par->gpio.reset)
            return;
        fbtft_par_dbg(DEBUG_RESET, par, "%s()
    ", __func__);
        gpiod_set_value_cansleep(par->gpio.reset, 1);
        msleep(10);
        gpiod_set_value_cansleep(par->gpio.reset, 0);
        msleep(200);
        gpiod_set_value_cansleep(par->gpio.reset, 1);
        msleep(10);
    } 

    修改复位函数的原因在于原本的函数拉低复位引脚后并为拉高。

    FBTFT的部分已经修改完毕,液晶屏使用的是SPI操作的,因此需要将fbtft驱动挂载在spi总线上,幸运的是对于F1C200S来说,内核已经有spi驱动了,因此我们只需要修改设备树就可以了,具体步骤如下:

    2.4 修改设备树

    打开arch/arm/boot/dts/suniv-f1c100s.dtsi文件,添加spi节点和pio节点

    spi1:spi@1c06000 {
                compatible = "allwinner,suniv-spi", "allwinner,sun8i-h3-spi";
                reg =<0x1c06000 0x1000>;
                interrupts =<0xb>;
                clocks = <&ccu CLK_BUS_SPI1>, <&ccu CLK_BUS_SPI1>;
                clock-names = "ahb", "mod";
                resets = <&ccu RST_BUS_SPI1>;
                status = "okay";
                #address-cells =<1>;
                #size-cells =<0>;
                bias-pull-up;
                pinctrl-names = "default";
                pinctrl-0 = <&spi1_pins>;
            };
    pio: pinctrl@1c20800 {
        compatible = "allwinner,suniv-f1c100s-pinctrl";
        reg = <0x01c20800 0x400>;
        interrupts =<38>,<39>,<40>;
        clocks = <&ccu CLK_BUS_PIO>, <&osc24M>, <&osc32k>;
        clock-names = "apb", "hosc", "losc";
        gpio-controller;
        interrupt-controller;
        #interrupt-cells =<3>;
        #gpio-cells =<3>;
    
        uart0_pe_pins: uart0-pe-pins {
            pins = "PE0", "PE1";
            function = "uart0";
        };
    
     
    
        mmc0_pins: mmc0-pins {
            pins = "PF0", "PF1", "PF2", "PF3", "PF4", "PF5";
            function = "mmc0";
        };
    
    
    
        spi1_pins: spi1-pins{
             pins = "PA2","PA0","PA3","PA1";
             function = "spi1";
        };
    
    
    }; 

    添加SPI节点,主要看spi1节点即可

    添加pio节点

    然后打开arch/arm/boot/dts/suniv-f1c100s-licheepi-nano.dts在spi1中添加st7789v子节点

    &spi1 {
        st7789v@0 {
            status = "okay";
            compatible = "sitronix,st7789v";
                   reg = <0>;
                   spi-max-frequency =<32000000>;        //SPI时钟32M
                   rotate =<90>;                    //屏幕旋转90度
                   spi-cpol;
                   spi-cpha;
                   rgb;                           //颜色格式RGB
                   fps =<30>;                      //刷新30帧率
                   buswidth =<8>;                   //总线宽度8
                   reset-gpios=<&pio 4 4 GPIO_ACTIVE_LOW>;   //GPIOE4
                   dc-gpios  =<&pio 4 5 GPIO_ACTIVE_LOW>;   //GPIOE5
                   debug =<0>;                     //不开启调试
            };
    }; 

    现在所有的修改都完成了,剩下的就是编译内核了,在内核根目录下执行

    make menuconfig

    启动图形配置界面, 

    2.5 内核配置

    由于FC1000S的SPI中有一个BUG,因此我们在开启SPI驱动的时候必须选择A31(Device Drivers -> SPI support)
    如图所示

    现在选择ST7789V驱动并编译进内核中,如下:

    Device Drivers  --->  
        [*] Staging drivers  --->  
            <*>   Support for small TFT LCD display modules  --->
                  <*>   FB driver for the ST7789V LCD Controller 

    保存退出,然后执行make命令编译内核,然后将镜像拷贝到tf卡第一分区中,此时可以看到屏幕已经可以驱动起来了,并且/dev目录下有fb0设备。


     注意

    对于1.14寸液晶屏而言,其屏幕有偏移,这里需要修改fbtft-core.c文件中的fbtft_set_addr_win函数

    static void fbtft_set_addr_win(struct fbtft_par *par, int xs, int ys, int xe,
                       int ye)
    {
        write_reg(par, MIPI_DCS_SET_COLUMN_ADDRESS,(xs+40) >> 8, xs+40, ((xe+40) >> 8) & 0xFF, (xe+40) & 0xFF);
    
        write_reg(par, MIPI_DCS_SET_PAGE_ADDRESS,((ys+52) >> 8) & 0xFF, (ys+52) & 0xFF, ((ye+52) >> 8) & 0xFF, (ye+52) & 0xFF);
    
        write_reg(par, MIPI_DCS_WRITE_MEMORY_START);
    }

    效果如下:

     

     是的,还是这张图……我能放N次^_^



    NetAnalyzer下载地址

    NetAnalzyer交流群:39753670 (PS 只提供交流平台,群主基本不说话^_^)

    [转载请保留作者信息  作者:冯天文 ]


  • 相关阅读:
    将方法作为方法的参数
    远程桌面无响应解决方案(转)
    QQ通信机制(转)
    电脑管家禁止程序修改文档后如何恢复权限
    SQL Sever——远程过程调用失败(0x800706be)
    JavaScript学习笔记之JavaScript调用C#编写的COM组件
    kendoUI 免费部分开发部分经验。
    mongoDB连接信息及生成对应的collection生成代码
    写个匹配某段html dom代码某属性的正则匹配方法
    微信获取用户支付共享地址
  • 原文地址:https://www.cnblogs.com/twzy/p/15160836.html
Copyright © 2011-2022 走看看