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

16 октября 2018

После знакомства с клавиатурой от смартфона 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. Как обычно, буду рад вашим вопросам и дополнениям.

Метки: , .

Понравился пост? Узнайте, как можно поддержать развитие этого блога.

Также подпишитесь на RSS, Facebook, ВКонтакте, Twitter или Telegram.