RP2350-LCD-0.96
Overview
| ||
Introduction
The RP2350-LCD-0.96 is a low-cost, high-performance microcontroller development board designed by Waveshare with flexible digital interfaces. In terms of hardware, it uses the RP2350A microcontroller chip officially developed by Raspberry Pi, equipped with a dual-core ARM Cortex-M33 processor and a dual-core Hazard 3 RISC-V processor, with a running frequency of up to 150MHz, built-in 520KB SRAM and 4MB memory, and up to 26 multi-function GPIO pins, a 0.96-inch LCD screen, and a battery interface, which is convenient for use in mobile applications. Its power IC TPS63000 is a high-efficiency DCDC buck-boost chip with a charge current up to 1A and a 1.8A current switch. For software development, either Raspberry Pi's C/C++ SDK, or the MicroPython is available, and there are complete development materials and tutorials, which makes it easy for you to get started, and integrate it into end products quickly.
Features
- Adopts RP2350A 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 processor, with clock operating frequencies up to 150MHz
- 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 a 0.96-inch LCD display
- Castellated module allows soldering directly to carrier boards
- 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
- 26 × multi-functional GPIO pins
- Multiple hardware peripherals
- SPI × 2
- I2C × 2
- UART × 2
- 12-bit ADC × 4
- Controllable PWM channel × 16
- Accurate clock and timer on-chip
- Temperature sensor
- Accelerated floating-point libraries on-chip
- 12 × Programmable I/O (PIO) state machines for custom peripheral support
- Onboard DC-DC chip MP28164, a high-efficiency DC-DC buck-boost chip with a load current of up to 2A
- Onboard DC-DC chip TPS63000, a high-efficiency DC-DC buck-boost chip with 1.8A current switch
Pinout Definition
Dimensions
Example Experiments
Download the Demo to your computer desktop to conduct some interesting experiments.
External LED Experiment
- Connect the hardware according to the figure below, connect the Micro USB connected to the computer, open the python file in the demo Lesson-5 External LED in Thonny, and you can see that the red light is flashing when you run the demo.
- Precautions for use: The longer pin of the LED is the positive pole, the shorter one is the negative pole, the negative pole should be connected to GND, the positive pole should be connected to the GPIO output port, and the resistor must be connected when using.
- Code analysis
led_external = machine.Pin(15, machine.Pin.OUT) #Set GP15 to output mode while True: led_external.toggle() #Change the state of the LED light every 5 seconds utime.sleep(5)
Traffic Light System Experiment
- Connect the hardware according to the figure below, connect the Micro USB connected to the computer, open the python file in the demo Lesson-9 Traffic-Light-System in Thonny, run the demo to see the traffic lights running normally, and the buzzer will be triggered when the button is pressed.
- Precautions for use: The longer pin of the LED is the positive pole, the shorter pin is the negative pole, the negative pole should be connected to GND, the positive pole should be connected to the GPIO output port, and the resistor must be connected when using. The red wire of the buzzer is connected to the GPIO port output, and the black wire is connected to GND.
- Code analysis
def button_reader_thread(): #Detect if a button is pressed global button_pressed while True: if button.value() == 1: button_pressed = True _thread.start_new_thread(button_reader_thread, ()) #Use the thread to detect keystrokes while True: if button_pressed == True: #If the button is pressed, the red light lights up and the buzzer alarms led_red.value(1) for i in range(10): buzzer.value(1) utime.sleep(0.2) buzzer.value(0) utime.sleep(0.2) global button_pressed button_pressed = False led_red.value(1) #In normal circumstances, when the light turns from red to green, the yellow light will be on for two seconds, then the yellow light and red light will go off, and the green light will be on utime.sleep(5) #When the light turns from green to red, the green light goes off first, the yellow light is on for two seconds, and then the red light is on led_amber.value(1) utime.sleep(2) led_red.value(0) led_amber.value(0) led_green.value(1) utime.sleep(5) led_green.value(0) led_amber.value(1) utime.sleep(5) led_amber.value(0)
Burglar Alarm LED Buzzer Experiment
- Connect the hardware according to the figure below, connect the Micro USB connected to the computer, open the python file in the demo Lesson-14 Burglar Alarm LED Buzzer in Thonny, run the demo and you can see that when you move in front of the Passive infrared sensor, the LED light will flash and the buzzer will sound an alarm.
Precautions for use: The middle pin of the Passive infrared sensor is the data output pin, and the pins on both sides can be connected to VCC and GND respectively.
- Code analysis
def pir_handler(pin): #Interrupt handling function, the buzzer sounds, the LED rapidly flashes print("ALARM! Motion detected!") for i in range(50): led.toggle() buzzer.toggle() utime.sleep_ms(100) sensor_pir.irq(trigger=machine.Pin.IRQ_RISING, handler=pir_handler) #Turn on the interrupt, and when the human sensor detects an exception, it will enter the interrupt handling function for processing while True: #If there is no exception, the state of LDE will be changed every 5 seconds led.toggle() utime.sleep(5)
Potentiometer Experiment
- Connect the hardware according to the figure below, connect the Micro USB connected to the computer, open the python file in the demo Lesson-16 Potentiometer in Thonny, run the demo, and rotate the potentiometer to see that the voltage value printed out in the Sheel window is also changing.
Precautions for use: The middle pin of the Potentiometer is the data output pin, and the pins on both sides can be connected to VCC and GND respectively.
- Code analysis
potentiometer = machine.ADC(26) #Use GP26 as the analog signal collection pin conversion_factor = 3.3 / (65535) while True: voltage = potentiometer.read_u16() * conversion_factor #Format the collected data and convert it into a voltage value print(voltage) #Print the voltage information, the voltage value will change as the sliding varistor rotates utime.sleep(2)
WS2812 Experiment
- Connect the hardware according to the figure below, connect the Micro USB connected to the computer, open the WS2812_RGB_LED.py file in the demo Lesson-25 WS2812 in Thonny, run the demo to see the RGB colors of blue, red, green, and white at once.
- Code analysis
#This code uses the state machine mechanism, the following code is a decorator, in which we can initialize the hardware, set the level of the pins, etc. #label("bitloop") We can define labels in the code to facilitate our execution by jumping to them. #jmp(not_x,"do_zero") When x=0, we adjust to the label "do_zero". #nop() .set(0) [T2 - 1] When x=0, it will jump here to execute. @asm_pio(sideset_init=PIO.OUT_LOW, out_shiftdir=PIO.SHIFT_LEFT, autopull=True, pull_thresh=24) def ws2812(): T1 = 2 T2 = 5 T3 = 1 label("bitloop") out(x, 1) .side(0) [T3 - 1] jmp(not_x, "do_zero") .side(1) [T1 - 1] jmp("bitloop") .side(1) [T2 - 1] label("do_zero") nop() .side(0) [T2 - 1]
# Create the StateMachine with the ws2812 program, outputting on Pin(22). sm = StateMachine(0, ws2812, freq=8000000, sideset_base=Pin(0)) #Create a state machine # Start the StateMachine, it will wait for data on its FIFO. sm.active(1) #Start state machine # Display a pattern on the LEDs via an array of LED RGB values. ar = array.array("I", [0 for _ in range(NUM_LEDS)]) print(ar) print("blue") for j in range(0, 255): for i in range(NUM_LEDS): ar[i] = j sm.put(ar,8) The method of #put() is to put the data into the output FIFO of the state machine time.sleep_ms(5)
LCD1602 I2C Experiment
- Connect the hardware according to the figure below, connect the Micro USB connected to the computer, open the python file in the demo Lesson-21 LCD1602 I2C in Thonny, save the RGB1602.py file as a Raspberry Pi Pico, and run the Choose_ Color.py to see a different color switch every 5 seconds; run Discoloration.py file to see the RGB color gradient effect.
- Code analysis
Choose_Color.py
#Define color rgb9 = (0,255,0) #Green lcd.setCursor(0, 0) #Set cursor position # print the number of seconds since reset: lcd.printout("Waveshare") #Write characters lcd.setCursor(0, 1) #Set the cursor position to the second row and the zero column lcd.printout("Hello,World!") #Write characters lcd.setRGB(rgb1[0],rgb1[1],rgb1[2]); #Set the backlight
Discoloration.py
t=0 while True: r = int((abs(math.sin(3.14*t/180)))*255); #RGB changes over time g = int((abs(math.sin(3.14*(t+60)/180)))*255); b = int((abs(math.sin(3.14*(t+120)/180)))*255); t = t + 3; lcd.setRGB(r,g,b); #Reset the RGB value # set the cursor to column 0, line 1 lcd.setCursor(0, 0) #Navigate to the first row and the zero column # print the number of seconds since reset: lcd.printout("Waveshare") #Write characters lcd.setCursor(0, 1) #Navigate to the second row and the zero column lcd.printout("Hello,World!") #Write characters time.sleep(0.3)
LVGL Demos
C
Demo Demonstration
The effect displayed in this example is to show an image through LVGL.
Demo Introduction
This example is used to test the LVGL widdet interaction and style beautification, etc.. For specific development of LVGL, please refer to LVGL development documentation.
Implement Function
- In this example, DMA is used to transfer color data to the SPI bus, reducing CPU utilization, and controlling CPU usage to less than 50% and memory usage to less than 35% during simple interactions.
- The example system clock is 200MHz. 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.
- This example displays an image through LVGL, demonstrating the simple use of LVGL widget.
Compile and Run
- Windows
- Refer to Windows Environment Setup Tutorial to complete the environment setup
- 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 already exists, you can go directly into it. If not, you can create this directory:
mkdir build cd build
- Execute cmake, automatically generate the Makefile file:
cmake -G "NMake Makefiles" ..
- Execute nmake to generate the executable file, and input the following content in the terminal:
nmake
After compilation, it will generate a .uf2 formatted file. - 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.
- Ubuntu
- Refer to Chapter 2. The SDK of Pico Getting Started to complete the environment setup
- Open a terminal, set the value of the environment variable PICO_SDK_PATH to the absolute path of pico-sdk, for example, if my pico-sdk path is "/home/pico/pico-sdk"
nano ~/.bashrc #Add the following content at the last line export PICO_SDK_PATH="/home/pico/pico-sdk"
- After setting, save and exit. The configuration takes effect
source ~/.bashrc
- Download the demo, enter the source code directory, if the build directory already exists, you can go directly into it. If not, you can create this directory:
mkdir build cd build
- Execute cmake, it will generate Makefile file:
cmake ..
- Execute nmake to generate the executable file, and input the following content in the terminal:
nmake
After compilation, it will generate a .uf2 formatted file. - 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.
Source Code Analysis
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 secondary development, please refer to the development documentation corresponding to the used version.
- The related settings for the LVGL library are at examples/inc/lv_conf.h of the project file folder, where you can set display refresh rates, system usage data, and so on.
- The application code for the LVGL library is located in the project folder at examples/src/LVGL_example.c.
LVGL Initialization
Before using LVGL image library, you need to initialize LVGL.
- The initialization function for the LVGL library
- Demo location: examples/src/LVGL_example.c
- Implementation function: Mainly used to initialize the hardware and structure variables required for LVGL.
LVGL_Init();
- LVGL library core initialization
- Demo location: examples/src/LVGL_example.c
/*2.Init LVGL core*/ lv_init();
LVGL Run
The LVGL library calls the heartbeat 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 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 interface
- Demo location: examples/src/LVGL_example.c
- 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.
//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
- Implementation method: To handle LVGL tasks, you need to regularly call lv_timer_handler(), in this example, it is called in the loop of the main function.
int main() { ... while(1) { lv_task_handler(); DEV_Delay_ms(5); ... } }
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_DISP_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: examples/inc/lv_conf.h
- 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 // Unit: ms, here is 10ms
- LVGL display color setting
- Demo location: examples/inc/lv_conf.h
- 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.
#define LV_COLOR_16_SWAP 1
- LVGL display related variable definition
- Demo location: examples/src/LVGL_example.c
- Implementation function: Define the display driver disp_drv, draw the buffer disp_buf. This example implements a double buffering mechanism, with the drawing buffer composed of buffers buf0 and buf1, both set to half the size of the screen display area, which can reduce jagged edges during large area refreshing while effectively improving 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.
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
- 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.
/*3.Init LVGL display*/ 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
- 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 ) Parameters: 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 demo, you can use it for creating TFT display window. lv_color_t *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: examples/src/LVGL_example.c
- 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_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
- 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); DEV_Digital_Write(LCD_CS_PIN, 1); lv_disp_flush_ready(&disp_drv); } }
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.
- LVGL widget initialization
- Demo location: examples/src/LVGL_example.c
- Implementation function: It primarily used for stylized widgets and layout widgets.
void Widgets_Init(void);
- LVGL create tile
- Demo location: examples/src/LVGL_example.c
- 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. Call lv_tileview_add_tile(tileview, row_id, col_id, dir) to create a new tile at 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) tile1 = lv_tileview_add_tile(tv, 0, 0, LV_DIR_BOTTOM);
- LVGL create widget
- Demo location: examples/src/LVGL_example.c
- 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 widget where tab1 is the parent object of the key, which can be replaced with a widget such as list, title, etc. that can have child objects sw = lv_switch_create(tab1);
- Alignment positioning of LVGL widget
- Demo location: examples/src/LVGL_example.c
- 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 btn widget 45 pixels to the left from the center point lv_obj_align(sw, LV_ALIGN_CENTER, -45, 0);

- LVGL widget to adjust font size
- Demo location: examples/inc/lv_conf.h and examples/src/LVGL_example.c
- 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 lv_obj_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 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 handling
- Demo location: examples/src/LVGL_example.c
- 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 lv_obj_add_event_cb(obj, event_cb, filter, user_data) 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 lv_obj_add_event_cb(sw, sw_event_cb,LV_EVENT_VALUE_CHANGED,NULL);
Python
Demo Demonstration
The effect displayed in this example is to show an image through LVGL.
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
- 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.
- This example displays an image through LVGL, demonstrating the simple use 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
- Open the LCD_1in14_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
- 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.
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_1in14_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 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 interface object and pass in LCD object as parameter
# Init WIDGETS WIDGETS(LCD=LCD)
- 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)

- LVGL widget 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
Resources
Supporting Resources
Documents
Demo
LCD Datasheet
Official Resources
Pico2
User Manuals
Schematic Diagram and Datasheets
- Pico2 Schematic diagram
- Pico2 Pinout definition
- Pico2 Datasheet
- RP2350 Datasheet
- RP2350 Hardware Design Reference Manual
Related Books
Raspberry Pi Open Source Demos
FAQ
No, you need to use a battery with an integrated protection circuit or connect an external protection circuit to prevent deep discharging.
Support
Technical Support
If you need technical support or have any feedback/review, please click the Submit Now button to submit a ticket, Our support team will check and reply to you within 1 to 2 working days. Please be patient as we make every effort to help you to resolve the issue.
Working Time: 9 AM - 6 PM GMT+8 (Monday to Friday)