前言
为了满足朋友的变态需求,我做了一个违背祖宗的决定(bushi)
本文章使用1.8寸SPI LCD屏幕、luckfox1106,通过设备树匹配st7735s的驱动
代码参考
【luckfox】2、添加lcd spi屏st7735和gc9306-CSDN博客
该文章使用1103,且SDK不为最新版(请按我写完文章的时间来算),导致在 rv1106g-luckfox-pico-pro-max.dts 添加 &spi0 处的代码后匹配的还是官方自带的 st7789v 的驱动。
解决方法:应在 rv1106-luckfox-pico-pro-max-ipc.dtsi,修改&spi0里的fbtft@0,改为st7735r(35r可完美匹配35s)
但是使用 cat /dev/urandom > /dev/fb0 测试一下花屏功能,发现屏幕没有任何反应,仔细检查发现屏幕背光并没有点亮,万用表测量背光控制引脚电平为0v。
RV1106点亮1.44寸SPI接口tftlcd-CSDN博客
使用这位作者的方式,创建一个backlight背光驱动,即可解决这个问题。
使用官方的方法来配置LVGL环境,并在此基础上显示一张图片的操作。
一、电路图
14 scl | 15 sda | 16 cs |
17 bl | 19 dc | 20 res |
二、dts、dtsi
rv1106g-luckfox-pico-pro-max.dts
在spidev@0中的状态一定要设置为 disabled
/**********spi**********/
/{
/*LCD_CS -- USE MISO*/
// gpio1pc3:gpio1pc3 {
// compatible = "regulator-fixed";
// pinctrl-names = "default";
// pinctrl-0 = <&gpio1_pc3>;
// regulator-name = "gpio1_pc3";
// regulator-always-on;
// };
/*LCD_BL */
// gpio2pb0:gpio2pb0 {
// // compatible = "regulator-fixed";
// compatible = "gpio-backlight";
// pinctrl-names = "default";
// pinctrl-0 = <&gpio2_pb0>;
// regulator-name = "gpio2_pb0";
// // gpios = <&gpio2 RK_PB0 GPIO_ACTIVE_HIGH>;
// regulator-always-on;
/* 创建一个backlight 背光驱动 */
backlight:backlight{
compatible = "gpio-backlight";
pinctrl-names = "default";
pinctrl-0 = <&gpio2_pb0>;
gpios = <&gpio2 RK_PB0 GPIO_ACTIVE_HIGH>;
default-on;
};
/*LCD_DC*/
gpio1pd0:gpio1pd0 {
compatible = "regulator-fixed";
pinctrl-names = "default";
pinctrl-0 = <&gpio1_pd0>;
regulator-name = "gpio1_pd0";
regulator-always-on;
};
/*LCD_RES*/
gpio1pd1:gpio1pd1 {
compatible = "regulator-fixed";
pinctrl-names = "default";
pinctrl-0 = <&gpio1_pd1>;
regulator-name = "gpio1_pd1";
regulator-always-on;
};
};
&pinctrl {
/*LCD_BL*/
gpio2-pb0 {
gpio2_pb0:gpio2-pb0 {
rockchip,pins = <2 RK_PB0 RK_FUNC_GPIO &pcfg_pull_none>;
};
};
/*LCD_DC*/
gpio1-pd0 {
gpio1_pd0:gpio1-pd0 {
rockchip,pins = <1 RK_PD0 RK_FUNC_GPIO &pcfg_pull_none>;
};
};
/*LCD_RES*/
gpio1-pd1 {
gpio1_pd1:gpio1-pd1 {
rockchip,pins = <1 RK_PD1 RK_FUNC_GPIO &pcfg_pull_none>;
};
};
spi0 {
/omit-if-no-ref/
spi0m0_pins: spi0m0-pins {
rockchip,pins =
/* spi0_clk_m0 */
<1 RK_PC1 4 &pcfg_pull_none>,
/* spie_miso_m0 */
/* <1 RK_PC3 6 &pcfg_pull_none>, */ // 先不动,还用uart4作为bl
/* spi_mosi_m0 */
<1 RK_PC2 6 &pcfg_pull_none>;
};
};
};
&spi0 {
status = "okay";
pinctrl-names = "default";
// pinctrl-0 = <&spi0m0_pins>; // 包含clk,mi,mo
pinctrl-0 = <&spi0m0_cs0 &spi0m0_pins>;
#address-cells = <1>;
#size-cells = <0>;
spidev@0 {
status = "disabled";
compatible = "rockchip,spidev";
spi-max-frequency = <1000000000>;
reg = <0>;
};
};
rv1106-luckfox-pico-pro-max-ipc.dtsi
此处只要修改fbtft@0即可
/*****************************PINCTRL********************************/
// SPI
&spi0 {
pinctrl-0 = <&spi0m0_clk &spi0m0_miso &spi0m0_mosi &spi0m0_cs0>;
#address-cells = <1>;
#size-cells = <0>;
spidev@0 {
compatible = "rockchip,spidev";
spi-max-frequency = <50000000>;
reg = <0>;
};
fbtft@0{
/*--------------------官方驱动屏幕代码-------------------*/
// compatible = "sitronix,st7789v";
// compatible = "sitronix,st7789v";
// reg = <0>;
// spi-max-frequency = <20000000>;
// fps = <30>;
// buswidth = <8>;
// debug = <0x7>;
// led-gpios = <&gpio2 RK_PB0 GPIO_ACTIVE_LOW>;//BL
// dc = <&gpio2 RK_PB1 GPIO_ACTIVE_HIGH>; //DC
// reset = <&gpio1 RK_PC3 GPIO_ACTIVE_LOW>; //RES
/*---------------------------------------------------*/
status = "okay";
compatible = "sitronix,st7735r";
reg = <0>;
spi-max-frequency = <48000000>; // 设为48M
spi-cpol;
spi-cpha;
rotate = <0>; // 旋转角度,lcd驱动里会读取并设置对应寄存器
fps = <30>;
rgb;
buswidth = <8>;
// cs = <&gpio1 RK_PC3 GPIO_ACTIVE_LOW>; //spi0_miso
led ; //BL
dc = <&gpio1 RK_PD0 GPIO_ACTIVE_HIGH>; //DC
reset = <&gpio1 RK_PD1 GPIO_ACTIVE_LOW>; //RES
debug = <0x1>;
};
};
三、luckfox_rv1106_linux_defconfig——编译驱动
CONFIG_BACKLIGHT_GPIO=y
CONFIG_FB_TFT_ST7735R=Y
CONFIG_SPI_MASTER=y
CONFIG_FB=y
四、更改fbtft驱动
由于内核版本变更,可能有写接口不适用。
对比milkv比较好添加驱动,而luckfox的改动又不同。
主要是fbtft-core.c的改动
1. 添加头文件,用于获取dtsi中引脚的高低有效电平配置
2. 更改fbtft_request_one_gpio
3. 更改fbtft_reset,添加cs引脚设置
4. 改变led-gpio为led
luckfox-pico\sysdrv\source\kernel\drivers\staging\fbtft\fbtft-core.c
#include <linux/gpio.h> //add
#include <linux/of_gpio.h> //add
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 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);
usleep_range(20, 40);
gpiod_set_value_cansleep(par->gpio.reset, 0);
msleep(120);
gpiod_set_value_cansleep(par->gpio.reset, 1);
msleep(120);
gpiod_set_value_cansleep(par->gpio.cs, 0); /* Activate chip */
msleep(120);
}
// 这里也可以设置led-gpios,但是你fbtft_request_gpios中也要相应改变。
static struct fbtft_platform_data *fbtft_properties_read(struct device *dev)
{
struct fbtft_platform_data *pdata;
if (!dev_fwnode(dev)) {
dev_err(dev, "Missing platform data or properties\n");
return ERR_PTR(-EINVAL);
}
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return ERR_PTR(-ENOMEM);
pdata->display.width = fbtft_property_value(dev, "width");
pdata->display.height = fbtft_property_value(dev, "height");
pdata->display.regwidth = fbtft_property_value(dev, "regwidth");
pdata->display.buswidth = fbtft_property_value(dev, "buswidth");
pdata->display.backlight = fbtft_property_value(dev, "backlight");
pdata->display.bpp = fbtft_property_value(dev, "bpp");
pdata->display.debug = fbtft_property_value(dev, "debug");
pdata->rotate = fbtft_property_value(dev, "rotate");
pdata->bgr = device_property_read_bool(dev, "bgr");
pdata->fps = fbtft_property_value(dev, "fps");
pdata->txbuflen = fbtft_property_value(dev, "txbuflen");
pdata->startbyte = fbtft_property_value(dev, "startbyte");
device_property_read_string(dev, "gamma", (const char **)&pdata->gamma);
if (device_property_present(dev, "led"))
pdata->display.backlight = 1;
if (device_property_present(dev, "init"))
pdata->display.fbtftops.init_display =
fbtft_init_display_from_property;
pdata->display.fbtftops.request_gpios = fbtft_request_gpios;
return pdata;
}
五、验证驱动
编译系统并烧入板子后,执行dmesg查看fb驱动是否加载正确了,而不是官方的st7789V。
[root@luckfox ]# dmesg | grep fb_
[ 0.107776] fb_st7735r spi0.0: fbtft_property_value: buswidth = 8
[ 0.107795] fb_st7735r spi0.0: fbtft_property_value: debug = 1
[ 0.107805] fb_st7735r spi0.0: fbtft_property_value: rotate = 0
[ 0.107814] fb_st7735r spi0.0: fbtft_property_value: fps = 30
[ 0.108006] fb_st7735r spi0.0: fbtft_request_one_gpio: 'reset' = GPIO57
[ 0.108182] fb_st7735r spi0.0: fbtft_request_one_gpio: 'dc' = GPIO56
[ 0.108236] fb_st7735r spi0.0: fbtft_request_one_gpio: 'led' = GPIO72
[ 1.011029] graphics fb0: fb_st7735r frame buffer, 128x160, 40 KiB video memory, 4 KiB buffer memory, fps=30, spi0.0 at 48 MHz
// 测试花屏
cat /dev/urandom > /dev/fb0
// 测试清屏
cat /dev/zero > /dev/fb0
# 背光关闭
echo 0 > /sys/class/backlight/backlight/brightness
# 背光打开
echo 1 > /sys/class/backlight/backlight/brightness
六、LVGL配置
6.1 在Ubuntu中拉取资源
在根目录下创建一个文件夹存放官方源码:
luckfox@luckfox:~$ mkdir lvgl
luckfox@luckfox:~$ cd lvgl
拉取资源:
git clone -b v8.1.0 https://github.com/lvgl/lvgl.git
git clone -b v8.1.0 https://github.com/lvgl/lv_drivers.git
git clone -b v8.1.0 https://github.com/lvgl/lv_demos.git
git clone --branch release/v8.2 --single-branch https://github.com/lvgl/lv_port_linux_frame_buffer.git
成功拉取后,在根目录下创建一个工程目录,并创建一个工程:
luckfox@luckfox:~$ mkdir -p lvgl_project/project_01
luckfox@luckfox:~$ cd lvgl_project/project_01/
6.2 复制文件
-
复制根目录下的 lvgl 文件夹中的 lvgl、lv_drivers 、lv_demos目录
-
复制 lv_port_linux_frame_buffer 中的 main.c 与 Makefile
-
复制 lvgl 中的 lv_conf_template.h 重命名为 lv_conf.h
-
复制 lv_drivers 中的 lv_drv_conf_template.h 重命名为 lv_drv_conf.h
-
复制 lv_demos 中的 lv_demo_conf_template.h 重命名为 lv_demo_conf.h
cp -r ~/lvgl/lvgl ./ cp -r ~/lvgl/lv_drivers ./ cp ~/lvgl/lvgl/lv_conf_template.h ./lv_conf.h cp ~/lvgl/lv_drivers/lv_drv_conf_template.h ./lv_drv_conf.h cp ~/lv_demos/lv_demo_conf_template.h ./lv_demo_conf.h cp -r lv_demos ./ cp ~/lvgl/lv_port_linux_frame_buffer/main.c ./ cp ~/lvgl/lv_port_linux_frame_buffer/Makefile ./
6.3 修改配置
lv_conf.h
15c15
< #if 1 /*Set it to "1" to enable content*/
---
> #if 0 /*Set it to "1" to enable content*/
52c52
< # define LV_MEM_SIZE (10U * 1024U * 1024U) /*[bytes]*/
---
> # define LV_MEM_SIZE (32U * 1024U) /*[bytes]*/
81c81
< #define LV_DISP_DEF_REFR_PERIOD 10 /*[ms]*/
---
> #define LV_DISP_DEF_REFR_PERIOD 30 /*[ms]*/
84c84
< #define LV_INDEV_DEF_READ_PERIOD 10 /*[ms]*/
---
> #define LV_INDEV_DEF_READ_PERIOD 30 /*[ms]*/
88c88
< #define LV_TICK_CUSTOM 1
---
> #define LV_TICK_CUSTOM 0
90,93c90,91
< // #define LV_TICK_CUSTOM_INCLUDE "Arduino.h" /*Header for the system time function*/
< // #define LV_TICK_CUSTOM_SYS_TIME_EXPR (millis()) /*Expression evaluating to current system time in ms*/
< #define LV_TICK_CUSTOM_INCLUDE <stdint.h> /*Header for the system time function*/
< #define LV_TICK_CUSTOM_SYS_TIME_EXPR (custom_tick_get()) /*Expression evaluating to current system time in ms*/
---
> #define LV_TICK_CUSTOM_INCLUDE "Arduino.h" /*Header for the system time function*/
> #define LV_TICK_CUSTOM_SYS_TIME_EXPR (millis()) /*Expression evaluating to current system time in ms*/
176c174
< #define LV_USE_LOG 1
---
> #define LV_USE_LOG 0
190c188
< # define LV_LOG_PRINTF 1
---
> # define LV_LOG_PRINTF 0
307,309c305,307
< #define LV_FONT_MONTSERRAT_8 1
< #define LV_FONT_MONTSERRAT_10 1
< #define LV_FONT_MONTSERRAT_12 1
---
> #define LV_FONT_MONTSERRAT_8 0
> #define LV_FONT_MONTSERRAT_10 0
> #define LV_FONT_MONTSERRAT_12 0
311c309
< #define LV_FONT_MONTSERRAT_16 1
---
> #define LV_FONT_MONTSERRAT_16 0
lv_drv_conf.h
11c11
< #if 1 /*Set it to "1" to enable the content*/
---
> #if 0 /*Set it to "1" to enable the content*/
319c319
< # define USE_FBDEV 1
---
> # define USE_FBDEV 0
lv_demo_conf.h
11c11
< #if 1 /*Set it to "1" to enable the content*/
---
> #if 0 /*Set it to "1" to enable the content*/
29c29
< #define LV_USE_DEMO_WIDGETS 1
---
> #define LV_USE_DEMO_WIDGETS 0
显示自定义图片
-
在线转换
-
转换图片
注意,PNG图片颜色格式选择CF_TRUE_COLOR_ALPHA,JPG图片颜色格式选择CF_TRUE_COLOR,转换完成后将c文件拷贝至工程目录
main.c
#include "lvgl/lvgl.h"
// #include "lvgl/demos/lv_demos.h"
#include "lv_demos/lv_demo.h"
#include "lv_drivers/display/fbdev.h"
// #include "lv_drivers/indev/evdev.h"
#include <unistd.h>
#include <pthread.h>
#include <time.h>
#include <sys/time.h>
#define DISP_BUF_SIZE (128 * 160)
int main(void)
{
/*LittlevGL init*/
lv_init();
/*Linux frame buffer device init*/
fbdev_init();
/*A small buffer for LittlevGL to draw the screen's content*/
static lv_color_t buf[DISP_BUF_SIZE];
/*Initialize a descriptor for the buffer*/
static lv_disp_draw_buf_t disp_buf;
lv_disp_draw_buf_init(&disp_buf, buf, NULL, DISP_BUF_SIZE);
/*Initialize and register a display driver*/
static lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
disp_drv.draw_buf = &disp_buf;
disp_drv.flush_cb = fbdev_flush;
disp_drv.hor_res = 128;
disp_drv.ver_res = 160;
lv_disp_drv_register(&disp_drv);
LV_IMG_DECLARE(fll); //申明外部图片
//无外部输入设备,注释下面代码
// evdev_init();
// static lv_indev_drv_t indev_drv_1;
// lv_indev_drv_init(&indev_drv_1); /*Basic initialization*/
// indev_drv_1.type = LV_INDEV_TYPE_POINTER;
// /*This function will be called periodically (by the library) to get the mouse position and state*/
// indev_drv_1.read_cb = evdev_read;
// lv_indev_t *mouse_indev = lv_indev_drv_register(&indev_drv_1);
//未移植鼠标样式,注释下面代码
// /*Set a cursor for the mouse*/
// LV_IMG_DECLARE(mouse_cursor_icon)
// lv_obj_t * cursor_obj = lv_img_create(lv_scr_act()); /*Create an image object for the cursor */
// lv_img_set_src(cursor_obj, &mouse_cursor_icon); /*Set the image source*/
// lv_indev_set_cursor(mouse_indev, cursor_obj); /*Connect the image object to the driver*/
//不使用官方demo
/*Create a Demo*/
// lv_demo_widgets();
lv_obj_t * img1 = lv_img_create(lv_scr_act());
lv_img_set_src(img1, &fll);
lv_obj_align(img1, LV_ALIGN_CENTER, 0, 0);
/*Handle LitlevGL tasks (tickless mode)*/
while(1) {
lv_timer_handler();
usleep(5000);
}
return 0;
}
/*Set in lv_conf.h as `LV_TICK_CUSTOM_SYS_TIME_EXPR`*/
uint32_t custom_tick_get(void)
{
static uint64_t start_ms = 0;
if(start_ms == 0) {
struct timeval tv_start;
gettimeofday(&tv_start, NULL);
start_ms = (tv_start.tv_sec * 1000000 + tv_start.tv_usec) / 1000;
}
struct timeval tv_now;
gettimeofday(&tv_now, NULL);
uint64_t now_ms;
now_ms = (tv_now.tv_sec * 1000000 + tv_now.tv_usec) / 1000;
uint32_t time_ms = now_ms - start_ms;
return time_ms;
}
Makefile
#
# Makefile
#
#CC ?= gcc
CC = /home/main/Desktop/luckfox-pico/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-gcc
LVGL_DIR_NAME ?= lvgl
LVGL_DIR ?= ${shell pwd}
CFLAGS ?= -O3 -g0 -I$(LVGL_DIR)/ -Wall -Wshadow -Wundef -Wmissing-prototypes -Wno-discarded-qualifiers -Wall -Wextra -Wno-unused-function -Wno-error=strict-prototypes -Wpointer-arith -fno-strict-aliasing -Wno-error=cpp -Wuninitialized -Wmaybe-uninitialized -Wno-unused-parameter -Wno-missing-field-initializers -Wtype-limits -Wsizeof-pointer-memaccess -Wno-format-nonliteral -Wno-cast-qual -Wunreachable-code -Wno-switch-default -Wreturn-type -Wmultichar -Wformat-security -Wno-ignored-qualifiers -Wno-error=pedantic -Wno-sign-compare -Wno-error=missing-prototypes -Wdouble-promotion -Wclobbered -Wdeprecated -Wempty-body -Wtype-limits -Wshift-negative-value -Wstack-usage=2048 -Wno-unused-value -Wno-unused-parameter -Wno-missing-field-initializers -Wuninitialized -Wmaybe-uninitialized -Wall -Wextra -Wno-unused-parameter -Wno-missing-field-initializers -Wtype-limits -Wsizeof-pointer-memaccess -Wno-format-nonliteral -Wpointer-arith -Wno-cast-qual -Wmissing-prototypes -Wunreachable-code -Wno-switch-default -Wreturn-type -Wmultichar -Wno-discarded-qualifiers -Wformat-security -Wno-ignored-qualifiers -Wno-sign-compare
LDFLAGS ?= -lm
BIN = demo
MAINSRC = ./main.c
CSRCS += fll.c# 添加这行来包含外部转换的.c图片
include $(LVGL_DIR)/lvgl/lvgl.mk
include $(LVGL_DIR)/lv_drivers/lv_drivers.mk
OBJEXT ?= .o
AOBJS = $(ASRCS:.S=$(OBJEXT))
COBJS = $(CSRCS:.c=$(OBJEXT))
MAINOBJ = $(MAINSRC:.c=$(OBJEXT))
SRCS = $(ASRCS) $(CSRCS) $(MAINSRC)
OBJS = $(AOBJS) $(COBJS)
## MAINOBJ -> OBJFILES
all: default
%.o: %.c
@$(CC) $(CFLAGS) -c $< -o $@
@echo "CC $<"
default: $(AOBJS) $(COBJS) $(MAINOBJ)
$(CC) -o $(BIN) $(MAINOBJ) $(AOBJS) $(COBJS) $(LDFLAGS)
clean:
rm -f $(BIN) $(AOBJS) $(COBJS) $(MAINOBJ)
编译
main@main-vm:~/Desktop/lvgl_project1$ ls
lv_demo_conf.h lv_drivers lvgl Makefile
fll.c lv_conf.h lv_demos lv_drv_conf.h main.c Makefile1
main@main-vm:~/Desktop/lvgl_project1$ file demo
demo: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV),
dynamically linked, interpreter /lib/ld-uClibc.so.0, with debug_info, not stripped
七、部署到板子并运行
[root@luckfox etc]# cd /
[root@luckfox ]# chmod 777 de
demo dev/
[root@luckfox ]# chmod 777 demo
[root@luckfox ]# ./demo