From Waveshare Wiki
Jump to: navigation, search

USB Type-C






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


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


LCD Parameters
Touch Chip CST816S Touch Port I2C
Display Chip GC9A01A Display Interface SPI
Resolution 240(H) RGB x 240(V) Display Size Φ32.4mm
Display Panel IPS Pixel Pitch 0.135(H)x 0.135(V)mm

IMU Parameters
Sensor QMI8658
Accelerometer Resolution: 16 bits
Measuring Range (Optional): ±2, ±4, ±8 ±16g
Gyroscope Resolution: 16 bits
Measuring Range (Optional): ±16, ±32, ±64, ±128, ±256, ±512, ±1024, ±2048°/sec


The touch sensitivity on the edge of the round screen is decreased due to its round shape design.





Pico Quick Start

Download Firmware

  • MicroPython Firmware Download

MicroPython Firmware Download.gif

  • C_Blink Firmware Download

C Blink Download.gif

Video Tutorial

  • Pico Tutorial I - Basic Introduction

  • Pico Tutorial II - GPIO

  • Pico Tutorial III - PWM

  • Pico Tutorial IV - ADC

  • Pico Tutorial V - UART

  • Pico Tutorial VI - To be continued...

Text Tutorial


MicroPython Series

C/C++ Series

Arduino IDE Series

Install Arduino IDE

  1. Download the Arduino IDE installation package from Arduino website.
    RoArm-M1 Tutorial II01.jpg
  2. Just click on "JUST DOWNLOAD".
    Arduino IDE Pico.png
  3. Click to install after downloading.
    RoArm-M1 Tutorial II02.gif
  4. Note: You will be prompted to install the driver during the installation process, we can click Install.

Install Arduino-Pico Core on Arduino IDE

  1. Open Arduino IDE, click the File on the left corner and choose "Preferences".
    RoArm-M1 Tutorial04.jpg
  2. Add the following link in "Additional boards manager URLs", then click OK.

    RoArm-M1 Tutorial II05.jpg
    Note: If you already have the ESP32 board URL, you can separate the URLs with commas like this:

  3. Click on Tools -> Board -> Board Manager -> Search for pico, it shows installed since my computer has already installed it.
    Pico Get Start 05.png
    Pico Get Start 06.png

Upload Demo At the First Time

  1. Press and hold the BOOTSET button on the Pico board, connect the Pico to the USB port of the computer via the Micro USB cable, and release the button when the computer recognizes a removable hard drive (RPI-RP2).
    Pico Get Start.gif
  2. Download the demo, open the D1-LED.ino under arduino\PWM\D1-LED path.
  3. Click Tools -> Port, remember the existing COM, do not need to click this COM (different computers show different COM, remember the existing COM on your computer).
    UGV1 doenload02EN.png
  4. Connect the driver board to the computer with a USB cable, then click Tools -> Ports, select uf2 Board for the first connection, and after the upload is complete, connecting again will result in an additional COM port.
    UGV1 doenload03EN.png
  5. Click Tools -> Board -> Raspberry Pi Pico/RP2040 -> Raspberry Pi Pico.
    Pico Get Start02.png
  6. After setting, click the right arrow to upload.
    Pico Get Start03.png
    • If you encounter problems during the period, you need to reinstall or replace the Arduino IDE version, uninstall the Arduino IDE clean, after uninstalling the software you need to manually delete all the contents of the folder C:\Users\[name]\AppData\Local\Arduino15 (you need to show the hidden files in order to see it) and then reinstall.

Open Source Demo

Application Demo

LVGL Example Demo

Example Effect

The demonstration displays three interfaces that can be switched by either sliding on the touchscreen or tilting with the six-axis sensor.

  • The first interface:
    • Display content: Waveshare Logo
  • The second interface
    • Display content: 6-axis sensor data
    • Refresh frequency: Updated every 500ms.
    • Switch mode: Switching can be achieved by swiping up from the bottom on the touchscreen in the first interface, or users can employ the six-axis sensor by tilting it to the right to trigger the input device confirmation event selecting interface, and then tilting upward to trigger the switch event for interface transition.
  • The third interface
    • Display content: Four interactive controls are available.
    • Interaction methods: Users can engage through touchscreen interactions or utilize a six-axis sensor for motion-based interactions (lifting the device up and down to switch between selected controls; right tilt for initial access to editing mode, and another right tilt for interaction).
    • Switch modes: Switching between controls can be achieved by swiping up from the bottom on the touchscreen in the second interface. Alternatively, users can employ the six-axis sensor by tilting it to the right to trigger the input device confirmation event selecting interface, and then tilting upward to trigger the switch event for interface transition.

Note: Since controls are sub-objects of the interface, it is not possible to switch directly from a selected interface to a selected control when using the six-axis sensor. You can use touch input to reselect controls in such cases.


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

Source Code Structure

  • The source code of the LVGL library is in ./lib/lvgl, and the version is 8.1. For the secondary development, you can refer to the development document.
  • The related setting of the LVGL library is in ./examples/inc/lv_conf.h, and you can set the display refreshing rate, the system occupied data, and so on.
  • The application code of the LVGL library is in ./examples/src/LVGL_example.c.


  1. This example utilizes DMA for transferring color data to the SPI bus, reducing CPU utilization. The CPU usage remains below 20% during simple interactions, and memory usage stays below 30%.
  2. The system clock in this example runs at 270MHz. The peripheral clock frequency of the SPI is set to match the system clock. Additionally, the LVGL library's double buffering mechanism is employed, ensuring smooth animations by rendering one buffer while transferring data in the other.
  3. In this example, touch screen and six-axis sensor are used as two input devices, in which the six-axis simulator emulates gyroscope data as input for encoder devices, which can be applied in various interactive scenarios.

Compile to run

  • Enter the source code directory. If the build directory exists, you can directly enter. If not, execute:
mkdir build
  • Enter build directory and add the SDK address:
cd build
export PICO_SDK_PATH=../../pico-sdk

Note: the path of pico-sdk needs to be adjusted by the reality.

  • Execute cmake, the Makefile file automatically generates:
cmake ..
  • Execute make to generate the executable file, and input it in the terminal:

Just wait for the compilation to be done and copy the resulting file in .uf2 format to pico.

Source Code Explanation

LVGL Initialization

  • Initialization function of the LVGL Library:
    • Function For initializing all 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 mainly involves initializing several core structure variables of LVGL. The operation of the LVGL library relies on these core structure variables.
    • Definition Method: Setting the sizes of buf0 and buf1 to half the screen display area is aimed at implementing the LVGL double buffering mechanism. This mechanism helps reduce noticeable jagged edges during extensive screen redraws while effectively enhancing the screen refresh rate. When using a single buffer, it's preferable to set it to 10% of the screen display area to significantly reduce system occupancy. However, this setup may result in more apparent jagged edges during extensive image refreshes.
static lv_disp_draw_buf_t disp_buf;                   //LVGL display 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 6-axis sensor analog encoder input device driver  
static lv_group_t *group;                             //Encoder control group                  
  • How to realize the initialization of the LVGL library
    • Function: improve the core structure variables of the LVGL library.
    • Method: The encoder input device of the LVGL library has both an edit mode and a browse mode. In the browse mode, triggering a switch event will change the selected control within the control group. Triggering a confirmation event will transition the selected control into edit mode. Therefore, when simulating an encoder with a six-axis sensor, it's essential to introduce the 'lv_group_t' structure variable as the control group for the six-axis sensor.
// Initialize LVGL core

// 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); 
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 the touch screen input device
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
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);//Adding encoder control devices to a control group
  • Run the LVGL library
    • Function: The LVGL library calls the function "lv_tick_inc" periodically to inform LVGL about the passage of time. This allows LVGL to update its internal time status and handle time-related tasks such as animations and timers. Additionally, within the main function loop, the "lv_task_handler" function needs to be called to manage predefined tasks.
    • Implementation method: It's crucial to ensure that the priority of lv_task_handler is lower than that of lv_tick_inc. Therefore, in this example, lv_tick_inc is called within a timer callback function.
add_repeating_timer_ms(5, repeating_lvgl_timer_callback, NULL, &lvgl_timer);//Timer callback function called every 5ms

static bool repeating_lvgl_timer_callback(struct repeating_timer *t) 
    return true;

int main()


LVGL Display

  • LVGL shows callback functions
    • Function: mainly accomplishes 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 )
    lv_disp_drv_t *disp_drv: Display driver structure pointer, which contains information and function pointers related to the display. This parameter is commonly used to signal the completion of a refresh
    const lv_area_t *area: Region structure pointer, containing positional information of the area to be refreshed. In this example, used for creating a window for TFT display
    lv_color_t *color_p: color structure pointer, color data representing what needs to be displayed within the refresh area. In this case, acting as DMA input read address to transfer data to the SPI bus, completing the drawing of the image
  • LVGL display color setting:
    • Target: The pixel color storage method built by the lv_color_t structure in its default state is inconsistent with the data required for transmission in this example. Direct transmission would result in color discrepancies in the displayed image.
    • Method: Modify the settings in the lv_conf.h folder to change how colors are stored.
#define LV_COLOR_16_SWAP 1
  • LVGL displays the refresh rate setting
    • Method: in lv_conf.h, you can also set the refresh rate time for the display buffer. Modifying this definition allows you to change the screen's refresh time.
#define LV_DISP_DEF_REFR_PERIOD  10     /*[ms]*/
  • LVGL Display Callback Function Implementation
    • Implementation Method: In this example, to maximize the reduction in processor utilization, DMA is utilized for the transfer of color data. 'color_p' is set as the read address, and the output data register of the SPI bus 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 of the image 
                          &spi_get_hw(LCD_SPI_PORT)->dr,          //SPI bus output data register address
                          color_p,                                //Address of the color data array to be refreshed
                          ((area->x2 + 1 - area-> x1)*(area->y2 + 1 - area -> y1))*2,
                          true                                    //Transfer immediately after setup
  • LVGL Refresh Completion Notification Implementation
    • Functionality: It's necessary to notify the LVGL core after each image refresh is completed so that LVGL can prepare for rendering the next refresh image.
    • Implementation Method: In this example, the LVGL image refresh completion is notified within the DMA transfer completion interrupt service function. Using a blocking notification mechanism would prevent leveraging the double buffering mechanism to enhance the refresh speed.
static void dma_handler(void)
    if (dma_channel_get_irq0_status(dma_tx)) {

LGVL Input

  • The input callback function of the LVGL.
    • Implementation Method: for refreshing the input events.
static void encoder_read_cb(lv_indev_drv_t * drv, lv_indev_data_t*data);
  lv_indev_drv_t *indev_drv: Pointer to the input device driver structure in LVGL. In this example, the structure is a touch screen input device driver.

  lv_indev_data_t *data: Pointer to the input device data structure in LVGL. In this example, this structure is used to store the state and data of the input device, including the current touch state (pressed or released) and the coordinates of the touch point.

static void encoder_read_cb(lv_indev_drv_t * drv, lv_indev_data_t*data);

  lv_indev_drv_t *indev_drv: Pointer to the input device driver structure in LVGL. In this example, this structure is the encoder input device driver for the six-axis sensor simulation.

  lv_indev_data_t *data: Pointer to the input device data structure in LVGL. In this example, this structure is used to store the state and data of the input device, including the current toggle event (lift up or lift down) and the OK event (lift right).
  • LVGL Frequency Settings for Calling Input Device Callback Functions
    • Method: LVGL defaults to call the input device callback function every 30ms to update the events triggered by the input device, which can be set in lv_conf.h.
#define LV_INDEV_DEF_READ_PERIOD 30     /*[ms]*/
  • Callback function implementation for touch screen input devices.
    • Implementation: Mainly through the touch interrupt to update the touch state and touch point coordinates of the touch screen.
static void touch_callback(uint gpio, uint32_t events)
  if (gpio == Touch_INT_PIN)
      CST816S_Get_Point();// Update the Touch_CTS816 global variable.
      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;
  • Six-axis sensor input device callback function implementation
    • Operation: The six-axis sensor analog encoder is used as an input device, with up and down lifts triggering switching events and right lifts triggering OK events.
    • Implementation: Since the data of the six-axis sensor changes more frequently, to minimize the misoperation of the situation use a timed multiple sampling method to update the device switching events and OK events.
static bool repeating_imu_diff_timer_callback(struct repeating_timer *t);//Input events updated every 50ms
    get_diff_data();//Multi-sampling recognizes input events updating 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 Control Layout

  • LVGL Control Initialization function
    • Function: for styling controls and laying out controls.
void Widgets_Init(void);
  • LVGL control creating function
    • Function: Creating controls requires using different function interfaces for different controls. It's possible to choose a parent object for creation.
lv_obj_t *btn = lv_btn_create(lv_scr_act()); //Create a control where the lv_scr_act layer is the parent of the button, which can be replaced with a list, title, etc. control that can have child objects.
  • Alignment Positioning of LVGL Controls
    • Function: Enables the control to be positioned offset based on a reference point. The center of the reference point control to which the control is aligned for the offset.
    • Alignment standard: The LVGL library has both internal and external alignment. By default, the top-left corner is used as the origin, left is the positive horizontal direction, and down is the positive vertical direction.
lv_obj_align(btn, LV_ALIGN_CENTER, -50 , 50);//Position the btn control 50 pixels left of center and 50 pixels down
  • Styling of LVGL controls
    • Function: Enables the control to be rendered according to a set style. Using the lv_obj_add_style function you can realize the rendering of each part of the control in different states.
static lv_style_t style_base;
lv_style_init(&style_base);                                                          //Initialize the 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));//Set the border color 
lv_style_set_border_width(&style_base, 2);                                           //Set the edit width  
lv_style_set_radius(&style_base, 10);                                                //Setting the size of the chamfer
lv_style_set_shadow_width(&style_base, 10);                                          //Set the width of the shadow

lv_obj_add_style(btn,&style_base,0);                                                 //Set the style of the btn theme, 0 can be replaced by the position and state
  • LVGL control group add function
    • Function: Incorporates the LVGL control into the control group so that the encoder input device can enter edit mode to modify the state of the control.
lv_group_add_obj(group, btn);//Add the btn control to the group of controls


Waveshare Resources



3D Drawing


Official Resources

Raspberry Pi Doecument

Raspberry Pi Open-source Demo

Development Software


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)