• sales

    +86-0755-88291180

RP2350-Touch-LCD-1.28 User Guide

Product overview

RP2350-Touch-LCD-1.28 is a low-cost, high-performance MCU board designed by Waveshare. Despite its compact size, it integrates a 1.28-inch capacitive touch LCD, a lithium-ion battery charging chip, an IMU sensor, and various other peripherals on board for easy development and embedding into the product.

Features

  • Adopts RP2350 microcontroller chip designed by Raspberry Pi in the United Kingdom
  • Adopts unique dual-core and dual-architecture design, equipped with dual-core ARM Cortex-M33 processor and dual-core Hazard3 RISC-V core processor, flexible clock running up to 150 MHz
  • Built-in 520KB of SRAM and 4MB of on-chip Flash
  • Using Type-C interface, keeping up with the trend of the times, no need to entangle the front and back plugging
  • Onboard 1.28inch capacitive touch display, 240×240 resolution, 65K color
  • Onboard Lithium battery recharge/discharge header, suitable for mobile scenarios
  • USB1.1 host and device support
  • Supports low-power sleep and hibernation modes
  • Drag-and-drop downloads can be made by USB recognition as mass storage
  • Temperature sensor
  • Accelerated floating-point libraries on-chip
  • 6 × GPIO pin via SH1.0 connector

Specifications

LCD parameters
Touch chipCST816STouch interfaceI2C
Display chipGC9A01ADisplay interfaceSPI
Resolution240(H)RGB x 240(V)Display sizeΦ32.4mm
Display panelIPSPixel size0.135(H)x0.135(V)mm


IMU parameters
Sensor nameQMI8658
Accelerometer characteristicsResolution: 16 bits
Range (optional): ±2, ±4, ±8, ±16g
Gyroscope characteristicsResolution: 16 bits
Range (optional): ±16, ±32, ±64, ±128, ±256, ±512, ±1024, ±2048°/sec

Precautions

  1. At the edge of the circular touch screen, the touch sensitivity and accuracy will be reduced, which is caused by the structure of the circular touch screen.

Pinout definition

Dimensions


Pico Getting Started

Basic Introduction

Raspberry Pi Pico Basics

MicroPython Series

Install Thonny IDE

In order to facilitate the development of Pico/Pico2 boards using MicroPython on a computer, it is recommended to download the Thonny IDE

  • Download Thonny IDE and follow the steps to install, the installation packages are all Windows versions, please refer to Thonny's official website for other versions
  • After installation, the language and motherboard environment need to be configured for the first use. Since we are using Pico/Pico2, pay attention to selecting the Raspberry Pi option for the motherboard environment
  • Configure MicroPython environment and choose Pico/Pico2 port
    • Connect Pico/Pico2 to your computer first, and in the lower right corner of Thonny left-click on the configuration environment option --> select Configture interpreter
    • In the pop-up window, select MicroPython (Raspberry Pi Pico), and choose the corresponding port

Flash Firmware

  • Click OK to return to the Thonny main interface, download the corresponding firmware library and burn it to the device, and then click the Stop button to display the current environment in the Shell window
  • Note: Flashing the Pico2 firmware provided by Micropython may cause the device to be unrecognized, please use the firmware below or in the package
  • How to download the firmware library for Pico/Pico2 in windows: After holding down the BOOT button and connecting to the computer, release the BOOT button, a removable disk will appear on the computer, copy the firmware library into it
  • How to download the firmware library for RP2040/RP2350 in windows: After connecting to the computer, press the BOOT key and the RESET key at the same time, release the RESET key first and then release the BOOT key, a removable disk will appear on the computer, copy the firmware library into it (you can also use the Pico/Pico2 method)

MicroPython Series Tutorials

【MicroPython】 machine.Pin class function details
【MicroPython】machine.PWM class function details
【MicroPython】machine.ADC class function details
【MicroPython】machine.UART class function details
【MicroPython】machine.I2C class function details
【MicroPython】machine.SPI class function details
【MicroPython】rp2.StateMachine class function details

C/C++ Series

For C/C++, it is recommended to use Pico VS Code for development. This is a Microsoft Visual Studio Code extension designed to make it easier for you to create, develop, and debug projects for the Raspberry Pi Pico series development boards. No matter if you are a beginner or an experienced professional, this tool can assist you in developing Pico with confidence and ease. Here's how to install and use the extension.

  • Official website tutorial: https://www.raspberrypi.com/news/pico-vscode-extension/
  • This tutorial is suitable for Raspberry Pi Pico, Pico2 and the RP2040 and RP2350 series development boards developed by Waveshare
  • The development environment defaults to Windows11. For other environments, please refer to the official tutorial for installation

Install VSCode

  1. First, click to download pico-vscode package, unzip and open the package, double-click to install VSCode

    Note: If vscode is installed, check if the version is v1.87.0 or later

Install Extension

  1. Click Extensions and select Install from VSIX
  2. Select the package with the vsix suffix and click Install
  3. Then vscode will automatically install raspberry-pi-pico and its dependency extensions, you can click Refresh to check the installation progress
  4. The text in the right lower corner shows that the installation is complete. Close VSCode

Configure Extension

  1. Open directory C:\Users\username and copy the entire .pico-sdk to that directory
  2. The Copy is completed
  3. Open vscode and configure the paths for the Raspberry Pi Pico extensions

    The configuration is as follows:
    Cmake Path:
    ${HOME}/.pico-sdk/cmake/v3.28.6/bin/cmake.exe
    
    Git Path:
    ${HOME}/.pico-sdk/git/cmd/git.exe    
    
    Ninja Path:
    ${HOME}/.pico-sdk/ninja/v1.12.1/ninja.exe
    
    Python3 Path:
    ${HOME}/.pico-sdk/python/3.12.1/python.exe             
    

New Project

  1. The configuration is complete, create a new project, enter the project name, select the path, and click Create to create the project
    To test the official example, you can click on the Example next to the project name to select
  2. The project is created successfully
  3. Select the SDK version
  4. Select Yes for advanced configuration
  5. Choose the cross-compilation chain, 13.2.Rel1 is applicable for ARM cores, RISCV.13.3 is applicable for RISCV cores. You can select either based on your requirements
  6. Select default for CMake version (the path configured earlier)
  7. Select default for Ninjaversion
  8. Select the development board
  9. Click Complie to compile
  10. The uf2 format file is successfully compiled

Import Project

  1. The Cmake file of the imported project cannot have Chinese (including comments), otherwise the import may fail
  2. To import your own project, you need to add a line of code to the Cmake file to switch between pico and pico2 normally, otherwise even if pico2 is selected, the compiled firmware will still be suitable for pico

    set(PICO_BOARD pico CACHE STRING "Board type")

Update Extension

  1. The extension version in the offline package is 0.15.2, and you can also choose to update to the latest version after the installation is complete

Arduino IDE Series

Install Arduino IDE

  1. First, go to Arduino official website to download the installation package of the Arduino IDE.
  2. Here, you can select Just Download.
  3. Once the download is complete, click Install.
    Notice: During the installation process, it will prompt you to install the driver, just click Install

Arduino IDE Interface

  1. After the first installation, when you open the Arduino IDE, it will be in English. You can switch to other languages in File --> Preferences, or continue using the English interface.
  2. In the Language field, select the language you want to switch to, and click OK.

Install Arduino-Pico Core in Arduino IDE

  1. Open the Arduino IDE, click on the file in the top left corner, and select Preferences
  2. Add the following link to the attached board manager URL, and then click OK
    https://github.com/earlephilhower/arduino-pico/releases/download/4.0.2/package_rp2040_index.json


    Note: If you already have an ESP32 board URL, you can use a comma to separate the URLs as follows:

    https://dl.espressif.com/dl/package_esp32_index.json,https://github.com/earlephilhower/arduino-pico/releases/download/4.0.2/package_rp2040_index.json
  3. Click Tools > Development Board > Board Manager > Search pico, as my computer has already been installed, it shows that it is installed

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 after the computer recognizes a removable hard disk (RPI-RP2).
  2. Download the program and open D1-LED.ino under the arduino\PWM\D1-LED path
  3. Click Tools --> Port, remember the existing COM, do not click this COM (the COM displayed is different on different computers, remember the COM on your own computer)
  4. Connect the driver board to the computer using a USB cable. Then, go to Tools > Port. For the first connection, select uf2 Board. After uploading, when you connect again, an additional COM port will appear
  5. Click Tools > Development Board > Raspberry Pi Pico > Raspberry Pi Pico or Raspberry Pi Pico 2
  6. After setting it up, click the right arrow to upload the program
  • If issues arise during this period, and if you need to reinstall or update the Arduino IDE version, it is necessary to uninstall the Arduino IDE completely. After uninstalling the software, you need to manually delete all contents within the C:\Users\[name]\AppData\Local\Arduino15 folder (you need to show hidden files to see this folder). Then, proceed with a fresh installation.

Open Source Demos

MircoPython video demo (github)
MicroPython firmware/Blink demos (C)
Raspberry Pi official C/C++ demo (github)
Raspberry Pi official micropython demo (github)
Arduino official C/C++ demo (github)


GUI API Details

If you have used our SPI screen before, you should be familiar with this example program

C

Underlying Hardware Interfaces

We have encapsulated the underlying layer, and due to different hardware platforms, the internal implementation is different. If you need to understand the internal implementation, you can check the corresponding directory
You can see a lot of definitions in DEV_Config.c(.h), which can be found in the directory: c\lib\Config

  • Data type:
#define UBYTE   uint8_t
#define UWORD   uint16_t
#define UDOUBLE uint32_t
  • Module initialization and exit processing:
void DEV_Module_Init(void);
void DEV_Module_Exit(void);
Note:
1. Here are some GPIOs before and after using the LCD screen.
  • GPIO read/write:
void 	DEV_Digital_Write(UWORD Pin, UBYTE Value);
UBYTE 	DEV_Digital_Read(UWORD Pin);
  • SPI write data:
void DEV_SPI_WriteByte(UBYTE Value);

Upper Layer Applications

For the screen, what if you need to paint, display Chinese and English characters, display pictures, etc., these are all done by the upper layer applications. Many of you have asked about some graphics processing, and we have provided some basic functions here The GUI can be found in the directory: c\lib\GUI\GUI_Paint.c(.h)

In the following directory are the character fonts that the GUI depends on: c\lib\Fonts

  • New Image Property: Create a new image property that includes the name, width, height, angle of rotation, and color of the image cache
void Paint_NewImage(UWORD *image, UWORD Width, UWORD Height, UWORD Rotate, UWORD Color)
Parameters:
 	image:  The name of the image cache, which is actually a pointer to the first address of the image cache;
 	Width:  The width of the image cache;
 	Height: The height of image cache;
 	Rotate: The angle of image rotation;
 	Color:  The initial color of the image;
  • Select Image Cache: The purpose of selecting the image cache is that you can create multiple image properties, and there can be multiple image caches. You can select each image you create
void Paint_SelectImage(UBYTE *image)
Parameters:
 	image: The name of the image cache, which is actually a pointer to the first address of the image cache;
  • Image Rotation: Set the rotation angle of the selected image, preferably after Paint_SelectImage (), you can choose to rotate 0, 90, 180, 270
void Paint_SetRotate(UWORD Rotate)
Parameters:
 	Rotate: Image rotation angles can be chosen as ROTATE_0, ROTATE_90, ROTATE_180, ROTATE_270 corresponding to 0, 90, 180, 270 degrees
【Note】Under different selection angles, the coordinates correspond to different starting pixels. Here taking 1.14 as an example, there are four images in sequence: 0°, 90°, 180°, 270°. For reference only
  • Image Mirror Flip: Set the mirror flip of the selected image, you can choose no mirroring, horizontal mirroring, vertical mirroring, and mirroring around the image center
void Paint_SetMirroring(UBYTE mirror)
Parameters:
 	mirror: The mirroring method of the image can be selected as MIRROR_NONE, MIRROR_HORIZONTAL, MIRROR_VERTICAL, and MIRROR_ORIGIN, which correspond to no mirroring, horizontal mirroring, vertical mirroring, and mirroring around the image center, respectively
  • Set the position and color of the point to be displayed in the cache: This is the core function of the GUI, handling point position and color displayed in the cache;
void Paint_SetPixel(UWORD Xpoint, UWORD Ypoint, UWORD Color)
Parameters:
 	Xpoint: The X position of the point in the image cache
 	Ypoint: The Y position of the point in the image cache
 	Color:  The color of the point
  • Image cache fill color: Fill the image cache with a certain color, which is generally used as a screen whitening
void Paint_Clear(UWORD Color)
Parameters:
 	Color: The filled color
  • Fill color of image cache window: Fill a certain part of the image cache window with a certain color, usually used as a window whitening function, commonly used for displaying time, whitening for one second
void Paint_ClearWindows(UWORD Xstart, UWORD Ystart, UWORD Xend, UWORD Yend, UWORD Color)
Parameters:
 	Xstart: The starting point X coordinate of the window
 	Ystart: The starting point Y coordinate of the window
 	Xend:   The endpoint X coordinate of the window
 	Yend:   The endpoint Y coordinate of the window
 	Color:  The filled color
  • Draw a dot: In the image cache, draw a dot at (Xpoint, Ypoint), you can choose the color, the size, and the style of the dot
void Paint_DrawPoint(UWORD Xpoint, UWORD Ypoint, UWORD Color, DOT_PIXEL Dot_Pixel, DOT_STYLE Dot_Style)
Parameters:
 	Xpoint: The X coordinate of the dot
 	Ypoint: The Y coordinate of the dot
 	Color:  The filled color
 	Dot_Pixel: The size of the dots, which provides the default 8 sizes of dots
 	 	 typedef enum {
 	 	 	 DOT_PIXEL_1X1  = 1,	// 1 x 1
 	 	 	 DOT_PIXEL_2X2  , 		// 2 X 2
 	 	 	 DOT_PIXEL_3X3  , 	 	// 3 X 3
 	 	 	 DOT_PIXEL_4X4  , 	 	// 4 X 4
 	 	 	 DOT_PIXEL_5X5  , 		// 5 X 5
 	 	 	 DOT_PIXEL_6X6  , 		// 6 X 6
 	 	 	 DOT_PIXEL_7X7  , 		// 7 X 7
 	 	 	 DOT_PIXEL_8X8  , 		// 8 X 8
 	 	} DOT_PIXEL;
 	Dot_Style: The style of the dot, the method to expand the size of the dot is that, whether to expand from the dot as the center or from the dot as the lower left corner to the upper right
 	 	typedef enum {
 	 	   DOT_FILL_AROUND  = 1,		
 	 	   DOT_FILL_RIGHTUP,
 	 	} DOT_STYLE;
  • Draw a line: In the image cache, you can draw a line from (Xstart, Ystart) to (Xend, Yend) and choose the color, the width and the style of the line
void Paint_DrawLine(UWORD Xstart, UWORD Ystart, UWORD Xend, UWORD Yend, UWORD Color, LINE_STYLE Line_Style , LINE_STYLE Line_Style)
Parameters:
 	Xstart: The starting point X coordinate of the line
 	Ystart: The starting point Y coordinate of the line
 	Xend:   The endpoint X coordinate of the line
 	Yend:   The endpoint Y coordinate of the line
 	Color:  The filled color
 	Line_width: The width of the lines, which provides the default 8 widths
 	 	typedef enum {
 	 	 	 DOT_PIXEL_1X1  = 1,	// 1 x 1
 	 	 	 DOT_PIXEL_2X2  , 		// 2 X 2
 	 	 	 DOT_PIXEL_3X3  ,		// 3 X 3
 	 	 	 DOT_PIXEL_4X4  ,		// 4 X 4
 	 	 	 DOT_PIXEL_5X5  , 		// 5 X 5
 	 	 	 DOT_PIXEL_6X6  , 		// 6 X 6
 	 	 	 DOT_PIXEL_7X7  , 		// 7 X 7
 	 	 	 DOT_PIXEL_8X8  , 		// 8 X 8
 	 	} DOT_PIXEL;
 	 Line_Style: The style of the line, which selects whether the line is connected in a straight line or as a dashed line
 	 	typedef enum {
 	 	 	 LINE_STYLE_SOLID = 0,
 	 	 	 LINE_STYLE_DOTTED,
 	 	} LINE_STYLE;
  • Draw a rectangle: In the image cache, you can draw a rectangle from (Xstart, Ystart) to (Xend, Yend) and choose the color, the width of the line, and whether to fill the inside of the rectangle
void Paint_DrawRectangle(UWORD Xstart, UWORD Ystart, UWORD Xend, UWORD Yend, UWORD Color, DOT_PIXEL Line_width, DRAW_FILL Draw_Fill)
Parameters:
 	Xstart: The starting point X coordinate of the rectangle
 	Ystart: The starting point Y coordinate of the rectangle
 	Xend:   The endpoint X coordinate of the rectangle
 	Yend:   The endpoint Y coordinate of the rectangle
 	Color:  The filled color
 	Line_width: The width of the four sides of the rectangle, providing the default 8 widths
 	 	typedef enum {
 	 	 	 DOT_PIXEL_1X1  = 1,	// 1 x 1
 	 	 	 DOT_PIXEL_2X2  , 		// 2 X 2
 	 	 	 DOT_PIXEL_3X3  ,		// 3 X 3
 	 	 	 DOT_PIXEL_4X4  ,		// 4 X 4
 	 	 	 DOT_PIXEL_5X5  , 		// 5 X 5
 	 	 	 DOT_PIXEL_6X6  , 		// 6 X 6
 	 	 	 DOT_PIXEL_7X7  , 		// 7 X 7
 	 	 	 DOT_PIXEL_8X8  , 		// 8 X 8
 	 	} DOT_PIXEL;
 	Draw_Fill: Filling, whether to fill the inside of the rectangle
 	 	typedef enum {
 	 	 	 DRAW_FILL_EMPTY = 0,
 	 	 	 DRAW_FILL_FULL,
 	 	} DRAW_FILL;
  • Draw a circle: In the image cache, draw a circle with a radius of Radius with (X_Center Y_Center) as the center of the circle, and you can choose the color, the width of the line, and whether to fill the inside of the circle
void Paint_DrawCircle(UWORD X_Center, UWORD Y_Center, UWORD Radius, UWORD Color, DOT_PIXEL Line_width, DRAW_FILL Draw_Fill)
Parameters:
 	X_Center: The X coordinate of the center of the circle
 	Y_Center: The Y coordinate of the center of the circle
 	Radius:  The radius of the circle
 	Color:    The filled color
 	Line_width: The width of the arc, which provides the default 8 widths
 	 	typedef enum {
 	 	 	 DOT_PIXEL_1X1  = 1,	// 1 x 1
 	 	 	 DOT_PIXEL_2X2  , 		// 2 X 2
 	 	 	 DOT_PIXEL_3X3  ,		// 3 X 3
 	 	 	 DOT_PIXEL_4X4  ,		// 4 X 4
 	 	 	 DOT_PIXEL_5X5  , 		// 5 X 5
 	 	 	 DOT_PIXEL_6X6  , 		// 6 X 6
 	 	 	 DOT_PIXEL_7X7  , 		// 7 X 7
 	 	 	 DOT_PIXEL_8X8  , 		// 8 X 8
 	 	} DOT_PIXEL;
 	Draw_Fill: Filling, whether to fill the inside of the circle
 	 	typedef enum {
 	 	 	 DRAW_FILL_EMPTY = 0,
 	 	 	 DRAW_FILL_FULL,
 	 	} DRAW_FILL;
  • Write Ascii characters: In the image cache, with (Xstart Ystart) as the left vertex, write an Ascii character, and you can select the Ascii code visual character font library, font foreground color, and font background color
void Paint_DrawChar(UWORD Xstart, UWORD Ystart, const char Ascii_Char, sFONT* Font, UWORD Color_Foreground, UWORD Color_Background)
Parameters:
 	Xstart: The left vertex X coordinate of the character
 	Ystart: The left vertex Y coordinate of the character
 	Ascii_Char: Ascii character
 	Font: The Ascii code visual character font library, provides the following fonts in the Fonts folder:
 	 	font8: 5*8 font
 	 	font12: 7*12 font
 	 	font16: 11*16 font
 	 	font20: 14*20 font
 	 	font24: 17*24 font
 	Color_Foreground: Font color
 	Color_Background: Background color
  • Write English strings: In the image cache, write a string of English characters with (Xstart Ystart) as the left vertex, and you can select the Ascii code visual character font library, font foreground color, and font background color
void Paint_DrawString_EN(UWORD Xstart, UWORD Ystart, const char * pString, sFONT* Font, UWORD Color_Foreground, UWORD Color_Background)
Parameters:
 	Xstart:  The left vertex X coordinate of the character
 	Ystart:  The left vertex Y coordinate of the character
 	pString: A string, a string is a pointer
 	Font: The Ascii code visual character font library, provides the following fonts in the Fonts folder:
 	 	font8: 5*8 font
 	 	font12: 7*12 font
 	 	font16: 11*16 font
 	 	font20: 14*20 font
 	 	font24: 17*24 font
 	Color_Foreground: Font color
 	Color_Background: Background color
  • Write Chinese strings: In the image cache, write a string of Chinese characters with (Xstart Ystart) as the left vertex, and you can select the GB2312 encoding character font library, font foreground color, and font background color
void Paint_DrawString_CN(UWORD Xstart, UWORD Ystart, const char * pString, cFONT* font, UWORD Color_Foreground, UWORD Color_Background)
Parameters:
 	Xstart:  The left vertex X coordinate of the character
 	Ystart:  The left vertex Y coordinate of the character
 	pString: A string, a string is a pointer
 	Font: The GB2312 encoding character library, the following fonts are provided in the Fonts folder:
 	 	font12CN: Ascii character font 11*21, Chinese font 16*21
 	 	font24CN: Ascii character font 24*41, Chinese font 32*41
 	Color_Foreground: Font color
 	Color_Background: Background color
  • Write numbers: In the image cache, write a string of numbers with (Xstart Ystart) as the left vertex, and you can select the Ascii code visual character font library, font foreground color, and font background color
void Paint_DrawNum(UWORD Xpoint, UWORD Ypoint, double Nummber, sFONT* Font, UWORD Digit,UWORD Color_Foreground, UWORD Color_Background);
Parameters:
 	Xstart:  The left vertex X coordinate of the character
 	Ystart:  The left vertex Y coordinate of the character
 	Number: The displayed number, here is stored in a 32-bit long int type, which can be displayed up to 2147483647.
 	Font: The Ascii code visual character font library, provides the following fonts in the Fonts folder:
 	 	font8: 5*8 font
 	 	font12: 7*12 font
 	 	font16: 11*16 font
 	 	font20: 14*20 font
 	 	font24: 17*24 font
        Digit: Display decimal places
 	Color_Foreground: Font color
 	Color_Background: Background color
  • Display time: In the image cache, display time with (Xstart Ystart) as the left vertex, and you can select the Ascii code visual character font library, font foreground color, and font background color
void Paint_DrawTime(UWORD Xstart, UWORD Ystart, PAINT_TIME *pTime, sFONT* Font, UWORD Color_Background, UWORD Color_Foreground)
Parameters:
 	Xstart: The left vertex X coordinate of the character
 	Ystart: The left vertex Y coordinate of the character
 	pTime:  The displayed time, here defines a time structure, just pass the numbers of the hour, minute, second to the parameters
 	Font: The Ascii code visual character font library, provides the following fonts in the Fonts folder:
 	 	font8: 5*8 font
 	 	font12: 7*12 font
 	 	font16: 11*16 font
 	 	font20: 14*20 font
 	 	font24: 17*24 font
 	Color_Foreground: Font color
 	Color_Background: Background color

QMI8658

  • Module initialization
unsigned char QMI8658_init(void);
  • Read data
void QMI8658_read_xyz(float acc[3], float gyro[3], unsigned int *tim_count);
Parameters:
	 float acc[3]: The array that stores acceleration values, represented as floating-point numbers, contains three elements, which are acceleration on the X, Y, and Z axes
	 float gyro[3]: The array that stores gyroscope values, represented as floating-point numbers, contains three elements, which are angular velocities on the X, Y, and Z axes

Python

Underlying Hardware Interface

  • Module initialization
def __init__(self)
  • Send a command
def write_cmd(self, cmd)
  • Send data
def write_data(self, buf)
  • Adjust backlight
def set_bl_pwm(self,duty)

Drawing GUI

  • Import a library
import framebuf
  • Create an object

The custom class LCD_1inch28 inherits the framebuf.FrameBuffer class from MicroPython, which provides many methods to draw images, let's create the LCD_1inch28 class object first

LCD = LCD_1inch28()
  • Draw a line
LCD.line(x1, y1, x2, y2, color)
Parameters:
	x1, y1: The x and y coordinates of the starting point
	x2, y2: The x and y coordinates of the endpoint
	color:  The color of the line
  • Draw a rectangle
LCD.fill_rect(x1, y1, w, h, color)
Parameters:
	x1, y1: The x and y coordinates of the upper left corner of the rectangle
        w, h:   The width and height of the rectangle
        color:  The filling color of the rectangle
  • Draw text
LCD.text(str, x, y, color)
Parameters:
         str:   Display text
	 x, y:  The x and y coordinates of the upper left corner of the text
         color: The color of the text
  • Change the text size
LCD.write_text(str, x, y, size, color)
Parameters:
	 size: The difference between this function and LCD.text is that it supports custom font size, which is used to specify the font size
  • Display
LCD.show()

QMI8658

  • Create an object
qmi8658=QMI8658()
  • Read and parse the XYZ data of the sensor
xyz=qmi8658.Read_XYZ()
Return values:
          xyz[0]~xyz[2]: The function returns an array, with the first three elements representing acceleration on the X, Y, and Z axes
	  xyz[3]~xyz[5]: These three elements represent the angular velocities on the X, Y, and Z axes

LVGL Demos

C

Example Demonstration

This example shows three interfaces that can be switched by swiping on the touchscreen or by lifting the IMU sensor.

  • The first interface
    • Display content: The logo of Waveshare Electronics.

  • The second interface
    • Display content: Data from the IMU sensor.
    • Refresh rate: Updated every 500ms.
    • Switching mode: You can switch by swiping from the bottom to the top on the touch screen of the first interface, or you can use the IMU sensor to lift the right to trigger the input device to determine the event selection interface, and then lift up to trigger the switching event to realize interface switching.

  • The third interface
    • Display content: Four controllable elements that can perform simple interactions.
    • Interaction method: You can touch to interact with the touch screen, or you can use the IMU sensor to lift the interaction (lift up and down to switch the selected widget, lift right for the first time to enter the editing mode, and lift right again to interact).
    • Switching mode: You can switch by swiping from the bottom to the top on the touch screen of the second interface, or you can use the IMU sensor to lift the right to trigger the input device to determine the event selection interface, and then lift up to trigger the switching event to realize interface switching.


      Note: Since the widget is a sub-object of the interface, it is not possible to switch from the selected interface to the selected widget when using an IMU sensor for interaction. You can reselect the widget by touching.

Demo Introduction

This example is used to test the interaction and style beautification of LVGL widgets. For specific development of LVGL, please refer to LVGL development documentation.

Source Code Structure

  • The source code of the LVGL library is located in the project folder at ./lib/lvgl, using version 8.1. For secondary development, please refer to the development documentation corresponding to the used version.
  • The related settings for the LVGL library are located in the project folder's ./examples/inc/lv_conf.h, where you can set display refresh rates, system usage data, and so on.
    Img1.png
  • The application code for the LVGL library is located in the project folder at ./examples/src/LVGL_example.c.
    RP2040-Touch-LCD-1.28-LVGL-src.png

Implement Function

  1. In this example, DMA is used to transfer color data to the SPI bus, reducing CPU utilization, and controlling CPU usage to less than 20% and memory usage to less than 30% during simple interactions.
  2. The example system clock is 270MHz. The peripheral clock frequency for SPI is set to match the system clock, and the LVGL library's dual buffer mechanism is utilized. Data transfer occurs in one buffer while the other buffer is rendering, ensuring smooth animation.
  3. In this example, a touch screen and an IMU sensor are used as two input devices, where the IMU simulator uses gyroscope data to simulate as an encoder input device, which can be applied to multiple interactive scenarios.

Compile and Run

  • Enter the source code directory, if the build directory already exists, you can go directly into it. If not, execute:
    mkdir build
  • Go to the build directory and add the address of the SDK:
    cd build
    export PICO_SDK_PATH=../../pico-sdk

Note: The path of the pico-sdk needs to be adjusted according to the actual situation

  • Execute cmake, automatically generate the Makefile file:
    cmake ..
  • Execute make to generate the executable file, and then enter in the terminal:
    make

After compilation, copy the generated file in the .uf2 format to the pico.

Source Code Analysis

LVGL Initialization

  • Initialization function for the LVGL library
    • Implementation function: Mainly used to initialize the hardware and structure variables required for LVGL.
    void LVGL_Init(void);
  • LVGL library core structure variable definition
    • Definition function: The initialization process of the LVGL library primarily involves initializing several core structure variables of the LVGL library, and the operation of the LVGL library relies on these core structure variables.
    • Definition method: The sizes of buf0 and buf1 are set to half of the screen display area to implement the LVGL dual buffer mechanism, which reduces the appearance of jagged edges during large area screen updates while effectively increasing the screen refresh rate; when using a single buffer, it is best to set it to 10% of the screen display area, which can effectively reduce system usage but may result in noticeable jagged edges during large image updates.
    // LVGL
    static lv_disp_draw_buf_t disp_buf;                   //LVGL displays buffer
    static lv_color_t buf0[DISP_HOR_RES * DISP_VER_RES/2];//LVGL color data buffer 0
    static lv_color_t buf1[DISP_HOR_RES * DISP_VER_RES/2];//LVGL color data buffer 1
    static lv_disp_drv_t disp_drv;                        //LVGL display driver
    
    static lv_indev_drv_t indev_ts;                         //LVGL touch screen input device driver
    static lv_indev_drv_t indev_en;                       //LVGL IMU sensor analog encoder input device drive
    static lv_group_t *group;                             //Encoder widget group                  
    
  • LVGL library initialization function implementation
    • Implementation function: Improve the core structure variables of the LVGL library according to the design requirements.
    • Implementation method: The LVGL library's encoder input device features editing and browsing modes. In browsing mode, triggering a switch event causes the selection of a widget within the widget group to change. Triggering a confirmation event leads to entering the editing mode of the selected widget. Therefore, when making an IMU sensor simulate an encoder, it is necessary to introduce an lv_group_t structure variable as the widget group for the IMU sensor.
    // Initialize LVGL core
    lv_init();
    
    // Initialize the LVGL display buffer structure variable disp_buf
    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);   
    
    // Initialize touch screen input device
    lv_indev_drv_init(&indev_ts); 
    indev_ts.type = LV_INDEV_TYPE_POINTER;    
    indev_ts.read_cb = ts_read_cb;            
    lv_indev_t * ts_indev = lv_indev_drv_register(&indev_ts);
    
    // Initialize the encoder input device
    lv_indev_drv_init(&indev_en);   
    indev_en.type = LV_INDEV_TYPE_ENCODER;  
    indev_en.read_cb = encoder_read_cb;         
    lv_indev_t * encoder_indev = lv_indev_drv_register(&indev_en);
    group = lv_group_create();
    lv_indev_set_group(encoder_indev, group);//Add the encoder widget device to the widget group
    
  • LVGL library run implementation
    • Function implementation: The LVGL library periodically calls the function lv_tick_inc to notify LVGL of the past time, so that LVGL can update its internal time state and handle time-related tasks such as animations, timers, etc. In the loop of the main function, it is also necessary to call the lv_task_handler function to process some set tasks.
    • Implementation method: It is necessary to ensure that the priority of lv_task_handler is lower than the priority of lv_tick_inc. Therefore, in this example, lv_tick_inc is called within the timer callback function.
    add_repeating_timer_ms(5, repeating_lvgl_timer_callback, NULL, &lvgl_timer);//The timer callback function is called every 5 ms
    
    static bool repeating_lvgl_timer_callback(struct repeating_timer *t) 
    {
        lv_tick_inc(5);
        return true;
    }
    
    int main()
    {
        ...
        while(1)
        {
          lv_task_handler();
          DEV_Delay_ms(5); 
        }
    
    }
    

LVGL Display

  • LVGL displays callback functions
    • Implementation function: Mainly complete the drawing of the image in the refresh area.
    void disp_flush( lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p )
    Parameter:
        lv_disp_drv_t *disp_drv: Displays driver structure pointers, which contain information about the display and function pointers. This parameter is often used to notify you that a refresh is complete
        const lv_area_t *area  : Region structure pointer, containing the position information of the area to be refreshed. In this case, the window used to create the TFT display
        lv_color_t *color_p    : Color structure pointer, indicating the color data to be displayed in the refresh area. In this case, the DMA input reads the address to transfer data to the SPI bus, completing the drawing of the image
    
  • LVGL display color setting
    • Purpose of setting: Since the pixel color storage method constructed by the lv_color_t structure in its default state does not match the data to be transmitted in this example, direct transmission would result in color discrepancies in the displayed image.
    • Setting method: Modify the storage method for color settings in the lv_conf.h directory.
    #define LV_COLOR_16_SWAP 1
    
  • LVGL display refresh rate setting
    • Setting method: In the lv_conf.h file, you can also set the display buffer refresh rate. You can modify this definition to change the screen refresh time.
    #define LV_DISP_DEF_REFR_PERIOD  10     /*[ms]*/
  • LVGL display callback function implementation
    • Implementation method: In this example, to maximize the reduction of processor utilization, DMA is used for color data transmission. color_p is set as the read address, and the SPI bus output data register is set as the write address.
    static void disp_flush_cb(lv_disp_drv_t * disp, const lv_area_t * area, lv_color_t * color_p)
    {
    
        LCD_1IN28_SetWindows(area->x1, area->y1, area->x2 , area->y2);//Set the display area for the image
        dma_channel_configure(dma_tx,
                              &c,
                              &spi_get_hw(LCD_SPI_PORT)->dr,          //The output data register address of the SPI bus
                              color_p,                                //The address of the color data array to be refreshed
                              ((area->x2 + 1 - area-> x1)*(area->y2 + 1 - area -> y1))*2,
                              true                                    //Transfers are made as soon as they are set up
                              );
    }
    
  • LVGL refresh completion notification is implemented
    • Implementation function: The LVGL core needs to be notified after each image refresh is completed, so that LVGL can prepare the next refresh image for rendering.
    • Implementation method: In this example, the completion of the DMA transmission is notified in the interrupt service function for LVGL image refresh. If a blocking notification mechanism is used, it is not possible to utilize the double buffering mechanism to increase the refresh rate.
    static void dma_handler(void)
    {
        if (dma_channel_get_irq0_status(dma_tx)) {
            dma_channel_acknowledge_irq0(dma_tx);
            lv_disp_flush_ready(&disp_drv);         
        }
    }
    

LVGL Input

  • LVGL's input device callback function
    • Implementation function: Mainly used to update input events.
    static void ts_read_cb(lv_indev_drv_t * drv, lv_indev_data_t*data);
    Parameter:
      lv_indev_drv_t *indev_drv: Pointer to the input device driver structure in LVGL. In this case, the structure is for a touch screen input device driver
      lv_indev_data_t *data    : Pointer to the input device data structure in LVGL. In this case, the structure is used to store the status and data of the input device, including the current touch state (pressed or released) and the coordinates of the touch points
    static void encoder_read_cb(lv_indev_drv_t * drv, lv_indev_data_t*data);
    Parameter:
      lv_indev_drv_t *indev_drv: Pointer to the input device driver structure in LVGL. In this case, the structure is a driver for an encoder input device simulating an IMU sensor
      lv_indev_data_t *data    : Pointer to the input device data structure in LVGL. In this case, the structure is used to store the status and data of the input device, including the current switch events (up or down) and the determining events (right up)
    
  • The frequency setting of LVGL calling the callback function of the input device
    • Setting method: By default, LVGL calls 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     /*[ms]*/
    
  • Callback function implementation of touch screen input device
    • Implementation method: It is mainly to update the touch state and touch point coordinates of the touch screen through touch interrupt.
    static void touch_callback(uint gpio, uint32_t events)
    {
      if (gpio == Touch_INT_PIN)
      {
          CST816S_Get_Point();//Update global variable Touch_CTS816
          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;
    }
    
    
  • IMU sensor input device callback function implementation
    • Operation method: Simulates an encoder as an input device. Trigger switching events by lifting up and down, and trigger confirmation events by lifting to the right.
    • Implementation: Due to the frequent data transformation of the IMU sensor, in order to reduce the misoperation, the switching event and determination event of the equipment are updated by timing multiple sampling.
    static bool repeating_imu_diff_timer_callback(struct repeating_timer *t);//Input events are updated every 50ms
    {
        get_diff_data();//Multiple sampling identification of input events updates the global variables encoder_diff and encoder_act
        return true;
    }
    
    static void encoder_read_cb(lv_indev_drv_t * drv, lv_indev_data_t*data)
    {
      data->enc_diff = encoder_diff;
      data->state    = encoder_act; 
    }
    

LVGL Widget Layout

  • LVGL widget initialization function
    • Implemented function: primarily used for stylization widgets and layout widgets.
    void Widgets_Init(void);
  • LVGL widget creation function
    • Implementation function: To create a widget, different widgets need to use different function interfaces, you can choose the parent object to create it.
    lv_obj_t *btn = lv_btn_create(lv_scr_act()); //Create a widget in which the lv_scr_act layer is the parent object of the key, which can be replaced with a widget that can have child objects, such as list and title
  • Alignment positioning of LVGL widget
    • Implemented function: Enables widget elements to be offset and positioned based on a reference point. The widget aligns to the center of the offset reference point widget.
    • Alignment standard: The LVGL library supports both internal and external alignment. By default, the upper-left corner is the origin, the left is the positive direction of the horizontal direction, and the downward direction is the positive direction of the vertical direction.
    lv_obj_align(btn, LV_ALIGN_CENTER, -50 , 50);//Position the btn widget at the center point to offset it by 50 pixels to the left and 50 pixels down
  • Stylization of LVGL widgets
    • Implemenment function: enable the widget to be rendered according to the set style. The lv_obj_add_style function can be used to render various parts of the widget in different states.
    static lv_style_t style_base;
    lv_style_init(&style_base);                                                          //Initialization style
    lv_style_set_bg_color(&style_base, lv_palette_main(LV_PALETTE_LIGHT_GREEN));         //Set the background color
    lv_style_set_border_color(&style_base, lv_palette_darken(LV_PALETTE_LIGHT_GREEN, 3));//Sets the color of the border
    lv_style_set_border_width(&style_base, 2);                                           //Set the width of the editor
    lv_style_set_radius(&style_base, 10);                                                //Set the size of the chamfer
    lv_style_set_shadow_width(&style_base, 10);                                          //Sets the width of the shadow
    
    lv_obj_add_style(btn,&style_base,0);                                                 //Set the style of the btn theme, 0 can be replaced with position and status
    RP2040-Touch-LCD-1.28-LVGL-style.png
    Before stylization   After stylization
  • LVGL widget group to add functions
    • Implemented function: Incorporate LVGL widgets into a widget group, enabling encoder input devices to enter edit mode for modifying the state of the widgets.
    lv_group_add_obj(group, btn);//Add the btn widget to the widget group


Python

Example Demonstration

This example shows four interfaces that can be switched by the IMU sensor.

  • The first interface:
    • Display content: Waveshare logo

  • The second and third interface:
    • Display content: 6-axis sensor data
    • Refresh rate: Updated every 300ms

  • The fourth interface:
    • Display content: Battery voltage (without backlight widget interface).
    • Refresh rate: Updated every 200ms

Demo Introduction

This demo is for testing LVGL widget interaction, 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 documentation.

Implement Function

  1. The example system clock is 230MHz. The peripheral clock frequency for SPI is set to match the system clock, and the LVGL library's dual buffer mechanism is utilized. Data transfer occurs in one buffer while the other buffer is rendering, ensuring smooth animation.
  2. This example uses a six axis simulator to simulate gyroscope data as an encoder input device, achieving interface switching and real-time display of six axis sensor and battery voltage data, demonstrating the simple application of LVGL widget.

Compile and Run

  • Operation steps
  • Before starting to use, it is necessary to set up an integrated development environment Thonny Python IDE (Windows version V3.3.3)
    • Download the demo, and press the onboard boot key, connect the board to the USB interface of the PC through a Micro USB cable. And then release the key, the PC will identify the pico as a removable driver. Finally, you need to copy the compiled file in .uf2 format to Pico.
    • Open Thonny, upload the examples and lib directories to the development board
      RP2040-Touch-LCD-1.69-LVGL-PY-1.jpg
    • Open the LCD_1in28_LVGL_test.py file under the examples directory, please refer to the steps below to run the demo
      500px-RP2040-Touch-LCD-1.69-LVGL-PY-2.jpg
  • Firmware building
    • Envitonment building: only test on Ubuntu 22.04
    • Github: lv_micropython
    • Refer to Raspberry Pi Pico port to compile
    • Firmware generation path: lv_micropython/ports/rp2/build-PICO/firmware.uf2

Source Code Analysis

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/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 for the LVGL library is located in examples/src/LVGL_example.c and lib/LVGL.py folders of the project folder.
    RP2350-Touch-LCD-1.69-LVGL-PY-3.jpg
    RP2350-Touch-LCD-1.69-LVGL-PY-4.jpg

LVGL Initialization

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

  • Import an LVGL library
    • Demo location: lib/LVGL.py
    • Implementation function: Import LVGL library and use alias "lv" to facilitate calling its functions
    import lvgl as lv
    
  • Create an LVGL object
    • Demo location: examples/LCD_1in69_LVGL_test.py
    • Implementation function: Create an LVGL object and pass in LCD and TSC objects as parameters
    # Init LVGL
    LVGL(LCD=LCD,TSC=TSC)
    
  • LVGL initialization
    • Demo location: lib/LVGL.py
    • Code description: LVGL core initializes the initialization function located in the LVGL class, which is automatically called when the LVGL object is created
    if not lv.is_initialized(): lv.init()
    

LVGL Run

The LVGL library periodically calls the heartbeat interface function lv.tick_inc to inform LVGL of the past time so that LVGL can update its internal time state to handle time-related tasks such as animations, timers, and so on. In addition, the lv.task_handler function needs to be called 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
    • Implementation method: Create an event_loop object. In the initialization function of this class, we create a timer. The timer will automatically call the heartbeat interface function and event handler function within a set time interval. The time interval for the call can be adjusted by passing the freq parameter, such as 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 interface
    • Demo location: lib/lv_utils.py
    • Implementation method: You need to make sure that the priority of lv.task_handler is lower than that of 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) // In this case, 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 # The number of tasks being processed has increased
            except:
                pass
    
  • LVGL Task Processor
    • Demo location: lib/lv_utils.py
    • Implementation method: To handle LVGL tasks, you need to call lv.task_handler at regular intervals, in this case after calling lv.tick_inc in the timer callback function.
    def task_handler(self, _):
        try:
            if lv._nesting.value == 0:
                lv.task_handler()
                if self.refresh_cb: self.refresh_cb()
            self.scheduled -= 1 # The number of tasks being processed has decreased
        except Exception as e:
            if self.exception_sink:
                self.exception_sink(e)
    

LVGL Display

To implement LVGL display, you must initialize a display driver and set the various properties of the display driver, such as color format, draw buffer, rendering mode, and display callback function. At each LV_DEF_REFR_PERIOD (set in lv_conf.h), LVGL detects if something has happened on the UI that needs to be redrawn. For example, a button is pressed, a chart is changed, an animation occurs, etc. When redrawing is needed, LVGL calls the display callback function to complete the drawing of the image in the refresh area.

  • LVGL display refreshing rate
    • Demo location: lv_micropython/lib/lv_bindings/lv_conf.h
    • Setting method: In lv_conf.h, you can modify LV_DEF_REFR_PERIOD to change the refresh time of the screen.
    #define LV_DEF_REFR_PERIOD 10 // Unit: ms, here is 10ms
    
  • LVGL display related variable definition
    • Demo location: lib/LVGL.py
    • Implementation function: The sizes of buf0 and buf1 are set to 33% of the screen display area to implement the LVGL dual buffer mechanism, which reduces the appearance of jagged edges during large area refresh of screen updates while effectively increasing the screen refresh rate; when using a single buffer, it is best to set it to 10% of the screen display area, which can effectively reduce system usage but may result in noticeable jagged edges during large area refresh of 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
    • Implementation function: According to the design requirements, improve the core structure variables of the LVGL library, initialize the display driver disp_drv, and set the drawing buffer, which is a simple array used by LVGL to render screen content. Once the rendering is ready, the content of the draw buffer will be 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 color format to 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 display callback function
    
  • LVGL display callback function
    • Demo location: lib/LVGL.py
    • Implementation function: Mainly complete the drawing of the image in the refresh area.
    def disp_drv_flush_cb(self,disp_drv,area,color_p)
    Parameters:
        disp_drv : Displays driver structure pointers, which contain information about the display and function pointers. This parameter is often used to notify you that a refresh is complete
        area     : Region structure pointer, containing 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 pointer, indicating the color data to be displayed in the refresh area. In this demo, it reads the address as DMA input to transmit data to the SPI bus and completes the image drawing
    
  • LVGL display callback function implementation
    • Demo location: lib/LVGL.py
    • Implementation method: In this example, to maximize the reduction of processor utilization, DMA is used for color data transmission. color_p is set as the read address, and the SPI bus output data register is set as the write address. The code is long and only shows part of it, please download the demo to view the full code.
    def disp_drv_flush_cb(self,disp_drv,area,color_p):
        ......
        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 position
        
        ...... # DMA configuration is executed immediately
    
        while self.dma.active(): # Wait for DMA to be idle
                pass
               
        self.disp_drv.flush_ready() # Notify LVGL that data transfer is complete
    

LVGL Input

In LVGL, users can register input devices such as touchpads, mice, keyboards, or encoders, etc. Users can control the user interface through these input devices to achieve better interaction.

  • LVGL calls input device callback functions for frequency settings
    • Demo location: lv_micropython/lib/lv_bindings/lv_conf.h
    • Setting method: It uses the same macro as the screen refresh time, and the input device callback function is called every 10ms to update the events triggered by the input device, and this 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
    • Setting method: Register the encoder device indev_drv and initialize it
    # Init touch screen as input device
    self.indev_drv = lv.indev_create()                 # Create an object
    self.indev_drv.set_type(lv.INDEV_TYPE.ENCODER)     # Register encoder device
    self.indev_drv.set_read_cb(self.indev_drv_read_cb) # Set callback function
    
  • LVGL input device callback function
    • Demo location: lib/LVGL.py
    • Implementation function: Mainly used to update input events
    def indev_drv_read_cb(indev_drv, data)
    Parameters:
      indev_drv : Pointer to the input device driver structure in LVGL. In this case, the structure is an encoder input device driver
      data      : Pointer to the input device driver structure in LVGL. In this case, the structure is used to store the status and data of the input device, including the current encoder state (pressed or released) and the incremental movement
    
  • Callback function implementation of encoder input device
    • Demo location: lib/C and lib/LVGL.py
    • Implementation method: Mainly by reading the six-axis sensor data in the callback function of the input device.
    def indev_drv_read_cb( self, indev_drv, data):
            for i in range(3):
                xyz = self.IMU.Read_XYZ()
                ud_diff += xyz[4]  
                lr_diff += xyz[3]
    
            ud_diff /= 3
            lr_diff /= 3
    
            # up or down
            if ud_diff > self.IMU.offset and ud_diff > 0 and self.IMU.but_flag:
                self.IMU.encoder_diff -= 2
                self.IMU.but_flag = False
            elif ud_diff < -self.IMU.offset and ud_diff < 0 and self.IMU.but_flag:
                self.IMU.encoder_diff += 2
                self.IMU.but_flag = False
            else:
                self.IMU.encoder_diff = 0
                self.IMU.but_flag = True
    
            # left or right
            if lr_diff > self.IMU.offset_x:
                self.IMU.encoder_act = lv.INDEV_STATE.PRESSED 
            else:
                self.IMU.encoder_act = lv.INDEV_STATE.RELEASED  
            
            data.enc_diff = self.IMU.encoder_diff
            data.state    = self.IMU.encoder_act
    

LVGL Widget Layout

In LVGL, we can create various user interfaces. The basic components of the interface are objects, also called widgets, such as buttons, labels, images, lists, charts, or text areas. In a interface, we can create multiple widgets simultaneously and set their positions, sizes, parent objects, styles, and event handlers and other basic properties.

  • Create an LVGL interface object
    • Demo location: examples/LVGL_example.py
    • Implementation function: Create an LVGL object and pass in LCD and IMU objects as parameters
    # Init WIDGETS
    WIDGETS(LCD=LCD,IMU=IMU)
    
  • LVGL create tile
    • Demo location: examples/LVGL_example.py
    • Implementation function: A Tile view is a container object whose elements, called tiles, can be arranged in a grid format. Users can navigate between tiles by swiping. Use the Tile view object to call add_tile(tileview, row_id, col_id, dir) to create a new tile on the row_id row and col_id column. dir can be lv.DIR.LEFT/RIGHT/TOP/BOTTOM/HOR/VER/ALL or a value to move to the adjacent tile in the given direction by sliding.
    //Create a tile at (0,0) with support for sliding down to (0,1)
    self.tv = lv.tileview(self.scr)
    self.tile1 = self.tv.add_tile(0, 0, lv.DIR.BOTTOM)
    
  • LVGL create widget
    • Demo location: examples/LVGL_example.py
    • Implementation function: To create a widget, different widgets need to use different function interfaces, you can choose the parent object to create it.
    //Create a table widget where tile2 is the parent object of the widget, which can be replaced with a widget such as list, title, etc. that can have child objects
    self.table_imu_data = lv.table(self.tile2)
    
  • Alignment positioning of LVGL widget
    • Demo location: examples/LVGL_example.py
    • Implementation function: Enable a widget to be offset and positioned based on a reference point. The widget aligns to the center of the offset reference point widget.
    • Alignment standard: The LVGL library supports both internal and external alignments. By default, the upper-left corner is the origin, the leftward as the positive horizontal direction, and the downward as the positive vertical direction.
    //Position the widget 15 pixels to the right from the center point
    self.table_imu_data.align(lv.ALIGN.CENTER, 15 ,0)
    
    RP2040-Touch-LCD-1.28-LVGL-align.png
  • LVGL widget to adjust font size
    • Demo location: lv_micropython/lib/lv_bindings/lv_conf.h and examples/LVGL_example.py
    • Implementation function: In actual use, an interface may need to use multiple font sizes. Multiple font sizes can be enabled in lv_conf.h and the default font size can be set. When setting the font size, it is necessary to stylize the widget so that it can be rendered according to the set style. The add_style function can be used to render various parts of the widget 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 an object
    table_imu_data.init()                                  // Initialize
    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 handling
    • Demo location: examples/LVGL_example.py
    • Implementation function: In LVGL, you can add an event handler callback function to a widget so that when the widget is clicked, scrolled, redrawn, etc., it triggers an event and enters the event handler callback function. Call obj.add_event_cb(event_cb, filter, None) function in the program to add the event filter handling function event_cb for the widget obj, when the widget obj triggers the filter event, the system will automatically call the event_cb function. The last parameter is a pointer to any custom data available in the event.
    //Add handler function sw_event_cb for event LV_EVENT_VALUE_CHANGED to widget sw
    self.sw.add_event_cb(self.sw_event_cb, lv.EVENT.VALUE_CHANGED, None)
    

Resources

Supporting Resources

Demos

Schematic and Distribution Diagram

3D Drawing

Datasheets

Official Resources

Raspberry Pi Official Documents


Raspberry Pi Open Source Demos

FAQ

Question: Raspberry Pi Pico 2 GPIO is configured as a pull-down input, why does reading IO show a high voltage level when the pin is left unconnected?

 Answer:

You can refer to the RP2350-E9 section in the RP2350 Datasheet


Support

Monday-Friday (9:30-6:30) Saturday (9:30-5:30)

Email: services01@spotpear.com