TXW81x LCD FAQ

责任与版权
修订记录
| 日期 | 版本 | 描 述 | 修订人 |
|
2026-04-13 |
V1.5 |
增加mcu屏幕自旋转和斜切线处理 | Ethan |
|
2026-02-25 |
V1.4 |
补充显存dump,rgb抖动,scale2/3异常,jpg解码异常,dcache操作,mcu屏帧间命令 | Ethan |
| 2025-04-11 |
V1.3 |
补充spilcd介绍 | Ethan |
| 2025-02-25 |
V1.2 |
补充介绍 | Ethan |
| 2024-08-26 |
V1.1 |
LCD_FAQ初始版 | Ethan |
1. 配置新屏幕流程
1.1. 简要说明
大致分为屏驱IO配置,LCDC驱动配置,显存配置,屏幕旋转配置。在此之前请先阅读屏幕规格书确定屏幕接口。
Note:下面基于FPV-V2.5.3.7-31224 SDK讲解。不同版本SDK会有一定差别。
1.2. 屏驱IO配置
主要是在project_config.h中操作,需要配置屏幕的控制信号口,数据输出口。
如果IO口有用到PB和PC口,VCAM_EN确保使能,否则部分PB PC口没有电平输出。
PB6-PB15、PC0-PC5是由VCAM电压供给。
-
控制信号口:左侧rgb,右侧mcu。

PIN_LCD_RESET //复位引脚设置(有背光控制的,可以参考复位引脚设置)
DOTCLK_PWR //RGB屏幕的DOTCLK像素时钟引脚,MCU屏幕则为WR写信号引脚
DE_ERD //RGB屏幕的DE同步引脚,MCU屏幕则为RD读信号引脚
VS_CS //RGB 屏幕的VS帧同步引脚,MCU屏幕则为CS片选引脚
HS_DC //RGB屏幕的HS行同步引脚,MCU屏幕则为DC命令数据选择引脚
LCD_TE //MCU屏幕的TE信号输出引脚
Note:如果所选的RGB屏幕是非直驱式,如7701这类驱动芯片,还需要额外配置SPI口用于屏驱芯片寄存器初始化。不使用的引脚配置255。
-
数据输出信号口:看屏幕配置的是几线模式,就配置几个数据口。

Note:没修改颜色顺序的情况。
-
RGB888 24位线宽,LCD_D0~D23对应为B0~B7 G0~G7 R0~R7
-
RGB666 18位线宽,LCD_D0~D17对应为B0~B5 G0~G5 R0~R5
-
RGB565 16位线宽,LCD_D0~D15对应为B0~B4 G0~G5 R0~R4
-
MCU8080 接口如8位线宽,则默认接LC_D0~D7
1.3. LCDC驱动配置
可以在sdk/lib/bus/spi/lcd文件夹下copy一份驱动配置文件进行修改。
RGB屏幕可以参考st7701s.c
MCU屏幕可以参考st7789v_mcu.c

主要配置:
lcd_bus_type //lcd总线类型 选择是RGB 还是MCU接口
bus_width //lcd 总线宽度,8线,16线,18线,24线
color_mode //颜色模式,rgb565 ,rgb666 ,rgb888
scan_mode //video层旋转角度
//在lcd.c初始化时会被lcdc_set_rotate_mirror接口调用
te_mode //没用到不用管
colrarray //颜色顺序不需修改默认就可以
pclk //RGB接口的时候是像素时钟速率,MCU接口时是WR写信号速率.
//需要根据屏幕规格书选择合适的速率!
screen_w/h //屏幕实际宽高设置
video_w/h //Video数据宽高
video_x/y //Video开始坐标默认0,0即可
osd_w/h //OSD数据宽高
osd_x/y //Osd开始坐标默认0,0即可
init_table //屏幕寄存器初始化表RGB非直驱屏幕和MCU屏幕都需要配置.
//可以找屏幕供应商索取,RGB直驱屏幕直接NULL就可以了
pclk_inv //RGB屏幕像素同步时钟是否反向,显示异常的时候可以反向试试
vlw,vbp,vfp,hlw,hbp,hfp //是RGB屏幕相关的无效行,无效列,有效区域设置
//需要阅读lcd规格书,或者找屏幕供应商获取
de_en //DE使能(需要用到DE同步模式使能)
vs_en //VSYNC使能
hs_en //HSYNC使能
de_inv //DE是否反向
hs_inv //HSYNC是否反向
vs_inv //VSYNC是否反向
mcu.resev //保留位不用管.以下是MCU接口屏幕相关配置RGB屏幕忽略
mcu.wr_inv //WR是否反向
mcu.rd_inv //RD是否反向
mcu.cs_inv //CS是否反向
mcu.dc_inv //DC是否反向
1.4. 显存配置
Vedio和OSD层显存配置
OSD编码BUF设置
LVGL缓存配置
-
设置Vedio层显存和OSD层显存大小。
显存大小通过修改lcd.h文件中SCALE_HIGH和SCALE_WIDTH宏实现。
//lcd.h
…
#define SCALE_WIDTH 320 // video旋转后拉伸的宽度,如果是旋转90度,这里是实际屏幕的高
#define SCALE_HIGH 240 // video旋转后拉伸的高度,如果旋转 90度,这里是实际屏幕的宽
#define PHOTO1_W 320 // video层photo1 宽度
#define PHOTO1_H 240 // video 层photo1 高度
Vedio显存定义在lcd.c中,有3 buff和4buff看需要选择buff数量或者直接默认。
//lcd.c
uint8 video_psram_config_mem[SCALE_CONFIG_W*SCALE_HIGH+SCALE_CONFIG_W*SCALE_HIGH/2]
uint8 video_psram_config_mem1[SCALE_CONFIG_W*SCALE_HIGH+SCALE_CONFIG_W*SCALE_HIGH/2]
uint8 video_psram_config_mem2[SCALE_CONFIG_W*SCALE_HIGH+SCALE_CONFIG_W*SCALE_HIGH/2]
uint8 video_psram_config_mem3[SCALE_CONFIG_W*SCALE_HIGH+SCALE_CONFIG_W*SCALE_HIGH/2]
//video 层photo1 decode buf
uint8 video_decode_config_mem[SCALE_PHOTO1_CONFIG_W*PHOTO1_H+SCALE_PHOTO1_CONFIG_W*PHOTO1_H/2]
uint8 video_decode_config_mem1[SCALE_PHOTO1_CONFIG_W*PHOTO1_H+SCALE_PHOTO1_CONFIG_W*PHOTO1_H/2]
uint8 video_decode_config_mem2[SCALE_PHOTO1_CONFIG_W*PHOTO1_H+SCALE_PHOTO1_CONFIG_W*PHOTO1_H/2]
OSD层显存定义在lv_port_disp.c中。
//lv_port_disp.c
uint8_t osd_menu565_buf[SCALE_HIGH*SCALE_WIDTH*2] __attribute__ ((aligned(4),section(".psram.src")));;
-
OSD编码BUF是pingpang buf,会把OSD显存数据压缩到OSD编码BUF中。
详细压缩规则可以查看《TXW81x 视频方案开发指南》。
//interface_mgnt.c
uint8_t osd_encode_buf[110*1024] __attribute__ ((aligned(4),section(".psram.src")));
uint8_t osd_encode_buf1[110*1024] __attribute__ ((aligned(4),section(".psram.src")));
Note:
如果OSD颜色复杂则需要加大编码BUF,防止编码数据不全,LCD显示异常。
-
LVGL缓存,由行宽列高决定,设置MY_DISP_HOR_RES和MY_DISP_VER_RES宏定义。目前配置的是10行。
//lv_port_disp.h
#define MY_DISP_HOR_RES 240 //设置成 实际屏幕宽度
#define MY_DISP_VER_RES 320 //设置成 实际屏幕高度
#define LV_VER_RES_MAX 240
#define LV_COLOR_DEPTH 16
//lv_port_disp.c
static lv_disp_draw_buf_t draw_buf_dsc_1;
/*A buffer for 10 rows 看内存使用情况可以酌情增加,sram不够可以修改到psram上*/
static lv_color_t buf_1[MY_DISP_HOR_RES * 10];
/*Initialize the display buffer*/
lv_disp_draw_buf_init(&draw_buf_dsc_1, buf_1, NULL, MY_DISP_HOR_RES * 10);
Note:
LVGL缓存buf1放到PSRAM中,只需数组末尾跟OSD显存一样添加修饰词即可
__attribute__ ((aligned(4),section(".psram.src")));
-
如果是横屏需要额外注意lcd_line_buf配置,这部分跟旋转相关,默认32行,行宽需要以实际屏幕行宽为准,设置不对会存在系统崩溃的情况。
SDK中有一个HX8282横屏配置,可以参考LCD_HX8282_EN宏包裹的相关处理。
#define LCD_ROTATE_LINE 32
#if LCD_HX8282_EN
uint8 lcd_line_buf[SCALE_WIDTH*LCD_ROTATE_LINE+(SCALE_WIDTH/2)*LCD_ROTATE_LINE*2 ]__attribute__ ((aligned(4)));
#else
uint8 lcd_line_buf[SCALE_HIGH*LCD_ROTATE_LINE+(SCALE_HIGH/2)*LCD_ROTATE_LINE*2 ]__attribute__ ((aligned(4)));
#endif
//Lcd.c->lcd_module_run
#if LCD_HX8282_EN
lcdc_set_rotate_linebuf_u_addr(lcd_dev,(uint32)lcd_line_buf+SCALE_WIDTH*LCD_ROTATE_LINE);
lcdc_set_rotate_linebuf_v_addr(lcd_dev,(uint32)lcd_line_buf+SCALE_WIDTH*LCD_ROTATE_LINE+(SCALE_WIDTH/2)*LCD_ROTATE_LINE);
#else
lcdc_set_rotate_linebuf_u_addr(lcd_dev,(uint32)lcd_line_buf+SCALE_HIGH*LCD_ROTATE_LINE);
lcdc_set_rotate_linebuf_v_addr(lcd_dev,(uint32)lcd_line_buf+SCALE_HIGH*LCD_ROTATE_LINE+(SCALE_HIGH/2)*LCD_ROTATE_LINE);
#endif
1.5. 屏幕旋转配置
Lcd屏幕旋转分为,OSD层的软件旋转和Vedio层的硬件旋转。
-
OSD层的软件旋转是通过LVGL实现的。
最终是操作lv_hal_disp.c文件中的rotated变量,设置lvgl的旋转角度90,180,270。
void lv_disp_drv_init(lv_disp_drv_t * driver)
{
…
//soft rotate
driver->sw_rotate = 1;
driver->rotated = LV_DISP_ROT_90;
…
}
Note:
不生效时有可能外面又设置了一次rotated变量,不同版本sdk会有差别
-
Vedio层的硬件旋转,支持90,180,270,通过下面接口
lcdc_set_rotate_mirror(lcd_dev,0,LCD_ROTATE_90);
可以参考user_demo.c中进入home界面的调用逻辑。
-
Vedio层设置180度旋转有别其他角度旋转,YUV偏移发生变化需要特别注意。
参考lcd.h中定义的宏LCD_SET_ROTATE_180是如何设置180度video旋转。
//->Lcd.h
……
#define LCD_SET_ROTATE_180 1
#if LCD_SET_ROTATE_180
#define Y_OFFSET SCALE_CONFIG_W*(SCALE_HIGH-1)
#define UV_OFFSET ((SCALE_CONFIG_W/2+3)/4)*4 * (SCALE_HIGH/2-1)
#define DEC_Y_OFFSET PHOTO1_W*(PHOTO1_H-1)//428*(240-1)
#define DEC_UV_OFFSET ((SCALE_PHOTO1_CONFIG_W/2+3)/4)*4 * (PHOTO1_H/2-1) //160*119//216*(119)
#else
……
#endif
Note:
屏幕配置基本完成,剩下的就是编译测试屏幕。
1.6. 跑一个LVGL测试demo验证屏幕
-
interface_mgnt.c添加一个lvgl测试demo,benchmark。

-
编译测试屏幕是否正常运行。

2. SDK FPV-2.5.3-29936屏幕配置RGB或MCU接口异常不生效
这个版本sdk,可能会遇到按上面配置步骤屏幕无法正常点亮问题!原因是这版sdk在lcd.c初始化末尾部分,强制设置了mcu接口6线模式,需屏蔽处理!
-
屏蔽强制设置像素clk数量,改成由颜色模式和总线宽度计算。

-
屏蔽此处重复设置lcdc 总线宽度6线,data引脚重定义。

Note:此处是因为开发板硬件接口固定,ST7789寄存器设置成mcu 6线接口,data数据口是要接d2_d7。这里为了兼容硬件,所以重复设置了一次!
3. MCU屏幕TE防撕裂配置
由于MCU屏幕是自带显存,当读取和写入不同步的情况,就会出现扫屏线!为了防止扫屏线的出现,主控需要通过屏幕的TE引脚判断何时开始刷新显存!
-
SDK如何配置,目前新版SDK已经做好TE触发LCDC发送,不需要像以前设置外部中断方式检测!直接设置#define LCD_TE 宏配置对应检测引脚,并配置上升沿和下降沿触发!

-
屏幕配置打开屏幕TE功能具体参考 屏驱手册 如ST7789 ,是35h。

-
调整屏幕帧率或者主控写入速度,如果读取扫描过快,还是会存在读取追上写入,出现扫屏线!
屏幕端配置C6h调整读取速率。

主控配置pclk调整写入速率。

最终如下图效果,确保wr写完一帧图像是在下一个te到来之前。

Note:蓝色是te 信号,绿色是 wr 信号,
-
确定屏幕扫描方向,和主控写入是否一致,不一致还是会存在扫屏线。ST7789设置36h调整MY,MX,扫描方向从左到右,自上而下!

4. LCD屏幕解码视频画面出现锯齿
当屏幕出现锯齿画面目前遇到的基本都是pingpang buffer切换设置引起的,也就是scale2拉伸输出和rotate旋转输入,出现了冲突!需要错开buffer顺序。
void lcd_done(uint32 irq_flag,uint32 irq_data,uint32 param1){
......
if(lcdc_decode_enable){
#if 1
if((decode_num%3) == 0){
lcdc_set_p1_rotate_y_src_addr(lcd_dev,(uint32)video_decode_mem1+dec_y_offset/**/);
lcdc_set_p1_rotate_u_src_addr(lcd_dev,(uint32)video_decode_mem1+scale_p1_w*p1_h+dec_uv_offset/**/);
lcdc_set_p1_rotate_v_src_addr(lcd_dev,(uint32)video_decode_mem1+scale_p1_w*p1_h+scale_p1_w*p1_h/4+dec_uv_offset/**/);
}else if((decode_num%3) == 1){
lcdc_set_p1_rotate_y_src_addr(lcd_dev,(uint32)video_decode_mem2+dec_y_offset/**/);
lcdc_set_p1_rotate_u_src_addr(lcd_dev,(uint32)video_decode_mem2+scale_p1_w*p1_h+dec_uv_offset/**/);
lcdc_set_p1_rotate_v_src_addr(lcd_dev,(uint32)video_decode_mem2+scale_p1_w*p1_h+scale_p1_w*p1_h/4+dec_uv_offset/**/);
}else{
lcdc_set_p1_rotate_y_src_addr(lcd_dev,(uint32)video_decode_mem+dec_y_offset/**/);
lcdc_set_p1_rotate_u_src_addr(lcd_dev,(uint32)video_decode_mem+scale_p1_w*p1_h+dec_uv_offset/**/);
lcdc_set_p1_rotate_v_src_addr(lcd_dev,(uint32)video_decode_mem+scale_p1_w*p1_h+scale_p1_w*p1_h/4+dec_uv_offset/**/);
}
#else
if((decode_num%3) == 0){
lcdc_set_p1_rotate_y_src_addr(lcd_dev,(uint32)video_decode_mem2+dec_y_offset/**/);
lcdc_set_p1_rotate_u_src_addr(lcd_dev,(uint32)video_decode_mem2+scale_p1_w*p1_h+dec_uv_offset/**/);
lcdc_set_p1_rotate_v_src_addr(lcd_dev,(uint32)video_decode_mem2+scale_p1_w*p1_h+scale_p1_w*p1_h/4+dec_uv_offset/**/);
}else if((decode_num%3) == 1){
lcdc_set_p1_rotate_y_src_addr(lcd_dev,(uint32)video_decode_mem+dec_y_offset/**/);
lcdc_set_p1_rotate_u_src_addr(lcd_dev,(uint32)video_decode_mem+scale_p1_w*p1_h+dec_uv_offset/**/);
lcdc_set_p1_rotate_v_src_addr(lcd_dev,(uint32)video_decode_mem+scale_p1_w*p1_h+scale_p1_w*p1_h/4+dec_uv_offset/**/);
}else{
lcdc_set_p1_rotate_y_src_addr(lcd_dev,(uint32)video_decode_mem1+dec_y_offset/**/);
lcdc_set_p1_rotate_u_src_addr(lcd_dev,(uint32)video_decode_mem1+scale_p1_w*p1_h+dec_uv_offset/**/);
lcdc_set_p1_rotate_v_src_addr(lcd_dev,(uint32)video_decode_mem1+scale_p1_w*p1_h+scale_p1_w*p1_h/4+dec_uv_offset/**/);
}
#endif
}
}
5. RGB接口屏幕画面抖动
每个厂家RGB接口屏幕晶体管的保电时间都不太一致的,有些刷慢点,停一停也没关系,有些比较敏感,稍有停顿就会出现画面抖动
5.1. Dclk 补齐
如何补齐可以参考官网SDK->2.5.3.7-32592 ,LCD_FILL_CLK宏相关设置,利用两个timer,一个做pwm,一个做定时,在vs触发后计算足够多的边沿后触发,pwm当dtclk补齐,
详细见sdk代码。
#if LCD_FILL_CLK
//RGB屏,pwm补dotclk操作(使用timer1、timer2)
if(lcdstruct.lcd_bus_type == LCD_BUS_RGB)
{
uint32_t lcd_fill_clk_en = 1;
。。。
。。。
}
#endif
5.2. LCD Auto Kick
操作syscfg_save ,以及ota相关对flash写操作引起的rgb闪动,可以通过使能lcd autokick后lcdc会自动发送显存的中数据到lcd屏幕,结束后在关闭使能。
lcdc_video_enable_auto_ks(lcd_dev,1);
...
//operation code here
...
os_sleep_ms(10);
lcdc_video_enable_auto_ks(lcd_dev,0);
如果是ota过程中rgb屏幕抖动,除了在libota_wirte_fw 前后使能和关闭lcdc_video_enable_autoks,而且建议在lcd_done额外做一些处理
-
libota_write_fw前后使能和关闭lcdc_video_enable_auto_ks

-
lcd_done里面,如果在升级,lcd_done直接反回,防止在中断里切地址。通过ota_updata 变量区分

5.3. 提升PSRAM使用带宽
大量的外设会使用到PSRAM,会导致PSRAM带宽紧张,要么提升PSRAM速率(部分型号可以提升到240Mhz),要么提升LCDC占用率(详细咨询FAE) device_burst_set
enum psram_cfg_enum{
...
PSRAM_DEF(4MByte_dri0_240M), // 选择240M 速率,目前只有4Mpsram 支持 调整240M
PSRAM_SELECT(4MByte_dri0_240M),
};
6. LCD对比度,饱和度,GAMMA,CCM设置
6.1. 对比度,饱和度设置
//使能对比度
lcdc_video_enable_constarast(lcd_dev, 1);
//使能饱和度
lcdc_video_enable_saturation(lcd_dev, 1);
//设置对比度 值 0~0x1f
lcdc_video_set_constarast_val(lcd_dev, 0x12);
//设置饱和度 值 0~0x10
lcdc_video_set_saturation_val(lcd_dev, 0x10);
6.2. CCM
是一种色彩矩阵校正算法,先通过色度计测出实际色块值,在与标准色块值比较求得矩阵值。之后输入的颜色都会通过矩阵值计算并输出颜色,达到一个线性校准调整lcd屏幕之间偏色问题。
//ccm 校正使能
lcdc_video_enable_ccm(lcd_dev, 1);
lcdc_video_set_CCM_COEF0(lcd_dev, 0x00df0100); //matrix 1 参数 3*10bit
lcdc_video_set_CCM_COEF1(lcd_dev, 0x0003b3f3); //matrix 2 参数 3*10bit
lcdc_video_set_CCM_COEF2(lcd_dev, 0x0df0000d); //matrix 3 参数 3*10bit
lcdc_video_set_CCM_COEF3(lcd_dev, 0x00000000); // 偏移值 3*8bit

6.3. GAMMA
这里是数字GAMMA,可以把rgb数据重映射到rgb_gamma_table中的值,起到一个非线性校正的作用。
//gamma 矫正使能
lcdc_video_enable_gamma(lcd_dev, 1);
lcdc_video_set_gamma_R(lcd_dev, (uint32_t)rgb_gamma_table);
lcdc_video_set_gamma_G(lcd_dev, (uint32_t)rgb_gamma_table);
lcdc_video_set_gamma_B(lcd_dev, (uint32_t)rgb_gamma_table);
7. MCU屏幕16线驱动注意事项
81x LCDC模块支持MCU接口16线和18线!这类屏幕一般初始化用的是8线d0-d7,显示数据刷新用的是d0-d15,因此lcdstruct结构体还是设置成LCD_BUS_WIDTH_8,等lcd_table初始化结束后,在通过以下两个接口操作切换成16线模式。
void lcd_module_run(){
...
...
lcdc_open(lcd_dev);
#ifdef LCD_GC9307_16L_EN
lcdc_set_bus_width(lcd_dev,LCD_BUS_WIDTH_16);
lcdc_set_lcd_visible_size(lcd_dev,lcdstruct.screen_w,lcdstruct.screen_h,1);
#else
//...
}
#endif
lcdc_set_start_run(lcd_dev);
}
8. SCALE画面剪裁设置
如果是100万像素镜头接入画面比例是16:9,而你的屏幕320*240那么显示部分是4:3!这样屏幕显示的镜头画面必定会拉伸变形!这个时候可以利用scale3 先拉伸到相近合适的比例之后只取一定尺寸画面输出,达到一个剪裁的效果,但显示面积会缩小!
void scale_to_lcd_config(){
struct scale_device *scale_dev;
scale_dev = (struct scale_device *)dev_get(HG_SCALE3_DEVID);
...
//设置输入图片大小尺寸,以及 输出大小尺寸
scale_set_in_out_size(scale_dev,photo_msg.in_w,photo_msg.in_h,SCALE_WIDTH,SCALE_HIGH);
#if LCD_33_WVGA
//设置实际拉伸 大小
scale_set_step(scale_dev,photo_msg.in_w,photo_msg.in_h,480,270);
#else
scale_set_step(scale_dev,photo_msg.in_w,photo_msg.in_h,SCALE_WIDTH,SCALE_HIGH);
#endif
...
Note:目前scale2 ,scale3支持剪裁设置。
9. SPI接口LCD接入方式讲解
因为lcdc不直接支持spi接口应用,需要从osd层和video层数据输入,截取显存数据流,在通过spi_lcd打点函数写入lcd内部显存。
Note:由于video层数据是yuv420,需要cpu运算软转rgb656屏越大会越耗时。如果需要显示视频流的应用,大屏不推荐。例子中是接入的160*80,可以找FAE同事获取。
9.1. 配置SPI LCD 引脚
参考例子中project_config.h中配置接口的reset, cs, clk ,dat,以及dc数据命令选择引脚。
这部分直接copy参考例子配置。
#define LCD_ST7735S_SPI_EN 1
#if LCD_ST7735S_SPI_EN
#define PIN_LCD_RESET PA_12//255//
#define PIN_SPI0_CS PA_5//PA_13//
#define PIN_SPI0_CLK PA_6//PA_14//
#define PIN_SPI0_IO0 PA_7//PE_3//
#define PIN_SPI0_IO1 255
#define PIN_SPI0_IO2 255
#define PIN_SPI0_IO3 255
#define PIN_SPI0_DC PA_11
#define DOTCLK_RWR 255
#define DE_ERD 255
#define VS_CS 255
#define HS_DC 255
#define LCD_TE 255
#else
...
#endif
9.2. 移值SPI LCD驱动文件
需要把sdk/app/user文件夹copy到你工程中,其中tft_spi_lcd文件夹里是屏幕驱动文件,包括了,初始化,和打点输出函数,可以借鉴这种形式
Note:注意注释,和红字部分
-
屏幕初始化部分 lcd_init 先要找屏厂获取,初始化寄存器的驱动代码
void lcd_spiSt7735s_80x160_init(void)
{
printf("lcd_spiSt7735s_80x160_init \n");
LCD_GPIOInit();//LCD GPIO初始化 ,SPI 接口初始化目前设置的是40Mhz ,4KB DMA
LCD_RESET(); //LCD 复位
os_sleep_ms(100);
...
//寄存器 初始化 ,找屏厂获取
...
}
-
lcd屏幕打点输出函数 draw_rgb /fill_rgb
void lcd_spiSt7735s_80x160_draw_rgb(int16_t sx, int16_t sy,int16_t ex, int16_t ey, uint16_t *color)
{
u32 i,j;
u32 packet_nums =0;
u32 else_nums=0;
uint16_t width = ex-sx+1;
uint16_t height = ey-sy+1;
uint32_t draw_size = width* height;
//step 1 设置显示坐标 ,不同驱动Ic 设置坐标命令 不同
LCD_SetWindows(sx, sy, ex, ey);
//step 2 显示数据 dma写入 , 接口是阻塞式
packet_nums=draw_size/ONE_PACKET_NUMS;
else_nums = draw_size%ONE_PACKET_NUMS;
if(packet_nums>0)
{
for(j=0;j<packet_nums;j++)
{
Lcd_Writebuffer_16Bit(&color[j*ONE_PACKET_NUMS],ONE_PACKET_NUMS); //写入数据
}
}
if(else_nums>0)
Lcd_Writebuffer_16Bit(&color[packet_nums*ONE_PACKET_NUMS],else_nums); //写入数据
}
9.3. 设置SPI LCD相关显存
例子中的应用是需要视频流和lvgl显示,所以除了osd,video层显存外,还需要合并后存储的空间,也就下面的spiLcd_buf。后续lvgl的显示数据以及video视频流数据会mix后保存在这个空间里面,可以放sram或者放psram上。
#if LCD_ST7735S_SPI_EN
//这里放sram 上是因为 用的816 不带psram
uint16_t spiLcd_buf[80*160] __attribute__ ((aligned(4)));
//也可以放到psram上
//uint16_t spiLcd_buf[80*160] __attribute__ ((aligned(4),section(".psram.src")));
#endif
9.4. 创建SPI LCD 处理任务
要注意的代码段都被LCD_ST7735S_SPI_EN宏包裹,可以全局搜索改动部分,copy到你的工程内部,以下是处理函数流程简介。
//lcd_module_run 中创建 spi_lcd 处理任务
void lcd_module_run()
{
...
if(lcdstruct.lcd_bus_type == LCD_BUS_SPI)
{
os_msgq_init(&lcd_yuv_msgq, 4);
...
if(user_gui_init())// 把spi_lcd 相关接口 注册一下,后续通过ds_dev 调用
ds_dev->init();// 初始化 spi_lcd
//创建 spi_lcd 处理任务,在当中会实现,yuv 转rgb lvgl 和video数据合并,和显存写入
csi_kernel_task_new((k_task_entry_t)spilcd_updata_task, "spilcd_updata", NULL, OS_TASK_PRIORITY_NORMAL, 0, NULL, 1024, &spilcd_task);
}
}
//所有的工作都在 这个任务中完成
void spilcd_updata_task()
{
...
while(1){
if(lvgl_init_flag) // LVGL 初始化完成标志
{
// 等待获取 scale3把dvp镜头数据 拉伸成lcd屏幕合适size大小的数据
yuv = os_msgq_get(&lcd_yuv_msgq, osWaitForever);
// 视频流的yuv数据转换成rgb数据
yuv_to_rgb(yuv, 80, 160);
// 合并 video显示数据和 lvgl 显示数据
mix_osd_buffer();
// 调用spi_lcd 显示接口 输出显示
ds_dev->draw_color(0,0,79,159,(uint16_t*)spiLcd_buf);
}
else
os_sleep_ms(10);
}
}
10. 如何导出设备显存数据
驱屏过程中,出现显示异常,往往无法判别是引脚驱动设置问题,还是ui库输出的显示问题这个时候就可以尝试把显存导出到电脑上查看。
-
连接设备,进入仿真,运行复现问题,之后暂停仿真。

-
获取显存地址,可以打开project/list/fpv_app_umac4.map 查看地址,看客户使用。

-
在cmd窗口中输入dump命令。

//dump binary memory 路径/文件名 内存起始地址 内存起始地址+数据长度
dump binary memory C:/Users/.../Documents/2.yuv 0x380c8000 0x380c8000+115200
dump binary memory C:/Users/.../Documents/1.rgb 0x380e1400 0x380e1400+153600
-
得到显存数据后通过,ffplay查看显存数据,video层是yuv420格式,osd层是rgb565。
ffplay -pixel_format yuv420p -video_size 320x240 -i 2.yuv
ffplay -pixel_format rgb565 -video_size 320x240 -i 1.rgb
Note:dump 命令可以把想要查看的内存内容导出,不只是显存。
11. SCALE2 SCALE3 拉伸对齐问题
如果通过点10上面dump出p0,p1显存花的情况,scale输出size要遵循8字节对齐要求,否则uv无法对齐会有问题。具体表现y值正常,uv不正常如下图:

12. JPG图片解码异常问题
下面罗列的是,可能会碰到jpg解码显示异常的一些问题:
-
对齐问题导致的,jpg size需要宽高16对齐。
-
格式导致的,推荐yuv420p 格式。不确认的可以 windows 画图打开另存为jpg。
-
多个结束符(0xffd9)导致的,有些转出的图片可能出现多个结束符,会导致解码异常。
13. DCACHE注意事项
有些时候做一些特效,额外对psram的显存数据如osd的rgb数据或者video的yuv数据,进行cpu操作时要注意需要dcache回写psram,否则会出现数据不对引起的花图现象!

上面是获取yuv数据cpu软转rgb565的一段处理代码,scale2模块会把拉伸好的yuv420p数据dma到yuv指向的psram内存上。
csi_dcache_invalid_range // dcache无效操作
不加dcache无效化操作,cpu会去dcache中取旧数据,因此会导致不能及时的操作最新数据,导致转换的rgb数据异常。加上dcache无效化操作,cpu操作yuv buf时就会从psram上去取最新的数据,保整转换的是最新yuv数据。
csi_dcache_clean_range // dcache回写操作
转换完成后最好进行dcache回写操作。yuv转rgb都是cpu操作,数据好一些都是在dcache里面。加上dcache回写psram操作,保证dcache最新数据能及时的保存到psram上,当dma操作这段psram内存的时候访问的是最新数据。
14. MCU屏幕如需添加帧间命令
某些特殊不通用的LCD控制器(格科微的GC9309NA,矽创的st77926), 如果需要在数据帧与帧之间添加命令的情况可以查看下面流程:
-
Step1:定义Framestartcmd_table

-
Step2:声明定义mcu屏幕命令发送函数。

-
Step3:分两种情况添加发送函数
-
带TE控制的,也就是LCD_TE设置不等255的情况,在TE中断中发送。

-
不带TE控制的,也就是LCD_TE设置255的情况。3buffer就在lcd_done函数末尾设置,4buffer就在lcd_squralbuf_done后面添加。

15. MCU屏幕本身旋转设置,以及斜切线处理
因为81x目前没有dma2d模块,以及osd层不支持硬件旋转。竖屏做横屏显示的时候往往觉的lvgl帧率不够。除了把lvgl缓存放到sram上,也可以关闭lvgl软件旋转,利用mcu屏幕本身旋转特性,释放cpu压力从而提高画图帧率。
15.1. 如何配置MCU屏幕旋转
-
根据LCD驱动芯片手册描述设置0x36寄存器 MV MX MY 位实现
MV | MY = 0xA0 // 270 度旋转

如果是90度或者270旋转,屏幕行列地址,2a,2b 也需要设置修改,如上图。
-
从新设置结构体相关参数,宽高以横屏方式设置,video和osd角度设置为0。

老版SDK需注意取消lvgl的软件旋转设置,新版SDK不用会自动获取。

-
如果用到video层,需要重设置一下lcd.c 文件中的 lcd_line_buf。否则视频流条纹


15.2. 如何处理斜切线问题
斜切线是因为扫描方向跟显存写入方向不一致导致。目前只能是在TE高电平结束前把数据写入。默认的TE高电平,一般只有几百us,不足以写完整帧,因此我们需要调整TE宽度。
-
TE触发修改为上升沿触发lcdc发送数据。

-
尽量提高WR速率,减少发送时间15.1中可以看到WR速率提升到20M。发送一帧大概8ms,有帮助提升后续LCD自刷新帧率。
-
利用B2寄存器调宽LCD TE高电平时间,实际就是调宽VBLANK

vblank = vbp +vfp 目前TE 宽度约增加到168行的时间,下图就是在TE脉冲之间就把数据发送完这样就不会存在 斜线情况。

Note:优缺点分析,虽然提升了画图帧率,但是由于vblank 的时间变长,将导致LCD的自刷新帧率下降至40FPS。
16. 屏幕上电闪一下杂色或者红色处理
引发的原因是上电的时候lvgl 还没启动完成,lcd osd已经把osd_enc_buf 中的数据发了出去。这种情况有两种处理方式
-
利用 背光引脚 延时打开控制。错开不稳定时间 即可。
-
把osd_enc_buf 初始值初始化为接近黑色的状态。

Note:osd数据编码规则:
假如有8个像素点为0x2000的颜色,osd编码后就是0xFFFF 0x0008 0x2000
0xFFFF是编码头,0x0008是颜色重复次数,0x2000是颜色