esp32-S3驱动TFT-st7789V2显示屏

esp32-S3

这里使用esp32-S3开发板
在这里插入图片描述

屏幕引脚(厂家) ESP32-S3 引脚 功能
BL IO42 背光引脚(屏幕) IO42 BL(高电平亮)
CS IO37 片选信号
DC IO39 指令/数据
RES -1 不接线
SDA IO40 MOSI
SCL IO41 SCK
VCC 3V3 电源
GND GND 接地

屏幕2.0寸(最终效果)

在这里插入图片描述
![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/3e6d6927608f454fb2f624844fb8310e.jpeg

项目结构

st7789_project/
├── CMakeLists.txt
├── Kconfig.projbuild
├── sdkconfig.defaults
└── main/
    ├── CMakeLists.txt
    ├── Kconfig.projbuild
    └── main.c  # 主程序代码

CMakeLists.txt

cmake_minimum_required(VERSION 3.5)

include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(st7789v2)

sdkconfig.defaults

# Basic configuration
CONFIG_IDF_TARGET="esp32s3"
CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240=y
CONFIG_FREERTOS_HZ=1000

# SPI configuration
CONFIG_SPI_ENABLE_DEBUG_LOG=y
CONFIG_SPI_MASTER_IN_IRAM=y

# Log configuration
CONFIG_LOG_DEFAULT_LEVEL_INFO=y
CONFIG_LOG_COLORS=y

# Power management
CONFIG_PM_ENABLE=y
CONFIG_PM_DFS_INIT_AUTO=y

# LCD pin configuration
CONFIG_LCD_BL_PIN=42
CONFIG_LCD_SCK_PIN=41
CONFIG_LCD_MOSI_PIN=40
CONFIG_LCD_DC_PIN=39
CONFIG_LCD_CS_PIN=45
CONFIG_LCD_RST_PIN=-1
    

main\CMakeLists.txt

idf_component_register(SRCS "main.c"
                INCLUDE_DIRS ".")

main\Kconfig.projbuild

menu "LCD Configuration"
    config LCD_BL_PIN
        int "LCD Backlight Pin"
        default 42
        help
            GPIO pin connected to LCD backlight

    config LCD_SCK_PIN
        int "LCD SCK Pin"
        default 41
        help
            GPIO pin connected to LCD SCK (SPI clock)

    config LCD_MOSI_PIN
        int "LCD MOSI Pin"
        default 40
        help
            GPIO pin connected to LCD MOSI (SPI data)

    config LCD_DC_PIN
        int "LCD DC Pin"
        default 39
        help
            GPIO pin connected to LCD DC (data/command)

    config LCD_CS_PIN
        int "LCD CS Pin"
        default 45
        help
            GPIO pin connected to LCD CS (chip select)

    config LCD_RST_PIN
        int "LCD Reset Pin"
        default -1
        help
            GPIO pin connected to LCD RST (reset)
endmenu
    

main\main.c

#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "sdkconfig.h"
#include "rom/ets_sys.h"

// 引脚定义(从配置文件读取)
#define LCD_SCK_PIN    CONFIG_LCD_SCK_PIN
#define LCD_MOSI_PIN   CONFIG_LCD_MOSI_PIN
#define LCD_RST_PIN    CONFIG_LCD_RST_PIN
#define LCD_DC_PIN     CONFIG_LCD_DC_PIN
#define LCD_CS_PIN     CONFIG_LCD_CS_PIN
#define LCD_BL_PIN     CONFIG_LCD_BL_PIN

// 屏幕参数(LVGL需要这些定义)
#define LCD_HOR_RES    240
#define LCD_VER_RES    320
#define LCD_PIXEL_CLOCK_HZ 10000000  // 10MHz SPI时钟

// 颜色定义(兼容LVGL的RGB格式)
#define RED            0xFF0000
#define GREEN          0x00FF00
#define BLUE           0x0000FF
#define WHITE          0xFFFFFF
#define BLACK          0x000000

static const char *TAG = "ST7789";

// 模块化引脚操作函数(解决宏定义语法问题)
static inline void pin_high(int pin) {
    if (pin >= 0 && pin <= 45) {
        gpio_set_level(pin, 1);
    }
}

static inline void pin_low(int pin) {
    if (pin >= 0 && pin <= 45) {
        gpio_set_level(pin, 0);
    }
}

// 延迟函数(LVGL兼容)
static inline void delay_us(uint32_t us) {
    ets_delay_us(us);
}

static inline void delay_ms(uint32_t ms) {
    vTaskDelay(pdMS_TO_TICKS(ms));
}

// 模拟SPI发送1字节(优化时序,兼容LVGL高速刷新)
static void spi_send_byte(uint8_t byte) {
    for (int i = 0; i < 8; i++) {
        pin_low(LCD_SCK_PIN);
        delay_us(1);
        
        // 使用if-else代替三元运算符,避免宏定义冲突
        if (byte & 0x80) {
            pin_high(LCD_MOSI_PIN);
        } else {
            pin_low(LCD_MOSI_PIN);
        }
        
        byte <<= 1;
        delay_us(1);
        pin_high(LCD_SCK_PIN);
        delay_us(1);
    }
}

// 发送命令和数据(为LVGL提供基础接口)
static void tft_cmd(uint8_t cmd) {
    pin_low(LCD_DC_PIN);  // 命令模式
    pin_low(LCD_CS_PIN);  // 选中芯片
    spi_send_byte(cmd);
    pin_high(LCD_CS_PIN); // 释放芯片
}

static void tft_data(uint8_t data) {
    pin_high(LCD_DC_PIN); // 数据模式
    pin_low(LCD_CS_PIN);  // 选中芯片
    spi_send_byte(data);
    pin_high(LCD_CS_PIN); // 释放芯片
}

// 批量发送数据(LVGL刷屏需要)
static void tft_data_burst(const uint8_t *data, uint32_t len) {
    pin_high(LCD_DC_PIN);
    pin_low(LCD_CS_PIN);
    for (uint32_t i = 0; i < len; i++) {
        spi_send_byte(data[i]);
    }
    pin_high(LCD_CS_PIN);
}

// 设置显示窗口(LVGL绘制区域需要)
static void set_window(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) {
    tft_cmd(0x2A); // 列地址
    tft_data(x1 >> 8);
    tft_data(x1 & 0xFF);
    tft_data(x2 >> 8);
    tft_data(x2 & 0xFF);

    tft_cmd(0x2B); // 行地址
    tft_data(y1 >> 8);
    tft_data(y1 & 0xFF);
    tft_data(y2 >> 8);
    tft_data(y2 & 0xFF);

    tft_cmd(0x2C); // 准备写入
}

// 全屏填充(测试用)
static void fill_screen(uint32_t color) {
    set_window(0, 0, LCD_HOR_RES-1, LCD_VER_RES-1);
    
    uint8_t color_buf[3] = {
        (color >> 16) & 0xFF,
        (color >> 8) & 0xFF,
        color & 0xFF
    };
    
    // 连续发送像素数据
    pin_high(LCD_DC_PIN);
    pin_low(LCD_CS_PIN);
    for (int i = 0; i < LCD_HOR_RES * LCD_VER_RES; i++) {
        spi_send_byte(color_buf[0]);
        spi_send_byte(color_buf[1]);
        spi_send_byte(color_buf[2]);
    }
    pin_high(LCD_CS_PIN);
}

// GPIO初始化(解决负引脚左移警告)
static void gpio_init(void) {
    gpio_config_t io_conf = {
        .mode = GPIO_MODE_OUTPUT,
        .pull_up_en = GPIO_PULLUP_DISABLE,
        .pull_down_en = GPIO_PULLDOWN_DISABLE,
        .intr_type = GPIO_INTR_DISABLE
    };

    // 计算有效引脚掩码(跳过-1的引脚)
    uint64_t pin_mask = 0;
    if (LCD_SCK_PIN > 0)    pin_mask |= 1ULL << LCD_SCK_PIN;
    if (LCD_MOSI_PIN > 0)   pin_mask |= 1ULL << LCD_MOSI_PIN;
    if (LCD_DC_PIN > 0)     pin_mask |= 1ULL << LCD_DC_PIN;
    if (LCD_CS_PIN > 0)     pin_mask |= 1ULL << LCD_CS_PIN;
    if (LCD_RST_PIN > 0)    pin_mask |= 1ULL << LCD_RST_PIN;
    if (LCD_BL_PIN > 0)     pin_mask |= 1ULL << LCD_BL_PIN;

    io_conf.pin_bit_mask = pin_mask;
    gpio_config(&io_conf);

    // 初始化默认电平
    pin_high(LCD_SCK_PIN);
    pin_high(LCD_MOSI_PIN);
    pin_high(LCD_CS_PIN);
    pin_high(LCD_DC_PIN);
    pin_high(LCD_BL_PIN); // 打开背光
}

// 屏幕初始化(厂家时序)
static void lcd_init(void) {
    gpio_init();

    // 复位(若引脚有效)
    if (LCD_RST_PIN > 0) {
        pin_low(LCD_RST_PIN);
        delay_ms(100);
        pin_high(LCD_RST_PIN);
        delay_ms(120);
    }

    // 初始化序列
    tft_cmd(0x11);
    delay_ms(120);

    // 显示区域配置
    tft_cmd(0x2A);
    tft_data(0x00);
    tft_data(0x00);
    tft_data(0x00);
    tft_data(LCD_HOR_RES - 1);

    tft_cmd(0x2B);
    tft_data(0x00);
    tft_data(0x00);
    tft_data(0x00);
    tft_data(LCD_VER_RES - 1);

    // 电源和时序配置
    tft_cmd(0xB2);
    tft_data(0x0C);
    tft_data(0x0C);
    tft_data(0x00);
    tft_data(0x33);
    tft_data(0x33);

    tft_cmd(0xB7);
    tft_data(0x56);
    tft_cmd(0xBB);
    tft_data(0x18);
    tft_cmd(0xC0);
    tft_data(0x2C);
    tft_cmd(0xC2);
    tft_data(0x01);
    tft_cmd(0xC3);
    tft_data(0x1F);
    tft_cmd(0xC4);
    tft_data(0x20);
    tft_cmd(0xC6);
    tft_data(0x0F);

    // 伽马配置
    tft_cmd(0xE0);
    uint8_t gamma_pos[] = {0xD0,0x0D,0x14,0x0B,0x0B,0x07,0x3A,0x44,0x50,0x08,0x13,0x13,0x2D,0x32};
    tft_data_burst(gamma_pos, sizeof(gamma_pos));

    tft_cmd(0xE1);
    uint8_t gamma_neg[] = {0xD0,0x0D,0x14,0x0B,0x0B,0x07,0x3A,0x44,0x50,0x08,0x13,0x13,0x2D,0x32};
    tft_data_burst(gamma_neg, sizeof(gamma_neg));

    // 像素格式和显示控制
    tft_cmd(0x36);
    tft_data(0x00);  // 屏幕方向
    tft_cmd(0x3A);
    tft_data(0x66);  // 18bit RGB
    tft_cmd(0x29);   // 显示开启

    ESP_LOGI(TAG, "ST7789初始化完成,分辨率:%dx%d", LCD_HOR_RES, LCD_VER_RES);
}

// 颜色切换测试任务
static void test_task(void *arg) {
    lcd_init();

    // 测试颜色数组
    uint32_t colors[] = {RED, GREEN, BLUE, WHITE, BLACK};
    const char* names[] = {"红色", "绿色", "蓝色", "白色", "黑色"};
    int count = sizeof(colors)/sizeof(colors[0]);
    int idx = 0;

    while (1) {
        fill_screen(colors[idx]);
        ESP_LOGI(TAG, "显示%s", names[idx]);
        idx = (idx + 1) % count;
        delay_ms(2000);
    }
}

// LVGL对接准备(后续添加)
/*
void lv_port_disp_init(void) {
    // 此处将实现LVGL显示接口,使用上面的set_window和tft_data_burst等函数
}
*/

void app_main(void) {
    xTaskCreate(test_task, "lcd_test", 4096, NULL, 5, NULL);
    // 后续添加:lv_init(); lv_port_disp_init();
}
    

注:首次编译会用到sdkconfig.defaults,后面如果更改sdkconfig.defaults引脚,需删除sdkconfig

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐