LVGL v9 exercise run on Raspberry Pi Pico 2 + Waveshare 1.9inch Touch LCD (170x320 ST7789V2 SPI LCD and CST816 I2C capacitive touch).
LVGL v9 exercise run on Raspberry Pi Pico 2 + Waveshare 1.9inch Touch LCD
(170x320 ST7789V2 SPI LCD and CST816 I2C capacitive touch)
Basic setup and connection, refer to last exercise: Raspberry Pi Pico 2 + Waveshare 1.9inch Touch LCD, using Arduino_GFX_Library in Arduino Framework.
To add LVGL function:
rpPico2_ST7789_CST816_lvgl_button.ino, Create a button to control onboard LED.
rpPico2_ST7789_CST816_lvgl_button_2.ino, Create a button and slider to control onboard LED.
Basic setup and connection, refer to last exercise: Raspberry Pi Pico 2 + Waveshare 1.9inch Touch LCD, using Arduino_GFX_Library in Arduino Framework.
To add LVGL function:
- Install LVGL library in Arduino IDE Library Manager.
- Go to lvgl library directory.
Copy lv_conf_template.h as lv_conf.h into the Arduino Libraries
directory.
- To enable the content of the file:
change the first #if 0 to #if 1
Exercise Code:/* clang-format off */
#if 1 //0 /* Set this to "1" to enable content */
rpPico2_ST7789_CST816_lvgl_button.ino, Create a button to control onboard LED.
/*
LVGL v9 exercise run on Raspberry Pi Pico 2 + Waveshare 1.9inch Touch LCD
(170x320 ST7789V2 SPI LCD and CST816 I2C capacitive touch).
Create a button to control onboard LED.
https://coxxect.blogspot.com/2025/12/lvgl-v9-exercise-run-on-raspberry-pi.html
Using board of Raspberry Pi Pico/RP2040/RP2350 by Earle F. Philhower III in Boards Manager.
https://github.com/earlephilhower/arduino-pico
Install Earle Philhower Raspberry Pi Pico Arduino core in Arduino IDE
https://coxxect.blogspot.com/2023/08/install-earle-philhower-raspberry-pi.html
*/
/*Using LVGL with Arduino requires some extra steps:
*Be sure to read the docs here: https://docs.lvgl.io/master/get-started/platforms/arduino.html */
#include <lvgl.h>
/*******************************************************************************
* Start of Arduino_GFX setting
* Raspberry Pi Pico dev board : CS: 17, DC: 22, RST: 21, BL: 20, SCK: 18, MOSI: 19, MISO: 16
******************************************************************************/
#include <Arduino_GFX_Library.h>
#include <Wire.h>
#define SPI_MOSI 19
#define SPI_SCK 18
#define TFT_CS 17
#define TFT_DC 22
#define TFT_RST 21
#define TFT_BL 20
#define TP_SDA 4 // default I2C SDA
#define TP_SCL 5 // default I2C SCL
#define TP_RST 3
#define TP_IRQ 2
#define TFT_ROTATION 0
#define TFT_WIDTH 170
#define TFT_HEIGHT 320
#define TFT_COLSTART 35
#define TFT_ROWSTART 0
#define CST816_ADDR 0x15
/* More data bus class: https://github.com/moononournation/Arduino_GFX/wiki/Data-Bus-Class */
Arduino_DataBus *bus = new Arduino_RPiPicoSPI(TFT_DC, TFT_CS, SPI_SCK, SPI_MOSI, GFX_NOT_DEFINED /* MISO */);
//Arduino_DataBus *bus = new Arduino_HWSPI(TFT_DC, TFT_CS);
//Arduino_DataBus *bus = new Arduino_SWSPI(TFT_DC, TFT_CS, SPI_SCK, SPI_MOSI, GFX_NOT_DEFINED /* MISO */);
/* More display class: https://github.com/moononournation/Arduino_GFX/wiki/Display-Class */
Arduino_GFX *gfx = new Arduino_ST7789(bus, TFT_RST, TFT_ROTATION, true /* IPS */,
TFT_WIDTH, TFT_HEIGHT,
TFT_COLSTART, TFT_ROWSTART);
/*******************************************************************************
* End of Arduino_GFX setting
******************************************************************************/
uint32_t screenWidth;
uint32_t screenHeight;
uint32_t bufSize;
lv_display_t *disp;
lv_color_t *disp_draw_buf;
uint32_t millis_cb(void)
{
return millis();
}
/* LVGL calls it when a rendered image needs to copied to the display*/
void my_disp_flush(lv_display_t *disp, const lv_area_t *area, uint8_t *px_map)
{
uint32_t w = lv_area_get_width(area);
uint32_t h = lv_area_get_height(area);
gfx->draw16bitRGBBitmap(area->x1, area->y1, (uint16_t *)px_map, w, h);
/*Call it to tell LVGL you are ready*/
lv_disp_flush_ready(disp);
}
// ==== CST816 data structure ====
struct TouchData {
uint8_t gesture;
uint8_t fingers;
uint16_t x;
uint16_t y;
};
TouchData currentData = {0,0,0,0};
/*Read the touchpad*/
void my_touchpad_read(lv_indev_t *indev, lv_indev_data_t *data)
{
Wire.beginTransmission(CST816_ADDR);
Wire.write(0x01);
Wire.endTransmission(false);
Wire.requestFrom(CST816_ADDR, 6);
if (Wire.available() == 6) {
currentData.gesture = Wire.read();
currentData.fingers = Wire.read();
currentData.x = (Wire.read() & 0x0F) << 8 | Wire.read();
currentData.y = (Wire.read() & 0x0F) << 8 | Wire.read();
if (currentData.fingers > 0) {
data->state = LV_INDEV_STATE_PRESSED;
data->point.x = currentData.x;
data->point.y = currentData.y;
} else {
data->state = LV_INDEV_STATE_RELEASED;
}
}
}
void setup()
{
delay(2000);
Serial.begin(115200);
delay(500);
Serial.println("--- Start ---");
// initialize digital pin LED_BUILTIN as an output.
pinMode(LED_BUILTIN, OUTPUT);
Wire.begin();
// Send a Reset to CST816
pinMode(TP_RST, OUTPUT);
digitalWrite(TP_RST, HIGH);
delay(50);
digitalWrite(TP_RST, LOW);
delay(30);
digitalWrite(TP_RST, HIGH);
delay(50);
Serial.println("Arduino_GFX LVGL_Arduino_v9 example ");
String LVGL_Arduino = String('V') + lv_version_major() + "." + lv_version_minor() + "." + lv_version_patch();
Serial.println(LVGL_Arduino);
// Init Display
if (!gfx->begin())
{
Serial.println("gfx->begin() failed!");
}
gfx->fillScreen(RGB565_BLACK);
// Turn ON back-light
pinMode(TFT_BL, OUTPUT);
digitalWrite(TFT_BL, LOW);
lv_init();
/*Set a tick source so that LVGL will know how much time elapsed. */
lv_tick_set_cb(millis_cb);
screenWidth = gfx->width();
screenHeight = gfx->height();
bufSize = screenWidth * 40;
Serial.println("LVGL disp_draw_buf heap_caps_malloc failed! malloc again...");
disp_draw_buf = (lv_color_t *)malloc(bufSize * 2);
if (!disp_draw_buf)
{
Serial.println("LVGL disp_draw_buf allocate failed!");
}
else
{
disp = lv_display_create(screenWidth, screenHeight);
lv_display_set_flush_cb(disp, my_disp_flush);
lv_display_set_buffers(disp, disp_draw_buf, NULL, bufSize * 2, LV_DISPLAY_RENDER_MODE_PARTIAL);
/*Initialize the (dummy) input device driver*/
lv_indev_t *indev = lv_indev_create();
lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER); /*Touchpad should have POINTER type*/
lv_indev_set_read_cb(indev, my_touchpad_read);
/* Create LVGL content */
lv_obj_t *label_top = lv_label_create(lv_scr_act());
lv_label_set_text(label_top, "\nLVGL! (V" GFX_STR(LVGL_VERSION_MAJOR) "." GFX_STR(LVGL_VERSION_MINOR) "." GFX_STR(LVGL_VERSION_PATCH) ")");
lv_obj_align(label_top, LV_ALIGN_TOP_MID, 0, 0);
lv_obj_t *label_bottom = lv_label_create(lv_screen_active());
lv_label_set_text(label_bottom, "coXXect.blogspot.com\n");
lv_obj_align(label_bottom, LV_ALIGN_BOTTOM_MID, 0, 0);
// create a ON/OFF button
lv_obj_t *btn_ONOFF = lv_btn_create(lv_screen_active()); // Create a button
lv_obj_set_size(btn_ONOFF, 100, 100);
lv_obj_align(btn_ONOFF, LV_ALIGN_CENTER, 0, 0);
lv_obj_t *btn_label_ONOFF = lv_label_create(btn_ONOFF); // Create label inside button
lv_label_set_text(btn_label_ONOFF, "ON"); // Set label text
lv_obj_center(btn_label_ONOFF); // Center label inside button
// Add event to toggle button text
lv_obj_add_event_cb(btn_ONOFF, [](lv_event_t * e) {
lv_obj_t * label = lv_obj_get_child((lv_obj_t *)lv_event_get_target(e), 0);
const char * text = lv_label_get_text(label);
if (strcmp(text, "ON") == 0){
// LED ON
lv_label_set_text(label, "OFF");
digitalWrite(LED_BUILTIN, HIGH); // Turn ON LED
}else{
// LED OFF
lv_label_set_text(label, "ON");
digitalWrite(LED_BUILTIN, LOW); // Turn OFF LED
}
}, LV_EVENT_CLICKED, NULL);
}
Serial.println("Setup done");
}
void loop()
{
lv_task_handler(); /* let the GUI do its work */
delay(5);
}
rpPico2_ST7789_CST816_lvgl_button_2.ino, Create a button and slider to control onboard LED.
/*
LVGL v9 exercise run on Raspberry Pi Pico 2 + Waveshare 1.9inch Touch LCD
(170x320 ST7789V2 SPI LCD and CST816 I2C capacitive touch).
Create a button and slider to control onboard LED.
https://coxxect.blogspot.com/2025/12/lvgl-v9-exercise-run-on-raspberry-pi.html
Using board of Raspberry Pi Pico/RP2040/RP2350 by Earle F. Philhower III in Boards Manager.
https://github.com/earlephilhower/arduino-pico
Install Earle Philhower Raspberry Pi Pico Arduino core in Arduino IDE
https://coxxect.blogspot.com/2023/08/install-earle-philhower-raspberry-pi.html
*/
/*Using LVGL with Arduino requires some extra steps:
*Be sure to read the docs here: https://docs.lvgl.io/master/get-started/platforms/arduino.html */
#include <lvgl.h>
/*******************************************************************************
* Start of Arduino_GFX setting
* Raspberry Pi Pico dev board : CS: 17, DC: 22, RST: 21, BL: 20, SCK: 18, MOSI: 19, MISO: 16
******************************************************************************/
#include <Arduino_GFX_Library.h>
#include <Wire.h>
#define SPI_MOSI 19
#define SPI_SCK 18
#define TFT_CS 17
#define TFT_DC 22
#define TFT_RST 21
#define TFT_BL 20
#define TP_SDA 4 // default I2C SDA
#define TP_SCL 5 // default I2C SCL
#define TP_RST 3
#define TP_IRQ 2
#define TFT_ROTATION 0
#define TFT_WIDTH 170
#define TFT_HEIGHT 320
#define TFT_COLSTART 35
#define TFT_ROWSTART 0
#define CST816_ADDR 0x15
/* More data bus class: https://github.com/moononournation/Arduino_GFX/wiki/Data-Bus-Class */
Arduino_DataBus *bus = new Arduino_RPiPicoSPI(TFT_DC, TFT_CS, SPI_SCK, SPI_MOSI, GFX_NOT_DEFINED /* MISO */);
//Arduino_DataBus *bus = new Arduino_HWSPI(TFT_DC, TFT_CS);
//Arduino_DataBus *bus = new Arduino_SWSPI(TFT_DC, TFT_CS, SPI_SCK, SPI_MOSI, GFX_NOT_DEFINED /* MISO */);
/* More display class: https://github.com/moononournation/Arduino_GFX/wiki/Display-Class */
Arduino_GFX *gfx = new Arduino_ST7789(bus, TFT_RST, TFT_ROTATION, true /* IPS */,
TFT_WIDTH, TFT_HEIGHT,
TFT_COLSTART, TFT_ROWSTART);
/*******************************************************************************
* End of Arduino_GFX setting
******************************************************************************/
uint32_t screenWidth;
uint32_t screenHeight;
uint32_t bufSize;
lv_display_t *disp;
lv_color_t *disp_draw_buf;
uint32_t millis_cb(void)
{
return millis();
}
/* LVGL calls it when a rendered image needs to copied to the display*/
void my_disp_flush(lv_display_t *disp, const lv_area_t *area, uint8_t *px_map)
{
uint32_t w = lv_area_get_width(area);
uint32_t h = lv_area_get_height(area);
gfx->draw16bitRGBBitmap(area->x1, area->y1, (uint16_t *)px_map, w, h);
/*Call it to tell LVGL you are ready*/
lv_disp_flush_ready(disp);
}
// ==== CST816 data structure ====
struct TouchData {
uint8_t gesture;
uint8_t fingers;
uint16_t x;
uint16_t y;
};
TouchData currentData = {0,0,0,0};
/*Read the touchpad*/
void my_touchpad_read(lv_indev_t *indev, lv_indev_data_t *data)
{
Wire.beginTransmission(CST816_ADDR);
Wire.write(0x01);
Wire.endTransmission(false);
Wire.requestFrom(CST816_ADDR, 6);
if (Wire.available() == 6) {
currentData.gesture = Wire.read();
currentData.fingers = Wire.read();
currentData.x = (Wire.read() & 0x0F) << 8 | Wire.read();
currentData.y = (Wire.read() & 0x0F) << 8 | Wire.read();
if (currentData.fingers > 0) {
data->state = LV_INDEV_STATE_PRESSED;
data->point.x = currentData.x;
data->point.y = currentData.y;
} else {
data->state = LV_INDEV_STATE_RELEASED;
}
}
}
int settingBrightness = 50;
void setBrightness(){
analogWrite(LED_BUILTIN, map(settingBrightness, 0, 100, 0, 255));
}
void setup()
{
delay(2000);
Serial.begin(115200);
delay(500);
Serial.println("--- Start ---");
// initialize digital pin LED_BUILTIN as an output.
pinMode(LED_BUILTIN, OUTPUT);
setBrightness();
Wire.begin();
// Send a Reset to CST816
pinMode(TP_RST, OUTPUT);
digitalWrite(TP_RST, HIGH);
delay(50);
digitalWrite(TP_RST, LOW);
delay(30);
digitalWrite(TP_RST, HIGH);
delay(50);
Serial.println("Arduino_GFX LVGL_Arduino_v9 Exercise ");
String LVGL_Arduino = String('V') + lv_version_major() + "." + lv_version_minor() + "." + lv_version_patch();
Serial.println(LVGL_Arduino);
// Init Display
if (!gfx->begin())
{
Serial.println("gfx->begin() failed!");
}
gfx->fillScreen(RGB565_BLACK);
// Turn ON back-light
pinMode(TFT_BL, OUTPUT);
digitalWrite(TFT_BL, LOW);
lv_init();
/*Set a tick source so that LVGL will know how much time elapsed. */
lv_tick_set_cb(millis_cb);
screenWidth = gfx->width();
screenHeight = gfx->height();
bufSize = screenWidth * 40;
Serial.println("LVGL disp_draw_buf heap_caps_malloc failed! malloc again...");
disp_draw_buf = (lv_color_t *)malloc(bufSize * 2);
if (!disp_draw_buf)
{
Serial.println("LVGL disp_draw_buf allocate failed!");
}
else
{
disp = lv_display_create(screenWidth, screenHeight);
lv_display_set_flush_cb(disp, my_disp_flush);
lv_display_set_buffers(disp, disp_draw_buf, NULL, bufSize * 2, LV_DISPLAY_RENDER_MODE_PARTIAL);
/*Initialize the (dummy) input device driver*/
lv_indev_t *indev = lv_indev_create();
lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER); /*Touchpad should have POINTER type*/
lv_indev_set_read_cb(indev, my_touchpad_read);
/* Create LVGL content */
// Top Label
lv_obj_t *label_top = lv_label_create(lv_scr_act());
lv_label_set_text(label_top, "\nLVGL! (V" GFX_STR(LVGL_VERSION_MAJOR) "." GFX_STR(LVGL_VERSION_MINOR) "." GFX_STR(LVGL_VERSION_PATCH) ")");
lv_obj_align(label_top, LV_ALIGN_TOP_MID, 0, 0);
// Bottom Label
lv_obj_t *label_bottom = lv_label_create(lv_screen_active());
lv_label_set_text(label_bottom, "coXXect.blogspot.com\n");
lv_obj_align(label_bottom, LV_ALIGN_BOTTOM_MID, 0, 0);
// Create a container
lv_obj_t *container = lv_obj_create(lv_scr_act());
lv_obj_set_size(container, screenWidth - 10, screenHeight - 80);
lv_obj_align(container, LV_ALIGN_CENTER, 0, 0);
// Setup Flex Flow
lv_obj_set_flex_flow(container, LV_FLEX_FLOW_COLUMN);
lv_obj_set_style_pad_row(container, 10, 0);
//
lv_obj_t *label_brightness = lv_label_create(container);
lv_label_set_text(label_brightness, "Brightness");
lv_obj_t *slider_brightness = lv_slider_create(container);
lv_obj_set_width(slider_brightness, screenWidth - 40);
lv_slider_set_range(slider_brightness, 0, 100);
lv_slider_set_value(slider_brightness, settingBrightness, LV_ANIM_OFF); // default 50%
lv_obj_t *slider_label = lv_label_create(container);
lv_label_set_text_fmt(slider_label, "%d %%", lv_slider_get_value(slider_brightness));
// Slider event: update label + LED brightness
lv_obj_add_event_cb(slider_brightness, [](lv_event_t * e) {
lv_obj_t * slider = (lv_obj_t *) lv_event_get_target(e);
settingBrightness = lv_slider_get_value(slider);
lv_obj_t * label = (lv_obj_t *)lv_event_get_user_data(e);
lv_label_set_text_fmt(label, "%d %%", settingBrightness);
setBrightness();
}, LV_EVENT_VALUE_CHANGED, slider_label);
// create a ON/OFF button
lv_obj_t *btn_ONOFF = lv_btn_create(container); // Create a button
lv_obj_set_size(btn_ONOFF, 100, 100);
lv_obj_t *btn_label_ONOFF = lv_label_create(btn_ONOFF); // Create label inside button
lv_label_set_text(btn_label_ONOFF, "OFF"); // Set label text
lv_obj_center(btn_label_ONOFF); // Center label inside button
// Add event to toggle button text
lv_obj_add_event_cb(btn_ONOFF, [](lv_event_t * e) {
lv_obj_t * label = lv_obj_get_child((lv_obj_t *)lv_event_get_target(e), 0);
const char * text = lv_label_get_text(label);
// Get the slider from user_data
lv_obj_t * slider = static_cast<lv_obj_t *>(lv_event_get_user_data(e));
if (strcmp(text, "ON") == 0) {
// LED ON
lv_label_set_text(label, "OFF");
setBrightness();
// Enable slider
lv_obj_clear_state(slider, LV_STATE_DISABLED); // Enable Slider
lv_obj_clear_flag(slider, LV_OBJ_FLAG_HIDDEN); // show
} else {
// LED OFF
lv_label_set_text(label, "ON");
analogWrite(LED_BUILTIN, 0);
// Disable slider
lv_obj_add_state(slider, LV_STATE_DISABLED); // Disable Slider
lv_obj_add_flag(slider, LV_OBJ_FLAG_HIDDEN); // hide
}
}, LV_EVENT_CLICKED, slider_brightness);
}
Serial.println("Setup done");
}
void loop()
{
lv_task_handler(); /* let the GUI do its work */
delay(5);
}
Comments
Post a Comment