ESP32-C3 SPI Display Issue: TFT_eSPI Library Fails While Direct Hardware SPI Works
Environment
- Board: ESP32-C3 (e.g., ESP32-C3-DevKitM-1)
-
Arduino Core:
[email protected] -
TFT_eSPI Version:
2.5.43 - Display: ILI9486 (320x480, SPI)
Symptoms
-
✅ Direct SPI (Working)
- Manual
SPIClassinitialization succeeds. - Display responds perfectly at 27MHz (Mode 0).
- Manual
-
❌ TFT_eSPI (Failing)
- Backlight turns on but no pixel activity.
Configuration
User_Setup.h
#define ILI9486_DRIVER
#define TFT_MOSI 6 // GPIO6 (ESP32-C3 SPI2)
#define TFT_SCLK 4 // GPIO4 (ESP32-C3 SPI2)
#define TFT_CS 7 // GPIO7
#define TFT_DC 3 // GPIO3
#define TFT_RST 10 // GPIO10
#define SPI_FREQUENCY 27000000
#define TFT_SPI_MODE SPI_MODE0
SPI Direct Code:
#include <SPI.h>
// 引脚定义
#define TFT_SCLK 4 // SPI时钟
#define TFT_MOSI 6 // SPI数据输出
#define TFT_RST 10 // 复位引脚
#define TFT_DC 3 // 数据/命令选择
#define TFT_CS 7 // 片选引脚
// 屏幕分辨率(根据实际屏幕修改)
#define TFT_WIDTH 320
#define TFT_HEIGHT 480
SPIClass mySPI(FSPI); // 使用FSPI(ESP32-C3的SPI2)
void tft_send_cmd(uint8_t cmd) {
digitalWrite(TFT_DC, LOW); // 命令模式
digitalWrite(TFT_CS, LOW);
mySPI.transfer(cmd);
digitalWrite(TFT_CS, HIGH);
}
void tft_send_data(uint8_t data) {
digitalWrite(TFT_DC, HIGH); // 数据模式
digitalWrite(TFT_CS, LOW);
mySPI.transfer(data);
digitalWrite(TFT_CS, HIGH);
}
void tft_init() {
// 初始化GPIO
pinMode(TFT_DC, OUTPUT);
pinMode(TFT_RST, OUTPUT);
pinMode(TFT_CS, OUTPUT);
// 复位序列
digitalWrite(TFT_RST, LOW);
delay(100);
digitalWrite(TFT_RST, HIGH);
delay(120);
// 初始化SPI
mySPI.begin(TFT_SCLK, -1, TFT_MOSI, TFT_CS); // MISO未使用设为-1
mySPI.setFrequency(40000000); // 40MHz
// 基础初始化命令(适用于大多数SPI TFT)
tft_send_cmd(0x01); // 软件复位
delay(150);
tft_send_cmd(0x11); // 退出睡眠模式
delay(120);
tft_send_cmd(0x3A); // 像素格式设置
tft_send_data(0x55); // 16位像素(RGB565)
delay(10);
tft_send_cmd(0x29); // 开启显示
delay(20);
}
void tft_fill_screen(uint16_t color) {
// 设置写入区域为全屏
tft_send_cmd(0x2A); // 列地址设置
tft_send_data(0x00); tft_send_data(0x00);
tft_send_data((TFT_WIDTH >> 8) & 0xFF);
tft_send_data(TFT_WIDTH & 0xFF);
tft_send_cmd(0x2B); // 行地址设置
tft_send_data(0x00); tft_send_data(0x00);
tft_send_data((TFT_HEIGHT >> 8) & 0xFF);
tft_send_data(TFT_HEIGHT & 0xFF);
tft_send_cmd(0x2C); // 内存写入命令
// 快速填充颜色
uint8_t hi = color >> 8, lo = color & 0xFF;
digitalWrite(TFT_DC, HIGH);
digitalWrite(TFT_CS, LOW);
for(uint32_t i = 0; i < TFT_WIDTH * TFT_HEIGHT; i++) {
mySPI.transfer(hi);
mySPI.transfer(lo);
}
digitalWrite(TFT_CS, HIGH);
}
void setup() {
Serial.begin(115200);
Serial.println("Starting TFT init...");
tft_init();
Serial.println("TFT initialized");
tft_fill_screen(0x0000); // RGB565黑色
Serial.println("Screen filled black");
}
void loop() {
// 保持显示
}
ESPI Code:
#include <TFT_eSPI.h>
TFT_eSPI tft = TFT_eSPI(); // 初始化库
void setup() {
// 初始化屏幕
pinMode(TFT_RST, OUTPUT);
digitalWrite(TFT_RST, LOW); // 复位屏幕
delay(50);
digitalWrite(TFT_RST, HIGH); // 解除复位
delay(50);
tft.init();
tft.setRotation(3); // 根据屏幕方向调整(0~3)
tft.fillScreen(TFT_BLACK); // 全屏填充黑色
}
void loop() {
// 无操作,保持黑屏
}
Circuit Diagram
The development board is ESP32-C3 Supermini
I have a similar issue with my ESP32C3: When building the project with ESP-IDF, I can see a kernel panic at TFT_eSPI::fillScreen(unsigned int), and its inlined function, TFT_eSPI::begin_tft_write(). Reading the objdump, TFT_eSPI tries to write to RAM using a pointer, which is set to 0x10, causing the equivalent of a segmentation fault.
related to #3743
I figure out this bug in v3.X.X, you must set pin TFT_MISO to a nonzero pin. If not, in v3.X.X esp32 core, this will cause the spi register (*_spi_cmd) never to refresh. (https://github.com/Bodmer/TFT_eSPI/blob/5793878d24161c1ed23ccb136f8564f332506d53/Processors/TFT_eSPI_ESP32_C3.h#L541-L546) And if you don't define TFT_MISO or TFT_MISO == -1, this code will set it to TFT_MOSI. https://github.com/Bodmer/TFT_eSPI/blob/5793878d24161c1ed23ccb136f8564f332506d53/Processors/TFT_eSPI_ESP32_C3.h#L316-L321
@libewa It is a library error.
https://github.com/Bodmer/TFT_eSPI/blob/5793878d24161c1ed23ccb136f8564f332506d53/Processors/TFT_eSPI_ESP32_C3.h#L71
You should change from #define SPI_PORT SPI2_HOST to #define SPI_PORT 2
Based by discussion
Having the same issue, but your code solved it? Somehow it is working Great. Thank you
Tôi đã tìm ra lỗi này trong v3.XX, bạn phải đặt chân TFT_MISO thành một chân khác 0. Nếu không, trong lõi esp32 v3.XX, điều này sẽ khiến thanh ghi spi (*_spi_cmd) không bao giờ được làm mới. (
TFT_eSPI/Bộ xử lý/TFT_eSPI_ESP32_C3.h
Dòng 541 đến 546 trong 5793878
#define TFT_WRITE_BITS ( D , B ) *_spi_mosi_dlen = B-1;
*_spi_w = D;
_spi_cmd = SPI_UPDATE;
trong khi (_spi_cmd & SPI_UPDATE);
_spi_cmd = SPI_USR;
trong khi (_spi_cmd & SPI_USR); ) Và nếu bạn không định nghĩa TFT_MISO hoặc TFT_MISO == -1, đoạn mã này sẽ đặt nó thành TFT_MOSI. TFT_eSPI/Bộ xử lý/TFT_eSPI_ESP32_C3.hDòng 316 đến 321 trong 5793878
#nếu đã xác định( CONFIG_IDF_TARGET_ESP32C3 ) || đã xác định( CONFIG_IDF_TARGET_ESP32S2 ) #nếu ( TFT_MISO == -1 ) #undef TFT_MISO #define TFT_MISO TFT_MOSI #kết thúc nếu #kết thúc nếu
I also didn't define MISO, but why does esp32 work and c3 doesn't?
With C3, you must define TFT_MISO to a pin that is neither 0 nor -1. In the 3.0 core, if TFT_MISO and TFT_MOSI have the same pin, it will cause the register stucking.
Với C3, bạn phải định nghĩa TFT_MISO cho một chân không phải là 0 hoặc -1. Trong lõi 3.0, nếu TFT_MISO và TFT_MOSI có cùng chân, thanh ghi sẽ bị kẹt.
Only C3, any other board? esp32 is working without define MISO
With C3, you must define TFT_MISO to a pin that is neither 0 nor -1. In the 3.0 core, if TFT_MISO and TFT_MOSI have the same pin, it will cause the register stucking.
I tried and the screen is still blank. #define USER_SETUP_INFO "User_Setup" #define ST7789_DRIVER // Full configuration option, define additional parameters below for this display
#define TFT_WIDTH 240 // ST7789 240 x 240 and 240 x 320 #define TFT_HEIGHT 280
#define CGRAM_OFFSET
#define TFT_MISO 5 // Automatically assigned with ESP8266 if not defined #define TFT_MOSI 6 // Automatically assigned with ESP8266 if not defined #define TFT_SCLK 4 // Automatically assigned with ESP8266 if not defined
#define TFT_CS 7 // Chip select control pin D8 #define TFT_DC 8 // Data Command control pin #define TFT_RST 9 // Reset pin (could connect to NodeMCU RST, see next line)
#define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH #define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters #define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters #define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm #define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:-. #define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. #define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts #define SMOOTH_FONT
#define SPI_FREQUENCY 40000000 #define SPI_READ_FREQUENCY 20000000
Working just put miso to number other than 0 Just in case I put it according to software SPI rather than Hardware SPI. https://github.com/Bodmer/TFT_eSPI/blob/5793878d24161c1ed23ccb136f8564f332506d53/Processors/TFT_eSPI_ESP32_C3.h#L71 there is lib issue #define SPI_PORT SPI2_HOST to #define SPI_PORT 2 Then try
@libewa It is a library error.
TFT_eSPI/Processors/TFT_eSPI_ESP32_C3.h
Line 71 in 5793878
#define SPI_PORT SPI2_HOST
You should change from
#define SPI_PORT SPI2_HOSTto#define SPI_PORT 2Based by discussion
I can confirm that this change fixed the library for me on my ESP32-C3 Supermini.
Setting TFT_MISO to a value greater than 0 did nothing.
See my comment in another issue. I've attached a tarball with minimal modification to codes with esp32c3. There is also an arduino example in it.
Xem bình luận của tôi ở một bài viết khác. Tôi đã đính kèm một tệp tarball với một số sửa đổi nhỏ cho mã lệnh esp32c3. Ngoài ra còn có một ví dụ về Arduino trong đó.
Ok, my screen working with esp32c3 2 issues:
- Edit SPI2_HOST to 2 or FSPI
- Define MISO different 0 or -1 Hope Bodmer fixes this.