← На главную

Используем клавиатуру от BlackBerry Q10 в своих проектах

Однажды, во время утреннего полистывания Twitter, мое внимание привлек проект господина @arturo182 (a.k.a Artur Pacholec). Проект представляет собой DIY смартфон на базе экранчика ILI9341 и qwerty-клавиатуры с подсветкой от BlackBerry Q10. Особенно меня впечатлила идея использования клавиатуры от смартфона. Было решено незамедлительно обзавестись такой клавиатурой и научиться с ней работать.

Вот как она выглядит:

Qwerty-клавиатура от BlackBerry Q10

Клавиатуру можно приобрести на eBay менее чем за 3$. Подключение к ней осуществляется через маленький коннектор с замысловатым названием BM14B(0.8)-24DS-0.4V(53). Быстрее и дешевле всего оказалось купить такие коннекторы в Чип-и-Дипе. Нам также понадобится переходник с этого разъема на обычные пины с шагом 2.54 мм. К счастью, @arturo182 заботливо выложил такой переходник на GitHub’е. Платы были заказаны на OSH Park.

Припаять коннектор не особо сложно, главное иметь под рукой хороший флюс и лупу с сильным увеличением. Лично я сначала залудил пады на плате, припаял коннектор за два пина, расположенных по диагонали, а затем, придавливая его к плате пинцетом, припаял все остальные пины. Если визуально плата в порядке, отмываем в УЗ-ванне (при таком расстоянии между пинами зубная щетка плохо справляется) и перепроверяем мультиметром.

Используя получившийся переходник, я подключил клавиатуру к отладочной плате LimeSTM32:

Подключение qwerty-клавиатуры к отладочной плате

Для питания клавиатуре нужно 3.3 В. Подсветка не такая яркая, чтобы ее было хорошо видно на фото. Однако в темноте свою функцию она выполняет прекрасно. Кнопки соединены по матричной схеме, 7 строк на 5 столбцов. Подавая высокое напряжение на один столбец и считывая напряжение со строк, мы можем определить, какая кнопка сейчас нажата.

Пример кода, использующего таймер и прерывания:

void keyboard_prepare() { HAL_GPIO_WritePin(Col1_GPIO_Port, Col1_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(Col2_GPIO_Port, Col2_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(Col3_GPIO_Port, Col3_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(Col4_GPIO_Port, Col4_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(Col5_GPIO_Port, Col5_Pin, GPIO_PIN_RESET); } char* buttons[][5] = { /* ROW 1 */ {"q", "e", "r", "u", "o"}, /* ROW 2 */ {"w", "s", "g", "h", "l"}, /* ROW 3 */ {"SYM", "d", "t", "y", "i"}, /* ROW 4 */ {"a", "p", "RSHIFT", "ENTER", "DEL"}, /* ROW 5 */ {"ALT", "x", "v", "b", "$"}, /* ROW 6 */ {" ", "z", "c", "n", "m"}, /* ROW 7 */ {"MIC", "LSHIFT", "f", "j", "k"}, }; char* buttons_alt[][5] = { /* ROW 1 */ {"#", "2", "3", "_", "+"}, /* ROW 2 */ {"1", "4", "/", ":", "\""}, /* ROW 3 */ {NULL, "5", "(", ")", "-"}, /* ROW 4 */ {"*", "@", NULL, NULL, NULL}, /* ROW 5 */ {NULL, "8", "?", "!", NULL}, /* ROW 6 */ {NULL, "7", "9", ",", "."}, /* ROW 7 */ {"0", NULL, "6", ";", "'"}, }; // button should be released before // pressing next button bool btn_released = true; bool shift_pressed = false; bool alt_pressed = false; uint32_t line_length = 0; void button_pressed(uint8_t row, uint8_t col) { char buff[16]; if(alt_pressed && (buttons_alt[row-1][col-1] != NULL)) { strncpy(buff, buttons_alt[row-1][col-1], sizeof(buff)); } else { strncpy(buff, buttons[row-1][col-1], sizeof(buff)); } if((strcmp(buff, "LSHIFT") == 0) || (strcmp(buff, "RSHIFT") == 0)) { shift_pressed = !shift_pressed; alt_pressed = false; } else if(strcmp(buff, "DEL") == 0) { if(line_length > 0) { UART_Printf("\x08 \x08"); line_length--; shift_pressed = false; alt_pressed = false; } } else if(strcmp(buff, "ALT") == 0) { alt_pressed = true; shift_pressed = false; // ULED will blink once. Since shift_pressed is false it will // be turned off shortly. HAL_GPIO_WritePin(ULED_GPIO_Port, ULED_Pin, GPIO_PIN_SET); } else if((strcmp(buff, "MIC") == 0) || (strcmp(buff, "SYM") == 0)) { // ignore } else if(strcmp(buff, "ENTER") == 0) { UART_Printf("\r\n"); line_length = 0; shift_pressed = false; alt_pressed = false; } else { // regular button if(shift_pressed && isalpha(buff[0])) { buff[0] = (char)toupper(buff[0]); } UART_Printf("%s", buff); line_length++; shift_pressed = false; alt_pressed = false; } } void set_current_column(uint8_t column) { switch(column) { case 1: HAL_GPIO_WritePin(Col5_GPIO_Port, Col5_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(Col1_GPIO_Port, Col1_Pin, GPIO_PIN_SET); break; case 2: HAL_GPIO_WritePin(Col1_GPIO_Port, Col1_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(Col2_GPIO_Port, Col2_Pin, GPIO_PIN_SET); break; case 3: HAL_GPIO_WritePin(Col2_GPIO_Port, Col2_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(Col3_GPIO_Port, Col3_Pin, GPIO_PIN_SET); break; case 4: HAL_GPIO_WritePin(Col3_GPIO_Port, Col3_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(Col4_GPIO_Port, Col4_Pin, GPIO_PIN_SET); break; default: HAL_GPIO_WritePin(Col4_GPIO_Port, Col4_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(Col5_GPIO_Port, Col5_Pin, GPIO_PIN_SET); break; } } uint8_t current_column = 1; bool some_button_pressed_during_scan = false; bool button_was_released = true; // called from HAL_TIM_PeriodElapsedCallback void change_column() { current_column++; if(current_column > 5) { button_was_released = !some_button_pressed_during_scan; current_column = 1; some_button_pressed_during_scan = false; } set_current_column(current_column); } // called from HAL_GPIO_EXTI_Callback void row_selected(uint16_t pin) { if(some_button_pressed_during_scan) { // process only one button per complete scan return; } some_button_pressed_during_scan = true; if(!button_was_released) { // if user holds a button don't process it multiple times return; } if(pin == GPIO_PIN_0) { button_pressed(1, current_column); } else if(pin == GPIO_PIN_1) { button_pressed(2, current_column); } else if(pin == GPIO_PIN_2) { button_pressed(3, current_column); } else if(pin == GPIO_PIN_3) { button_pressed(4, current_column); } else if(pin == GPIO_PIN_4) { button_pressed(5, current_column); } else if(pin == GPIO_PIN_5) { button_pressed(6, current_column); } else if(pin == GPIO_PIN_6) { button_pressed(7, current_column); } } void init() { keyboard_prepare(); HAL_TIM_Base_Start_IT(&htim3); UART_Printf("Ready!\r\n"); HAL_Delay(1); } void loop() { HAL_GPIO_WritePin(ULED_GPIO_Port, ULED_Pin, shift_pressed ? GPIO_PIN_SET : GPIO_PIN_RESET); HAL_Delay(100); }

Прошу принять во внимание, что здесь в обработчике прерывания используется блокирующая передача по UART. В боевом коде вы этого, вероятно, не хотите. Также в полной версии исходников к этому посту вы найдете пример кода без прерываний, который просто крутится в цикле.

Ну вот, теперь берем ILI9341, добавляем слот для SD-карты, GSM-модуль, микрофон, динамик, и получаем DIY мобильный телефон! А какие потрясающие применения данной клавиатуре приходят вам на ум?

Дополнение: В продолжение темы – Как я паял маленькую qwerty-клавиатуру.