feat: add display and touch initialization in DisplayHandler
This commit is contained in:
11
main/display/constants.h
Normal file
11
main/display/constants.h
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "driver/spi_master.h"
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
|
||||||
|
#define PIN_TOUCH_IRQ GPIO_NUM_4
|
||||||
|
#define PIN_TOUCH_SDA GPIO_NUM_5
|
||||||
|
#define PIN_TOUCH_SCL GPIO_NUM_6
|
||||||
|
#define PIN_BUSY GPIO_NUM_7
|
||||||
|
#define PIN_RST GPIO_NUM_8
|
||||||
|
#define PIN_DC GPIO_NUM_9
|
||||||
|
#define PIN_CS GPIO_NUM_10
|
||||||
@@ -1,62 +1,157 @@
|
|||||||
#include "display.h"
|
#include "display/display.h"
|
||||||
#include "common/constants.h"
|
#include "common/constants.h"
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "esp_log.h"
|
||||||
#include "freertos/task.h"
|
|
||||||
#include "freertos/event_groups.h"
|
|
||||||
// TODO: implement actual display functionality
|
|
||||||
|
|
||||||
DisplayHandler::DisplayHandler(QueueHandle_t touch_queue, SemaphoreHandle_t lvgl_mutex) {
|
DisplayHandler::~DisplayHandler() {
|
||||||
(void)touch_queue;
|
if (_spi_mutex != nullptr) {
|
||||||
(void)lvgl_mutex;
|
vSemaphoreDelete(_spi_mutex);
|
||||||
}
|
}
|
||||||
|
if (_spi != nullptr) {
|
||||||
DisplayHandler::~DisplayHandler() { }
|
spi_bus_remove_device(_spi);
|
||||||
|
}
|
||||||
EInkDisplayHandler::EInkDisplayHandler(QueueHandle_t touch_queue, SemaphoreHandle_t lvgl_mutex)
|
if (_tp_handle != nullptr) {
|
||||||
: DisplayHandler(touch_queue, lvgl_mutex) { }
|
esp_lcd_touch_del(_tp_handle);
|
||||||
|
}
|
||||||
EInkDisplayHandler::~EInkDisplayHandler() { }
|
if (_tp_io_handle != nullptr) {
|
||||||
|
esp_lcd_panel_io_del(_tp_io_handle);
|
||||||
void EInkDisplayHandler::init(EventGroupHandle_t system_event_group) {
|
|
||||||
if (system_event_group != NULL) {
|
|
||||||
xEventGroupSetBits(system_event_group, DISPLAY_READY_BIT);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void EInkDisplayHandler::start_event_loop() {
|
|
||||||
// Minimal background task to represent display processing
|
void DisplayHandler::init_devices(bool set_display_ready /*= true*/) {
|
||||||
xTaskCreate(
|
ESP_LOGI("DisplayHandler", "Initializing display and touch...");
|
||||||
// use the static adapter and pass `this` as the task parameter
|
_epd_init();
|
||||||
EInkDisplayHandler::task_adapter,
|
_touch_init();
|
||||||
"display_task",
|
ESP_LOGI("DisplayHandler", "Display and touch initialized.");
|
||||||
2048,
|
if (set_display_ready) {
|
||||||
this,
|
ESP_LOGI("DisplayHandler", "Setting display ready bit.");
|
||||||
tskIDLE_PRIORITY + 1,
|
xEventGroupSetBits(_system_event_group, DISPLAY_READY_BIT | TOUCH_CALIBRATED_BIT);
|
||||||
nullptr
|
}
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
|
||||||
void EInkDisplayHandler::task_adapter(void* arg) {
|
void DisplayHandler::epd_write_cmd(uint8_t cmd) {
|
||||||
EInkDisplayHandler* self = static_cast<EInkDisplayHandler*>(arg);
|
ESP_LOGI("DisplayHandler", "epd_write_cmd: waiting to send 0x%02X", cmd);
|
||||||
if (self) {
|
xSemaphoreTake(_spi_mutex, portMAX_DELAY);
|
||||||
self->run_event_loop();
|
_dangerous_epd_write_cmd_without_lock(cmd);
|
||||||
|
xSemaphoreGive(_spi_mutex);
|
||||||
|
ESP_LOGI("DisplayHandler", "epd_write_cmd: 0x%02X done", cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DisplayHandler::epd_write_data(uint8_t data) {
|
||||||
|
ESP_LOGI("DisplayHandler", "epd_write_data: waiting to send 0x%02X", data);
|
||||||
|
xSemaphoreTake(_spi_mutex, portMAX_DELAY);
|
||||||
|
_dangerous_epd_write_data_without_lock(data);
|
||||||
|
xSemaphoreGive(_spi_mutex);
|
||||||
|
ESP_LOGI("DisplayHandler", "epd_write_data: 0x%02X done", data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DisplayHandler::epd_write_cmd_with_data(uint8_t cmd, const uint8_t* data, size_t data_len) {
|
||||||
|
ESP_LOGI("DisplayHandler", "epd_write_cmd_with_data: waiting to send cmd 0x%02X with %u bytes of data", cmd, (unsigned)data_len);
|
||||||
|
xSemaphoreTake(_spi_mutex, portMAX_DELAY);
|
||||||
|
_dangerous_epd_write_cmd_without_lock(cmd);
|
||||||
|
for (size_t i = 0; i < data_len; ++i) {
|
||||||
|
_dangerous_epd_write_data_without_lock(data[i]);
|
||||||
|
}
|
||||||
|
xSemaphoreGive(_spi_mutex);
|
||||||
|
ESP_LOGI("DisplayHandler", "epd_write_cmd_with_data: cmd 0x%02X with %u bytes of data done", cmd, (unsigned)data_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Private methods
|
||||||
|
//
|
||||||
|
|
||||||
|
void DisplayHandler::_dangerous_epd_write_cmd_without_lock(uint8_t cmd) {
|
||||||
|
ESP_LOGI("DisplayHandler", "_dangerous_epd_write_cmd_without_lock: sending 0x%02X", cmd);
|
||||||
|
gpio_set_level(PIN_DC, 0); // Command mode
|
||||||
|
spi_transaction_t t {};
|
||||||
|
t.length = 8;t.tx_buffer = &cmd;
|
||||||
|
esp_err_t err = spi_device_polling_transmit(_spi, &t);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE("DisplayHandler", "Failed to send data 0x%02X", cmd);
|
||||||
} else {
|
} else {
|
||||||
printf("EInkDisplayHandler::task_adapter received null pointer\n");
|
ESP_LOGI("DisplayHandler", "_dangerous_epd_write_cmd_without_lock: 0x%02X sent", cmd);
|
||||||
}
|
|
||||||
// If run_event_loop ever returns, delete the task.
|
|
||||||
vTaskDelete(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EInkDisplayHandler::run_event_loop() {
|
|
||||||
for (;;) {
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
shutdown_display_handlerFunc EInkDisplayHandler::get_shutdown_display_handler() {
|
void DisplayHandler::_dangerous_epd_write_data_without_lock(uint8_t data) {
|
||||||
return nullptr;
|
ESP_LOGI("DisplayHandler", "_dangerous_epd_write_data_without_lock: sending 0x%02X", data);
|
||||||
|
gpio_set_level(PIN_DC, 1); // Data mode
|
||||||
|
spi_transaction_t t = { };
|
||||||
|
t.length = 8; t.tx_buffer = &data;
|
||||||
|
esp_err_t err = spi_device_polling_transmit(_spi, &t);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE("DisplayHandler", "Failed to send data 0x%02X", data);
|
||||||
|
} else {
|
||||||
|
ESP_LOGI("DisplayHandler", "_dangerous_epd_write_data_without_lock: 0x%02X sent", data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
restart_display_handlerFunc EInkDisplayHandler::get_restart_display_handler() {
|
// required to be called by inheriting class after SPI device is created
|
||||||
return nullptr;
|
void DisplayHandler::_epd_init(void) {
|
||||||
|
ESP_LOGI("DisplayHandler", "Initializing EPD...");
|
||||||
|
// 1. Hardware Reset
|
||||||
|
gpio_set_level(PIN_RST, 0);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
gpio_set_level(PIN_RST, 1);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
|
||||||
|
// 2. Initialization Sequence
|
||||||
|
const uint8_t panel_setting_data[] = { 0x1F };
|
||||||
|
epd_write_cmd_with_data(0x00, panel_setting_data, 1); // Panel Setting
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
const uint8_t vcom_data[] = { 0x10, 0x07 };
|
||||||
|
epd_write_cmd_with_data(0x50, vcom_data, 2); // VCOM
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
epd_write_cmd(0x04); // Power ON
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(100)); // Wait for power on
|
||||||
|
|
||||||
|
// Check BUSY pin
|
||||||
|
ESP_LOGI("DisplayHandler", "Waiting for EPD to be ready...");
|
||||||
|
while (gpio_get_level(PIN_BUSY) == 1) {
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
}
|
||||||
|
ESP_LOGI("DisplayHandler", "EPD is ready.");
|
||||||
|
const uint8_t booster_data[] = { 0x27, 0x27 };
|
||||||
|
epd_write_cmd_with_data(0x06, booster_data, 2); // Booster Soft Start
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
}
|
||||||
|
|
||||||
|
void DisplayHandler::_touch_init(void) {
|
||||||
|
ESP_LOGI("DisplayHandler", "Initializing touch...");
|
||||||
|
// 1. Initialize I2C Bus
|
||||||
|
i2c_config_t conf = {};
|
||||||
|
conf.mode = I2C_MODE_MASTER;
|
||||||
|
conf.sda_io_num = PIN_TOUCH_SDA;
|
||||||
|
conf.scl_io_num = PIN_TOUCH_SCL;
|
||||||
|
conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
|
||||||
|
conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
|
||||||
|
conf.master.clk_speed = 400000;
|
||||||
|
|
||||||
|
i2c_param_config(I2C_NUM_0, &conf);
|
||||||
|
i2c_driver_install(I2C_NUM_0, I2C_MODE_MASTER, 0, 0, 0);
|
||||||
|
ESP_LOGI("DisplayHandler", "I2C driver installed");
|
||||||
|
|
||||||
|
// 2. Initialize GT911
|
||||||
|
ESP_LOGI("DisplayHandler", "Initializing GT911 touch controller...");
|
||||||
|
esp_lcd_panel_io_i2c_config_t tp_io_config = {};
|
||||||
|
// temporarily disable -Wmissing-field-initializers, as ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG macro does not set all fields
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
|
||||||
|
esp_lcd_panel_io_i2c_config_t default_tp_io_config = ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG();
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
tp_io_config.dev_addr = default_tp_io_config.dev_addr;
|
||||||
|
tp_io_config.control_phase_bytes = default_tp_io_config.control_phase_bytes;
|
||||||
|
tp_io_config.dc_bit_offset = default_tp_io_config.dc_bit_offset;
|
||||||
|
tp_io_config.lcd_cmd_bits = default_tp_io_config.lcd_cmd_bits;
|
||||||
|
tp_io_config.flags = default_tp_io_config.flags;
|
||||||
|
esp_lcd_new_panel_io_i2c(I2C_NUM_0, &tp_io_config, &_tp_io_handle);
|
||||||
|
|
||||||
|
esp_lcd_touch_config_t tp_cfg = {};
|
||||||
|
tp_cfg.x_max = 800;
|
||||||
|
tp_cfg.y_max = 480;
|
||||||
|
tp_cfg.rst_gpio_num = PIN_RST;
|
||||||
|
tp_cfg.int_gpio_num = PIN_TOUCH_IRQ;
|
||||||
|
|
||||||
|
esp_lcd_touch_new_i2c_gt911(_tp_io_handle, &tp_cfg, &_tp_handle);
|
||||||
|
ESP_LOGI("DisplayHandler", "GT911 touch controller initialized");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,48 +1,38 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
#include "driver/spi_master.h"
|
||||||
#include <stdio.h>
|
#include "driver/gpio.h"
|
||||||
#include <inttypes.h>
|
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
#include "esp_system.h"
|
#include "esp_lcd_touch_gt911.h"
|
||||||
|
#include "display/constants.h"
|
||||||
typedef void (*shutdown_display_handlerFunc)(void);
|
#include <driver/i2c.h>
|
||||||
typedef void (*restart_display_handlerFunc)(void);
|
|
||||||
|
|
||||||
class DisplayHandler {
|
class DisplayHandler {
|
||||||
public:
|
public:
|
||||||
DisplayHandler(QueueHandle_t touch_queue, SemaphoreHandle_t lvgl_mutex);
|
DisplayHandler(
|
||||||
// the system_event_group is used to set display-ready bit
|
EventGroupHandle_t system_event_group
|
||||||
virtual void init(EventGroupHandle_t system_event_group) = 0;
|
) : _system_event_group(system_event_group) { }
|
||||||
virtual void start_event_loop() = 0;
|
~DisplayHandler();
|
||||||
// get a handler to perform display shutdown cleanup, this is called after event loop ends and DisplayHandler is deleted
|
|
||||||
virtual shutdown_display_handlerFunc get_shutdown_display_handler() = 0;
|
// required to be called by inheriting class after SPI device is created
|
||||||
virtual restart_display_handlerFunc get_restart_display_handler() = 0;
|
// set set_display_ready to false if further initialization is needed before marking display ready
|
||||||
virtual ~DisplayHandler() = 0;
|
void init_devices(bool set_display_ready = true);
|
||||||
|
|
||||||
|
void epd_write_cmd(uint8_t cmd);
|
||||||
|
void epd_write_data(uint8_t data);
|
||||||
|
|
||||||
|
void epd_write_cmd_with_data(uint8_t cmd, const uint8_t* data, size_t data_len);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DisplayHandler(const DisplayHandler&) = delete;
|
SemaphoreHandle_t _spi_mutex = xSemaphoreCreateMutex();
|
||||||
DisplayHandler& operator=(const DisplayHandler&) = delete;
|
spi_device_handle_t _spi = nullptr;
|
||||||
};
|
EventGroupHandle_t _system_event_group = nullptr;
|
||||||
|
esp_lcd_panel_io_handle_t _tp_io_handle = nullptr;
|
||||||
class EInkDisplayHandler : public DisplayHandler {
|
esp_lcd_touch_handle_t _tp_handle = nullptr;
|
||||||
public:
|
|
||||||
EInkDisplayHandler(QueueHandle_t touch_queue, SemaphoreHandle_t lvgl_mutex);
|
void _dangerous_epd_write_cmd_without_lock(uint8_t cmd);
|
||||||
void init(EventGroupHandle_t system_event_group) override;
|
void _dangerous_epd_write_data_without_lock(uint8_t data);
|
||||||
void start_event_loop() override;
|
|
||||||
shutdown_display_handlerFunc get_shutdown_display_handler() override;
|
void _epd_init(void);
|
||||||
restart_display_handlerFunc get_restart_display_handler() override;
|
void _touch_init(void);
|
||||||
~EInkDisplayHandler() override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Task adapter used for FreeRTOS task creation. It forwards to the
|
|
||||||
// instance `run_event_loop()` method using the `this` pointer passed
|
|
||||||
// as the task parameter.
|
|
||||||
static void task_adapter(void* arg);
|
|
||||||
|
|
||||||
// Instance method that implements the display task loop.
|
|
||||||
void run_event_loop();
|
|
||||||
// prevent copying
|
|
||||||
EInkDisplayHandler(const EInkDisplayHandler&) = delete;
|
|
||||||
EInkDisplayHandler& operator=(const EInkDisplayHandler&) = delete;
|
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user