本文章參考:
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 displa
y結構體,然後修改width和height,
2.3 修改顯示核心程式碼
然後修改fbtft-core.c檔案,
先新增兩個標頭檔案:
#include "linux/gpio.h" #include "linux/of_gpio.h"
新增標頭檔案的目的是後面需要用到申請gpio函式。
然後找到fbtft_request_one_gpio
和fbtft_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\n", 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\n", 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\n", 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()\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); }
修改復位函式的原因在於原本的函式拉低復位引腳後併為拉高。
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次^_^