本人【Luckfox幸狐 RV1106 Linux 开发板测评】帖子链接:
四、通过PC机共享网络接入Internet和Ubuntu下Python点灯
本篇测评基于上一测评中开启的SPI0接口,进一步通过修改设备树文件<SDK目录>/sysdrv/source/kernel/arch/arm/boot/dts/rv1106g-luckfox-pico-pro-max.dts,使得Luckfox Pro Max驱动(SPI0口)的0.96寸合宙LCD(ST7735)注册为FrameBuffer设备。这样,LVGL就可以基于FB作为显示接口。
本次实验主要参考Luckfox Wiki之LVGL使用指南篇(https://wiki.luckfox.com/zh/Luckfox-Pico/Luckfox-Pico-LVGL)。
官方文档中提供的LVGL.zip包含系统镜像、lvgl_demo工程和设备文件,可惜是基于微雪Pico-LCD-1.3显示屏(ST7789驱动),与本人手中的合宙LCD不同。好在两款LCD驱动器都是Sitronix公司的产品,而且Linux系统中都有驱动。
微雪1.3寸屏附带五向开关和ABXY四按键,所以LVGL.zip中的设备树文件除了SPI0的配置还有这些按键控制IO的配置,因为用到IO多,所以I2C3等接口都被禁用了。本人使用的合宙0.96寸屏只附带五向开关,所以没有直接拷贝设备树文件,只是修改了SPI0部分。完整的rv1106g-luckfox-pico-pro-max.dts文件代码如下:
// 基于官方案例提供设备树源码,剔除注释部分和未使用IO部分
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2022 Rockchip Electronics Co., Ltd.
*/
/dts-v1/;
#include "rv1106.dtsi"
#include "rv1106-evb.dtsi"
#include "rv1106-luckfox-pico-pro-max-ipc.dtsi"
/ {
model = "Luckfox Pico Max";
compatible = "rockchip,rv1103g-38x38-ipc-v10", "rockchip,rv1106";
/*KEY*/
/*KEY-DOWN*/
gpio2pa0:gpio2pa0 {
compatible = "regulator-fixed";
pinctrl-names = "default";
pinctrl-0 = <&gpio2_pa0>;
regulator-name = "gpio2_pa0";
regulator-always-on;
};
/*KEY-RIGHT*/
gpio2pa2:gpio2pa2 {
compatible = "regulator-fixed";
pinctrl-names = "default";
pinctrl-0 = <&gpio2_pa2>;
regulator-name = "gpio2_pa2";
regulator-always-on;
};
/*KEY-LEFT*/
gpio2pa4:gpio2pa4 {
compatible = "regulator-fixed";
pinctrl-names = "default";
pinctrl-0 = <&gpio2_pa4>;
regulator-name = "gpio2_pa4";
regulator-always-on;
};
/*KEY-CENTER*/
gpio1pc6:gpio1pc6 {
compatible = "regulator-fixed";
pinctrl-names = "default";
pinctrl-0 = <&gpio1_pc6>;
regulator-name = "gpio1_pc6";
regulator-always-on;
};
/*KEY-UP*/
gpio1pc7:gpio1pc7 {
compatible = "regulator-fixed";
pinctrl-names = "default";
pinctrl-0 = <&gpio1_pc7>;
regulator-name = "gpio1_pc7";
regulator-always-on;
};
/*LCD_RES*/
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";
pinctrl-names = "default";
pinctrl-0 = <&gpio2_pb0>;
regulator-name = "gpio2_pb0";
regulator-always-on;
};
/*LCD_DC*/
gpio2pb1:gpio2pb1 {
compatible = "regulator-fixed";
pinctrl-names = "default";
pinctrl-0 = <&gpio2_pb1>;
regulator-name = "gpio2_pb1";
regulator-always-on;
};
};
/**********GPIO**********/
&pinctrl {
/*KEY*/
gpio2-pa0 {
gpio2_pa0:gpio2-pa0 {
rockchip,pins = <2 RK_PA0 RK_FUNC_GPIO &pcfg_pull_up>;
};
};
gpio2-pa2 {
gpio2_pa2:gpio2-pa2 {
rockchip,pins = <2 RK_PA2 RK_FUNC_GPIO &pcfg_pull_up>;
};
};
gpio2-pa4 {
gpio2_pa4:gpio2-pa4 {
rockchip,pins = <2 RK_PA4 RK_FUNC_GPIO &pcfg_pull_up>;
};
};
gpio1-pc6 {
gpio1_pc6:gpio1-pc6 {
rockchip,pins = <1 RK_PC6 RK_FUNC_GPIO &pcfg_pull_up>;
};
};
gpio1-pc7 {
gpio1_pc7:gpio1-pc7 {
rockchip,pins = <1 RK_PC7 RK_FUNC_GPIO &pcfg_pull_up>;
};
};
/*RESET*/
gpio1-pc3 {
gpio1_pc3:gpio1-pc3 {
rockchip,pins = <1 RK_PC3 RK_FUNC_GPIO &pcfg_pull_none>;
};
};
/*BL*/
gpio2-pb0 {
gpio2_pb0:gpio2-pb0 {
rockchip,pins = <2 RK_PB0 RK_FUNC_GPIO &pcfg_pull_none>;
};
};
/*LCD_DC*/
gpio2-pb1 {
gpio2_pb1:gpio2-pb1 {
rockchip,pins = <2 RK_PB1 RK_FUNC_GPIO &pcfg_pull_none>;
};
};
};
/**********FLASH**********/
&sfc {
status = "okay";
flash@0 {
compatible = "spi-nand";
reg = <0>;
spi-max-frequency = <75000000>;
spi-rx-bus-width = <4>;
spi-tx-bus-width = <1>;
};
};
/**********ETH**********/
&gmac {
status = "okay";
};
/**********USB**********/
&usbdrd_dwc3 {
status = "okay";
dr_mode = "peripheral";
};
/**********I2C**********/
&i2c3 {
status = "okay";
pinctrl-0 = <&i2c3m1_xfer>;
clock-frequency = <100000>;
};
/**********SPI**********/
&spi0 {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&spi0m0_cs0 &spi0m0_pins>;
st7735s@0{
status = "okay";
compatible = "sitronix,st7735r";
reg = <0>;
spi-max-frequency = <48000000>;
// width = <80>;
// height = <160>;
rotate = <270>;
fps = <30>;
buswidth = <8>;
debug = <0x7>;
led-gpios = <&gpio2 RK_PB0 GPIO_ACTIVE_HIGH>;//BL
dc = <&gpio2 RK_PB1 GPIO_ACTIVE_HIGH>; //DC
reset = <&gpio1 RK_PC3 GPIO_ACTIVE_LOW>; //RES
};
};
&pinctrl {
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>, */
/* spi_mosi_m0 */
<1 RK_PC2 6 &pcfg_pull_none>;
};
};
};
除了设备树文件,还要修改<SDK目录>/sysdrv/source/kernel/arch/arm/configs/luckfox_rv1106_linux_defconfig,以添加FB文件支持。直接编辑文件可以省去make menuconfig的步骤。
图12-1 FB设备支持添加项
设备树和配置文件修改后,就可以再次编译内核并烧写开发板了。更新系统镜像后再次启动开发板,就可以看到设备文件:/dev/fb0。
图12-2 开发板具备了fb0
官方提供的LVGL.zip中包含测试项目lvgl_demo,将项目文件夹拷贝到虚拟机,因为项目是基于FB的LVGL移植案例,所以不用再做其它修改,只需要更新Makefile中CC变量的赋值为Luckfox SDK路径。
另外,合宙屏在使用案例时一上来控制台输出报错信息:ioctl(FBIOBLANK): Invalid argument,参考一篇技术贴(https://blog.csdn.net/klp1358484518/article/details/130032766),需要注释掉lv_drivers/display/fbdev.c中的几行代码:
// 位于fbdev_init()函数
// Make sure that the display is on.
// if (ioctl(fbfd, FBIOBLANK, FB_BLANK_UNBLANK) != 0) {
// perror("ioctl(FBIOBLANK)");
// return;
// }
当然,由于本人使用的是0.96寸屏,所以main.c做了修改——原案例显示图片适配1.3寸屏,这里改为屏幕中心显示一行字符串。main.c的代码如下:
#include "lvgl/lvgl.h"
#include "DEV_Config.h"
#include "lv_drivers/display/fbdev.h"
#include <unistd.h>
#include <pthread.h>
#include <time.h>
#include <sys/time.h>
#define DISP_BUF_SIZE (160 * 128)
void fbdev_flush(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_p);
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 = 160;
disp_drv.ver_res = 128;
lv_disp_drv_register(&disp_drv);
/*Initialize pin*/
DEV_ModuleInit();
lv_obj_t *scr = lv_disp_get_scr_act(NULL);
// 显示字符串
lv_obj_t *label = lv_label_create(scr);
lv_label_set_text(label, "Luckfox LVGL FB!");
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
/*Create a cursor*/
lv_obj_t *cursor = lv_img_create(scr);
lv_img_set_src(cursor, LV_SYMBOL_GPS);
lv_obj_set_pos(cursor, 70, 40);
int x=70,y=40,move=0;
/*Handle LitlevGL tasks (tickless mode)*/
while(1) {
lv_timer_handler();
usleep(5000);
/*Joystick*/
if(GET_KEY_RIGHT == 0){
x += 1;
if(x > 226)x = 226;
move =1;
}
else if(GET_KEY_LEFT == 0){
x -= 1;
if(x < 0)x = 0;
move =1;
}
else if(GET_KEY_UP == 0){
y -= 1;
if(y < 0)y = 0;
move =1;
}
else if(GET_KEY_DOWN == 0){
y += 1;
if(y > 224)y = 224;
move =1;
}
else if(GET_KEY_PRESS == 0){
x = 80;
y = 64;
move =1;
}
if(move == 1){
lv_obj_set_pos(cursor, x, y);
move = 0;
}
}
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;
}
图12-3 案例效果——五向开关拨动屏幕上的图标也会移动
项目看似简单但也耗费了在下一天的时间进行调试,主要是屏幕适配的问题,最初屏幕是纵向显示的(即上图字体左旋90°)。而本人又将调整屏幕方向的思路放在修改程序代码上,使用lv_disp_set_rotation()函数,但是没有成功,判断就是底层驱动不支持旋转屏幕,于是转思路尝试修改设备文件。注:后续发现LVGL文档中提到如果没有硬件支持,可以使用lv_draw_sw_rotate()函数进行转屏,不过自己没有进行尝试,也不清楚是否有效。
图12-4 LVGL在线文档Rotation部分页面截图
关于ST7735设备树节点描述的资料不太好找,本人也是搜索了几篇技术博客,从中分析到“旋转、宽高”属性的设置方法。不过经过验证,屏幕尺寸如果依照0.98屏的真实参数设置会出现部分屏幕不能使用的问题,索性只设置了rotate。
而关于宽高设置后出现问题的原因,个人猜测可能是Linux源码中提供的ST7735r驱动就是适配160*128尺寸的,不能兼容其它尺寸。这一点也是查看了Linux源码 Documentation中的相应说明文档分析出来的——<Luckfox SDK>/sysdrv/source/kernel/Documentation/devicetree/bindings/display/sitronix,st7735r.yaml。
图12-5 合宙0.96寸屏的设备树节点
图12-6 设置了宽高属性的效果——屏幕一部分无法使用
图12-7 sitronix,st7735r.yaml文档内容截图
上述系统镜像烧写后,开发板中通过命令fbset,可以看到当前屏幕分辨率就是160x128——关于真实屏幕参数为160x80系统却可以识别为160x128在下也是觉得蛮魔性的。有鉴于此,在下也是在程序中设置屏幕尺寸为160x128,没想到还真能准确显示了。
图12-8 fbset命令查看屏幕分辨率
图12-9 LVGL设置宽高依据系统分辨率
这个屏幕什么价格,不贵的话我也搞一个,显示起来还挺方便
引用: LitchiCheng 发表于 2024-2-19 09:34 这个屏幕什么价格,不贵的话我也搞一个,显示起来还挺方便
合宙的,10几块钱吧,其实还不如买微雪的,官方案例可以适配,尺寸也大点。
引用: sonicfirr 发表于 2024-2-19 10:33 合宙的,10几块钱吧,其实还不如买微雪的,官方案例可以适配,尺寸也大点。
可以可以,我去看看微雪的