Dashboard

Overview

This document aims to guide users in quickly setting up a development environment, including compiling the SDK, flashing firmware, upgrading firmware, and capturing logs. Users can download the test files from the SDK to ensure that the EVB (Evaluation Board) functions properly and is compatible with the development environment.

The RealTek RTL8772GWP Dashboard SDK provides commonly used basic features such as vehicle speed monitoring, GUI framework, and message distribution. Additionally, the SDK offers easily expandable API interfaces, facilitating secondary development for Dashboard manufacturers.

Note

  • The Dashboard example described in this document uses the RealTek RTL8772GWP chip, paired with the EK9716 RGB (800 by 480) display screen. If developers choose to use the default hardware environment described in this document, they can directly run the example project.

  • If you need to adapt to other models of display screens, please refer Port Adaptation LCD for re-adapting the LCD screen driver.

  • Additionally, the RealTek RTL8772GWP chip supports display screen interfaces: i8080, QSPI/SPI, RGB888/RGB565, but does not support MIPI interface.

Practical Application Case

The following diagram illustrates the modules included in the Dashboard application:

../../../../_images/fig_module_8772GWP.jpeg

The following diagram presents an overview of the modules included in the Dashboard application, it consists of a motherboard, a daughterboard, an LCD screen and a CAN module.

../../../../_images/hal_env.png

Dashboard Application Case

Supported Features

Realtek dashboard application provides complete support for the dashboard features.

  • Supports BLE/USB communication modes

  • Supports USB 3.0/2.0

  • Supports CAN communication

  • Supports the dashboard feature

  • Supports OTA (Over-the-Air) software upgrade

  • Supports incoming call notification and message pop-up feature

The RTL8772GWP supports the Bluetooth LE 5.3 specification and has a wide power supply range of 1.8V to 3.3V, giving developers more flexibility in selecting compatible peripherals.

Requirements

  1. Dashboard Demo Evaluation Board (RTL8772GWP EVB ) ×1, with screen (EK9716)

  2. MicroUSB 2.0 cable ×2

  3. PC (requires USB 2.0 or higher connection)

  4. Extract the BEE4-Bcut-SDK-DASHBOARD-vx.x.x file

  5. Install the FT232 device driver on the PC

  6. CAN BUS

  1. ARM Keil MDK: https://developer.arm.com

  2. BeeMPTool_kits: RealMCU

  3. DebugAnalyzer: RealMCU

  4. Image Convert Tool: https://docs.realmcu.com/HoneyGUI/en/latest/tool/index.html

Note

  • For more detailed usage, please refer to Quick Start.

Different platforms support different panel interfaces, and developers must rely on the functionality of the IC platform to select the appropriate panel. LCD Hardware Platforms the supported interfaces for each platform.

LCD Hardware Platforms

Hardware Platforms

Display interface

RTL8772GWP HDK

I8080, QSPI/SPI, RGB888/RGB565

Wiring

The EVB evaluation board provides a hardware environment for user development and application debugging. The EVB consists of a main board and a daughter board. It has download mode and working mode, and users need to use different wiring methods to set the EVB into the desired mode.

RTL8772GWP EVB Port

EVB Power Supply Wiring, both the USB interfaces of user CON1 and CON2 need to be connected to a computer to provide power to the EVB.

../../../../_images/board_power.png

EVB Power Supply Wiring

Downloading Mode

../../../../_images/log.png

UART-USB Wiring

Short P3_1 and TX , short P3_0 and RX, short LOW and LOG, then press reset button to enter download programming mode.

Note

After power-on, the chip will read the logic level signal of the Log Pin. If the level is low, it will bypass the flash and enter programming mode; otherwise, it will run the application layer program.

Working Mode

To ensure the normal operation of the EVB, it is necessary to disconnect the shorting between LOW and LOG.

Log Wiring

Connect RX and LOG using a jumper cap.

Software Configuration and Environment

The software environment that needs to be configured includes:

  • Keil (to generate APP image)

  • Log tool (to analyze code execution)

  • MP tool (to generate System Config and OTA Header image files)

  • Dashboard APK (third-party tool for testing)

  • Image convert tool (to convert image resources into bin files compatible with RTK GUI recognition)

For specific usage, please refer to Tools.

Image Convert Tool

../../../../_images/image_tool.png

Image Convert Tool

  1. Import the directory to be converted.

  2. Set the conversion format: Choose whether to compress, output image color space format, output image properties (png, bmp, jpg), output image save location, and other parameters.

  3. Convert the images.

Dashboard APK

The Bluetooth device name of the demonstration board is BLE_DASHBOARD .

  1. Click Connect Device

  2. Select BLE_DASHBOARD

../../../../_images/Dashboard_APK.png

Dashboard APK Device Connection

Note

To test the message popup functionality, make sure that your Bluetooth connection is established. If you encounter any unresponsive message popups, check the log for any relevant message-related entries. If you don’t see any message-related logs, you can try resetting the “Dashboard” option in the “Open Notification Permission” section of the main interface by toggling it off and then on again.

GUI Simulator

  1. Configure the simulator environment, please refer to: https://docs.realmcu.com/Honeygui/en/latest/get-started/index.html#id3

  2. Enter the simulator SDK\subsys\HoneyGUI.code-workspace and enter the software interface.

  3. Open the macro CONFIG_REALTEK_BUILD_GUI_800_480 in SDK\subsys\gui\win32_sim\menu_config.h, uncomment it, and close the default demo macro CONFIG_REALTEK_BUILD_GUI_454_454_DEMO.

  4. Debug and run the GUI simulator according to the figure below.

../../../../_images/GUI_monitor.png

GUI Debug and Run the Simulator

  1. Simulator running effect, GUI Simulator Running Effect.

../../../../_images/gui_dashboard.png

GUI Simulator Running Effect

LCD

The RTL8772GWP chip supports RGB mode LCD. Users do not need to write screen drivers from scratch, they can drive LCD screens with just a few simple steps. The RGB signal of RTL8772GWP is a fixed pin and cannot be mapped arbitrarily. There is only one set, as shown below. In the demonstration project, we will use the EK9716 display driver in the RGB888 interface, with a panel size of 800 x 480 as the target display. The input format will be RGB565 and the output format will be 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

Configurations

The default configuration of the dashboard feature in the SDK is as follows:

  1. CONFIG_REALTEK_BUILD_GUI (on)
    Used to open GUI demo in HoneyGUI.
  2. CONFIG_REALTEK_BUILD_GUI_800_480_DEMO (on)
    Enable building upper-level demo related files.
  3. CONFIG_REALTEK_BUILD_LVGL_GUI (off)
    By default, RTK GUI demo is executed. If you need to switch to LVGL demo, you need to turn on CONFIG_REALTEK_BUILD_LVGL_GUI in menu_config.h (set to “1”), and add ENABLE_LVGL_GUI macro definition in keil precompilation.
  4. CONFIG_REALTEK_BUILD_SCRIPT_AS_A_APP (off)
    If you need to use the RVD tool for UI layout, you need to open the CONFIG_REALTEK_BUILD_SCRIPT_AS_A_APP macro definition (uncomment it).
  5. CONFIG_REALTEK_BUILD_PPE (on)
    Enable hardware accelerated Pixel Process Engine (PPE1.0), image effects processing (scaling, transparency, pixel convert processing, etc.).
  6. CONFIG_REALTEK_CAN (off)
    Build CAN related project files.

Scons Construction Project

  1. Enable related macros in the menu_config.h file. For example, to build a CAN project, enable CONFIG_REALTEK_CAN and set it to “1”

  2. Run the cmd command in the proj directory: scons --target=MDK5. The run is successful and the following appears at the end: scons: done building targets.

../../../../_images/SCONS_success.png

SCONS Success

Building and Downloading

Refer to Compilation and Download to build and download. It is worth noting that:

  1. In the step Generating Flash Map, the developer need to generate the flash_map.ini according to the SDK\applications\dashboard\proj\flash_map.h.

  2. In the step Generating System Config File, dashboard requires at least 2 slave links and the AE feature. Other features can be configured as required.

    ../../../../_images/config_file_stack.png

    Dashboard Config File

    Note

    This chapter introduces the sample compilation method (such as keil, gcc, etc.) and how to download it to the hardware board (Jlink, mptool, etc.).

3. In the step Generating App Image, the path of dashboard SDK keil project is SDK\applications\dashboard\proj\mdk. APP image is compiled by this project.

SWD Wiring

RTL87x2G(BEE4)-SWD compatible interface

RTL8772GWP

SWD

RTL8772GWP

SWD

GND(black)

GND

P1_0(green)

SWIO

P1_1(yellow)

SWCK

VDEX(red)

Vterf/3.3V

../../../../_images/SWD_wiring.png

SWD Wiring

KEIL Interface Settings

Refer to Keil MDK perform keil interface settings.

Experimental Verification

After burning the sample program to your EVB board, users can test whether the navigation information transmission, message pop-up window, etc. are normal by using the application development kit Dashboard.

EVB Debugging

To ensure that EVB can be used normally, users can follow the steps below:

  1. Prepare an EVB, confirm that the EVB interface wiring is correct (UART-USB Wiring), put the EVB into download mode, and power on the EVB.

  2. Double-click to run MPTool.exe , and refer to Bring Up EVB for specific operation steps and file paths.

    1. At Load Layout, click:guilabel:.. to load flash_map.ini file.

    2. Click Browse to load all burning files following files.

    3. Click User Data to add user resource root.bin.

    4. Click Detcet to successfully identify the COM port of EVB.

    5. Click Open to successfully open the COM port.

    6. Click Download to burn, and the progress bar shows the current burning progress.

    7. The progress bar shows OK, indicating that the file is burned successfully.

../../../../_images/MPTool_download.png

MPTool Burning Interface

  1. After the program is burned, refer to Working Mode to put the EVB into working mode.

    1. Observe the EVB behavior (search for BLE broadcast on the phone), or check whether the SoC-side Log is as expected.

    2. For EVB hardware peripheral wiring, please refer to Wiring, and for program burning, please refer to Compilation and Download. After completing the above steps, short-circuit the LOG and RX on the back of the EVB, press the Reset button on the EVB board or power on the EVB board again.

Users can use the following two methods to detect whether the SoC-side program is running normally:

Method 1:

EVB will directly send out BLE broadcasts, which can be viewed on a mobile phone. The mobile device installs Dashboard_V1.2.8.38.apk, searches for and connects to the BLE signal, and the boot screen displays the status GUI Simulator Running Effect (one frame).

Note

Broadcast name: BLE_DASHBOARD, Bluetooth address: [25:63:87:4C:E0:01]

../../../../_images/dashboard_display.png

GUI Screen Display

Method 2:

Open Dashboard APK and click Connect Device to enter the BLE device search interface.Dashboard APP Search InterfaceSelect BLE_DASHBOARD to connect. The Bluetooth icon on the main interface lights up and switches to waiting for navigation mode. Enter the start and end locations in APK and the interface will enter the simulated navigation state.

../../../../_images/dashboard_apk1.jpg

Dashboard APP main interface

../../../../_images/dashboard_apk2.jpg

Dashboard APP Search Interface

Log Capture and Analysis

Please refer to Log Wiring for the Dashboard Navigation Interface EVB Board Wiring. After the EVB board is powered on, once the finish gui_mian Log appears, it means that the device interface initialization is complete and the screen refreshes normally. Click Connect Device on the mobile phone dashboard apk interface, and the Log prints GAP adv stoped:because connection created, which means that the mobile phone is successfully connected to the device.

If the user needs to modify the flash map, he can refer to Flash Guide.

Software Design

This chapter mainly introduces the software-related technical parameters and behavioral specifications of the RTL8772GWP dashboard solution. It provides a software overview of all dashboard functions, which are used to guide dashboard development and track problems encountered in software testing.

Source Code Directory

  • Project directory: sdk\application\dashboard\proj

  • Source code directory: sdk\application\dashboard\src

Source files in dashboard application project are currently categorized into several groups as below.

└── 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 Layout

Application default flash layout header file: 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

  • OTA Header

4K

0x04012000

  • System Patch code

32K

0x04013000

  • BT Lowerstack Patch code

60K

0x0401B000

  • BT Host code

212K

0x0402A000

  • APP code

1008K

0x0405F000

  • APP Config File

0K

0x0415B000

  • APP data1

0K

0x0415B000

  • APP data2

16K

0x0415B000

  • APP data3

0K

0x0415F000

  • APP data4

0K

0x0415F000

  • APP data5

0K

0x0415F000

  • APP data6

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

Important

Software Architecture

The system software architecture is shown below:

../../../../_images/template_sw_architecture.png

Software Architecture

  • Platform: Includes OTA, Flash, FTL and etc.

  • IO Drivers: Provide application layer access to the interface of RTL87x2G peripherals.

  • OSIF: Abstraction layer for real-time operating systems.

  • GAP: Abstraction layer which user application communicates with BLE stack.

../../../../_images/GUI_SW__architecture.png

Dashboard Application System Diagram

/* 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();
}
/*-----------------------------------------------------------*/

Tasks and Priorities

RTL8772GWP uses RTOS by default, and encapsulates a layer of OS if interface on this basis. RTL8772GWP has built-in timer task, idle task, flash task, BT controller stack task, BT host stack task, and the dashboard application has its own APP task, GUI task, communication task, and CAN task.

../../../../_images/dashboard_task.png

Dashboard application task composition

Description of each Task Level of the Dashboard

Task

Describe

Priority

Timer

Implementing software timers required by FreeRTOS

6

BT controller stack

Implement the Bluetooth protocol stack below HCI

6

BT Host stack

Implement Bluetooth protocol stack above HCI

2

Flash

Handling asynchronous flash requests

1

Idle

Run background tasks,including DLPS

0

App task

Process different messages and events through message queues and event processing functions to manage and control Bluetooth functions

6

GUI task

Processing image generation and screen data transmission

1

Communication task

Handling Bluetooth interactive communications

1

CAN task

Handling CAN communication

1

Note

  • Multiple application tasks can be created, and memory resources will be allocated accordingly.

  • Idle tasks and timer tasks are provided by FreeRTOS.

  • Tasks have been configured to be preemptive based on their priority using the SysTick interrupt.

  • Interrupt Service Routines (ISR) have been implemented by the vendor.

App task initialization related code entry:

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 Task

../../../../_images/app_task.jpg

APP Task Architecture

  1. The BLE related status is handled by gap_handle_msg to process the messages sent by the upper layer, and the BLE data sending and receiving callbacks are registered to the read/write interface registered when the app creates the service.

  2. The user application layer freely implements the control of BLE behavior, including broadcast switch, establishing connection, updating connection parameters, etc.

  3. New APP task: app_dashboard_launcher The structure shows two basic elements: “thread_entry” and “ui_design”. The “ui_design” function creates the UI interface and the required system resources. Developers can put periodically executed programs in “thread_entry” or message entry to receive messages from other tasks.

    1. The “ui_design” function creates the UI interface and required system resources.

    2. Developers can place programs that execute periodically in the “thread_dentry” or message entry to receive messages from other tasks.

      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 Task

GUI refresh is triggered by keystrokes, timers, and other application behaviors. Before the interface is refreshed, the user updates the information of each control in the interface according to the current behavior. The core part of the GUI is drawn in the frame buffer according to the type and position of each control. The frame buffer can be internal RAM or PSRAM. In order to save RAM resources, a whole frame of FrameBuffer is divided into multiple parts (10 lines are used as a block in the current SDK, and a total of 48 blocks are drawn), and the segments are drawn and sent to the screen synchronously through LCDC. When using PSRAM, each part is first updated to the whole frame PSRAM, and then the whole frame is sent directly to the screen through DPI.

../../../../_images/gui_task_architecture.png

GUI Task Architecture

The screen resolution in the demo application is 800*480. We will create two Window widgets as basic containers to display information in the BLE connected and unconnected states. The functions app_dashboard_create_main_display() and app_dashboard_create_connected_display() will create the remaining widgets and attach them to the two window widgets respectively.

Dashboard API description

Dashboard API interface

description

app_dashboard_launcher_ui_design ()

main entrance of UI interface

app_dashboard_create_main_display ()

display dashboard home page information

app_dashboard_data_get_show_main_display ()

get dashboard home page information

app_dashboard_create_connected_display ()

display navigation page information

app_dashboard_initialize_data ()

initialize control content

app_update_gui_widget ()

update control content

app_dashboard_get_data ()

get dashboard information

app_dashboard_data_set_battery_level ()

set the latest battery data

app_dashboard_data_get_battery_level ()

get current battery data

app_dashboard_data_set_car_speed ()

set the current car speed

app_dashboard_data_get_car_speed ()

get the current car speed

app_dashboard_data_set_car_turn_info ()

set the current car turning information

app_dashboard_data_get_car_turn_info ()

get the current car turning information

app_dashboard_data_set_bluetooth_status ()

set the current Bluetooth status

app_dashboard_data_get_bluetooth_status ()

get the current Bluetooth status

app_dashboard_data_set_navi_status ()

set the current navigation status

app_dashboard_data_get_navi_status ()

get the current navigation status

app_dashboard_data_update_navi_status ()

update the current navigation status

app_dashboard_data_set_phone_status ()

set the current phone status

app_dashboard_data_get_phone_status ()

get the current phone status

app_dashboard_data_update_phone_status ()

set the current phone information

app_dashboard_data_set_message_data_update ()

set the current message information

app_dashboard_data_get_message_data_update ()

get the current message information

app_dashboard_data_update_message_status ()

update the current message information

app_dashboard_data_set_current_timer ()

set the current time information

app_dashboard_data_get_current_timer ()

get the current time information

app_dashboard_data_set_tense_timer_info ()

set the current tense information

app_dashboard_data_get_tense_timer_info ()

get the current tense information

app_dashboard_data_set_reject_end_call ()

set the rejection and hang up the phone

app_dashboard_data_set_accept_call ()

set the answer phone

app_dashboard_update_main_display_speed_info ()

update the main interface speed information

app_dashboard_update_main_display_turn_info ()

update the main interface turn information

app_dashboard_update_main_display_bluetooth_info()

Update the Bluetooth connection information on the main interface

app_dashboard_update_main_display_phone_infor()

Update the phone information on the main interface

app_dashboard_update_main_display_message_infor()

Update the message information on the main interface

GUI control description

GUI control

description

gui_win_create()

create win window

gui_set_font_mem_resourse()

find and set font resources in memory

gui_win_set_animate()

set window animation effect

gui_img_set_attribute()

set the attributes of image object

gui_img_create_from_mem()

create image object

gui_text_create()

create text object

gui_text_mode_set()

set the text mode of text object

For more details about GUI controls, please refer to the documentation: https://docs.realmcu.com/Honeygui/en/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();
}
  1. Create a BLE non-connected widget

    The app_dashboard_create_main_display function will rely on the layout file BLE non-connected state layout to assign the widgets to the correct coordinates. We will register two events “obj_animate” to handle the state of the panel before and after refresh. At the end of this function, we will call the refresh function of each widget to display the latest information to the user.

    ../../../../_images/BLE_no_connect_display.png

    BLE non-connected state layout

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(&current_phone_status);
    short_tel_number = gui_text_create(target_main_display,  "short_tel_number",  322, 415, 158, 30);
    memcpy(&show_tel_number[0], &current_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(&current_message_status);
    memcpy(&show_message_data[0], &current_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(&current_phone_status);
    app_dashboard_update_main_display_message_infor(&current_message_status);
}
  1. BLE connection status layout

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(&current_navi_data);

    memcpy(&show_c_navigation_msg[0], &current_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], &current_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], &current_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(&current_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], &current_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(&current_message_status);
    memcpy(&show_c_message_data[0], &current_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(&current_phone_status);
    app_dashboard_update_connected_display_message_infor(&current_message_status);
}

BLE Communication Task

The communication task is responsible for the management of BLE data transmission, packaging the data according to the current transmission data command format and sending it through the Bluetooth protocol stack. Customers generally use their own Bluetooth data interaction format. Tasks can be used as a reference for sending methods or they can be implemented by themselves. Developers must refer to Bluetooth to familiarize themselves with the BLE service structure and how to process messages. In this design document, we will use the specified BLE service of the dashboard application as a demonstration. Developers can choose which BLE services to use in the mobile application according to their product requirements.

../../../../_images/dashboard_ble_task.png

Dashboard Communication Block Diagram

After the mobile APP successfully connects to the dashboard device, the APP can transmit necessary information to the device using BLE write requests. All messages will be written via characteristics.

    /**      * @brief write characteristic data from stack. *
* @param ServiceId ServiceId generated when register to BT Host 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;
}

All received messages will be published to the “raw_data_receive_queue_handle” queue, which will be processed in the 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);
        }
    }
}

Once the received data passes the data integrity check, the API “L2_frame_resolve” will process the request. In the dashboard application, developers only need to process three types of data: “NAVIGATION_INFORMATION_ID”, “NOTIFY_COMMAND_ID” and “CONTROL_COMMAND_ID”.

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

Whenever BLE receives a message from the mobile APP, it will update the data to the dashboard data container, and then the UI update thread will get the latest information and refresh the display.

CAN Communication Task

RTL8772GWP CAN external USB CAN FD analyzer, communicates with CAN controller through PC-side data transmission and reception, and receives data sent by PC (similar to external signal) in CAN interrupt.

canopen creates 3 tasks separately:

  • send task

  • receive task

  • canopen (canfestival) task

Note

The canopen task contains all APP layer behaviors, and the user can redefine the task according to his own private protocol based on the behaviors of the canopen (canfestival) protocol (including the interaction between the master node and the slave node).

Send task process

Once there is data to be sent, it is immediately put into a loop buffer, and the send task will monitor the loop buffer in real time whether there is data.

Receive task process

Once the hardware can module has data, it will trigger can_handler, and use one of the two methods of basic rx / dma rx in the handler to move the data out, and then send it to the communication protocol layer through the canfestival API, and make corresponding behaviors for the received msg.

  1. Pin definition

#define CAN_TX_PIN          P1_4
#define CAN_RX_PIN          P1_6
  1. CAN sending task

/**
 * @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;
                    }
                }
            }
        }
    }
}
  1. CAN receiving task

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

Troubleshooting

Graphic Data Loading Process

Load the program and drawing data from the external Flash to PSRAM, and display the drawing data to the TFT. The specific process is shown in the figure below Data Path.

  1. User resource display file processing: Graphic resources are processed using the image convert tool, and the image binary file is output, including the image attribute header (image size, compression, color format (RGB565, RGB888, etc.)), and User Data: root.bin is created.

  2. Download the flash area: Use the MPtool tool to burn into the EVB XIP flash area.

  3. Read data in Flash: Use QSPI, 80MHz bandwidth, and double-edge data (DTR) reading method.

  4. Allocate memory: Before loading image data, it is necessary to allocate enough memory to store image pixel data (SRAM) according to the image size and format.

  5. Compressed graphics pixel processing: The GUI image control indexes the address of the file in the flash by the image name, determines whether it is compressed, and needs to be decompressed if compressed, and is moved from the flash to the SRAM via DMA. The pixel is processed by the PPE pixel processing engine and stored in the previously allocated memory (SRAM).

  6. Uncompressed graphics pixel processing: The GUI image control indexes the address of the file in the flash by the image name, determines whether it is compressed, and the uncompressed image is directly moved from the flash to the SRAM via DMA, processed by the PPE, and stored in the SRAM.

    Note

    PPE processes uncompressed data, and compressed data needs to be decompressed before it can be processed by PPE.

  7. TFT display: After PPE processing, the pixel is moved to the PSRAM cache via DMA, and finally displayed on the screen via LCDC (OPI).

../../../../_images/display_progress.png

Data Path

PPE1.0 Performance Measurement

As shown below, seven tasks have been created for the user application:

  1. PPE function: Blend, unit ms, Destination fixed format RGB565, screen size 480 by 480, error no more than 180us.

    1. PPE:RGB565 Bypass mode (Source to Destination),unit:ms

      SRAM

      PSRAM

      FLASH

      7.178

      7.18

      PSRAM

      5.64

      13.524

      SRAM

      3.426

      5.228

    2. PPE:RGB565 source over mode (Source to Destination),unit:ms

      SRAM

      PSRAM

      FLASH

      7.474

      13.642

      PSRAM

      5.918

      20.846

      SRAM

      5.04

      13.528

    3. PPE:RGB8565 Bypass mode (Source to Destination),unit:ms

      SRAM

      PSRAM

      FLASH

      11.518

      11.52

      PSRAM

      8.754

      16.932

      SRAM

      4.488

      5.23

    4. PPE:RGB8565 source over mode (Source to Destination),unit:ms

      SRAM

      PSRAM

      FLASH

      11.775

      14.546

      PSRAM

      8.99

      24.11

      SRAM

      5.775

      13.528

  2. PPE function: Scale, unit ms, Destination fixed format RGB565, error does not exceed 80us.

    1. PPE: 480 * 480 reduced by 2 times to 240 * 240 (Source to Destination), unit: ms

      SRAM

      PSRAM

      FLASH

      3.762

      3.758

      PSRAM

      2.891

      4.83

      SRAM

      1.54

      1.493

    2. PPE: 240 * 240, enlarged 2 times to 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 Adaptation LCD

LCD screen adaptation needs to be adapted according to the screen cache situation. In this sample, the default condition is that PSRAM is used as the screen cache. To select GRAM as the screen cache, please refer to the GRAM section.

RAMLESS

  1. Screen resolution information configuration (refer to the screen specification), mainly configure LCD width, height, pixel depth information. Take the example as an example, modify the corresponding parameters in the EK9716_800480_rgb.h header file.

#define EK9716_800480_LCD_WIDTH                   800  //Screen width
#define EK9716_800480_LCD_HEIGHT                  480  //Screen Height
#define EK9716_DRV_PIXEL_BITS                     16  //Pixel Depth
  1. The section height needs to be configured. The height selection depends on the actual situation. In the example project, LCD_SECTION_HEIGHT = 10 is configured. The corresponding parameters can be modified in gui_port_dc.c.

#define LCD_SECTION_HEIGHT                   10
  1. PSRAM needs to be configured with address information as screen cache. In gui_port_dc.c, the example of configuring PSRAM cache for framebuffer is as follows:

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;
Screen display DMA transfer API

API

description

memcpy_by_dma_start ()

DMA starts to transfer data

memcpy_by_dma_done ()

wait for DMA transfer to complete

  1. Other interface APIs of LCD driver layer, corresponding to gui_port_dc.c.

    LCD screen display driver interface

    API

    description

    drv_lcd_get_screen_width ()

    get screen width

    drv_lcd_get_screen_height ()

    get screen height

    drv_lcd_get_pixel_bits ()

    get screen pixel depth

    port_gui_lcd_power_on ()

    screen startup

    port_gui_lcd_power_off ()

    screen shutdown

    gui_port_dc_init ()

    initialize LCD

    port_gui_lcd_update ()

    update each segment of LCD

    drv_lcd_update ()

    transfer the specified buffer to the screen

    Update the contents of the frame buffer to the LCD screen using the port_gui_lcd_update() interface. By default, the screen uses PSRAM as the frame buffer.

    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

Compared with PSRAM, GRAM has a slower read and write speed. Therefore, for scenes that require frequent updates of display content (such as animation or video playback), GRAM may not be suitable. The following explains several key functions required for screen updates:

  1. Wait for the previous section to be transferred: middle layer API interface rtk_lcd_hal_transfer_done(), bottom layer implementation 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
}
  1. Set the drawing area: the middle layer API interface rtk_lcd_hal_set_window(), corresponding to the underlying implementation 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);
}
  1. Start from the frame buffer transfer: the middle layer API interface rtk_lcd_hal_start_transfer(), corresponding to the bottom layer implementation rtk_lcd_hal_start_transfer().

void rtk_lcd_hal_start_transfer(uint8_t *buf, uint32_t len)
{

#if (DMA_LINKLIST == 0)
    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();  // clear DMA FIFO
    LCDC_ClearTxPixelCnt();  // Clear the pixel counter for the transfer

    LCDC_SwitchMode(LCDC_AUTO_MODE);  // Switch the LCD controller to automatic mode
    LCDC_SwitchDirect(LCDC_TX_MODE);  // Switch the LCD controller to transmit mode
    LCDC_SetTxPixelLen(len);  // Set the number of pixels to send

    LCDC_Cmd(ENABLE);  //Enable LCD Controller
#if DMA_LINKLIST
    LCDC_DMA_MultiBlockCmd(ENABLE);  // In DMA linked list mode, enable multi-block transfer
#endif
    LCDC_DMAChannelCmd(LCDC_DMA_CHANNEL_NUM, ENABLE);  // Enable DMA channel
    LCDC_DmaCmd(ENABLE);  // Enable DMA
#if (TE_VALID == 1)
    LCDC_TeCmd(ENABLE);  // If TE is valid, the TE (Transfer End) signal is enabled
#endif
#if (TE_VALID == 0)
    LCDC_AutoWriteCmd(ENABLE);  // If TE is invalid, the automatic write command is enabled
#endif

}
  1. Te signal configuration to prevent screen tearing.
    The hardware has a TE signal pin, which is configured as a falling edge trigger. At the same time, in the te callback function, modify the te flag.
  2. Screen parameter configuration, completely refer to the situation where PSRAM is used as screen cache, configure basic screen information, and modify section height.

Reference

  1. RTL8772GWP_Cluster_SDK_Programming_Guide R2