F1C100S添加SPI LCD液晶驱动
在使用F1C100S的时候常常会使用小尺寸的液晶屏,比如市场上比较常见的1.14寸液晶屏,下面我们来为该液晶屏添加驱动。
下面以内核Linux-5.7内核版本,液晶驱动芯片为ST7789V,四线SPI接口为例:(例中液晶引脚接到SPI1上)
注意:SPI总线的SCLK和SDA两个引脚必须接上拉电阻,10k即可。
这里说明下硬件连接,因为有部分人遇到无法成功问题:
LCD液晶引脚 | F1C100S板子 |
SCLK | PA2 |
SDA | PA1 |
CS | GND |
RESET | PE7 |
DC | PE10 |
1.设备树中添加SPI节点和ST7789V节点:
spi0:spi@1c05000 { compatible = "allwinner,suniv-spi", "allwinner,sun8i-h3-spi"; reg = <0x1c05000 0x1000>; interrupts = <0xa>; clocks = <&ccu CLK_BUS_SPI0>, <&ccu CLK_BUS_SPI0>; clock-names = "ahb", "mod"; resets = <&ccu RST_BUS_SPI0>; status = "disabled"; #address-cells = <1>; #size-cells = <0>; pinctrl-names = "default"; pinctrl-0 = <&spi0_pins>; }; 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 = "disabled"; #address-cells = <1>; #size-cells = <0>; bias-pull-up; pinctrl-names = "default"; pinctrl-0 = <&spi1_pins>; };
同时在IO节点中申明引脚定义,如下所示:
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"; }; lcd_rgb666_pins: lcd-rgb666-pins { pins = "PD0", "PD1", "PD2", "PD3", "PD4", "PD5", "PD6", "PD7", "PD8", "PD9", "PD10", "PD11", "PD12", "PD13", "PD14", "PD15", "PD16", "PD17", "PD18", "PD19", "PD20", "PD21"; function = "lcd"; }; mmc0_pins: mmc0-pins { pins = "PF0", "PF1", "PF2", "PF3", "PF4", "PF5"; function = "mmc0"; }; spi0_pins: spi0-pins{ pins = "PC0", "PC1", "PC2", "PC3"; function = "spi0"; }; spi1_pins: spi1-pins{ pins = "PA2","PA0","PA3","PA1"; function = "spi1"; }; };
在设备树中添加ST7789V节点:
&spi1 { st7789v@0 { status = "okay"; compatible = "sitronix,st7789v"; reg = <0>; spi-max-frequency = <32000000>; //最大速度32M rotate = <0>; //屏幕旋转角度 spi-cpol; //SPI引脚模式 spi-cpha; //SPI引脚模式 rgb; //颜色格式为RGB fps = <30>; //刷新帧数30 buswidth = <8>; //总线宽度8位 reset-gpios = <&pio 4 7 GPIO_ACTIVE_LOW>; //GPIOE7 dc-gpios = <&pio 4 10 GPIO_ACTIVE_LOW>; //GPIOE10 debug = <0>; }; };
2.修改drivers/staging/fbtft/fbtft-core.c文件中的gpio申请函数:
这里简单说下原因:因为在5.2版本之后使用的是另一种方式申请gpio,但是这种方式只是简单的给出了接口,实际并没有申请到gpio,因此当不去修改申请方式的时候,即使内核log提示已经申请成功并注册了驱动,但是实际并没有,导致液晶屏无法驱动起来。
先添加如下头文件:
#include <linux/gpio.h>
#include <linux/of_gpio.h>
然后修改两个函数:
static int fbtft_request_one_gpio(struct fbtft_par *par, const char *name, int index, struct gpio_desc **gpiop) { struct device *dev = par->info->device; struct device_node *node = dev->of_node; int gpio, flags, ret = 0; enum of_gpio_flags of_flags; if (of_find_property(node, name, NULL)) { gpio = of_get_named_gpio_flags(node, name, index, &of_flags); if (gpio == -ENOENT) return 0; if (gpio == -EPROBE_DEFER) return gpio; if (gpio < 0) { dev_err(dev, "failed to get '%s' from DT\n", name); return gpio; } //active low translates to initially low flags = (of_flags & OF_GPIO_ACTIVE_LOW) ? GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH; ret = devm_gpio_request_one(dev, gpio, flags, dev->driver->name); if (ret) { dev_err(dev, "gpio_request_one('%s'=%d) failed with %d\n", name, gpio, ret); return ret; } *gpiop = gpio_to_desc(gpio); fbtft_par_dbg(DEBUG_REQUEST_GPIOS, par, "%s: '%s' = GPIO%d\n", __func__, name, gpio); } return ret; }
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; }
3.由于5.7版本的fbtft复位部分有问题,最后没有拉高,所以我们需要修改drivers/staging/fbtft/fbtft-core.c中的fbtft_reset函数:
static void fbtft_reset(struct fbtft_par *par) { if (!par->gpio.reset) return; fbtft_par_dbg(DEBUG_RESET, par, "%s()\n", __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); }
4.最后由于fbtft提供的ST7789V的初始化代码部分有问题,导致颜色对不上,因此我们这里使用STM32中的初始化,修改如下:
修改drivers/staging/fbtft/fb_st7789v.c中的init_display函数:
static int init_display(struct fbtft_par *par) { par->fbtftops.reset(par); mdelay(50); 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,0x19); write_reg(par,0xC0,0x2C); write_reg(par,0xC2,0x01); write_reg(par,0xC3,0x12); write_reg(par,0xC4,0x20); write_reg(par,0xC6,0x0F); write_reg(par,0xD0,0xA4,0xA1); write_reg(par,0xE0,0xD0,0x04,0x0D,0x11,0x13,0x2B,0x3F,0x54,0x4C,0x18,0x0D,0x0B,0x1F,0x23); write_reg(par,0xE1,0xD0,0x04,0x0C,0x11,0x13,0x2C,0x3F,0x44,0x51,0x2F,0x1F,0x1F,0x20,0x23); write_reg(par,0x21); write_reg(par,0x11); mdelay(50); write_reg(par,0x29); mdelay(200); return 0; }
由于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 -j4编译镜像: