Как я паял маленькую qwerty-клавиатуру
16 октября 2018
После знакомства с клавиатурой от смартфона BlackBerry Q10 мне захотелось кое-что выяснить. Что будет, если взять от этой клавиатуры только пластиковые клавиши, а саму плату с кнопками развести самостоятельно? В этом случае не пришлось бы возиться с крохотными коннекторами, через которые подключается оригинальная клавиатура. Также мы были бы уверены в качестве модуля. А то эти клавиатуры выдирают из старых телефонов, и кто знает, в каком они там состоянии (залипающие кнопки и т.п.).
Были использованы следующие компоненты:
- SMD-кнопки 4x4x1.5 мм, которые продают по 1.5$ за 100 штук;
- Четыре резистора на 10 кОм размера 1206;
- Flexible Flat Cable (FFC), 20 дорожек с шагом 1 мм;
- Соответствующий коннектор для гибкого плоского кабеля;
- Также для подключения к отладочной плате был куплен переходник с коннектора на пины с шагом 2.54 мм;
Плата была разведена в KiCad. Первая версия платы не использовала земельные полигоны и была вытравлена в домашних условиях. Оказалась, что в таком виде плата очень чувствительна к наводкам. Я постоянно получал ложные считывания, просто проводя пальцем над модулем. Поэтому следующая версия платы была сделана двусторонней, с большими земельными полигонами с обеих сторон. На этот раз плату было решено заказать на OSH Park.
Вот что получилось в итоге:
Тот же модуль, но с пластиковыми клавишами, посаженными на термоклей:
Пример кода:
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. Как обычно, буду рад вашим вопросам и дополнениям.
Метки: STM32, Электроника.
Вы можете прислать свой комментарий мне на почту, или воспользоваться комментариями в Telegram-группе.