Как использовать клавиатуру с Raspberry Pi 4

В данном материале разберём простой способ подключения клавиатуры к Raspberry Pi 4 для различных задач.

Клавиатура с Raspberry Pi 4

Для пользователей доступно множество вариантов ввода данных в Raspberry Pi. Один из них — применение 16-кнопочной клавиатуры, содержащей цифры от нуля до девяти и несколько дополнительных кнопок:

Далее мы расскажем о принципе работы таких клавиатур и о том, как без труда интегрировать их в проекты с Raspberry Pi 4.

Клавиатура

Используемую клавиатуру можно разбить на четыре строки и четыре столбца:

Клавиатура — строки и столбцы

Для определения нажатой кнопки Raspberry Pi последовательно подаёт импульс на каждый из четырёх рядов клавиатуры. Когда пользователь нажимает кнопку, подключённую к линии, находящейся в данный момент в состоянии HIGH, соответствующий столбец также переходит в HIGH.

По комбинации строки и столбца можно определить, какая именно кнопка была нажата. К примеру, если пользователь нажимает кнопку B, расположенную во второй строке четвёртого столбца, Raspberry Pi обнаруживает нажатие в момент подачи импульса на вторую строку и последующей проверки состояния четырёх столбцов.

Подключение клавиатуры к Raspberry Pi 4

Клавиатуры подобного типа не требуют внешнего питания для работы. Достаточно просто подсоединить все восемь линий данных клавиатуры к любым восьми контактам GPIO на Raspberry Pi:

Схема подключения клавиатуры к Raspberry Pi 4

Использована та же цветовая маркировка, что и на изображении выше. Синие провода обозначают строки, а оранжевые — столбцы.

Простой пример кода

После выполнения подключений согласно приведённой выше схеме можно запустить несложную тестовую программу, которая будет выводить в консоль Raspberry Pi информацию о нажимаемых клавишах:

# GPIO setup and imports omitted

def readLine(line, characters):
    GPIO.output(line, GPIO.HIGH)
    if(GPIO.input(C1) == 1):
        print(characters[0])
    if(GPIO.input(C2) == 1):
        print(characters[1])
    if(GPIO.input(C3) == 1):
        print(characters[2])
    if(GPIO.input(C4) == 1):
        print(characters[3])
    GPIO.output(line, GPIO.LOW)

try:
    while True:
        readLine(L1, ["1","2","3","A"])
        readLine(L2, ["4","5","6","B"])
        readLine(L3, ["7","8","9","C"])
        readLine(L4, ["*","0","#","D"])
        time.sleep(0.1)
except KeyboardInterrupt:
    print("\nApplication stopped!")

Как видно, тестовая программа содержит метод readLine. Этот метод подаёт импульс на одну строку, после чего проверяет, какая из кнопок была нажата в момент, когда линия находилась в HIGH. Процедура повторяется для всех четырёх рядов. Метод также принимает список символов, соответствующих кнопкам.

Результат работы простого примера кода

Данная программа является базовым примером. Она не способна определить, удерживается ли кнопка нажатой, — при каждом отправленном импульсе фиксируется новое нажатие. Далее приводится более продвинутая программа, корректно распознающая отдельные нажатия и реализующая простую кодовую блокировку.

Результат работы продвинутого примера кода

Бюджетные клавиатуры, как правило, устроены одинаково: четыре строки и четыре столбца. Ввод определяется путём подачи импульса в каждую строку с последующей проверкой столбцов для выявления нажатой кнопки.

Продвинутый код

Базовую версию приложения реализовать несложно, однако при необходимости более продвинутого функционала код может существенно усложниться.

Пример 1

import RPi.GPIO as GPIO
import time

L1 = 5
L2 = 6
L3 = 13
L4 = 19

C1 = 12
C2 = 16
C3 = 20
C4 = 21

GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)

GPIO.setup(L1, GPIO.OUT)
GPIO.setup(L2, GPIO.OUT)
GPIO.setup(L3, GPIO.OUT)
GPIO.setup(L4, GPIO.OUT)

GPIO.setup(C1, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(C2, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(C3, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(C4, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)

def readLine(line, characters):
    GPIO.output(line, GPIO.HIGH)
    if(GPIO.input(C1) == 1):
        print(characters[0])
    if(GPIO.input(C2) == 1):
        print(characters[1])
    if(GPIO.input(C3) == 1):
        print(characters[2])
    if(GPIO.input(C4) == 1):
        print(characters[3])
    GPIO.output(line, GPIO.LOW)

try:
    while True:
        readLine(L1, ["1","2","3","A"])
        readLine(L2, ["4","5","6","B"])
        readLine(L3, ["7","8","9","C"])
        readLine(L4, ["*","0","#","D"])
        time.sleep(0.1)
except KeyboardInterrupt:
    print("\nApplication stopped!")

Пример 2

import RPi.GPIO as GPIO
import time

# These are the GPIO pin numbers where the
# lines of the keypad matrix are connected
L1 = 5
L2 = 6
L3 = 13
L4 = 19

# These are the four columns
C1 = 12
C2 = 16
C3 = 20
C4 = 21

# The GPIO pin of the column of the key that is currently
# being held down or -1 if no key is pressed
keypadPressed = -1

secretCode = "4789"
input = ""

# Setup GPIO
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)

GPIO.setup(L1, GPIO.OUT)
GPIO.setup(L2, GPIO.OUT)
GPIO.setup(L3, GPIO.OUT)
GPIO.setup(L4, GPIO.OUT)

# Use the internal pull-down resistors
GPIO.setup(C1, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(C2, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(C3, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(C4, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)

# This callback registers the key that was pressed
# if no other key is currently pressed
def keypadCallback(channel):
    global keypadPressed
    if keypadPressed == -1:
        keypadPressed = channel

# Detect the rising edges on the column lines of the
# keypad. This way, we can detect if the user presses
# a button when we send a pulse.
GPIO.add_event_detect(C1, GPIO.RISING, callback=keypadCallback)
GPIO.add_event_detect(C2, GPIO.RISING, callback=keypadCallback)
GPIO.add_event_detect(C3, GPIO.RISING, callback=keypadCallback)
GPIO.add_event_detect(C4, GPIO.RISING, callback=keypadCallback)

# Sets all lines to a specific state. This is a helper
# for detecting when the user releases a button
def setAllLines(state):
    GPIO.output(L1, state)
    GPIO.output(L2, state)
    GPIO.output(L3, state)
    GPIO.output(L4, state)

def checkSpecialKeys():
    global input
    pressed = False

    GPIO.output(L3, GPIO.HIGH)

    if (GPIO.input(C4) == 1):
        print("Input reset!");
        pressed = True

    GPIO.output(L3, GPIO.LOW)
    GPIO.output(L1, GPIO.HIGH)

    if (not pressed and GPIO.input(C4) == 1):
        if input == secretCode:
            print("Code correct!")
            # TODO: Unlock a door, turn a light on, etc.
        else:
            print("Incorrect code!")
            # TODO: Sound an alarm, send an email, etc.
        pressed = True

    GPIO.output(L3, GPIO.LOW)

    if pressed:
        input = ""

    return pressed

# reads the columns and appends the value, that corresponds
# to the button, to a variable
def readLine(line, characters):
    global input
    # We have to send a pulse on each line to
    # detect button presses
    GPIO.output(line, GPIO.HIGH)
    if(GPIO.input(C1) == 1):
        input = input + characters[0]
    if(GPIO.input(C2) == 1):
        input = input + characters[1]
    if(GPIO.input(C3) == 1):
        input = input + characters[2]
    if(GPIO.input(C4) == 1):
        input = input + characters[3]
    GPIO.output(line, GPIO.LOW)

try:
    while True:
        # If a button was previously pressed,
        # check, whether the user has released it yet
        if keypadPressed != -1:
            setAllLines(GPIO.HIGH)
            if GPIO.input(keypadPressed) == 0:
                keypadPressed = -1
            else:
                time.sleep(0.1)
        # Otherwise, just read the input
        else:
            if not checkSpecialKeys():
                readLine(L1, ["1","2","3","A"])
                readLine(L2, ["4","5","6","B"])
                readLine(L3, ["7","8","9","C"])
                readLine(L4, ["*","0","#","D"])
                time.sleep(0.1)
            else:
                time.sleep(0.1)
except KeyboardInterrupt:
    print("\nApplication stopped!")

На этом всё. Успешных проектов.