← На главную

Как я паял маленькую qwerty-клавиатуру

После знакомства с клавиатурой от смартфона BlackBerry Q10 мне захотелось кое-что выяснить. Что будет, если взять от этой клавиатуры только пластиковые клавиши, а саму плату с кнопками развести самостоятельно? В этом случае не пришлось бы возиться с крохотными коннекторами, через которые подключается оригинальная клавиатура. Также мы были бы уверены в качестве модуля. А то эти клавиатуры выдирают из старых телефонов, и кто знает, в каком они там состоянии (залипающие кнопки и т.п.).

Были использованы следующие компоненты:

Плата была разведена в KiCad. Первая версия платы не использовала земельные полигоны и была вытравлена в домашних условиях. Оказалась, что в таком виде плата очень чувствительна к наводкам. Я постоянно получал ложные считывания, просто проводя пальцем над модулем. Поэтому следующая версия платы была сделана двусторонней, с большими земельными полигонами с обеих сторон. На этот раз плату было решено заказать на OSH Park.

Вот что получилось в итоге:

Маленькая самодельная qwerty-клавиатура

Тот же модуль, но с пластиковыми клавишами, посаженными на термоклей:

DIY qwerty-клавиатура с пластиковыми клавишами

Пример кода:

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); HAL_GPIO_WritePin(Col6_GPIO_Port, Col6_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(Col7_GPIO_Port, Col7_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(Col8_GPIO_Port, Col8_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(Col9_GPIO_Port, Col9_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(Col10_GPIO_Port, Col10_Pin, GPIO_PIN_RESET); } void change_column(uint8_t column) { switch(column) { case 0: HAL_GPIO_WritePin(Col10_GPIO_Port, Col10_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(Col1_GPIO_Port, Col1_Pin, GPIO_PIN_SET); break; case 1: HAL_GPIO_WritePin(Col1_GPIO_Port, Col1_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(Col2_GPIO_Port, Col2_Pin, GPIO_PIN_SET); break; case 2: HAL_GPIO_WritePin(Col2_GPIO_Port, Col2_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(Col3_GPIO_Port, Col3_Pin, GPIO_PIN_SET); break; case 3: HAL_GPIO_WritePin(Col3_GPIO_Port, Col3_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(Col4_GPIO_Port, Col4_Pin, GPIO_PIN_SET); break; case 4: HAL_GPIO_WritePin(Col4_GPIO_Port, Col4_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(Col5_GPIO_Port, Col5_Pin, GPIO_PIN_SET); break; case 5: HAL_GPIO_WritePin(Col5_GPIO_Port, Col5_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(Col6_GPIO_Port, Col6_Pin, GPIO_PIN_SET); break; case 6: HAL_GPIO_WritePin(Col6_GPIO_Port, Col6_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(Col7_GPIO_Port, Col7_Pin, GPIO_PIN_SET); break; case 7: HAL_GPIO_WritePin(Col7_GPIO_Port, Col7_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(Col8_GPIO_Port, Col8_Pin, GPIO_PIN_SET); break; case 8: HAL_GPIO_WritePin(Col8_GPIO_Port, Col8_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(Col9_GPIO_Port, Col9_Pin, GPIO_PIN_SET); break; default: // 9 HAL_GPIO_WritePin(Col9_GPIO_Port, Col9_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(Col10_GPIO_Port, Col10_Pin, GPIO_PIN_SET); } } bool keyboard_read(uint8_t* out_row, uint8_t* out_col) { keyboard_prepare(); for(uint8_t col = 0; col < 10; col++) { change_column(col); if(HAL_GPIO_ReadPin(Row1_GPIO_Port, Row1_Pin) == GPIO_PIN_SET) { *out_col = col; *out_row = 0; return true; } else if(HAL_GPIO_ReadPin(Row2_GPIO_Port, Row2_Pin) == GPIO_PIN_SET) { *out_col = col; *out_row = 1; return true; } else if(HAL_GPIO_ReadPin(Row3_GPIO_Port, Row3_Pin) == GPIO_PIN_SET) { *out_col = col; *out_row = 2; return true; } else if(HAL_GPIO_ReadPin(Row4_GPIO_Port, Row4_Pin) == GPIO_PIN_SET) { *out_col = col; *out_row = 3; return true; } } return false; } void init() { UART_Printf("Ready!\r\n"); HAL_Delay(1); } static uint32_t total_clicks = 0; void loop() { uint8_t row, col; if(keyboard_read(&row, &col)) { // discard impossible reads which sometimes // can happen because of noise RF signals // or because of accidental touch of button contacts if((row <= 2) || ((row == 3) && (col <= 4))) { total_clicks++; UART_Printf("row = %d, col = %d\r\n", row, col); UART_Printf("clks = %ld\r\n", total_clicks); } } HAL_Delay(10); }

В исходниках к посту вы также найдете версию кода, использующую прерывания и таймеры.

Оказывается, это работает. Правда, в отличие от оригинала, такая клавиатура не имеет подсветки. Также она работает более шумно, чем ваниальная клавиатура от BlackBerry Q10, и клавиши у нее не такие мягкие. Следует также учитывать, что наивный код на основе прерываний будет иметь ложные срабатывания из-за наводок, хотя они и случаются редко. Для решения этой проблемы нужно, к примеру, учитывать время, в течение которого кнопка была нажата, отбрасывая слишком короткие нажатия.

Не могу сказать, что модуль существенно лучше или хуже оригинальной клавиатуры. Оба модуля имеют свои плюсы и минусы. Но если сомневаетесь, лучше заказать три оригинальные клавиатуры из разных магазинов. Благо, они недорогие. Тогда одна из клавиатур почти наверняка окажется неплохой (а возможно, и все три). Также вы получите мягкие клавиши, подсветку и более стабильную работу при использовании прерываний. А время, которое уйдет на возню с маленьким коннектором, скорее всего окажется меньше времени, требуемого на впаивание 35 кнопок.

Полную версию исходников вы найдете в этом репозитории на GitHub. Как обычно, буду рад вашим вопросам и дополнениям.