Description
What MCU/Processor/Board and compiler are you using?
esp32s3
What do you want to achieve?
I want to see a demo using a display with 3-wire SPI (no D/C pin, 9 bit data).
What have you tried so far?
I managed to show the first frame on the screen but then it doesn’t update.
Code to reproduce
#include <stdio.h>
#include <unistd.h>
#include <sys/lock.h>
#include <sys/param.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "sdkconfig.h"
#include "esp_rom_sys.h"
#include "esp_timer.h"
#include "esp_err.h"
#include "esp_log.h"
#include "hal/gpio_ll.h"
#include "soc/gpio_struct.h"
#include "soc/io_mux_reg.h"
#include "lvgl.h"
#include "esp_lcd_panel_io.h"
#include "esp_lcd_panel_vendor.h"
#include "esp_lcd_panel_ops.h"
#include "driver/spi_master.h"
// Define pin numbers for the display
#define PIN_NUM_SCLK 12
#define PIN_NUM_MOSI 11
#define PIN_NUM_CS 10
#define PIN_NUM_RST 1
#define PIN_NUM_BL 41
// Define LCD dimensions
#define LCD_WIDTH 240
#define LCD_HEIGHT 320
static const char *TAG = "ST7789_LVGL";
// External function to display demo UI
extern void example_lvgl_demo_ui(lv_disp_t *disp);
static inline void spi_delay() {
//esp_rom_delay_us(1);
}
// Function to send a 9-bit data or command to the display via SPI
static void spi_send_9bit(bool is_data, uint8_t byte) {
uint16_t word = (is_data ? 0x100 : 0x000) | byte;
// Optimized bit-banging
GPIO.out_w1tc = (1 << PIN_NUM_CS); // Set CS low
for (int i = 8; i >= 0; i--) {
// Clock low
GPIO.out_w1tc = (1 << PIN_NUM_SCLK);
if ((word >> i) & 0x01)
GPIO.out_w1ts = (1 << PIN_NUM_MOSI); // Set MOSI high if bit is 1
else
GPIO.out_w1tc = (1 << PIN_NUM_MOSI); // Set MOSI low if bit is 0
// Clock high
GPIO.out_w1ts = (1 << PIN_NUM_SCLK);
}
GPIO.out_w1ts = (1 << PIN_NUM_CS); // Set CS high
}
// Function to send a command to the LCD
static void lcd_send_cmd(uint8_t cmd) { spi_send_9bit(false, cmd); }
// Function to send data to the LCD
static void lcd_send_data(uint8_t data) { spi_send_9bit(true, data); }
// Function to reset the LCD display
static void lcd_reset() {
gpio_set_level(PIN_NUM_RST, 0); // Set reset pin low
vTaskDelay(pdMS_TO_TICKS(100)); // Wait for 100ms
gpio_set_level(PIN_NUM_RST, 1); // Set reset pin high
vTaskDelay(pdMS_TO_TICKS(100)); // Wait for 100ms
}
// Function to initialize the ST7789 LCD display
static void st7789_init() {
lcd_reset();
lcd_send_cmd(0x01); vTaskDelay(pdMS_TO_TICKS(150)); // Software reset
lcd_send_cmd(0x11); vTaskDelay(pdMS_TO_TICKS(120)); // Sleep out
lcd_send_cmd(0x36); lcd_send_data(0x00); // Memory data access control
lcd_send_cmd(0x3A); lcd_send_data(0x55); // Pixel format
lcd_send_cmd(0x21); // Inversion on
lcd_send_cmd(0x29); // Display on
vTaskDelay(pdMS_TO_TICKS(50)); // Wait for 50ms
}
// Function to set the window for drawing on the display
static void lcd_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) {
lcd_send_cmd(0x2A); // Set column address
lcd_send_data(x0 >> 8); lcd_send_data(x0 & 0xFF);
lcd_send_data(x1 >> 8); lcd_send_data(x1 & 0xFF);
lcd_send_cmd(0x2B); // Set row address
lcd_send_data(y0 >> 8); lcd_send_data(y0 & 0xFF);
lcd_send_data(y1 >> 8); lcd_send_data(y1 & 0xFF);
lcd_send_cmd(0x2C); // Write to memory
}
// Function to flush the screen buffer to the display
void my_flush_cb(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p) {
int32_t w = area->x2 - area->x1 + 1; // Calculate width of the area
int32_t h = area->y2 - area->y1 + 1; // Calculate height of the area
lcd_set_window(area->x1, area->y1, area->x2, area->y2); // Set window to draw
// Send the buffer contents to the screen
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
uint16_t color = lv_color_to16(*color_p); // Convert LVGL color to RGB565
lcd_send_data(color >> 8); // Send high byte
lcd_send_data(color & 0xFF); // Send low byte
color_p++; // Move to the next pixel
}
}
// Signal that the flush is complete
lv_disp_flush_ready(disp_drv); // Mark the display as ready for next update
}
// ========== app_main ==============
// Main application entry point
void app_main(void) {
gpio_config_t io_conf = {
.mode = GPIO_MODE_OUTPUT,
.pin_bit_mask = (1ULL << PIN_NUM_SCLK) |
(1ULL << PIN_NUM_MOSI) |
(1ULL << PIN_NUM_CS) |
(1ULL << PIN_NUM_RST) |
(1ULL << PIN_NUM_BL) // Configure GPIO pins
};
gpio_config(&io_conf);
gpio_set_level(PIN_NUM_CS, 1); // Set CS high
gpio_set_level(PIN_NUM_SCLK, 1); // Set SCLK high
gpio_set_level(PIN_NUM_BL, 1); // Set backlight on
ESP_LOGI(TAG, "Initializing ST7789...");
st7789_init(); // Initialize the display
lv_init(); // Initialize LVGL library
// Memory for the screen buffers
static lv_color_t buf1[LCD_WIDTH * 40]; // First buffer with 40 lines
static lv_color_t buf2[LCD_WIDTH * 40]; // Second buffer for double buffering
// Create the display driver
static lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv); // Initialize display driver structure
disp_drv.hor_res = LCD_WIDTH; // Set horizontal resolution
disp_drv.ver_res = LCD_HEIGHT; // Set vertical resolution
disp_drv.flush_cb = my_flush_cb; // Set flush callback for screen updates
static lv_disp_draw_buf_t draw_buf;
lv_disp_draw_buf_init(&draw_buf, buf1, buf2, LCD_WIDTH * 40); // Initialize the draw buffer
disp_drv.draw_buf = &draw_buf;
// Register the display driver
lv_disp_drv_register(&disp_drv);
example_lvgl_demo_ui(NULL); // Run the LVGL demo UI
// Main loop
while (1) {
lv_timer_handler(); // Handle LVGL tasks
vTaskDelay(pdMS_TO_TICKS(20)); // Delay to allow for updates
}
}