#include <stdio.h>
#include <string.h> // Provides declaration for memset
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/spi_master.h"
#include "driver/gpio.h"
#include "esp_system.h"
// ---- Pin Definitions (according to your connections) ----
#define PIN_NUM_MISO 19
#define PIN_NUM_MOSI 23
#define PIN_NUM_CLK 16
#define PIN_NUM_CS 15
#define PIN_NUM_DC 2
#define PIN_NUM_RST 4
// --------- LCD Driver Selection ---------
typedef enum {
LCD_ILI9341,
LCD_ST7789
} lcd_driver_t;
#define CURRENT_LCD LCD_ILI9341
#if CURRENT_LCD == LCD_ILI9341
#define TFT_WIDTH 240
#define TFT_HEIGHT 320
#elif CURRENT_LCD == LCD_ST7789
#define TFT_WIDTH 240
#define TFT_HEIGHT 240
#endif
// --------------------------------------------------------------------------
// Helper macro to handle ESP-IDF error checks (without aborting)
// --------------------------------------------------------------------------
#define ESP_ERROR_CHECK_WITHOUT_ABORT(x) do { \
esp_err_t __err_rc = (x); \
if (__err_rc != ESP_OK) { \
printf("ESP ERROR %s:%d (%s)\n", __FILE__, __LINE__, \
esp_err_to_name(__err_rc)); \
} \
} while(0)
// --------------------------------------------------------------------------
// Low-Level SPI Communication Functions (Internal)
// --------------------------------------------------------------------------
// Send a command byte to the TFT display.
static void tft_send_cmd(spi_device_handle_t spi, const uint8_t cmd)
{
gpio_set_level(PIN_NUM_DC, 0); // Command mode.
spi_transaction_t t = {
.length = 8, // Length in bits.
.tx_buffer = &cmd,
};
ESP_ERROR_CHECK_WITHOUT_ABORT(spi_device_transmit(spi, &t));
}
// Send data bytes to the TFT display.
static void tft_send_data(spi_device_handle_t spi, const uint8_t *data, int len)
{
if (len == 0) return;
gpio_set_level(PIN_NUM_DC, 1); // Data mode.
spi_transaction_t t = {
.length = len * 8, // Length in bits.
.tx_buffer = data,
};
ESP_ERROR_CHECK_WITHOUT_ABORT(spi_device_transmit(spi, &t));
}
// --------------------------------------------------------------------------
// Public API Functions
// --------------------------------------------------------------------------
// Set the drawing window (region) on the display.
void tft_set_window(spi_device_handle_t spi, uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1)
{
uint8_t data[4];
// Set column addresses.
tft_send_cmd(spi, 0x2A);
data[0] = x0 >> 8;
data[1] = x0 & 0xFF;
data[2] = x1 >> 8;
data[3] = x1 & 0xFF;
tft_send_data(spi, data, 4);
// Set row addresses.
tft_send_cmd(spi, 0x2B);
data[0] = y0 >> 8;
data[1] = y0 & 0xFF;
data[2] = y1 >> 8;
data[3] = y1 & 0xFF;
tft_send_data(spi, data, 4);
// Write to RAM.
tft_send_cmd(spi, 0x2C);
}
// Fill the entire screen with the specified 16-bit color.
void tft_fill_screen(spi_device_handle_t spi, uint16_t color)
{
tft_set_window(spi, 0, 0, TFT_WIDTH - 1, TFT_HEIGHT - 1);
gpio_set_level(PIN_NUM_DC, 1); // Switch to data mode.
uint8_t highByte = color >> 8;
uint8_t lowByte = color & 0xFF;
uint8_t lineBuf[TFT_WIDTH * 2];
// Prepare one line of pixel data.
for (int i = 0; i < TFT_WIDTH; i++) {
lineBuf[i * 2] = highByte;
lineBuf[i * 2 + 1] = lowByte;
}
// Transmit the same line for every row.
spi_transaction_t t;
for (int y = 0; y < TFT_HEIGHT; y++) {
memset(&t, 0, sizeof(t));
t.length = sizeof(lineBuf) * 8;
t.tx_buffer = lineBuf;
ESP_ERROR_CHECK_WITHOUT_ABORT(spi_device_transmit(spi, &t));
}
}
// Set the rotation (orientation) of the display.
void tft_set_rotation(spi_device_handle_t spi, uint8_t rotation)
{
uint8_t madctl = 0x00;
switch (rotation % 4) {
case 0: madctl = 0x00; break;
case 1: madctl = 0x60; break;
case 2: madctl = 0xC0; break;
case 3: madctl = 0xA0; break;
}
tft_send_cmd(spi, 0x36);
tft_send_data(spi, &madctl, 1);
}
// --------------------------------------------------------------------------
// Initialization Functions for Specific LCD Drivers
// --------------------------------------------------------------------------
// Initialize the ILI9341 based TFT display.
static void tft_init_ili9341(spi_device_handle_t spi)
{
// Software Reset.
tft_send_cmd(spi, 0x01);
vTaskDelay(pdMS_TO_TICKS(150));
// Power Control A.
{
uint8_t data[] = {0x39, 0x2C, 0x00, 0x34, 0x02};
tft_send_cmd(spi, 0xCB);
tft_send_data(spi, data, sizeof(data));
}
// Power Control B.
{
uint8_t data[] = {0x00, 0xC1, 0x30};
tft_send_cmd(spi, 0xCF);
tft_send_data(spi, data, sizeof(data));
}
// Driver timing control A.
{
uint8_t data[] = {0x85, 0x00, 0x78};
tft_send_cmd(spi, 0xE8);
tft_send_data(spi, data, sizeof(data));
}
// Driver timing control B.
{
uint8_t data[] = {0x00, 0x00};
tft_send_cmd(spi, 0xEA);
tft_send_data(spi, data, sizeof(data));
}
// Power on sequence control.
{
uint8_t data[] = {0x64, 0x03, 0x12, 0x81};
tft_send_cmd(spi, 0xED);
tft_send_data(spi, data, sizeof(data));
}
// Pump ratio control.
{
uint8_t data[] = {0x20};
tft_send_cmd(spi, 0xF7);
tft_send_data(spi, data, 1);
}
// Power Control, VRH[5:0].
{
uint8_t data[] = {0x10};
tft_send_cmd(spi, 0xC0);
tft_send_data(spi, data, 1);
}
// Power Control, SAP[2:0]; BT[3:0].
{
uint8_t data[] = {0x10};
tft_send_cmd(spi, 0xC1);
tft_send_data(spi, data, 1);
}
// VCM Control.
{
uint8_t data[] = {0x3E, 0x28};
tft_send_cmd(spi, 0xC5);
tft_send_data(spi, data, sizeof(data));
}
// VCM Control2.
{
uint8_t data[] = {0x86};
tft_send_cmd(spi, 0xC7);
tft_send_data(spi, data, 1);
}
// Memory Access Control (MADCTL): set rotation and BGR mode.
{
uint8_t data[] = {0x48};
tft_send_cmd(spi, 0x36);
tft_send_data(spi, data, 1);
}
// Pixel Format Set (16 bit).
{
uint8_t data[] = {0x55};
tft_send_cmd(spi, 0x3A);
tft_send_data(spi, data, 1);
}
// Frame Rate Control.
{
uint8_t data[] = {0x00, 0x18};
tft_send_cmd(spi, 0xB1);
tft_send_data(spi, data, sizeof(data));
}
// Display Function Control.
{
uint8_t data[] = {0x08, 0x82, 0x27};
tft_send_cmd(spi, 0xB6);
tft_send_data(spi, data, sizeof(data));
}
// Exit Sleep Mode.
tft_send_cmd(spi, 0x11);
vTaskDelay(pdMS_TO_TICKS(120));
// Turn on Display.
tft_send_cmd(spi, 0x29);
vTaskDelay(pdMS_TO_TICKS(20));
}
// Initialize the ST7789 based TFT display.
void tft_init_st7789(spi_device_handle_t spi)
{
tft_send_cmd(spi, 0x01); // Software reset.
vTaskDelay(pdMS_TO_TICKS(150));
tft_send_cmd(spi, 0x11); // Sleep out.
vTaskDelay(pdMS_TO_TICKS(120));
// Set Color Mode to RGB565.
tft_send_cmd(spi, 0x3A);
{
uint8_t color_mode = 0x55;
tft_send_data(spi, &color_mode, 1);
}
// Memory Access Control (MADCTL) - adjust for rotation.
tft_send_cmd(spi, 0x36);
{
uint8_t madctl = 0x00;
tft_send_data(spi, &madctl, 1);
}
// Display On.
tft_send_cmd(spi, 0x29);
vTaskDelay(pdMS_TO_TICKS(20));
}
// Unified initialization function for supported LCD drivers.
void tft_init(spi_device_handle_t spi, lcd_driver_t driver)
{
switch (driver) {
case LCD_ILI9341:
tft_init_ili9341(spi);
break;
case LCD_ST7789:
tft_init_st7789(spi);
break;
default:
// Optionally handle an unknown driver case.
break;
}
}
// --------------------------------------------------------------------------
// Main Application Entry Point
// --------------------------------------------------------------------------
void app_main(void)
{
// 1. Configure SPI bus.
spi_bus_config_t buscfg = {
.miso_io_num = PIN_NUM_MISO,
.mosi_io_num = PIN_NUM_MOSI,
.sclk_io_num = PIN_NUM_CLK,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
.max_transfer_sz = 4096
};
ESP_ERROR_CHECK_WITHOUT_ABORT(spi_bus_initialize(HSPI_HOST, &buscfg, SPI_DMA_CH_AUTO));
// 2. Attach the LCD to the SPI bus.
spi_device_interface_config_t devcfg = {
.clock_speed_hz = 20 * 1000 * 1000, // 20 MHz.
.mode = 0, // SPI mode 0.
.spics_io_num = PIN_NUM_CS, // CS pin.
.queue_size = 7,
};
spi_device_handle_t spi;
ESP_ERROR_CHECK_WITHOUT_ABORT(spi_bus_add_device(HSPI_HOST, &devcfg, &spi));
// 3. Configure DC and RST pins as outputs.
gpio_set_direction(PIN_NUM_DC, GPIO_MODE_OUTPUT);
gpio_set_direction(PIN_NUM_RST, GPIO_MODE_OUTPUT);
// Hardware Reset.
gpio_set_level(PIN_NUM_RST, 0);
vTaskDelay(pdMS_TO_TICKS(100));
gpio_set_level(PIN_NUM_RST, 1);
vTaskDelay(pdMS_TO_TICKS(100));
// 4. Initialize the current display.
tft_init(spi, CURRENT_LCD);
// 5. Clear the screen with several colors.
tft_fill_screen(spi, 0x07E0); // Green.
vTaskDelay(pdMS_TO_TICKS(1000));
tft_fill_screen(spi, 0x7D7C); // Sky Blue.
vTaskDelay(pdMS_TO_TICKS(1000));
tft_fill_screen(spi, 0xF81F); // Vivid Magenta.
vTaskDelay(pdMS_TO_TICKS(1000));
// 6. Keep the task alive.
printf("Example finished!\n");
vTaskDelay(pdMS_TO_TICKS(1000));
}