前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >全志R128应用开发案例——DBI驱动ST7789V1.3寸LCD屏

全志R128应用开发案例——DBI驱动ST7789V1.3寸LCD屏

作者头像
阿志小管家
发布于 2024-02-02 11:18:36
发布于 2024-02-02 11:18:36
65400
代码可运行
举报
运行总次数:0
代码可运行

DBI驱动ST7789V1.3寸LCD

之前介绍了 R128 平台使用 SPI 驱动显示屏 ST7789V1.3寸 LCD,接下来介绍的是使用 DBI 接口驱动。

R128 平台提供了 SPI DBI 的 SPI TFT 接口,具有如下特点:

  • Supports DBI Type C 3 Line/4 Line Interface Mode
  • Supports 2 Data Lane Interface Mode
  • Supports data source from CPU or DMA
  • Supports RGB111/444/565/666/888 video format
  • Maximum resolution of RGB666 240 x 320@30Hz with single data lane
  • Maximum resolution of RGB888 240 x 320@60Hz or 320 x 480@30Hz with dual data lane
  • Supports tearing effect
  • Supports software flexible control video frame rate

同时,提供了 SPILCD 驱动框架以供 SPI 屏幕使用。

此次适配的SPI屏为 ZJY130S0800TG01,使用的是 DBI 进行驱动。

DBI接口的全称是 Display Bus Serial Interface ,在显示屏数据手册中,一般会说这是SPI接口,所以有人会误认为SPI屏可以使用 normal spi 去直接驱动。

SPI 接口就是俗称的4线模式,这是因为发送数据时需要额外借助DC线来区分命令和数据,与sclkcssda共四线。

DBI 分为多种接口,包括

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
0L3I1
1L3I2
2L4I1
3L4I2
4D2LI

L3I1L3I2是三线模式(不需要DC脚),区别是读时序,也就是是否需要额外脚来读寄存器。读写时序图如下:

  • L3I1写时序
  • L3I1读时序

L4I1L4I2是四线模式,与spi接口协议一样,区别是DC脚的控制是否自动化控制,另外I2和I1的区别是读时序,也就是否需要额外脚来读取寄存器。

  • L4I写时序
在这里插入图片描述
在这里插入图片描述
  • L4I读时序
在这里插入图片描述
在这里插入图片描述

D2LI是两data lane模式。发送命令部分时序与读时序与L3I1一致,下图是发送数据时的时序,不同像素格式时钟周期数量不一样。

  • D2LI写时序
在这里插入图片描述
在这里插入图片描述

可以知道,在3线模式时,发送命令前有1位A0用于指示当前发送的是数据,还是命令。而命令后面接着的数据就没有这个A0位了,代表 SPI 需要在 9 位和 8 位之间来回切换,而在读数据时,更是需要延时 dummy clock 才能读数据,normal spi 都很难,甚至无法实现。所以 normal spi 只能模拟 4 线的 DBI 的写操作。读操作只能通过模拟IO来实现。

对于R128这类支持 DBI 接口的CPU,可以选择不去了解 SPI。直接选用 DBI 来驱动屏幕。由于不需要模拟延时和切换数据,屏幕驱动效率将有明显提升。

在这里插入图片描述
在这里插入图片描述

引脚配置如下:

R128 Devkit

TFT 模块

PA12

CS

PA13

SCL

PA18

SDA

PA9

BLK

PA20

RES

PA19

DC

3V3

VCC

GND

GND

载入方案

我们使用的开发板是 R128-Devkit,需要开发 C906 核心的应用程序,所以载入方案选择 r128s2_module_c906

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ source envsetup.sh 
$ lunch_rtos 1
在这里插入图片描述
在这里插入图片描述

设置 DBI 驱动

屏幕使用的是SPI驱动,所以需要勾选SPI驱动,运行 mrtos_menuconfig 进入配置页面。前往下列地址找到 SPI Devices

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Drivers Options  --->
    soc related device drivers  --->
        DBI Devices --->
        -*- enable dbi driver
在这里插入图片描述
在这里插入图片描述
配置 SPI 引脚

DBI同样使用 SPI 控制器,所以需要配置SPI的相关配置。打开你喜欢的编辑器,修改文件:board/r128s2/module/configs/sys_config.fex,在这里我们不需要用到SPI WP引脚,注释掉即可。SPI HOLD 需要作为 DC 脚接入LCD模块。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
;----------------------------------------------------------------------------------
;SPI controller configuration
;----------------------------------------------------------------------------------
;Please config spi in dts
[spi1]
spi1_used       = 1
spi1_cs_number  = 1
spi1_cs_bitmap  = 1
spi1_cs0        = port:PA12<6><0><3><default>
spi1_sclk       = port:PA13<6><0><3><default>
spi1_mosi       = port:PA18<6><0><3><default>
spi1_miso       = port:PA21<6><0><3><default>
spi1_hold       = port:PA19<6><0><2><default>
;spi1_wp         = port:PA20<6><0><2><default>
在这里插入图片描述
在这里插入图片描述

设置 PWM 驱动

屏幕背光使用的是PWM驱动,所以需要勾选PWM驱动,运行 mrtos_menuconfig 进入配置页面。前往下列地址找到 PWM Devices

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Drivers Options  --->
    soc related device drivers  --->
        PWM Devices --->
        -*- enable pwm driver
在这里插入图片描述
在这里插入图片描述
配置 PWM 引脚

打开你喜欢的编辑器,修改文件:board/r128s2/module/configs/sys_config.fex,增加 PWM1 节点

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[pwm1]
pwm_used        = 1
pwm_positive    = port:PA9<4><0><3><default>
在这里插入图片描述
在这里插入图片描述

设置 SPI LCD 驱动

SPI LCD 由专门的驱动管理。运行 mrtos_menuconfig 进入配置页面。前往下列地址找到 SPILCD Devices ,注意同时勾选 spilcd hal APIs test 方便测试使用。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Drivers Options  --->
    soc related device drivers  --->
        [*] DISP Driver Support(spi_lcd)
        [*]   spilcd hal APIs test
在这里插入图片描述
在这里插入图片描述

编写 SPI LCD 显示屏驱动

获取屏幕初始化序列

首先询问屏厂提供驱动源码

在这里插入图片描述
在这里插入图片描述

找到 LCD 的初始化序列代码

在这里插入图片描述
在这里插入图片描述

找到屏幕初始化的源码

在这里插入图片描述
在这里插入图片描述

整理后的初始化代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
LCD_WR_REG(0x11); // Sleep out 
delay_ms(120);    // Delay 120ms 
//************* Start Initial Sequence **********// 
LCD_WR_REG(0x36);
LCD_WR_DATA8(0x00);

LCD_WR_REG(0x3A);     
LCD_WR_DATA8(0x05);   

LCD_WR_REG(0xB2);     
LCD_WR_DATA8(0x1F);   
LCD_WR_DATA8(0x1F);   
LCD_WR_DATA8(0x00);   
LCD_WR_DATA8(0x33);   
LCD_WR_DATA8(0x33);   

LCD_WR_REG(0xB7);     
LCD_WR_DATA8(0x35);   

LCD_WR_REG(0xBB);     
LCD_WR_DATA8(0x20);   // 2b

LCD_WR_REG(0xC0);     
LCD_WR_DATA8(0x2C);   

LCD_WR_REG(0xC2);     
LCD_WR_DATA8(0x01);   

LCD_WR_REG(0xC3);     
LCD_WR_DATA8(0x01);   

LCD_WR_REG(0xC4);     
LCD_WR_DATA8(0x18);   // VDV, 0x20:0v

LCD_WR_REG(0xC6);     
LCD_WR_DATA8(0x13);   // 0x13:60Hz   

LCD_WR_REG(0xD0);     
LCD_WR_DATA8(0xA4);   
LCD_WR_DATA8(0xA1);   

LCD_WR_REG(0xD6);     
LCD_WR_DATA8(0xA1);   // sleep in后,gate输出为GND

LCD_WR_REG(0xE0);     
LCD_WR_DATA8(0xF0);   
LCD_WR_DATA8(0x04);   
LCD_WR_DATA8(0x07);   
LCD_WR_DATA8(0x04);   
LCD_WR_DATA8(0x04);   
LCD_WR_DATA8(0x04);   
LCD_WR_DATA8(0x25);   
LCD_WR_DATA8(0x33);   
LCD_WR_DATA8(0x3C);   
LCD_WR_DATA8(0x36);   
LCD_WR_DATA8(0x14);   
LCD_WR_DATA8(0x12);   
LCD_WR_DATA8(0x29);   
LCD_WR_DATA8(0x30);   

LCD_WR_REG(0xE1);     
LCD_WR_DATA8(0xF0);   
LCD_WR_DATA8(0x02);   
LCD_WR_DATA8(0x04);   
LCD_WR_DATA8(0x05);   
LCD_WR_DATA8(0x05);   
LCD_WR_DATA8(0x21);   
LCD_WR_DATA8(0x25);   
LCD_WR_DATA8(0x32);   
LCD_WR_DATA8(0x3B);   
LCD_WR_DATA8(0x38);   
LCD_WR_DATA8(0x12);   
LCD_WR_DATA8(0x14);   
LCD_WR_DATA8(0x27);   
LCD_WR_DATA8(0x31);   

LCD_WR_REG(0xE4);     
LCD_WR_DATA8(0x1D);   // 使用240根gate  (N+1)*8
LCD_WR_DATA8(0x00);   // 设定gate起点位置
LCD_WR_DATA8(0x00);   // 当gate没有用完时,bit4(TMG)设为0

LCD_WR_REG(0x21);     

LCD_WR_REG(0x29);     
用现成驱动改写 SPI LCD 驱动

选择一个现成的 SPI LCD 改写即可,这里选择 nv3029s.c 驱动来修改

在这里插入图片描述
在这里插入图片描述

复制这两个驱动,重命名为 st7789v.c

在这里插入图片描述
在这里插入图片描述

先编辑 st7789v.hnv3029s 改成 st7789v

在这里插入图片描述
在这里插入图片描述
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#ifndef _ST7789V_H
#define _ST7789V_H

#include "panels.h"

struct __lcd_panel st7789v_panel;

#endif /*End of file*/

编辑 st7789v.cnv3029s 改成 st7789v

在这里插入图片描述
在这里插入图片描述
编写初始化序列

先删除 static void LCD_panel_init(unsigned int sel) 中的初始化函数。

在这里插入图片描述
在这里插入图片描述

然后将屏厂提供的初始化序列复制进来

在这里插入图片描述
在这里插入图片描述

然后按照 spi_lcd 框架的接口改写驱动接口,具体接口如下

屏厂函数

SPILCD框架接口

LCD_WR_REG

sunxi_lcd_cmd_write

LCD_WR_DATA8

sunxi_lcd_para_write

delay_ms

sunxi_lcd_delay_ms

可以直接进行替换

在这里插入图片描述
在这里插入图片描述

完成后如下

在这里插入图片描述
在这里插入图片描述

然后对照屏厂提供的驱动修改 address 函数

在这里插入图片描述
在这里插入图片描述

做如下修改

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static void address(unsigned int sel, int x, int y, int width, int height)
{
	sunxi_lcd_cmd_write(sel, 0x2B); /* Set row address */
	sunxi_lcd_para_write(sel, (y >> 8) & 0xff);
	sunxi_lcd_para_write(sel, y & 0xff);
	sunxi_lcd_para_write(sel, (height >> 8) & 0xff);
	sunxi_lcd_para_write(sel, height & 0xff);
	sunxi_lcd_cmd_write(sel, 0x2A); /* Set coloum address */
	sunxi_lcd_para_write(sel, (x >> 8) & 0xff);
	sunxi_lcd_para_write(sel, x & 0xff);
	sunxi_lcd_para_write(sel, (width >> 8) & 0xff);
	sunxi_lcd_para_write(sel, width & 0xff);
	sunxi_lcd_cmd_write(sel, 0x2c);
}

完成驱动如下

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include "st7789v.h"

static void LCD_power_on(u32 sel);
static void LCD_power_off(u32 sel);
static void LCD_bl_open(u32 sel);
static void LCD_bl_close(u32 sel);
static void LCD_panel_init(u32 sel);
static void LCD_panel_exit(u32 sel);
#define RESET(s, v) sunxi_lcd_gpio_set_value(s, 0, v)
#define power_en(sel, val) sunxi_lcd_gpio_set_value(sel, 0, val)

static struct disp_panel_para info[LCD_FB_MAX];

static void address(unsigned int sel, int x, int y, int width, int height)
{
	sunxi_lcd_cmd_write(sel, 0x2B); /* Set row address */
	sunxi_lcd_para_write(sel, (y >> 8) & 0xff);
	sunxi_lcd_para_write(sel, y & 0xff);
	sunxi_lcd_para_write(sel, (height >> 8) & 0xff);
	sunxi_lcd_para_write(sel, height & 0xff);
	sunxi_lcd_cmd_write(sel, 0x2A); /* Set coloum address */
	sunxi_lcd_para_write(sel, (x >> 8) & 0xff);
	sunxi_lcd_para_write(sel, x & 0xff);
	sunxi_lcd_para_write(sel, (width >> 8) & 0xff);
	sunxi_lcd_para_write(sel, width & 0xff);
	sunxi_lcd_cmd_write(sel, 0x2c);
}

static void LCD_panel_init(unsigned int sel)
{
	if (bsp_disp_get_panel_info(sel, &info[sel])) {
		lcd_fb_wrn("get panel info fail!\n");
		return;
	}

	sunxi_lcd_cmd_write(sel, 0x11); // Sleep out 
	sunxi_lcd_delay_ms(120);    // Delay 120ms 
	//************* Start Initial Sequence **********// 
	sunxi_lcd_cmd_write(sel, 0x36);
	sunxi_lcd_para_write(sel, 0x00);

	sunxi_lcd_cmd_write(sel, 0x3A);     
	sunxi_lcd_para_write(sel, 0x05);   

	sunxi_lcd_cmd_write(sel, 0xB2);     
	sunxi_lcd_para_write(sel, 0x1F);   
	sunxi_lcd_para_write(sel, 0x1F);   
	sunxi_lcd_para_write(sel, 0x00);   
	sunxi_lcd_para_write(sel, 0x33);   
	sunxi_lcd_para_write(sel, 0x33);   

	sunxi_lcd_cmd_write(sel, 0xB7);     
	sunxi_lcd_para_write(sel, 0x35);   

	sunxi_lcd_cmd_write(sel, 0xBB);     
	sunxi_lcd_para_write(sel, 0x20);   // 2b

	sunxi_lcd_cmd_write(sel, 0xC0);     
	sunxi_lcd_para_write(sel, 0x2C);   

	sunxi_lcd_cmd_write(sel, 0xC2);     
	sunxi_lcd_para_write(sel, 0x01);   

	sunxi_lcd_cmd_write(sel, 0xC3);     
	sunxi_lcd_para_write(sel, 0x01);   

	sunxi_lcd_cmd_write(sel, 0xC4);     
	sunxi_lcd_para_write(sel, 0x18);   // VDV, 0x20:0v

	sunxi_lcd_cmd_write(sel, 0xC6);     
	sunxi_lcd_para_write(sel, 0x13);   // 0x13:60Hz   

	sunxi_lcd_cmd_write(sel, 0xD0);     
	sunxi_lcd_para_write(sel, 0xA4);   
	sunxi_lcd_para_write(sel, 0xA1);   

	sunxi_lcd_cmd_write(sel, 0xD6);     
	sunxi_lcd_para_write(sel, 0xA1);   // sleep in后,gate输出为GND

	sunxi_lcd_cmd_write(sel, 0xE0);     
	sunxi_lcd_para_write(sel, 0xF0);   
	sunxi_lcd_para_write(sel, 0x04);   
	sunxi_lcd_para_write(sel, 0x07);   
	sunxi_lcd_para_write(sel, 0x04);   
	sunxi_lcd_para_write(sel, 0x04);   
	sunxi_lcd_para_write(sel, 0x04);   
	sunxi_lcd_para_write(sel, 0x25);   
	sunxi_lcd_para_write(sel, 0x33);   
	sunxi_lcd_para_write(sel, 0x3C);   
	sunxi_lcd_para_write(sel, 0x36);   
	sunxi_lcd_para_write(sel, 0x14);   
	sunxi_lcd_para_write(sel, 0x12);   
	sunxi_lcd_para_write(sel, 0x29);   
	sunxi_lcd_para_write(sel, 0x30);   

	sunxi_lcd_cmd_write(sel, 0xE1);     
	sunxi_lcd_para_write(sel, 0xF0);   
	sunxi_lcd_para_write(sel, 0x02);   
	sunxi_lcd_para_write(sel, 0x04);   
	sunxi_lcd_para_write(sel, 0x05);   
	sunxi_lcd_para_write(sel, 0x05);   
	sunxi_lcd_para_write(sel, 0x21);   
	sunxi_lcd_para_write(sel, 0x25);   
	sunxi_lcd_para_write(sel, 0x32);   
	sunxi_lcd_para_write(sel, 0x3B);   
	sunxi_lcd_para_write(sel, 0x38);   
	sunxi_lcd_para_write(sel, 0x12);   
	sunxi_lcd_para_write(sel, 0x14);   
	sunxi_lcd_para_write(sel, 0x27);   
	sunxi_lcd_para_write(sel, 0x31);   

	sunxi_lcd_cmd_write(sel, 0xE4);     
	sunxi_lcd_para_write(sel, 0x1D);   // 使用240根gate  (N+1)*8
	sunxi_lcd_para_write(sel, 0x00);   // 设定gate起点位置
	sunxi_lcd_para_write(sel, 0x00);   // 当gate没有用完时,bit4(TMG)设为0

	sunxi_lcd_cmd_write(sel, 0x21);     

	sunxi_lcd_cmd_write(sel, 0x29);   

	if (info[sel].lcd_x < info[sel].lcd_y)
		address(sel, 0, 0, info[sel].lcd_x - 1, info[sel].lcd_y - 1);
	else
		address(sel, 0, 0, info[sel].lcd_y - 1, info[sel].lcd_x - 1);
}

static void LCD_panel_exit(unsigned int sel)
{
	sunxi_lcd_cmd_write(sel, 0x28);
	sunxi_lcd_delay_ms(20);
	sunxi_lcd_cmd_write(sel, 0x10);
	sunxi_lcd_delay_ms(20);
	sunxi_lcd_pin_cfg(sel, 0);
}

static s32 LCD_open_flow(u32 sel)
{
	lcd_fb_here;
	/* open lcd power, and delay 50ms */
	LCD_OPEN_FUNC(sel, LCD_power_on, 50);
	/* open lcd power, than delay 200ms */
	LCD_OPEN_FUNC(sel, LCD_panel_init, 200);

	LCD_OPEN_FUNC(sel, lcd_fb_black_screen, 50);
	/* open lcd backlight, and delay 0ms */
	LCD_OPEN_FUNC(sel, LCD_bl_open, 0);

	return 0;
}

static s32 LCD_close_flow(u32 sel)
{
	lcd_fb_here;
	/* close lcd backlight, and delay 0ms */
	LCD_CLOSE_FUNC(sel, LCD_bl_close, 50);
	/* open lcd power, than delay 200ms */
	LCD_CLOSE_FUNC(sel, LCD_panel_exit, 10);
	/* close lcd power, and delay 500ms */
	LCD_CLOSE_FUNC(sel, LCD_power_off, 10);

	return 0;
}

static void LCD_power_on(u32 sel)
{
	/* config lcd_power pin to open lcd power0 */
	lcd_fb_here;
	power_en(sel, 1);

	sunxi_lcd_power_enable(sel, 0);

	sunxi_lcd_pin_cfg(sel, 1);
	RESET(sel, 1);
	sunxi_lcd_delay_ms(100);
	RESET(sel, 0);
	sunxi_lcd_delay_ms(100);
	RESET(sel, 1);
}

static void LCD_power_off(u32 sel)
{
	lcd_fb_here;
	/* config lcd_power pin to close lcd power0 */
	sunxi_lcd_power_disable(sel, 0);
	power_en(sel, 0);
}

static void LCD_bl_open(u32 sel)
{
	sunxi_lcd_pwm_enable(sel);
	/* config lcd_bl_en pin to open lcd backlight */
	sunxi_lcd_backlight_enable(sel);
	lcd_fb_here;
}

static void LCD_bl_close(u32 sel)
{
	/* config lcd_bl_en pin to close lcd backlight */
	sunxi_lcd_backlight_disable(sel);
	sunxi_lcd_pwm_disable(sel);
	lcd_fb_here;
}


/* sel: 0:lcd0; 1:lcd1 */
static s32 LCD_user_defined_func(u32 sel, u32 para1, u32 para2, u32 para3)
{
	lcd_fb_here;
	return 0;
}

static int lcd_set_var(unsigned int sel, struct fb_info *p_info)
{
	return 0;
}

static int lcd_set_addr_win(unsigned int sel, int x, int y, int width, int height)
{
	address(sel, x, y, width, height);
	return 0;
}

static int lcd_blank(unsigned int sel, unsigned int en)
{
	return 0;
}

struct __lcd_panel st7789v_panel = {
    /* panel driver name, must mach the name of lcd_drv_name in sys_config.fex
       */
	.name = "st7789v",
	.func = {
		.cfg_open_flow = LCD_open_flow,
		.cfg_close_flow = LCD_close_flow,
		.lcd_user_defined_func = LCD_user_defined_func,
		.blank = lcd_blank,
		.set_var = lcd_set_var,
		.set_addr_win = lcd_set_addr_win,
	},
};
对接驱动框架

完成了屏幕驱动的编写,接下来需要对接到 SPILCD 驱动框架。首先编辑 Kconfig

在这里插入图片描述
在这里插入图片描述

增加 st7789v 的配置

在这里插入图片描述
在这里插入图片描述
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
config LCD_SUPPORT_ST7789
    bool "LCD support st7789v panel"
    default n
    ---help---
        If you want to support st7789v panel for display driver, select it.

然后编辑 panels.cpanel_array 里增加 st7789 驱动的引用

在这里插入图片描述
在这里插入图片描述

如下图

在这里插入图片描述
在这里插入图片描述
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#ifdef CONFIG_LCD_SUPPORT_ST7789V
    &st7789v_panel,
#endif

之后编辑 panels.h 同样增加引用

在这里插入图片描述
在这里插入图片描述

如下图

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#ifdef CONFIG_LCD_SUPPORT_ST7789V
extern struct __lcd_panel st7789v_panel;
#endif

最后编辑外层的 Makefile 增加编译选项

在这里插入图片描述
在这里插入图片描述

如下所示

在这里插入图片描述
在这里插入图片描述
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
obj-${CONFIG_LCD_SUPPORT_ST7789V} += panels/st7789v.o
选择 ST7789V 驱动

在 SPILCD 驱动选择界面可以看到 LCD_FB panels select 选择 SPI 屏幕的驱动

进入 LCD_FB panels select 选项

在这里插入图片描述
在这里插入图片描述

选择并勾选 [*] LCD support st7789v panel

配置 SPI LCD 引脚

这里是重点部分:打开你喜欢的编辑器,修改文件:board/r128s2/module/configs/sys_config.fex

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[lcd_fb0]
lcd_used            = 1   
lcd_model_name      = "spilcd"   
lcd_driver_name     = "st7789v" 
; 屏幕规格配置
lcd_x               = 240
lcd_y               = 240  
lcd_width           = 48
lcd_height          = 48
; SPI 速率
lcd_data_speed      = 50
; PWM 背光配置项
lcd_pwm_used        = 1
lcd_pwm_ch          = 1
lcd_pwm_freq        = 5000 
lcd_pwm_pol         = 0 
lcd_backlight       = 100
; 配置 lcd_if = 1DBI 模式,双缓冲
lcd_if              = 1
fb_buffer_num       = 2
; 配置屏幕传输数据的像素格式,这里是 LCDFB_FORMAT_RGB_565
lcd_pixel_fmt       = 10
; 配置 DBI 接口像素格式,这里是 RGB565
lcd_dbi_fmt         = 2
; 配置 DBI 时钟的行为模式,这里是自动停止模式。有数据就有时钟,没发数据就没有时钟
lcd_dbi_clk_mode    = 0
; 屏幕没有 TE 脚,配置 TE0
lcd_dbi_te          = 0
; 配置屏幕 DBI 格式 L4I1
lcd_dbi_if          = 2
; 输入图像数据 RGB 顺序识别设置,这里配置是 RGB 格式
lcd_rgb_order       = 0
; 设置屏的刷新率,单位Hz。当lcd_dbi_te使能时,这个值设置无效。
lcd_fps             = 60
; 使用 SPI1 作为通讯接口
lcd_spi_bus_num     = 1
lcd_frm             = 2
lcd_gamma_en        = 1

lcd_power_num       = 0
lcd_gpio_regu_num   = 0
lcd_bl_percent_num  = 0

;RESET Pin
lcd_gpio_0          = port:PA20<1><0><2><0>

编译打包

运行命令 mp 编译打包,可以看到编译了 st7789v.o

在这里插入图片描述
在这里插入图片描述

测试

烧录启动之后,屏幕背光启动,但是屏幕全黑。

输入 test_spilcd ,屏幕显示蓝色。

输入 lv_examples 1 可以显示 lvgl 界面

在这里插入图片描述
在这里插入图片描述

常见问题

LVGL 颜色异常

这是由于 LVGL 配置的 LV_COLOR_DEPTH 为 32,但是 SPI 屏配置为16位。请修改 lv_conf.h,也请注意 LV_COLOR_16_SWAP 仅有 SPI 需要设置为 1,在使用 DBI 驱动的时候不需要配置为 1。

在这里插入图片描述
在这里插入图片描述
出现部分花屏
在这里插入图片描述
在这里插入图片描述
  • 检查 address 函数是否正确
  • 检查 sys_config.fex 屏幕配置分辨率是否正确
SPI LCD 颜色相关问题

首先,得先确定显示屏使用的是SPI接口,还是DBI接口,不同的接口,输入数据的解析方式是不一样的。

DBI接口的全称是 Display Bus Serial Interface ,在显示屏数据手册中,一般会说这是SPI接口,所以有人会误认为SPI屏可以使用 normal spi 去直接驱动。

阅读lcd_dbi_if部分的介绍可以知道,在3线模式时,发送命令前有1位A0用于指示当前发送的是数据,还是命令。而命令后面接着的数据就没有这个A0位了,代表SPI需要在9位和8位之间来回切换,而在读数据时,更是需要延时 dummy clock 才能读数据,normal spi 都很难,甚至无法实现。所以 normal spi 只能模拟4 线的DBI的写操作。

对于R128这类支持DBI接口的CPU,可以选择不去了解SPI。如果需要用到SPI去驱动显示屏,必须把显示屏设置成小端。

RGB565和RGB666

SPI显示屏一般支持RGB444,RGB565和RGB666,RGB444使用的比较少,所以只讨论RGB565和RGB666.

RGB565代表一个点的颜色由2字节组成,也就是R(红色)用5位表示,G(绿色)用6位表示,B(蓝色)用5位表示,如下图所示:

在这里插入图片描述
在这里插入图片描述

RGB666一个点的颜色由3字节组成,每个字节代表一个颜色,其中每个字节的低2位会无视,如下图所示:

在这里插入图片描述
在这里插入图片描述
SPI 接口

因为SPI接口的通讯效率不高,所以建议使用RGB565的显示,以 jlt35031c 显示屏为例,他的显示驱动芯片是 ST7789v,设置显示格式的方式是往 3a 寄存器写入0x55(RGB565)或者 0x66(RGB666),在 R128SDK 中,已经把 jlt35031c 的通讯格式写死为 0x55lcd_pixel_fmt配置选项无效:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
sunxi_lcd_cmd_write(sel, 0x3a);
sunxi_lcd_para_write(sel, 0x55);

在例程中,输入的数据是 0xff,0x00,0xff,0x00,对于SPI接口,是按字节发送。实际上,例程只需要每次发送2字节即可,因为前后发送的都是相同的ff 00,所以没有看出问题。

根据对 565 的数据解析,我们拆分 ff 00 就可以得到红色分量是 0b11111,也就是 31,绿色是0b111000,也就是 56,,蓝色是 0.我们等效转换成 RGB888,有:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
R = 31/31*255 = 255
G = 56/63*255 = 226

在调色板输入对应颜色,就可以得到黄色

在这里插入图片描述
在这里插入图片描述

因为 DBI 通讯效率较高,所以可以使用 RGB565 或者 RGB666,使用 DBI 接口,也就是 lcd_if 设置为1时,驱动会根据 lcd_pixel_fmt 配置寄存器,以 SDK 中的 kld2844b.c 为例,这显示屏的显示驱动也是 ST7789,但是不同的屏幕,厂家封装时已经限制了通讯方式,所以即使是能使用 DBI 接口的驱动芯片的屏幕,或许也用不了DBI。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
sunxi_lcd_cmd_write(sel, 0x3A); /* Interface Pixel Format */
/* 55----RGB565;66---RGB666 */
if (info[sel].lcd_pixel_fmt == LCDFB_FORMAT_RGB_565 ||
    info[sel].lcd_pixel_fmt == LCDFB_FORMAT_BGR_565) {
    sunxi_lcd_para_write(sel, 0x55);
    if (info[sel].lcd_pixel_fmt == LCDFB_FORMAT_RGB_565)
        rotate &= 0xf7;
    else
        rotate |= 0x08;
} else if (info[sel].lcd_pixel_fmt < LCDFB_FORMAT_RGB_888) {
    sunxi_lcd_para_write(sel, 0x66);
    if (info[sel].lcd_pixel_fmt == LCDFB_FORMAT_BGRA_8888 ||
        info[sel].lcd_pixel_fmt == LCDFB_FORMAT_BGRX_8888 ||
        info[sel].lcd_pixel_fmt == LCDFB_FORMAT_ABGR_8888 ||
        info[sel].lcd_pixel_fmt == LCDFB_FORMAT_XBGR_8888) {
        rotate |= 0x08;
    }
} else {
    sunxi_lcd_para_write(sel, 0x66);
}

对于 DBI 格式,不再是以字节的形式去解析,而是以字的方式去解析,为了统一,软件已经规定了,RGB565 格式时,字大小是2字节,也就是16位,而 RGB666 格式时,字大小是4字节,也就是32位。

对于 RGB565 格式,同样是设置为 0xff,0x00。因为屏幕是大端,而芯片存储方式是小端,所以芯片的 DBI 模块,会自动把数据从新排列,也就是实际上 DBI 发送数据时,会先发送0x00,再发送0xff,也就是红色分量为0,绿色分量为 0b000111,也就是7,蓝色分量是 0x11111,也就是31,我们同样转换成RGB888

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
G = 7/63*255 = 28
B= 31/31*255 = 255

在调色板上输入,可以得到蓝色。

如果是 RGB666,虽然占用的是3个字节,但是没有CPU是3字节对齐的,所以需要一次性输入4字节,然后 DBI 硬件模块,会自动舍弃1个字节,软件同意舍弃了最后一个字节。

依旧以例程为例,例程输入了 0xff,0x00,0xff,0x00,为了方便说明,标准为 0xff(1),0x00(1),0xff(2),0x00(2),其中 0x00(2)会被舍弃掉,然后发送顺序是0xff(2),0x00(1),0xff(1),也就是 0xff(2) 是红色分量,0xff(1) 是蓝色分量,混合可以得到紫色。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-02-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
全志R128应用开发案例——SPI驱动ST7789V1.3寸LCD
此次适配的SPI屏为 ZJY130S0800TG01,使用的是 SPI 进行驱动。
阿志小管家
2024/02/02
6910
全志R128应用开发案例——SPI驱动ST7789V1.3寸LCD
全志R128点屏SPI LCD颜色相关问题
首先,得先确定显示屏使用的是SPI接口,还是DBI接口,不同的接口,输入数据的解析方式是不一样的。
阿志小管家
2024/02/02
2010
全志R128点屏SPI LCD颜色相关问题
基于全志D1-H 芯片与 Tina Linux 框架下的 LCD 屏幕适配
Linux 提供了一套完整的屏幕驱动,支持 RGB,MIPI DSI,eDP,LVDS,E-INK屏幕,也支持低分辨率的 SPI,IIC 屏幕。具体屏幕的驱动情况,需要根据芯片而确定。本文将通过介绍 D1-H Kernel 中的 LCD 驱动,讲解配置屏幕驱动的基本方法。
阿志小管家
2024/02/02
1.7K0
基于全志D1-H 芯片与 Tina Linux 框架下的 LCD 屏幕适配
全志R128应用开发案例——SPI 驱动 TFT LCD 屏
我们使用的开发板是 R128-Devkit,需要开发 C906 核心的应用程序,所以载入方案选择 r128s2_module_c906
阿志小管家
2024/02/02
2620
全志R128应用开发案例——SPI 驱动 TFT LCD 屏
全志R128基础组件开发——显示与屏幕驱动①
RTOS 提供了一套完整的屏幕驱动,支持 RGB, i8080, SPI, DBI 格式的屏幕。
阿志小管家
2024/02/02
6050
全志R128基础组件开发——显示与屏幕驱动①
使用基于全志D1-H的LicheeRV的 86 Panel 与 Tina BSP 实现 RGB 与 SPI 双屏显示
Tina 提供了2种 SPI TFT 显示屏的驱动方式。第一种是官方推荐的 fbdev 方式,使用 Framebuffer implementaion without display hardware of AW 进行 SPI屏幕的驱动。另外一种是使用 fbtft 进行 SPI 屏幕驱动。 fbdev 方式由于 pinctrl 在新内核中调用方式出现修改,所以暂时无法使用。修改难度较大。fbtft 虽然官方wiki表明不建议在 Linux 5.4 中使用,但是其实也是可以使用的,只需要修改一下 GPIO 的注册方式就行。
阿志小管家
2024/02/02
3310
使用基于全志D1-H的LicheeRV的 86 Panel 与 Tina BSP 实现 RGB 与 SPI 双屏显示
LCD模组驱动开发
由于使用的是 SPI0,所以 TinyVision 的 LCD 模块并不支持使用MIPI-DBI进行驱动,这里我们使用普通的SPI模拟时序。
韦东山
2024/08/24
2090
LCD模组驱动开发
全志 Tina Linux LCD显示屏调试指南 支持MIPI DSI RGB LVDS I8080 SPI等接口,开发板支持百问网T113 D1-H哪吒 DongshanPI-D1s V853
素时钟不超过180MHz 都支持。或者两个串行RGB 接口,串行RGB 的最高分辨率最大不超过800*480@60
韦东山
2022/12/28
5.7K0
全志 Tina Linux LCD显示屏调试指南 支持MIPI DSI RGB LVDS I8080 SPI等接口,开发板支持百问网T113 D1-H哪吒 DongshanPI-D1s V853
解决LicheeRV 86 Panel在tina2.0配置lcd GPIO引脚及colorbar闪屏的问题
设备树修改参考了https://github.com/Tina-Linux/tina-d1x-lichee-rv和sipeed提供的licheerv_d1_compile。
阿志小管家
2024/02/02
5860
解决LicheeRV 86 Panel在tina2.0配置lcd GPIO引脚及colorbar闪屏的问题
在全志XR806上移植st7789屏幕驱动
很高兴有机会参加本次极术社区举办的“「免费试用」搭载安谋科技STAR-MC1的全志XR806 Wi-Fi+BLE 开发板试用活动”。 去年就对全志的mcu芯片感兴趣了,一直没有机会接触,看到本次极术社区提供的全志wifi + BLE开发板试用,就马上参加了。板子拿到手之后,很快就搭建好了环境,由于自己时间安排的问题,一直没有空搞,这两天赶紧搞了一下。
阿志小管家
2024/02/02
3220
在全志XR806上移植st7789屏幕驱动
全志R128基础组件开发——显示与屏幕驱动②
Lcd HV panel Interface, 这个参数只有在 lcd_if=0 时才有效。定义 RGB 同步屏下的几种接口类型,设置相应值的对应含义为:
阿志小管家
2024/02/02
3300
全志R128基础组件开发——显示与屏幕驱动②
3.100ASK_V853-PRO开发板支持七寸RGB屏
​ 如果已经使用我们的增加的补丁文件,默认启动有Tina Linux的logo,同时还支持了lvgl示例和触摸。可在开发板的串口终端上输入lv_examples,可以发现我们提供有5个lvgl示例。输入lv_examples 0,可运行第一个lvgl示例。
韦东山
2023/05/23
6600
3.100ASK_V853-PRO开发板支持七寸RGB屏
单片机—HLK-W801并口驱动ST7789
买了这块并口的屏幕,是为了做一个nes模拟器的游戏机,之前用的SPI的屏幕,显示游戏画面还是比较耗时,毕竟是串行数据,所以准备试一下并行接口的屏幕,顺便理解一下并口8080的驱动方式。
全栈程序员站长
2022/07/28
2K0
单片机—HLK-W801并口驱动ST7789
基于STM32 HAL库硬件SPI的ST7789驱动(TFT-LCD 240*320)
  在日常开发项目中,显示屏是经常使用的一个部件,显示屏的种类也是多种多样,但对于开发者来说,主要关心的是显示屏所用到的驱动芯片,本次给小伙伴们带来的是 STM32 下的 HAL 库硬件 SPI 驱动 320*240 分辨率的 TFT-LCD,LCD 显示屏驱动芯片为 ST7789,一步步实现如何驱动此类驱动芯片的 LCD 屏幕,话不多说,上干货!
用户8913398
2021/08/16
9.7K1
基于STM32 HAL库硬件SPI的ST7789驱动(TFT-LCD 240*320)
用纯.NET开发并制作一个智能桌面机器人(二):用.NET IoT库编写驱动控制两个屏幕
从.NET IoT入门开始这篇文章《用纯.NET开发并制作一个智能桌面机器人(一):从.NET IoT入门开始》想必大家应该都看过了,也有很多人都该着手购买树莓派Zero 2W进行上手体验了,那么我们这篇文章就开始真正的实践了,玩硬件肯定是要亲自操作得出成果才会开心,由于牵扯到硬件,所以有的时候软件没问题,但是硬件接线错误或者接触不良都会结果不正常,这个时候就需要我们有个强大的内心了,不能被困难打倒,不能半途而废,图上的为我画的PCB板子最终脱离数据线的效果。
郑子铭
2025/03/10
1410
用纯.NET开发并制作一个智能桌面机器人(二):用.NET IoT库编写驱动控制两个屏幕
全志D1-H移植为7寸MIPI屏
到Tina根目录下make kernel_menuconfig将下面驱动配置上
阿志小管家
2024/02/02
2440
全志D1-H移植为7寸MIPI屏
STM32驱动ST7701S芯片(vⅰV0手机换屏价)
大家好,又见面了,我是你们的朋友全栈君。 1、配置GPIO口 void LCD_GPIO_Config(void) { /*定义一个GPIO_InitTypeDef类型的
全栈程序员站长
2022/07/28
3K0
全志R128平台SPI与DBI点屏性能大对比
阿志小管家
2024/02/02
2600
全志R128平台SPI与DBI点屏性能大对比
STM32Cube-17 | 使用硬件SPI驱动TFT-LCD(ST7789)
本篇详细的记录了如何使用STM32CubeMX配置STM32L431RCT6的硬件SPI外设与ST7789通信,驱动16bit TFT-LCD 屏幕。
Mculover666
2020/07/16
5.2K0
STM32Cube-17 | 使用硬件SPI驱动TFT-LCD(ST7789)
DshanMCU-R128s2 配置引脚复用
R128 平台使用 sys_config.fex 作为引脚配置文件,他会在打包时打包编译进入系统,在系统运行时会解析并配置,系统解析 sys_config.fex 的驱动配置位于 lichee\rtos-components\aw\sys_config_script 中。
韦东山
2023/12/23
1890
推荐阅读
相关推荐
全志R128应用开发案例——SPI驱动ST7789V1.3寸LCD
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验