esp32-S3驱动TFT-st7789V2显示屏
本文介绍了使用ESP32-S3开发板驱动2.0寸ST7789V2 TFT显示屏的项目。项目采用SPI通信方式,详细说明了硬件引脚连接方案(包括背光、片选、数据/命令等引脚配置),并提供了完整的项目结构。代码部分实现了基本的SPI通信、显示屏初始化和控制功能,支持设置显示窗口和批量数据传输,为后续集成LVGL图形库奠定了基础。项目通过CMake和Kconfig管理配置,便于在不同环境下灵活调整参数。
·
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寸(最终效果)


项目结构
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
更多推荐



所有评论(0)