diff --git a/Boards/LilygoTdeck/Source/hal/TdeckDisplay.cpp b/Boards/LilygoTdeck/Source/hal/TdeckDisplay.cpp index eec548dd..d2cab120 100644 --- a/Boards/LilygoTdeck/Source/hal/TdeckDisplay.cpp +++ b/Boards/LilygoTdeck/Source/hal/TdeckDisplay.cpp @@ -117,12 +117,12 @@ bool TdeckDisplay::start() { } if (esp_lcd_panel_swap_xy(panelHandle, true) != ESP_OK) { - TT_LOG_E(TAG, "Failed to init panel"); + TT_LOG_E(TAG, "Failed to swap XY "); return false; } if (esp_lcd_panel_mirror(panelHandle, true, false) != ESP_OK) { - TT_LOG_E(TAG, "Failed to init panel"); + TT_LOG_E(TAG, "Failed to mirror panel"); return false; } diff --git a/Boards/M5stackCore2/CMakeLists.txt b/Boards/M5stackCore2/CMakeLists.txt index bcae790d..6746e3cb 100644 --- a/Boards/M5stackCore2/CMakeLists.txt +++ b/Boards/M5stackCore2/CMakeLists.txt @@ -1,5 +1,5 @@ idf_component_register( - SRC_DIRS "Source" + SRC_DIRS "Source" "Source/hal" "Source/ft6x36" INCLUDE_DIRS "Source" - REQUIRES Tactility M5stackShared M5Unified + REQUIRES Tactility esp_lvgl_port esp_lcd esp_lcd_ili9341 driver vfs fatfs M5Unified ) diff --git a/Boards/M5stackCore2/Source/InitBoot.cpp b/Boards/M5stackCore2/Source/InitBoot.cpp new file mode 100644 index 00000000..9a7a8285 --- /dev/null +++ b/Boards/M5stackCore2/Source/InitBoot.cpp @@ -0,0 +1,68 @@ +#include +#include +#include +#include "Log.h" +#include "hal/Core2DisplayConstants.h" +#include "M5Unified/src/utility/AXP192_Class.hpp" + +#define TAG "core2" + +#define CORE2_SPI2_PIN_SCLK GPIO_NUM_18 +#define CORE2_SPI2_PIN_MOSI GPIO_NUM_23 +#define CORE2_SPI2_PIN_MISO GPIO_NUM_38 + +m5::I2C_Class i2c; +m5::AXP192_Class axpDevice(0x34, 400000, &i2c); + +static bool initSpi2() { + TT_LOG_I(TAG, LOG_MESSAGE_SPI_INIT_START_FMT, SPI2_HOST); + const spi_bus_config_t bus_config = { + .mosi_io_num = CORE2_SPI2_PIN_MOSI, + .miso_io_num = CORE2_SPI2_PIN_MISO, + .sclk_io_num = CORE2_SPI2_PIN_SCLK, + .data2_io_num = GPIO_NUM_NC, + .data3_io_num = GPIO_NUM_NC, + .data4_io_num = GPIO_NUM_NC, + .data5_io_num = GPIO_NUM_NC, + .data6_io_num = GPIO_NUM_NC, + .data7_io_num = GPIO_NUM_NC, + .max_transfer_sz = CORE2_LCD_DRAW_BUFFER_SIZE, + .flags = 0, + .isr_cpu_id = INTR_CPU_ID_AUTO, + .intr_flags = 0 + }; + + if (spi_bus_initialize(SPI2_HOST, &bus_config, SPI_DMA_CH_AUTO) != ESP_OK) { + TT_LOG_E(TAG, LOG_MESSAGE_SPI_INIT_FAILED_FMT, SPI2_HOST); + return false; + } + + return true; +} + +bool initAxp() { + if (!i2c.begin(I2C_NUM_0, GPIO_NUM_21, GPIO_NUM_22)) { + TT_LOG_E(TAG, "I2C init failed"); + return false; + } + + if (!axpDevice.begin()) { + TT_LOG_E(TAG, "AXP init failed"); + return false; + } + + axpDevice.setLDO2(3300); // LCD + SD peripheral power supply + axpDevice.setLDO3(0); // VIB_MOTOR STOP + axpDevice.setGPIO2(false); // SPEAKER STOP + axpDevice.writeRegister8(0x9A, 255); // PWM 255 (LED OFF) + axpDevice.writeRegister8(0x92, 0x02); // GPIO1 PWM + axpDevice.setChargeCurrent(390); // Core2 battery = 390mAh + axpDevice.setDCDC3(3300); + + return true; +} + +bool initBoot() { + TT_LOG_I(TAG, "initBoot"); + return initAxp() && initSpi2(); +} \ No newline at end of file diff --git a/Boards/M5stackCore2/Source/InitBoot.h b/Boards/M5stackCore2/Source/InitBoot.h new file mode 100644 index 00000000..f3e5bf89 --- /dev/null +++ b/Boards/M5stackCore2/Source/InitBoot.h @@ -0,0 +1,3 @@ +#pragma once + +bool initBoot(); diff --git a/Boards/M5stackCore2/Source/InitLvgl.cpp b/Boards/M5stackCore2/Source/InitLvgl.cpp new file mode 100644 index 00000000..faa92dfe --- /dev/null +++ b/Boards/M5stackCore2/Source/InitLvgl.cpp @@ -0,0 +1,31 @@ +#include "Log.h" +#include "Thread.h" +#include "lvgl/LvglSync.h" +#include "esp_lvgl_port.h" + +#define TAG "core2" + +// LVGL +// The minimum task stack seems to be about 3500, but that crashes the wifi app in some scenarios +// At 4000, it crashes when the fps renderer is available +#define CORE2_LVGL_TASK_STACK_DEPTH 8192 + +bool initLvgl() { + const lvgl_port_cfg_t lvgl_cfg = { + .task_priority = tt::THREAD_PRIORITY_RENDER, + .task_stack = CORE2_LVGL_TASK_STACK_DEPTH, + .task_affinity = -1, // core pinning + .task_max_sleep_ms = 500, + .timer_period_ms = 5 + }; + + TT_LOG_D(TAG, "LVGL port init"); + if (lvgl_port_init(&lvgl_cfg) != ESP_OK) { + TT_LOG_E(TAG, "LVGL port init failed"); + return false; + } + + tt::lvgl::syncSet(&lvgl_port_lock, &lvgl_port_unlock); + + return true; +} diff --git a/Boards/M5stackCore2/Source/InitLvgl.h b/Boards/M5stackCore2/Source/InitLvgl.h new file mode 100644 index 00000000..e57bd9bd --- /dev/null +++ b/Boards/M5stackCore2/Source/InitLvgl.h @@ -0,0 +1,3 @@ +#pragma once + +bool initLvgl(); diff --git a/Boards/M5stackCore2/Source/M5stackCore2.cpp b/Boards/M5stackCore2/Source/M5stackCore2.cpp index 686bbba9..a8b4608d 100644 --- a/Boards/M5stackCore2/Source/M5stackCore2.cpp +++ b/Boards/M5stackCore2/Source/M5stackCore2.cpp @@ -1,18 +1,22 @@ #include "M5stackCore2.h" -#include "M5stackShared.h" +#include "InitBoot.h" +#include "InitLvgl.h" +#include "hal/Core2Display.h" +#include "hal/Core2SdCard.h" +#include "hal/Core2Power.h" extern const tt::hal::Configuration m5stack_core2 = { - .initBoot = m5stack_bootstrap, - .initLvgl = m5stack_lvgl_init, + .initBoot = initBoot, + .initLvgl = initLvgl, .createDisplay = createDisplay, - .sdcard = createM5SdCard(), - .power = m5stack_get_power, + .sdcard = createSdCard(), + .power = createPower, .i2c = { tt::hal::i2c::Configuration { .name = "Internal", .port = I2C_NUM_0, - .initMode = tt::hal::i2c::InitByExternal, - .canReinit = false, + .initMode = tt::hal::i2c::InitByTactility, + .canReinit = false, // Might be set to try after trying out what it does AXP and screen .hasMutableConfiguration = false, .config = (i2c_config_t) { .mode = I2C_MODE_MASTER, @@ -29,7 +33,7 @@ extern const tt::hal::Configuration m5stack_core2 = { tt::hal::i2c::Configuration { .name = "External", // (Grove) .port = I2C_NUM_1, - .initMode = tt::hal::i2c::InitByExternal, + .initMode = tt::hal::i2c::InitByTactility, .canReinit = true, .hasMutableConfiguration = true, .config = (i2c_config_t) { diff --git a/Boards/M5stackCore2/Source/ft6x36/FT6X36.cpp b/Boards/M5stackCore2/Source/ft6x36/FT6X36.cpp new file mode 100644 index 00000000..48ba32fe --- /dev/null +++ b/Boards/M5stackCore2/Source/ft6x36/FT6X36.cpp @@ -0,0 +1,378 @@ +#include "FT6X36.h" + +#include "freertos/FreeRTOS.h" + +#define CONFIG_FT6X36_DEBUG false + +FT6X36 *FT6X36::_instance = nullptr; +static const char *TAG = "i2c-touch"; + +//Handle indicating I2C is ready to read the touch +SemaphoreHandle_t TouchSemaphore = xSemaphoreCreateBinary(); + +FT6X36::FT6X36(i2c_port_t port, gpio_num_t interruptPin) +{ + _instance = this; + _port = port; + _intPin = interruptPin; +} + +// Destructor should detach interrupt to the pin +FT6X36::~FT6X36() +{ + if (_intPin >= 0) + gpio_isr_handler_remove((gpio_num_t)_intPin); +} + +bool FT6X36::begin(uint8_t threshold, uint16_t width, uint16_t height) +{ + _touch_width = width; + _touch_height = height; + if (width == 0 || height ==0) { + ESP_LOGE(TAG,"begin(uint8_t threshold, uint16_t width, uint16_t height) did not receive the width / height so touch cannot be rotation aware"); + } + + uint8_t data_panel_id; + readRegister8(FT6X36_REG_PANEL_ID, &data_panel_id); + + if (data_panel_id != FT6X36_VENDID) { + ESP_LOGE(TAG,"FT6X36_VENDID does not match. Received:0x%x Expected:0x%x\n",data_panel_id,FT6X36_VENDID); + return false; + } + ESP_LOGI(TAG, "\tDevice ID: 0x%02x", data_panel_id); + + uint8_t chip_id; + readRegister8(FT6X36_REG_CHIPID, &chip_id); + if (chip_id != FT6206_CHIPID && chip_id != FT6236_CHIPID && chip_id != FT6336_CHIPID) { + ESP_LOGE(TAG,"FT6206_CHIPID does not match. Received:0x%x\n",chip_id); + return false; + } + ESP_LOGI(TAG, "\tFound touch controller with Chip ID: 0x%02x", chip_id); + + if (_intPin >= 0) + { + // INT pin triggers the callback function on the Falling edge of the GPIO + gpio_config_t io_conf; + io_conf.intr_type = GPIO_INTR_NEGEDGE; // GPIO_INTR_NEGEDGE repeats always interrupt + io_conf.pin_bit_mask = 1ULL<<_intPin; + io_conf.mode = GPIO_MODE_INPUT; + io_conf.pull_down_en = (gpio_pulldown_t) 0; // disable pull-down mode + io_conf.pull_up_en = (gpio_pullup_t) 1; // pull-up mode + gpio_config(&io_conf); + + esp_err_t isr_service = gpio_install_isr_service(0); + printf("ISR trigger install response: 0x%x %s\n", isr_service, (isr_service==0)?"ESP_OK":""); + gpio_isr_handler_add((gpio_num_t)_intPin, isr, (void*) 1); + } + + writeRegister8(FT6X36_REG_DEVICE_MODE, 0x00); + writeRegister8(FT6X36_REG_THRESHHOLD, threshold); + writeRegister8(FT6X36_REG_TOUCHRATE_ACTIVE, 0x0E); + return true; +} + +void FT6X36::registerTouchHandler(void (*fn)(TPoint point, TEvent e)) +{ + _touchHandler = fn; + if (CONFIG_FT6X36_DEBUG) printf("Touch handler function registered\n"); +} + +uint8_t FT6X36::touched() +{ + uint8_t data_buf; + esp_err_t ret = readRegister8(FT6X36_REG_NUM_TOUCHES, &data_buf); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Error reading from device: %s", esp_err_to_name(ret)); + } + + if (data_buf > 2) + { + data_buf = 0; + } + + return data_buf; +} + +void FT6X36::loop() +{ + processTouch(); +} + +void IRAM_ATTR FT6X36::isr(void* arg) +{ + /* Un-block the interrupt processing task now */ + xSemaphoreGive(TouchSemaphore); + //xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL); +} + +void FT6X36::processTouch() +{ + /* Task move to Block state to wait for interrupt event */ + if (_intPin >= 0) + { + if (xSemaphoreTake(TouchSemaphore, portMAX_DELAY) == false) return; + } + + readData(); + uint8_t n = 0; + TRawEvent event = (TRawEvent)_touchEvent[n]; + TPoint point{_touchX[n], _touchY[n]}; + + switch (event) { + + case TRawEvent::PressDown: + _points[0] = point; + _dragMode = false; + // Note: Is in microseconds. Ref https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/esp_timer.html + _touchStartTime = esp_timer_get_time()/1000; + fireEvent(point, TEvent::TouchStart); + break; + + case TRawEvent::Contact: + // Dragging makes no sense IMHO. Since the X & Y are not getting updated while dragging + // Dragging && _points[0].aboutEqual(point) - Not used IDEA 2: && (lastEvent == 2) + if (!_dragMode && + (abs(lastX-_touchX[n]) <= maxDeviation || abs(lastY-_touchY[n])<=maxDeviation) && + esp_timer_get_time()/1000 - _touchStartTime > 300) { + _dragMode = true; + fireEvent(point, TEvent::DragStart); + #if defined(CONFIG_FT6X36_DEBUG_EVENTS) && CONFIG_FT6X36_DEBUG_EVENTS==1 + printf("EV: DragStart\n"); + #endif + + } else if (_dragMode) { + fireEvent(point, TEvent::DragMove); + #if defined(CONFIG_FT6X36_DEBUG_EVENTS) && CONFIG_FT6X36_DEBUG_EVENTS==1 + printf("EV: DragMove\n"); + #endif + } + fireEvent(point, TEvent::TouchMove); + + // For me the _touchStartTime shouold be set in both PressDown & Contact events, but after Drag detection + _touchStartTime = esp_timer_get_time()/1000; + break; + + case TRawEvent::LiftUp: + + _points[9] = point; + _touchEndTime = esp_timer_get_time()/1000; + + //printf("TIMEDIFF: %lu End: %lu\n", _touchEndTime - _touchStartTime, _touchEndTime); + + fireEvent(point, TEvent::TouchEnd); + if (_dragMode) { + fireEvent(point, TEvent::DragEnd); + #if defined(CONFIG_FT6X36_DEBUG_EVENTS) && CONFIG_FT6X36_DEBUG_EVENTS==1 + printf("EV: DragEnd\n"); + #endif + _dragMode = false; + } + + if ( _touchEndTime - _touchStartTime <= 900) { + // Do not get why this: _points[0].aboutEqual(point) (Original library) + fireEvent(point, TEvent::Tap); + _points[0] = {0, 0}; + _touchStartTime = 0; + + #if defined(CONFIG_FT6X36_DEBUG_EVENTS) && CONFIG_FT6X36_DEBUG_EVENTS==1 + printf("EV: Tap\n"); + #endif + _dragMode = false; + } + + break; + + case TRawEvent::NoEvent: + #if defined(CONFIG_FT6X36_DEBUG_EVENTS) && CONFIG_FT6X36_DEBUG_EVENTS==1 + printf("EV: NoEvent\n"); + #endif + break; + } + // Store lastEvent + lastEvent = (int) event; + lastX = _touchX[0]; + lastY = _touchY[0]; +} + +void FT6X36::poll(TPoint * point, TEvent * e) +{ + readData(); + // TPoint point{_touchX[0], _touchY[0]}; + TRawEvent event = (TRawEvent)_touchEvent[0]; + + if (point != NULL) + { + point->x = _touchX[0]; + point->y = _touchY[0]; + } + if (e != NULL) + { + switch (event) + { + case TRawEvent::PressDown: + *e = TEvent::TouchStart; + break; + case TRawEvent::Contact: + *e = TEvent::TouchMove; + break; + case TRawEvent::LiftUp: + default: + *e = TEvent::TouchEnd; + break; + } + } +} + +uint8_t FT6X36::read8(uint8_t regName) { + uint8_t buf; + readRegister8(regName, &buf); + return buf; +} + +#define data_size 16 // Discarding last 2: 0x0E & 0x0F as not relevant +bool FT6X36::readData(void) +{ + esp_err_t ret; + uint8_t data[data_size]; + uint8_t touch_pnt_cnt; // Number of detected touch points + readRegister8(FT6X36_REG_NUM_TOUCHES, &touch_pnt_cnt); + + // Read data + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, (FT6X36_ADDR<<1), ACK_CHECK_EN); + i2c_master_write_byte(cmd, 0, ACK_CHECK_EN); + i2c_master_stop(cmd); + ret = i2c_master_cmd_begin(_port, cmd, 1000 / portTICK_PERIOD_MS); + i2c_cmd_link_delete(cmd); + if (ret != ESP_OK) { + return ret; + } + + cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, (FT6X36_ADDR<<1)|1, ACK_CHECK_EN); + i2c_master_read(cmd, data, data_size, I2C_MASTER_LAST_NACK); + i2c_master_stop(cmd); + ret = i2c_master_cmd_begin(_port, cmd, 1000 / portTICK_PERIOD_MS); + i2c_cmd_link_delete(cmd); + + if (CONFIG_FT6X36_DEBUG) { + //printf("REGISTERS:\n"); + for (int16_t i = 0; i < data_size; i++) + { + printf("%x:%x ", i, data[i]); + } + printf("\n"); + } + + const uint8_t addrShift = 6; + + // READ X, Y and Touch events (X 2) + for (uint8_t i = 0; i < 2; i++) + { + _touchX[i] = data[FT6X36_REG_P1_XH + i * addrShift] & 0x0F; + _touchX[i] <<= 8; + _touchX[i] |= data[FT6X36_REG_P1_XL + i * addrShift]; + _touchY[i] = data[FT6X36_REG_P1_YH + i * addrShift] & 0x0F; + _touchY[i] <<= 8; + _touchY[i] |= data[FT6X36_REG_P1_YL + i * addrShift]; + _touchEvent[i] = data[FT6X36_REG_P1_XH + i * addrShift] >> 6; + } + + // Make _touchX[idx] and _touchY[idx] rotation aware + switch (_rotation) + { + case 1: + swap(_touchX[0], _touchY[0]); + swap(_touchX[1], _touchY[1]); + _touchY[0] = _touch_width - _touchY[0] -1; + _touchY[1] = _touch_width - _touchY[1] -1; + break; + case 2: + _touchX[0] = _touch_width - _touchX[0] - 1; + _touchX[1] = _touch_width - _touchX[1] - 1; + _touchY[0] = _touch_height - _touchY[0] - 1; + _touchY[1] = _touch_height - _touchY[1] - 1; + break; + case 3: + swap(_touchX[0], _touchY[0]); + swap(_touchX[1], _touchY[1]); + _touchX[0] = _touch_height - _touchX[0] - 1; + _touchX[1] = _touch_height - _touchX[1] - 1; + break; + } + if (CONFIG_FT6X36_DEBUG) { + printf("X0:%d Y0:%d EVENT:%d\n", _touchX[0], _touchY[0], _touchEvent[0]); + //printf("X1:%d Y1:%d EVENT:%d\n", _touchX[1], _touchY[1], _touchEvent[1]); + } + return true; +} + +void FT6X36::writeRegister8(uint8_t reg, uint8_t value) +{ + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, FT6X36_ADDR << 1 | I2C_MASTER_WRITE, ACK_CHECK_EN); + i2c_master_write_byte(cmd, reg , ACK_CHECK_EN); + i2c_master_write_byte(cmd, value , ACK_CHECK_EN); + i2c_master_stop(cmd); + i2c_master_cmd_begin(_port, cmd, 1000 / portTICK_PERIOD_MS); + i2c_cmd_link_delete(cmd); +} + +uint8_t FT6X36::readRegister8(uint8_t reg, uint8_t *data_buf) +{ + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, FT6X36_ADDR << 1 | I2C_MASTER_WRITE, ACK_CHECK_EN); + i2c_master_write_byte(cmd, reg, I2C_MASTER_ACK); + // Research: Why it's started a 2nd time here + i2c_master_start(cmd); + i2c_master_write_byte(cmd, (FT6X36_ADDR << 1) | I2C_MASTER_READ, true); + + i2c_master_read_byte(cmd, data_buf, I2C_MASTER_NACK); + i2c_master_stop(cmd); + esp_err_t ret = i2c_master_cmd_begin(_port, cmd, 1000 / portTICK_PERIOD_MS); + i2c_cmd_link_delete(cmd); + + + //FT6X36_REG_GESTURE_ID. Check if it can be read! +#if defined(FT6X36_DEBUG) && FT6X36_DEBUG==1 + printf("REG 0x%x: 0x%x\n",reg,ret); +#endif + + return ret; +} + +void FT6X36::fireEvent(TPoint point, TEvent e) +{ + if (_touchHandler) + _touchHandler(point, e); +} + +void FT6X36::debugInfo() +{ + printf(" TH_DIFF: %d CTRL: %d\n", read8(FT6X36_REG_FILTER_COEF), read8(FT6X36_REG_CTRL)); + printf(" TIMEENTERMONITOR: %d PERIODACTIVE: %d\n", read8(FT6X36_REG_TIME_ENTER_MONITOR), read8(FT6X36_REG_TOUCHRATE_ACTIVE)); + printf(" PERIODMONITOR: %d RADIAN_VALUE: %d\n", read8(FT6X36_REG_TOUCHRATE_MONITOR), read8(FT6X36_REG_RADIAN_VALUE)); + printf(" OFFSET_LEFT_RIGHT: %d OFFSET_UP_DOWN: %d\n", read8(FT6X36_REG_OFFSET_LEFT_RIGHT), read8(FT6X36_REG_OFFSET_UP_DOWN)); + printf("DISTANCE_LEFT_RIGHT: %d DISTANCE_UP_DOWN: %d\n", read8(FT6X36_REG_DISTANCE_LEFT_RIGHT), read8(FT6X36_REG_DISTANCE_UP_DOWN)); + printf(" DISTANCE_ZOOM: %d CIPHER: %d\n", read8(FT6X36_REG_DISTANCE_ZOOM), read8(FT6X36_REG_CHIPID)); + printf(" G_MODE: %d PWR_MODE: %d\n", read8(FT6X36_REG_INTERRUPT_MODE), read8(FT6X36_REG_POWER_MODE)); + printf(" FIRMID: %d FOCALTECH_ID: %d STATE: %d\n", read8(FT6X36_REG_FIRMWARE_VERSION), read8(FT6X36_REG_PANEL_ID), read8(FT6X36_REG_STATE)); +} + +void FT6X36::setRotation(uint8_t rotation) { + _rotation = rotation; +} + +void FT6X36::setTouchWidth(uint16_t width) { + printf("touch w:%d\n",width); + _touch_width = width; +} + +void FT6X36::setTouchHeight(uint16_t height) { + printf("touch h:%d\n",height); + _touch_height = height; +} diff --git a/Boards/M5stackCore2/Source/ft6x36/FT6X36.h b/Boards/M5stackCore2/Source/ft6x36/FT6X36.h new file mode 100644 index 00000000..3f57761b --- /dev/null +++ b/Boards/M5stackCore2/Source/ft6x36/FT6X36.h @@ -0,0 +1,184 @@ +#include +#include +#include "driver/gpio.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include +#include "esp_log.h" +#include "driver/i2c.h" +#include "sdkconfig.h" +#include + +#ifndef ft6x36_h +#define ft6x36_h +// I2C Constants +#define I2C_MASTER_TX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */ +#define I2C_MASTER_RX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */ + +#define ACK_CHECK_EN 0x1 /*!< I2C master will check ack from slave*/ +#define ACK_CHECK_DIS 0x0 /*!< I2C master will not check ack from slave */ +#define ACK_VAL 0x0 /*!< I2C ack value */ +#define NACK_VAL 0x1 /*!< I2C nack value */ + +//SemaphoreHandle_t print_mux = NULL; + +#define FT6X36_ADDR 0x38 + +#define FT6X36_REG_DEVICE_MODE 0x00 +#define FT6X36_REG_GESTURE_ID 0x01 +#define FT6X36_REG_NUM_TOUCHES 0x02 +#define FT6X36_REG_P1_XH 0x03 +#define FT6X36_REG_P1_XL 0x04 +#define FT6X36_REG_P1_YH 0x05 +#define FT6X36_REG_P1_YL 0x06 +#define FT6X36_REG_P1_WEIGHT 0x07 +#define FT6X36_REG_P1_MISC 0x08 +#define FT6X36_REG_P2_XH 0x09 +#define FT6X36_REG_P2_XL 0x0A +#define FT6X36_REG_P2_YH 0x0B +#define FT6X36_REG_P2_YL 0x0C +#define FT6X36_REG_P2_WEIGHT 0x0D +#define FT6X36_REG_P2_MISC 0x0E +#define FT6X36_REG_THRESHHOLD 0x80 +#define FT6X36_REG_FILTER_COEF 0x85 +#define FT6X36_REG_CTRL 0x86 +#define FT6X36_REG_TIME_ENTER_MONITOR 0x87 +#define FT6X36_REG_TOUCHRATE_ACTIVE 0x88 +#define FT6X36_REG_TOUCHRATE_MONITOR 0x89 // value in ms +#define FT6X36_REG_RADIAN_VALUE 0x91 +#define FT6X36_REG_OFFSET_LEFT_RIGHT 0x92 +#define FT6X36_REG_OFFSET_UP_DOWN 0x93 +#define FT6X36_REG_DISTANCE_LEFT_RIGHT 0x94 +#define FT6X36_REG_DISTANCE_UP_DOWN 0x95 +#define FT6X36_REG_DISTANCE_ZOOM 0x96 +#define FT6X36_REG_LIB_VERSION_H 0xA1 +#define FT6X36_REG_LIB_VERSION_L 0xA2 +#define FT6X36_REG_CHIPID 0xA3 +#define FT6X36_REG_INTERRUPT_MODE 0xA4 +#define FT6X36_REG_POWER_MODE 0xA5 +#define FT6X36_REG_FIRMWARE_VERSION 0xA6 +#define FT6X36_REG_PANEL_ID 0xA8 +#define FT6X36_REG_STATE 0xBC + +#define FT6X36_PMODE_ACTIVE 0x00 +#define FT6X36_PMODE_MONITOR 0x01 +#define FT6X36_PMODE_STANDBY 0x02 +#define FT6X36_PMODE_HIBERNATE 0x03 + +/* Possible values returned by FT6X36_GEST_ID_REG */ +#define FT6X36_GEST_ID_NO_GESTURE 0x00 +#define FT6X36_GEST_ID_MOVE_UP 0x10 +#define FT6X36_GEST_ID_MOVE_RIGHT 0x14 +#define FT6X36_GEST_ID_MOVE_DOWN 0x18 +#define FT6X36_GEST_ID_MOVE_LEFT 0x1C +#define FT6X36_GEST_ID_ZOOM_IN 0x48 +#define FT6X36_GEST_ID_ZOOM_OUT 0x49 + +#define FT6X36_VENDID 0x11 +#define FT6206_CHIPID 0x06 +#define FT6236_CHIPID 0x36 +#define FT6336_CHIPID 0x64 + +#define FT6X36_DEFAULT_THRESHOLD 22 + +// From: https://github.com/lvgl/lv_port_esp32/blob/master/components/lvgl_esp32_drivers/lvgl_touch/ft6x36.h +#define FT6X36_MSB_MASK 0x0F +#define FT6X36_LSB_MASK 0xFF + +enum class TRawEvent +{ + PressDown, + LiftUp, + Contact, + NoEvent +}; + +enum class TEvent +{ + None, + TouchStart, + TouchMove, + TouchEnd, + Tap, + DragStart, + DragMove, + DragEnd +}; + +struct TPoint +{ + uint16_t x; + uint16_t y; + /** + * This is being used in the original library but I'm not using it in this implementation + */ + bool aboutEqual(const TPoint point) + { + return abs(x - point.x) <= 5 && abs(y - point.y) <= 5; + } +}; + + + +class FT6X36 +{ + static void IRAM_ATTR isr(void* arg); +public: + // TwoWire * wire will be replaced by ESP-IDF https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/i2c.html + FT6X36(i2c_port_t = I2C_NUM_0, gpio_num_t interruptPin = GPIO_NUM_NC); + ~FT6X36(); + bool begin(uint8_t threshold = FT6X36_DEFAULT_THRESHOLD, uint16_t width = 0, uint16_t height = 0); + void registerTouchHandler(void(*fn)(TPoint point, TEvent e)); + uint8_t touched(); + void loop(); + void processTouch(); + void debugInfo(); + void poll(TPoint * point, TEvent * event); + // Helper functions to make the touch display aware + void setRotation(uint8_t rotation); + void setTouchWidth(uint16_t width); + void setTouchHeight(uint16_t height); + // Pending implementation. How much x->touch y↓touch is placed (In case is smaller than display) + void setXoffset(uint16_t x_offset); + void setYoffset(uint16_t y_offset); + // Smart template from EPD to swap x,y: + template static inline void + swap(T& a, T& b) + { + T t = a; + a = b; + b = t; + } + void(*_touchHandler)(TPoint point, TEvent e) = nullptr; + + bool readData(void); +private: + void writeRegister8(uint8_t reg, uint8_t val); + uint8_t readRegister8(uint8_t reg, uint8_t *data_buf); + void fireEvent(TPoint point, TEvent e); + uint8_t read8(uint8_t regName); + static FT6X36 * _instance; + + i2c_port_t _port; + int8_t _intPin; + + // Make touch rotation aware: + uint8_t _rotation = 0; + uint16_t _touch_width = 0; + uint16_t _touch_height = 0; + + uint8_t _touches; + uint16_t _touchX[2], _touchY[2], _touchEvent[2]; + TPoint _points[10]; + uint8_t _pointIdx = 0; + unsigned long _touchStartTime = 0; + unsigned long _touchEndTime = 0; + uint8_t lastEvent = 3; // No event + uint16_t lastX = 0; + uint16_t lastY = 0; + bool _dragMode = false; + const uint8_t maxDeviation = 5; +}; + +#endif \ No newline at end of file diff --git a/Boards/M5stackCore2/Source/ft6x36/LICENSE b/Boards/M5stackCore2/Source/ft6x36/LICENSE new file mode 100644 index 00000000..607a0afb --- /dev/null +++ b/Boards/M5stackCore2/Source/ft6x36/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 strange_v + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Boards/M5stackCore2/Source/ft6x36/README.md b/Boards/M5stackCore2/Source/ft6x36/README.md new file mode 100644 index 00000000..a9146b24 --- /dev/null +++ b/Boards/M5stackCore2/Source/ft6x36/README.md @@ -0,0 +1,8 @@ +This project is an adaption of the code at https://github.com/martinberlin/FT6X36-IDF which is an adaptation of https://github.com/strange-v/FT6X36 +The original license is an MIT license and is included in this directory. + +Changes: +- Remove Kconfig-based configuratio +- Removed I2C init code +- Allow for passing a different I2C port + \ No newline at end of file diff --git a/Boards/M5stackCore2/Source/hal/Core2Display.cpp b/Boards/M5stackCore2/Source/hal/Core2Display.cpp new file mode 100644 index 00000000..ea3efe69 --- /dev/null +++ b/Boards/M5stackCore2/Source/hal/Core2Display.cpp @@ -0,0 +1,164 @@ +#include "Core2Display.h" +#include "Core2DisplayConstants.h" +#include "Log.h" + +#include +#include + +#include "driver/gpio.h" +#include "esp_err.h" +#include "esp_lcd_ili9341.h" +#include "esp_lcd_panel_ops.h" +#include "esp_lvgl_port.h" +#include "Core2Touch.h" + +#define TAG "yellow_display" + +bool Core2Display::start() { + TT_LOG_I(TAG, "Starting"); + + const esp_lcd_panel_io_spi_config_t panel_io_config = ILI9341_PANEL_IO_SPI_CONFIG( + CORE2_LCD_PIN_CS, + CORE2_LCD_PIN_DC, + nullptr, + nullptr + ); + + if (esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)CORE2_LCD_SPI_HOST, &panel_io_config, &ioHandle) != ESP_OK) { + TT_LOG_E(TAG, "Failed to create panel"); + return false; + } + + const esp_lcd_panel_dev_config_t panel_config = { + .reset_gpio_num = GPIO_NUM_NC, + .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_BGR, + .data_endian = LCD_RGB_DATA_ENDIAN_LITTLE, + .bits_per_pixel = CORE2_LCD_BITS_PER_PIXEL, + .flags = { + .reset_active_high = false + }, + .vendor_config = nullptr + }; + + if (esp_lcd_new_panel_ili9341(ioHandle, &panel_config, &panelHandle) != ESP_OK) { + TT_LOG_E(TAG, "Failed to create ili9341"); + return false; + } + + if (esp_lcd_panel_reset(panelHandle) != ESP_OK) { + TT_LOG_E(TAG, "Failed to reset panel"); + return false; + } + + if (esp_lcd_panel_init(panelHandle) != ESP_OK) { + TT_LOG_E(TAG, "Failed to init panel"); + return false; + } + + if (esp_lcd_panel_mirror(panelHandle, false, false) != ESP_OK) { + TT_LOG_E(TAG, "Failed to set panel to mirror"); + return false; + } + + if (esp_lcd_panel_invert_color(panelHandle, true) != ESP_OK) { + TT_LOG_E(TAG, "Failed to set panel to invert"); + return false; + } + + if (esp_lcd_panel_disp_on_off(panelHandle, true) != ESP_OK) { + TT_LOG_E(TAG, "Failed to turn display on"); + return false; + } + + const lvgl_port_display_cfg_t disp_cfg = { + .io_handle = ioHandle, + .panel_handle = panelHandle, + .control_handle = nullptr, + .buffer_size = CORE2_LCD_DRAW_BUFFER_SIZE, + .double_buffer = true, + .trans_size = 0, + .hres = CORE2_LCD_HORIZONTAL_RESOLUTION, + .vres = CORE2_LCD_VERTICAL_RESOLUTION, + .monochrome = false, + .rotation = { + .swap_xy = false, + .mirror_x = false, + .mirror_y = false, + }, + .color_format = LV_COLOR_FORMAT_RGB565, + .flags = { + .buff_dma = false, + .buff_spiram = true, + .sw_rotate = false, + .swap_bytes = true, + .full_refresh = false, + .direct_mode = false + } + }; + + displayHandle = lvgl_port_add_disp(&disp_cfg); + TT_LOG_I(TAG, "Finished"); + return displayHandle != nullptr; +} + +bool Core2Display::stop() { + tt_assert(displayHandle != nullptr); + + lvgl_port_remove_disp(displayHandle); + + if (esp_lcd_panel_del(panelHandle) != ESP_OK) { + return false; + } + + if (esp_lcd_panel_io_del(ioHandle) != ESP_OK) { + return false; + } + + displayHandle = nullptr; + return true; +} + +/** + * Note: + * The datasheet implies this should work, but it doesn't: + * https://www.digikey.com/htmldatasheets/production/1640716/0/0/1/ILI9341-Datasheet.pdf + * + * This repo claims it only has 1 curve: + * https://github.com/brucemack/hello-ili9341 + * + * I'm leaving it in as I'm not sure if it's just my hardware that's problematic. + */ +void Core2Display::setGammaCurve(uint8_t index) { + uint8_t gamma_curve; + switch (index) { + case 0: + gamma_curve = 0x01; + break; + case 1: + gamma_curve = 0x04; + break; + case 2: + gamma_curve = 0x02; + break; + case 3: + gamma_curve = 0x08; + break; + default: + return; + } + const uint8_t param[] = { + gamma_curve + }; + + if (esp_lcd_panel_io_tx_param(ioHandle , LCD_CMD_GAMSET, param, 1) != ESP_OK) { + TT_LOG_E(TAG, "Failed to set gamma"); + } +} + +tt::hal::Touch* _Nullable Core2Display::createTouch() { + return static_cast(new Core2Touch()); +} + +tt::hal::Display* createDisplay() { + return static_cast(new Core2Display()); +} diff --git a/Boards/M5stackCore2/Source/hal/Core2Display.h b/Boards/M5stackCore2/Source/hal/Core2Display.h new file mode 100644 index 00000000..272a3681 --- /dev/null +++ b/Boards/M5stackCore2/Source/hal/Core2Display.h @@ -0,0 +1,34 @@ +#pragma once + +#include "lvgl.h" +#include "hal/Display.h" + +#include + +extern lv_disp_t* displayHandle; + +class Core2Display : public tt::hal::Display { + +private: + + esp_lcd_panel_io_handle_t ioHandle = nullptr; + esp_lcd_panel_handle_t panelHandle = nullptr; + lv_display_t* displayHandle = nullptr; + +public: + + bool start() override; + + bool stop() override; + + tt::hal::Touch* _Nullable createTouch() override; + + bool supportsBacklightDuty() const override { return false; } + + void setGammaCurve(uint8_t index) override; + uint8_t getGammaCurveCount() const override { return 4; }; + + lv_display_t* _Nullable getLvglDisplay() const override { return displayHandle; } +}; + +tt::hal::Display* createDisplay(); diff --git a/Boards/M5stackCore2/Source/hal/Core2DisplayConstants.h b/Boards/M5stackCore2/Source/hal/Core2DisplayConstants.h new file mode 100644 index 00000000..fa65c5a1 --- /dev/null +++ b/Boards/M5stackCore2/Source/hal/Core2DisplayConstants.h @@ -0,0 +1,11 @@ +#pragma once + +// Display +#define CORE2_LCD_SPI_HOST SPI2_HOST +#define CORE2_LCD_PIN_CS GPIO_NUM_5 +#define CORE2_LCD_PIN_DC GPIO_NUM_15 +#define CORE2_LCD_HORIZONTAL_RESOLUTION 320 +#define CORE2_LCD_VERTICAL_RESOLUTION 240 +#define CORE2_LCD_BITS_PER_PIXEL 16 +#define CORE2_LCD_DRAW_BUFFER_HEIGHT (CORE2_LCD_VERTICAL_RESOLUTION / 10) +#define CORE2_LCD_DRAW_BUFFER_SIZE (CORE2_LCD_HORIZONTAL_RESOLUTION * CORE2_LCD_DRAW_BUFFER_HEIGHT * (CORE2_LCD_BITS_PER_PIXEL / 8)) diff --git a/Boards/M5stackCore2/Source/hal/Core2Power.cpp b/Boards/M5stackCore2/Source/hal/Core2Power.cpp new file mode 100644 index 00000000..5e84bb92 --- /dev/null +++ b/Boards/M5stackCore2/Source/hal/Core2Power.cpp @@ -0,0 +1,63 @@ +#include "Core2Power.h" + +#include "utility/AXP192_Class.hpp" +#include "TactilityCore.h" + +#define TAG "core2_power" + +extern m5::AXP192_Class axpDevice; + +bool Core2Power::supportsMetric(MetricType type) const { + switch (type) { + case BATTERY_VOLTAGE: + case CHARGE_LEVEL: + case IS_CHARGING: + return true; + case CURRENT: + return false; + } + + return false; // Safety guard for when new enum values are introduced +} + +bool Core2Power::getMetric(Power::MetricType type, Power::MetricData& data) { + switch (type) { + case BATTERY_VOLTAGE: + data.valueAsUint32 = (uint32_t)TT_MAX((axpDevice.getBatteryVoltage() * 1000.f), 0.0f); + return true; + case CHARGE_LEVEL: { + auto level = axpDevice.getBatteryLevel(); + if (level > 0) { + data.valueAsUint8 = (uint8_t)level; + return true; + } else { + return false; + } + } + case IS_CHARGING: + data.valueAsBool = axpDevice.isCharging(); + return true; + case CURRENT: + return false; + } + + return false; // Safety guard for when new enum values are introduced +} + +bool Core2Power::isAllowedToCharge() const { + return allowedToCharge; +} + +void Core2Power::setAllowedToCharge(bool canCharge) { + allowedToCharge = canCharge; + axpDevice.setBatteryCharge(canCharge); +} + +static std::shared_ptr power; + +std::shared_ptr createPower() { + if (power == nullptr) { + power = std::make_shared(); + } + return power; +} diff --git a/Boards/M5stackCore2/Source/hal/Core2Power.h b/Boards/M5stackCore2/Source/hal/Core2Power.h new file mode 100644 index 00000000..1494da4e --- /dev/null +++ b/Boards/M5stackCore2/Source/hal/Core2Power.h @@ -0,0 +1,27 @@ +#pragma once + +#include "hal/Power.h" +#include + +using namespace tt::hal; + +class Core2Power : public Power { + +private: + + bool allowedToCharge = true; + +public: + + Core2Power() = default; + ~Core2Power() override = default; + + bool supportsMetric(MetricType type) const override; + bool getMetric(Power::MetricType type, Power::MetricData& data) override; + + bool supportsChargeControl() const override { return true; } + bool isAllowedToCharge() const override; + void setAllowedToCharge(bool canCharge) override; +}; + +std::shared_ptr createPower(); diff --git a/Boards/M5stackCore2/Source/hal/Core2SdCard.cpp b/Boards/M5stackCore2/Source/hal/Core2SdCard.cpp new file mode 100644 index 00000000..2e6e4310 --- /dev/null +++ b/Boards/M5stackCore2/Source/hal/Core2SdCard.cpp @@ -0,0 +1,31 @@ +#include "Core2SdCard.h" + +#include "lvgl/LvglSync.h" +#include "hal/SpiSdCard.h" + +#include + +#define CORE2_SDCARD_SPI_FREQUENCY 800000U +#define CORE2_SDCARD_PIN_CS GPIO_NUM_4 +#define CORE2_LCD_PIN_CS GPIO_NUM_5 + +std::shared_ptr createSdCard() { + auto* configuration = new tt::hal::SpiSdCard::Config( + CORE2_SDCARD_SPI_FREQUENCY, + CORE2_SDCARD_PIN_CS, + GPIO_NUM_NC, + GPIO_NUM_NC, + GPIO_NUM_NC, + SdCard::MountBehaviourAtBoot, + tt::lvgl::getLvglSyncLockable(), + { + CORE2_LCD_PIN_CS + } + ); + + auto* sdcard = (SdCard*) new SpiSdCard( + std::unique_ptr(configuration) + ); + + return std::shared_ptr(sdcard); +} diff --git a/Boards/M5stackCore2/Source/hal/Core2SdCard.h b/Boards/M5stackCore2/Source/hal/Core2SdCard.h new file mode 100644 index 00000000..a6671951 --- /dev/null +++ b/Boards/M5stackCore2/Source/hal/Core2SdCard.h @@ -0,0 +1,7 @@ +#pragma once + +#include "hal/SdCard.h" + +using namespace tt::hal; + +std::shared_ptr createSdCard(); diff --git a/Boards/M5stackCore2/Source/hal/Core2Touch.cpp b/Boards/M5stackCore2/Source/hal/Core2Touch.cpp new file mode 100644 index 00000000..a971a5fe --- /dev/null +++ b/Boards/M5stackCore2/Source/hal/Core2Touch.cpp @@ -0,0 +1,94 @@ +#include "Core2Touch.h" +#include + +#define TAG "core2_touch" + +static void touchReadCallback(lv_indev_t* indev, lv_indev_data_t* data) { + auto* touch = (Core2Touch*)lv_indev_get_driver_data(indev); + touch->readLast(data); +} + +static int32_t threadCallback(void* context) { + auto* touch = (Core2Touch*)context; + touch->driverThreadMain(); + return 0; +} + +Core2Touch::Core2Touch() : + driverThread(tt::Thread("core2_touch", 4096, threadCallback, this)) +{ } + +void Core2Touch::driverThreadMain() { + TPoint point = { .x = 0, .y = 0 }; + TEvent event = TEvent::None; + + while (!shouldInterruptDriverThread()) { + driver.processTouch(); + driver.poll(&point, &event); + + if (mutex.lock(100)) { + switch (event) { + case TEvent::TouchStart: + case TEvent::TouchMove: + case TEvent::DragStart: + case TEvent::DragMove: + case TEvent::DragEnd: + lastState = LV_INDEV_STATE_PR; + lastPoint.x = point.x; + lastPoint.y = point.y; + break; + case TEvent::TouchEnd: + lastState = LV_INDEV_STATE_REL; + lastPoint.x = point.x; + lastPoint.y = point.y; + break; + case TEvent::Tap: + case TEvent::None: + break; + } + mutex.unlock(); + } + } +} + +bool Core2Touch::shouldInterruptDriverThread() { + bool interrupt = false; + if (mutex.lock(50 / portTICK_PERIOD_MS)) { + interrupt = interruptDriverThread; + mutex.unlock(); + } + return interrupt; +} + +bool Core2Touch::start(lv_display_t* display) { + TT_LOG_I(TAG, "start"); + + driverThread.start(); + + uint16_t width = lv_display_get_horizontal_resolution(display); + uint16_t height = lv_display_get_vertical_resolution(display); + if (!driver.begin(FT6X36_DEFAULT_THRESHOLD, width, height)) { + TT_LOG_E(TAG, "driver.begin() failed"); + return false; + } + + deviceHandle = lv_indev_create(); + lv_indev_set_type(deviceHandle, LV_INDEV_TYPE_POINTER); + lv_indev_set_driver_data(deviceHandle, this); + lv_indev_set_read_cb(deviceHandle, touchReadCallback); + + TT_LOG_I(TAG, "start success"); + return true; +} + +bool Core2Touch::stop() { + lv_indev_delete(deviceHandle); + interruptDriverThread = true; + driverThread.join(); + return true; +} + +void Core2Touch::readLast(lv_indev_data_t* data) { + data->point = lastPoint; + data->state = lastState; +} diff --git a/Boards/M5stackCore2/Source/hal/Core2Touch.h b/Boards/M5stackCore2/Source/hal/Core2Touch.h new file mode 100644 index 00000000..eca8301e --- /dev/null +++ b/Boards/M5stackCore2/Source/hal/Core2Touch.h @@ -0,0 +1,32 @@ +#pragma once + +#include "hal/Touch.h" +#include "TactilityCore.h" +#include "ft6x36/FT6X36.h" + +class Core2Touch : public tt::hal::Touch { + +private: + + lv_indev_t* _Nullable deviceHandle = nullptr; + FT6X36 driver = FT6X36(I2C_NUM_0, GPIO_NUM_39); + tt::Thread driverThread; + bool interruptDriverThread = false; + tt::Mutex mutex; + + lv_point_t lastPoint = { .x = 0, .y = 0 }; + lv_indev_state_t lastState = LV_INDEV_STATE_RELEASED; + + bool shouldInterruptDriverThread(); + +public: + + Core2Touch(); + + bool start(lv_display_t* display) override; + bool stop() override; + + void readLast(lv_indev_data_t* data); + lv_indev_t* _Nullable getLvglIndev() override { return deviceHandle; } + void driverThreadMain(); +}; diff --git a/Boards/M5stackShared/Source/hal/M5stackDisplay.h b/Boards/M5stackShared/Source/hal/M5stackDisplay.h index e4e50f08..983f2fd3 100644 --- a/Boards/M5stackShared/Source/hal/M5stackDisplay.h +++ b/Boards/M5stackShared/Source/hal/M5stackDisplay.h @@ -17,10 +17,6 @@ public: bool stop() override; - void setPowerOn(bool turnOn) override {} - bool isPoweredOn() const override { return true; }; - bool supportsPowerControl() const override { return false; } - tt::hal::Touch* _Nullable createTouch() override; lv_display_t* _Nullable getLvglDisplay() const override { return displayHandle; } diff --git a/Boards/Simulator/Source/hal/SdlDisplay.h b/Boards/Simulator/Source/hal/SdlDisplay.h index 6ff17407..e0172f10 100644 --- a/Boards/Simulator/Source/hal/SdlDisplay.h +++ b/Boards/Simulator/Source/hal/SdlDisplay.h @@ -13,10 +13,6 @@ public: bool stop() override { tt_crash("Not supported"); } - void setPowerOn(bool turnOn) override {} - bool isPoweredOn() const override { return displayHandle != nullptr; } - bool supportsPowerControl() const override { return false; } - tt::hal::Touch* _Nullable createTouch() override { return dynamic_cast(new SdlTouch()); } lv_display_t* _Nullable getLvglDisplay() const override { return displayHandle; } diff --git a/Boards/YellowBoard/Source/Lvgl.cpp b/Boards/YellowBoard/Source/Lvgl.cpp index 1f3ff115..eae5c552 100644 --- a/Boards/YellowBoard/Source/Lvgl.cpp +++ b/Boards/YellowBoard/Source/Lvgl.cpp @@ -7,7 +7,7 @@ bool twodotfour_lvgl_init() { const lvgl_port_cfg_t lvgl_cfg = { - .task_priority = tt::Thread::PriorityHigh, + .task_priority = tt::THREAD_PRIORITY_RENDER, .task_stack = 8096, .task_affinity = -1, // core pinning .task_max_sleep_ms = 500, diff --git a/Boards/YellowBoard/Source/hal/YellowDisplay.cpp b/Boards/YellowBoard/Source/hal/YellowDisplay.cpp index 331802da..fe5b9eae 100644 --- a/Boards/YellowBoard/Source/hal/YellowDisplay.cpp +++ b/Boards/YellowBoard/Source/hal/YellowDisplay.cpp @@ -23,7 +23,8 @@ static bool initBacklight() { .duty_resolution = TWODOTFOUR_LCD_BACKLIGHT_LEDC_DUTY_RES, .timer_num = TWODOTFOUR_LCD_BACKLIGHT_LEDC_TIMER, .freq_hz = TWODOTFOUR_LCD_BACKLIGHT_LEDC_FREQUENCY, - .clk_cfg = LEDC_AUTO_CLK + .clk_cfg = LEDC_AUTO_CLK, + .deconfigure = false }; if (ledc_timer_config(&ledc_timer) != ESP_OK) { diff --git a/Boards/YellowBoard/Source/hal/YellowDisplay.h b/Boards/YellowBoard/Source/hal/YellowDisplay.h index 9a702731..1d60aa44 100644 --- a/Boards/YellowBoard/Source/hal/YellowDisplay.h +++ b/Boards/YellowBoard/Source/hal/YellowDisplay.h @@ -20,10 +20,6 @@ public: bool stop() override; - void setPowerOn(bool turnOn) override {} - bool isPoweredOn() const override { return true; }; - bool supportsPowerControl() const override { return false; } - tt::hal::Touch* _Nullable createTouch() override; void setBacklightDuty(uint8_t backlightDuty) override; diff --git a/Boards/YellowBoard/Source/hal/YellowDisplayConstants.h b/Boards/YellowBoard/Source/hal/YellowDisplayConstants.h index 30ab803d..a188175b 100644 --- a/Boards/YellowBoard/Source/hal/YellowDisplayConstants.h +++ b/Boards/YellowBoard/Source/hal/YellowDisplayConstants.h @@ -1,6 +1,6 @@ #pragma once -// Dipslay backlight (PWM) +// Display backlight (PWM) #define TWODOTFOUR_LCD_BACKLIGHT_LEDC_TIMER LEDC_TIMER_0 #define TWODOTFOUR_LCD_BACKLIGHT_LEDC_MODE LEDC_LOW_SPEED_MODE #define TWODOTFOUR_LCD_BACKLIGHT_LEDC_CHANNEL LEDC_CHANNEL_0 diff --git a/Boards/YellowBoard/Source/hal/YellowTouch.cpp b/Boards/YellowBoard/Source/hal/YellowTouch.cpp index 72a02a95..0a5e8466 100644 --- a/Boards/YellowBoard/Source/hal/YellowTouch.cpp +++ b/Boards/YellowBoard/Source/hal/YellowTouch.cpp @@ -7,7 +7,7 @@ #include "esp_lcd_touch.h" #include "esp_lvgl_port.h" -#define TAG "m5stack_touch" +#define TAG "twodotfour_touch" bool YellowTouch::start(lv_display_t* display) { TT_LOG_I(TAG, "Starting"); diff --git a/Tactility/Source/lvgl/Statusbar.cpp b/Tactility/Source/lvgl/Statusbar.cpp index b26dfb69..a258d1e1 100644 --- a/Tactility/Source/lvgl/Statusbar.cpp +++ b/Tactility/Source/lvgl/Statusbar.cpp @@ -35,12 +35,12 @@ typedef struct { PubSubSubscription* pubsub_subscription; } Statusbar; -static void statusbar_lock() { - statusbar_data.mutex.acquire(TtWaitForever); +static bool statusbar_lock(uint32_t timeoutTicks) { + return statusbar_data.mutex.lock(timeoutTicks); } -static void statusbar_unlock() { - statusbar_data.mutex.release(); +static bool statusbar_unlock() { + return statusbar_data.mutex.unlock(); } static void statusbar_constructor(const lv_obj_class_t* class_p, lv_obj_t* obj); @@ -113,7 +113,7 @@ lv_obj_t* statusbar_create(lv_obj_t* parent) { lv_obj_t* left_spacer = spacer_create(obj, 1, 1); lv_obj_set_flex_grow(left_spacer, 1); - statusbar_lock(); + statusbar_lock(TtWaitForever); for (int i = 0; i < STATUSBAR_ICON_LIMIT; ++i) { lv_obj_t* image = lv_image_create(obj); lv_obj_set_size(image, STATUSBAR_ICON_SIZE, STATUSBAR_ICON_SIZE); @@ -129,11 +129,12 @@ lv_obj_t* statusbar_create(lv_obj_t* parent) { } static void update_main(Statusbar* statusbar) { - statusbar_lock(); - for (int i = 0; i < STATUSBAR_ICON_LIMIT; ++i) { - update_icon(statusbar->icons[i], &(statusbar_data.icons[i])); + if (statusbar_lock(50 / portTICK_PERIOD_MS)) { + for (int i = 0; i < STATUSBAR_ICON_LIMIT; ++i) { + update_icon(statusbar->icons[i], &(statusbar_data.icons[i])); + } + statusbar_unlock(); } - statusbar_unlock(); } static void statusbar_event(TT_UNUSED const lv_obj_class_t* class_p, lv_event_t* event) { @@ -154,7 +155,7 @@ static void statusbar_event(TT_UNUSED const lv_obj_class_t* class_p, lv_event_t* } int8_t statusbar_icon_add(const char* _Nullable image) { - statusbar_lock(); + statusbar_lock(TtWaitForever); int8_t result = -1; for (int8_t i = 0; i < STATUSBAR_ICON_LIMIT; ++i) { if (!statusbar_data.icons[i].claimed) { @@ -174,7 +175,7 @@ int8_t statusbar_icon_add(const char* _Nullable image) { void statusbar_icon_remove(int8_t id) { TT_LOG_I(TAG, "id %d: remove", id); tt_check(id >= 0 && id < STATUSBAR_ICON_LIMIT); - statusbar_lock(); + statusbar_lock(TtWaitForever); StatusbarIcon* icon = &statusbar_data.icons[id]; icon->claimed = false; icon->visible = false; @@ -184,25 +185,27 @@ void statusbar_icon_remove(int8_t id) { } void statusbar_icon_set_image(int8_t id, const char* image) { - TT_LOG_I(TAG, "id %d: set image %s", id, image); + TT_LOG_I(TAG, "id %d: set image %s", id, image ? image : "(none)"); tt_check(id >= 0 && id < STATUSBAR_ICON_LIMIT); - statusbar_lock(); - StatusbarIcon* icon = &statusbar_data.icons[id]; - tt_check(icon->claimed); - icon->image = image; - tt_pubsub_publish(statusbar_data.pubsub, nullptr); - statusbar_unlock(); + if (statusbar_lock(50 / portTICK_PERIOD_MS)) { + StatusbarIcon* icon = &statusbar_data.icons[id]; + tt_check(icon->claimed); + icon->image = image; + tt_pubsub_publish(statusbar_data.pubsub, nullptr); + statusbar_unlock(); + } } void statusbar_icon_set_visibility(int8_t id, bool visible) { TT_LOG_I(TAG, "id %d: set visibility %d", id, visible); tt_check(id >= 0 && id < STATUSBAR_ICON_LIMIT); - statusbar_lock(); - StatusbarIcon* icon = &statusbar_data.icons[id]; - tt_check(icon->claimed); - icon->visible = visible; - tt_pubsub_publish(statusbar_data.pubsub, nullptr); - statusbar_unlock(); + if (statusbar_lock(50 / portTICK_PERIOD_MS)) { + StatusbarIcon* icon = &statusbar_data.icons[id]; + tt_check(icon->claimed); + icon->visible = visible; + tt_pubsub_publish(statusbar_data.pubsub, nullptr); + statusbar_unlock(); + } } } // namespace diff --git a/Tactility/Source/service/statusbar/Statusbar.cpp b/Tactility/Source/service/statusbar/Statusbar.cpp index a77a1be9..8c5397e8 100644 --- a/Tactility/Source/service/statusbar/Statusbar.cpp +++ b/Tactility/Source/service/statusbar/Statusbar.cpp @@ -10,6 +10,7 @@ #include "service/wifi/Wifi.h" #include "service/ServiceRegistry.h" #include "TactilityHeadless.h" +#include "lvgl/LvglSync.h" namespace tt::service::statusbar { @@ -182,7 +183,10 @@ static void onUpdate(std::shared_ptr parameter) { } static void onStart(ServiceContext& service) { + lvgl::lock(TtWaitForever); auto data = std::make_shared(); + lvgl::unlock(); + service.setData(data); // TODO: Make thread-safe for LVGL diff --git a/TactilityCore/Source/Dispatcher.cpp b/TactilityCore/Source/Dispatcher.cpp index dabf0d7e..96caf755 100644 --- a/TactilityCore/Source/Dispatcher.cpp +++ b/TactilityCore/Source/Dispatcher.cpp @@ -6,6 +6,7 @@ namespace tt { #define TAG "Dispatcher" #define BACKPRESSURE_WARNING_COUNT 100 +#define WAIT_FLAG 1 Dispatcher::Dispatcher() : mutex(Mutex::TypeNormal) @@ -22,13 +23,12 @@ void Dispatcher::dispatch(Callback callback, std::shared_ptr context) { // Mutate if (mutex.lock(1000 / portTICK_PERIOD_MS)) { queue.push(std::move(message)); - TT_LOG_I(TAG, "dispatch"); if (queue.size() == BACKPRESSURE_WARNING_COUNT) { TT_LOG_W(TAG, "Backpressure: You're not consuming fast enough (100 queued)"); } - mutex.unlock(); + tt_check(mutex.unlock()); // Signal - eventFlag.set(1); + eventFlag.set(WAIT_FLAG); } else { TT_LOG_E(TAG, LOG_MESSAGE_MUTEX_LOCK_FAILED); } @@ -37,15 +37,15 @@ void Dispatcher::dispatch(Callback callback, std::shared_ptr context) { uint32_t Dispatcher::consume(uint32_t timeout_ticks) { // Wait for signal and clear TickType_t start_ticks = kernel::getTicks(); - if (eventFlag.wait(1, TtFlagWaitAny, timeout_ticks) == TtStatusErrorTimeout) { + if (eventFlag.wait(WAIT_FLAG, TtFlagWaitAny, timeout_ticks) == WAIT_FLAG) { + eventFlag.clear(WAIT_FLAG); + } else { return 0; } TickType_t ticks_remaining = TT_MAX(timeout_ticks - (kernel::getTicks() - start_ticks), 0); - eventFlag.clear(1); - - TT_LOG_I(TAG, "Dispatcher continuing"); + TT_LOG_I(TAG, "Dispatcher continuing (%d ticks)", (int)ticks_remaining); // Mutate bool processing = true; @@ -53,16 +53,15 @@ uint32_t Dispatcher::consume(uint32_t timeout_ticks) { do { if (mutex.lock(ticks_remaining / portTICK_PERIOD_MS)) { if (!queue.empty()) { - TT_LOG_I(TAG, "Dispatcher popping from queue"); auto item = queue.front(); queue.pop(); consumed++; processing = !queue.empty(); // Don't keep lock as callback might be slow - tt_check(mutex.unlock()); item->callback(item->context); } else { + processing = false; tt_check(mutex.unlock()); } diff --git a/TactilityCore/Source/DispatcherThread.cpp b/TactilityCore/Source/DispatcherThread.cpp index 506fe748..ed59f93e 100644 --- a/TactilityCore/Source/DispatcherThread.cpp +++ b/TactilityCore/Source/DispatcherThread.cpp @@ -25,7 +25,7 @@ DispatcherThread::~DispatcherThread() { void DispatcherThread::_threadMain() { do { - dispatcher.consume(1000); + dispatcher.consume(1000 / portTICK_PERIOD_MS); } while (!interruptThread); } diff --git a/TactilityCore/Source/Mutex.cpp b/TactilityCore/Source/Mutex.cpp index 085cce89..392f736c 100644 --- a/TactilityCore/Source/Mutex.cpp +++ b/TactilityCore/Source/Mutex.cpp @@ -61,7 +61,6 @@ TtStatus Mutex::acquire(uint32_t timeout) const { } else { return TtStatusOk; } - break; case TypeRecursive: if (xSemaphoreTakeRecursive(semaphore, timeout) != pdPASS) { if (timeout != 0U) { @@ -72,7 +71,6 @@ TtStatus Mutex::acquire(uint32_t timeout) const { } else { return TtStatusOk; } - break; default: tt_crash("mutex type unknown/corrupted"); } @@ -90,7 +88,6 @@ TtStatus Mutex::release() const { } else { return TtStatusOk; } - break; } case TypeRecursive: if (xSemaphoreGiveRecursive(semaphore) != pdPASS) { @@ -98,7 +95,6 @@ TtStatus Mutex::release() const { } else { return TtStatusOk; } - break; default: tt_crash("mutex type unknown/corrupted"); } diff --git a/TactilityCore/Source/kernel/critical/Critical.h b/TactilityCore/Source/kernel/critical/Critical.h index e5706c6e..162ab1e8 100644 --- a/TactilityCore/Source/kernel/critical/Critical.h +++ b/TactilityCore/Source/kernel/critical/Critical.h @@ -2,14 +2,6 @@ #include -#ifndef TT_CRITICAL_ENTER -#define TT_CRITICAL_ENTER() __TtCriticalInfo __tt_critical_info = __tt_critical_enter(); -#endif - -#ifndef TT_CRITICAL_EXIT -#define TT_CRITICAL_EXIT() __tt_critical_exit(__tt_critical_info); -#endif - namespace tt::kernel::critical { typedef struct { diff --git a/TactilityHeadless/Source/hal/Display.h b/TactilityHeadless/Source/hal/Display.h index b8ec7515..6bdac615 100644 --- a/TactilityHeadless/Source/hal/Display.h +++ b/TactilityHeadless/Source/hal/Display.h @@ -11,9 +11,9 @@ public: virtual bool start() = 0; virtual bool stop() = 0; - virtual void setPowerOn(bool turnOn) = 0; - virtual bool isPoweredOn() const = 0; - virtual bool supportsPowerControl() const = 0; + virtual void setPowerOn(bool turnOn) {} + virtual bool isPoweredOn() const { return true; } + virtual bool supportsPowerControl() const { return false; } virtual Touch* _Nullable createTouch() = 0;