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

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 分为多种接口,包括

0:L3I1
1:L3I2
2:L4I1
3:L4I2
4:D2LI

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 DevkitTFT 模块
PA12CS
PA13SCL
PA18SDA
PA9BLK
PA20RES
PA19DC
3V3VCC
GNDGND

载入方案

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

$ source envsetup.sh 
$ lunch_rtos 1

在这里插入图片描述

设置 DBI 驱动

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

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模块。

;----------------------------------------------------------------------------------
;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

Drivers Options  --->soc related device drivers  --->PWM Devices --->-*- enable pwm driver

在这里插入图片描述

配置 PWM 引脚

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

[pwm1]
pwm_used        = 1
pwm_positive    = port:PA9<4><0><3><default>

在这里插入图片描述

设置 SPI LCD 驱动

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

Drivers Options  --->soc related device drivers  --->[*] DISP Driver Support(spi_lcd)[*]   spilcd hal APIs test

在这里插入图片描述

编写 SPI LCD 显示屏驱动

获取屏幕初始化序列

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

在这里插入图片描述

找到 LCD 的初始化序列代码

在这里插入图片描述

找到屏幕初始化的源码

在这里插入图片描述

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

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);   // 2bLCD_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:0vLCD_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输出为GNDLCD_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)设为0LCD_WR_REG(0x21);     LCD_WR_REG(0x29);     

用现成驱动改写 SPI LCD 驱动

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

在这里插入图片描述

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

在这里插入图片描述

先编辑 st7789v.hnv3029s 改成 st7789v

在这里插入图片描述

#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_REGsunxi_lcd_cmd_write
LCD_WR_DATA8sunxi_lcd_para_write
delay_mssunxi_lcd_delay_ms

可以直接进行替换

在这里插入图片描述

完成后如下

在这里插入图片描述

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

在这里插入图片描述

做如下修改

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);
}

完成驱动如下

#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);   // 2bsunxi_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:0vsunxi_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输出为GNDsunxi_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)*8sunxi_lcd_para_write(sel, 0x00);   // 设定gate起点位置sunxi_lcd_para_write(sel, 0x00);   // 当gate没有用完时,bit4(TMG)设为0sunxi_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);elseaddress(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 的配置

在这里插入图片描述

config LCD_SUPPORT_ST7789bool "LCD support st7789v panel"default n---help---If you want to support st7789v panel for display driver, select it.

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

在这里插入图片描述

如下图

在这里插入图片描述

#ifdef CONFIG_LCD_SUPPORT_ST7789V&st7789v_panel,
#endif

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

在这里插入图片描述

如下图

#ifdef CONFIG_LCD_SUPPORT_ST7789V
extern struct __lcd_panel st7789v_panel;
#endif

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

在这里插入图片描述

如下所示

在这里插入图片描述

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

[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 = 1 为 DBI 模式,双缓冲
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 脚,配置 TE 为 0
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        = 1lcd_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配置选项无效:

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,有:

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。

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;elserotate |= 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

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) 是蓝色分量,混合可以得到紫色。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/162253.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python判断出栈顺序是否合法_合适出栈序列

题目&#xff1a; 有一个含1~n的n个整数序列a&#xff0c;通过一个栈可以产生多种出栈序列&#xff0c;设计一个算法采用链栈判断序列b&#xff08;为1~n的某个排列&#xff09;是否为一个合适的出栈序列&#xff0c;并用相关数据进行测试。 解释&#xff1a; ①栈空&…

深度学习框架TensorFlow.NET之数据类型及张量2(C#)

环境搭建参考&#xff1a; 深度学习框架TensorFlow.NET环境搭建1&#xff08;C#&#xff09;-CSDN博客 由于本文作者水平有限&#xff0c;如有写得不对的地方&#xff0c;往指出 声明变量&#xff1a;tf.Variable 声明常量&#xff1a;tf.constant 下面通过代码的方式进行学…

1.UML面向对象类图和关系

文章目录 4种静态结构图类图类的表示类与类之间的关系依赖关系(Dependency)关联关系(Association)聚合(Aggregation)组合(Composition)实现(Realization)继承/泛化(Inheritance/Generalization)常用的UML工具reference欢迎访问个人网络日志🌹🌹知行空间🌹🌹 4种静态结构…

MySQL索引优化与查询优化

1. 索引失效案例 MySQL中提高性能的一个最有效的方式是对数据表设计合理的索引。索引提供了访问高效数据的方法&#xff0c;并且加快查询的速度&#xff0c;因此索引对查询的速度有着至关重要的影响。 使用索引可以快速地定位表中的某条记录&#xff0c;从而提高数据库查询的速…

mybatis在springboot当中的使用

1.当使用Mybatis实现数据访问时&#xff0c;主要&#xff1a; - 编写数据访问的抽象方法 - 配置抽象方法对应的SQL语句 关于抽象方法&#xff1a; - 必须定义在某个接口中&#xff0c;这样的接口通常使用Mapper作为名称的后缀&#xff0c;例如AdminMapper - Mybatis框架底…

Swift语言配合HTTP写的一个爬虫程序

下段代码使用Embassy库编写一个Swift爬虫程序来爬取jshk的内容。我会使用proxy_host为duoip&#xff0c;proxy_port为8000的爬虫IP服务器。 使用Embassy库编写一个Swift爬虫程序可以实现从网页上抓取数据的功能。下面是一个简单的步骤&#xff1a; 1、首先&#xff0c;需要在X…

RabbitMQ 消息应答与发布

目录 一、消息应答 1、自动应答&#xff08;默认&#xff09; 2、手动消息应答的方法 ​编辑 3、消息重新入队 4、手动应答案列与效果演示 二、RabbitMQ持久化 1、队列持久化 2、消息持久化 三、不公平分发&#xff08;能者多劳&#xff0c;弱者少劳&#xff09; 1、…

PHP语言、B/S手术麻醉临床信息管理系统源码

手术麻醉临床信息管理系统有着完善的临床业务功能&#xff0c;能够涵盖整个围术期的工作&#xff0c;能够采集、汇总、存储、处理、展现所有的临床诊疗资料。通过该系统的实施&#xff0c;能够规范麻醉科的工作流程&#xff0c;实现麻醉手术过程的信息数字化&#xff0c;自动生…

Blender vs 3ds Max:谁才是3D软件的未来

在不断发展的3D建模和动画领域&#xff0c;两大软件巨头Blender和3ds Max一直在争夺顶级地位。 随着技术的进步和用户需求的演变&#xff0c;一个重要问题逐渐浮出水面&#xff1a;Blender是否最终会取代3ds Max&#xff1f;本文将深入探讨二者各自的优势和劣势、当前状况&…

Flink SQL时间属性和窗口介绍

&#xff08;1&#xff09;概述 时间属性&#xff08;time attributes&#xff09;&#xff0c;其实就是每个表模式结构&#xff08;schema&#xff09;的一部分。它可以在创建表的 DDL 里直接定义为一个字段&#xff0c;也可以在 DataStream 转换成表时定义。 一旦定义了时间…

数票通批量开票小妙招,双11开票不用愁

每年双十一都是电商企业和财务“疯狂”的日子&#xff0c;随着疫情的回暖&#xff0c;消费者的消费能力蹭蹭上涨&#xff0c;商家的口袋盆满钵满&#xff0c;而财务的加班永无休止&#xff0c;批量开票成为财务人摆脱双十一加班的必备技能。 面对双十一狂欢&#xff0c;相信不少…

高校双十一快递爆仓警告,做快递代取能不能赚钱

今年双11期间的快递数量预计将超过往年。今年以来&#xff0c;快递行业竞争日益激烈&#xff0c;特别是价格战的竞争更是白热化。电商平台也针对低价展开了一系列促销活动&#xff0c;这种策略有望刺激快递单量增长。低价吸引力激发了用户的购物欲望&#xff0c;将导致高校快递…