#include "TpagerKeyboard.h" #include #include #include "freertos/queue.h" #include "driver/gpio.h" #include #define TAG "tpager_keyboard" #define ENCODER_A GPIO_NUM_40 #define ENCODER_B GPIO_NUM_41 #define ENCODER_ENTER GPIO_NUM_7 #define USER_BTN GPIO_NUM_0 #define KB_ROWS 4 #define KB_COLS 11 static constexpr char keymap_lc[KB_ROWS][KB_COLS] = { {'\0', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p'}, {'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', '\n', '\0'}, {'z', 'x', 'c', 'v', 'b', 'n', 'm', '\0', LV_KEY_BACKSPACE, ' ', '\0'}, {'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'} }; static constexpr char keymap_uc[KB_ROWS][KB_COLS] = { {'\0', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P'}, {'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', '\n', '\0'}, {'Z', 'X', 'C', 'V', 'B', 'N', 'M', '\0', LV_KEY_BACKSPACE, ' ', '\0'}, {'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'} }; static constexpr char keymap_sy[KB_ROWS][KB_COLS] = { {'\0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0'}, {'.', '/', '+', '-', '=', ':', '\'', '"', '@', '\t', '\0'}, {'_', '$', ';', '?', '!', ',', '.', '\0', LV_KEY_BACKSPACE, ' ', '\0'}, {'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'} }; static QueueHandle_t rotaryMsg; static QueueHandle_t keyboardMsg; /** * 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(lv_indev_t* indev, lv_indev_data_t* data) { TpagerKeyboard* kb = (TpagerKeyboard*)lv_indev_get_user_data(indev); uint8_t read_buffer = 0x00; static bool enter_prev = false; // Defaults data->key = 0; data->state = LV_INDEV_STATE_RELEASED; char encstate = 0; char keypress = 0; if (xQueueReceive(keyboardMsg, &keypress, pdMS_TO_TICKS(50)) == pdPASS) { TT_LOG_I(TAG, "KEY %c (%d)", keypress, keypress); data->key = keypress; data->state = LV_INDEV_STATE_PRESSED; }/* else if (xQueueReceive(rotaryMsg, &encstate, pdMS_TO_TICKS(50)) == pdPASS) { data->key = encstate; if (encstate == LV_KEY_NEXT) data->enc_diff = 1; if (encstate == LV_KEY_PREV) data->enc_diff = -1; data->state = LV_INDEV_STATE_PRESSED; }*/ //bool enter = !gpio_get_level(ENCODER_ENTER); /*if (enter != enter_prev) { TT_LOG_I(TAG, "Encoder CHANGE %d", enter); data->key = LV_KEY_ENTER; data->state = enter ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED; enter_prev = enter; }*/ } static void encoder_read_callback(lv_indev_t* indev, lv_indev_data_t* data) { TpagerKeyboard* kb = (TpagerKeyboard*)lv_indev_get_user_data(indev); uint8_t read_buffer = 0x00; const int enterFilterThreshold = 2; static int enterFilter = 0; static int pulses_prev = 0; // Defaults data->enc_diff = 0; data->state = LV_INDEV_STATE_RELEASED; int pulses = kb->getEncoderPulses(); int pulse_diff = (pulses - pulses_prev); if ((pulse_diff > 4) || (pulse_diff < -4)) { data->enc_diff = pulse_diff / 4; pulses_prev = pulses; } // TT_LOG_I(TAG, "Encoder DIFF %d PULSE %d", data->enc_diff, pulses); char encstate = 0; char keypress = 0; /*if (xQueueReceive(rotaryMsg, &encstate, pdMS_TO_TICKS(50)) == pdPASS) { //data->key = encstate; if (encstate == LV_KEY_NEXT) data->enc_diff = 1; if (encstate == LV_KEY_PREV) data->enc_diff = -1; if (encstate == LV_KEY_RIGHT) data->enc_diff = 100; if (encstate == LV_KEY_LEFT) data->enc_diff = -100; //data->state = LV_INDEV_STATE_PRESSED; }*/ bool enter = !gpio_get_level(ENCODER_ENTER); if (enter && (enterFilter < enterFilterThreshold)) { enterFilter++; } if (!enter && (enterFilter > 0)) { enterFilter--; } if (enterFilter == enterFilterThreshold) { data->state = LV_INDEV_STATE_PRESSED; } //TT_LOG_I(TAG, "ENTER F %d", enterFilter); } static void button_read_callback(lv_indev_t* indev, lv_indev_data_t* data) { if(!gpio_get_level(USER_BTN)) { /*Is there a button press? (E.g. -1 indicated no button was pressed)*/ data->state = LV_INDEV_STATE_PRESSED; /*Set the pressed state*/ TT_LOG_I(TAG, "USR BTN PRESS"); } else { data->state = LV_INDEV_STATE_RELEASED; /*Set the released state*/ } data->btn_id = 0; /*Save the last button*/ } void TpagerKeyboard::processEncoder() { } void TpagerKeyboard::processKeyboard() { static bool shift_pressed = false; static bool sym_pressed = false; static bool cap_toggle = false; static bool cap_toggle_armed = true; if (keypad->update()) { for (int i=0; ipressed_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; TT_LOG_I(TAG, "KB PRESS R=%d C=%d", row, col); if ((row == 1) && (col == 10)) { sym_pressed = true; TT_LOG_I(TAG, "KB SYM ON"); } if ((row == 2) && (col == 7)) { shift_pressed = true; TT_LOG_I(TAG, "KB SHFT ON"); } } if ((sym_pressed && shift_pressed) && cap_toggle_armed) { cap_toggle = !cap_toggle; cap_toggle_armed = false; TT_LOG_I(TAG, "KB CAP TOGGLE"); } for (int i=0; ipressed_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; char chr = '\0'; if (sym_pressed) { chr = keymap_sy[row][col]; } else if (shift_pressed || cap_toggle) { chr = keymap_uc[row][col]; } else { chr = keymap_lc[row][col]; } if (chr != '\0') xQueueSend(keyboardMsg, (void *)&chr, portMAX_DELAY); } for (int i=0; ireleased_key_count; i++) { auto row = keypad->released_list[i].row; auto col = keypad->released_list[i].col; TT_LOG_I(TAG, "KB REL R=%d C=%d", row, col); if ((row == 1) && (col == 10)) { sym_pressed = false; TT_LOG_I(TAG, "KB SYM OFF"); } if ((row == 2) && (col == 7)) { shift_pressed = false; TT_LOG_I(TAG, "KB SHFT OFF"); } } if ((!sym_pressed && !shift_pressed) && !cap_toggle_armed) { cap_toggle_armed = true; TT_LOG_I(TAG, "KB CAP TOGGLE REARM"); } } /* char encstate = encoder.process(); char encbtn = 0; switch (encstate) { case DIR_CW: TT_LOG_I(TAG, "ENC CW"); encbtn = sym_pressed ? LV_KEY_RIGHT : LV_KEY_NEXT; break; case DIR_CCW: TT_LOG_I(TAG, "ENC CCW"); encbtn = sym_pressed ? LV_KEY_LEFT : LV_KEY_PREV; break; default: break; } if (encbtn != 0) xQueueSend(rotaryMsg, (void *)&encbtn, portMAX_DELAY); */ } bool TpagerKeyboard::start(lv_display_t* display) { //encoder.begin(); initEncoder(); keypad->init(KB_ROWS, KB_COLS); gpio_input_enable(ENCODER_ENTER); gpio_input_enable(USER_BTN); assert(encoderTimer == nullptr); encoderTimer = std::make_unique(tt::Timer::Type::Periodic, [this] { processEncoder(); processKeyboard(); }); kbHandle = lv_indev_create(); lv_indev_set_type(kbHandle, LV_INDEV_TYPE_KEYPAD); lv_indev_set_read_cb(kbHandle, &keyboard_read_callback); lv_indev_set_display(kbHandle, display); lv_indev_set_user_data(kbHandle, this); btnHandle = lv_indev_create(); lv_indev_set_type(btnHandle, LV_INDEV_TYPE_BUTTON); static const lv_point_t btn_points[1] = { {472, 111} //{20, 30} }; lv_indev_set_button_points(btnHandle, btn_points); lv_indev_set_read_cb(btnHandle, &button_read_callback); lv_indev_set_display(btnHandle, display); lv_indev_set_user_data(btnHandle, this); encHandle = lv_indev_create(); lv_indev_set_type(encHandle, LV_INDEV_TYPE_ENCODER); lv_indev_set_read_cb(encHandle, &encoder_read_callback); lv_indev_set_display(encHandle, display); lv_indev_set_user_data(encHandle, this); encoderTimer->start(20 / portTICK_PERIOD_MS); return true; } bool TpagerKeyboard::stop() { assert(encoderTimer); encoderTimer->stop(); encoderTimer = nullptr; lv_indev_delete(kbHandle); kbHandle = nullptr; lv_indev_delete(encHandle); encHandle = nullptr; lv_indev_delete(btnHandle); btnHandle = nullptr; return true; } bool TpagerKeyboard::isAttached() const { return tt::hal::i2c::masterHasDeviceAtAddress(keypad->getPort(), keypad->getAddress(), 100); } void TpagerKeyboard::initEncoder(void) { pcnt_unit_config_t unit_config = { .low_limit = -127, .high_limit = 126, .flags = { .accum_count = 1 }, }; ESP_ERROR_CHECK(pcnt_new_unit(&unit_config, &encPcntUnit)); //ESP_LOGI(encoder, "set glitch filter"); pcnt_glitch_filter_config_t filter_config = { .max_glitch_ns = 1000, }; ESP_ERROR_CHECK(pcnt_unit_set_glitch_filter(encPcntUnit, &filter_config)); //ESP_LOGI(encoder, "install pcnt channels"); pcnt_chan_config_t chan_a_config = { .edge_gpio_num = ENCODER_A, .level_gpio_num = ENCODER_B, }; pcnt_channel_handle_t pcnt_chan_a = NULL; ESP_ERROR_CHECK(pcnt_new_channel(encPcntUnit, &chan_a_config, &pcnt_chan_a)); pcnt_chan_config_t chan_b_config = { .edge_gpio_num = ENCODER_B, .level_gpio_num = ENCODER_A, }; pcnt_channel_handle_t pcnt_chan_b = NULL; ESP_ERROR_CHECK(pcnt_new_channel(encPcntUnit, &chan_b_config, &pcnt_chan_b)); //ESP_LOGI(encoder, "set edge and level actions for pcnt channels"); ESP_ERROR_CHECK(pcnt_channel_set_edge_action(pcnt_chan_a, PCNT_CHANNEL_EDGE_ACTION_DECREASE, PCNT_CHANNEL_EDGE_ACTION_INCREASE)); ESP_ERROR_CHECK(pcnt_channel_set_level_action(pcnt_chan_a, PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_INVERSE)); ESP_ERROR_CHECK(pcnt_channel_set_edge_action(pcnt_chan_b, PCNT_CHANNEL_EDGE_ACTION_INCREASE, PCNT_CHANNEL_EDGE_ACTION_DECREASE)); ESP_ERROR_CHECK(pcnt_channel_set_level_action(pcnt_chan_b, PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_INVERSE)); //ESP_LOGI(encoder, "add watch points and register callbacks"); int watch_points[] = {-127,126}; for (size_t i = 0; i < sizeof(watch_points) / sizeof(watch_points[0]); i++) { ESP_ERROR_CHECK(pcnt_unit_add_watch_point(encPcntUnit, watch_points[i])); } /*pcnt_event_callbacks_t cbs = { .on_reach = pcnt_on_reach, }; hEncoder.queue = xQueueCreate(10, sizeof(int)); ESP_ERROR_CHECK(pcnt_unit_register_event_callbacks(encPcntUnit, &cbs, hEncoder.queue)); */ //ESP_LOGI(encoder, "enable pcnt unit"); ESP_ERROR_CHECK(pcnt_unit_enable(encPcntUnit)); //ESP_LOGI(encoder, "clear pcnt unit"); ESP_ERROR_CHECK(pcnt_unit_clear_count(encPcntUnit)); //ESP_LOGI(encoder, "start pcnt unit"); ESP_ERROR_CHECK(pcnt_unit_start(encPcntUnit)); //External callback register. //hEncoder.callback = callback; } int TpagerKeyboard::getEncoderPulses() { int pulses = 0; pcnt_unit_get_count(encPcntUnit, &pulses); return pulses; } extern std::shared_ptr tca8418; std::shared_ptr createKeyboard() { rotaryMsg = xQueueCreate(5, sizeof(char)); keyboardMsg = xQueueCreate(20, sizeof(char)); return std::make_shared(tca8418); }