@ccccchduan 这里更强
livpo 发布的最佳帖子
-
【R128】应用开发案例——点亮一颗 LED 灯
基于R128-S2设计的全套开发板已上线淘宝百问网韦东山老师个人店进行售卖,包含黑色的DshanMCU-R128s2-R16N16模组和全套的DshanMCU-R128s2-DEVKIT。
- DshanMCU-R128s2-R16N16模组:39.9元
- DshanMCU-R128s2-DEVKIT开发板:59.9元
R128开发板购买链接:https://item.taobao.com/item.htm?spm=a21n57.1.0.0.46b0523cMfarLo&id=736154682975&ns=1&abbucket=5#detail
点亮一颗 LED 灯
本文案例代码 下载地址 点亮一颗 LED 灯案例代码 https://www.aw-ol.com/downloads?cat=24 首先我们搭建电路,如下:
引脚 LED PA18 红色 LED PA13 绿色 LED PA12 黄色 LED 载入方案
我们使用的开发板是 R128-Devkit,需要开发 C906 核心的应用程序,所以载入方案选择
r128s2_module_c906
$ source envsetup.sh $ lunch_rtos 1
勾选 GPIO 驱动
mrtos_menuconfig
找到下列驱动Drivers Options ---> soc related device drivers ---> GPIO devices ---> [*] enable GPIO driver [*] enbale GPIO hal APIs Test command
编写程序
打开你喜欢的编辑器,修改文件:
lichee/rtos/projects/r128s2/module_c906/src/main.c
引入头文件
#include <hal_gpio.h>
使用 GPIO 配置引脚
配置 GPIO 的上下拉状态
使用
hal_gpio_set_pull(gpio_pin_t pin, gpio_pull_status_t pull);
来设置。这里我们设置PA18
引脚为默认上拉状态。hal_gpio_set_pull(GPIOA(18), GPIO_PULL_UP);
配置 GPIO 输入输出模式
使用
hal_gpio_set_direction(gpio_pin_t pin, gpio_direction_t direction);
来设置 GPIO 的输入输出模式,这里配置为输出模式。hal_gpio_set_direction(GPIOA(18), GPIO_DIRECTION_OUTPUT);
配置 GPIO 的 MUX 功能
GPIO 通常有多种功能,需要配置 MUX 选择需要的功能,使用
hal_gpio_pinmux_set_function(gpio_pin_t pin, gpio_muxsel_t function_index);
来设置 GPIO 的复用功能,这里配置为GPIO 输出模式(GPIO_MUXSEL_OUT
)hal_gpio_pinmux_set_function(GPIOA(18), GPIO_MUXSEL_OUT);
配置 GPIO 的电平
使用
hal_gpio_set_data(gpio_pin_t pin, gpio_data_t data);
来配置 GPIO 的电平,这里配置PA18
为高电平点亮 LEDhal_gpio_set_data(GPIOA(18), GPIO_DATA_HIGH);
完整的配置 GPIO
hal_gpio_set_pull(GPIOA(18), GPIO_PULL_UP); // 配置 GPIO 的上下拉状态 hal_gpio_set_direction(GPIOA(18), GPIO_DIRECTION_OUTPUT); // 配置 GPIO 输入输出模式 hal_gpio_pinmux_set_function(GPIOA(18), GPIO_MUXSEL_OUT); // 配置 GPIO 的 MUX 功能 hal_gpio_set_data(GPIOA(18), GPIO_DATA_HIGH); // 配置 GPIO 的电平
以此类推,我们同时配置
PA18
,PA13
,PA12
的 GPIOhal_gpio_set_pull(GPIOA(18), GPIO_PULL_UP); hal_gpio_set_direction(GPIOA(18), GPIO_DIRECTION_OUTPUT); hal_gpio_pinmux_set_function(GPIOA(18), GPIO_MUXSEL_OUT); hal_gpio_set_data(GPIOA(18), GPIO_DATA_HIGH); hal_gpio_set_pull(GPIOA(13), GPIO_PULL_UP); hal_gpio_set_direction(GPIOA(13), GPIO_DIRECTION_OUTPUT); hal_gpio_pinmux_set_function(GPIOA(13), GPIO_MUXSEL_OUT); hal_gpio_set_data(GPIOA(13), GPIO_DATA_HIGH); hal_gpio_set_pull(GPIOA(12), GPIO_PULL_UP); hal_gpio_set_direction(GPIOA(12), GPIO_DIRECTION_OUTPUT); hal_gpio_pinmux_set_function(GPIOA(12), GPIO_MUXSEL_OUT); hal_gpio_set_data(GPIOA(12), GPIO_DATA_HIGH);
结果
编译固件后烧录,可以看到三色 LED 灯同时亮起。
流水灯
为了实现流水灯,我们先实现一个
sleep
函数static inline int msleep(int ms) { vTaskDelay(ms / portTICK_RATE_MS); }
然后实现流水灯逻辑即可,之前已经设置过的GPIO状态不需要重复设置。
while (1) { hal_gpio_set_data(GPIOA(18), GPIO_DATA_HIGH); hal_gpio_set_data(GPIOA(13), GPIO_DATA_LOW); hal_gpio_set_data(GPIOA(12), GPIO_DATA_LOW); msleep(100); hal_gpio_set_data(GPIOA(18), GPIO_DATA_LOW); hal_gpio_set_data(GPIOA(13), GPIO_DATA_HIGH); hal_gpio_set_data(GPIOA(12), GPIO_DATA_LOW); msleep(100); hal_gpio_set_data(GPIOA(18), GPIO_DATA_LOW); hal_gpio_set_data(GPIOA(13), GPIO_DATA_LOW); hal_gpio_set_data(GPIOA(12), GPIO_DATA_HIGH); msleep(100); }
-
【R128】基础组件开发指南——显示与屏幕驱动
基于R128-S2设计的全套开发板已上线淘宝百问网韦东山老师个人店进行售卖,包含黑色的DshanMCU-R128s2-R16N16模组和全套的DshanMCU-R128s2-DEVKIT。
- DshanMCU-R128s2-R16N16模组:39.9元
- DshanMCU-R128s2-DEVKIT开发板:59.9元
R128开发板购买链接:https://item.taobao.com/item.htm?spm=a21n57.1.0.0.46b0523cMfarLo&id=736154682975&ns=1&abbucket=5#detail
显示与屏幕驱动
RTOS 提供了一套完整的屏幕驱动,支持 RGB, i8080, SPI, DBI 格式的屏幕。
(1)RGB 接口
RGB接口在全志平台又称HV接口(Horizontal同步和Vertical同步)。有些LCD屏支持高级的功能比如 gamma,像素格式的设置等,但是 RGB 协议本身不支持图像数据之外的传输,所以无法通过 RGB 管脚进行对 LCD 屏进行配置,所以拿到一款 RGB 接口屏,要么不需要初始化命令,要么这个屏会提供额外的管脚给 SoC 来进行配置,比如 SPI 和 I2C 等。RGB 屏幕有许多格式,不同的位宽,不同的时钟周期。下表是位宽与时钟周期的区别。
位宽 时钟周期数 颜色数量和格式 并行\串行 RGB 24 bits 1 cycle 16.7M colors, RGB888 并行 18 bits 1 cycle 262K colors, RGB666 并行 16 bits 1 cycle 65K colors, RGB565 并行 6 bits 3 cycles 262K colors, RGB666 串行 6 bits 3 cycles 65K colors, RGB565 串行 串行 RGB 是相对于并行 RGB 来说,而并不是说它只用一根线来发数据,只要通过多个时钟周期才能把一个像素的数据发完,那么这样的 RGB 接口就是串行 RGB。
(2)I8080 屏幕
Intel 8080 接口屏(又称 MCU 接口),很老的协议,一般用在分辨率很小的屏上。
管脚的控制脚有6种:
- CS 片选信号,决定该芯片是否工作.
- RS 寄存器选择信号,低表示选择 index 或者 status 寄存器,高表示选择控制寄存器。实际场景中一般接SoC的LCD_DE脚(数据使能脚)
- WR (低表示写数据) 数据命令区分信号,也就是写时钟信号,一般接 SoC 的 LCD_CLK 脚
- RD (低表示读数据)数据读信号,也就是读时钟信号,一般接 SoC 的 LCD_HSYNC 脚
- RESET 复位LCD( 用固定命令系列 0 1 0来复位)
- Data 是双向的数据通路
I8080 根据的数据位宽接口有 8/9/16/18,连哪些脚参考,即使位宽一样,连的管脚也不一样,还要考虑的因素是 RGB 格式。
- RGB565,总共有 65K 这么多种颜色
- RGB666,总共有 262K 那么多种颜色
- 9bit 固定为 262K
(3)SPI 屏幕
SPI LCD 是使用 SPI 总线传输图像数据的屏幕,只会出现在很低分辨率的屏幕上。一般来说开屏前都需要初始化操作。
适配 LCD 屏幕的步骤
- 确保全志显示框架的内核配置有使能
- 前期准备以下资料和信息:
- 屏手册。主要是描述屏基本信息和电气特性等,向屏厂索要。
- Driver IC 手册。主要是描述屏 IC 的详细信息。这里主要是对各个命令进行详解,对我们进行初始化定制有用,向屏厂索要。
- 屏时序信息。请向屏厂索要。
- 屏初始化代码,请向屏厂索要。一般情况下 DSI 和 I8080 屏等都需要初始化命令对屏进行初始化。
- 万用表。调屏避免不了测量相关电压。
- 通过第2步屏厂提供的资料,定位该屏的类型,然后选择一个已有同样类型的屏驱动作为模板进行屏驱动添加或者直接在上面修改。
- 修改屏驱动目录下的
panel.c
和panel.h
。在全局结构体变量panel_array
中新增刚才添加strcut __lcd_panel
的变量指针。panel.h
中新增strcut __lcd_panel
的声明。 - 修改 Makefile。在 lcd 屏驱动目录的上一级的
Makefile
文件中的disp-objs
中新增刚才添加屏驱动.o - 修改
sys_config.fex
中的lcd0
节点。 - 编译测试
LCD 屏幕驱动源码
LCD 屏幕驱动源码结构
. ├── Kconfig ├── Makefile ├── disp │ ├── Kconfig │ ├── Makefile │ ├── de # Display Engine 层驱动,包括图层与显示控制 │ │ ├── Makefile │ │ ├── bsp_display.h │ │ ├── disp_capture.c │ │ ├── disp_capture.h │ │ ├── disp_device.c │ │ ├── disp_device.h │ │ ├── disp_display.c │ │ ├── disp_display.h │ │ ├── disp_enhance.c │ │ ├── disp_enhance.h │ │ ├── disp_features.c │ │ ├── disp_features.h │ │ ├── disp_hdmi.c │ │ ├── disp_hdmi.h │ │ ├── disp_lcd.c │ │ ├── disp_lcd.h │ │ ├── disp_manager.c │ │ ├── disp_manager.h │ │ ├── disp_private.c │ │ ├── disp_private.h │ │ ├── disp_smart_backlight.c │ │ ├── disp_smart_backlight.h │ │ ├── disp_tv.c │ │ ├── disp_tv.h │ │ ├── disp_vdevice.c │ │ ├── disp_vdevice.h │ │ ├── include.h │ │ └── lowlevel_v2x # DISP 底层驱动,硬件寄存器交互 │ │ ├── Makefile │ │ ├── de_ase.c │ │ ├── de_ase_type.h │ │ ├── de_bws.c │ │ ├── de_bws_type.h │ │ ├── de_ccsc.c │ │ ├── de_clock.c │ │ ├── de_clock.h │ │ ├── de_csc.h │ │ ├── de_csc_type.h │ │ ├── de_dcsc.c │ │ ├── de_dsi.c │ │ ├── de_dsi.h │ │ ├── de_dsi_28.c │ │ ├── de_dsi_type.h │ │ ├── de_dsi_type_28.h │ │ ├── de_eink.c │ │ ├── de_eink.h │ │ ├── de_enhance.c │ │ ├── de_enhance.h │ │ ├── de_fcc.c │ │ ├── de_fcc_type.h │ │ ├── de_fce.c │ │ ├── de_fce_type.h │ │ ├── de_feat.c │ │ ├── de_feat.h │ │ ├── de_gsu.c │ │ ├── de_gsu_type.h │ │ ├── de_hal.c │ │ ├── de_hal.h │ │ ├── de_lcd.c │ │ ├── de_lcd.h │ │ ├── de_lcd_sun50iw10.c │ │ ├── de_lcd_type.h │ │ ├── de_lti.c │ │ ├── de_lti_type.h │ │ ├── de_peak.c │ │ ├── de_peak_type.h │ │ ├── de_rtmx.c │ │ ├── de_rtmx.h │ │ ├── de_rtmx_type.h │ │ ├── de_scaler.h │ │ ├── de_scaler_table.c │ │ ├── de_scaler_table.h │ │ ├── de_smbl.c │ │ ├── de_smbl.h │ │ ├── de_smbl_tab.h │ │ ├── de_smbl_type.h │ │ ├── de_vep.h │ │ ├── de_vep_table.c │ │ ├── de_vep_table.h │ │ ├── de_vsu.c │ │ ├── de_vsu_type.h │ │ ├── de_wb.c │ │ ├── de_wb.h │ │ ├── de_wb_type.h │ │ ├── disp_al.c │ │ ├── disp_al.h │ │ ├── disp_eink_data.c │ │ ├── disp_eink_data.h │ │ ├── disp_waveform.c │ │ ├── disp_waveform.h │ │ ├── rtmx_eink.c │ │ └── rtmx_eink.h │ ├── dev_disp.c # DISP 公共端口 │ ├── dev_disp.h │ ├── disp_debug.c │ ├── disp_debug.h │ ├── disp_sys_intf.c │ ├── disp_sys_intf.h │ ├── lcd # LCD 面板驱动,包括自定义初始化控制,上下电时序控制 │ │ ├── Kconfig │ │ ├── S6D7AA0X01.c │ │ ├── S6D7AA0X01.h │ │ ├── VVX07H005A10.c │ │ ├── VVX07H005A10.h │ │ ├── WilliamLcd.c │ │ ├── WilliamLcd.h │ │ ├── b080uan01_mipi1200x1920.c │ │ ├── b080uan01_mipi1200x1920.h │ │ ├── cl40bc1019_cpu.c │ │ ├── cl40bc1019_cpu.h │ │ ├── cpu_gg1p4062utsw.c │ │ ├── cpu_gg1p4062utsw.h │ │ ├── default_eink.c │ │ ├── default_eink.h │ │ ├── default_panel.c │ │ ├── default_panel.h │ └── wtq05027d01.h ├── soc # SoC 层特化驱动 │ ├── Kconfig │ ├── Makefile │ ├── VVX07H005A10_mipi_config.c │ ├── disp_board_config.c │ ├── disp_board_config.h │ ├── he0801a068_mipi_config.c │ ├── platform_resource.c │ ├── platform_resource.h │ ├── sun20iw2.c └── tv # TV 驱动,R128不使用 ├── Makefile ├── de_tve_sun8iw11.c ├── de_tve_sun8iw7.c ├── de_tve_v1.c ├── de_tvec.h ├── drv_tv.c ├── drv_tv.h ├── gm7121.c ├── tv_ac200.c ├── tv_ac200.h ├── tv_ac200_lowlevel.c └── tv_ac200_lowlevel.h
屏驱动源码位置
RGB 面板驱动
对于不需要初始化的 RGB 屏幕(一般是 40PIN,50PIN)使用
default_panel.c
lichee/rtos‑hal/hal/source/disp2/disp/lcd/default_panel.c
LCD 面板特化驱动
部分 LCD 面板需要写 IIC,SPI初始化,或者有特殊的上下电要求,需要编写特化的屏幕驱动
lichee/rtos‑hal/hal/source/disp2/disp/lcd/
配置文件
其中 “芯片型号” 例如 r128s3,和 “板子名称” 例如 pro,请根据实际替换。
board/芯片型号/板子名称/configs/
屏幕驱动配置
lcd 相关代码包含在 disp 驱动模块中,执行命令进入 menuconfig 配置主界面,并按以下步骤操作:
添加新屏
添加一款新屏幕通常需要以下步骤:
panel.c
和panel.h
,当用户添加新屏驱动时,是需要修改这两个文件的,需要将屏结构体变量添加到全局结构体变量panel_array中。lcd_source.c
和lcd_source.h
,这两个文件实现的是给屏驱动使用的函数接口,比如电源开关,gpio,dsi 读写接口等,用户不需要修改只需要用。- 屏驱动。除了上面提到的源文件外,其它的一般一个 c 文件和一个 h 文件就代表一个屏驱动。
- 在屏驱动源码位置的上一级,有用户需要修改的
Makefile
文件。
我们可以打开
lichee/rtos‑hal/hal/source/disp2/disp/lcd/default_panel.c
作为屏驱动的例子,在该文件的最后:struct __lcd_panel default_panel = { /* panel driver name, must mach the lcd_drv_name in sys_config.fex */ .name = "default_lcd", .func = { .cfg_panel_info = LCD_cfg_panel_info, .cfg_open_flow = LCD_open_flow, .cfg_close_flow = LCD_close_flow, }, };
-
该全局变量
default_panel
的成员name
与lcd_driver_name
必须一致,这个关系到驱动能否找到指定的文件。 -
接 下 来 是
func
成 员 的 初 始 化, 这 里 最 主 要 实 现 三 个 回 调 函 数。LCD_cfg_panel_info
,LCD_open_flow
和LCD_close_flow
。 -
开关屏流程即屏上下电流程,屏手册或者 driver IC 手册中里面的 Power on Sequence 和 Power off Sequence。用于开关屏的操作流程如下图所示
- 其中,
LCD_open_flow
和LCD_close_flow
称为开关屏流程函数。方框中的函数,如LCD_power_on
,称为开关屏步骤函数。 - 不需要进行初始化操作的 LCD 屏,例如部分 RGB 屏等,
LCD_panel_init
及LCD_panel_exit
这些函数可以为空。
LCD_open_flow
LCD_open_flow 函数只会在系统初始化的时候调用一次,执行每个 LCD_OPEN_FUNC 即是把对应的开屏步骤函数进行注册,先注册先执行,但并没有立刻执行该开屏步骤函数。
函数原型:
static __s32 LCD_open_flow(__u32 sel)
函数常用内容为:
static __s32 LCD_open_flow(__u32 sel) { LCD_OPEN_FUNC(sel, LCD_power_on,10); LCD_OPEN_FUNC(sel, LCD_panel_init, 50); LCD_OPEN_FUNC(sel, sunxi_lcd_tcon_enable, 100); LCD_OPEN_FUNC(sel, LCD_bl_open, 0); return 0; }
如上,调用四次 LCD_OPEN_FUNC 注册了四个回调函数,对应了四个开屏流程, 先注册先执行。实际上注册多少个函数是用户自己的自由,只要合理即可。
- LCD_power_on 即打开 LCD 电源,再延迟 10ms;这个步骤一般用于打开 LCD 相关电源和相关管脚比如复位脚。这里一般是使用电源控制函数说明和管脚控制函数说明进行操作。
- LCD_panel_init 即初始化屏,再延迟 50ms;不需要初始化的屏,可省掉此步骤,这个函数一般用于发送初始化命令给屏进行屏初始化。如果是 I8080 屏用I8080 接口函数说明,如果是其它情况比如 i2c 或者 spi 可以看使用 iic/spi 串行接口初始化,也可以用 GPIO 来进行模拟。
- sunxi_lcd_tcon_enable 打开 TCON,再延迟 100ms;这一步是固定的,表示开始发送图像信号。
- LCD_bl_open 打开背光,再延迟 0ms。前面三步搞定之后才开背光,这样不会看到闪烁。这里一般使用的函数请看背光控制函数说明。
LCD_OPEN_FUNC
注册开屏步骤函数到开屏流程中,记住这里是注册不是执行!
函数原型:
void LCD_OPEN_FUNC(__u32 sel, LCD_FUNC func, __u32 delay)
参数说明:
func
是一个函数指针,其类型是:void (*LCD_FUNC) (__u32 sel)
,用户自己定义的函数必须也要用统一的形式。比如:void user_defined_func(__u32 sel) { // do something }
delay
是执行该步骤后,再延迟的时间,时间单位是毫秒。LCD_OPEN_FUNC 的第二个参数是前后两个步骤的延时长度,单位 ms,注意这里的数值请按照屏手册规定去填,乱填可能导致屏初始化异常或者开关屏时间过长,影响用户体验。
LCD_close_flow
与
LCD_open_flow
对应的是LCD_close_flow
,它用于注册关屏函数。使用LCD_CLOSE_FUNC
进行函数注册,先注册先执行。这里只是注册回调函数,不是立刻执行。static s32 LCD_close_flow(u32 sel) { /* close lcd backlight, and delay 0ms */ LCD_CLOSE_FUNC(sel, LCD_bl_close, 0); /* close lcd controller, and delay 0ms */ LCD_CLOSE_FUNC(sel, sunxi_lcd_tcon_disable, 50); /* open lcd power, than delay 200ms */ LCD_CLOSE_FUNC(sel, LCD_panel_exit, 100); /* close lcd power, and delay 500ms */ LCD_CLOSE_FUNC(sel, LCD_power_off, 0); return 0; }
- 先关闭背光,这样整个关屏过程,用户不会看到闪烁的过程;
- 关闭 TCON(即停止发送数据)再延迟 50ms;
- 执行关屏代码,再延迟 200ms;(不需要初始化的屏,可省掉此步骤)
- 最后关闭电源,再延迟 0ms。
LCD_cfg_panel_info
配置的 TCON 扩展参数,比如 gamma 功能和颜色映射功能。
函数原型:
static void LCD_cfg_panel_info(__panel_extend_para_t *info)
TCON 的扩展参数只能在屏文件中配置,参数的定义:
lcd_frm
Lcd Frame Rate Modulator, FRM 是解决由于 PIN 减少导致的色深问题,有些 LCD 屏的像素格式是 18bit 色深(RGB666)或 16bit 色深(RGB565),建议打开 FRM 功能,通过 dither 的方式弥补色深,使显示达到 24bit 色深(RGB888)的效果。如下图所示,上图是色深为 RGB66 的 LCD 屏显示,下图是打开 dither 后的显示,打开 dither 后色彩渐变的地方过度平滑。
参数设置相应值对应含义为:
0:RGB888 ‑‑ RGB888 direct 1:RGB888 ‑‑ RGB666 dither 2:RGB888 ‑‑ RGB565 dither
lcd_gamma_en
Lcd Gamma Correction Enable,设置相应值的对应含义为:
0:LCD 的 Gamma 校正功能关闭 1:LCD 的 Gamma 校正功能开启
设置为 1 时,需要在屏驱动中对
lcd_gamma_tbl[256]
进行赋值。lcd_cmap_en
Lcd Color Map Enable, 设置为 1 时,需要对
lcd_cmap_tbl [2][3][4]
进行赋值Lcd Color Map Table
。每个像素有 R、G、B 三个单元,每四个像素组成一个选择项,总共有 12 个可选。数组第一维表示奇偶行,第二维表示像素的 RGB,第三维表示第几个像素,数组的内容即表示该位置映射到的内容。
LCD CMAP 是对像素的映射输出功能,只有像素有特殊排布的 LCD 屏才需要配置。
LCD CMAP 定义每行的 4 个像素为一个总单元,每个像素分 R、G、B 3 个小单元,总共有 12 个小单元。通过 lcd_cmap_tbl 定义映射关系,输出的每个小单元可随意映射到 12 个小单元之一。
__u32 lcd_cmap_tbl[2][3][4] = { { {LCD_CMAP_G0,LCD_CMAP_B1,LCD_CMAP_G2,LCD_CMAP_B3}, {LCD_CMAP_B0,LCD_CMAP_R1,LCD_CMAP_B2,LCD_CMAP_R3}, {LCD_CMAP_R0,LCD_CMAP_G1,LCD_CMAP_R2,LCD_CMAP_G3}, }, { {LCD_CMAP_B3,LCD_CMAP_G2,LCD_CMAP_B1,LCD_CMAP_G0}, {LCD_CMAP_R3,LCD_CMAP_B2,LCD_CMAP_R1,LCD_CMAP_B0}, {LCD_CMAP_G3,LCD_CMAP_R2,LCD_CMAP_G1,LCD_CMAP_R0}, }, };
如上,上三行代表奇数行的像素排布,下三行代表偶数行的像素排布;
每四个像素为一个单元,第一列代表每四个像素的第一个像素映射,第二列代表每四个像素的第二个像素映射,以此类推。
如上的定义,像素的输出格式如下图所示。
lcd_rb_swap
调换
TCON
模块RGB
中的 R 分量和 B 分量。0:不变 1:调换R分量和B分量
需要 gamma 校正,或色彩映射,在
sys_config.fex
中将相应模块的enable
参数置 1,lcd_gamma_en
,lcd_cmap_en
,并且填充 3 个系数表,lcd_gamma_tbl
,lcd_cmap_tbl
,注意的是:gamma,模板提供了 18 段拐点值,然后再插值出所有的值(255 个)。可以往相应表格内添加子项以补充细节部分。cmap_tbl 的大小是固定的,不能减小或增加表的大小。最终生成的 gamma 表项是由 rgb 三个 gamma 值组成的,各占 8bit。目前提供的模板中,三个 gamma 值是相同的。延时函数
函数原型
(毫秒级别)
s32 sunxi_lcd_delay_ms(u32 ms)
(微秒级别)
s32 sunxi_lcd_delay_us(u32 us)
图像数据使能函数
打开 LCD 控制器,开始刷新 LCD 显示
void sunxi_lcd_tcon_enable(u32 screen_id)
关闭 LCD 控制器,停止刷新数据
void sunxi_lcd_tcon_disable(u32 screen_id)
背光控制函数
打开背光,操作的是
sys_config.fex
中lcd_bl
配置的gpio
。void sunxi_lcd_backlight_enable(u32 screen_id)
关闭背光,操作的是
sys_config.fex
中lcd_bl
配置的gpio
。void sunxi_lcd_backlight_disable(u32 screen_id)
打开PWM背光,打开时 pwm 将往外输出 pwm 波形。对应的是
lcd_pwm_ch
所对应的那一路 pwm。s32 sunxi_lcd_pwm_enable(u32 screen_id)
关闭PWM背光,打开时 pwm 将往外输出 pwm 波形。对应的是
lcd_pwm_ch
所对应的那一路 pwm。s32 sunxi_lcd_pwm_disable(u32 screen_id)
电源控制函数
打开
Lcd
电源,操作的是sys_config.fex
中的lcd_power/lcd_power1/lcd_power2
。(pwr_id
标识电源索引)void sunxi_lcd_power_enable(u32 screen_id, u32 pwr_id)
关闭
Lcd
电源,操作的是sys_config.fex
中的lcd_power/lcd_power1/lcd_power2
。(pwr_id
标识电源索引)void sunxi_lcd_power_disable(u32 screen_id, u32 pwr_id)
-
pwr_id = 0:对应于 sys_config.fex 中的 lcd_power。
-
pwr_id = 1:对应于 sys_config.fex 中的 lcd_power1。
-
pwr_id = 2:对应于 sys_config.fex 中的 lcd_power2。
-
pwr_id = 3:对应于 sys_config.fex 中的 lcd_power3。
sunxi_lcd_pin_cfg
配置 lcd 的 io
函数原型
s32 sunxi_lcd_pin_cfg(u32 screen_id, u32 bon)
配置
lcd
的data/clk
等pin
,对应sys_config.fex
中的lcdd0‑lcdd23/lcddclk/lcdde/lcdhsync/lcdvsync
。参数:
- Bon: 1: 为开,0:为配置成 disable 状态。
I8080 接口函数说明
显示驱动提供 5 个接口函数可供使用。如下:
sunxi_lcd_cpu_write
设定 CPU 屏的指定寄存器为指定的值。
函数原型
void sunxi_lcd_cpu_write(__u32 sel, __u32 index, __u32 data) { sunxi_lcd_cpu_write_index(sel, index); sunxi_lcd_cpu_wirte_data(sel, data); }
实现了 8080 总线上的两个写操作
sunxi_lcd_cpu_write_index
实现第一个写操作,这时 PIN 脚 RS(A1)为低电平,总线数据上的数据内容为参数 index 的值。sunxi_lcd_cpu_wirte_data
实现第二个写操作,这时 PIN 脚 RS(A1)为高电平,总线数据上的数据内容为参数 data 的值。
sunxi_lcd_cpu_write_index
设定 CPU 屏为指定寄存器。
void sunxi_lcd_cpu_write_index(__u32 sel,__u32 index)
参数:
- sel:显示屏 id
- index: 要设定的寄存器
sunxi_lcd_cpu_write_data
设定 CPU 屏寄存器的值为指定的值
void sunxi_lcd_cpu_write_data(__u32 sel, __u32 data)
参数:
- sel:显示屏 id
- index: 要设定的寄存器的值
tcon0_cpu_rd_24b_data
读操作
s32 tcon0_cpu_rd_24b_data(u32 sel, u32 index, u32 *data, u32 size)
参数:
- sel:显示屏 id
- index: 要读取的寄存器
- data:用于存放读取接口的数组指针,用户必须保证其有足够空间存放数据
- size:要读取的字节数
管脚控制函数
sunxi_lcd_gpio_set_value
LCD_GPIO PIN 脚上输出高电平或低电平
s32 sunxi_lcd_gpio_set_value(u32 screen_id, u32 io_index, u32 value)
参数:
- io_index = 0:对应于 sys_config.fex 中的 lcd_gpio_0。
- io_index = 1:对应于 sys_config.fex 中的 lcd_gpio_1。
- io_index = 2:对应于 sys_config.fex 中的 lcd_gpio_2。
- io_index = 3:对应于 sys_config.fex 中的 lcd_gpio_3。
- value = 0:对应 IO 输出低电平。
- value = 1:对应 IO 输出高电平。
只用于该 GPIO 定义为输出的情形。
sunxi_lcd_gpio_set_direction
设置 LCD_GPIO PIN 脚为输入或输出模式
s32 sunxi_lcd_gpio_set_direction(u32 screen_id, u32 io_index, u32 direction)
参数:
- io_index = 0:对应于 sys_config.fex 中的 lcd_gpio_0。
- io_index = 1:对应于 sys_config.fex 中的 lcd_gpio_1。
- io_index = 2:对应于 sys_config.fex 中的 lcd_gpio_2。
- io_index = 3:对应于 sys_config.fex 中的 lcd_gpio_3。
- direction = 0:对应 IO 设置为输入。
- direction = 1:对应 IO 设置为输出。
一部分屏需要进行初始化操作,在开屏步骤函数中,对应于
LCD_panel_init
函数,提供了几种方式对屏的初始化。对于 CPU 屏,是通过 8080 总线的方式,使用的是 LCDIO(PD,PH)进行初始化。这种初始化方式,其总线的引脚位置定义与 CPU 屏一致。对于 SPI/IIC 初始化的 LCD,使用独立的IO初始化。使用 SPI 初始化
一般使用 GPIO 模拟的方式初始化 SPI 屏幕,其中 SPI 模拟如下所示
#define spi_scl_1 sunxi_lcd_gpio_set_value(0, 3, 1) // 配置 lcd_gpio_3 为 SCL #define spi_scl_0 sunxi_lcd_gpio_set_value(0, 3, 0) #define spi_sdi_1 sunxi_lcd_gpio_set_value(0, 2, 1) // 配置 lcd_gpio_2 为 SDI #define spi_sdi_0 sunxi_lcd_gpio_set_value(0, 2, 0) #define spi_cs_1 sunxi_lcd_gpio_set_value(0, 1, 1) // 配置 lcd_gpio_1 为 CS #define spi_cs_0 sunxi_lcd_gpio_set_value(0, 1, 0) static void spi_write_cmd(u8 value) { int i; spi_cs_0; spi_scl_0; spi_sdi_0; spi_scl_1; spi_scl_0; for (i = 0; i < 8; i++) { if (value & 0x80) spi_sdi_1; else spi_sdi_0; value <<= 1; spi_scl_1; spi_scl_0; } spi_cs_1; } static void spi_write_data(u8 value) { int i; spi_cs_0; spi_scl_0; spi_sdi_1; spi_scl_1; spi_scl_0; for (i = 0; i < 8; i++) { if (value & 0x80) spi_sdi_1; else spi_sdi_0; value <<= 1; spi_scl_1; spi_scl_0; } spi_cs_1; }
然后就可以调用
spi_write_cmd(u8 value)
与spi_write_data(u8 value)
函数写入初始化命令。也可以使用 硬件 SPI 初始化屏幕,代码如下
static int spi_init(void) { int ret = ‑1; struct spi_master *master; master = spi_busnum_to_master(1); if (!master) { lcd_fb_wrn("fail to get master\n"); goto OUT } spi_device = spi_alloc_device(master); if (!spi_device) { lcd_fb_wrn("fail to get spi device\n"); goto OUT; } spi_device‑> bits_per_word = 8; spi_device‑> max_speed_hz = 50000000; /*50MHz*/ spi_device‑> mode = SPI_MODE_0; ret = spi_setup(spi_device); if (ret) { lcd_fb_wrn("Faile to setup spi\n"); goto FREE; } lcd_fb_inf("Init spi1:bits_per_word:%d max_speed_hz:%d mode:%d\n", spi_device‑> bits_per_word, spi_device‑> max_speed_hz, spi_device‑> mode); ret = 0; goto OUT; FREE: spi_master_put(master); kfree(spi_device); spi_device = NULL; OUT: return ret; } static int comm_out(unsigned int sel, unsigned char cmd) { struct spi_transfer t; if (!spi_device) return ‑1; DC(sel, 0); memset(&t, 0, sizeof(struct spi_transfer)); t.tx_buf = &cmd; t.len = 1; t.bits_per_word = 8; t.speed_hz = 24000000; return spi_sync_transfer(spi_device, &t, 1); }
首先调用
spi_init
函数对spi
硬件进行初始化,spi_init
函数可以分为几个步骤,第一获取master
;根据实际的硬件连接,选择spi
(代码中选择了spi1
),如果这一步返回错误说spi
没有配置好。第二步设置spi device
,这里包括最大速度,spi
传输模式,以及每个字包含的比特数。最后调用spi_setup
完成master
和device
的关联。comm_out
是一个spi
传输的例子,核心就是spi_sync_transfer
函数。并行 RGB 接口
当我们配置并行 RGB 接口时,在配置里面并不需要区分是 24 位,18 位和 16 位,最大位宽是哪种是参考 pin mux 表格,如果 LCD 屏本身支持的位宽比 SoC 支持的位宽少,当然只能选择少的一方。
因为不需要初始化,RGB 接口极少出现问题,重点关注 lcd 的 timing 的合理性,也就是lcd_ht,lcd_hspw,lcd_hbp,lcd_vt,lcd_vspw 和 lcd_vbp 这个属性的合理性。
下面是典型并行 RGB 接口 sys_config.fex 配置示例,其中用空行把配置分成几个部分
;-------------------------------------------------- ;Parallel RGB LCD ;-------------------------------------------------- [lcd0] ; Part 1 lcd_used = 1 lcd_driver_name = "default_lcd" ; Part 2 lcd_if = 0 ; Part 3 lcd_x = 480 lcd_y = 480 lcd_width = 150 lcd_height = 94 lcd_rb_swap = 0 lcd_dclk_freq = 21 lcd_hv_clk_phase = 1 ; Part 4 lcd_backlight = 150 lcd_pwm_used = 1 lcd_pwm_ch = 5 lcd_pwm_freq = 5000 lcd_pwm_pol = 1 ; Part 5 lcd_hbp = 80 lcd_ht = 648 lcd_hspw = 8 lcd_vbp = 10 lcd_vt = 522 lcd_vspw = 2 lcd_lvds_if = 0 lcd_lvds_colordepth = 1 lcd_lvds_mode = 0 lcd_frm = 1 lcd_io_phase = 0x0000 lcd_gamma_en = 0 lcd_bright_curve_en = 0 lcd_cmap_en = 0 deu_mode = 0 lcdgamma4iep = 22 smart_color = 90 ; Part 6 ;LCD_D2-LCD_D7 lcd_gpio_4 = port:PA00<8><0><3><0> lcd_gpio_5 = port:PA01<8><0><3><0> lcd_gpio_6 = port:PA02<8><0><3><0> lcd_gpio_7 = port:PA03<8><0><3><0> lcd_gpio_8 = port:PA04<8><0><3><0> lcd_gpio_9 = port:PA05<8><0><3><0> ;LCD_D10-LCD_D15 lcd_gpio_10 = port:PA11<8><0><3><0> lcd_gpio_11 = port:PA10<8><0><3><0> lcd_gpio_12 = port:PA08<8><0><3><0> lcd_gpio_13 = port:PA07<8><0><3><0> lcd_gpio_14 = port:PA06<8><0><3><0> lcd_gpio_15 = port:PA09<8><0><3><0> ;LCD_D18-LCD_D23 lcd_gpio_16 = port:PA12<8><0><3><0> lcd_gpio_17 = port:PA13<8><0><3><0> lcd_gpio_18 = port:PA14<8><0><3><0> lcd_gpio_19 = port:PA15<8><0><3><0> lcd_gpio_20 = port:PB03<8><0><3><0> lcd_gpio_21 = port:PB02<8><0><3><0> ;LCD_VSYNC, LCD_HSYNC, LCD_DCLK, LCD_DE lcd_gpio_0 = port:PA18<8><0><3><0> lcd_gpio_1 = port:PA19<8><0><3><0> lcd_gpio_2 = port:PA20<8><0><3><0> lcd_gpio_3 = port:PA21<8><0><3><0>
- 第一部分,决定该配置是否使用,以及使用哪个屏驱动,lcd_driver_name 决定了用哪个屏驱动来初始化,这里是 default_lcd,是针对不需要初始化设置的 RGB 屏。
- 第二部分决定下面的配置是一个并行 RGB 的配置。
- 第三部分决定 SoC 中的 LCD 模块发送时序。请查看屏时序参数说明。
- 第四部分决定背光(pwm 和 lcd_bl_en)。请看背光相关参数。
- 第五部分是显示效果部分的配置,如果非 24 位的 RGB,那么一般情况下需要设置lcd_frm。
- 第六部分就是电源和管脚配置。是用 RGB666 还是 RGB888,需要根据实际 pinmux 表来决定,如果该芯片只有 18 根 rgb 数据则只能 rgb18。请看电源和管脚参数。
串行 RGB 接口
串行 RGB 是相对于并行 RGB 来说,而并不是说它只用一根线来发数据,只要通过多个时钟周期才能把一个像素的数据发完,那么这样的 RGB 接口就是串行 RGB。
同样与并行 RGB 接口一样,配置中并不需要也无法体现具体是哪种串行 RGB 接口,你要做的就是把硬件连接对就行。
这里需要注意的是,对于该接口,SoC 总共需要三个周期才能发完一个 pixel,所以我们配置时序的时候,需要满足
lcd_dclk_freq*3=lcd_ht*lcd_vt60
,或者lcd_dclk_freq=lcd_ht/3*lcd_vt*60
要么 3 倍lcd_ht
要么 3 倍lcd_dclk_freq
。I8080 接口
Intel 8080 接口屏 (又称 MCU 接口) 很老的协议,一般用在分辨率很小的屏上
管脚的控制脚有 6 种:
管脚 作用说明 CS 片选信号 决定该芯片是否工作。 RS 寄存器选择信号 低表示选择 index 或者 status 寄存器,高表示选择控制寄存器。实际场景中一般接 SoC 的 LCD_DE 脚(数据使能脚)。 WR 数据命令区分信号 即写时钟信号,一般接 SoC 的 LCD_CLK 脚。(低表示写数据) RD 数据读信号 即读时钟信号,一般接 SoC 的 LCD_HSYNC 脚。(低表示读数据) RESET 复位 LCD(用固定命令系列 0 1 0 来复位) Data 双向数据 I8080 根据 Data 的数据位宽接口有 8/9/16/18,连哪些脚参考,即使位宽一样,连的管脚也不一样,还要考虑的因素是 rgb 格式。
- RGB565,总共有 65K 种颜色
- RGB666,总共有 262K 种颜色。
- 9bit 固定为 262K。
RGB 和 I8080 管脚配置示意图
sys_config 参数说明
LCD 接口参数说明
lcd_driver_name
Lcd 屏驱动的名字(字符串),必须与屏驱动的名字对应。
lcd_model_name
Lcd 屏模型名字,非必须,可以用于同个屏驱动中进一步区分不同屏。
lcd_if
Lcd Interface,设置相应值的对应含义为:
0:HV RGB接口 1:CPU/I80接口
lcd_hv_if
Lcd HV panel Interface, 这个参数只有在 lcd_if=0 时才有效。定义 RGB 同步屏下的几种接口类型,设置相应值的对应含义为:
0:Parallel RGB 8:Serial RGB 10:Dummy RGB 11:RGB Dummy 12:Serial YUV (CCIR656)
lcd_hv_clk_phase
这个参数只有在 lcd_if=0 时才有效。定义 RGB 同步屏的 clock 与 data 之间的相位关系。总共有 4个相位可供调节,设置相应值的对应含义为:
0: 0 degree 1: 90 degree 2: 180 degree 3: 270 degree
lcd_hv_sync_polarity
这个参数只有在 lcd_if=0 时才有效。定义 RGB 同步屏的 hsync 和 vsync 的极性。设置相应值的对应含义为:
0:vsync active low,hsync active low 1:vsync active high,hsync active low 2:vsync active low,hsync active high 3:vsync active high,hsync active high
lcd_hv_srgb_seq
这个参数只有在 lcd_if=0 且 lcd_hv_if=8(Serial RGB)时才有效。定义奇数行 RGB 输出的顺序:
0: Odd lines R‑G‑B; Even line R‑G‑B 1: Odd lines B‑R‑G; Even line R‑G‑B 2: Odd lines G‑B‑R; Even line R‑G‑B 4: Odd lines R‑G‑B; Even line B‑R‑G 5: Odd lines B‑R‑G; Even line B‑R‑G 6: Odd lines G‑B‑R; Even line B‑R‑G 8: Odd lines R‑G‑B; Even line B‑R‑G 9: Odd lines B‑R‑G; Even line G‑B‑R 10: Odd lines G‑B‑R; Even line G‑B‑R
lcd_hv_syuv_seq
这个参数只有在 lcd_if=0 且 lcd_hv_if=12(Serial YUV)时才有效。定义 YUV 输出格式:
0:YUYV 1:YVYU 2:UYVY 3:VYU
lcd_hv_syuv_fdly
这个参数只有在 lcd_if=0 且 lcd_hv_if=12(Serial YUV)时才有效。定义 CCIR656 编码时 F 相对有效行延迟的行数:
0:F toggle right after active video line 1:Delay 2 lines (CCIR PAL) 2:Delay 3 lines (CCIR NTSC)
lcd_cpu_if
这个参数只有在 lcd_if=1 时才有效, 具体时序可参照RGB 和 I8080 管脚配置示意图中 CPU 那几列。设置相应值的对应含义为:
0:18bit/1cycle (RGB666) 2: 16bit/3cycle (RGB666) 4:16bit/2cycle (RGB666) 6:16bit/2cycle (RGB666) 8:16bit/1cycle (RGB565) 10:9bit/1cycle (RGB666) 12:8bit/3cycle (RGB666) 14:8bit/2cycle (RGB565)
lcd_cpu_te
设置相应值的对应含义为,设置为 0 时,刷屏间隔时间为 lcd_ht × lcd_vt;设置为 1 或 2 时,刷屏间隔时间为两个 te 脉冲:
0:frame trigged automatically 1:frame trigged by te rising edge 2:frame trigged by te falling edge
lcd_cpu_mode
设置相应值的对应含义为,设置为 0 时,刷屏间隔时间为 lcd_ht × lcd_vt;设置为 1 或 2 时,刷屏间隔时间为两个 te 脉冲:
0:中断自动根据时序,由场消隐信号内部触发 1:中断根据数据Block的counter触发或者由外部te触发。
屏时序参数说明
下面几个参数对于调屏非常关键,决定了发送端(SoC)发送数据时序。由于涉及到发送端和接收端的调试,除了分辨率和尺寸之外,其它几个数值都不是绝对不变的,两款一样分辨率,同种接口的屏,它们的数值也有可能不一样。
获取途径如下:
- 询问 LCD 屏厂。
- 从屏手册或者 Driver IC 手册中查找(向屏厂索要这些文档)
- 在前面两步都搞不定的情况下,可以根据 vesa 标准来设置,主要是 DMT 和 CVT 标准。
由下面两条公式得知,我们不需要设置
lcd_hfp
和lcd_vfp
参数,因为驱动会自动根据其它几个已知参数中算出lcd_hfp
和lcd_vfp
。LCD 时序参数
lcd_x
显示屏的水平像素数量,即屏分辨率中的宽
lcd_y
显示屏的垂直行数,即屏分辨率中的高。
lcd_ht
指一行总的 dclk 的 cycle 个数。
lcd_hbp
指有效行间,行同步信号(hsync)开始,到有效数据开始之间的 dclk 的 cycle 个数,包括同步信号区。包含了 hspw 段,即lcd_hbp=实际的hbp+实际的hspw。
lcd_hspw
指行同步信号的宽度。单位为 1 个 dclk 的时间(即是 1 个 data cycle 的时间)。
lcd_vt
指一场的总行数。
lcd_vbp
指场同步信号(vsync)开始,到有效数据行开始之间的行数,包括场同步信号区。包含了 vspw 段,即 lcd_vbp= 实际的 vbp+ 实际的 vspw。
lcd_vspw
指场同步信号的宽度。单位为行。
lcd_dclk_freq
传输像素传送频率(单位为 MHz)。
fps = (lcd_dclk_freq * 1000 * 1000) / (ht * vt)
这个值根据以下公式计算
lcd_dclk_freq = lcd_ht * lcd_vt * fps
注意:
- 后面的三个参数都是从屏手册中获得,fps 一般是 60。
- 如果是串行接口,发完一个像素需要 2 到 3 个周期的,那么可以用以下公式计算:
lcd_dclk_freq * cycles = lcd_ht * lcd_vt * fps lcd_dclk_freq = lcd_ht * cycles * lcd_vt * fps
lcd_width
此参数描述 lcd 屏幕的物理宽度,单位是 mm,用于计算 dpi。
lcd_height
此参数描述 lcd 屏幕的物理高度,单位是 mm,用于计算 dpi。
背光相关参数
目前用得比较广泛的就是 pwm 背光调节,原理是利用 pwm 脉冲开关产生的高频率闪烁效应,通过调节占空比,达到欺骗人眼,调节亮暗的目的。
lcd_pwm_used
是否使用 pwm,此参数标识用以背光亮度的控制
lcd_pwm_ch
此参数标识使用的 Pwm 通道,这里是指使用 SoC 哪个 pwm 通道,通过查看原理图连接可知。
lcd_pwm_freq
这个参数配置 PWM 信号的频率,单位为 Hz。
- 频率不宜过低否则很容易就会看到闪烁,频率不宜过快否则背光调节效果差。部分屏手册会标明所允许的 pwm 频率范围,请遵循屏手册固定范围进行设置。
- 在低亮度的时候容易看到闪烁,是正常现象,目前已知用上 pwm 的背光都是如此。
lcd_pwm_pol
这个参数配置 PWM 信号的占空比的极性。设置相应值对应含义为:
0:active high 1:active low
lcd_pwm_max_limit
Lcd backlight PWM 最高限制,以亮度值表示。
比如 150,则表示背光最高只能调到 150,0‑255 范围内的亮度值将会被线性映射到 0‑150 范围内。用于控制最高背光亮度,节省功耗
lcd_bl_en
背光使能脚,非必须,看原理图是否有,用于使能或者禁止背光电路的电压。
示例:
lcd_bl_en = port:PD24<1><2><default><1>
含义:PD24 输出高电平时打开 LCD 背光;下拉,默认高电平。
- 第一个尖括号:功能分配。1 为输出。
- 第二个尖括号:内置电阻。使用 0 的话,标示内部电阻高阻态,如果是 1 则是内部电阻上拉,2就代表内部电阻下拉。使用 default 的话代表默认状态,即电阻上拉。其它数据无效。
- 第三个尖括号:驱动能力。default 表驱动能力是等级 1。
- 第四个尖括号:电平。0 为低电平,1 为高电平。
需要在屏驱动调用相应的接口进行开、关的控制。
一般来说,高电平是使能,在这个前提下,建议将内阻电阻设置成下拉,防止硬件原因造成的上拉,导致背光提前亮。默认电平填写高电平,这是 uboot 显示过度到内核显示、平滑无闪烁的需要。
lcd_bl_n_percent
背光映射值,n 为 (0‑100)。
此功能是针对亮度非线性的 LCD 屏的,按照配置的亮度曲线方式来调整亮度变化,以使亮度变化更线性。
比如 lcd_bl_50_percent = 60,表明将 50% 的亮度值调整成 60%,即亮度比原来提高 10%。
修改此属性不当可能导致背光调节效果差。
lcd_backlight
背光默认值,0‑255。
此属性决定在 uboot 显示 logo 阶段的亮度,进入都内核时则是读取保存的配置来决定亮度。
显示 logo 阶段,一般来说需要比较亮的亮度,业内做法都是如此。
显示效果相关参数
lcd_frm
Lcd Frame Rate Modulator, FRM 是解决由于 PIN 减少导致的色深问题,有些 LCD 屏的像素格式是 18bit 色深(RGB666)或 16bit 色深(RGB565),建议打开 FRM 功能,通过 dither 的方式弥补色深,使显示达到 24bit 色深(RGB888)的效果。如下图所示,上图是色深为 RGB66 的 LCD 屏显示,下图是打开 dither 后的显示,打开 dither 后色彩渐变的地方过度平滑。
参数设置相应值对应含义为:
0:RGB888 ‑‑ RGB888 direct 1:RGB888 ‑‑ RGB666 dither 2:RGB888 ‑‑ RGB565 dither
lcd_gamma_en
Lcd Gamma Correction Enable,设置相应值的对应含义为:
0:LCD 的 Gamma 校正功能关闭 1:LCD 的 Gamma 校正功能开启
设置为 1 时,需要在屏驱动中对
lcd_gamma_tbl[256]
进行赋值。lcd_cmap_en
Lcd Color Map Enable, 设置为 1 时,需要对
lcd_cmap_tbl [2][3][4]
进行赋值Lcd Color Map Table
。每个像素有 R、G、B 三个单元,每四个像素组成一个选择项,总共有 12 个可选。数组第一维表示奇偶行,第二维表示像素的 RGB,第三维表示第几个像素,数组的内容即表示该位置映射到的内容。
LCD CMAP 是对像素的映射输出功能,只有像素有特殊排布的 LCD 屏才需要配置。
LCD CMAP 定义每行的 4 个像素为一个总单元,每个像素分 R、G、B 3 个小单元,总共有 12 个小单元。通过 lcd_cmap_tbl 定义映射关系,输出的每个小单元可随意映射到 12 个小单元之一。
__u32 lcd_cmap_tbl[2][3][4] = { { {LCD_CMAP_G0,LCD_CMAP_B1,LCD_CMAP_G2,LCD_CMAP_B3}, {LCD_CMAP_B0,LCD_CMAP_R1,LCD_CMAP_B2,LCD_CMAP_R3}, {LCD_CMAP_R0,LCD_CMAP_G1,LCD_CMAP_R2,LCD_CMAP_G3}, }, { {LCD_CMAP_B3,LCD_CMAP_G2,LCD_CMAP_B1,LCD_CMAP_G0}, {LCD_CMAP_R3,LCD_CMAP_B2,LCD_CMAP_R1,LCD_CMAP_B0}, {LCD_CMAP_G3,LCD_CMAP_R2,LCD_CMAP_G1,LCD_CMAP_R0}, }, };
如上,上三行代表奇数行的像素排布,下三行代表偶数行的像素排布;
每四个像素为一个单元,第一列代表每四个像素的第一个像素映射,第二列代表每四个像素的第二个像素映射,以此类推。
如上的定义,像素的输出格式如下图所示。
lcd_rb_swap
调换
TCON
模块RGB
中的 R 分量和 B 分量。0:不变 1:调换R分量和B分量
需要 gamma 校正,或色彩映射,在
sys_config.fex
中将相应模块的enable
参数置 1,lcd_gamma_en
,lcd_cmap_en
,并且填充 3 个系数表,lcd_gamma_tbl
,lcd_cmap_tbl
,注意的是:gamma,模板提供了 18 段拐点值,然后再插值出所有的值(255 个)。可以往相应表格内添加子项以补充细节部分。cmap_tbl 的大小是固定的,不能减小或增加表的大小。最终生成的 gamma 表项是由 rgb 三个 gamma 值组成的,各占 8bit。目前提供的模板中,三个 gamma 值是相同的。电源和管脚参数
lcd_power
配置好之后,需要在屏驱动调用相应的接口进行开、关的控制。
注意:如果有多个电源需要打开,则定义 lcd_power1,lcd_power2 等。
lcd_pin_power
用法 lcd_power一致,区别是用户设置之后,不需要在屏驱动中去操作,而是驱动框架自行在屏驱动之前使能,在屏驱动之后禁止。
注意:如果需要多组,则添加 lcd_pin_power1,lcd_pin_power2 等。除了 lcddx 之外,这里的电源还有可能是 pwm 所对应管脚的电源。
lcd_gpio_0
示例:
lcd_gpio_0 = port:PD25<0><0><default><0>
含义:lcd_gpio_0 引脚为 PD25。
- 第一个尖括号:功能分配。1 为输出。
- 第二个尖括号:内置电阻。使用 0 的话,标示内部电阻高阻态,如果是 1 则是内部电阻上拉,2就代表内部电阻下拉。使用 default 的话代表默认状态,即电阻上拉。其它数据无效。
- 第三个尖括号:驱动能力。default 表驱动能力是等级 1。
- 第四个尖括号:电平。0 为低电平,1 为高电平。
注意:如果有多个 gpio 脚需要控制,则定义 lcd_gpio_0,lcd_gpio_1 等。
- 配置 LCD 的控制 PIN。可以在屏驱动调用相应的接口进行拉高,拉低的控制,例如 LCD 的 RESET 脚等。
- 配置 LCD 的数据 PIN。重点关注 PIN 脚的复用功能数值,具体的 IO 对应关系可参考 user manual 手册进行配置
调试
系统起来之后可以输入disp相关调试命令,来协助调试。
选项 参数 解释 举例 空 空 打印出当前显示的信息 disp -c Screen_id,color 模式 显示 colorbar。共有 8 种模式,0 到 8 disp ‑c 0 8 -b Screen_id, 背光值 调整 lcd 背光,背光值范围时 0 到 255 disp ‑b 0 255 -d Screen_id, 文件路径 抓 DE 图层回写到文件 disp ‑d 0 /sdmmc/xx.bmp -s Screen_id,显示类型,显示分辨率 切换显示类型或分辨率 disp ‑s 0 1 4 打开LCD 显示 查看显示信息
输入disp命令,会有 Log 打印信息。以下信息是所有信息中最重要的。
disp screen 0: derate 297000000 hz, ref_fps:60 mgr0: 1280x800 fmt[rgb] cs[0x204] range[full] eotf[0x4] bits[8bits] err[0] force_sync[0] unblank direct_show[false] lcd output backlight( 50) fps:60.9 1280x 800 err:0 skip:31 irq:1942 vsync:0 vsync_skip:0 BUF enable ch[1] lyr[0] z[0] prem[N] a[globl 255] fmt[ 8] fb[1280, 800;1280, 800;1280, 800] crop[ 0, 0,1280, 800] frame[ 0, 0,1280, 800] addr[ 0, 0, 0] flags[0x 0] trd[0,0]
lcd output
表示当前显示接口是 LCD 输出。
1280x800
表示当前 LCD 的分辨率,与 sys_config.fex 中 lcd0 的设置一样。
ref_fps:60
是根据你在 sys_config.fex 的 lcd0 填的时序算出来的理论值。
fps:60.9
后面的数值是实时统计的,正常来说应该是在 60(期望的 fps) 附近,如果差太多则不正常,重新检查屏时序,和在屏驱动的初始化序列是否有被调用到。
irq:1942
这是 vsync 中断的次数,每加 1 都代表刷新了一帧,正常来说是一秒 60(期望的 fps)次,重复 cat sys,如果无变化,则异常。
BUF
开头的表示图层信息,一行 BUF 表示一个图层,如果一个 BUF 都没有出现,那么将是黑屏,不过和屏驱动本身关系就不大了,应该查看应用层 & 框架层。
err:0
这个表示缺数,如果数字很大且一直变化,屏幕会花甚至全黑,全红等。
skip:31
这个表示跳帧的数量,如果这个数值很大且一直变化,有可能卡顿,如果数字与 irq 后面的数字一样,说明每一帧都跳,会黑屏(有背光)
查看时钟信息
hal_ccmu
这个命令可以看哪个时钟是否使能,然后频率是多少。与显示相关的是 tcon,pll_video等。
查看接口自带 colorbar
显示是一整条链路,中间任何一个环节出错,最终的表现都是显示异常,图像显示异常几个可能原因:
- 图像本身异常。
- 图像经过 DE(Display Engine)后异常。
- 图像经过接口模块后异常。这是我们关注的点。
有一个简单的方法可以初步判断,接口模块(tcon 和 dsi 等)可以自己输出内置的一些 patten,比如说彩条,灰阶图,棋盘图等。当接口输出这些内置 patten 的时候,如果这时候显示就异常,这说明了:
- LCD 的驱动或者配置有问题
- LCD 屏由于外部环境导致显示异常
显示自带 patten 的方式:
disp ‑c 0 X
上面的操作是显示 colorbar,其中的 X 可以是 0 到 8
FAQ
屏显示异常
总结过往经验,绝大部分屏显异常都是由于上下电时序和 timing 不合理导致。
黑屏‑无背光
问题表现:完全黑屏,背光也没有
- 屏驱动添加失败。驱动没有加载屏驱动,导致背光电源相关函数没有运行到。这个你可以通过相关模块的测试命令定位下。
- pwm 配置和背光电路的问题,pwm 的信息可以查看 pwm 模块测试命令和背光相关参数,另外就是直接测量下硬件测量下相关管脚和电压,再检查屏是否初始化成功。
黑屏‑有背光
黑屏但是有背光,可能有多种原因导致,请依次按以下步骤检查
- 没送图层。如果应用没有送任何图层那么表现的现象就是黑屏,通过查看显示信息一小节可以确定有没有送图层。如果确定没有图层,可以通过查看接口自带 colorbar,确认屏能否正常显示。
- SoC 端的显示接口模块没有供电。SoC 端模块没有供电自然无法传输视频信号到屏上。
- 复位脚没有复位。如果有复位脚,请确保硬件连接正确,确保复位脚的复位操作有放到屏驱动中。
- sys_config.fex 中 lcd0 有严重错误。第一个是 lcd 的 timing 搞错了,请严格按照屏手册中的提示来写。参考屏时序参数说明。第二个就是,接口类型搞错,比如接的 DSI 屏,配置却写成LVDS 的。
- 屏的初始化命令不对。包括各个步骤先后顺序,延时等,这个时候请找屏厂确认初始化命令。
闪屏
分为几种:
- 屏的整体在闪:这个最大可能是背光电路的电压不稳定,检查电压
- 屏部分在闪,而且是概率性:sys_config.fex 中的时序填写不合理。
- 屏上由一个矩形区域在闪:屏极化导致,需要关机放一边再开机则不会。
条形波纹
有些 LCD 屏的像素格式是 18bit 色深(RGB666)或 16bit 色深(RGB565),建议打开 FRM 功能,通过 dither 的方式弥补色深,使显示达到 24bit 色深(RGB888)的效果。
设置 [lcd0] 的 lcd_frm 属性可以改善这种现象。
背光太亮或者太暗
重新配置背光参数
花屏
花屏的第一个原因是 fps 过高,超过屏的限制:
FPS 异常是一件非常严重的事情,关系到整个操作系统的稳定,如果 fps 过高会造成系统带宽增加,送显流程异常,fps 过高还会造成 LCD 屏花屏不稳定,容易造成 LCD 屏损坏,FPS 过低则造成用户体验过差。
- 通过查看查看显示信息一节,可以得知现在的实时统计的 fps
- 如果 fps 离正常值差很多,首先检查 sys_config.fex 中 [lcd0] 节点,所填信息必须满足下面公式:
lcd_dclk_freq * num_of_pixel_clk = lcd_ht * lcd_vt * fps / 1e9
其中,num_of_pixel_clk 通常为 1,表示发送一个像素所需要的时钟周期为 1 一个,低分辨率的MCU 和串行接口通常需要 2 到 3 个时钟周期才能发送完一个像素。
如果上面填写没有错,通过查看查看时钟信息可以确认下几个主要时钟的频率信息,把这些信息和 sys_config.fex 发给维护者进一步分析。
RGB 接口或者 I8080 接口显示抖动有花纹
- 改大时钟管脚的管脚驱动能力
- 修改时钟相位,也就是修改 lcd_hv_clk_phase。由于发送端和接收端时钟相位的不同导致接收端解错若干位。
LCD 屏出现极化和残影
何谓液晶极化现象:实际上就是液晶电介质极化。就是在外界电场作用下,电介质内部沿电场方向产生感应偶极矩,在电解质表明出现极化电荷的现象叫做电介质的极化。
通俗的讲就是在液晶面板施加一定电压后,会聚集大量电荷,当电压消失的时候,这些聚集的电荷也要释放,但由于介电效应,这些聚集的电荷不会立刻释放消失,这些不会马上消失的惰性电荷造成了液晶的 DC 残留从而形成了极化现象。
几种常见的液晶极化现象
- 液晶长期静止某个画面的时候,切换到灰阶画面的时候出现屏闪,屏闪一段时间后消失。这种现象属于残留电荷放电的过程。
- 液晶长期静止某个画面的时候,出现四周发黑中间发白的现象,业内称为黑白电视框异常。
- 非法关机的时候,重新上电会出现屏闪,屏闪一定时间后消失。与第一种原因相同。
- 残影现象:当液晶静止在一个画面比较久的情况下,切换其他画面出现的镜像残留。残影的本质来说是液晶 DC 残留电荷导致,某种意义来说也属于液晶极化现象
针对液晶屏出现极化和残影现象,有如下对策。
- 调整 vcom 电压大小。
VCOM 是液晶分子偏转的参考电压,要求要稳定,对液晶显示有直接影响,具体的屏不同的话也是不同的。电压的具体值是根据输入的数据以及 Vcom 电压大小来确定的,用来显示各种不同灰阶,也就是实现彩色显示 GAMMA。Gamma 电压是用来控制显示器的灰阶的,一般情况下分为G0~G14,不同的 Gamma 电压与 Vcom 电压之间的压差造成液晶旋转角度不同从而形成亮度的差异,Vcom 电压最好的状况是位于 G0 和 G14 的中间值,这样液晶屏的闪烁状况会最好。调节 vcom 电压的方式,如果屏管脚有 vcom 管脚,直接调整相关电路,如果屏 driver IC 提供寄存器接口,可以通过寄存器接口来调整大小。
- 严格按照屏规定的上下电时序来对屏进行开关屏。许多极化残影现象并非长时间显示静止显示某个画面导致的,而是由于关机或者关屏时没有严格按照下电时序导致的,比如该关的电没关,或者延时不够。
典型屏幕参数配置
1024x600 RGB666 屏幕
;-------------------------------------------------- ;Parallel RGB LCD ;-------------------------------------------------- [lcd0] lcd_used = 1 lcd_driver_name = "default_lcd" lcd_backlight = 150 lcd_if = 0 lcd_x = 1024 lcd_y = 600 lcd_width = 150 lcd_height = 94 lcd_rb_swap = 0 lcd_dclk_freq = 48 lcd_pwm_used = 1 lcd_pwm_ch = 7 lcd_pwm_freq = 500000 lcd_pwm_pol = 1 lcd_hbp = 160 lcd_ht = 1344 lcd_hspw = 20 lcd_vbp = 20 lcd_vt = 635 lcd_vspw = 3 lcd_lvds_if = 0 lcd_lvds_colordepth = 1 lcd_lvds_mode = 0 lcd_frm = 0 lcd_io_phase = 0x0000 lcd_gamma_en = 0 lcd_bright_curve_en = 0 lcd_cmap_en = 0 ;reset ;lcd_gpio_0 = port:GPIO_EXP15<1><0><3><1> ;cs ;lcd_gpio_1 = port:GPIO_EXP03<1><0><3><0> ;sdi ;lcd_gpio_2 = port:GPIO_EXP06<1><0><3><0> ;scl ;lcd_gpio_3 = port:GPIO_EXP07<1><0><3><0> ;LCD_D2-LCD_D7 lcd_gpio_4 = port:PA00<8><0><3><0> lcd_gpio_5 = port:PA01<8><0><3><0> lcd_gpio_6 = port:PA02<8><0><3><0> lcd_gpio_7 = port:PA03<8><0><3><0> lcd_gpio_8 = port:PA04<8><0><3><0> lcd_gpio_9 = port:PA05<8><0><3><0> ;LCD_D10-LCD_D15 lcd_gpio_10 = port:PA11<8><0><3><0> lcd_gpio_11 = port:PA10<8><0><3><0> lcd_gpio_12 = port:PA08<8><0><3><0> lcd_gpio_13 = port:PA07<8><0><3><0> lcd_gpio_14 = port:PA06<8><0><3><0> lcd_gpio_15 = port:PA09<8><0><3><0> ;LCD_D18-LCD_D23 lcd_gpio_16 = port:PA12<8><0><3><0> lcd_gpio_17 = port:PA13<8><0><3><0> lcd_gpio_18 = port:PA14<8><0><3><0> lcd_gpio_19 = port:PA15<8><0><3><0> lcd_gpio_20 = port:PB03<8><0><3><0> lcd_gpio_21 = port:PB02<8><0><3><0> ;LCD_VSYNC, LCD_HSYNC, LCD_DCLK, LCD_DE lcd_gpio_0 = port:PA18<8><0><3><0> lcd_gpio_1 = port:PA19<8><0><3><0> lcd_gpio_2 = port:PA20<8><0><3><0> lcd_gpio_3 = port:PA21<8><0><3><0>
320x480 ST7796 i8080 屏幕
;-------------------------------------------------- ;MCU LCD ;-------------------------------------------------- [lcd0] lcd_used = 1 lcd_driver_name = "cl40bc1019_cpu" lcd_backlight = 150 lcd_if = 1 lcd_x = 320 lcd_y = 480 lcd_width = 150 lcd_height = 94 lcd_rb_swap = 0 lcd_pwm_used = 1 lcd_pwm_ch = 7 lcd_pwm_freq = 5000 lcd_pwm_pol = 1 lcd_cpu_mode = 0 lcd_cpu_te = 0 lcd_cpu_if = 12 lcd_dclk_freq = 32 lcd_hbp = 75 lcd_ht = 1060 lcd_hspw = 40 lcd_vbp = 6 lcd_vt = 490 lcd_vspw = 2 lcd_lvds_if = 0 lcd_lvds_colordepth = 1 lcd_lvds_mode = 0 lcd_frm = 0 lcd_io_phase = 0x0000 lcd_gamma_en = 0 lcd_bright_curve_en = 0 lcd_cmap_en = 0 deu_mode = 0 lcdgamma4iep = 22 smart_color = 90 ;reset pin lcd_gpio_0 = port:PB03<1><0><3><0> ;CS lcd_gpio_1 = port:PA12<1><0><3><0> ;LCD_D3-LCD_D7 lcd_gpio_2 = port:PA01<8><0><3><0> lcd_gpio_3 = port:PA02<8><0><3><0> lcd_gpio_4 = port:PA03<8><0><3><0> lcd_gpio_5 = port:PA04<8><0><3><0> lcd_gpio_6 = port:PA05<8><0><3><0> ;LCD_D10-LCD_D12 lcd_gpio_7 = port:PA11<8><0><3><0> lcd_gpio_8 = port:PA10<8><0><3><0> lcd_gpio_9 = port:PA08<8><0><3><0> ;WR lcd_gpio_10 = port:PA06<7><0><3><0> ;RD lcd_gpio_11 = port:PA07<7><0><3><0> ;RS lcd_gpio_12 = port:PA09<7><0><3><0>
480x480 RGB 86 面板屏
;-------------------------------------------------- ;Parallel RGB LCD ;-------------------------------------------------- [lcd0] lcd_used = 1 lcd_driver_name = "p0400060a" lcd_backlight = 150 lcd_if = 0 lcd_x = 480 lcd_y = 480 lcd_width = 94 lcd_height = 94 lcd_rb_swap = 0 lcd_dclk_freq = 21 lcd_hv_clk_phase = 1 lcd_pwm_used = 1 lcd_pwm_ch = 6 lcd_pwm_freq = 5000 lcd_pwm_pol = 1 lcd_hbp = 80 lcd_ht = 648 lcd_hspw = 8 lcd_vbp = 10 lcd_vt = 522 lcd_vspw = 2 lcd_lvds_if = 0 lcd_lvds_colordepth = 1 lcd_lvds_mode = 0 lcd_frm = 1 lcd_io_phase = 0x0000 lcd_gamma_en = 0 lcd_bright_curve_en = 0 lcd_cmap_en = 0 deu_mode = 0 lcdgamma4iep = 22 smart_color = 90 ;reset lcd_gpio_0 = port:PB01<1><0><3><1> ;cs lcd_gpio_1 = port:PA27<1><0><3><0> ;sdi lcd_gpio_2 = port:PA28<1><0><3><0> ;scl lcd_gpio_3 = port:PB00<1><0><3><0> ;LCD_D2-LCD_D7 lcd_gpio_4 = port:PA00<8><0><3><0> lcd_gpio_5 = port:PA01<8><0><3><0> lcd_gpio_6 = port:PA02<8><0><3><0> lcd_gpio_7 = port:PA03<8><0><3><0> lcd_gpio_8 = port:PA04<8><0><3><0> lcd_gpio_9 = port:PA05<8><0><3><0> ;LCD_D10-LCD_D15 lcd_gpio_10 = port:PA11<8><0><3><0> lcd_gpio_11 = port:PA10<8><0><3><0> lcd_gpio_12 = port:PA08<8><0><3><0> lcd_gpio_13 = port:PA07<8><0><3><0> lcd_gpio_14 = port:PA06<8><0><3><0> lcd_gpio_15 = port:PA09<8><0><3><0> ;LCD_D18-LCD_D23 lcd_gpio_16 = port:PA12<8><0><3><0> lcd_gpio_17 = port:PA13<8><0><3><0> lcd_gpio_18 = port:PA14<8><0><3><0> lcd_gpio_19 = port:PA15<8><0><3><0> lcd_gpio_20 = port:PB03<8><0><3><0> lcd_gpio_21 = port:PB02<8><0><3><0> ;LCD_VSYNC, LCD_HSYNC, LCD_DCLK, LCD_DE lcd_gpio_22 = port:PA18<8><0><3><0> lcd_gpio_23 = port:PA19<8><0><3><0> lcd_gpio_24 = port:PA20<8><0><3><0> lcd_gpio_25 = port:PA21<8><0><3><0>
320x320 i8080 86 面板屏
[lcd0] lcd_used = 1 lcd_driver_name = "d392t9390v0_cpu" lcd_backlight = 200 lcd_if = 1 lcd_x = 320 lcd_y = 320 lcd_width = 78 lcd_height = 78 lcd_rb_swap = 1 lcd_pwm_used = 1 lcd_pwm_ch = 7 lcd_pwm_freq = 50000 lcd_pwm_pol = 1 lcd_cpu_mode = 0 lcd_cpu_te = 0 lcd_cpu_if = 12 lcd_dclk_freq = 32 lcd_hbp = 75 lcd_ht = 1060 lcd_hspw = 40 lcd_vbp = 6 lcd_vt = 490 lcd_vspw = 2 lcd_lvds_if = 0 lcd_lvds_colordepth = 1 lcd_lvds_mode = 0 lcd_frm = 0 lcd_io_phase = 0x0000 lcd_gamma_en = 0 lcd_bright_curve_en = 0 lcd_cmap_en = 0 deu_mode = 0 lcdgamma4iep = 22 smart_color = 90 ;reset pin lcd_gpio_0 = port:PA12<1><0><3><0> ;LCD_D3-LCD_D7 lcd_gpio_2 = port:PA01<8><0><3><0> lcd_gpio_3 = port:PA02<8><0><3><0> lcd_gpio_4 = port:PA03<8><0><3><0> lcd_gpio_5 = port:PA04<8><0><3><0> lcd_gpio_6 = port:PA05<8><0><3><0> ;LCD_D10-LCD_D12 lcd_gpio_7 = port:PA11<8><0><3><0> lcd_gpio_8 = port:PA10<8><0><3><0> lcd_gpio_9 = port:PA08<8><0><3><0> ;WR lcd_gpio_10 = port:PA06<7><0><3><0> ;RD lcd_gpio_11 = port:PA07<7><0><3><0> ;RS lcd_gpio_12 = port:PA09<7><0><3><0>
800x480 标准 40Pin RGB屏
[lcd0] lcd_used = 1 lcd_driver_name = "default_lcd" lcd_backlight = 150 lcd_if = 0 lcd_x = 800 lcd_y = 480 lcd_width = 150 lcd_height = 94 lcd_rb_swap = 0 lcd_dclk_freq = 33 lcd_pwm_used = 1 lcd_pwm_ch = 6 lcd_pwm_freq = 5000 lcd_pwm_pol = 1 lcd_hbp = 46 lcd_ht = 1055 lcd_hspw = 0 lcd_vbp = 23 lcd_vt = 525 lcd_vspw = 0 lcd_lvds_if = 0 lcd_lvds_colordepth = 1 lcd_lvds_mode = 0 lcd_frm = 0 lcd_io_phase = 0x0000 lcd_gamma_en = 0 lcd_bright_curve_en = 0 lcd_cmap_en = 0 deu_mode = 0 lcdgamma4iep = 22 smart_color = 90 ;LCD_D2-LCD_D7 lcd_gpio_0 = port:PA00<8><0><3><0> lcd_gpio_1 = port:PA01<8><0><3><0> lcd_gpio_2 = port:PA02<8><0><3><0> lcd_gpio_3 = port:PA03<8><0><3><0> lcd_gpio_4 = port:PA04<8><0><3><0> lcd_gpio_5 = port:PA05<8><0><3><0> ;LCD_D10-LCD_D15 lcd_gpio_6 = port:PA11<8><0><3><0> lcd_gpio_7 = port:PA10<8><0><3><0> lcd_gpio_8 = port:PA08<8><0><3><0> lcd_gpio_9 = port:PA07<8><0><3><0> lcd_gpio_10 = port:PA06<8><0><3><0> lcd_gpio_11 = port:PA09<8><0><3><0> ;LCD_D18-LCD_D23 lcd_gpio_12 = port:PA12<8><0><3><0> lcd_gpio_13 = port:PA13<8><0><3><0> lcd_gpio_14 = port:PA14<8><0><3><0> lcd_gpio_15 = port:PA15<8><0><3><0> lcd_gpio_16 = port:PB03<8><0><3><0> lcd_gpio_17 = port:PB02<8><0><3><0> ;LCD_VSYNC, LCD_HSYNC, LCD_DCLK, LCD_DE lcd_gpio_18 = port:PA18<8><0><3><0> lcd_gpio_19 = port:PA19<8><0><3><0> lcd_gpio_20 = port:PA20<8><0><3><0> lcd_gpio_21 = port:PA21<8><0><3><0>
-
全志T527芯片详解【二】:高清图像编解码
硬件模块加持
T527集成了多个图形显示和编解码相关的硬件模块,为高清图像显示、高清视频播放和多路高清摄像头输入提供了强大的硬件基础:
- ARM Mail-G57 GPU
- 自研显示引擎(Display Engine)
- 去隔行处理单元(De-interIace)
- 2D图像加速单元(Graphic2D)
- 视频编解码引擎(Video Engine)
- 自研视觉处理器(ISP)
- 视觉缩放处理单元(VIPP)
- ......
在硬件加速模块加持下,T527可以轻松实现4K视频的录制和播放,并支持H.264\H.265\VP9等多种主流的编解码格式,同时支持多路编解码的场景。
Video decoder
- H.265 MP decoder up to 4K@60fps
- H.264 BL/MP/HP decoder up to 4K@30fps
- VP9 decoder up to 4K@60fps
- Multi-format 1080p@60fps video playback,including VP8,MPEG1/2SP/MP,MPEG4,SP/ASP,AVS+/AVS JIZHUN
Video encoder
- H.264 encoder up to 4K@25fps
- MJPEG encoder up to 4K@15fps
- JPEG encoder up to 8K x 8K resolution
丰富的图像输入输出接口
T527上集成了大量图像输入、输出接口,通过与编解码模块的配合,可以实现4K+1080P的双屏幕异显和实现多达6路摄像头图像的交织输入,为如:收银机、智能座舱、360°摄像头等需要多屏幕交互或多摄像头输出的场景提供了高效的解决方案。
屏幕接口:MIPI DSI 、LVDS、HDMI、eDP、RGB.......
- HDMI2.0b up to 4K@60fps
- 4+4-lane MIPI-DSI output,supporting up to 2.5K@60fps and 4K@45fps
- 2xLVDS interface with dual link, up to I080p@60fps
- 2xRGB interfaces with DE/SYNC mode, up to I080p@60fps
- eDP1.3 up to 2.5K@60fps and 4K@30fps
摄像头接口:MIPI CSI、Parallel CSI
Parallel CSl interface- 8/10/12/16-bit width
- Supports BT.656 up to 4720P@30fps and BT.1120 up to 41080P@30fps
MIPI CSI interface
- 24 lane/42 lane/4+2*2 lane MIPICSI,flexible combination, up to 2.0 Gbit/s per lane in HS transmission,compliant with MIPI-CSI2V1.1 and MIPI DPHYV1.1
- Maximumvideocaptureresolution of 8M@30fps
自研高清画质算法矩阵
全志在显示引擎上硬件固化了自研的高清画质算法矩阵AWonder1.0,提供了多维度的图像处理算法,包括:
- 智能降噪
- 明亮动态
- 清晰细节
- 绚丽色彩
- 精锐去隔行
- 臻彩HDR
- AI-PQ
- ......
帮助图像减少噪点、提升对比度、减少锯齿、丰富色彩等,并通过AI场景和人像识别,令图像更清晰、色彩更自然。
自研安防级专业图像信号处理器
T527集成了全志自研的安防级专业图像信号处理器 (ISP),该ISP在前序版本上大幅升级多个模块,能更好地应对如行车、户外等运动和光照复杂的场景。
- 时空域多级降噪
- 宽动态合成
- 局部色调映射算法
- ......
同时全志自研推出AI-ISP智能图像处理算法「全志智影」,通过ISP和NPU深度配合,提升了图像的感光度,帮助实现黑光全彩等功能,在车载场景下可以实现全天候无盲区的行车安全监控。
相比传统ISP算法,「全志智影」AI-ISP能够更好地提升暗部清晰度及颜色表现,改善画面整体亮度及色度噪点表现,并大程度地抑制运动拖影的出现。
自研图像和视觉参数调节工具
T527配套了全志自研量产工具
- 屏幕画质调试工具:TigerLCD
- 视觉图像调试工具:TigerISP
通过可视化的软件工具,帮助工程师更便捷地对图像参数进行调试,减少工程师工作量,提升开发效率,让图像色彩显示更精准。
-
技术帖 | 飞凌嵌入式T113-i开发板的休眠及唤醒操作
飞凌嵌入式OK113i-S开发板支持两种休眠方式:freeze和mem。
这两种方式可以通过/sys/power/state文件节点进行操作,用户可以通过在该文件节点写入freeze或mem来触发相应的休眠状态。
在进行休眠之前,系统会配置唤醒源。一旦系统进入休眠状态,可以通过这些唤醒源(如按键、RTC等)在需要时唤醒系统。这种设计允许用户根据需要选择何时以及通过何种方式快速唤醒系统,实现了功耗最小化和快速恢复的平衡。这一机制使得系统在休眠状态下能够极大地减少功耗,同时保留了用户在唤醒后迅速使用系统的便利性。
本篇内容小编会为大家介绍如何让飞凌嵌入式OK113i-S开发板进入休眠模式,以及如何通过RTC时钟实现定时唤醒。
关于两种休眠模式
- freeze
冻结I/O设备,将它们置于低功耗状态,使处理器进入空闲状态,唤醒最快,耗电比其它方式高。实测OK113i-S开发板在只接串口线的情况下5V供电,电流约为0.112A。
- mem
挂起到内存,计算机将目前的运行状态等数据存放在内存,关闭硬盘、外设等设备,进入等待状态。此时内存仍然需要电力维持其数据,但整机耗电很少。恢复时计算机从内存读出数据,回到挂起前的状态,恢复速度较快。实测OK113i-S开发板在只接串口线情况下5V供电,电流约为0.076A。
1、cat /sys/power/state可以看到OK113i-S开发板支持的模式有哪些:
2、echo freeze > /sys/power/state 进入freeze模式:
3、echo mem > /sys/power/state 进入mem模式:
通过RTC定时唤醒
注意:此处需要使用内部RTC,外部RTC不支持唤醒功能,后面我们还会提及。
进入开发板的内核配置:
root@ubuntu: /home/forlinx/work/linux/OK113i-linux-sdk# ./build.sh menuconfig
根据下图框选进行功能选择:
配置完成后保存,然后修改设备树文件,打开内部RTC功能。
保存后进行编译:
编译成功后打包成镜像,烧写完成后,我们在串口终端进行测试。
进入串口终端进行测试:
echo “+15”> /sys/class/rtc/rtc0/wakealarm
此处为15秒定时,可自由设置时间,命令执行后就会生效,RTC会单独计时,如果是15秒后才进入休眠,不会触发唤醒。(注意此处需要使用内部RTC,外部RTC不支持唤醒功能)
echo mem > /sys/power/state
(这里两条指令输入时要紧凑,两条指令间,间隔太长就无效了)
(这里需要注意,我们在未打开内部RTC时,我们的外部RTC默认节点是rtc0,修改后外部rtc设备节点会变更成rtc1。)
到这里,我们就完成了在飞凌嵌入式OK113i-S开发板上实现休眠以及通过RTC定时唤醒的全部操作了,当然,不同的主控平台板卡的具体操作会有差异,但是整体思路是一样的,具体可以根据相对应的平台查看相关资料来确定具体步骤,希望本文提供的方法能够对屏幕前的工程师朋友们的项目开发有所帮助。
-
我DIY了一个可以声控刷抖音的神器,这下连手指都解放了
用一块板两个模块三根线就可以爆改手机,实现语音控制刷抖音,上下滑动视频、点赞那都是基础操作,关键是还可以一键睡觉,懒人福音!
所谓一块板两个模块三根线,一块开发板就是负责接收和控制的香橙派H616开发板,两个模块分别是负责发送的语音识别模块SU-03T和一个mic,所以除了电源外,只需要将语音识别模块的TX(B7)接到香橙派的RX就可以,算上电源共三根线:
项目需求只是简单的通过语音指令来控制安卓手机刷抖音,以实现视频切换和点赞等功能:
1、开机播报“你好,我是你的刷抖音助手”
2、当说出“你好抖音助手"可以唤醒模块,模块回复“抖音助手在”
3、当超过10s没有指令或说出“退下”时,模块会进入休眠模式,并回复“有需要再叫我”
4、当说出“下一个视频”或“这个不好看”时,模块回复“切换至下一个视频”,并划到下一个视频
5、当说出“上一个视频”或“刚刚那个挺好看”时,模块回复“切换至上一个视频”,并划回上一个视频
6、当说出“点个赞”或“这个视频不错”时,模块回复“已为您点赞”,并点赞当前视频
7、当说出“不想看了”时,模块回复“已为您关闭屏幕”,并关闭手机屏幕,一键入睡!语音识别模块配置
项目中所需要的是一个非特定语音识别,也就是不识别特定人类的声音的模块。所以SU-03T这个模块的操作相较于其他语音识别模块更简单,不需要编程或二次开发,只需要通过厂家给的网站配置后即可使用。
创建产品
设置PIN脚为串口模式
对于语音识别模块,串口的RX和TX分别对应B6和B7,并设置相应的波特率
设置唤醒词
设置指令语句
设置控制详情
参数的设置就是行为的名字,大写字母 ,16进制ASCII码,已空格分开
- next -> 4E 45 58 54
- pre -> 50 52 45
- zan -> 5A 41 4E
- guan -> 47 55 41 4E
测试
在完成以上步骤,并下载SDK并烧写进入语音识别模块后,可以打开串口助手来测试一下,分别说出对应的指令,看看语音识别模块是否会向串口发送对应的字符。
分别说出了四条指令,可见串口输出正确!
编写香橙派代码框架
语音识别模块设置完成后,就可以将语音识别模块接到香橙派并进行Linux部分的代码编写,连接方式如下图所示:
新创建一个“douyin”文件夹,将语音刷抖音项目的代码放在这里面:
然后将一些代码拷贝进来方便修改:
重新编译一下,并说出4条控制语句:可见,语音识别模块成功识别了指令,香橙派成功的接收了语音识别模块通过串口打印的字符!
现在,就需要修改serial_douyin.c中接收的代码,添加数据处理的部分,先简单写一个数据处理的框架
void *write_serial(void *arg) { char readbuf[32] = {'\0'}; while(1){ while(serialDataAvail (*((int *)arg))){ serialGetstring (*((int *)arg),readbuf) ; if(strcmp(readbuf,"NEXT") == 0 ){ printf("收到下一条视频指令\n"); }else if(strcmp(readbuf,"PRE") == 0){ printf("收到上一条视频指令\n"); }else if(strcmp(readbuf,"ZAN") == 0){ printf("收到点赞指令\n"); }else if(strcmp(readbuf,"GUAN") == 0){ printf("收到关闭指令\n"); }else{ printf("未知指令\n"); } memset(readbuf,'\0',32); } } pthread_exit(NULL); }
重新编译一下,并说出4条控制语句:
可见,函数框架正确,接下来只需要将printf替换成真正的操作手机的代码就可以了。
将手机接入香橙派
将我破旧的小米5C再次拿出哈哈哈,然后通过 TYPE-C -- USB 连接到香橙派:
香橙派输入dmesg指令查看手机接入情况,可见已经成功识别
由于安卓手机的底层也是用Linux系统来操作的,所以可以通过香橙派来直接进入控制手机shell的界面,但需要先安装adb工具,adb是做安卓开发中常用的工具
sudo apt-get insall adb
安装完之后,执行”adb devices“指令,发现好像权限不太对,因此需要在安卓手机上设置权限
报错的本质原因是香橙派系统还不支持USB设备的热拔插和UDEV的机制。
什么是UDEV
udev是一个设备管理工具,udev以守护进程的形式运行,通过侦听内核发出来的uevent来管理/dev目录下的设备文件。
udev在用户空间运行,而不在内核空间 运行。它能够根据系统中的硬 件设备的状态动态更新设备文件,包括设备文件的创建,删除等。设备文件通常放在/dev目录下。使用udev后,在/dev目录下就只包含系统中真正存在的设备。
解决办法:在 /etc/udev/rules.d 文件夹下创建规则文件:
cd /etc/udev/rules.d/ sudo vim 51-android.rules
然后在文件中添加内容:
cd /etc/udev/rules.d/ sudo vim 51-android.rules
然后重新拔插手机,再次执行”adb devices“指令
没有任何报错了,此时”adb shell“指令会显示连接成功,此时如果“ls”一下,可以看到很多文件没有权限,因为没有root,不过不重要,到这里手机端的配置就好了
现在可以成功的连入手机内部的系统,关键就在于对于滑动或点击屏幕的指令模拟了,
- adb shell input swipe <起始x坐标> <起始y坐标> <结束x坐标> <结束y坐标> <滑动持续时间ms>
- adb shell input keyevent <按键事件的常量>
adb shell input swipe 540 1300 540 500 500 //下滑 adb shell input swipe 540 500 540 1300 500 //上滑 adb shell "seq 3 | while read i;do input tap 350 1050 & input tap 350 1050 &sleep 0.01;done;" //点赞 adb shell input keyevent 26 //锁屏
最终实现效果
现在有了基本的代码模型,和控制手机的具体指令,接下来的工作就是在数据处理的部分,执行adb指令了,显然,使用system函数就可以:
int main () { int fd ; int ret; pthread_t read_thread; pthread_t write_thread; if ((fd = myserialOpen ("/dev/ttyS5", 115200)) < 0) //打开驱动文件,配置波特率 { fprintf (stderr, "Unable to open serial device: %s\n", strerror (errno)) ; return 1 ; } /* if (wiringPiSetup () == -1) { fprintf (stdout, "Unable to start wiringPi: %s\n", strerror (errno)) ; return 1 ; }*/ ret = pthread_create(&read_thread,NULL,read_serial,(void *)&fd); if(ret != 0){ printf("read_serial create error\n"); return 1; } ret = pthread_create(&write_thread,NULL,write_serial,(void *)&fd); if(ret != 0){ printf("write_serial create error\n"); return 1; } pthread_join(read_thread,NULL); pthread_join(write_thread,NULL); return 0 ; }
和项目需求一致,可见,我的手并没有碰到手机屏幕,只是说出了对应的指令,手机就会有所反应:
并且在香橙派终端也可以看到指令历史:
-
【R128】应用开发案例——按键输入
基于R128-S2设计的全套开发板已上线淘宝百问网韦东山老师个人店进行售卖,包含黑色的DshanMCU-R128s2-R16N16模组和全套的DshanMCU-R128s2-DEVKIT。
- DshanMCU-R128s2-R16N16模组:39.9元
- DshanMCU-R128s2-DEVKIT开发板:59.9元
R128开发板购买链接:https://item.taobao.com/item.htm?spm=a21n57.1.0.0.46b0523cMfarLo&id=736154682975&ns=1&abbucket=5#detail
按键输入
本文案例代码 下载地址 按键输入案例代码 https://www.aw-ol.com/downloads?cat=24 首先我们搭建电路,如下:
引脚 按键 PA25 按键1脚 GND 按键3脚 载入方案
我们使用的开发板是 R128-Devkit,需要开发 C906 核心的应用程序,所以载入方案选择
r128s2_module_c906
$ source envsetup.sh $ lunch_rtos 1
勾选 GPIO 驱动
mrtos_menuconfig
找到下列驱动Drivers Options ---> soc related device drivers ---> GPIO devices ---> [*] enable GPIO driver [*] enbale GPIO hal APIs Test command
编写程序
打开你喜欢的编辑器,修改文件:
lichee/rtos/projects/r128s2/module_c906/src/main.c
引入头文件
#include <hal_gpio.h>
使用 GPIO 配置引脚
配置 GPIO 的上下拉状态
使用
hal_gpio_set_pull(gpio_pin_t pin, gpio_pull_status_t pull);
来设置。这里我们设置PA25
引脚为默认上拉状态。hal_gpio_set_pull(GPIOA(25), GPIO_PULL_UP);
配置 GPIO 输入输出模式
使用
hal_gpio_set_direction(gpio_pin_t pin, gpio_direction_t direction);
来设置 GPIO 的输入输出模式,这里配置为输入模式。hal_gpio_set_direction(GPIOA(25), GPIO_DIRECTION_INPUT);
配置 GPIO 的 MUX 功能
GPIO 通常有多种功能,需要配置 MUX 选择需要的功能,使用
hal_gpio_pinmux_set_function(gpio_pin_t pin, gpio_muxsel_t function_index);
来设置 GPIO 的复用功能,这里配置为GPIO 输入模式(GPIO_MUXSEL_IN
)hal_gpio_pinmux_set_function(GPIOA(25), GPIO_MUXSEL_IN);
获取 GPIO 的电平
使用
int hal_gpio_get_data(gpio_pin_t pin, gpio_data_t *data);
来获取 GPIO 的电平,这里获取 A25 的电平状态。gpio_data_t gpio_data; hal_gpio_get_data(GPIOA(25), &gpio_data);
完整的配置 GPIO
gpio_data_t gpio_data; hal_gpio_set_pull(GPIOA(25), GPIO_PULL_UP); hal_gpio_set_direction(GPIOA(25), GPIO_DIRECTION_INPUT); hal_gpio_pinmux_set_function(GPIOA(25), GPIO_MUXSEL_IN); while(1){ hal_gpio_get_data(GPIOA(25), &gpio_data); if(gpio_data == GPIO_DATA_LOW){ printf("Key Pressed!\n"); } }
结果
按下按键,串口会输出
Key Pressed!
-
全志T527芯片详解【一】:计算性能
高效架构 性能稳健
内置8*ARM Cortex-A55,8核主频可运行至1.8GHz,提供更稳健强劲的处理能力
- Octa-core ARM Cortex-A55 in a DynamlQ big.LITTLE configuration, up to 1.8 GHz
- 32KB L1 I-cache and 32KB L1 D-cache per A55 core
- Optional 64KB L2 cache per“LITTLE”core
- Optional 128KB L2 cache per“big”core
图形显示 清晰流畅
集成ARM Mail-G57 GPU图形显示能力更上一层楼
- ARM G57 MC1 GPU
- Supports OpenGL ES 3.2/2.0/1.1, Vulkan 1.1/1.2/1.3, and OpenCL2.2
- Anti-aliasing algorithm
- High memory bandwidth and low power consumption in 3D graphics processing
边缘计算 AI赋能
集成2Tops NPU为端侧语音、自然语言处理、图像处理及画质增强等AI应用提供性能支持
- 2 TOPS NPU
- Embedded 512KB internal buffer
- Supports deep learning frameworks:TensorFlow, Pytorch, Caffe, Onnx NN, TFLite.......
声音算法 浑然天成
内置HiFi4 DSP,频率可达600MHz,广泛应用于图像、音频及数字信号处理的专用领域为 影音娱乐、工业生产提供专属算力
- HiFi4 Audio DSP
- 32KB I-cache +32KB D-cache
独立MCU 控制更实时
内置RISC-V架构MCU,主频可达200MHz独立运行RTOS系统,为工业及机器人系统上的实时处理、高速响应及工业级的稳定运行提供重要保障
- RISC-V CPU, up to 200 MHz
- 16 KB I-cache and 16 KB D-cache·RV32IMAFC instructions
更多全志T527芯片详解系列专题内容即将发布
联系我们
微信公众号: Allwinnertech
官方邮箱:service@allwinnertech.com -
【R128】应用开发案例——SPI驱动ST7789V1.3寸LCD
基于R128-S2设计的全套开发板已上线淘宝百问网韦东山老师个人店进行售卖,包含黑色的DshanMCU-R128s2-R16N16模组和全套的DshanMCU-R128s2-DEVKIT。
- DshanMCU-R128s2-R16N16模组:39.9元
- DshanMCU-R128s2-DEVKIT开发板:59.9元
R128开发板购买链接:https://item.taobao.com/item.htm?spm=a21n57.1.0.0.46b0523cMfarLo&id=736154682975&ns=1&abbucket=5#detail
SPI驱动ST7789V1.3寸LCD
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
,使用的是 SPI 进行驱动。引脚配置如下:
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
$ source envsetup.sh $ lunch_rtos 1
设置 SPI 驱动
屏幕使用的是SPI驱动,所以需要勾选SPI驱动,运行
mrtos_menuconfig
进入配置页面。前往下列地址找到SPI Devices
Drivers Options ---> soc related device drivers ---> SPI Devices ---> -*- enable spi driver
配置 SPI 引脚
打开你喜欢的编辑器,修改文件:
board/r128s2/module/configs/sys_config.fex
,在这里我们不需要用到 SPI HOLD与SPI WP引脚,注释掉即可。;---------------------------------------------------------------------------------- ;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); // 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.h
将nv3029s
改成st7789v
#ifndef _ST7789V_H #define _ST7789V_H #include "panels.h" struct __lcd_panel st7789v_panel; #endif /*End of file*/
编辑
st7789v.c
将nv3029s
改成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
函数做如下修改
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); // 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
的配置config LCD_SUPPORT_ST7789V bool "LCD support st7789v panel" default n ---help--- If you want to support st7789v panel for display driver, select it.
然后编辑
panels.c
在panel_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 = 32 lcd_height = 32 ; 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 为 SPI 模式,双缓冲 lcd_if = 0 fb_buffer_num = 2 ; SPI 模式下以下配置无效 lcd_pixel_fmt = 11 lcd_dbi_fmt = 2 lcd_dbi_clk_mode = 1 lcd_dbi_te = 1 lcd_dbi_if = 4 lcd_rgb_order = 0 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 lcd_spi_dc_pin = port:PA19<1><0><3><0> ;RESET Pin lcd_gpio_0 = port:PA20<1><0><2><0>
编译打包
运行命令
mp
编译打包,可以看到编译了st7789v.o
测试
烧录启动之后,屏幕背光启动,但是屏幕全黑。
输入
test_spilcd
,屏幕显示黄色。输入
lv_examples 1
可以显示lvgl
界面常见问题
LVGL 出现 DMA Over Size
这是由于 LVGL 配置的
LV_COLOR_DEPTH
为 32,但是 SPI 屏配置为16位。请修改lv_conf.h
出现部分花屏
- 检查
address
函数是否正确 - 检查
sys_config.fex
屏幕配置分辨率是否正确
livpo 发布的最新帖子
-
回复: 搭建开发环境,出现问了了,总是报fatal: cannot obtain manifest https://sdk.aw-ol.com/git_repo/V853Tina_Open/manifest.git
@aaa0557li 在 搭建开发环境,出现问了了,总是报fatal: cannot obtain manifest https://sdk.aw-ol.com/git_repo/V853Tina_Open/manifest.git 中说:
git config --global user.email "you@example.com"
git config --global user.name "Your Name" -
回复: TinaLinux 无法创建 swap 分区吗?
可以的,
【FAQ】全志V853芯片 swap功能简介与tina上swap分区使用方法
https://bbs.aw-ol.com/topic/1626/share/1