@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
屏幕配置分辨率是否正确
-
百问网Avaota A1 T527开发板
全志科技T527采用多核异构设计,集成了CPU、GPU、NPU、DSP、MCU等各种计算单元,为各种复杂场景的多任务处理、专用数据处理以及算法应用提供了高效且灵活的解决方案,为生成式AI、人工智能算法的场景化落地提供了坚实的硬件基础。
为了方便开发者进行产品预研和项目开发,百问网推出了搭载全志T527芯片的AvaotaA1开发板。AvaotaA1是一款硬件+软件全开源的卡片电脑,提供全面开放的软硬件资料,小巧的板型搭载了8核处理器并引出了丰富的外部资源,还支持Avaota OS、Android13、Armbian等多种操作系统。
硬件资源
AvaotaA1开发板采用98mm x 73mm的接口丰富易用SBC板型设计,可以很好的兼容目前市面上的绝大多数主流开发板,在足够的板型尺寸下,开发板板载了500+元器件、14个外设接口以及40个拓展GPIO口。
- CPU: 八核 Cortex-A55,最高可达 1.8GHz
- GPU: Mali G57,支持 OpenGL ES 3.2/2.0/1.1,Vulkan 1.1/1.2/1.3,OpenCL 2.2
- RCPU: XuanTie E906 RISC-V @ 200MHz
- DSP: HIFI4 DSP @ 600MHz
- NPU: 可选最高可达 2TOPS 的计算能力
- 内存芯片: 支持 LPDDR4,可选 1/2/4GB
- 存储芯片: 支持 eMMC 5.1,可选 16/32/64/128GB
- 相机: MIPI CSI 接口,支持 1300 万像素,4 路 MIPI CSI
- 网络通信接口: 2 x 千兆以太网,WIFI6 2.4G + 5G,BT5.4
- USB 接口: 包括 USB OTG,USB3.0 OTG,USB2.0 HOST
- 音频接口: 3.5mm 音频接口,具有录音功能
- 显示接口: 包括 RGB 接口,MIPI DSI 接口,DP 接口,HDMI2.0 接口,LVDS 接口,MIPI DBI 接口
- 其他接口模块: 包括调试串口,TF 卡槽,CAN 接口,RGB LED,红外(IR),UART,IIC,I2S,SPI,PWM
在显示接口方面,AvaotaA1板载了一块240x135@60fps的1.14寸IPS屏,可用于显示LVGL UI、显示输出、显示系统性能等基础功能实现。
结合板载的HDMI、DP、MIPI/RGB/LVDS显示接口,AvaotaA1能够轻松实现4K60以及4K+1080P双屏异显的功能场景,结合NPU以及全志自研的新一代画质引擎,可支持AI超分等画质增强算法,提供更佳的显示体验。
软件系统支持
AvaotaA1支持专为Avaota SBC系列所打造的Avaota OS,Avaota OS是基于Ubuntu的特殊定制版,借助于Avaota SBC提供的性能和功能,Avaota OS在Ubuntu的基础上进行了深度优化,为用户提供了一体化的操作系统体验。
Avaota OS不仅继承了Ubuntu的强大功能和稳定性,还增加了许多独特的特色功能。这些功能包括针对Avaota硬件的优化,如支持特定的传感器和接口,以及预先配置的软件包,以满足各种应用场景的需求。并通过对系统内核、资源管理和功耗控制的调整,以提供优异的性能和稳定性。
除了百问网支持的专属Avaota OS,AvaotaA1也支持Android13、Buildroot、Armbian、Openwrt等多种适用于不同应用领域的操作系统,并附上了详尽的移植教程。
开发板购买&资料获取
目前R128 Devkit全套开发板已上线淘宝百问网韦东山老师个人店进行售卖,包含2+16和4+32两种裸板配置的绿色AvaotaA1 SBC,并可自行选购Type-C USB调试器:
- 2GB+16GB裸板:249元
- 4GB+32GB裸板:349元
- Type-C USB调试器:49.9元
另外关于AvaotaA1开发板的硬件设计、数据手册、软件系统源码等所有资料都在Avaota官方网站上开源,并配置了详尽的开发文档供开发者学习,感兴趣的小伙伴可以复制以下链接或者点击【阅读原文】获取更多详细资料。
AvaotaA1开发板文档:
https://docs.avaota.fun/avaota-sbc/ -
PhoenixSuit烧录工具四种烧录模式说明
如图所示,分四种烧录模式分别是:单或多分区下载、保留数据升级、分区擦除升级和全盘擦除升级。
下面对四种烧录模式进行说明:
- 单或多分区下载:用户加载固件之后,会从固件信息读取到MBR表的分区信息,然后将每个分区名字显示在UI上提供可选择单或多个分区烧录,若全部不选择,则默认下载物理分区的boot0和boot1。
- 保留数据升级:此模式仅擦除sys_partition配置的分区,但不包含udisk或user_data分区的数据。
- 分区擦除升级:此模式仅擦除sys_partition配置的分区,与“保留数据升级”相反,擦除包含udisk或user_data。
- 全盘擦除升级:顾名思义,就是将flash进行初始化,所有的数据将会被擦除
-
【R128】外设模块配置——USB外设功能配置
基于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
USB 外设功能配置
USB 功能简介
USB 功能模块包括了USB Host,USB Device 和OTG 功能。
USB Host 目前已经支持上的功能有:Mass Storage,UVC。
USB Device 目前已经支持上的功能有:ADB,UAC。
OTG 主要用作Host 与Device 的切换,如当板子通过 USB 线连接到 USB 主机 (PC) 上时,
此时 OTG 是加载成 USB Device;若当前板子是通过 OTG 线连接一个USB 设备,此时 OTG 则加载
成 USB Host。USB 外设特性
- Complies with USB 2.0 Specification
- Supports High-Speed (HS, 480-Mbps), Full-Speed (FS, 12-Mbps), and Low-Speed (LS, 1.5-Mbps) in Host mode
- Supports High-Speed (HS, 480 Mbps), Full-Speed (FS, 12 Mbps) in Device mode
- Supports the UTMI+ Level 3 interface. The 8-bit bidirectional data buses are used
- Supports bi-directional endpoint0 for Control transfer
- Supports up to 8 User-Configurable Endpoints for Bulk, Isochronous and Interrupt bi-directional transfers (Endpoint1, Endpoint2, Endpoint3, Endpoint4)
- Supports up to (4KB+64Bytes) FIFO for EPs (Including EP0)
- Supports High-Bandwidth Isochronous & Interrupt transfers
- Automated splitting/combining of packets for Bulk transfers
- Supports point-to-point and point-to-multipoint transfer in both Host and Peripheral mode
- Includes automatic ping capabilities
- Soft connect/disconnect function
- Performs all transaction scheduling in hardware
- Power Optimization and Power Management capabilities
- Includes interface to an external Normal DMA controller for every Eps
USB 配置介绍
sys_config.fex
配置说明sys_config.fex 中主要是对 OTG 功能进行配置,各个配置的含义可如下所示:
Key Value [usbc0] 控制器0的配置。 usb_used: USB使能标志。置1,表示系统中USB模块可用,置0,则表示系统USB禁用。 usb_port_type USB端口的使用情况。 0: device only;1: host only;2: OTG;usb_detect_type: USB usb_detect_mode USB端口的检查方式。0: 线程轮询;1: id中断触发 usb_id_gpio USB ID pin脚配置。具体请参考gpio配置说明。 usb_det_vbus_gpio USB DET_VBUS pin脚配置。具体请参考gpio配置说明。 usb_drv_vbus_gpio USB DRY_VBUS pin脚配置。具体请参考gpio配置说明。 usb_drv_vbus_type vbus设置方式。0: 无; 1: gpio; 2: axp。 usb_det_vbus_gpio "axp_ctrl",表示axp 提供。 usbh_driver_level usb驱动能力等级 usbh_irq_flag usb中断标志 示例:
;-------------------------------- ;--- USB0控制标志 ;-------------------------------- [usbc0] usb_used = 1 usb_port_type = 2 usb_detect_type = 1 usb_detect_mode = 0 usb_id_gpio = port:PB04<0><0><default><default> usb_det_vbus_gpio = port:PA24<0><0><default><default> usb_drv_vbus_gpio = port:PA29<1><0><default><default> usb_drv_vbus_type = 1 usbh_driver_level = 5 usbh_irq_flag = 0
rtos menuconfig 配置说明
- 使能USB 驱动
‑> Drivers Options ‑> soc related device drivers ‑> USB Drivers [*] USB
- 使能OTG
‑> Drivers Options ‑> soc related device drivers ‑> USB Drivers [*] USB_MANAGER
- 使能USB Host
使能完USB Host 之后,还需要选择:OHCI 与EHCI(一共有0 和1 两组,对于R128 来说,只需要使能USB0)。另外,还要选择功能驱动(Mass Storage,UVC),不然只是使能USB Host 则无法正常运行USB Host 的功能。
USB Host 控制器驱动配置如下:
USB Host ‑> Drivers Options ‑> soc related device drivers ‑> USB Drivers [*] USB_HOST OHCI ‑> Drivers Options ‑> soc related device drivers ‑> USB Drivers ‑> USB HOST [*] USB_OHCI_0 EHCI ‑> Drivers Options ‑> soc related device drivers ‑> USB Drivers ‑> USB HOST [*] USB_EHCI_0
- 使能USB Device
USB Device 除了UDC 的使能之外,也需要选择对应的功能驱动Gadget 以及功能驱动对应的上层应用。
USB Device 控制器驱动配置如下:
USB Device ‑> Drivers Options ‑> soc related device drivers ‑> USB Drivers [*] USB_DEVICE USB Device使能dma通信 ‑> Drivers Options ‑> soc related device drivers ‑> USB Drivers ‑> USB DEVICE [*] UDC_USE_DMA
USB 源码结构
lichee/rtos‑hal/hal/source/usb ├── common/ ├── core/ ├── gadget/ │ ├── function/ ├── hid/ │ ├── Class/ │ ├── Client/ │ │ ├── KeyBoard/ │ │ └── Mouse/ │ ├── Include/ ├── host/ ├── include/ ├── manager/ ├── platform/ ├── storage/ │ ├── Class/ │ ├── Disk/ │ ├── include/ │ └── Misc/ ├── udc/ └── uvc/ ├── Class/ ├── drv_webcam/ │ ├── dev_cfg/ │ └── webcam_core/ ├── Include/ ├── Misc/ └── Webcam/
- common: USB 驱动初始化公用文件。
- core: USB Host 驱动框架层文件。
- gadget: USB Deivce 功能驱动gadget 驱动总入口文件,function 则是各个功能驱动的驱动文件。
- hid: USB Host HID 协议及驱动文件。
- host: USB Host 硬件控制器驱动。
- include: USB 公用头文件。
- manager: USB OTG 驱动。
- platform: 不同平台的配置文件。
- storage: USB Host Mass Storage 协议及驱动文件。
- udc: USB Deivce 硬件控制器驱动。
- uvc: USB Host UVC 协议及驱动文件。
详细说明请见:HAL USB
USB 常用功能说明
配置OTG 功能
OTG 功能下,需要根据USB ID 脚去进行Device/Host 模式的切换;如果需要支持NULL 模式(既不加载Device 也不加载Host 驱动), 那么还需要VBUS 状态检测引脚。
涉及到的主要改动点:
在sys_config.fex,修改如下配置: usb_port_type配置为2,即OTG模式。 usb_id_gpio配置对应的USB ID引脚。 usb_det_vbus_gpio, 需要根据实际情况进行配置: 1.如果需要检测VBUS状态,则按下面情况进行配置: 配置成对应的gpio即可。 2.如果不需要检测VBUS状态(缺少NULL模式) 那么直接填写USB ID的gpio配置(也就是VBUS与ID状态一致)。
USB OTG 驱动,会根据ID 和VBUS 的状态,自动切换成对应的模式。ID 和VBUS 的对应关系如下表:
ID VBUS 模式 0 0 Host 1 0 Null 0 1 Host 1 1 Device - ID 脚一般情况下为高电平,只有接入OTG 线时会拉低;
- VBUS 为1 表示micro USB 口有接入外部电源;
- 一般不会出现ID 为0,VBUS 为1 的情况。这表示接入OTG 线,但是还检测到VBUS;
- 如果没有VBUS 检测,ID 只有0 和1 的两种情况, 也就是说要么加载device 驱动,要么加载host 驱动; 这会带来一些影响:usb 相关时钟一直被打开,导致有一定功耗,以及硬件射频。
USB Gadget 功能配置
USB Gadget 支持众多功能,它们的配置方法比较类似,只需要在mrtos_menuconfig 中选上对应的Gadget 功能驱动即可在系统初始化时自动加载。与Linux 不一样的是,RTOS 的gadget 功能配置全部hardcode 在功能驱动里,无需像Linux 一样需要在应用层手动进行gadget 配置。
另外,目前RTOS 的USB 驱动还不支持composite gadget,因此只能支持加载单一的gadget 功能驱动,无法同时多个功能。
ADB 功能
adb 的全称为Android Debug Bridge,就是起到调试桥的作用。通过ADB,可以直接在PC 上通过命令行控制小机端的控制台;也可以通过ADB 进行文件传输。
menuconfig 驱动相关配置:
‑> Drivers Options ‑> soc related device drivers ‑> USB Drivers ‑> USB DEVICE [*] DRIVERS_USB_GADGET_ADB
menuconfig ADBD 应用相关配置:
‑> System components ‑> aw components ‑> USB Components Support ‑> USB Gadget Support [*] adbd service
在RTOS 的USB 框架中,一旦加载了adb gadget,就会自动启用adbd 服务,直接连上PC 就可以使用了。
adb 正常启动的相关log:
[usb0] insmod device driver! adbd version:AW‑V1.1.6, compiled on: Apr 11 2023 10:33:24 adbd service init successful
PC 运行效果图如下图所示:
UAC 功能
UAC 全称为USB Audio Class,USB 音频类。
通过UAC,可以实现实时获取音频设备的音频数据,并且通过UAC 实现操控设备音量,采样率,等参数。UAC 实现对外接音频操作,从用户功能来说,主要包括USB 麦克风、USB 声卡和其它音频设备的功能控制和接口标准。
menuconfig 驱动相关配置:
‑> Drivers Options ‑> soc related device drivers ‑> USB Drivers ‑> USB DEVICE [*] DRIVERS_USB_GADGET_UAC
menuconfig UACD 应用相关配置:
‑> System components ‑> aw components ‑> USB Components Support ‑> USB Gadget Support [*] uacd service ‑> System components ‑> aw components ‑> USB Components Support ‑> USB Gadget Support ‑> uacd audio function [*] AudioSystem local audio
uacd 正常启动的相关log:
[usb0] insmod device driver! uacd version:AW‑V0.5, compiled on: Apr 27 2023 10:44:02 [UACD‑INFO][u_audio_init] line:167 stream=1, rate=48000, ch=2, bits=16, audio_buf_size=192 [UACD‑INFO][u_audio_init] line:167 stream=0, rate=16000, ch=2, bits=16, audio_buf_size=64 [UACD‑INFO][u_audio_stop_capture] line:320 [UACD‑INFO][u_audio_stop_playback] line:457 [UACD‑INFO][u_audio_stop_capture] line:320 [UACD‑INFO][u_audio_stop_playback] line:457
启动成功之后,能够在PC 端看到新增了一个音频输入和输出的设备,如下图:
USB Host 功能配置
接入OTG 线后,成功切换成 USB Host 的log 可参考如下:
[ehci‑usb0] insmod host driver! calibration finish, val:0x19, usbc_no:0 ehci insmod status = 1 [usbh core]: add gen_dev SW USB2.0 'Enhanced' Host Controller (EHCI) Driver [D(rv.)] devops: register dev(sunxi_timer) ok USB 0.0 started, EHCI 1.00 [usbh core]: adding sub dev (config #1, interface 0) usb match id suceessfull [hub]: usb hub probe [hub]: 1 port detected [usbh hub]: local power source is good [E(rv.)] pm device sunxi_ehci0(00000000082AE1D0) has already registered [ohci‑usb0] insmod host driver! calibration finish, val:0x19, usbc_no:0 [usbh core]: add gen_dev SW USB2.0 'Open' Host Controller (OHCI) Driver [usbh core]: adding sub dev (config #1, interface 0) usb match id suceessfull [hub]: usb hub probe [hub]: 1 port detected [usbh hub]: local power source is good [D(rv.)] devops: register dev(sunxi_ohci0) ok
U 盘功能
选上以下配置:
Mass Storage ‑> Drivers Options ‑> soc related device drivers ‑> USB Drivers ‑> USB HOST [*] Mass Storage support [*] USB CD support
等待U 盘挂载成功,可以发现根目录下多了一个usb_msc 的文件夹,这个即是U 盘挂载的文件夹。可以通过该文件夹与U 盘进行读取/传输文件。
- 目前R128 只支持fat32 文件系统的U 盘,其他文件系统U 盘会挂载失败。
- 只支持挂载单分区的U 盘,如果U 盘被分解成了多个分区的话,只能挂载上第一个分区。
接入U 盘后,系统开始识别U 盘。成功识别到U 盘的log 信息如下:
ehci_irq: highspeed device connect port debounce 0... port debounce 0... port debounce 25... port debounce 50... port debounce 75... hub_port_init: udev address = 0 [hub_port_init]: new high speed USB device address 0 usb hub set new address(2) [usbh core]: adding sub dev (config #1, interface 0) usb match id suceessfull mscDevProbe begin [msc]: GetMaxLUN successful, max lun is 0 begin mscLunAdd disk, send last lun msg......... mscLun‑>LunNo=0 mscLun‑>mscDev‑>MaxLun=1 BlkDev‑>last_lun=1 Wrn: short transfer, urb_state(0), want_len(192), real_len(70) Wrn: short transfer, urb_state(0), want_len(192), real_len(24) ‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑Disk Information‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑ WriteProtect = 0 MediaPresent = 1 WCE = 0 RCD = 0 capacity = 29532M, sector number = 60481536 sector_size = 512 DevNo = 0 ClassName = DevName = SCSI_DISK_000 ‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑ hub_port = 1 cnt = 0 Classname = DISK DevName = SCSI_DISK_000 DeviceName = SCSI_DISK_000 Vender = TOSHIBA USB FLASH DRIVE PMAP Product = USB FLASH DRIVE PMAP Serial = PMAP HubPortNo = 1 DeviceType = 2 DiskRead: block(0, 1) is adjacence max capacity(39ae000), can't use special write mount usb mass storage successull!! .............................................................................. [USB Disk]: Register new device, class = [DISK], dev = [SCSI_DISK_000] .............................................................................. end mscLunAdd mscDevScanThread end...
然后通过ls 查看usb_msc,可以看到U 盘里的文件:
c906>ls dev data usb_msc c906>ls usb_msc System Volume Information 3.txt
USB 摄像头
选上以下配置:
UVC ‑> Drivers Options ‑> soc related device drivers ‑> USB Drivers ‑> USB HOST [*] USB_CAMERA
接上USB 摄像头后,成功识别出摄像头后会出现以下log:
ehci_irq: highspeed device connect port debounce 0... port debounce 0... port debounce 25... port debounce 50... port debounce 75... hub_port_init: udev address = 0 [hub_port_init]: new high speed USB device address 0 usb hub set new address(3) [hub] :skipped 1 descriptor after configuration skipped 6 descriptors after interface skipped 1 descriptor after endpoint skipped 26 descriptors after interface num_ep:0 skipped 1 descriptor after endpoint skipped 4 descriptors after interface num_ep:0 num_ep:0 skipped 2 descriptors after interface skipped 1 descriptor after endpoint [usbh core]: adding sub dev (config #1, interface 0) usb match id suceessfull UVCDevProbe begin Probing generic UVC device device quirks 0x0 Found format MJPEG. ‑ 1920x1080 (30.0 fps) ‑ 1280x720 (30.0 fps) ‑ 640x480 (30.0 fps) ‑ 640x360 (30.0 fps) ‑ 352x288 (30.0 fps) ‑ 320x240 (30.0 fps) ‑ 320x180 (30.0 fps) ‑ 176x144 (30.0 fps) ‑ 160x120 (30.0 fps) ‑ 1920x1080(30.0 fps) Found format YUV 4:2:2 (YUYV). ‑ 1920x1080 (5.0 fps) ‑ 1280x720 (10.0 fps) ‑ 640x480 (30.0 fps) ‑ 640x360 (30.0 fps) ‑ 352x288 (30.0 fps) ‑ 320x240 (30.0 fps) ‑ 320x180 (30.0 fps) ‑ 176x144 (30.0 fps) ‑ 160x120 (30.0 fps) ‑ 1920x1080 (5.0 fps) Found UVC 1.00 device USB 2.0 Camera (0c45:6366) num_altsetting=7 UVC device initialized. DRV_WEBCAM_MInit webcam plug in message... [usbh core]: adding sub dev (config #1, interface 1) [usbh core]: adding sub dev (config #1, interface 2) [usbh core]: adding sub dev (config #1, interface 3)
通过log 信息能够看到,在识别出USB 摄像头后,会打印出该摄像头支持的格式以及分辨率。
接着通过uvc 测试命令,对UVC 功能进行测试:
usb uvc_test
测试命令调用完成之后,会在/data 目录下生成5 张名为/data/source_frame_x.jpg 的图片。通过adb pull 将图片拉到PC 端,然后在PC 端打开图片即可看到USB 摄像头拍下的照片。
USB 调试方法
USB OTG 功能调试
除了OTG 的自动切换功能,还可以进行手动的切换,需要使用到USB 命令。
USB Device/Host 的手动切换方法:
输入usb ‑h 能够看见usb 的全部命令。
- USB Device 的相关命令
usb udc {‑i|‑r} [<port>] ‑i:指的是指定需要进行切换的USB口。目前R128芯片只有USB0是支持Device模式的。 ‑r:指的是remove,注销当前的Device模式。 比如说将USB0切换成Device模式,则运行: usb udc ‑i 0
- USB Host 的相关命令
usb hci {‑i|‑r} [<port>] ‑i:指的是指定需要进行切换的USB口。 ‑r:指的是remove,注销当前的Host模式。 比如说将USB0切换成Host模式,则运行: usb hci ‑i 0
USB 相关工具
ADB
ADB 功能是从Android 移植过来的,设备端会运行adbd 服务,而Host 端(一般为PC) 通过adb工具进行调试,如adb shell, adb push/pull 等。
ADB 功能说明
adb shell 功能
PC端执行adb shell,可进入控制台。
PC端执行adb shell + command, 例如adb shell ls /可以直接将结果输出到终端。
adb push/pull 功能
推送文件到小机端: adb push test.bin /data 从小机端拉取文件: adb pull /data/test.bin .
adb 网络连接
如果需要用此功能,需要额外进行menuconfig 的配置,配置方法如下:
‑> System components ‑> aw components ‑> USB Components Support ‑> USB Gadget Support [*] adb local transport (5555) adb local transport port
adb local transport port 是用于配置端口号的,用于在adb 网络连接时匹配端口的,默认为5555。
在进行网络adb 连接之前,需要保证PC 和小机端在同一个局域网中,并且可以ping 通。
小机端运行ifconfig,查看当前小机端的IP,假设IP为192.168.1.101。
则PC端执行adb connect 192.168.1.101建立连接,然后就可以开始执行adb shell等命令了。
-
交叉编译NumCpp到ARM上的方法(以全志V853为例)
前段时间,在电脑上安装了Ubuntu/Windows系统双系统,现在拿Ubuntu系统试试水。
NumCpp是类似Numpy的C++版本,可以方便的进行一些矩阵的操作,现在我们来把它交叉编译到ARM上去。
1. 获取源码
要交叉编译NumCpp,首先要获取依赖库boost源代码以及NumCpp的源代码。
在这里我使用的是boost1.68,可以从下面这个网址下载。
https://www.boost.org/Boost C++ Libraries https://www.boost.org/
NumCpp可以从GitHub上clone下来
https://github.com/dpilger26/NumCppGitHub - dpilger26/NumCpp: C++ implementation of the Python Numpy library C++ implementation of the Python Numpy library. Contribute to dpilger26/NumCpp development by creating an account on GitHub. https://github.com/dpilger26/NumCpp
2. 交叉编译boost
将下载好的压缩包解压。
cd boost_1_68_0 ./bootstrap.sh --with-libraries=all --with-toolset=gcc
其中,--with-libraries可以选择自己想要安装的库。
执行完这一步之后,打开新生成的project-config.jam,修改下面这几行中的gcc部分。一定要注意空格!(每一个空格缺一不可),其中的arm-openwrt-linux-gcc的路径可以换成你的交叉编译器的路径
if ! gcc in [ feature.values <toolset> ] { using gcc : arm : /home/xinzhe/toolchains/rootfsbuilt/arm/toolchain-sunxi-musl-gcc-830/toolchain/bin/arm-openwrt-linux-gcc ; }
之后执行
./b2
检查输出的log中的编译器是否已经是交叉编译器,然后再执行
./b2 install --prefix=/home/xinzhe/Documents/boost_arm
其中--prefix后面是你想要的boost库安装路径。
安装完成后,检查安装的boost库是否为ARM版本。
3.交叉编译NumCpp
Clone完成NumCpp之后,进入NumCpp文件夹。
由于我之前编译过x86-64版本的NumCpp,因此在编译ARM版本的时候,需要修改CMakeLists.txt,让cmake找到ARM版本的boost库。
在CMakeLists.txt中寻找Boost有关代码的上方加入
set(CMAKE_FIND_ROOT_PATH /home/xinzhe/Documents/boost_arm) set(CMAKE_FIND_ROOT_PATH /home/xinzhe/Documents/boost_arm) if(NUMCPP_NO_USE_BOOST) target_compile_definitions(${ALL_INTERFACE_TARGET} INTERFACE -DNUMCPP_NO_USE_BOOST) else() find_package(Boost 1.68.0 REQUIRED COMPONENTS date_time ) target_link_libraries(${ALL_INTERFACE_TARGET} INTERFACE Boost::boost $<$<OR:$<CXX_COMPILER_ID:Clang>,$<CXX_COMPILER_ID:GNU>>:Boost::date_time> ) endif()
同时,在NumCpp文件夹里,创建一个Compile.cmake文件,来指定交叉编译器。内容如下:
set(CMAKE_SYSTEM_NAME Linux) set(TOOLCHAIN_PATH /home/xinzhe/toolchains/rootfsbuilt/arm/toolchain-sunxi-musl-gcc-830/toolchain/arm-openwrt-linux-muslgnueabi) set(CMAKE_C_COMPILER /home/xinzhe/toolchains/rootfsbuilt/arm/toolchain-sunxi-musl-gcc-830/toolchain/bin/arm-openwrt-linux-gcc) set(CMAKE_CXX_COMPILER /home/xinzhe/toolchains/rootfsbuilt/arm/toolchain-sunxi-musl-gcc-830/toolchain/bin/arm-openwrt-linux-g++)
其中的TOOLCHAIN_PATH、CMAKE_C_COMPILER、CMAKE_CXX_COMPILER替换成自己的交叉编译工具。
然后在NumCpp文件夹下执行下面的操作
mkdir build cd build cmake .. -DCMAKE_TOOLCHAIN_FILE=../Compile.cmake
然后检查输出的log中,C编译工具是否为交叉编译工具,以及找到的boost库是否为我们刚刚创建的ARM版本的。
检查完毕后,修改/NumCpp/build目录下的cmake_install.cmake,修改其中的CMAKE_INSTALL_PREFIX为自己想要安装的目录。
# Install script for directory: /home/xinzhe/Documents/NumCpp # Set the install prefix if(NOT DEFINED CMAKE_INSTALL_PREFIX) set(CMAKE_INSTALL_PREFIX "/home/xinzhe/Documents/boost_arm") endif() string(REGEX REPLACE "/$" "" CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}")
然后执行:
cmake --build . --target install
我们就在boost_arm下得到了ARM版本的boost和ARM版本的NumCpp的编译结果文件
4. 编译自己的代码
创建NumCpp_arm文件夹,放入自己的main.cpp,并创建Compile.cmake(同上),CMakeLists.txt。
其中CMakeLists.txt代码如下:
cmake_minimum_required(VERSION 3.20) project("HelloWorld" CXX) add_executable(${PROJECT_NAME} main.cpp) set(CMAKE_FIND_ROOT_PATH /home/xinzhe/Documents/boost_arm) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_FLAGS "-g -std=c++17 -lstdc++fs") find_package(NumCpp 2.10.1 REQUIRED) target_link_libraries(${PROJECT_NAME} NumCpp::NumCpp stdc++fs )
其中的CMAKE_CXX_FLAGS的设置,以及stdc++fs添加到target_link_libraries里,是为了防止一下的报错。
xinzhe@xinzhe-G3-3590:~/Documents/NumCpp/examples/NumCpp_arm/build$ make Consolidate compiler generated dependencies of target HelloWorld [ 50%] Linking CXX executable HelloWorld CMakeFiles/HelloWorld.dir/main.cpp.o: In function `std::filesystem::exists(std::filesystem::__cxx11::path const&)': main.cpp:(.text._ZNSt10filesystem6existsERKNS_7__cxx114pathE[_ZNSt10filesystem6existsERKNS_7__cxx114pathE]+0x1c): undefined reference to `std::filesystem::status(std::filesystem::__cxx11::path const&)' CMakeFiles/HelloWorld.dir/main.cpp.o: In function `std::filesystem::__cxx11::path::path<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::filesystem::__cxx11::path>(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::filesystem::__cxx11::path::format)': main.cpp:(.text._ZNSt10filesystem7__cxx114pathC2INSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEES1_EERKT_NS1_6formatE[_ZNSt10filesystem7__cxx114pathC5INSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEES1_EERKT_NS1_6formatE]+0x60): undefined reference to `std::filesystem::__cxx11::path::_M_split_cmpts()' collect2: error: ld returned 1 exit status make[2]: *** [CMakeFiles/HelloWorld.dir/build.make:98: HelloWorld] Error 1 make[1]: *** [CMakeFiles/Makefile2:83: CMakeFiles/HelloWorld.dir/all] Error 2 make: *** [Makefile:91: all] Error 2
然后,我们执行一下操作
mkdir build cd build cmake .. -DCMAKE_TOOLCHAIN_FILE=../Compile.cmake
检查输出的log,交叉编译工具对不对,boost与NumCpp是否为ARM版本。
最后执行
make
下面的是我的编译结果,可以看到编译成功
xinzhe@xinzhe-G3-3590:~/Documents/NumCpp/examples/NumCpp_arm/build$ make [ 50%] Building CXX object CMakeFiles/HelloWorld.dir/main.cpp.o [100%] Linking CXX executable HelloWorld [100%] Built target HelloWorld
检查一下生成的可执行文件是够为ARM版本。
xinzhe@xinzhe-G3-3590:~/Documents/NumCpp/examples/NumCpp_arm/build$ file HelloWorld HelloWorld: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-musl-armhf.so.1, with debug_info, not stripped
5.移植到V853上
将HelloWorld文件和boost_arm整个文件夹push到板子上
xinzhe@xinzhe-G3-3590:~/Documents/NumCpp/examples/NumCpp_arm/build$ adb push HelloWorld /root HelloWorld: 1 file pushed. 3.6 MB/s (2607428 bytes in 0.684s)
然后到HelloWorld所在目录下执行
root@TinaLinux:~# export LD_LIBRARY_PATH=/root/boost_arm/lib:$LD_LIBRARY_PATH
可以看到我的HelloWorld正常执行
root@TinaLinux:~# ./HelloWorld [[0.452916, 0.119087, -0.459488, -0.733428, 0.374606, 0.244954, -0.237323, -0.109258, 0.907443, -0.413274, -0.157849, 0.158918, 0.0474297, -0.519952, 1.38254, -0.174767, -0.911914, -0.812675, -0.506005, -0.222257, -0.192745, -1.04207, 0.424627, 0.217617, 0.277484, -0.0261248, -0.0865481, -0.371074, -0.275607, 0.937942,
到这里,交叉编译就完成啦,希望各位小伙伴也能顺利进行!
原文链接:https://blog.csdn.net/where_are_u/article/details/129863901
-
【R128】软件配置——RTOS 软件包配置
基于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 软件包配置
本文将介绍 RTOS 软件包、地址,内核配置等。
Kconfig 简介
有过 linux 内核开发经验的人,对 menuconfig 不会陌生。对于各类内核,只要是支持 menuconfig 配置界面,都是使用 Kconfig。
换言之:
- menuconfig:支持配置内核的图形化界面。
- Kconfig:生成 menuconfig 界面的脚本语言。
menuconfig 操作简介
我们运行 mrtos_menuconfig 之后,会打开如下图所示的界面
整个显示界面大致分为三部分
上方是操作的说明和图例说明。
<Enter>
按下Enter键进入子菜单。<Y>
按下Y键,选中这项功能。<N>
按下N键,排除这项功能 。<M>
按下M键,以模块的形式选择。<Esc><Esc>
按两下Esc键,返回上一级菜单<?>
按下?键,查看高亮功能的帮助信息</>
按下/键,搜索[*]
被选中的项目[ ]
未被选择的项目<M>
以模块形式被选择的项目< >
未被选择的模块
中间部分就是我们要选择的各项功能了,使用上下两个方向键进行选择,也可以使用关键字进行跳转。
最下方是功能选择按钮,功能与案件基本重合。使用左右方向键选择,Enter键选中。
<Select>
进入下级菜单< Exit >
退回上一级菜单,在最顶层菜单就是退出menuconfig< Help >
显示当前选项的帮助信息< Save >
手动保存配置文件< Load >
加载指定的配置文件,默认加载的是.config
RTOS menuconfig 说明
了解了menuconfig的基本操作,我们再来了解一下 RTOS 的 menuconfig 具体都有是么内容。
Build target sunxi arch
分别选择芯片的系列,对于R128平台,使用的是
sun20iw2p1
平台Build OS or Library
选择编译为 Library 还是 OS,这里我们使用的是 OS 所以设置为编译 OS
Build System
这部分配置 RTOS 的配套编译脚手架,配置编译使用的编译工具链的路径,libc的类型等等。
Architecture Options
架构配置,配置CPU的架构,入口地址,分配的内存长度,RV核心的地址,DSP核心的地址。这里也也配置默认启动的核心,可以关闭 C906 和 DSP 核心的启动。
Kernel Options
FreeRTOS 内核相关的配置,一般不需要修改。
Drivers Options
驱动配置,分为
soc related device drivers
和other drivers
soc related device drivers
包括各种外设驱动,与驱动的单元测试。
other drivers
包括 WiFi 驱动,蓝牙驱动等等
System components
组件选择,软件包,协议栈等相关选择
aw components
全志提供的相关组件,核间通信组件,多媒体组件等。
thirdparty components
第三方提供的组件,lvgl,协议栈等等
Projects Options
项目配置,选择项目方案
menuconfig 配置保存
完成配置之后,我们可以选择使用左右方向键选择 < save > 并按下Enter键。
如果修改配置文件名称的话,就是将当前的配置另外保存到指定文件;不修改的话默认保存在.config中, 然后选择 ok 确认,就可以继续进行配置了修改了。如果不想保存,那么可以按两下 Esc 键返回。
当然也可以不使用上面的操作,在全部修改都完成之后,连按 Esc 键,就会退出menuconfig。 在退出时会提醒是否保存配置文件,选择 Yes 即可保存配置,如果不想保存可以选择 No , 如果还想继续修改,可以按两下 Esc 键返回 menuconfig 继续修改。
-
在V853上进行Opencv库的编译步骤
OpenCV(Open Source Computer Vision)是一个开放源代码的计算机视觉库,它提供了一系列函数和算法,用于处理图像和视频。通过使用OpenCV,您可以进行各种计算机视觉任务,例如图像处理、对象识别、目标追踪、人脸检测和机器学习等。它提供了底层图像处理功能,以及高级功能和模块,如特征提取、边缘检测、图像分割和物体测量等。
当V853需要进行图像的预处理操作时,涉及到使用opencv库中的相关接口和函数。所以如果要基于853进行模型的输入预处理或者输出后处理操作时,就不可避免的要依赖到opencv库。
先获取GitHub中的opencv源码:
https://github.com/opencv/opencv
https://github.com/opencv/opencv_contrib进入目录下,创建build目录:
mkdir build cd build/
运行cmake指令配置参数生成Makefile文件:
cmake -D CMAKE_BUILD_TYPE=Release -D CMAKE_INSTALL_PREFIX=/usr/local -D OPENCV_EXTRA_MODULES_PATH=/xxxxx/opencv/opencv_contrib/modules ..
当然,如果需要将库外置不编入环境的话可以修改CMAKE_INSTALL_PREFIX的值,直接改为对应路径即可。
选择库的相关指令(指定编译/不编译某些库,优化opencv库大小):指定编译opencv_core库:
-DBUILD_opencv_core=ON
指定不编译opencv_hdf库:
-DBUILD_opencv_hdf=OFF
使用make指令进行编译:
make -j4
make完成并不代表结束,还需要生成opencv库使用。
编译完成后,加上install命令,将其加载/注册到usr文件夹中,这样每次编译 就不用使用一整个opencv库/包了。
make install
当编译可执行文件需要链接opencv库时,可以编写一个cmake来编译文件:
# 声明要求的 cmake 最低版本 cmake_minimum_required( VERSION 2.8 ) # 声明一个 cmake 工程 project( test ) #添加OPENMP库 FIND_PACKAGE( OpenMP REQUIRED) if(OPENMP_FOUND) message("OPENMP FOUND") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}") endif() #添加OPENCV库 #指定OpenCV版本,代码如下 ##find_package(OpenCV 3.3 REQUIRED) #如果不需要指定OpenCV版本,代码如下 set(OpenCV_DIR ${CMAKE_SOURCE_DIR}/usr/lib/cmake/opencv4) find_package(OpenCV REQUIRED) #添加OpenCV头文件 include_directories(./ ${OpenCV_INCLUDE_DIRS}) # 添加一个可执行程序 # 语法:add_executable( 程序名 源代码文件 ) add_executable(main ${DIRSRCS} ${NPULIB_DIRSRCS}) # 将库文件链接到可执行程序上 target_link_libraries(main ${OpenCV_LIBS} ${VIP_LIBS} -lstdc++ -lpthread -lrt -lm -ldl)
可以看到,该cmake链接了opencv库,
set(OpenCV_DIR ${CMAKE_SOURCE_DIR}/usr/lib/cmake/opencv4)
该方法通过链接opencv库中的cmake文件然后来找到对应的opencv包,保证主程序能够顺利调用。
可以在github上下载yolov7处理代码,链接库进行验证。如若cmake生成makefile正常,make编译也正常,成功执行例程过后,opencv库成功生成。
-
又进化了!T113智能家居86盒圆屏版(圆屏加一体化驱动板+CNC外壳+炫酷LVGL UI)
PCB产品发车QQ群号:787148555 ——繁花T113板(太极派)交流群
PCB产品发车QQ群号:787148555 ——繁花T113板(太极派)交流群
PCB产品发车QQ群号:787148555 ——繁花T113板(太极派)交流群
文末有详细PCB发车介绍
流畅到要设置帧率限制的神之眼/符玄个性时钟
巨丝滑的高清高帧率视频播放
如上,你所见到的这款圆滚滚的小玩意,就是魔改了一款在前几期的文章中我们所介绍过的,一款方形的T113-S3智能家居86盒,原项目因其验证过的原理图和PCB(√),可初始化的屏幕(√),详尽的SDK和保姆级的部署教程(√),从而受到了不少开发者的关注,其中也包括一些爱整活的小伙伴。
于是萨纳兰的黄昏在86盒的原作者FanHuaCloud大佬加持下,又给86盒挖了个新坑,为了解决之前ESP32所驱动圆屏只能播放MJPEG并且帧率较低的尴尬问题,集圆屏加一体化驱动板+外壳+炫酷LVGL UI于一身的圆形86盒横空出世,并命名其为——T113太极派。
硬件设计简介
T113太极派可以简单理解为是方形86盒的圆形改版,两者在硬件的设计上大体相同,圆屏由于面积较小,元件排列比方形的困难许多,调整了很久的布局,只是调整了背光芯片以及其它部件的PCB布局走线。
01、全志T113-S3主控,双核A7,内存128M,支持硬件解码,和D1s Pin to Pin,支持相互替换
02、7701S 2.1寸SPI RGB接口圆屏,带触摸
03、RTL8723 WIFI模块,用于连接网络
04、128MB SPI Nand,支持从SPI Nand启动系统
05、双USB接口,其中一个支持Host/Device,另一个仅支持Host
06、板载麦克风和一个耳机口
07、支持Typec口接DAC小尾巴输出音频目前的版本是插电使用的超薄版本 ,后续可能会增加带扩展版的支持电池的充电版本,其最主要的特点是作者为这款圆形的太极派专门给它建模做了一个极其轻薄的CNC外壳,触摸屏直径是71.8mm,外壳直径为74mm。树脂版外壳厚度为13mm,CNC版外壳厚度为10mm。
软件系统及UI界面适配
APP是设备的灵魂,肯定不能只跑个lvgl demo看看那些图形蹦来蹦去最后得个数字。前面做的这一切,都是为了将之前的代码移植过来。本APP使用LVGL 8.3.11编写。目前有以下功能(方屏版和圆屏板都可用,相同的功能在不同形状的屏幕上会有不同的表现形式)
AIDA64无线副屏
原理很简单,电脑端开启AIDA64,新建一个Remote Panel,将指定的设备信息设置为指定格式后放上去,本设备在联网后就会定时获取信息,并动态展示。目前该界面还是为方屏设计的,圆屏还没有特别好的创意,等有了再改。
音乐频谱节拍器
同样针对方屏和圆屏做了两套不同的UI,不但UI不同,而且频谱样式也不同,方屏是条形频谱,圆形是放射形渐变频谱。频谱的实现是由全志的Tplayer的音频回调函数将PCM信息推送给对应的FFT进程,然后FFT计算后推送给页面展示来实现的。另:全志的tplayer有bug,如果设置单曲循环,且播放的音乐为flac格式,在音乐播放完,跳转会开头的时候会出错。为解决这个问题,APP在遇到flac音乐单曲循环的时候,会重新初始化该音乐。
又是频谱,没错我就是频谱星人。拾音频谱做了专属样式,实现是通过alsa接口读取麦克风PCM数据,然后同样推送给FFT进程。圆形版本的拾音频谱叠加了视频背景(视频背景由b站up主渣渣一块钱4个制作)。
动态天气时钟
以之前HTC手机的那个动态天气动画作为背景的时钟,根据不同的天气加载不同的动态背景,使用了高德天气API。
神之眼/符玄个性时钟
移植了之前做的一些个性主题时钟,比如随机星座时钟等,比较二次元,结合全志硬解视频功能,支持mp4视频播放作为动态背景 有兴趣的可以看我相关的视频展示。效果见文首视频连接
视频/相册播放器
针对方屏和圆屏做了两套不同的UI。除了按钮功能外,在屏幕右侧区域上下滑是调节音量,左侧区域上下滑调节亮度,中间区域左右滑调节进度。总之,将全志t113的硬件解码功能全部解锁出来,并配上lvgl播放器UI~
SDK系统
修掉了很多问题,直接编译可用,后续提供一键编译的虚拟机(不含视频演示ui相关),可以很好解决初学者反复踩坑环境搭建问题。
SDK中已经集成了我的APP所用到的一切库,如
- fftw3:FFT库
- Libjpeg:改为了最新的版本
- Taglib:读取音乐文件里面的图像
开源资料获取
本文所有内有内容均转载自原作者本人的B站视频账号及立创开源硬件平台的工程页面,文章内所提到的圆形86盒硬件设计不开源,可以参考方形的86盒PCB设计,可以直接使用的SDK已开源在帖子内,感兴趣的小伙伴可以复制下方链接或者戳文末的“阅读原文”获取。
- 作者萨纳兰的黄昏个人网站:https://pressf5.run/
- B站视频介绍:https://www.bilibili.com/video/BV1ip421m77K
- 方形PCB参考及开源SDK获取:https://oshwhub.com/planevina/t11386-box-revision
关于发车:QQ群号:787148555
目前圆屏pcb添加了一个接喇叭的功放电路,正在打样验证,预计下周初即验证完,然后就会准备smt事项。具体一个板子多少钱会根据到时候贴的数量来看,多就会便宜点少就会贵点。
- 程序:买了板子的免费激活视频中展示的程序。
- 外壳:样品已用fdm打印验证过,树脂版的已下单三维猴验证,同样下周可验证完。会在群内直接提供stl文件供自己打印(也有可能统一下单三维猴树脂打印)。我个人非常喜欢cnc壳,看到时候有没有人一起凑一凑去加工一些。
- sdk:可使用繁花在立创上开源的那个,后面也会再整理一套
-
【R128】应用开发案例——LVGL 与 SPI TFT GUI
基于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
LVGL 与 SPI TFT GUI
本次使用的是 Dshan_Display Module,如下图:
引脚配置如下:
R128 Devkit TFT 模块 PA12 CS PA13 SCK PA18 MOSI PA9 PWM PA20 RESET PA19 RS 3V3 3.3V GND GND 载入方案
我们使用的开发板是 R128-Devkit,需要开发 C906 核心的应用程序,所以载入方案选择
r128s2_module_c906
$ source envsetup.sh $ lunch_rtos 1
配置 SPI LCD 驱动
驱动配置请参照 《SPI 驱动 TFT LCD 屏》文章进行配置。
配置 LVGL 软件包
运行
mrtos_menuconfig
进入配置页面。在如下地址找到勾选相关软件包。System components ---> thirdparty components ---> [*] Littlevgl-8 ---> [*] lvgl examples [*] lvgl-8.1.0 use sunxifb double buffer [*] lvgl-8.1.0 use sunxifb cache
注意,
lv_examples
与lv_g2d_test
不能同时勾选,否则会报错重复定义错误。错误如下:
如果出现
update_mbr_failed
,请参照常见问题修改分区表增大分区容量修改 LVGL 显示配置
进入
lichee/rtos-components/thirdparty/littlevgl-8/lv_examples/src/lv_conf.h
找到LV_COLOR_DEPTH
并修改为 16 位,LV_COLOR_16_SWAP
置为 1 进行红蓝颜色交换。测试
编译打包刷写后,在控制台输入
lv_examples
可以查看相关命令。运行
lv_examples 1
即可在屏幕显示 LVGL 界面。 -
回复: YuzukiChameleon,xr829扫卡失败[XRADIO_ERR] mac address not found in efuse.
看看这个帖子有没有帮助:
【FAQ】全志D1芯片 XR829扫卡失败问题排查
https://bbs.aw-ol.com/topic/757/share/1 -
回复: TinaLinux 无法创建 swap 分区吗?
可以的,
【FAQ】全志V853芯片 swap功能简介与tina上swap分区使用方法
https://bbs.aw-ol.com/topic/1626/share/1