Description:
I have purchased a Waveshare 1.69" display including a ESP32-S3. I have a project, where I want to use this display, but before making custom PCB, I would like to make a simple hello world example.
What am I using:
Waveshare 1.69" with ESP32-S3 and 240x280 display using ST7789
What do you want to achieve?
Simplest possible Hello World example
What have you tried so far?
A lot… over the last 3 months. I have finally got something to compile, and that have been a struggle in it selves, since not many online examples seems to compile out of the box. My problem is, that my software crashes at this line:
lv_display_t *disp = lv_st7789_create(240, 280, LV_LCD_FLAG_NONE, send_cmd, send_color);
and I have used weeks on it, without getting anywhere. Any input is welcome, please help a struggling guy
complete code
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "lvgl/lvgl.h"
#include "driver/gpio.h"
#include "driver/spi_master.h"
#include "drivers/display/st7789/lv_st7789.h"
#include "esp_timer.h"
#include <string.h>
#define TAG "Main"
/*
#define CONFIG_WIDTH 240
#define CONFIG_HEIGHT 240
#define CONFIG_MOSI_GPIO 23
#define CONFIG_SCLK_GPIO 18
#define CONFIG_CS_GPIO -1
#define CONFIG_DC_GPIO 19
#define CONFIG_RESET_GPIO 15
#define CONFIG_BL_GPIO -1
*/
#define BLINK_GPIO GPIO_NUM_2 // The built-in LED is connected to GPIO2
#define ST7789_SPI_HOST SPI3_HOST
#define ST7789_DMA_CHAN 1
#define ST7789_PIN_NUM_CS 5
#define ST7789_PIN_NUM_DC 4
#define ST7789_PIN_NUM_MOSI 7
#define ST7789_PIN_NUM_SCLK 6
#define ST7789_PIN_NUM_RST 8
#define ST7789_PIN_NUM_BCKL 15 // bruges vi ikke lige nu
// SPI device handle
spi_device_handle_t spi;
//static void lv_tick_task(void *arg) {
// lv_tick_inc(portTICK_PERIOD_MS);
//}
/* Send short command to the LCD. This function shall wait until the transaction finishes. */
void send_cmd(lv_display_t *disp, const uint8_t *cmd, size_t cmd_size, const uint8_t *param, size_t param_size)
{
spi_transaction_t t;
memset(&t, 0, sizeof(t));
t.length = 8;
t.tx_buffer = &cmd;
t.user = (void *)0; // D/C line low for command
ESP_ERROR_CHECK(spi_device_polling_transmit(spi, &t));
}
/* Send large array of pixel data to the LCD. If necessary, this function has to do the byte-swapping. This function can do the transfer in the background. */
void send_color(lv_display_t *disp, const uint8_t *cmd, size_t cmd_size, uint8_t *param, size_t param_size)
{
spi_transaction_t t;
memset(&t, 0, sizeof(t));
t.length = 8 * cmd_size;
t.tx_buffer = cmd;
t.user = (void *)1; // D/C line high for data
ESP_ERROR_CHECK(spi_device_polling_transmit(spi, &t));
}
void send_data(const uint8_t *data, int len) {
spi_transaction_t t;
memset(&t, 0, sizeof(t));
t.length = 8 * len;
t.tx_buffer = data;
t.user = (void *)1; // D/C line high for data
ESP_ERROR_CHECK(spi_device_polling_transmit(spi, &t));
}
void display_flush(lv_display_t *disp, const lv_area_t *area, lv_color_t *color_p) {
uint32_t size = (area->x2 - area->x1 + 1) * (area->y2 - area->y1 + 1);
// Set the address window
uint8_t cmd_column[] = {0x2A, (area->x1 >> 8) & 0xFF, area->x1 & 0xFF, (area->x2 >> 8) & 0xFF, area->x2 & 0xFF};
spi_transaction_t t;
memset(&t, 0, sizeof(t));
t.length = 8 * sizeof(cmd_column); // 8 bits per byte
t.tx_buffer = cmd_column;
t.user = (void *)0; // D/C line low for command
ESP_ERROR_CHECK(spi_device_polling_transmit(spi, &t));
uint8_t cmd_row[] = {0x2B, (area->y1 >> 8) & 0xFF, area->y1 & 0xFF, (area->y2 >> 8) & 0xFF, area->y2 & 0xFF};
memset(&t, 0, sizeof(t));
t.length = 8 * sizeof(cmd_row); // 8 bits per byte
t.tx_buffer = cmd_row;
t.user = (void *)0; // D/C line low for command
ESP_ERROR_CHECK(spi_device_polling_transmit(spi, &t));
uint8_t cmd_write[] = {0x2C};
memset(&t, 0, sizeof(t));
t.length = 8 * sizeof(cmd_write); // 8 bits per byte
t.tx_buffer = cmd_write;
t.user = (void *)0; // D/C line low for command
ESP_ERROR_CHECK(spi_device_polling_transmit(spi, &t));
// Send the pixel data using send_data function
send_data((uint8_t *)color_p, size * 2); // size * 2 because each pixel is 16 bits
// Tell LVGL that flushing is done
lv_disp_flush_ready(disp);
}
void lvgl_driver_init(void)
{
// SPI bus configuration
spi_bus_config_t buscfg = {
.mosi_io_num = ST7789_PIN_NUM_MOSI,
.sclk_io_num = ST7789_PIN_NUM_SCLK,
.miso_io_num = -1,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
.max_transfer_sz = 240 * 280 * 2 + 8 // Adjust based on your display resolution
};
ESP_ERROR_CHECK(spi_bus_initialize(ST7789_SPI_HOST, &buscfg, SPI_DMA_CH_AUTO));
// SPI device configuration
spi_device_interface_config_t devcfg = {
.clock_speed_hz = 10 * 1000 * 1000, // Clock out at 10 MHz
.mode = 0, // SPI mode 0
.spics_io_num = ST7789_PIN_NUM_CS, // CS pin
.queue_size = 7,
.flags = SPI_DEVICE_HALFDUPLEX
};
ESP_ERROR_CHECK(spi_bus_add_device(ST7789_SPI_HOST, &devcfg, &spi));
}
void init_gpio(void) {
gpio_config_t io_conf;
// Configure DC pin
io_conf.intr_type = GPIO_INTR_DISABLE;
io_conf.mode = GPIO_MODE_OUTPUT;
io_conf.pin_bit_mask = (1ULL << ST7789_PIN_NUM_DC);
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
gpio_config(&io_conf);
// Configure RST pin
io_conf.pin_bit_mask = (1ULL << ST7789_PIN_NUM_RST);
gpio_config(&io_conf);
// Configure backlight pin
io_conf.pin_bit_mask = (1ULL << ST7789_PIN_NUM_BCKL);
gpio_config(&io_conf);
}
void reset_display(void) {
gpio_set_level(ST7789_PIN_NUM_RST, 0);
vTaskDelay(pdMS_TO_TICKS(100));
gpio_set_level(ST7789_PIN_NUM_RST, 1);
vTaskDelay(pdMS_TO_TICKS(100));
}
void app_main(void)
{
// Initialize LVGL
lv_init();
// Initialize SPI and I2C bus used by the drivers
lvgl_driver_init();
// Display resolution
uint32_t hor_res = 240;
uint32_t ver_res = 280;
// Initialize display buffer
static lv_color_t buf1[240 * 10]; // LV_HOR_RES_MAX
static lv_color_t buf2[240 * 10]; // LV_HOR_RES_MAX
/**
* Create an LCD display with ST7789 driver
* @param hor_res horizontal resolution
* @param ver_res vertical resolution
* @param flags default configuration settings (mirror, RGB ordering, etc.)
* @param send_cmd platform-dependent function to send a command to the LCD controller (usually uses polling transfer)
* @param send_color platform-dependent function to send pixel data to the LCD controller (usually uses DMA transfer: must implement a 'ready' callback)
* @return pointer to the created display
*/
lv_display_t *disp = lv_st7789_create(240, 280, LV_LCD_FLAG_NONE, send_cmd, send_color);
// Initialize display driver
// lv_disp_t *disp = lv_display_create(hor_res, ver_res);
lv_display_set_flush_cb(disp, &display_flush);
lv_display_set_buffers(disp, &buf1, &buf2, sizeof(buf1), LV_DISP_RENDER_MODE_PARTIAL);
/*
// Set up tick task
const esp_timer_create_args_t periodic_timer_args = {
.callback = &lv_tick_task,
.name = "periodic_gui"
};
esp_timer_handle_t periodic_timer;
ESP_ERROR_CHECK(esp_timer_create(&periodic_timer_args, &periodic_timer));
ESP_ERROR_CHECK(esp_timer_start_periodic(periodic_timer, 1 * 1000));
*/
// Example: Create a simple label
lv_obj_t *label = lv_label_create(lv_scr_act());
lv_label_set_text(label, "Hello, LVGL!");
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
// init_gpio();
// reset_display();
long blinker = esp_timer_get_time()/1000;
bool blink = false;
gpio_reset_pin(BLINK_GPIO);
gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT);
// Handle LVGL tasks
while (1) {
vTaskDelay(pdMS_TO_TICKS(10));
lv_task_handler();
if (esp_timer_get_time()/1000 - blinker > 500) {
gpio_set_level(BLINK_GPIO, blink);
blinker = esp_timer_get_time()/1000;
blink = !blink;
// printf("blinking\n");
lv_obj_t *label = lv_label_create(lv_scr_act());
lv_label_set_text(label, "Hello, LVGL!");
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
}
}
}