• sales

    +86-0755-88291180

RP2040-Touch-LCD-1.69 User Guide

Overview

Introduction

RP2040-Touch-LCD-1.69 is a low-cost, high-performance MCU board designed by Waveshare, tiny size, with an onboard 1.69inch capacitive touch LCD display, Li-ion battery recharge manager, 6-axis sensor (3-axis accelerometer and 3-axis gyroscope), RTC, buzzer and so on, which makes it easy for you to develop and integrate it into products quickly.

Feature

  • RP2040 microcontroller chip designed by Raspberry Pi in the United Kingdom.
  • Dual-core Arm Cortex M0+ processor, flexible clock running up to 133 MHz.
  • 264KB of SRAM, and 4MB of onboard Flash memory.
  • Type-C connector, keeps it up to date, easier to use.
  • Onboard 1.69inch capacitive touch display, 240×240 resolution, 65K color.
  • Lithium battery recharge/discharge header, suitable for mobile devices.
  • Onboard RTC chip and RTC battery header for timing and scheduling functions.
  • Onboard buzzer for sound-producing peripheral.
  • USB 1.1 with device and host support.
  • Low-power sleep and dormant modes.
  • Drag-and-drop programming using mass storage over USB.
  • Accurate clock and timer on-chip.
  • Temperature sensor.
  • On-chip accelerated floating point library.

Specification

LCD Parameters
Touch ChipCST816TTouch PortI2C
Display ChipST7789V2Display InterfaceSPI
Resolution240(H)RGB x 280(V)Display Size27.972mm × 32.634mm
Display PanelIPSPixel Pitch0.11655mm × 0.11655mm


IMU Parameters
SensorQMI8658
AccelerometerResolution: 16 bits
Measuring Range (Optional): ±2, ±4, ±8 ±16g
GyroscopeResolution: 16 bits
Measuring Range (Optional): ±16, ±32, ±64, ±128, ±256, ±512, ±1024, ±2048°/sec

Pinout


Dimensions


Pico Quick Start

Download Firmware

  • MicroPython Firmware Download


  • C_Blink Firmware Download


Video Tutorial

  • Pico Tutorial I - Basic Introduction


  • Pico Tutorial II - GPIO


  • Pico Tutorial III - PWM


  • Pico Tutorial IV - ADC


  • Pico Tutorial V - UART


  • Pico Tutorial VI - To be continued...

Text Tutorial

Introduction

MicroPython Series

C/C++ Series

Arduino IDE Series

Install Arduino IDE

  1. Download the Arduino IDE installation package from Arduino website.
  2. Just click on "JUST DOWNLOAD".
  3. Click to install after downloading.
  4. Note: You will be prompted to install the driver during the installation process, we can click Install.

Install Arduino-Pico Core on Arduino IDE

  1. Open Arduino IDE, click the File on the left corner and choose "Preferences".
    RoArm-M1 Tutorial04.jpg
  2. Add the following link in "Additional boards manager URLs", then click OK.
    https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json


    Note: If you already have the ESP32 board URL, you can separate the URLs with commas like this:

    https://dl.espressif.com/dl/package_esp32_index.json,https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json
    
  3. Click on Tools -> Board -> Board Manager -> Search for pico, it shows installed since my computer has already installed it.

Upload Demo At the First Time

  1. Press and hold the BOOTSET button on the Pico board, connect the Pico to the USB port of the computer via the Micro USB cable, and release the button when the computer recognizes a removable hard drive (RPI-RP2).

  2. Download the demo from #Resource, open the D1-LED.ino under arduino\PWM\D1-LED path.
  3. Click Tools -> Port, remember the existing COM, do not need to click this COM (different computers show different COM, remember the existing COM on your computer).
  4. Connect the driver board to the computer with a USB cable, then click Tools -> Ports, select uf2 Board for the first connection, and after the upload is complete, connecting again will result in an additional COM port.
  5. Click Tools -> Board -> Raspberry Pi Pico/RP2040 -> Raspberry Pi Pico.

  6. After setting, click the right arrow to upload.
    • If you encounter problems during the period, you need to reinstall or replace the Arduino IDE version, uninstall the Arduino IDE clean, after uninstalling the software you need to manually delete all the contents of the folder C:\Users\[name]\AppData\Local\Arduino15 (you need to show the hidden files in order to see it) and then reinstall.


Open Source Demo

Application Demo

LVGL Example Demo

C

Example Effect

This demo displays four interfaces that can be switched by sliding on the touchscreen.

  • The first interface:
    • Display content: Waveshare Logo.
  • The second interface:
    • Display content: 6-axis sensor data
    • Refresh frequency: Updated every 500ms.

  • The third interface:
    • Display content: RTC date and time data
    • Refresh frequency: Updated every 300ms

  • The fourth interface:
    • Display content: two interfaces for controlling buzzer and LCD backlight.
    • How to interact: There are two options at the top of the interface: BEEP and LCD BL. You can touch the top option to switch to the corresponding control interface. BEEP is the buzzer control interface, which contains a switch and a slider. The switch is used to control the buzzer start/stop and the slider is used to control the buzzer frequency. LCD BL is the LCD backlight control interface, in this interface, you can control the brightness of the screen by scrolling the wheel. LCD BL is the LCD backlight control interface, in this interface, you can control the screen brightness by scrolling the wheel.

Example

This example is for testing LVGL control interaction, styling, etc. For details, you can refer to LVGL development document.

Functions
  1. This example uses DMA to transmit color data to the SPI bus, reducing the CPU usage rate, so that the CPU occupancy rate can be controlled to be below 50% and the memory occupancy rate to be below 35% during the simple interaction.
  2. The system clock of this example is 200MHz, set the peripheral clock frequency of SPI to be the same as the system clock, and at the same time, use the double-buffer mechanism of LVGL library, when transferring the data in one buffer, the other buffer will be used for rendering, which ensures the smoothness of the animation.
  3. This example uses a touch screen switching interface to realize real-time display of six-axis sensors, RTC data, and real-time control of buzzer switching, frequency, and screen brightness through the control, demonstrating the simple use of LVGL controls.
Compile & Run
  • Windows
    • Please refer to this link to build your Windows environment.
    • Open VS 2022 -> Tool -> Command Line -> Developer Powershell
    • Set the absolute address of pico-sdk as "PICO_SDK_PATH",for example, set pico-sdk address as“D:\pico\pico-sdk”
      setx PICO_SDK_PATH "D:\pico\pico-sdk"
    • Download the demo,enter the source code directory, if the build directory exists, you can directly enter; if not, you can create this directory.
      mkdir build
      cd build
    • Execute cmake, it will automatically generate Makefile file:
      cmake -G "NMake Makefiles" ..
    • Execute nmake to generate executable file, and input the following commands in the terminal:
      nmake
      After compilation, it will generate a .uf2 formatted file.
    • Press the onboard boot key, connect the USB interface of the board to the PC through a Micro USB cable. And then release the key, the PC will identify the pico as a portable driver. Finally, you need to copy the generated .uf2 formatted file to Pico.
  • Ubuntu
    • Please refer to Pico user manual Chapter 2. The SDK.
    • Open a terminal,set the value of PICO_SDK_PATH as the absolute address of pico-sdk. For example, the address of my pico-sdk is "/home/pi/pico/pico-sdk"
      nano ~/.bashrc
      #Add the following content at the last line
      export PICO_SDK_PATH="/home/pi/pico/pico-sdk"
    • After setting, save and exit. The configuration takes effect.
      source ~/.bashrc
    • Download the demo, enter the directory of the source code. If build directory exists, you can directly enter the directory. If not, you can create one.
      mkdir build
      cd build
    • Execute cmake, it will generate Makefile file:
      cmake ..
    • Execute make to generate executable file, and input the following content in the terminal:
      make
      After compilation, it will generate a .uf2 formatted file.
    • Press the boot key on the board, connect the USB interface of the board to the PC through a Micro USB cable. And then release the key, the PC will identify the pico as a portable driver. Finally, you need to copy the generated .uf2 formatted file to Pico.

Source Code Explanation

Source Code Structure
  • The source code of the LVGL library is at lib/lvgl of the project file folder, and the version used is "8.1". For the second development, you can refer to the corresponding document.
  • The related setting of LVGL library is at examples/inc/lv_conf.h of the project file folder, and can set the display frequency, the system occupied data, and so on.

  • The application demo of the LVGL library is at examples/src/LVGL_example.c of the project file folder.

LVGL Initialization

Before using LVGL image library, you need to initialize LVGL.

  • The initialization function for LVGL library
    • Demo location: examples/src/LVGL_example.c
    • Function: mainly initialize the hardware and structure variables required for initializing LVGL
LVGL_Init();
  • LVGL library core initialization
    • Demo location: examples/src/LVGL_example.c
/*2.Init LVGL core*/
lv_init();
Running LVGL

The LVGL library calls the function lv_tick_inc at regular intervals to notify LVGL of the elapsed time so that LVGL can update its internal time state and handle time-related tasks such as animations, timers, etc. The main function also calls the lv_task_handler function in a loop so that LVGL can handle events and tasks in a timely manner. The lv_task_handler function also needs to be called in the loop of the main function so that LVGL can handle events and tasks in time to ensure that the user interface responds and refreshes.

  • LVGL heartbeat function:
    • Demo location: examples/src/LVGL_example.c
    • Method: Need to make sure that lv_task_handler has a lower priority than lv_tick_inc, so in this case lv_tick_inc is called in the timer callback function.
//Timer callback function called every 5ms
add_repeating_timer_ms(5, repeating_lvgl_timer_callback, NULL, &lvgl_timer);

static bool repeating_lvgl_timer_callback(struct repeating_timer *t) 
{
    lv_tick_inc(5);
    return true;
}
  • LVGL Task Processor
    • Demo location: examples/src/LCD_XinXX_LVGL_test.c
    • Method: Handle LVGL tasks, and need to regularly call lv_timer_handler(), and this example can be called in the cycle of the main function.
int main()
{
    ...
    while(1)
    {
      lv_task_handler();
      DEV_Delay_ms(5); 
      ...
    }
}
LVGL Display

To display LVGL, you need to initialize a display driver and set various properties of the display driver, such as color format, draw buffer, rendering mode, and display callback functions. At each LV_DISP_DEF_REFR_PERIOD (set in lv_conf.h), LVGL detects if something has happened on the UI that requires recoloring. For example, a button is pressed, a chart is changed, an animation occurs, etc. When recoloring, LVGL calls the display callback function to finish drawing the image in the refresh area.

  • Set LVGL display refreshing rate
    • Demo location: examples/inc/lv_conf.h
    • Method: in lv_conf.h, you can set the time of display buffer refreshing frequency, and can modify this definition to change the refreshing time of the screen.
#define LV_DISP_DEF_REFR_PERIOD  10  // Unit:ms, 10ms
  • Set LVGL display color
    • Demo location: examples/inc/lv_conf.h
    • Setting target: As the pixels and color storage method set by the lv_color_t structure are different with the data to be transmitted in this demo, if it directly transmitd the data, the color of the displayed image may be different.
#define LV_COLOR_16_SWAP 1
  • Display LVGL related variables definition
    • Demo location: examples/src/LVGL_example.c
    • Function: Define the display driver disp_drv and the drawing buffer disp_buf. This example realizes the double-buffer mechanism, the drawing buffer consists of buffer buf0 and buf1, the size of which is set to half of the screen display area, which is able to reduce the jaggedness of large-area screen refreshing and improve the screen refresh rate effectively; it is best to set it to 10% of the screen display area when using a single-buffer to reduce the system usage but the more obvious jaggedness will appear when refreshing large-area images. When using a single buffer, it is better to set it to 10% of the screen display area, which can effectively reduce the system usage but there will be more obvious jaggedness when refreshing the image in a large area.
static lv_disp_drv_t disp_drv;
static lv_disp_draw_buf_t disp_buf;
static lv_color_t buf0[DISP_HOR_RES * DISP_VER_RES/2];
static lv_color_t buf1[DISP_HOR_RES * DISP_VER_RES/2];
  • LVGL display device registration
    • Demo location: examples/src/LVGL_example.c
    • Function: Refine the LVGL library core struct variables according to the design requirements, initialize the display driver disp_drv, and set up the draw buffer, which is a simple array used by LVGL to render the contents of the screen. Once rendering is ready, the contents of the draw buffer are sent to the display using the disp_drv_flush_cb function set in the display driver.
lv_disp_draw_buf_init(&disp_buf, buf0, buf1, DISP_HOR_RES * DISP_VER_RES / 2); 
lv_disp_drv_init(&disp_drv);    
disp_drv.flush_cb = disp_flush_cb;
disp_drv.draw_buf = &disp_buf;        
disp_drv.hor_res = DISP_HOR_RES;
disp_drv.ver_res = DISP_VER_RES;
lv_disp_t *disp= lv_disp_drv_register(&disp_drv);   
  • LVGL display callback function:
    • Demo location: examples/src/LVGL_example.c
    • Function: mainly finish drawing in the refresh areas
static void disp_flush_cb(lv_disp_drv_t * disp, const lv_area_t * area, lv_color_t * color_p)
Parameters:
    lv_disp_drv_t *disp_drv: display the driver structure cursor, including the information related to the display and function cursor. This parameter is used to informing the refreshing is finished.
    const lv_area_t *area: Area structure cursor, including the position information of the area to be refreshed. In this demo, you can use it for creating TFT display window.
    lv_color_t *color_p: color structure cursor, indicating the color data displayed in the refreshed area. In this demo, it can be sent the data to the SPI bus as the DMA input reading address, and then the image drawing is finished.
  • LVGL display callback function
    • Demo position: examples/src/LVGL_example.c
    • Method: to significantly reduce the cpu usage rate, you can use DMA to transmit color data. Set color_p as the reading address and the output data register of SPI bus can be set as the writing address.
static void disp_flush_cb(lv_disp_drv_t * disp, const lv_area_t * area, lv_color_t * color_p)
{
    LCD_SetWindows(area->x1, area->y1, area->x2+1, area->y2+1);
    DEV_Digital_Write(LCD_DC_PIN, 1);
    DEV_Digital_Write(LCD_CS_PIN, 0);
    dma_channel_configure(dma_tx,
                          &c,
                          &spi_get_hw(LCD_SPI_PORT)->dr, 
                          color_p, // read address
                          ((area->x2 + 1 - area-> x1)*(area->y2 + 1 - area -> y1))*2,
                          true);
}
  • LVGL Refresh completion notification implementation
    • Demo location: examples/src/LVGL_example.c
    • Function: the LVGL kernel needs to be notified when each image refresh is complete so that LVGL can prepare the next refresh image for rendering.
    • Display method: This example notifies the LVGL image refresh completion in the DMA transfer completion interrupt service function. If the blocking notification mechanism is used, it is not possible to utilize the double-buffering mechanism to increase the refresh speed.
static void dma_handler(void)
{
    if (dma_channel_get_irq0_status(dma_tx)) {
        dma_channel_acknowledge_irq0(dma_tx);
        DEV_Digital_Write(LCD_CS_PIN, 1);
        lv_disp_flush_ready(&disp_drv);     
    }
}
LVGL Input

In LVGL, users are allowed to register input devices such as touchpad, mouse, keyboard or encoder devices. Users can control the user interface through these input devices for better interaction.

  • LVGL calls input device callback functions for frequency settings.
    • Demo location:examples/inc/lv_conf.h
    • How to set: LVGL defaults to call the input device callback function every 30ms to update the events triggered by the input device, which can be set in lv_conf.h.
#define LV_INDEV_DEF_READ_PERIOD 30 // Unit:ms, here is 30ms
  • LVGL input device registration
    • Demo location: examples/src/LVGL_example.c
    • How to set: register touchscreen device indev_ts and initialize it.
/*4.Init touch screen as input device*/ 
lv_indev_drv_init(&indev_ts);           // Device initialization  
indev_ts.type = LV_INDEV_TYPE_POINTER;  // Register it as touchscreen device  
indev_ts.read_cb = ts_read_cb;          // Set callback function  
lv_indev_t * ts_indev = lv_indev_drv_register(&indev_ts); // Registered device 
DEV_IRQ_SET(Touch_INT_PIN, GPIO_IRQ_EDGE_RISE, &touch_callback); //Enable touch interrupt  
  • The callback function of the LVGL input devices
    • Demo location:examples/src/LVGL_example.c
    • Function: mainly for updating input events.
static void ts_read_cb(lv_indev_drv_t * drv, lv_indev_data_t*data);
Parameters:
  lv_indev_drv_t *indev_drv: the cursor to the input device driver structure in LVGL. In this example, the structure is the touchscreen input device driver
  lv_indev_data_t *data: the cursor to the input device data structure in LVGL. In this example, this structure is used to store the state and data of the input device, including the current touch state (pressed or released) and the coordinates of the touch point
  • Callback function implementation for touchscreen input devices
    • Demo location:examples/src/LVGL_example.c
    • How to set: update the touch state and touch point coordinates through the touch interrupt function.
static void touch_callback(uint gpio, uint32_t events)
{
    if (gpio == Touch_INT_PIN)
    {
        CST816S_Get_Point();
        gesture = CST816S_Get_Gesture();
        ts_x = Touch_CTS816.x_point;
        ts_y = Touch_CTS816.y_point;
        ts_act = LV_INDEV_STATE_PRESSED;
    }
}

static void ts_read_cb(lv_indev_drv_t * drv, lv_indev_data_t*data)
{
    data->point.x = ts_x;
    data->point.y = ts_y; 
    data->state = ts_act;
    ts_act = LV_INDEV_STATE_RELEASED;
}
LVGL Widget Layout

In LVGL we can establish different user interfaces, including objects, or widgets, such as Button, Label, Image, List, chart or text areas. In one interface, you can create multiple widgets at the same time, and we can set their position, dimensions, parent object, formats and event process demo, etc.

  • LVGL widget initialization
    • Demo location: examples/src/LVGL_example.c
    • Function: Mainly used for stylized controls and layout controls
void Widgets_Init(void);
  • LVGL creates blocks
    • Demo location: examples/src/LVGL_example.c
    • Function: A Tile view is a container object whose elements (called block) can be arranged in a grid. The user can navigate between the tiles by swiping. Call lv_tileview_add_tile(tileview, row_id, col_id, dir) to create a new tile on row_id and col_id. dir can be LV_DIR_LEFT/RIGHT/TOP/BOTTOM/HOR/VER/ALL or a value to move to neighboring tiles in a given direction with a Slide to move to neighboring blocks in the given direction.
//Create a block at (0,0), support slide downward to (0,1)
tile1 = lv_tileview_add_tile(tv, 0, 0, LV_DIR_BOTTOM);
  • LVGL creates widget
    • Demo location: examples/src/LVGL_example.c
    • Function: create widgets, Different controls require the use of different function interfaces, which can be created by selecting the parent object.
//Create a widget, tab1 is the parent of the button, which can be replaced by a list, title, etc. that can have children objects.
sw = lv_switch_create(tab1);
  • Align LVGL widgets
    • Demo location: examples/inc/lv_conf.h, examples/src/LVGL_example.c
    • Function: Enables the control to be positioned offset based on a reference point. The center of the reference point control to which the control is aligned for the offset.
    • Alignment standard: The LVGL library has both internal and external alignment. By default, the top-left corner is used as the origin, and the leftward direction is the positive horizontal direction, and the downward direction is the positive vertical direction.
//Position the btn control 45 pixels to the left of its center.
lv_obj_align(sw, LV_ALIGN_CENTER, -45, 0);


  • LVGL widget to adjust font size
    • Demo location: examples/inc/lv_conf.hexamples/src/LVGL_example.c
    • Function: In practice, an interface may require multiple font sizes. You can enable multiple font sizes in lv_conf.h and also can set the default font size. When setting the font size, you need to set the style of the widget. Use lv_obj_add_style function to add styles in different parts of the widgets.
#define LV_FONT_MONTSERRAT_16 1                                // Enable font 16
#define LV_FONT_MONTSERRAT_18 1                                // Enable font 18
#define LV_FONT_DEFAULT &lv_font_montserrat_18                 // Set the default font size as 18

static lv_style_t style_label;                
lv_style_init(&style_label);                                    // Initialize style
lv_style_set_text_font(&style_label, &lv_font_montserrat_16);  // Set the font size as 16
lv_obj_add_style(label,&style_label,0);                         // Set label theme style 
  • LVGL widget event handle
    • Demo location: examples/src/LVGL_example.c
    • Function: in LVGL, you can add the event handler callback function for the widget. This allows the widget to trigger an event when it is clicked, scrolled, redrawn, etc. so that it enters the event handler callback function. The lv_obj_add_event_cb(obj, event_cb, filter, user_data) function is called in the demo to add the event filter handler event_cb to the control obj, which is automatically called when the control obj triggers the filter event. The last argument is a pointer to any custom data available in the event.
//Add event LV_EVENT_VALUE_CHANGED handler function sw_event_cb to the widget sw.
lv_obj_add_event_cb(sw, sw_event_cb,LV_EVENT_VALUE_CHANGED,NULL);

Python

Sample Effect

This demo displays four interfaces that can be switched by sliding on the touchscreen.

  • The first interface:
    • Display content: Waveshare Logo.
  • The second interface:
    • Display content: 6-axis sensor data
    • Refresh frequency: Updated every 500ms.

  • The third interface:
    • Display content: RTC date and time data
    • Refresh frequency: Updated every 300ms

  • The fourth interface:
    • Display content: two interfaces for controlling buzzer and LCD backlight.
    • How to interact: There are two options at the top of the interface: BEEP and LCD BL. You can touch the top option to switch to the corresponding control interface. BEEP is the buzzer control interface, which contains a switch and a slider. The switch is used to control the buzzer start/stop and the slider is used to control the buzzer frequency. LCD BL is the LCD backlight control interface, in this interface, you can control the brightness of the screen by scrolling the wheel. LCD BL is the LCD backlight control interface, in this interface, you can control the screen brightness by scrolling the wheel.

Example

This demo is for testing LVGL widget interraction, style design and so on. The version used is 9.1. As the development document of LVGL V9 does not provide Python example, you can refer to LVGL development tutorial.

Function
  1. The system clock of this demo is 240MHz. Set the peripheral clock frequency of SPI to be the same as the system clock, and at the same time, use the double-buffer mechanism of LVGL library, when transferring the data in one buffer, the other buffer will be used for rendering, which ensures the smoothness of the animation.
  • This example uses a touch screen switching interface to realize real-time display of six-axis sensors, RTC data, and real-time control of buzzer switching, frequency, and screen brightness through the control, demonstrating the simple use of LVGL controls.
Compile to Run
  • Operation steps:
    • Before using, you need to set the integrated development Thonny Python IDE (Windows V3.3.3).
    • Download the demo, and press the boot key on the board. Connect the board to the USB interface of the PC through Micro USB cable, and then release the key. The PC identifies the pico as a removable hard driver, and then you need to copy the files in .uf2 format under the Python directory to the Pico.
    • Open Thonny, upload the examples and lib directory to the development board.

    • Open the LCD_1in69_LVGL_test.py file under the examples directory, please refer to the steps below to run the demo.

  • Firmware building
    • Envitonment building: only test on Ubuntu 22.04
    • Github:lv_micropython
    • Please refer to Raspberry Pi Pico port to compile.
    • Generate firmware path: lv_micropython/ports/rp2/build-PICO/firmware.uf2

Source Code Explanation

Source Code Structure
  • For LVGL library source code, you can refer to lv_micropython.
  • The related setting of the LVGL library is at lv_micropython repository/lib/lv_bindings/lv_conf.h file folder, which sets the refreshing frequency of the display, system occupied data and so on.
  • The application code of the LVGL library is at examples/LVGL_example.py and lib/LVGL.py of the project file folder.


LVGL Initialization

Before using LVGL image library, you need to import the LVGL library and initialize it.

  • Import LVGL library.
    • Demo location: lib/LVGL.py
    • Function: imports LVGL library, and use "lv" to call the function.
import lvgl as lv
  • Create LVGL object.
    • Demo location: examples/LCD_1in69_LVGL_test.py
    • Function: create LVGL object and import LCD and TSC object as the parameters.
# Init LVGL
LVGL(LCD=LCD,TSC=TSC)
  • LVGL initialization
    • Demo location: lib/LVGL.py
    • Demo description: LVGL core initialization is at the initialization function of LVGL class. It can automatically be called when creating the LVGL object.
if not lv.is_initialized(): lv.init()
Running LVGL

The LVGL library calls the function lv_tick_inc at regular intervals to notify LVGL of the elapsed time so that LVGL can update its internal time state and handle time-related tasks such as animations, timers, etc. The main function also calls the lv_task_handler function in a loop so that LVGL can handle events and tasks in a timely manner. The lv_task_handler function also needs to be called in the loop of the main function so that LVGL can handle events and tasks in time to ensure that the user interface responds and refreshes.

  • LVGL running interface
    • Demo location: lib/LVGL.py
    • Method: Create an "event_loop" object. In the initialization function of this class, we create a timer. This timer will automatically call the heartbeat interface function and the event handler function at set intervals. The interval between calls can be adjusted by passing the freq parameter, e.g. lv_utils.event_loop(freq=200), which defaults to 40 ms.
# Create event loop if not yet present
if not lv_utils.event_loop.is_running(): self.event_loop=lv_utils.event_loop()
  • LVGL heartbeat function:
    • Demo location: lib/lv_utils.py
    • Method: Need to make sure that lv_task_handler has a lower priority than lv_tick_inc, so in this case lv_tick_inc is called in the timer callback function.
#Timer callback function called every 5ms
self.timer.init(mode=Timer.PERIODIC, period=self.delay, callback=self.timer_cb) // here self.delay = 5

def timer_cb(self, t):
    lv.tick_inc(self.delay)
    if self.scheduled < self.max_scheduled:
        try:
            micropython.schedule(self.task_handler_ref, 0)
            self.scheduled += 1 # Increase in the number of tasks being processed
        except:
            pass
  • LVGL Task Processor
    • Demo location: lib/lv_utils.py
    • Method: Handle LVGL tasks, and need to regularly call lv.task_handler, this example calls lv.tick_inc in the timer callback function and then calls.
def task_handler(self, _):
    try:
        if lv._nesting.value == 0:
            lv.task_handler()
            if self.refresh_cb: self.refresh_cb()
        self.scheduled -= 1 # the handling tasks are decreasing 
    except Exception as e:
        if self.exception_sink:
            self.exception_sink(e)
LVGL Display

To display LVGL, you need to initialize a display driver and set various properties of the display driver, such as color format, draw buffer, rendering mode, and display callback functions. At each LV_DISP_DEF_REFR_PERIOD (set in lv_conf.h), LVGL detects if something has happened on the UI that requires recoloring. For example, a button is pressed, a chart is changed, an animation occurs, etc. When recoloring, LVGL calls the display callback function to finish drawing the image in the refresh area.

  • Set LVGL display refreshing rate
    • Demo location: lv_micropython/lib/lv_bindings/lv_conf.h
    • Method: in lv_conf.h, you can modify LV_DEF_REFR_PERIOD to change the refreshing time of the screen.
#define LV_DEF_REFR_PERIOD 10  // Unit:ms, 10ms
  • Display LVGL related variables definition
    • Demo location: lib/LVGL.py
    • Function: The size of buf0 and buf1 is set to 33% of the screen display area in order to realize the LVGL double buffer mechanism, which is able to reduce the jaggedness of large-area screen refresh while effectively improving the screen refresh rate; when using a single buffer, it is best to set it to 10% of the screen display area, which effectively reduces the system usage, but there will be a more noticeable jaggedness when refreshing a large-area image.
# Init LVGL display
buf1 = lv.draw_buf_create(self.LCD.width, self.LCD.height // 3 , color_format, 0)
buf2 = lv.draw_buf_create(self.LCD.width, self.LCD.height // 3, color_format, 0)   
  • LVGL display device registration
    • Demo location: lib/LVGL.py
    • Function: Refine the LVGL library core struct variables according to the design requirements, initialize the display driver disp_drv, and set up the draw buffer, which is a simple array used by LVGL to render the contents of the screen. Once rendering is ready, the contents of the draw buffer are sent to the display using the disp_drv_flush_cb function set in the display driver.
self.disp_drv = lv.display_create(self.LCD.width, self.LCD.height) #  Create a display driver object and set the width and height
self.disp_drv.set_color_format(color_format) # Set the color format as RGB565
self.disp_drv.set_draw_buffers(buf1, buf2)   # Set the drawing buffer  
self.disp_drv.set_render_mode(lv.DISPLAY_RENDER_MODE.PARTIAL) # Set the rendering mode to partial refresh mode   
self.disp_drv.set_flush_cb(self.disp_drv_flush_cb) # Set the display callback function  
  • LVGL display callback function:
    • Demo location: lib/LVGL.py
    • Function: mainly finish drawing in the refresh areas
def disp_drv_flush_cb(self,disp_drv,area,color_p)
Parameters:
    disp_drv: display the driver structure cursor, including the information related to the display and function cursor. This parameter is used to informing the refreshing is finished.
    area: Area structure cursor, including the position information of the area to be refreshed. In this demo, you can use it for creating TFT display window.
    color_p: color structure cursor, indicating the color data displayed in the refreshed area. In this demo, it can be sent the data to the SPI bus as the DMA input reading address, and then the image drawing is finished.
  • LVGL display callback function
    • Demo position: lib/LVGL.py
    • Method: to significantly reduce the cpu usage rate, you can use DMA to transmit color data. Set color_p as the reading address and the output data register of SPI bus can be set as the writing address. The code is long to show only part of the complete code, please download the sample to view:
def disp_drv_flush_cb(self,disp_drv,area,color_p):
    self.rp2_wait_dma() # wait for DMA space
    
    ......
    self.rgb565_swap_func(data_view, size)  # convert color format  
    
    self.LCD.setWindows(area.x1, area.y1, area.x2+1, area.y2+1) # Set LVGL interface display location 
    
    ...... # DMA Configuration

    self.rp2_dma.enable() # Enable DMA

    self.rp2_wait_dma() # Wait for DMA idle
           
    self.disp_drv.flush_ready() # Inform LVGL data transmission finished  
}
LVGL Input

In LVGL, users are allowed to register input devices such as touchpad, mouse, keyboard or encoder devices. Users can control the user interface through these input devices for better interaction.

  • LVGL calls input device callback functions for frequency settings.
    • Demo location:lv_micropython/lib/lv_bindings/lv_conf.h
    • How to set: it uses the same definition with the screen's refresh time. The input device callback function is called every 10ms to update the events triggered by the input device, which can be set by modifying the LV_DEF_REFR_PERIOD definition.
#define LV_DEF_REFR_PERIOD 10 // Unit:ms, here is 10ms
  • LVGL input device registration
    • Demo location: examples/LVGL_example.py
    • How to set: register touchscreen device indev_drv and initialize it.
# Init touch screen as input device
self.indev_drv = lv.indev_create()                 # Create object
self.indev_drv.set_type(lv.INDEV_TYPE.POINTER)     # Register the touchscreen 
self.indev_drv.set_read_cb(self.indev_drv_read_cb) # Set callback function
  • The callback function of the LVGL input devices
    • Demo location:lib/LVGL.py
    • Function: mainly for updating input events.
def indev_drv_read_cb(indev_drv, data)
Parameters:
  indev_drv: the cursor to the input device driver structure in LVGL. In this example, the structure is the touchscreen input device driver
  data: the cursor to the input device data structure in LVGL. In this example, this structure is used to store the state and data of the input device, including the current touch state (pressed or released) and the coordinates of the touch point
  • Callback function implementation for touchscreen input devices
    • Demo location:lib/CST816T.py, lib/LVGL.py
    • How to set: update the touch state and touch point coordinates through the touch interrupt function.
def Int_Callback(self,pin):
    if self.Mode == 0 :
        self.Gestures = self._read_byte(0x01)

    elif self.Mode == 1 :           
        self.Flag = 1
        self.get_point()
    
    elif self.Mode == 2 :           
        self.Flag = 1
        self.get_point()
        self.Gestures = self._read_byte(0x01)

def indev_drv_read_cb( self, indev_drv, data):
    self.rp2_wait_dma()  

    data.point.x = self.TSC.X_point
    data.point.y = self.TSC.Y_point
    
    data.state = 1 if self.TSC.Flag == 1 else 0 
    self.TSC.Flag = 0
LVGL Widget Layout

In LVGL, we can build a variety of different user interfaces. The basic components of an interface are objects, also known as widgets, such as buttons, labels, images, lists, charts, or text areas. Multiple controls can be created at the same time in an interface, and we can set their basic properties such as position, size, parent, style, and event handlers.

  • Create LVGL interface object
    • Demo location:examples/LVGL_example.py
    • Function: Create LVGL interface object, import LCD, TSC, IMU, RTC, and BEEP_pwm object as parameters
# Init WIDGETS
WIDGETS(LCD=LCD,TSC=TSC,IMU=IMU,RTC=RTC,BEEP_pwm=BEEP_pwm)
  • LVGL create blocks
    • Demo location:examples/LVGL_example.py
    • Function: A Tile view is a container object whose elements (called blocks) can be arranged in a grid. The user can navigate between the tiles by swiping. Use the Tile view object to call add_tile(tileview, row_id, col_id, dir) to create a new tile on row_id and col_id columns. dir can be lv.DIR.LEFT/RIGHT/TOP/BOTTOM/HOR/VER/ALL or a value to be slid into adjacent tiles in a given direction. to move to a neighboring block in the given direction.
//Create a block at (0,0), support sliding downward to (0,1)
self.tv = lv.tileview(self.scr)
self.tile1 = self.tv.add_tile(0, 0, lv.DIR.BOTTOM)
  • LVGL creates widget
    • Demo location: examples/LVGL_example.py
    • Function: create widget, different widget needs different function interface, and can select parent object to create.
//Create a table widget, tile2 is the parent object of this widget, you can replace it with widget with children object such as list and title 
self.table_imu_data = lv.table(self.tile2)
  • Align and place LVGL widgets
    • Demo location:examples/LVGL_example.py
    • Function: Enables the control to be positioned offset based on a reference point. The center of the reference point control to which the control is aligned for the offset.
    • Alignment standard: The LVGL library has both internal and external alignment. By default, the top-left corner is used as the origin, left is the positive horizontal direction, and down is the positive vertical direction.
//Position the control 15 pixels to the right of center.
self.table_imu_data.align(lv.ALIGN.CENTER, 15 ,0)

  • LVGL widget switch font size
    • Demo location:lv_micropython/lib/lv_bindings/lv_conf.h and examples/LVGL_example.py
    • Function:In practice, an interface may need to use multiple font sizes, you can enable multiple font sizes in lv_conf.h and set the default font size. When setting font sizes, you need to stylize the control so that it renders according to the set style. The add_style function enables the rendering of various parts of the control in different states.
#define LV_FONT_MONTSERRAT_16 1                        // Enable font 16
#define LV_FONT_MONTSERRAT_18 1                        // Enable font 18
#define LV_FONT_DEFAULT &lv_font_montserrat_18         // Set the default font size as 18

table_imu_data= lv.style_t()                           // Create object
table_imu_data.init()                                  // Initialization
table_imu_data.set_text_font(lv.font_montserrat_16)    // Set the font size as 16

self.table_imu_data.add_style(style_imu_table, 0)      // Set the style
  • LVGL Widget Event Handler
    • Demo location:examples/LVGL_example.py
    • Function:In LVGL, you can add an event handler callback function to a control so that when the control is clicked, scrolled, redrawn, etc., it triggers an event that goes to the event handler callback function. Call the obj.add_event_cb(event_cb, filter, None) function in your program to add the event filter handler event_cb to the control obj. The event_cb function is automatically called when the control obj triggers the filter event. The last argument is a pointer to any custom data available in the event.
//Add event LV_EVENT_VALUE_CHANGED handler sw_event_cb to control sw.
self.sw.add_event_cb(self.sw_event_cb, lv.EVENT.VALUE_CHANGED, None)