Dashboard
概述
本文档旨在指导用户快速配置开发环境,包括编译SDK,烧录固件,升级固件及抓取Log。用户可以下载SDK中的测试文件,确保EVB可以正常运作,并与开发环境兼容且具备相应功能。
RealTek RTL8772GWP仪表盘SDK提供常用的基本功能,包括行驶速度监控、GUI框架、消息分发等。此外,SDK还提供了易于扩展的API接口,方便仪表盘厂商进行二次开发。
备注
本文所描述的仪表盘样例,采用RealTek RTL8772GWP芯片,搭配EK9716 RGB(800 * 480)显示屏,开发者若选用本文默认的硬件环境,可直接运行样例工程。
如果需要适配其他型号的显示屏,请参考 Port适配LCD,重新适配LCD屏幕驱动。
此外,RealTek RTL8772GWP芯片适配显示屏接口:支持i8080、qspi/spi、RGB888/RGB565,不支持MIPI接口。
实用案例
下图显示了 Dashboard 应用包含的模块:
下图显示了Dashboard应用包含的模块实物概览,由一块母板、一块子板,一块LCD屏幕和一块CAN模块组成。
支持功能
Realtek Dashboard应用程序提供完整功能支持。
支持BLE/USB两种通信模式
支持USB 3.0/2.0
支持电量检测
支持CAN通信
支持findmy功能
支持OTA软件升级
支持来电提醒、消息弹窗功能
RTL8772GWP支持Bluetooth LE 5.3规范,具有 1.8V 至 3.3V 的宽电源范围,开发人员可以更灵活地选用适配的外设。
环境需求
Dashboard demo评估板(RTL8772GWP EVB )×1,带屏(EK9716)
MicroUSB2.0数据线 x 2
PC(需要USB 2.0或更高连接)
解压BEE4-Bcut-SDK-DASHBOARD-vx.x.x文件
在PC上安装FT232设备驱动程序
CAN BUS
ARM Keil MDK: https://developer.arm.com
BeeMPTool_kits: RealMCU
DebugAnalyzer: RealMCU
Image Convert Tool: https://docs.realmcu.com/HoneyGUI/cn/latest/tool/index.html
备注
更多详细使用,请查阅 快速入门。
不同的平台支持不同的面板接口,开发者必须依赖IC平台的功能来选择合适的面板。LCD Hardware 平台 显示了每个平台的支持接口。
Hardware Platforms |
Display interface |
---|---|
RTL8772GWP HDK |
I8080, QSPI/SPI, RGB888/RGB565 |
硬件连线
EVB评估板提供了用户开发和应用调试的硬件环境。EVB由主板和子板组成。它有下载模式和工作模式,用户需要使用不同的接线方法来设置EVB进入所需的模式。
RTL8772GWP EVB接口
根据EVB供电接线 ,用户CON1和CON2的USB接口均需要与电脑连接给EVB供电。
下载模式
将P3_1和TX短接,P3_0和RX短接,LOW和LOG短接,接着按下 reset 键,进入下载烧录模式。
备注
芯片在上电后会读取Log Pin的电平信号,如果电平为低,则Bypass Flash,进入烧录模式,否则运行应用层程序。
工作模式
EVB正常工作需要将LOW和LOG短接断开。
Log接线
RX和LOG用跳线帽短接。
软件配置及环境
需要配置的软件环境主要有:
Keil(用于生成应用程序映像)
Log工具(用于分析代码执行情况)
MP工具(用于生成系统配置和OTA Header镜像文件)
Dashboard APK(用于测试的第三方工具)
图像转换工具(用于将图像资源转换为符合RTK GUI识别的bin文件)
具体使用请参考 软件工具。
Image Convert Tool
导入待转换目录。
设置转换格式:选择是否压缩、输出图片color space格式、输出图片属性(png、bmp、jpg)、输出图片保存位置等参数。
转换图片。
Dashboard APK
演示板的蓝牙设备名称是 BLE_DASHBOARD 。
点击 Connect Device
选择 BLE_DASHBOARD
备注
测试消息弹窗功能,在确保蓝牙连接的情况下,若出现消息弹出未响应,查看log未出现消息相关log,可重新设置主界面“打开通知权限”里面重新关闭-开启“仪表盘”。
GUI模拟器
开启模拟器步骤:
配置模拟器环境,请参考: https://docs.realmcu.com/Honeygui/cn/latest/get-started/index.html#id3
进入模拟器
SDK\subsys\HoneyGUI.code-workspace
,进入软件界面。打开
SDK\subsys\gui\win32_sim\menu_config.h
中宏CONFIG_REALTEK_BUILD_GUI_800_480
,取消注释,同时关闭默认demo宏CONFIG_REALTEK_BUILD_GUI_454_454_DEMO
。根据下图进行GUI 模拟器调试运行。
查看GUI 模拟器运行结果。
LCD
TL8772GWP芯片支持RGB模式LCD。用户不需要从头开始编写屏幕驱动程序,只需要几个简单的步骤就可以驱动LCD屏幕。RTL8772GWP的RGB信号是固定引脚,不能任意映射,只有一套,如下所示。在演示项目中,我们使用RGB888接口中的EK9716显示驱动程序,尺寸为 800 x 480 的面板作为目标显示,输入格式将使用RGB565,输出格式为RGB888。
#define LCDC_DATA23 P5_0
#define LCDC_DATA22 P5_1
#define LCDC_DATA21 P5_2
#define LCDC_DATA20 P5_3
#define LCDC_DATA19 P5_4
#define LCDC_DATA18 P5_5
#define LCDC_DATA17 P3_5
#define LCDC_DATA16 P3_4
#define LCDC_DATA15 P3_3
#define LCDC_DATA14 P3_2
#define LCDC_DATA13 P9_2
#define LCDC_DATA11 P4_6
#define LCDC_DATA10 P4_5
#define LCDC_DATA9 P4_4
#define LCDC_DATA8 P1_2
#define LCDC_DATA12 P4_7
#define LCDC_DATA6 P4_2
#define LCDC_DATA5 P4_1
#define LCDC_DATA7 P4_3
#define LCDC_DATA4 P4_0
#define LCDC_DATA3 P0_7
#define LCDC_DATA2 P0_6
#define LCDC_DATA1 P0_5
#define LCDC_DATA0 P0_4
#define LCDC_RGB_WRCLK P0_2
#define LCDC_HSYNC P1_5
#define LCDC_VSYNC P0_1
#define LCDC_CSN_DE P0_0
#define LCDC_RESET P2_2
配置选项
SDK中Dashboard功能的默认配置如下:
- CONFIG_REALTEK_BUILD_GUI(开)用于HoneyGUI开启GUI demo。
- CONFIG_REALTEK_BUILD_GUI_800_480_DEMO(开)使能构建上层事例demo相关文件。
- CONFIG_REALTEK_BUILD_LVGL_GUI(关)默认情况下执行RTK GUI demo,若需要切换至LVGL demo,需要将
menu_config.h
中CONFIG_REALTEK_BUILD_LVGL_GUI
打开(置“1”),同时,在keil预编译中添加ENABLE_LVGL_GUI
宏定义。 - CONFIG_REALTEK_BUILD_SCRIPT_AS_A_APP(关)若需要使用RVD tool进行UI布局,需要将
CONFIG_REALTEK_BUILD_SCRIPT_AS_A_APP
宏定义打开(取消注释)。 - CONFIG_REALTEK_BUILD_PPE(开)使能硬件加速Pixel Process Engine(PPE1.0),图像特效处理(缩放、透明度、pixel convert处理等)。
- CONFIG_REALTEK_CAN(关)构建CAN相关工程文件。
SCONS 构建工程
menu_config.h文件开启相关宏,例如构建CAN工程需要开启
CONFIG_REALTEK_CAN
,置“1”proj目录下运行cmd命令:scons --target=MDK5,运行成功标志,结尾出现:scons: done building targets.
编译和下载
请参阅 编译和下载,值得注意的是:
在 生成Flash Map 步骤中,开发人员需要根据
SDK\applications\dashboard\proj\flash_map.h
生成flash_map.ini
。在 生成System Config File 步骤中,Dashboard需要至少2条slave links,需要支持LE AE ADV功能。其他功能可根据需要进行配置。
备注
该章节介绍sample的编译方法(如keil、gcc等)以及如何download到硬件板上(Jlink、mptool等)。
在 编译APP Image 步骤中,Dashboard SDK keil工程的路径为:
SDK\applications\dashboard\proj\mdk
。APP image由该工程编译得到。
J-Link下载和调试
J-Link是由SEGGER公司开发的一种仿真器/调试器。它是一款专门针对Arm处理器的工具,可用于开发、调试和测试嵌入式设备的固件。J-Link具有高速且可靠的仿真性能,支持实时调试和快速下载固件,可以帮助开发人员加快调试和验证嵌入式系统的过程,提高开发效率和质量。
J-Link支持多种连接接口,如JTAG、SWD等,因为SWD使用的接线更少,所以RTL8772GWP采用这种接口: RTL87x2G(BEE4)-SWD对应接口。另外,J-Link也可以与多种开发环境和IDE(如Keil MDK、IAR Embedded Workbench等)兼容,Keil环境设置可以参考 Keil MDK。
建议使用J-Link Software v6.44(或更新)版本,更多信息可以参考 J-Link下载 进行编译和调试。
SWD接线
RTL8772GWP |
SWD |
---|---|
GND(黑) |
GND |
P1_0(绿) |
SWIO |
P1_1(黄) |
SWCK |
VDEX(红) |
Vterf/3.3V |
KEIL界面设置
请参阅 Keil MDK 进行keil界面设置。
测试验证
将示例程序烧写到您的EVB板后,用户可以通过使用应用程序的开发套件 仪表盘 ,测试导航信息传输、消息弹窗等是否正常。
EVB调试
为确保EVB可以正常使用,用户可以按照以下步骤进行操作:
准备一块EVB,确认EVB接口接线正确(UART-USB接线),让EVB进入下载模式,给EVB上电。
双击运行 MPTool.exe,具体操作步骤和文件路径参考 启动EVB。
在 Load Layout 处,点击 .. 加载
flash_map.ini
文件。点击 Browse 加载所有 烧录文件。
点击 User Data 添加用户资源root.bin。
点击 Detcet 成功识别EVB的COM口。
点击 Open 成功打开COM口。
点击 Download 进行烧录,进度条显示当前烧录进度。
进度条显示OK表示文件烧录成功。
程序烧录完成后,参考工作模式,使EVB进入工作模式。
用户可以通过以下两种方式来检测SoC端程序是否有正常运行:
- 方法一:
EVB会直接发出BLE广播,可以使用手机查看。手机设备通过安装Dashboard_V1.2.8.38.apk,搜索并连接BLE信号,其中开机屏幕显示状态 GUI 模拟器运行结果 (其中一帧画面)。
备注
广播名称:BLE_DASHBOARD,蓝牙地址:[25:63:87:4C:E0:01]
- 方法二:
打开Dashboard APK,点击 Connect Device ,进入BLE设备搜索界面,Dashboard APP搜索界面选择 BLE_DASHBOARD 进行连接,主界面蓝牙图标亮起,同时切换进等待导航模式,在APK中输入起点、终点位置,界面切入模拟导航状态。
日志抓取和分析
Dashboard导航界面EVB板接线请参考Log接线 。EVB板上电后,一旦出现 finish gui_mian 的Log,则代表设备界面初始化完成,屏幕正常刷图。点击手机Dashboard apk界面的 Connect Device ,Log打印 GAP adv stoped:because connection created,则代表手机与设备连接成功。
用户有修改flash map的需求,可以查阅Flash指南。
软件设计介绍
本章主要介绍RTL8772GWP Dashboard解决方案的软件相关技术参数和行为规范,为Dashboard的所有功能提供软件概述,用于指导Dashboard的开发和追踪软件测试中遇到的问题。
源代码目录
Project directory:
sdk\application\dashboard\proj
Source code directory:
sdk\application\dashboard\src
Dashboard SDK中的源文件目前分为以下几类:
└── Project: dashboard
└── app includes app main
├── menu_config.h
├── main.c
├── dashboard_init.c
├── flash_map.h
├── board.h
└── mem_config.h
└── Device includes startup code
├── startup_rtl.c
└── system_rtl.c
├── Lib includes all binary symbol files that user application is built on
├── Peripheral includes all peripheral drivers and module code used by the application
└── Profile includes BLE profiles or services used by the find my application
├── ancs_client.c
├── gatt_client.c
├── bas.c
├── dis.c
├── dfu_service.c
├── ota_service.c
└── tps.c tx power service
└── dfu includes the dashboard dfu application implementation
├── dfu_common.c
└── ble_dfu_transport.c
└── dfu_task includes the dashboard dfu task application implementation
├── dfu_main.c
├── dfu_task.c
└── dfu_app.c
├── rtk_gui includes all gui port
├── realgui/3rd includes Third-party library
├── realgui/app includes Realtek gui app
├── realgui/dc includes Realtek gui app
├── realgui/engine includes all gui engine
├── realgui/demo includes all gui demo
├── realgui/input includes all gui input
├── realgui/misc includes all gui misc
├── realgui/widget includes all gui widget
├── database includes all database
└── fs includes the file system
└── romfs.c
└── hal_drivers includes the dashboard hardware layer driver
├── drv_flash.c
├── drv_dlps.c
├── drv_lcd.c
└── drv_gpio.c
└── lcd_low_driver includes the dashboard hardware layer lcd driver
└── EK9716_800480_rgb.c
└── Compiler includes the dashboard compiler
├── syscall.c
└── syscall_mem.c
└── bt_app includes the dashboard bluetooth app
├── app_le_link_util.c
├── app_ble_bas.c
├── ancs.c
├── app_gap.c
├── app_task.c
├── his_ms.c
├── writband_private_service.c
├── app_ble_ota.c
├── app_ble_bwps.c
└── app_ble_dfu.c
├── communication includes all communication drivers and module code used by the application
└── findmy
Flash布局
应用程序默认的flash布局头文件: sdk\application\dashboard\proj\flash_map.h
Example layout with a total flash size of 1MB |
Size(byte) |
Start Address |
---|---|---|
Reserved |
4K |
0x04000000 |
OEM Header |
4K |
0x04001000 |
Bank0 Boot Patch |
32K |
0x04002000 |
Bank1 Boot Patch |
32K |
0x0400A000 |
OTA Bank0 |
1332K |
0x04012000 |
|
4K |
0x04012000 |
|
32K |
0x04013000 |
|
60K |
0x0401B000 |
|
212K |
0x0402A000 |
|
1008K |
0x0405F000 |
|
0K |
0x0415B000 |
|
0K |
0x0415B000 |
|
16K |
0x0415B000 |
|
0K |
0x0415F000 |
|
0K |
0x0415F000 |
|
0K |
0x0415F000 |
|
0K |
0x0415F000 |
OTA Bank1 |
0K |
0x040AC000 |
Bank0 Secure APP code |
16K |
0x0415F000 |
Bank0 Secure APP Data |
0K |
0x04163000 |
Bank1 Secure APP code |
0K |
0x04163000 |
Bank1 Secure APP Data |
0K |
0x04163000 |
OTA Temp |
1008K |
0x04163000 |
FTL |
16K |
0x0425F000 |
APP Defined Section1 |
8K |
0x04263000 |
APP Defined Section2 |
0K |
0x04265000 |
重要
如需调整Flash布局,请参考 快速入门 中 生成Flash Map 的步骤。
调整Flash布局后,必须使用新的
flash_map.ini
重新进行 生成OTA Header 和 生成System Config File 步骤。
软件架构
系统软件架构如下图所示:
Platform: 包括OTA、Flash、FTL 等。
IO Drivers: 提供对RTL8772GWP外设接口的应用层访问。
OSIF: 实时操作系统的抽象层。
GAP: 用户应用程序与BLE协议栈通信的抽象层。
/* Non-Secure main. */
int main(void)
{
DBG_DIRECT("Non-Secure World: main");
dashboard_system_clock_init();
dashboard_components_init();
dashboard_task_init();
/* Start scheduler. */
os_sched_start();
}
/*-----------------------------------------------------------*/
任务和优先级
RTL8772GWP默认使用RTOS,并在此基础上封装了一层OS if接口。 RTL8772GWP 内置timer task、idle task、flash task、BT controller stack task、BT host stack task,仪表盘应用程序有自己的app task、GUI task、communication task和CAN task。
Task |
描述 |
优先级 |
---|---|---|
Timer |
实现FreeRTOS所需的软件定时器 |
6 |
BT controller stack |
实现 HCI 以下的BLE协议栈 |
6 |
BT Host stack |
实现 HCI 以上的BLE协议栈 |
2 |
Flash |
处理异步flash请求 |
1 |
Idle |
运行后台任务,包括 DLPS |
0 |
App task |
通过消息队列和事件处理函数来处理不同的消息和事件,实现蓝牙功能的管理和控制 |
6 |
GUI task |
处理图像的生成,屏幕数据的传输 |
1 |
Communication task |
处理蓝牙交互通信 |
1 |
CAN task |
处理CAN通信 |
1 |
备注
可以创建多个应用任务,并相应地分配内存资源。
FreeRTOS提供Idle任务和Timer任务。
已使用 SysTick 中断将任务配置为根据其优先级进行抢占。
中断服务例程 (ISR) 已由供应商实施。
App task初始化相关代码入口:
void dashboard_task_init(void)
{
#if (RTK_BT_TASK == 1) || (RTK_BLE_TASK == 1)
bt_task_init();
#endif
#ifdef ENABLE_RTK_GUI
#ifdef ENABLE_RTK_GUI_SCRIPT_AS_A_APP
gui_server_init();
#else
gui_main();
#endif
#endif
}
void dashboard_system_clock_init(void)
{
uint32_t actual_mhz;
int32_t ret0, ret1, ret2, ret3;
ret0 = pm_cpu_freq_set(125, &actual_mhz);
ret1 = flash_nor_set_seq_trans_enable(FLASH_NOR_IDX_SPIC0, 1);
ret2 = fmc_flash_nor_clock_switch(FLASH_NOR_IDX_SPIC0, 160, &actual_mhz);
ret3 = flash_nor_try_high_speed_mode(0, FLASH_NOR_DTR_4_BIT_MODE);
APP_PRINT_INFO5("ret0 %d , ret1 %d , ret2 %d , ret3 %d actual_mhz %d", ret0, ret1, ret2, ret3,
actual_mhz);
fmc_psram_clock_switch(PSRAM_IDX_SPIC1, 160, &actual_mhz);
drv_dlps_exit_cbacks_register("psram", psram_winbond_opi_init);
pm_display_freq_set(CLK_PLL1_SRC, 100, 100);
DBG_DIRECT("finish dashboard_system_clock_init");
}
void dashboard_components_init(void)
{
#if (RTK_HAL_GPIO == 1)
hw_pin_init();
#endif
#if (RTK_HAL_UART == 1)
hw_uart_init();
#endif
#if (RTK_HAL_I2C == 1)
hw_i2c_init();
#endif
#if (RTK_HAL_ADC == 1)
hw_adc_init();
#endif
#if (RTK_HAL_LCD == 1)
hw_lcd_init();
#endif
#if (RTK_HAL_TOUCH == 1)
drv_touch_init();
#endif
#ifdef MODULE_VG_LITE
hw_gpu_init();
#endif
#if (RTK_HAL_RTC == 1)
hw_rtc_init();
#endif
#if (WS_USING_LETTER_SHELL == 1)
userShellInit();
#endif
#ifdef RTK_PRIVATE_SERVICE
communicate_task_init();
#endif // DEBUG
#if (RTK_HAL_DLPS == 1)
pwr_mgr_init();
#endif
}
APP 任务
APP任务实现的功能包括:
BLE相关状态由gap_handle_msg处理上层发送过来的消息,BLE数据发送和接收回调到APP创建服务时注册的读/写接口。
用户应用层自由实现对BLE行为的控制,包括广播开关、建立连接、更新连接参数等。
新增app task:app_dashboard_launcher 结构显示了两个基本元素:“thread_entry” 和 “ui_design”。
“ui_design”函数创建UI界面和所需的系统资源。
开发者可以将周期性执行的程序放在“thread_entry”或消息入口中来接来自其他任务的消息。
gui_app_t app_dashboard_launcher = { .screen = { .name = "app_dashboard_launcher", .x = 0, .y = 0, }, .thread_entry = app_dashboard_launcher_update_thread, .ui_design = app_dashboard_launcher_ui_design, }; gui_app_t *get_app_dashboard_launcher(void) { return &app_dashboard_launcher; }
GUI 任务
GUI 刷新由按键、计时器和其他应用程序行为触发。 在界面刷新之前,用户根据当前行为更新界面中各个控件的信息。 GUI核心部分根据每个控件的类型和位置在帧缓冲区中绘制。帧缓冲区可以是内部 RAM 或 PSRAM为了节省RAM资源,将一整帧FrameBuffer分割成多个部分(当前SDK中将10行作为一块,共分48块绘制),分段绘制并通过LCDC同步发送到屏幕。使用PSRAM时,首先将各部分更新为整帧PSRAM,然后将整个帧通过DPI直接发送到屏幕。
演示应用程序中的屏幕分辨率为 800*480,我们将创建两个 Window 小部件作为基础容器,以显示 BLE 连接和非连接状态下的信息。函数 app_dashboard_create_main_display()
和 app_dashboard_create_conn-ected_display()
将创建剩余的小部件并将它们分别附加到这两个窗口小部件。
Dashboard API接口 |
描述 |
---|---|
|
UI界面主入口 |
|
显示仪表盘主页信息 |
|
获取仪表盘主页信息 |
|
显示导航页面信息 |
|
初始化控件内容 |
|
更新控件内容 |
|
获取仪表盘信息 |
|
设置最新的电池数据 |
|
获取当前电池数据 |
|
设置当前小车速度 |
|
获取当前小车速度 |
|
设置当前小车转向信息 |
|
获取当前小车转向信息 |
|
设置当前蓝牙状态 |
|
获取当前蓝牙状态 |
|
设置当前导航状态 |
|
获取当前导航状态 |
|
更新当前导航状态 |
|
设置当前手机状态 |
|
获取当前手机状态 |
|
设置当前手机信息 |
|
设置当前message信息 |
|
获取当前message信息 |
|
更新当前message信息 |
|
设置当前时间信息 |
|
获取当前时间信息 |
|
设置当前tense信息 |
|
获取当前tense信息 |
|
设置拒绝和挂断手机 |
|
设置接听手机 |
|
更新主界面速度信息 |
|
更新主界面转向信息 |
|
更新主界面蓝牙连接信息 |
|
更新主界面手机信息 |
|
更新主界面message信息 |
GUI API接口 |
描述 |
---|---|
|
创建win窗口 |
|
查找并设置内存中的字体资源 |
|
设置窗口动画效果 |
|
设置图像对象的属性 |
|
创建image对象 |
|
创建文本对象 |
|
设置文本对象的文本模式 |
GUI控件相关更详细内容,请参考文档: https://docs.realmcu.com/Honeygui/cn/latest/widgets/index.html
void app_dashboard_launcher_ui_design(gui_app_t *app)
{
win_connected_display = gui_win_create(&(app->screen),
"win_connected_display", 0, 0, 800, 480);
win_main_display = gui_win_create(&(app->screen), "win_main_display", 0, 0, 800, 480);
gui_font_mem_init(HARMONYOS_SIZE28_BITS1_FONT_BIN);
gui_font_mem_init(HARMONYOS_SIZE32_BITS1_FONT_BIN);
gui_font_mem_init(HARMONYOS_SIZE56_BITS1_FONT_BIN);
app_dashboard_create_main_display(win_main_display);
app_dashboard_create_connected_display(win_connected_display);
win_main_display->base.not_show = false;
win_connected_display->base.not_show = true;
app_dashboard_initialize_data();
}
- 创建BLE非连接状态的控件app_dashboard_create_main_display 函数将依赖于 布局文件 将小部件分配给正确的协调。 我们将注册两个事件“obj_animate”来处理面板刷新前后的状态。 在此函数的最后,我们将调用每个小部件的刷新函数以向用户显示最新信息。
void app_dashboard_create_main_display(gui_win_t *target_main_display) { /* set update callback */ gui_win_set_animate(target_main_display, 1000, -1, paint_main_display_cb, target_main_display); /* set Image data */ dashboard_background = gui_img_create_from_mem(target_main_display, "dashboard_background", BACKGROUND_BIN, 0, 0, 800, 480); speed_high_digital = gui_img_create_from_mem(target_main_display, "speed_high_digital", SPED0_BIN, 360, 202, 40, 60); speed_low_digital = gui_img_create_from_mem(target_main_display, "speed_low_digital", SPED0_BIN, 406, 202, 40, 60); bluetooth_status = gui_img_create_from_mem(target_main_display, "bluetooth_status", BTOF_BIN, 48, 10, 23, 30); left_turn_light_status = gui_img_create_from_mem(target_main_display, "left_turn_light_status", TL_OF_BIN, 48, 208, 42, 40); right_turn_light_status = gui_img_create_from_mem(target_main_display, "right_turn_light_status", TR_OF_BIN, 710, 208, 42, 40); hour_high_digital = gui_img_create_from_mem(target_main_display, "hour_high_digital", TIMER0_BIN, 355, 12, 9, 16); hour_low_digital = gui_img_create_from_mem(target_main_display, "hour_low_digital", TIMER0_BIN, 366, 12, 9, 16); min_high_digital = gui_img_create_from_mem(target_main_display, "min_high_digital", TIMER0_BIN, 387, 12, 9, 18); min_low_digital = gui_img_create_from_mem(target_main_display, "min_low_digital", TIMER0_BIN, 399, 12, 9, 18); tense_high_digital = gui_img_create_from_mem(target_main_display, "tense_high_digital", APM_A_BIN, 417, 12, 9, 18); tense_low_digital = gui_img_create_from_mem(target_main_display, "tense_low_digital", APM_M_BIN, 432, 12, 9, 18); bat_high_digital = gui_img_create_from_mem(target_main_display, "bat_high_digital", TIMER0_BIN, 394, 360, 9, 18); bat_low_digital = gui_img_create_from_mem(target_main_display, "bat_low_digital", TIMER0_BIN, 406, 360, 9, 18); tel_box = gui_img_create_from_mem(target_main_display, "tel_box", TELBOX_BIN, 253, 410, 295, 49); tel_accept = gui_img_create_from_mem(target_main_display, "tel_accept", TELBOX_BIN, 253, 410, 295, 49); tel_reject_end = gui_img_create_from_mem(target_main_display, "tel_reject_end", TELBOX_BIN, 253, 410, 295, 49); refuse_button = gui_img_create_from_mem(target_main_display, "refuse_button", REFUS_BIN, 263, 416, 36, 36); ans_button = gui_img_create_from_mem(target_main_display, "ans_button", ANS_BIN, 500, 416, 36, 36); tel_box_left_button = gui_img_create_from_mem(target_main_display, "tel_box_left_button", SYMB1_BIN, 315, 416, 36, 36); tel_box_right_button = gui_img_create_from_mem(target_main_display, "tel_box_right_button", SYMB2_BIN, 479, 416, 36, 36); dashboard_pointer = gui_img_create_from_mem(target_main_display, "dashboard_Cpointer", DASHBOARD_0_BIN, 243, 84, 0, 0); short_message = gui_img_create_from_mem(target_main_display, "short_message", MESSAGE_BIN, 221, 0, 359, 80); /* set font data */ app_phone_data current_phone_status; app_dashboard_data_get_phone_status(¤t_phone_status); short_tel_number = gui_text_create(target_main_display, "short_tel_number", 322, 415, 158, 30); memcpy(&show_tel_number[0], ¤t_phone_status.current_phone_number[0], current_phone_status.current_phone_number_len); gui_text_set(short_tel_number, (char *)show_tel_number, GUI_FONT_SRC_BMP, gui_rgb(UINT8_MAX, UINT8_MAX, UINT8_MAX), current_phone_status.current_phone_number_len, 28); gui_text_mode_set(short_tel_number, CENTER); short_tel_accept = gui_text_create(target_main_display, "short_tel_accept", 360, 415, 800, 30); gui_text_set(short_tel_accept, "calling", GUI_FONT_SRC_BMP, gui_rgb(UINT8_MAX, UINT8_MAX, UINT8_MAX), 7, 32); app_message_data current_message_status; app_dashboard_data_get_message_data_update(¤t_message_status); memcpy(&show_message_data[0], ¤t_message_status.wechat_msg[0], current_message_status.wechat_msg_len); short_message_data = gui_text_create(target_main_display, "short_message_data", 300, 10, 240, 50); gui_text_set(short_message_data, (char *)show_message_data, GUI_FONT_SRC_BMP, gui_rgb(UINT8_MAX, UINT8_MAX, UINT8_MAX), (current_message_status.wechat_msg_len - 1), 32); gui_text_mode_set(short_message_data, MULTI_LEFT); /* Prepare the intial data */ app_dashboard_update_main_display_time_info(); app_dashboard_update_main_display_tense_apm_info(app_dashboard_data_get_tense_timer_info()); app_dashboard_update_main_display_battery_info(app_dashboard_data_get_battery_level()); app_dashboard_update_main_display_speed_info(app_dashboard_data_get_car_speed()); app_dashboard_update_main_display_turn_info(app_dashboard_data_get_car_turn_info()); app_dashboard_update_main_display_bluetooth_info(app_dashboard_data_get_bluetooth_status()); app_dashboard_update_main_display_phone_infor(¤t_phone_status); app_dashboard_update_main_display_message_infor(¤t_message_status); }
BLE连接状态布局
void app_dashboard_create_connected_display(gui_win_t *target_connected_display) { /* set update callback */ gui_win_set_animate(target_connected_display, 1000, -1, paint_connected_display_cb, target_connected_display); /* set Image data */ dashboard_c_background = gui_img_create_from_mem(target_connected_display, "dashboard_c_background", BACKGROUND_C_BIN, 0, 0, 800, 480); speed_high_c_digital = gui_img_create_from_mem(target_connected_display, "speed_high_c_digital", SPED_C0_BIN, 214, 232, 27, 40); speed_low_c_digital = gui_img_create_from_mem(target_connected_display, "speed_low_c_digital", SPED_C0_BIN, 251, 232, 27, 40); hour_high_c_digital = gui_img_create_from_mem(target_connected_display, "hour_high_digital", TIMER0_BIN, 355, 12, 9, 16); hour_low_c_digital = gui_img_create_from_mem(target_connected_display, "hour_low_digital", TIMER0_BIN, 366, 12, 9, 16); min_high_c_digital = gui_img_create_from_mem(target_connected_display, "min_high_digital", TIMER0_BIN, 387, 12, 9, 18); min_low_c_digital = gui_img_create_from_mem(target_connected_display, "min_low_digital", TIMER0_BIN, 399, 12, 9, 18); tense_high_c_digital = gui_img_create_from_mem(target_connected_display, "tense_high_c_digital", APM_CA_BIN, 417, 12, 9, 18); tense_low_c_digital = gui_img_create_from_mem(target_connected_display, "tense_low_c_digital", APM_CM_BIN, 432, 12, 9, 18); bat_high_c_digital = gui_img_create_from_mem(target_connected_display, "bat_high_c_digital", TIMER0_BIN, 238, 371, 9, 18); bat_low_c_digital = gui_img_create_from_mem(target_connected_display, "bat_low_c_digital", TIMER0_BIN, 250, 371, 9, 18); left_turn_light_c_status = gui_img_create_from_mem(target_connected_display, "left_turn_light_c_status", TL_OF_C_BIN, 110, 10, 42, 40); right_turn_light_c_status = gui_img_create_from_mem(target_connected_display, "right_turn_light_c_status", TR_OF_C_BIN, 665, 10, 42, 40); navi_c_status = gui_img_create_from_mem(target_connected_display, "navi_c_status", NAVI_C1_BIN, 505, 170, 268, 218); refuse_c_button = gui_img_create_from_mem(target_connected_display, "refuse_c_button", REFUS_BIN, 502, 410, 36, 36); ans_c_button = gui_img_create_from_mem(target_connected_display, "ans_c_button", ANS_BIN, 740, 410, 36, 36); tel_box_left_c_button = gui_img_create_from_mem(target_connected_display, "tel_box_left_c_button", SYMB1_BIN, 554, 414, 36, 36); tel_box_right_c_button = gui_img_create_from_mem(target_connected_display, "tel_box_right_c_button", SYMB2_BIN, 718, 414, 36, 36); dashboard_Cpointer = gui_img_create_from_mem(target_connected_display, "dashboard_Cpointer", DASHBOARD_C2_BIN, 110, 133, 9, 18); short_c_message = gui_img_create_from_mem(target_connected_display, "short_c_message", MESSAGE_BIN, 221, 0, 359, 80); /* set font data */ app_navi_data current_navi_data; app_dashboard_data_get_navi_data_update(¤t_navi_data); memcpy(&show_c_navigation_msg[0], ¤t_navi_data.navigation_msg[0], current_navi_data.navigation_num_len); short_c_navi_message_1 = gui_text_create(target_connected_display, "short_c_navi_message_1", 490, 88, 150, 60); gui_text_set(short_c_navi_message_1, (char *)show_c_navigation_msg, GUI_FONT_SRC_BMP, gui_rgb(UINT8_MAX, UINT8_MAX, UINT8_MAX), current_navi_data.navigation_num_len, 56); gui_text_mode_set(short_c_navi_message_1, RIGHT); memcpy(&show_c_navigation_unit[0], ¤t_navi_data.navigation_unit[0], current_navi_data.navigation_unit_len); short_c_navi_message_3 = gui_text_create(target_connected_display, "short_c_navi_message_3", 640, 108, 150, 40); gui_text_set(short_c_navi_message_3, (char *)show_c_navigation_unit, GUI_FONT_SRC_BMP, gui_rgb(0xcc, 0xcc, 0xcc), current_navi_data.navigation_unit_len, 32); gui_text_mode_set(short_c_navi_message_3, LEFT); memcpy(&show_c_road_names[0], ¤t_navi_data.road_names[0], current_navi_data.road_num_len); short_c_navi_message_2 = gui_text_create(target_connected_display, "short_c_navi_message_2", 490, 148, 300, 40); gui_text_set(short_c_navi_message_2, (char *)show_c_road_names, GUI_FONT_SRC_BMP, gui_rgb(0xcc, 0xcc, 0xcc), current_navi_data.road_num_len, 32); gui_text_mode_set(short_c_navi_message_2, CENTER); app_phone_data current_phone_status; app_dashboard_data_get_phone_status(¤t_phone_status); short_c_tel_number = gui_text_create(target_connected_display, "short_c_tel_number", 560, 410, 158, 30); memcpy(&show_c_tel_number[0], ¤t_phone_status.current_phone_number[0], current_phone_status.current_phone_number_len); gui_text_set(short_c_tel_number, (char *)show_c_tel_number, GUI_FONT_SRC_BMP, gui_rgb(UINT8_MAX, UINT8_MAX, UINT8_MAX), current_phone_status.current_phone_number_len, 28); gui_text_mode_set(short_c_tel_number, CENTER); short_c_tel_accept = gui_text_create(target_connected_display, "short_c_tel_accept", 600, 410, 800, 30); gui_text_set(short_c_tel_accept, "calling", GUI_FONT_SRC_BMP, gui_rgb(UINT8_MAX, UINT8_MAX, UINT8_MAX), 7, 28); app_message_data current_message_status; app_dashboard_data_get_message_data_update(¤t_message_status); memcpy(&show_c_message_data[0], ¤t_message_status.wechat_msg[0], current_message_status.wechat_msg_len); short_c_message_data = gui_text_create(target_connected_display, "short_c_message_data", 300, 10, 240, 50); gui_text_set(short_c_message_data, (char *)show_c_message_data, GUI_FONT_SRC_BMP, gui_rgb(UINT8_MAX, UINT8_MAX, UINT8_MAX), (current_message_status.wechat_msg_len - 1), 32); gui_text_mode_set(short_c_message_data, MULTI_LEFT); /* Prepare the intial data */ app_dashboard_update_connected_display_time_info(); app_dashboard_update_connected_display_tense_apm_info(app_dashboard_data_get_tense_timer_info()); app_dashboard_update_connected_display_battery_info(app_dashboard_data_get_battery_level()); app_dashboard_update_connected_display_speed_info(app_dashboard_data_get_car_speed()); app_dashboard_update_connected_display_turn_info(app_dashboard_data_get_car_turn_info()); app_dashboard_update_connected_display_phone_infor(¤t_phone_status); app_dashboard_update_connected_display_message_infor(¤t_message_status); }
BLE通信任务
通信任务负责BLE数据传输的管理,根据当前传输数据命令格式将数据打包并通过蓝牙协议栈发送出去。 客户一般使用自己的蓝牙数据交互格式,任务可以作为发送方法的参考,也可以自己实现。开发者必须参考 Bluetooth,熟悉BLE服务结构以及如何处理消息。在本设计文档中,我们将通过仪表板应用程序的指定 BLE 服务作为演示。开发人员可以根据自己的产品需求选择手机应用程序采用哪些 BLE 服务。
移动应用程序成功连接到仪表板设备后,应用程序可以使用 BLE 写入请求向设备传送必要的信息,所有消息都将通过特征写入。
/** * @brief write characteristic data from stack. * * @param ServiceId ServiceId generated when register to upper stack. * @param iAttribIndex Attribute index of getting characteristic data. * @param wLength length of data to be written. * @param pValue pointer of data to be written. * @return TProfileResult profile procedure results. */ T_APP_RESULT bwps_service_attr_write_cb(uint8_t conn_id, T_SERVER_ID service_id, uint16_t attrib_index, T_WRITE_TYPE write_type, uint16_t length, uint8_t *p_value, P_FUN_WRITE_IND_POST_PROC *p_write_ind_post_proc) { T_APP_RESULT wCause = APP_RESULT_SUCCESS; uint8_t *buf = NULL; APP_PRINT_INFO2("bwps_service_attr_write_cb: attrib_index = %d, data %b", attrib_index, CE_BINARY(length, p_value)); if (p_value == NULL) { APP_PRINT_ERROR0("bwps_service_attr_write_cb, p_value is NULL"); wCause = APP_RESULT_INVALID_VALUE_SIZE; return wCause; } switch (attrib_index) { default: wCause = APP_RESULT_ATTR_NOT_FOUND; APP_PRINT_ERROR2("bwps_service_attr_write_cb Error: attrib_index 0x%x, length %d", attrib_index, length); break; case GATT_SRV_BWPS_TX_INDEX: APP_PRINT_INFO0("bwps_service_attr_write_cb, GATT_SRV_BWPS_TX_INDEX"); /* copy gatt write value in tx_buffer, the first byte is value length*/ #if 1 buf = malloc((length + 1) * sizeof(uint8_t)); buf[0] = length; memcpy(buf + 1, p_value, length); extern void *raw_data_receive_queue_handle; if (os_msg_send(raw_data_receive_queue_handle, &buf, 0) == false) { APP_PRINT_ERROR0("send_msg_to_l1send_task_fail"); free(buf); } #endif break; case GATT_SRV_BWPS_DEVNAME_INDEX: APP_PRINT_INFO0("BWPS: update device name\n"); break; } return wCause; }
所有收到的消息都会发布到“raw_data_receive_queue_handle”队列, 它将在 API“resolve_remote_data”中处理。
void l1receive_task(void *pvParameters) { uint8_t *buf = NULL; os_alloc_secure_ctx(1024); os_msg_queue_create(&raw_data_receive_queue_handle, "rec_raw_data", 0x10, sizeof(void *)); while (true) { if (os_msg_recv(raw_data_receive_queue_handle, &buf, 0xFFFFFFFF) == true) { //rt_kprintf("buf2 = 0x%x \n", buf); resolve_remote_data(buf + 1, buf[0]); free(buf); } } }
一旦接收到的数据通过了数据完整性检查,API“L2_frame_resolve”将处理该请求。 在仪表盘应用程序中,开发人员只需处理“NAVIGATION_INFORMATION_ID”、“NOTIFY_COMMAND_ID”和“CONTROL_COMMAND_ID”,3种类型数据。
bool L2_frame_resolve(uint8_t *pData, uint16_t length) { if ((pData == NULL) || (length == 0)) { return false; } WRISTBAND_COMMUNICATE_COMMAND command_id = (WRISTBAND_COMMUNICATE_ COMMAND)pData[0]; uint8_t first_key = pData[2]; uint16_t first_value_length = (((pData[3] << 8) | pData[4]) & 0x1FF); APP_PRINT_INFO1("L2_frame_resolve length is %d", length); switch (command_id) { case NAVIGATION_INFORMATION_ID: { APP_PRINT_INFO1("NAVIGATION_INFORMATION_ID, KEY = 0x%x", first_key); APP_PRINT_INFO1("NAVIGATION_INFORMATION_ID,length is %d", length); APP_PRINT_INFO1("RECEIVE navi info is %b", TRACE_BINARY(length, (uint8_t *)pData + L2_FIRST_VALUE_POS)); resolve_Navigation_command(first_key, pData + L2_FIRST_VALUE_POS, first_value_length); } break; case NOTIFY_COMMAND_ID: { APP_PRINT_INFO2("NOTIFY_COMMAND_ID, KEY = 0x%x, info= 0x%x", first_key, pData[5]); resolve_Notify_command(first_key, pData + L2_FIRST_VALUE_POS, first_value_length, pData[5]); } break; case CONTROL_COMMAND_ID: { APP_PRINT_INFO1("CONTROL_COMMAND, KEY = 0x%x", first_key); resolve_Control_command(first_key, pData + L2_FIRST_VALUE_POS, first_value_length); } break; default: break; } return true; }
每当BLE收到来自移动应用程序的消息时,它会将数据更新到仪表盘数据容器,然后UI更新线程将获取最新信息并刷新显示。
CAN 通信任务
RTL8772GWP CAN 外接 USB CAN FD 分析仪,通过 PC 端收发数据与 CAN controller 进行通信,在 CAN中断中接收 PC(类似外部信号)下发的数据。
canopen单独创建了3个task:
send task
receive task
canopen (canfestival) task
备注
其中canopen task里面都是APP层的行为,基于canopen (canfestival)协议的行为(包括主节点、从节点之间的交互)用户可根据自己的私用协议重新定义该task。
- Send task流程
一旦有数据需要发送,立马放到一个loop buffer,send task会实时监控loop buffer是否有数据。
- Receive task流程
一旦硬件can模块有数据,会触发can_handler,在handler里用basic rx / dma rx两种方式之一,把数据搬运出来,然后通过canfestival的API,送到通信协议层,针对收到的msg作出对应的行为。
引脚定义
#define CAN_TX_PIN P1_4 #define CAN_RX_PIN P1_6
CAN 发送任务
/** * @brief canSend task to send CANFestival messages. * @param p_param: Parameters sending to the task * @return void */ void canSend_Task(void *p_param) { static CAN_TxMsgDef CO_TxMsg; #if CAN_RX_FIFO_EN uint8_t TxMsgBuf_MaxCnt = 12; //when Rx_FIFO enable, only index_0~11 MBs can be used for Tx. #else uint8_t TxMsgBuf_MaxCnt = 16; #endif while (true) { /*wait for CANOpen tx message*/ if (!loop_queue_is_empty(p_canSend_queue)) { if (true == loop_queue_read_buf(p_canSend_queue, &CO_TxMsg, sizeof(CAN_TxMsgDef))) { for (uint8_t TxMsgBufId = 0; TxMsgBufId < CAN_MESSAGE_BUFFER_MAX_CNT; TxMsgBufId++) { /* if the MB is idle, then it can send the data. */ FlagStatus Tx_req = CAN_GetMBnStatusTxReqFlag(TxMsgBufId); FlagStatus Rx_vld = CAN_GetMBnStatusRxValidFlag(TxMsgBufId); FlagStatus Rx_rdy = CAN_GetMBnStatusRxReadyFlag(TxMsgBufId); if ((Tx_req == RESET) && (Rx_vld == RESET) && (Rx_rdy == RESET)) { CO_TxMsg.CO_TxFrame.msg_buf_id = TxMsgBufId; CANError_TypeDef tx_error = CAN_SetMsgBufTxMode(&CO_TxMsg.CO_TxFrame, CO_TxMsg.data, CO_TxMsg.dlc); CAN_MBTxINTConfig(CO_TxMsg.CO_TxFrame.msg_buf_id, ENABLE); break; } } } } } }
CAN 接收任务
void canReceive_Task(void *p_param) { static CAN_RxMsgDef Rx_msg; static Message msg; while (true) { /*wait for CANOpen rx message*/ if (!loop_queue_is_empty(p_canReceive_queue)) { if (true == loop_queue_read_buf(p_canReceive_queue, &Rx_msg, sizeof(CAN_RxMsgDef))) { msg.cob_id = Rx_msg.mb_info.standard_frame_id; msg.rtr = Rx_msg.mb_info.rtr_bit; msg.len = Rx_msg.mb_info.data_length; memcpy(msg.data, Rx_msg.data, sizeof(Rx_msg.data)); for (uint8_t index = 0; index < msg.len; index++) { APP_PRINT_INFO2("[CAN] CO_RxMsg.data[%d] 0x%02X", index, msg.data[index]); } APP_PRINT_INFO1("msg.cob_id = 0x%x", msg.cob_id); /*use CANOpen API*/ TIM_INTConfig(CANOPEN_TIMx, DISABLE); #if CANOPEN_ROLE_MASTER_SUPPORT canDispatch(&Master_Data, &msg); #else canDispatch(&slave_all_Data, &msg); #endif TIM_INTConfig(CANOPEN_TIMx, ENABLE); } } } }
常见问题
图形数据加载过程
从外部Flash加载程序和绘制数据到PSRAM,并将绘制数据显示到TFT,数据通路 如下所示。
用户资源显示文件处理:图形资源使用image convert tool处理,输出图片二进制文件,包换图片属性header(图片尺寸、是否压缩、颜色格式(RGB565、RGB888等)),制作User Data:root.bin。
下载flash区域:使用MPtool工具烧录进EVB XIP flash区域。
Flash内数据读取:采用QSPI,80MHz带宽,双边沿数据(DTR)读取方式。
分配内存:在加载图像数据之前,需要根据图像的尺寸和格式来分配足够的内存来存储图像像素数据(SRAM)。
压缩图形像素处理:GUI image控件通过图片名的方式索引文件在flash中地址,判断是否压缩,压缩需解压处理,从flash经DMA搬运至SRAM,通过PPE像素处理引擎,处理pixel并存储在之前分配的内存中(SRAM)。
未压缩图形像素处理:GUI image控件通过图片名的方式索引文件在flash中地址,判断是否压缩,未压缩图片直接从flash经DMA搬运至SRAM经过PPE处理,并存储在SRAM。
备注
PPE处理未压缩数据,压缩数据需解压后,才能经过PPE处理。
TFT显示:PPE处理后pixel经DMA搬运到PSRAM缓存,最后经LCDC(OPI)显示在屏幕上。
PPE1.0性能衡量
PPE功能:Blend,单位 ms, Destination 固定格式RGB565,屏幕尺寸 480 by 480, 误差不超过180us。
PPE:RGB565 Bypass模式数据 (Source to Destination),unit:ms
SRAM
PSRAM
FLASH
7.178
7.18
PSRAM
5.64
13.524
SRAM
3.426
5.228
PPE:RGB565 source over模式数据 (Source to Destination),unit:ms
SRAM
PSRAM
FLASH
7.474
13.642
PSRAM
5.918
20.846
SRAM
5.04
13.528
PPE:RGB8565 Bypass模式数据 (Source to Destination),unit:ms
SRAM
PSRAM
FLASH
11.518
11.52
PSRAM
8.754
16.932
SRAM
4.488
5.23
PPE:RGB8565 source over模式数据 (Source to Destination),unit:ms
SRAM
PSRAM
FLASH
11.775
14.546
PSRAM
8.99
24.11
SRAM
5.775
13.528
PPE功能:Scale,单位 ms,Destination 固定格式RGB565, 误差不超过80us。
PPE:480 * 480缩小2倍至240 * 240 (Source to Destination),unit:ms
SRAM
PSRAM
FLASH
3.762
3.758
PSRAM
2.891
4.83
SRAM
1.54
1.493
PPE:240 * 240 放大2倍至480 * 480 (Source to Destination),unit:ms
SRAM
PSRAM
FLASH
16.119
16.119
PSRAM
7.78
13.507
SRAM
3.214
5.167
Port适配LCD
LCD屏幕适配需要根据屏幕缓存情况适配,本样例默认条件是PSRAM作为屏幕缓存,选择GRAM作为屏幕缓存请参考 GRAM 小节。
RAMLESS
屏幕分辨率信息配置(参考屏幕规格书),主要配置LCD宽度、高度、像素深度信息。以示例为例,修改在
EK9716_800480_rgb.h
头文件中的对应参数。
#define EK9716_800480_LCD_WIDTH 800 //Screen width #define EK9716_800480_LCD_HEIGHT 480 //Screen Height #define EK9716_DRV_PIXEL_BITS 16 //Pixel Depth
需要配置section高度,高度选择根据实际情况而定,在示例工程中配置LCD_SECTION_HEIGHT = 10,对应参数可以在
gui_port_dc.c
中修改。
#define LCD_SECTION_HEIGHT 10
PSRAM作为屏幕缓存需要配置地址信息。在
gui_port_dc.c
中,为framebuffer配置PSRAM缓存的示例如下:
static uint32_t video_buffer1 = SPIC1_ADDR; static uint32_t video_buffer2 = (SPIC1_ADDR + EK9716_800480_LCD_WIDTH * EK9716_800480_LCD_HEIGHT * 2); static uint32_t current_buffer = SPIC1_ADDR;
API
说明
memcpy_by_dma_start()
DMA开始搬运数据
memcpy_by_dma_done()
等待DMA搬运数完成
LCD驱动层其他接口API,对应
gui_port_dc.c
。 API
说明
drv_lcd_get_screen_width()
获取屏幕宽度
drv_lcd_get_screen_height()
获取屏幕高度
drv_lcd_get_pixel_bits()
获取屏幕像素深度
port_gui_lcd_power_on()
屏幕启动
port_gui_lcd_power_off()
屏幕关闭
gui_port_dc_init()
初始化LCD
port_gui_lcd_update()
更新LCD的每一个区段(segment)
drv_lcd_update()
将指定缓存区传输到屏幕
将帧缓冲区的内容更新的LCD屏幕上,使用
port_gui_lcd_update()
接口,默认情况屏幕搭配PSRAM作为帧缓冲区。void port_gui_lcd_update(struct gui_dispdev *dc) { uint32_t i = dc->section_count; uint32_t total_section_cnt = (drv_lcd_get_screen_height() / LCD_SECTION_HEIGHT + (( drv_lcd_get_screen_height() % LCD_SECTION_HEIGHT) ? 1 : 0)); uint32_t section = (drv_lcd_get_screen_height() / LCD_SECTION_HEIGHT + (( drv_lcd_get_screen_height() % LCD_SECTION_HEIGHT) ? 1 : 0)); #if 0 //gram if (i == 0) { drv_lcd_set_window(0, dc->fb_height * dc->section_count, dc->fb_width, dc->fb_height); drv_lcd_start_transfer(dc->frame_buf, dc->fb_width * dc->fb_height); } else if (i == section - 1) { uint32_t last_height = dc->screen_height - dc->section_count * dc->fb_height; drv_lcd_transfer_done(); drv_lcd_set_window(0, dc->fb_height * dc->section_count, dc->fb_width, last_height); drv_lcd_start_transfer(dc->frame_buf, dc->fb_width * last_height); drv_lcd_transfer_done(); } else { drv_lcd_transfer_done(); drv_lcd_set_window(0, dc->fb_height * dc->section_count, dc->fb_width, dc->fb_height); drv_lcd_start_transfer(dc->frame_buf, dc->fb_width * dc->fb_height); } #else //psram void *dst = (void *)(current_buffer + i * dc->fb_width * dc->fb_height * source_bytes_per_pixel); if (i == 0) { memcpy_by_dma_start(dst, dc->frame_buf, dc->fb_width * dc->fb_height * source_bytes_per_pixel); } else if (i == section - 1) { uint32_t last_height = dc->screen_height - dc->section_count * dc->fb_height; memcpy_by_dma_done(); memcpy_by_dma_start(dst, dc->frame_buf, dc->fb_width * last_height * source_bytes_per_pixel); memcpy_by_dma_done(); drv_lcd_update((uint8_t *)current_buffer, 0, 0, drv_lcd_get_screen_width(), drv_lcd_get_screen_height()); if (current_buffer == video_buffer1) { current_buffer = video_buffer2; } else if (current_buffer == video_buffer2) { current_buffer = video_buffer1; } else { current_buffer = video_buffer1; } } else { memcpy_by_dma_done(); memcpy_by_dma_start(dst, dc->frame_buf, dc->fb_width * dc->fb_height * source_bytes_per_pixel); } #endif }
GRAM
相较于PSRAM,GRAM读写速度更慢。因此,对于需要频繁更新显示内容的场景(如动画或视频播放)而言 ,选用GRAM可能不太适合。下面解释屏幕更新所需要的几个关键函数:
等待前面section搬运结束:中间层API接口
rtk_lcd_hal_transfer_done()
,底层实现rtk_lcd_hal_transfer_done()
。
void rtk_lcd_hal_transfer_done(void) { LCDC_HANDLER_DMA_FIFO_CTRL_t handler_reg_0x18; do { handler_reg_0x18.d32 = LCDC_HANDLER->DMA_FIFO_CTRL; } while (handler_reg_0x18.b.dma_enable != RESET); LCDC_HANDLER_OPERATE_CTR_t handler_reg_0x14; do { handler_reg_0x14.d32 = LCDC_HANDLER->OPERATE_CTR; } while (handler_reg_0x14.b.auto_write_start != RESET); LCDC_ClearDmaFifo(); // clear DMA FIFO LCDC_ClearTxPixelCnt(); // clear the pixel counter for the transfer LCDC_AXIMUXMode(LCDC_FW_MODE); // switch AXI MUX mode to FW mode LCDC_Cmd(DISABLE); // disable LCD controller }
设置绘制区域:中间层API接口
rtk_lcd_hal_set_window()
,对应底层实现rtk_lcd_hal_set_window()
。
void rtk_lcd_hal_set_window(uint16_t xStart, uint16_t yStart, uint16_t w, uint16_t h) { uint8_t data[4]; uint16_t xEnd = xStart + w - 1; uint16_t yEnd = yStart + h - 1; xStart = xStart + 0x14; xEnd = xEnd + 0x14; data[0] = xStart >> 8; data[1] = xStart & 0xff; data[2] = xEnd >> 8; data[3] = xEnd & 0xff; incna3311_cmd_param4(0x2A, data); data[0] = yStart >> 8; data[1] = yStart & 0xff; data[2] = yEnd >> 8; data[3] = yEnd & 0xff; incna3311_cmd_param4(0x2B, data); uint32_t len_byte = (xEnd - xStart + 1) * (yEnd - yStart + 1) * OUTPUT_PIXEL_BYTES; incna3311_enter_data_output_mode(len_byte); }
从帧缓存区搬运开始:中间层API接口
rtk_lcd_hal_start_transfer()
,对应底层实现rtk_lcd_hal_start_transfer()
。
void rtk_lcd_hal_start_transfer(uint8_t *buf, uint32_t len) { #if (DMA_LINKLIST == 0) //无DMA链表 LCDC_DMA_InitTypeDef LCDC_DMA_InitStruct = {0}; LCDC_DMA_StructInit(&LCDC_DMA_InitStruct); LCDC_DMA_InitStruct.LCDC_DMA_ChannelNum = LCDC_DMA_CHANNEL_NUM; LCDC_DMA_InitStruct.LCDC_DMA_DIR = 4; LCDC_DMA_InitStruct.LCDC_DMA_SourceInc = LCDC_DMA_SourceInc_Inc; LCDC_DMA_InitStruct.LCDC_DMA_DestinationInc = LCDC_DMA_DestinationInc_Fix; LCDC_DMA_InitStruct.LCDC_DMA_SourceDataSize = LCDC_DMA_DataSize_Word; LCDC_DMA_InitStruct.LCDC_DMA_DestinationDataSize = LCDC_DMA_DataSize_Word; LCDC_DMA_InitStruct.LCDC_DMA_SourceMsize = LCDC_DMA_Msize_8; LCDC_DMA_InitStruct.LCDC_DMA_DestinationMsize = LCDC_DMA_Msize_8; LCDC_DMA_InitStruct.LCDC_DMA_SourceAddr = (uint32_t)buf; LCDC_DMA_InitStruct.LCDC_DMA_Multi_Block_En = 0; LCDC_DMA_Init(LCDC_DMA_CHANNEL_INDEX, &LCDC_DMA_InitStruct); #else LCDC_DMA_InitTypeDef LCDC_DMA_InitStruct = {0}; LCDC_DMA_StructInit(&LCDC_DMA_InitStruct); LCDC_DMA_InitStruct.LCDC_DMA_ChannelNum = LCDC_DMA_CHANNEL_NUM; LCDC_DMA_InitStruct.LCDC_DMA_DIR = 4; LCDC_DMA_InitStruct.LCDC_DMA_SourceInc = LCDC_DMA_SourceInc_Inc; LCDC_DMA_InitStruct.LCDC_DMA_DestinationInc = LCDC_DMA_DestinationInc_Fix; LCDC_DMA_InitStruct.LCDC_DMA_SourceDataSize = LCDC_DMA_DataSize_Word; LCDC_DMA_InitStruct.LCDC_DMA_DestinationDataSize = LCDC_DMA_DataSize_Word; LCDC_DMA_InitStruct.LCDC_DMA_SourceMsize = LCDC_DMA_Msize_8; LCDC_DMA_InitStruct.LCDC_DMA_DestinationMsize = LCDC_DMA_Msize_8; LCDC_DMA_InitStruct.LCDC_DMA_SourceAddr = 0; LCDC_DMA_InitStruct.LCDC_DMA_Multi_Block_Mode = LLI_TRANSFER;//LLI_TRANSFER or LLI_WITH_CONTIGUOUS_SAR LCDC_DMA_InitStruct.LCDC_DMA_Multi_Block_En = 1; LCDC_DMA_InitStruct.LCDC_DMA_Multi_Block_Struct = LCDC_DMA_LINKLIST_REG_BASE + 0x50; LCDC_DMA_Init(LCDC_DMA_CHANNEL_INDEX, &LCDC_DMA_InitStruct); LCDC_SET_GROUP1_BLOCKSIZE(ICNA3311_LCD_WIDTH * INPUT_PIXEL_BYTES); LCDC_SET_GROUP2_BLOCKSIZE(ICNA3311_LCD_WIDTH * INPUT_PIXEL_BYTES); /*16 pixel aligned for GPU*/ LCDC_DMALLI_InitTypeDef LCDC_DMA_LLI_Init = {0}; LCDC_DMA_LLI_Init.g1_source_addr = (uint32_t)buf; LCDC_DMA_LLI_Init.g1_sar_offset = ICNA3311_LCD_WIDTH * INPUT_PIXEL_BYTES * 2; LCDC_DMA_LLI_Init.g2_source_addr = (uint32_t)(buf + ICNA3311_LCD_WIDTH * INPUT_PIXEL_BYTES); LCDC_DMA_LLI_Init.g2_sar_offset = ICNA3311_LCD_WIDTH * INPUT_PIXEL_BYTES * 2; LCDC_DMA_LinkList_Init(&LCDC_DMA_LLI_Init, &LCDC_DMA_InitStruct);//LLI_TRANSFER or LLI_WITH_CONTIGUOUS_SAR #endif LCDC_ClearDmaFifo(); // 清空 DMA FIFO LCDC_ClearTxPixelCnt(); // 清零传输的像素计数器 LCDC_SwitchMode(LCDC_AUTO_MODE); // 切换 LCD 控制器到自动模式 LCDC_SwitchDirect(LCDC_TX_MODE); // 切换 LCD 控制器到发送模式 LCDC_SetTxPixelLen(len); // 设置发送的像素数目 LCDC_Cmd(ENABLE); // 使能 LCD 控制器 #if DMA_LINKLIST LCDC_DMA_MultiBlockCmd(ENABLE); // 在 DMA 链表模式下,使能多块传输 #endif LCDC_DMAChannelCmd(LCDC_DMA_CHANNEL_NUM, ENABLE); // 使能 DMA 通道 LCDC_DmaCmd(ENABLE); // 使能 DMA #if (TE_VALID == 1) LCDC_TeCmd(ENABLE); // 如果 TE 有效,则使能 TE (Transfer End) 信号 #endif #if (TE_VALID == 0) LCDC_AutoWriteCmd(ENABLE); // 如果 TE 无效,则使能自动写入命令 #endif }
- Te信号配置,防止屏幕撕裂。硬件带TE信号引脚,配置引脚为下降沿触发,同时在te回调函数中,修改te标志位。
屏幕参数配置,完全参照PSRAM作为屏幕缓存的情况,配置屏幕基础信息,修改section高度。
参考文档
RTL8772GWP_Cluster_SDK_Programming_Guide R2