NanoPi NEO Air使用十三:使用自带的fbtft驱动点亮SPI接口TFT屏幕,ST7789V,模块加载的方式
Posted 丘木木
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了NanoPi NEO Air使用十三:使用自带的fbtft驱动点亮SPI接口TFT屏幕,ST7789V,模块加载的方式相关的知识,希望对你有一定的参考价值。
上节使用fbtft来驱动SPI接口TFT屏幕,但是是编译进内核的方式。
本节再来测试下编译成模块的用法。
引脚
240x240分辨率,1.3寸,主控为ST7789VW。
与开发板的引脚连接确定如下:
功能 | IO |
---|---|
GND | Pin6 |
5V | Pin2 |
LCD_RESET | Pin7-PG11 |
LCD_DC | Pin22-PA1 |
SPICLK | Pin23-PC2 |
SPIMOSI | Pin19-PC0 |
从开发板的引脚图发现,开发板使用的SPI0。
修改设备树
打开/home/ql/linux/H3/linux/arch/arm/boot/dts/sun8i-h3-nanopi.dtsi,找到spi0节点,把上节添加的节点注释掉,避免干扰本节的测试。修改为如下:
&spi0
/* needed to avoid dtc warning */
#address-cells = <1>;
#size-cells = <0>;
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&spi0_pins &spi0_cs_pins>;
cs-gpios = <&pio 2 3 GPIO_ACTIVE_HIGH>, <&pio 0 6 GPIO_ACTIVE_HIGH>; /*SPI-CS:PC3 and PA6*/
pitft: pitft@0
compatible = "sitronix,st7789vw";
reg = <0>;
status = "okay";
spi-max-frequency = <96000000>;
rotate = <0>;
fps = <33>;
buswidth = <8>;
dc-gpios = <&pio 0 1 GPIO_ACTIVE_HIGH>; /* PA1 */
reset-gpios = <&pio 6 11 GPIO_ACTIVE_HIGH>; /* PG11 */
led-gpios = <&pio 0 0 GPIO_ACTIVE_LOW>; /* PA0 */
debug = <0x0>;
;
;
&pio
leds_npi: led_pins
pins = "PA10";
function = "gpio_out";
;
pinctrl_testTFTRes: testTFTRes_pins
pins = "PG11";
function = "gpio_out";
;
pinctrl_testTFTDc: testTFTDc_pins
pins = "PA1";
function = "gpio_out";
;
pinctrl_testTFTBk: testTFTBk_pins
pins = "PA0";
function = "gpio_out";
;
spi0_cs_pins: spi0_cs_pins
pins = "PC3", "PA6";
function = "gpio_out";
;
;
关闭HDMI,否则设备默认HDMI输出,tft黑屏
&hdmi
//status = "okay";
status = "disable";
;
&hdmi_out
hdmi_out_con: endpoint
remote-endpoint = <&hdmi_con_in>;
;
;
&sound_hdmi
//status = "okay";
status = "disable";
;
编辑Device
根据Linux统一设备模型思想:Device提供硬件信息描述,Driver提供使用硬件信息的方法。
我们打开 /kernel/driver/staging/fbtft/fbtft_device.c文件,看到
static struct fbtft_device_display displays[] =
.name = "adafruit18",
.spi = &(struct spi_board_info)
.modalias = "fb_st7735r",
.max_speed_hz = 32000000,
.mode = SPI_MODE_0,
.platform_data = &(struct fbtft_platform_data)
.display =
.buswidth = 8,
.backlight = 1,
,
.gamma = ADAFRUIT18_GAMMA,
,
.name = "adafruit18_green",
.spi = &(struct spi_board_info)
.modalias = "fb_st7735r",
.max_speed_hz = 4000000,
.mode = SPI_MODE_0,
.platform_data = &(struct fbtft_platform_data)
.display =
.buswidth = 8,
.backlight = 1,
.fbtftops.set_addr_win =
adafruit18_green_tab_set_addr_win,
,
.bgr = true,
.gamma = ADAFRUIT18_GAMMA,
,
......................省略
上面的结构体描述了大量的显示屏信息,我们在最后面仿造前面的,加上ST7789VW屏幕的描述
,
.name = "qlexceltft", //你自己取,后面加载模块的时候要用
.spi = &(struct spi_board_info)
.modalias = "fb_st7789vW", //匹配显示屏的名字
.max_speed_hz = 96000000, //最大SPI速率
.mode = SPI_MODE_0, //spi的模式
.bus_num=0, //使用SPI0
.chip_select = 0, //使用SPI0的CS0管脚
.platform_data = &(struct fbtft_platform_data)
.display =
.buswidth = 8, //8位总线
.backlight = 1, //有背光
,
// .bgr = true,
.gpios = (const struct fbtft_gpio [])
"reset", 203 , //PG11 reset占用的管脚号
"dc", 1 , //PA1 dc占用的管脚号
"led", 0 , //PA0 背光占用的管脚号
,
添加Driver
接着我们修改位于统一设备模型中的driver
打开/home/ql/linux/H3/linux/drivers/staging/fbtft/路径,看下有没有fb_st7789vw.c文件,如果有下面的步骤就可以忽略了。
如果没有,那么新建一个fb_st7789vw.c文件,把下面内容复制进去,保存。
/*
* FB driver for the ST7789V LCD Controller
*
* Copyright (C) 2015 Dennis Menschel
*
* 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.
*/
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <video/mipi_display.h>
#include <linux/gpio.h>
#include "fbtft.h"
#define DRVNAME "fb_st7789vw"
#define DEFAULT_GAMMA \\
"D0 04 0D 11 13 2B 3F 54 4C 18 0D 0B 1F 23\\n" \\
"D0 04 0C 11 13 2C 3F 44 51 2F 1F 1F 20 23"
/**
* enum st7789v_command - ST7789V display controller commands
*
* @PORCTRL: porch setting
* @GCTRL: gate control
* @VCOMS: VCOM setting
* @VDVVRHEN: VDV and VRH command enable
* @VRHS: VRH set
* @VDVS: VDV set
* @VCMOFSET: VCOM offset set
* @PWCTRL1: power control 1
* @PVGAMCTRL: positive voltage gamma control
* @NVGAMCTRL: negative voltage gamma control
*
* The command names are the same as those found in the datasheet to ease
* looking up their semantics and usage.
*
* Note that the ST7789V display controller offers quite a few more commands
* which have been omitted from this list as they are not used at the moment.
* Furthermore, commands that are compliant with the MIPI DCS have been left
* out as well to avoid duplicate entries.
*/
enum st7789v_command
PORCTRL = 0xB2,
GCTRL = 0xB7,
VCOMS = 0xBB,
LCMCTRL = 0xC0,
VDVVRHEN = 0xC2,
VRHS = 0xC3,
VDVS = 0xC4,
VCMOFSET = 0xC5,
FRCTRL2 = 0xC6,
PWCTRL1 = 0xD0,
PVGAMCTRL = 0xE0,
NVGAMCTRL = 0xE1,
;
#define MADCTL_BGR BIT(3) /* bitmask for RGB/BGR order */
#define MADCTL_MV BIT(5) /* bitmask for page/column order */
#define MADCTL_MX BIT(6) /* bitmask for column address order */
#define MADCTL_MY BIT(7) /* bitmask for page address order */
/**
* init_display() - initialize the display controller
*
* @par: FBTFT parameter object
*
* Most of the commands in this init function set their parameters to the
* same default values which are already in place after the display has been
* powered up. (The main exception to this rule is the pixel format which
* would default to 18 instead of 16 bit per pixel.)
* Nonetheless, this sequence can be used as a template for concrete
* displays which usually need some adjustments.
*
* Return: 0 on success, < 0 if error occurred.
*/
static int init_display(struct fbtft_par *par)
/* turn off sleep mode */
write_reg(par, MIPI_DCS_EXIT_SLEEP_MODE);
mdelay(120);
write_reg(par, MIPI_DCS_SET_ADDRESS_MODE, 0x0);
write_reg(par, MIPI_DCS_SET_PIXEL_FORMAT, MIPI_DCS_PIXEL_FMT_16BIT);
write_reg(par, PORCTRL, 0x0C, 0x0C, 0x00, 0x33, 0x33);
write_reg(par, GCTRL, 0x35);
write_reg(par, VCOMS, 0x19);
write_reg(par, LCMCTRL, 0x2C);
write_reg(par, VDVVRHEN, 0x01);
write_reg(par, VRHS, 0x12);
write_reg(par, VDVS, 0x20);
write_reg(par, FRCTRL2, 0x0F);
write_reg(par, PWCTRL1, 0xA4, 0xA1);
write_reg(par, MIPI_DCS_ENTER_INVERT_MODE);
write_reg(par, MIPI_DCS_SET_DISPLAY_ON);
return 0;
static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
write_reg(par, MIPI_DCS_SET_COLUMN_ADDRESS,
xs >> 8, xs & 0xFF, xe >> 8, xe & 0xFF);
write_reg(par, MIPI_DCS_SET_PAGE_ADDRESS,
ys >> 8, ys & 0xFF, ye >> 8, ye & 0xFF);
write_reg(par, MIPI_DCS_WRITE_MEMORY_START);
/**
* set_var() - apply LCD properties like rotation and BGR mode
*
* @par: FBTFT parameter object
*
* Return: 0 on success, < 0 if error occurred.
*/
static int set_var(struct fbtft_par *par)
u8 madctl_par = 0;
if (par->bgr)
madctl_par |= MADCTL_BGR;
switch (par->info->var.rotate)
case 0:
break;
case 90:
madctl_par |= (MADCTL_MV | MADCTL_MY);
break;
case 180:
madctl_par |= (MADCTL_MX | MADCTL_MY);
break;
case 270:
madctl_par |= (MADCTL_MV | MADCTL_MX);
break;
default:
return -EINVAL;
write_reg(par, MIPI_DCS_SET_ADDRESS_MODE, madctl_par);
return 0;
/**
* set_gamma() - set gamma curves
*
* @par: FBTFT parameter object
* @curves: gamma curves
*
* Before the gamma curves are applied, they are preprocessed with a bitmask
* to ensure syntactically correct input for the display controller.
* This implies that the curves input parameter might be changed by this
* function and that illegal gamma values are auto-corrected and not
* reported as errors.
*
* Return: 0 on success, < 0 if error occurred.
*/
static int set_gamma(struct fbtft_par *par, u32 *curves)
int i;
int j;
int c; /* curve index offset */
/*
* Bitmasks for gamma curve command parameters.
* The masks are the same for both positive and negative voltage
* gamma curves.
*/
static const u8 gamma_par_mask[] =
0xFF, /* V63[3:0], V0[3:0]*/
0x3F, /* V1[5:0] */
0x3F, /* V2[5:0] */
0x1F, /* V4[4:0] */
0x1F, /* V6[4:0] */
0x3F, /* J0[1:0], V13[3:0] */
0x7F, /* V20[6:0] */
0x77, /* V36[2:0], V27[2:0] */
0x7F, /* V43[6:0] */
0x3F, /* J1[1:0], V50[3:0] */
0x1F, /* V57[4:0] */
0x1F, /* V59[4:0] */
0x3F, /* V61[5:0] */
0x3F, /* V62[5:0] */
;
for (i = 0; i < par->gamma.num_curves; i++)
c = i * par->gamma.num_values;
for (j = 0; j < par->gamma.num_values; j++)
curves[c + j] &= gamma_par_mask[j];
write_reg(
par, PVGAMCTRL + i,
curves[c + 0], curves[c + 1], curves[c + 2],
curves[c + 3], curves[c + 4], curves[c + 5],
curves[c + 6], curves[c + 7], curves[c + 8],
curves[c + 9], curves[c + 10], curves[c + 11],
curves[c + 12], curves[c + 13]);
return 0;
/**
* blank() - blank the display
*
* @par: FBTFT parameter object
* @on: whether to enable or disable blanking the display
*
* Return: 0 on success, < 0 if error occurred.
*/
static int blank(struct fbtft_par *par, bool on)
if (on)
write_reg(par, MIPI_DCS_SET_DISPLAY_OFF);
else
write_reg(par, MIPI_DCS_SET_DISPLAY_ON);
return 0;
static struct fbtft_display display =
.regwidth = 8,
.width = 240,
.height = 240,
.gamma_num = 2,
.gamma_len = 14,
.gamma = DEFAULT_GAMMA,
.fbtftops =
.init_display = init_display,
.set_addr_win = set_addr_win,
.set_var = set_var,
.set_gamma = set_gamma,
.blank = blank,
,
;
FBTFT_REGISTER_DRIVER(DRVNAME, "sitronix,st7789vw", &display);
MODULE_ALIAS("spi:" DRVNAME);
MODULE_ALIAS("platform:" DRVNAME);
MODULE_ALIAS("spi:st7789vw");
MODULE_ALIAS("platform:st7789vw");
MODULE_DESCRIPTION("FB driver for the ST7789VW LCD Controller");
MODULE_AUTHOR("FriendlyElec");
MODULE_LICENSE("GPL");
在Makefile中添加obj-$(CONFIG_FB_TFT_ST7789VW) += fb_st7789vw.o
在Kconfig中添加
config FB_TFT_ST7789VW
tristate "FB driver for the ST7789VW LCD Controller"
depends on FB_TFT
help
This enables generic framebuffer support for the Sitronix ST7789VW
display controller. The controller is intended for small color
displays with a resolution of up to 240x240 pixels.
Say Y if you have such a display that utilizes this controller.
编译
到menuconfig中使能此驱动
make menuconfig ARCH=arm CROSS_COMPILE=arm-linux-
Device Drivers --->
[*] Staging drivers --->
<*> Support for small TFT LCD display modules --->
将FB driver for the ST7789VW LCD Controller
和Module to for adding FBTFT devices
选择成模块
# 编译内核、设备树、模块
make zImage dtbs modules ARCH=arm CROSS_COMPILE=arm-linux-
# 网络更新内核
scp arch/arm/boot/zImage root@192.168.0.101:/boot
# 网络更新设备树
scp arch/arm/boot/dts/sun8i-h3-nanopi-neo-air.dtb root@192.168.0.101:/boot
# 网络上传模块
scp drivers/staging/fbtft/fbtft_device.ko root@192.168.0.101:/lib/modules/4.14.111/
scp drivers/staging/fbtft/fb_st7789vw.ko root@192.168.0.101:/lib/modules/4.14.111/
上电测试
上电后,切换root,加载模块
insmod fbtft_device.ko name=qlexceltft busnum=0 cs=0 rotate=0 debug=7
insmod fb_st7789vw.ko
insmod fbtft_device.ko后面的参数解释如下:
- name:fbtft_device里你自己设置的
- busnum:用于指定使用第几个spi控制器
- CS:SPI的第几个CS片选线
- rotate:屏幕翻转角度
- debug:数字越大,调试的时候打印的信息越多,debug的时候可以设置为7.
执行成功的话屏幕应该点亮了,通过lsmod
命令就能看到模块已被加载,在/dev目录下,会产生fb0设备,如果执行失败,可以通过dmesg | grep "fb"
来查看内核日志,分析具体原因,一般都是由于参数错了导致;
设置开机自动加载模块
1、在st7735s.ko fbtft_device.ko 驱动模块目录下(/lib/modules/4.x.x/kernel/driver/staging/fbtft)执行sudo depmod -A 生成map文件。
2、编辑文件 /etc/modules,添加如下:
fb_st7735s
fbtft_device
3、新建配置文件 /etc/modprobe.d/fbtft.conf,内容如下:
options fbtft_device name=qlexceltft busnum=0 gpios=reset:3,dc:17 rotate=90 custom=1 height=128 width=128
4、上面 3 步完成后重启,应该就可以看到屏幕被点亮并显示了 console,说明驱动被自动加载了
以上是关于NanoPi NEO Air使用十三:使用自带的fbtft驱动点亮SPI接口TFT屏幕,ST7789V,模块加载的方式的主要内容,如果未能解决你的问题,请参考以下文章
NanoPi NEO Air使用十三:移植RTL8723BU驱动
NanoPi NEO Air使用九:使用Linux内核自带的LED驱动
NanoPi NEO Air使用十二:使用自带的fbtft驱动点亮SPI接口TFT屏幕,ST7789V