Pico LCD 2
| ||
Introduction
A Pico C demo is provided
Specifications
Parameter Name | Parameter |
Supply voltage | 2.6V ~ 5.5V |
Operating current | 40mA |
LCD type | IPS |
Control chip | ST7789V |
Communication interface | 4-wire SPI |
Resolution | 240(V)RGB x 320(H) Pixels |
Pixel size | 0.0975 (H) x 0.0975 (V) mm |
Display size | 30.60mm(H)x 40.80(V)mm |
Dimensions | 35(H)x52.00(V) mm |
Pinout Definition
LCD & Controller
The built-in controller used in this LCD is ST7789VW, which is an LCD controller with 240 × RGB × 320 pixels, while the pixels of this LCD are 240(H)RGB × 320(V). Hence, the LCD's internal RAM is fully used.
This LCD supports 12-bit, 16-bit, and 18-bit per pixel input color formats, namely RGB444, RGB565, and RGB666 color formats, this demo uses RGB565 color format, which is also a commonly used RGB Format.
This LCD uses a 4-wire SPI communication interface, which can greatly save the GPIO port, and the communication speed will be faster.
Communication Protocol
Note: The difference from the traditional SPI protocol is that the data line sent from the slave to the host is hidden because it only needs to be displayed. Please refer to Datasheet Page 66 for the table.
RESX is the reset pin, it is pulled low when the module is powered on, usually set to 1;
CSX is the slave chip select pin, and the chip is enabled only when CS is low;
D/CX is the data/command control pin of the chip, when DC = 0 a command is written; when DC = 1, data is written
SDA is the data transmitted data, that is, RGB data;
SCL is the SPI communication clock pin.
For SPI communication, the data transmission has timing sequences involving clock phase (CPHA) and clock polarity (CPOL):
CPHA determines whether data is captured on the first or second clock edge. When CPHA = 0, data is captured on the first clock edge.
CPOL determines the idle level of the clock; CPOL = 0 means low.
From the diagram, it can be seen that when SCLK falls on the first edge, data transmission starts. One clock cycle transmits 8 bits, using SPI0, bitwise transmission, high bit first, and low bit last.
Pico Tutorial
Hardware Connection
When connecting the Pico/Pico2, please be careful not to reverse the corresponding direction. You can judge the direction by observing the end with a USB silkscreen on the module and the USB port end of the Pico/Pico2 (you can also judge the direction by the pin number of the female connector on the module and the pin number of the Pico/Pico2).
You can connect the display according to the table.
LCD | Pico/Pico2 | Function |
VCC | VSYS | Power input |
GND | GND | Ground |
DIN | GP11 | SPI communication MOSI pin, slave device data input |
CLK | GP10 | SPI communication SCK pin, slave device clock input |
CS | GP9 | SPI chip select pin (active low) |
DC | GP8 | Data/command control pin (high for data, low for command) |
RST | GP12 | External reset pin (active low) |
BL | GP13 | Backlight control |
KEY0 | GP15 | User key KEY0 |
KEY1 | GP17 | User key KEY1 |
KEY2 | GP2 | User key KEY2 |
KEY3 | GP3 | User key KEY3 |
Basic Introduction
PICO series1——introduction
PICO series2—— peripheral GPIO
PICO series3——PWM
PICO series4——ADC
PICO series5——UART
PICO series6——I2C
PICO series7——SPI
Demo Download
Open the Raspberry Pi terminal and execute:
Method 1: Download from our official website, we recommend this method.
sudo apt-get install p7zip-full cd ~ sudo wget https://files.waveshare.com/wiki/common/Pico_code.7z 7z x Pico_code.7z -o./Pico_code cd ~/Pico_code cd c/build/
Demo
C
- The following tutorial is for operating on a Raspberry Pi, but due to the multi-platform and portable characteristics of cmake, it can also be successfully compiled on a PC, with some differences in operation that you will need to judge on your own.
- For compilation, please make sure that it is in the c directory:
cd ~/Pico_code/c/
Create and enter the build directory, and add the SDK: Here ../../pico-sdk is the directory of your SDK. There is a build in the demo, which can be accessed directly
cd build export PICO_SDK_PATH=../../pico-sdk (Note: Make sure to write the correct path to your own SDK)
Execute cmake, automatically generate the Makefile file:
#Pico cmake -DPICO_BOARD=pico -DPICO_PLATFORM=rp2040 .. #Pico2 cmake -DPICO_BOARD=pico2 -DPICO_PLATFORM=rp2350 ..
Open the main.c file in the c folder, you can modify the example program as needed, which can drive the company's Pico series display, and the source code will be updated continuously. Please choose the corresponding LCD or OLED test function, and comment out the irrelevant functions.
Execute make to generate an executable file, the first compilation takes a long time
make -j9
After the compilation is completed, the uf2 file will be generated. Press and hold the button on the Pico/Pico2 board, connect Pico/Pico2 to the USB port of the Raspberry Pi via Micro USB cable, then release the button. After connecting, the Raspberry Pi will automatically recognize a removable disk, and copy the main.uf2 file in the build folder to the recognized removable disk.
#Pico cp main.uf2 /media/pi/RPI-RP2/ #Pico2 cp main.uf2 /media/pi/RP2350
Python
Working with Windows
- 1. Press and hold the BOOTSET button on the Pico board, connect the Pico/Pico2 to the USB port of the computer via the Micro USB cable, and release the button after the computer recognizes a removable hard disk.
- 2. Copy the .uf2 format file from the Python directory to the recognized removable drive
- Pico: rp2-pico-20210418-v1.15.uf2
- Pico 2: rp2-pcio2-20240809-v1.24.0.uf2
- 3. Open Thonny IDE (Note: Make sure to use the latest version of Thonny, otherwise there is no support package for Pico, the latest version under Windows is v3.3.3)
- 4. Click Tools > Settings - > Interpreter and select Pico/Pico2 and the corresponding port as shown in the figure
- 5. File -> Open -> python/Pico-LCD-2/Pico-LCD-2.py, click Run, as shown in the following figure:
This demo provides a simple program.
Working with Raspberry Pi
- The process of firmware flashing is the same as on Windows. You can choose to copy the. uf2 format file to Pico/Pico2 on your PC or Raspberry Pi.
- 2. Open the Thonny IDE on the Raspberry Pi (click on Raspberry Pi logo -> Programming -> Thonny Python IDE), you can view the version information by going to Help->About Thonny
To ensure your version is supported by the Pico package, you can also click on Tools -> Options... -> Interpreter and select MicroPython (Raspberry Pi Pico and ttyACM0 port)
As shown below:
If your current Thonny version does not have the pico support package, input the following command to update the Thonny IDE
sudo apt upgrade thonny
- 3. Click File -> Open... -> python/Pico-LCD-2/Pico-LCD-2.py to run the script
GUI API Details
If you have used our SPI screen before, you should be familiar with this example program
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), located 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)
The character fonts that GUI depends on are in the following directory: RaspberryPi\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 flipping; 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) Parameter: 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 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 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, where a time structure is defined, and only the hours, minutes, and seconds are passed 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
LVGL Demos
Function Introduction
This demo is compatible with multiple LCDs and can be customized based on whether the device is equipped with a joystick and the number of buttons. Use the joystick to move a virtual pointer, and use the buttons to switch interfaces, simulate pointer clicks, and toggle switch states and other functions. An overview of the demo functions for each LCD is shown in the table below:
Model | Interface1 | Interface2 | Interface3 |
Pico-LCD-0.96 | An image | A virtual pointer, a button, a label to record the number of button clicks | None |
Pico-LCD-1.14 | An image | A virtual pointer, a button, a label to record the number of button clicks | None |
Pico-LCD-1.3 | An image | A virtual pointer, a button, a label to record the number of button clicks | Two switches |
Pico-LCD-1.44 | An image | A button, a label to record the number of button clicks | Two switches |
Pico-LCD-1.8 | An image | An image | None |
Pico-LCD-2 | An image | A button, a label to record the number of button clicks | Two switches |
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 35% and memory usage to less than 20% during simple interactions.
- The example system clock is 150MHz. 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 demonstrates the simple use of LVGL widgets by switching the interface through buttons and switching between button and switch states by pressing a button.
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.
- Raspberry Pi/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/pi/pico/pico-sdk"
nano ~/.bashrc #Add the following content at the last line export PICO_SDK_PATH="/home/pi/pico/pico-sdk"
- After setting, save and exit. The configuration takes effect
source ~/.bashrc
- Download the demo, enter the 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 make to generate the executable file, and input the following content in the terminal:
make
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 examples/src/LVGL_example.c and examples/src/LCD_XinXX_LVGL_test.c of the project folder. Where XinXX represents the size of the LCD, for example, the file corresponding to 0in96 is LCD_0in96_LVGL_test.c.
Data Storage
This example uses a data structure to store and manage data related to LVGL. It contains pointers to different interfaces, pointers to widgets (buttons, labels, switches) in each interface, and variables used to record button clicks and button states.
- Data Structure
- Demo location: examples/inc/LVGL_example.h
- Function introduction: Used for storing and managing the required data.
typedef struct { lv_obj_t *scr[4]; // Used to store different interfaces lv_obj_t *cur; // Pointer lv_obj_t *btn; // Button lv_obj_t *label; // Label lv_obj_t *sw_1; // Switch 1 lv_obj_t *sw_2; // Switch 2 uint16_t click_num; // Button clicks uint16_t KEY_now; // Current key status uint16_t KEY_old; // Previous key status } lvgl_data_struct;
Key Reading
For key reading, this example uses key scanning methods to implement it, and we will provide a detailed introduction below. The program also provides corresponding comments.
- Key Initialization
- Demo location: examples/src/LCD_XinXX_LVGL_test.c
- Function introduction: Initialize all physical buttons of the module.
LCD_XINXX_KEY_Init();
- Key-related macro definitions
- Demo location: lib/Config/DEV_Config.h
- Usage: This demo is compatible with multiple LCDs. If there is a joystick, set the value of LCD_AS_JOY to 1. If there is no joystick, set it to 0. The state of all keys is stored in a hexadecimal variable, and each bit of that variable can be used to represent the state of a key, such as the state of KEY_A in the first bit and the state of KEY_B in the second bit. Each bit has two states of 1 or 0, which means that the key is pressed or released. If the first bit is 1, it means that KEY_A is pressed, and 0 means that KEY_A is released. This method supports detecting multiple keys being pressed simultaneously.
#define LCD_HAS_JOY 1 #define KEY_A 0x0001 #define KEY_B 0x0002 #define KEY_X 0x0004 #define KEY_Y 0x0008 #define KEY_UP 0x0010 #define KEY_DOWN 0x0020 #define KEY_LEFT 0x0040 #define KEY_RIGHT 0x0080 #define KEY_CTRL 0x0100 #define KEY_0 0x0200 #define KEY_1 0x0400 #define KEY_2 0x0800 #define KEY_3 0x1000
- Key reading
- Demo location: examples/src/LCD_XinXX_LVGL_test.c
- Implementation method: Define a hexadecimal variable KEY_Value, initialized to 0. If a key press is detected, the corresponding data position of the key is 1. After reading the status of all keys, return KEY_Value. Due to the difference in the number of buttons carried by each module, the key reading function will also be different, here is only an example of Pico-LCD-0.96, please refer to the full example for other models.
static uint16_t LCD_0IN96_Read_KEY(void) { uint16_t KEY_Value = 0; if (DEV_Digital_Read(LCD_KEY_A) == 0) KEY_Value |= KEY_A; if (DEV_Digital_Read(LCD_KEY_B) == 0) KEY_Value |= KEY_B; if (DEV_Digital_Read(LCD_KEY_UP) == 0) KEY_Value |= KEY_UP; else if (DEV_Digital_Read(LCD_KEY_DOWN) == 0) KEY_Value |= KEY_DOWN; else if (DEV_Digital_Read(LCD_KEY_LEFT) == 0) KEY_Value |= KEY_LEFT; else if (DEV_Digital_Read(LCD_KEY_RIGHT) == 0) KEY_Value |= KEY_RIGHT; else if (DEV_Digital_Read(LCD_KEY_CTRL) == 0) KEY_Value |= KEY_CTRL; return KEY_Value; }
- Key-value storage
- Demo location: examples/src/LCD_XinXX_LVGL_test.c
- Implementation method: Read the current key value and save it to dat ->KEY_now. After processing the key, save the current key value to dat->KEY_old. In the next key processing, this value will serve as the previous state of the key and be used to determine changes in the key state. If a bit was 0 in the previous cycle and is 1 now, it indicates that the key has just been pressed. If a bit was 1 in the previous cycle and is 0 now, it indicates that the key was pressed and released.
while(1) { dat->KEY_now = LCD_XINXX_Read_KEY(); // Read current key value handle_key_press(dat); // Key processing dat->KEY_old = dat->KEY_now; // Save as old key value }
- Key processing
- Demo location: examples/src/LVGL_example.c
- Implementation method: Use dat->KEY_now to determine whether the current key is pressed through AND operation, and combine dat->KEY_old to determine whether the current key is newly pressed.
void handle_key_press(lvgl_data_struct *dat) { // Determine if the KEY_UP button is pressed if(dat->KEY_now & KEY_UP) .... // Determine if the KEY_A button is newly pressed if((dat->KEY_now & KEY_A) && !(dat->KEY_old & KEY_A)) .... // Determine if the KEY_A button is released else if(!(dat->KEY_now & KEY_A)) .... }
LCD Parameter Configuration
To adapt to multiple LCD displays, this example sets three key parameters, allowing users to configure them externally based on the specific characteristics of the target LCD.
- LCD parameter definition
- Demo location: examples/src/LVGL_example.c
- Implementation function: DISP_HOR_RES and DISP_VER_RESD are mainly used to initialize the LVGL display buffer; LCD_SetWindows is a function pointer that points to the LCD display position setting function, mainly used to set the display position of the LVGL interface.
// LCD LCD_SetWindowsFunc LCD_SetWindows; uint16_t DISP_HOR_RES; uint16_t DISP_VER_RES;
- LCD parameter configuration
- Demo location: examples/src/LCD_XinXX_LVGL_test.c
- Implementation function: Configure parameters according to the LCD model used.
LCD_SetWindows = LCD_XINXX_SetWindows; // Point to LCD display function DISP_HOR_RES = LCD_XINXX_WIDTH; // LCD horizontal resolution DISP_VER_RES = LCD_XINXX_HEIGHT; // LCD LCD vertical resolution
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 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. 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.
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.
static void disp_flush_cb(lv_disp_drv_t * disp, const lv_area_t * area, lv_color_t * color_p) Parameters: lv_disp_drv_t *disp_drv: 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 control initialization function
- Demo location: examples/src/LCD_XinXX_LVGL_test.c
- Implementation function: Implement stylized and layout widgets, with the parameter dat used as an input and output parameter to store the initialized interface and various widgets.
static void Widgets_Init(lvgl_data_struct *dat);
- LVGL create interface
- Demo location: examples/src/LCD_XinXX_LVGL_test.c
- Implementation function: Create an interface.
//lv_obj_t *scr[4] is a pointer array, where each element can be used to point to an interface dat->scr[0] = lv_obj_create(NULL);
- LVGL create widget
- Demo location: examples/src/LCD_XinXX_LVGL_test.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 in which the dat->scr[1] is the parent object of the key, which can be replaced with a widget such as list, title, etc. that can have child objects dat->btn = lv_btn_create(dat->scr[1]);
- Alignment positioning of LVGL widget
- Demo location: examples/src/LCD_XinXX_LVGL_test.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 at the center point to offset it by 50 pixels to the left and 50 pixels down lv_obj_align(btn, LV_ALIGN_CENTER, -50 , 50);

- LVGL widget 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_14 1 // Enable font 14 #define LV_FONT_MONTSERRAT_16 1 // Enable font 16 #define LV_FONT_DEFAULT &lv_font_montserrat_14 // Set the default font size as 14 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 Interface Function
In this demo, some functions are customized, mainly for pointer movement and clicking.
- Custom interface functions related to LVGL widgets
- Demo location: examples/src/LVGL_example.c
- Implementation function: Mainly used to move the widget and judge whether the pointer click is valid, here is only a brief description, please see the full example for details.
static void widgets_up(lv_obj_t *widgets); // Move widget up static void widgets_down(lv_obj_t *widgets, uint16_t DISP_VER_RES); // Move widget down static void widgets_left(lv_obj_t *widgets); // Move widget left static void widgets_right(lv_obj_t *widgets, uint16_t DISP_HOR_RES); // Move widget right static bool click_valid(lv_obj_t *cur, lv_obj_t *widgets); // Determine whether the pointer click is valid
Pico Getting Started
Firmware Download
Basic Introduction
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
-
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
-
Click Extensions and select Install from VSIX
-
Select the package with the vsix suffix and click Install
-
Then vscode will automatically install raspberry-pi-pico and its dependency extensions, you can click Refresh to check the installation progress
-
The text in the right lower corner shows that the installation is complete. Close VSCode
Configure Extension
-
Open directory C:\Users\username and copy the entire .pico-sdk to that directory
-
The copy is completed
-
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
-
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
-
The project is created successfully
-
Select the SDK version
-
Select Yes for advanced configuration
-
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
-
Select Default for CMake version (the path configured earlier)
-
Select Default for Ninjaversion
-
Select the development board
-
Click Complie to compile
-
The uf2 format file is successfully compiled
Import Project
- The Cmake file of the imported project cannot have Chinese (including comments), otherwise the import may fail
-
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
-
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
-
First, go to Arduino official website to download the installation package of the Arduino IDE.
-
Here, you can select Just Download.
-
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
-
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.
-
In the Language field, select the language you want to switch to, and click OK.
Install Arduino-Pico Core in Arduino IDE
-
Open the Arduino IDE, click on the file in the top left corner, and select Preferences
-
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
-
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
-
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).
- Download the program and open D1-LED.ino under the arduino\PWM\D1-LED path
-
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)
-
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
-
Click Tools > Development Board > Raspberry Pi Pico > Raspberry Pi Pico or Raspberry Pi Pico 2
- 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)
FAQ
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)