#include "ButtonControl.h" #include #include static const auto LOGGER = tt::Logger("ButtonControl"); ButtonControl::ButtonControl(const std::vector& pinConfigurations) : pinConfigurations(pinConfigurations) { pinStates.resize(pinConfigurations.size()); for (const auto& pinConfiguration : pinConfigurations) { tt::hal::gpio::configure(pinConfiguration.pin, tt::hal::gpio::Mode::Input, false, false); } } ButtonControl::~ButtonControl() { if (driverThread != nullptr && driverThread->getState() != tt::Thread::State::Stopped) { interruptDriverThread = true; driverThread->join(); } } void ButtonControl::readCallback(lv_indev_t* indev, lv_indev_data_t* data) { // Defaults data->enc_diff = 0; data->state = LV_INDEV_STATE_RELEASED; auto* self = static_cast(lv_indev_get_driver_data(indev)); if (self->mutex.lock(100)) { for (int i = 0; i < self->pinConfigurations.size(); i++) { const auto& config = self->pinConfigurations[i]; std::vector::reference state = self->pinStates[i]; const bool trigger = (config.event == Event::ShortPress && state.triggerShortPress) || (config.event == Event::LongPress && state.triggerLongPress); state.triggerShortPress = false; state.triggerLongPress = false; if (trigger) { switch (config.action) { case Action::UiSelectNext: data->enc_diff = 1; break; case Action::UiSelectPrevious: data->enc_diff = -1; break; case Action::UiPressSelected: data->state = LV_INDEV_STATE_PRESSED; break; case Action::AppClose: // TODO: implement break; } } } self->mutex.unlock(); } } void ButtonControl::updatePin(std::vector::const_reference configuration, std::vector::reference state) { if (tt::hal::gpio::getLevel(configuration.pin)) { // if pressed if (state.pressState) { // check time for long press trigger auto time_passed = tt::kernel::getMillis() - state.pressStartTime; if (time_passed > 500) { // state.triggerLongPress = true; } } else { state.pressStartTime = tt::kernel::getMillis(); state.pressState = true; } } else { // released if (state.pressState) { auto time_passed = tt::kernel::getMillis() - state.pressStartTime; if (time_passed < 500) { LOGGER.debug("Trigger short press"); state.triggerShortPress = true; } state.pressState = false; } } } void ButtonControl::driverThreadMain() { while (!shouldInterruptDriverThread()) { if (mutex.lock(100)) { for (int i = 0; i < pinConfigurations.size(); i++) { updatePin(pinConfigurations[i], pinStates[i]); } mutex.unlock(); } tt::kernel::delayMillis(5); } } bool ButtonControl::shouldInterruptDriverThread() const { bool interrupt = false; if (mutex.lock(50 / portTICK_PERIOD_MS)) { interrupt = interruptDriverThread; mutex.unlock(); } return interrupt; } void ButtonControl::startThread() { LOGGER.info("Start"); mutex.lock(); interruptDriverThread = false; driverThread = std::make_shared("ButtonControl", 4096, [this] { driverThreadMain(); return 0; }); driverThread->start(); mutex.unlock(); } void ButtonControl::stopThread() { LOGGER.info("Stop"); mutex.lock(); interruptDriverThread = true; mutex.unlock(); driverThread->join(); mutex.lock(); driverThread = nullptr; mutex.unlock(); } bool ButtonControl::startLvgl(lv_display_t* display) { if (deviceHandle != nullptr) { return false; } startThread(); deviceHandle = lv_indev_create(); lv_indev_set_type(deviceHandle, LV_INDEV_TYPE_ENCODER); lv_indev_set_driver_data(deviceHandle, this); lv_indev_set_read_cb(deviceHandle, readCallback); return true; } bool ButtonControl::stopLvgl() { if (deviceHandle == nullptr) { return false; } lv_indev_delete(deviceHandle); deviceHandle = nullptr; stopThread(); return true; }