LilyGo T-Deck keyboard support & display driver improvements (#19)
* LilyGo T-Deck keyboard support * reverse logic * docs and readability * cleanup * optimize driver buffer * cleanup
This commit is contained in:
committed by
GitHub
parent
14eb43211d
commit
ccbe6b7ab8
@@ -1,29 +1,61 @@
|
||||
#include "esp_log.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "config.h"
|
||||
#include "keyboard.h"
|
||||
#include "kernel.h"
|
||||
#include "esp_lvgl_port.h"
|
||||
#include "log.h"
|
||||
|
||||
#define TAG "lilygo_tdeck_bootstrap"
|
||||
#define TDECK_PERI_POWERON GPIO_NUM_10
|
||||
#define TAG "tdeck_bootstrap"
|
||||
|
||||
lv_disp_t* lilygo_tdeck_init_display();
|
||||
|
||||
static void tdeck_power_on() {
|
||||
static bool tdeck_power_on() {
|
||||
ESP_LOGI(TAG, "power on");
|
||||
gpio_config_t device_power_signal_config = {
|
||||
.pin_bit_mask = BIT64(TDECK_PERI_POWERON),
|
||||
.pin_bit_mask = BIT64(TDECK_POWERON_GPIO),
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pull_up_en = GPIO_PULLUP_DISABLE,
|
||||
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||
.intr_type = GPIO_INTR_DISABLE,
|
||||
};
|
||||
gpio_config(&device_power_signal_config);
|
||||
gpio_set_level(TDECK_PERI_POWERON, 1);
|
||||
|
||||
if (gpio_config(&device_power_signal_config) != ESP_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (gpio_set_level(TDECK_POWERON_GPIO, 1) != ESP_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void lilygo_tdeck_bootstrap() {
|
||||
tdeck_power_on();
|
||||
static bool init_i2c() {
|
||||
const i2c_config_t i2c_conf = {
|
||||
.mode = I2C_MODE_MASTER,
|
||||
.sda_io_num = GPIO_NUM_18,
|
||||
.sda_pullup_en = GPIO_PULLUP_DISABLE,
|
||||
.scl_io_num = GPIO_NUM_8,
|
||||
.scl_pullup_en = GPIO_PULLUP_DISABLE,
|
||||
.master.clk_speed = 400000
|
||||
};
|
||||
|
||||
return i2c_param_config(TDECK_I2C_BUS_HANDLE, &i2c_conf) == ESP_OK
|
||||
&& i2c_driver_install(TDECK_I2C_BUS_HANDLE, i2c_conf.mode, 0, 0, 0) == ESP_OK;
|
||||
}
|
||||
|
||||
bool lilygo_tdeck_bootstrap() {
|
||||
if (!tdeck_power_on()) {
|
||||
TT_LOG_E(TAG, "failed to power on device");
|
||||
}
|
||||
|
||||
// Give keyboard's ESP time to boot
|
||||
// It uses I2C and seems to interfere with the touch driver
|
||||
tt_delay_ms(500);
|
||||
|
||||
if (!init_i2c()) {
|
||||
TT_LOG_E(TAG, "failed to init I2C");
|
||||
}
|
||||
|
||||
keyboard_wait_for_response();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "driver/i2c.h"
|
||||
#include "driver/gpio.h"
|
||||
|
||||
#define TDECK_I2C_BUS_HANDLE (0)
|
||||
#define TDECK_POWERON_GPIO GPIO_NUM_10
|
||||
@@ -6,7 +6,7 @@
|
||||
#include "esp_log.h"
|
||||
#include "esp_lvgl_port.h"
|
||||
|
||||
#define TAG "lilygo_tdeck_display"
|
||||
#define TAG "tdeck_display"
|
||||
|
||||
#define LCD_SPI_HOST SPI2_HOST
|
||||
#define LCD_PIN_SCLK GPIO_NUM_40
|
||||
@@ -21,6 +21,7 @@
|
||||
#define LCD_VERTICAL_RESOLUTION 240
|
||||
#define LCD_BITS_PER_PIXEL 16
|
||||
#define LCD_DRAW_BUFFER_HEIGHT (LCD_VERTICAL_RESOLUTION / 10)
|
||||
#define LCD_SPI_TRANSFER_HEIGHT (LCD_VERTICAL_RESOLUTION / 10)
|
||||
|
||||
// Backlight PWM
|
||||
#define LCD_BACKLIGHT_LEDC_TIMER LEDC_TIMER_0
|
||||
@@ -60,7 +61,7 @@ static void tdeck_backlight() {
|
||||
lv_disp_t* lilygo_tdeck_init_display() {
|
||||
ESP_LOGI(TAG, "creating display");
|
||||
|
||||
int draw_buffer_size = LCD_HORIZONTAL_RESOLUTION * LCD_DRAW_BUFFER_HEIGHT * (LCD_BITS_PER_PIXEL / 8);
|
||||
int max_transfer_size = LCD_HORIZONTAL_RESOLUTION * LCD_SPI_TRANSFER_HEIGHT * (LCD_BITS_PER_PIXEL / 8);
|
||||
|
||||
spi_bus_config_t bus_config = {
|
||||
.sclk_io_num = LCD_PIN_SCLK,
|
||||
@@ -68,7 +69,7 @@ lv_disp_t* lilygo_tdeck_init_display() {
|
||||
.miso_io_num = LCD_PIN_MISO,
|
||||
.quadwp_io_num = -1, // Quad SPI LCD driver is not yet supported
|
||||
.quadhd_io_num = -1, // Quad SPI LCD driver is not yet supported
|
||||
.max_transfer_sz = draw_buffer_size,
|
||||
.max_transfer_sz = max_transfer_size,
|
||||
};
|
||||
|
||||
if (spi_bus_initialize(LCD_SPI_HOST, &bus_config, SPI_DMA_CH_AUTO) != ESP_OK) {
|
||||
@@ -154,17 +155,18 @@ lv_disp_t* lilygo_tdeck_init_display() {
|
||||
.io_handle = io_handle,
|
||||
.panel_handle = panel_handle,
|
||||
.buffer_size = LCD_HORIZONTAL_RESOLUTION * LCD_DRAW_BUFFER_HEIGHT * (LCD_BITS_PER_PIXEL / 8),
|
||||
.double_buffer = false,
|
||||
.double_buffer = true, // Disable to free up SPIRAM
|
||||
.hres = LCD_HORIZONTAL_RESOLUTION,
|
||||
.vres = LCD_VERTICAL_RESOLUTION,
|
||||
.monochrome = false,
|
||||
.rotation = {
|
||||
.swap_xy = true, // TODO: check if code above is still needed
|
||||
.swap_xy = true,
|
||||
.mirror_x = true,
|
||||
.mirror_y = false,
|
||||
},
|
||||
.flags = {
|
||||
.buff_dma = true,
|
||||
.buff_dma = false,
|
||||
.buff_spiram = true,
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
#include "keyboard.h"
|
||||
#include "config.h"
|
||||
#include "hal/lv_hal.h"
|
||||
#include "tactility_core.h"
|
||||
#include "ui/lvgl_keypad.h"
|
||||
#include <driver/i2c.h>
|
||||
|
||||
#define TAG "tdeck_keyboard"
|
||||
#define KEYBOARD_SLAVE_ADDRESS 0x55
|
||||
|
||||
typedef struct {
|
||||
lv_indev_drv_t* driver;
|
||||
lv_indev_t* device;
|
||||
} KeyboardData;
|
||||
|
||||
static inline esp_err_t keyboard_i2c_read(uint8_t* output) {
|
||||
return i2c_master_read_from_device(
|
||||
TDECK_I2C_BUS_HANDLE,
|
||||
KEYBOARD_SLAVE_ADDRESS,
|
||||
output,
|
||||
1,
|
||||
configTICK_RATE_HZ / 10
|
||||
);
|
||||
}
|
||||
|
||||
void keyboard_wait_for_response() {
|
||||
TT_LOG_I(TAG, "wake await...");
|
||||
bool awake = false;
|
||||
uint8_t read_buffer = 0x00;
|
||||
do {
|
||||
awake = keyboard_i2c_read(&read_buffer) == ESP_OK;
|
||||
if (!awake) {
|
||||
tt_delay_ms(100);
|
||||
}
|
||||
} while (!awake);
|
||||
TT_LOG_I(TAG, "awake");
|
||||
}
|
||||
|
||||
/**
|
||||
* The callback simulates press and release events, because the T-Deck
|
||||
* keyboard only publishes press events on I2C.
|
||||
* LVGL currently works without those extra release events, but they
|
||||
* are implemented for correctness and future compatibility.
|
||||
*
|
||||
* @param indev_drv
|
||||
* @param data
|
||||
*/
|
||||
static void keyboard_read_callback(TT_UNUSED struct _lv_indev_drv_t* indev_drv, lv_indev_data_t* data) {
|
||||
static uint8_t last_buffer = 0x00;
|
||||
uint8_t read_buffer = 0x00;
|
||||
|
||||
// Defaults
|
||||
data->key = 0;
|
||||
data->state = LV_INDEV_STATE_RELEASED;
|
||||
|
||||
if (keyboard_i2c_read(&read_buffer) == ESP_OK) {
|
||||
if (read_buffer == 0 && read_buffer != last_buffer) {
|
||||
TT_LOG_I(TAG, "released %d", last_buffer);
|
||||
data->key = last_buffer;
|
||||
data->state = LV_INDEV_STATE_RELEASED;
|
||||
} else if (read_buffer != 0) {
|
||||
TT_LOG_I(TAG, "pressed %d", read_buffer);
|
||||
data->key = read_buffer;
|
||||
data->state = LV_INDEV_STATE_PRESSED;
|
||||
}
|
||||
}
|
||||
|
||||
last_buffer = read_buffer;
|
||||
}
|
||||
|
||||
Keyboard keyboard_alloc(_Nullable lv_disp_t* display) {
|
||||
KeyboardData* data = malloc(sizeof(KeyboardData));
|
||||
|
||||
data->driver = malloc(sizeof(lv_indev_drv_t));
|
||||
memset(data->driver, 0, sizeof(lv_indev_drv_t));
|
||||
lv_indev_drv_init(data->driver);
|
||||
|
||||
data->driver->type = LV_INDEV_TYPE_KEYPAD;
|
||||
data->driver->read_cb = &keyboard_read_callback;
|
||||
data->driver->disp = display;
|
||||
|
||||
data->device = lv_indev_drv_register(data->driver);
|
||||
tt_check(data->device != NULL);
|
||||
|
||||
tt_lvgl_keypad_set_indev(data->device);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void keyboard_free(Keyboard keyboard) {
|
||||
KeyboardData* data = (KeyboardData*)keyboard;
|
||||
lv_indev_delete(data->device);
|
||||
free(data->driver);
|
||||
free(data);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "lvgl.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void keyboard_wait_for_response();
|
||||
|
||||
typedef void* Keyboard;
|
||||
|
||||
Keyboard keyboard_alloc(_Nullable lv_disp_t* display);
|
||||
void keyboard_free(Keyboard keyboard);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,9 +1,10 @@
|
||||
#include "esp_lvgl_port.h"
|
||||
#include "keyboard.h"
|
||||
#include "log.h"
|
||||
#include "ui/lvgl_sync.h"
|
||||
#include <thread.h>
|
||||
|
||||
#define TAG "lilygo_tdeck_lvgl"
|
||||
#define TAG "tdeck_lvgl"
|
||||
|
||||
lv_disp_t* lilygo_tdeck_init_display();
|
||||
bool lilygo_tdeck_init_touch(esp_lcd_panel_io_handle_t* io_handle, esp_lcd_touch_handle_t* touch_handle);
|
||||
@@ -33,7 +34,6 @@ bool lilygo_init_lvgl() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Add touch
|
||||
if (!lilygo_tdeck_init_touch(&touch_io_handle, &touch_handle)) {
|
||||
return false;
|
||||
@@ -53,5 +53,7 @@ bool lilygo_init_lvgl() {
|
||||
// Set syncing functions
|
||||
tt_lvgl_sync_set(&lvgl_port_lock, &lvgl_port_unlock);
|
||||
|
||||
keyboard_alloc(display);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,37 +1,16 @@
|
||||
#include "config.h"
|
||||
#include "driver/i2c.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_lcd_touch_gt911.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
#include "driver/i2c.h"
|
||||
|
||||
#define TOUCH_I2C_PORT 0
|
||||
|
||||
#define TAG "lilygo_tdeck_touch"
|
||||
#define TAG "tdeck_touch"
|
||||
|
||||
bool lilygo_tdeck_init_touch(esp_lcd_panel_io_handle_t* io_handle, esp_lcd_touch_handle_t* touch_handle) {
|
||||
ESP_LOGI(TAG, "creating touch");
|
||||
|
||||
const i2c_config_t i2c_conf = {
|
||||
.mode = I2C_MODE_MASTER,
|
||||
.sda_io_num = GPIO_NUM_18,
|
||||
.sda_pullup_en = GPIO_PULLUP_DISABLE,
|
||||
.scl_io_num = GPIO_NUM_8,
|
||||
.scl_pullup_en = GPIO_PULLUP_DISABLE,
|
||||
.master.clk_speed = 400000
|
||||
};
|
||||
|
||||
if (i2c_param_config(TOUCH_I2C_PORT, &i2c_conf) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "i2c config failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (i2c_driver_install(TOUCH_I2C_PORT, i2c_conf.mode, 0, 0, 0) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "i2c driver install failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
const esp_lcd_panel_io_i2c_config_t touch_io_config = ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG();
|
||||
|
||||
if (esp_lcd_new_panel_io_i2c((esp_lcd_i2c_bus_handle_t)TOUCH_I2C_PORT, &touch_io_config, io_handle) != ESP_OK) {
|
||||
if (esp_lcd_new_panel_io_i2c((esp_lcd_i2c_bus_handle_t)TDECK_I2C_BUS_HANDLE, &touch_io_config, io_handle) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "touch io i2c creation failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user