После знакомства с клавиатурой от смартфона BlackBerry Q10 мне захотелось кое-что выяснить. Что будет, если взять от этой клавиатуры только пластиковые клавиши, а саму плату с кнопками развести самостоятельно? В этом случае не пришлось бы возиться с крохотными коннекторами, через которые подключается оригинальная клавиатура. Также мы были бы уверены в качестве модуля. А то эти клавиатуры выдирают из старых телефонов, и кто знает, в каком они там состоянии (залипающие кнопки и т.п.).
Были использованы следующие компоненты:
- SMD-кнопки 4 x 4 x 1.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 . Как обычно, буду рад вашим вопросам и дополнениям.