From f775a59409d01f325006007aef70a11bc4e7a4bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominic=20H=C3=B6glinger?= Date: Mon, 30 Jun 2025 22:16:33 +0200 Subject: [PATCH] Pager: Add rudamentary keyboard backlight --- .../Source/hal/TpagerKeyboard.cpp | 91 ++++++++++++++++++- .../Source/hal/TpagerKeyboard.h | 13 +++ 2 files changed, 101 insertions(+), 3 deletions(-) diff --git a/Boards/LilygoTLoraPager/Source/hal/TpagerKeyboard.cpp b/Boards/LilygoTLoraPager/Source/hal/TpagerKeyboard.cpp index 462ae706..769fa049 100644 --- a/Boards/LilygoTLoraPager/Source/hal/TpagerKeyboard.cpp +++ b/Boards/LilygoTLoraPager/Source/hal/TpagerKeyboard.cpp @@ -12,6 +12,7 @@ #define ENCODER_A GPIO_NUM_40 #define ENCODER_B GPIO_NUM_41 #define ENCODER_ENTER GPIO_NUM_7 +#define BACKLIGHT GPIO_NUM_46 #define KB_ROWS 4 #define KB_COLS 11 @@ -63,6 +64,7 @@ static void encoder_read_callback(lv_indev_t* indev, lv_indev_data_t* data) { static int enter_filter = 0; const int pulses_click = 4; static int pulses_prev = 0; + bool anyinput = false; // Defaults data->enc_diff = 0; @@ -73,6 +75,7 @@ static void encoder_read_callback(lv_indev_t* indev, lv_indev_data_t* data) { if ((pulse_diff > pulses_click) || (pulse_diff < -pulses_click)) { data->enc_diff = pulse_diff / pulses_click; pulses_prev = pulses; + anyinput = true; } bool enter = !gpio_get_level(ENCODER_ENTER); @@ -85,6 +88,11 @@ static void encoder_read_callback(lv_indev_t* indev, lv_indev_data_t* data) { if (enter_filter == enter_filter_threshold) { data->state = LV_INDEV_STATE_PRESSED; + anyinput = true; + } + + if (anyinput) { + kb->makeBacklightImpulse(); } } @@ -93,8 +101,10 @@ void TpagerKeyboard::processKeyboard() { static bool sym_pressed = false; static bool cap_toggle = false; static bool cap_toggle_armed = true; + bool anykey_pressed = false; if (keypad->update()) { + anykey_pressed = (keypad->pressed_key_count > 0); for (int i=0; i < keypad->pressed_key_count; i++) { auto row = keypad->pressed_list[i].row; auto col = keypad->pressed_list[i].col; @@ -113,7 +123,7 @@ void TpagerKeyboard::processKeyboard() { cap_toggle_armed = false; } - for (int i=0; ipressed_key_count; i++) { + for (int i=0; i < keypad->pressed_key_count; i++) { auto row = keypad->pressed_list[i].row; auto col = keypad->pressed_list[i].col; auto hold = keypad->pressed_list[i].hold_time; @@ -129,7 +139,7 @@ void TpagerKeyboard::processKeyboard() { if (chr != '\0') xQueueSend(keyboardMsg, (void *)&chr, portMAX_DELAY); } - for (int i=0; ireleased_key_count; i++) { + for (int i=0; i < keypad->released_key_count; i++) { auto row = keypad->released_list[i].row; auto col = keypad->released_list[i].col; @@ -144,10 +154,15 @@ void TpagerKeyboard::processKeyboard() { if ((!sym_pressed && !shift_pressed) && !cap_toggle_armed) { cap_toggle_armed = true; } + + if (anykey_pressed) { + makeBacklightImpulse(); + } } } bool TpagerKeyboard::start(lv_display_t* display) { + backlightOkay = initBacklight(BACKLIGHT, 30000, LEDC_TIMER_0, LEDC_CHANNEL_1); initEncoder(); keypad->init(KB_ROWS, KB_COLS); gpio_input_enable(ENCODER_ENTER); @@ -157,6 +172,11 @@ bool TpagerKeyboard::start(lv_display_t* display) { processKeyboard(); }); + assert(backlightImpulseTimer == nullptr); + backlightImpulseTimer = std::make_unique(tt::Timer::Type::Periodic, [this] { + processBacklightImpuse(); + }); + kbHandle = lv_indev_create(); lv_indev_set_type(kbHandle, LV_INDEV_TYPE_KEYPAD); lv_indev_set_read_cb(kbHandle, &keyboard_read_callback); @@ -170,16 +190,20 @@ bool TpagerKeyboard::start(lv_display_t* display) { lv_indev_set_user_data(encHandle, this); inputTimer->start(20 / portTICK_PERIOD_MS); + backlightImpulseTimer->start(50 / portTICK_PERIOD_MS); return true; } bool TpagerKeyboard::stop() { assert(inputTimer); - inputTimer->stop(); inputTimer = nullptr; + assert(backlightImpulseTimer); + backlightImpulseTimer->stop(); + backlightImpulseTimer = nullptr; + lv_indev_delete(kbHandle); kbHandle = nullptr; lv_indev_delete(encHandle); @@ -243,6 +267,67 @@ int TpagerKeyboard::getEncoderPulses() { } +bool TpagerKeyboard::initBacklight(gpio_num_t pin, uint32_t frequencyHz, ledc_timer_t timer, ledc_channel_t channel) { + backlightPin = pin; + backlightTimer = timer; + backlightChannel = channel; + + ledc_timer_config_t ledc_timer = { + .speed_mode = LEDC_LOW_SPEED_MODE, + .duty_resolution = LEDC_TIMER_8_BIT, + .timer_num = backlightTimer, + .freq_hz = frequencyHz, + .clk_cfg = LEDC_AUTO_CLK, + .deconfigure = false + }; + + if (ledc_timer_config(&ledc_timer) != ESP_OK) { + TT_LOG_E(TAG, "Backlight timer config failed"); + return false; + } + + ledc_channel_config_t ledc_channel = { + .gpio_num = backlightPin, + .speed_mode = LEDC_LOW_SPEED_MODE, + .channel = backlightChannel, + .intr_type = LEDC_INTR_DISABLE, + .timer_sel = backlightTimer, + .duty = 0, + .hpoint = 0, + .sleep_mode = LEDC_SLEEP_MODE_NO_ALIVE_NO_PD, + .flags = { + .output_invert = 0 + } + }; + + if (ledc_channel_config(&ledc_channel) != ESP_OK) { + TT_LOG_E(TAG, "Backlight channel config failed"); + } + + return true; +} + +bool TpagerKeyboard::setBacklightDuty(uint8_t duty) { + if (!backlightOkay) { + TT_LOG_E(TAG, "Backlight not ready"); + return false; + } + return (ledc_set_duty(LEDC_LOW_SPEED_MODE, backlightChannel, duty) == ESP_OK) && + (ledc_update_duty(LEDC_LOW_SPEED_MODE, backlightChannel) == ESP_OK); +} + +void TpagerKeyboard::makeBacklightImpulse() { + backlightImpulseDuty = 255; + setBacklightDuty(backlightImpulseDuty); +} + +void TpagerKeyboard::processBacklightImpuse() { + if (backlightImpulseDuty > 64) { + backlightImpulseDuty--; + setBacklightDuty(backlightImpulseDuty); + } +} + extern std::shared_ptr tca8418; std::shared_ptr createKeyboard() { keyboardMsg = xQueueCreate(20, sizeof(char)); diff --git a/Boards/LilygoTLoraPager/Source/hal/TpagerKeyboard.h b/Boards/LilygoTLoraPager/Source/hal/TpagerKeyboard.h index dfeaa949..c497057e 100644 --- a/Boards/LilygoTLoraPager/Source/hal/TpagerKeyboard.h +++ b/Boards/LilygoTLoraPager/Source/hal/TpagerKeyboard.h @@ -5,6 +5,8 @@ #include #include +#include +#include #include @@ -15,11 +17,20 @@ private: lv_indev_t* _Nullable kbHandle = nullptr; lv_indev_t* _Nullable encHandle = nullptr; pcnt_unit_handle_t encPcntUnit = nullptr; + gpio_num_t backlightPin = GPIO_NUM_NC; + ledc_timer_t backlightTimer; + ledc_channel_t backlightChannel; + bool backlightOkay = false; + int backlightImpulseDuty = 0; + std::shared_ptr keypad; std::unique_ptr inputTimer; + std::unique_ptr backlightImpulseTimer; void initEncoder(void); + bool initBacklight(gpio_num_t pin, uint32_t frequencyHz, ledc_timer_t timer, ledc_channel_t channel); void processKeyboard(); + void processBacklightImpuse(); public: TpagerKeyboard(std::shared_ptr tca) : keypad(std::move(tca)) {} @@ -34,6 +45,8 @@ public: lv_indev_t* _Nullable getLvglIndev() override { return kbHandle; } int getEncoderPulses(); + bool setBacklightDuty(uint8_t duty); + void makeBacklightImpulse(); }; std::shared_ptr createKeyboard();