昉·星光系列课程2:昉·星光开发板按键处理

课程2:昉·星光开发板按键处理

本次课程为昉·星光开发板IoT开发系列课程的第二讲,将带领同学们在昉·星光开发板上,通过GPIO进行按键处理。

一、学习目标

  • 学习昉·星光开发板GPIO输入和普通按键处理
  • 学习昉·星光开发板上矩阵键盘的处理

二、准备工作

在开始本次课程的实际操作之前,同学们需要做好一些准备工作,课程中涉及到的硬件如下:

  • 开发板:昉·星光开发板
  • LED:单色LED模块
  • 按键与键盘:
    • 普通按键
    • 薄膜键盘
  • 杜邦线:若干

三、Ubuntu系统设备权限设置

在第一讲中,当运行GPIO相关的指令,或者运行Python程序时,都使用到了sudo来取得root权限运行。

大多数时候,我们希望在普通用户环境下,也能编程控制GPIO设备。

在Ubuntu系统中,可以通过设置合适的udev规则,来使得普通用户也可以得到所需的权限。

GPIO设备的udev权限设置如下:

# 添加gpio用户组

sudo groupadd -f -r gpio

# 将当前用户添加到gpio组

sudo usermod -a -G gpio $USER

# 设置gpio的udev权限规则

sudo nano /etc/udev/rules.d/99-gpio.rules

# 将下面的内容添加到文件中

SUBSYSTEM=="gpio", KERNEL=="gpiochip*", ACTION=="add", RUN+="/bin/chgrp -R gpio /sys/class/gpio/export /sys/class/gpio/unexport /dev/gpiochip0 /dev/gpiochip1", RUN+="/bin/chmod -R u+rw,g=u /sys/class/gpio/export /sys/class/gpio/unexport /dev/gpiochip0 /dev/gpiochip1"

SUBSYSTEM=="gpio", KERNEL=="gpio*", ACTION=="add", RUN+="/bin/chgrp -R gpio /sys%p", RUN+="/bin/chmod -R u+rw,g=u /sys%p", RUN+="/bin/chmod -R u+rw,g=u /sys%p/value"

SUBSYSTEM=="gpio", KERNEL=="gpio*", ACTION=="change", ENV{TRIGGER}!="none", RUN+="/bin/chgrp -R gpio /sys%p", RUN+="/bin/chmod -R u+rw,g=u /sys%p", RUN+="/bin/chmod -R u+rw,g=u /sys%p/value"

# 重置udev规则使其生效

sudo udevadm control --reload-rules

# 查看规则:列出文件的用户和组均为root gpio

ls -lh /dev/gpiochip* /sys/class/gpio/{export,unexport}

# 如有可能,重启生效更彻底

sudo reboot

四、普通按键处理

在第一讲中,我们通过GPIO控制LED,是通过GPIO输出高低电平来控制的。

而要使用按键,则需要通过GPIO输入信息。通常按键会有按下时候的电平,本次课程实验准备的按键模块,是按下低电平。

我们需要获取按键的状态,并根据状态来设置LED,如果按下按键,则点亮LED,如果松开按键,则熄灭LED。

首先,参考下图,将单色LED模块和按键模块,连接到昉·星光开发板:

实物连接如下:

然后,编写如下的程序:

# -*- coding: utf-8 -*-
# file: ~/projects/key/button.py
import time

import gpio as GPIO

LED = 448
BUTTON = 450

GPIO.setup(LED, GPIO.OUT)
GPIO.setup(BUTTON, GPIO.IN)

value_prev = 1
GPIO.output(LED, GPIO.LOW)
while True:
    value = GPIO.input(BUTTON)
    if not value == value_prev:
        value_prev = value
        if value == 1:
            GPIO.output(LED, GPIO.LOW)
        else:
            GPIO.output(LED, GPIO.HIGH)
    time.sleep(0.1)

上述程序的逻辑较为简单,就是检测按键状态,然后为高电平(松开)则熄灭LED,为低电平(按下)则点亮LED。

编写完成后,运行 button.py ,即可按键控制LED了。

# 如果遇到错误,再次运行,直到正常运行不出错为止:

python3 button.py

五、矩阵键盘处理

除了通常的普通按键以外,常用的按键还有ADC按键和矩阵按键。ADC按键通过检测电压值来区分不同的按键,矩阵按键则通常通过逐行扫描的方式来检测按键。

因为昉·星光开发板上没有ADC模块,需要外接转接板才能使用ADC按键,因此本次课程就先不讲ADC按键了。

矩阵按键仅需要足够的GPIO口,就可以进行检测。昉·星光开发板有40Pin,足够使用了。

矩阵键盘的原理并不是很复杂,通常一个4x4的矩阵键盘结构如下:

矩阵键盘通过上线路(列)和下线路(行)的联通,来进行按键的区分检测。

线路的默认电平都为低电平。每次检测时,先将上线路4条线路中的第1条线路设置为高电平,然后马上检测下线路4条线路的电平状态。如果检测到下线路某条线为高电平,说明这条线路,与上线路第1条线的交叉点联通,从而确定具体的按键位置。如果没有检测到高电平,则将上线路4条线路中的第2条线路设置为高电平,然后重复下线路检测过程。直到检测完成。因为检测的速度很快,所以可以较为准确的确定具体的按键。

这样检测的好处在于,只需要n+m根线,即可检测n*m个按键,极大了减少了开发板上GPIO口的占用。

注意:在有的资料中,会选择默认高电平,检测低电平的方式;另外,上下线路原则上,是可以互换的,只需要对好逻辑关系即可。

了解以上原理以后,我们就可以开始实际操作了。参考下图,将矩阵键盘连接到昉·星光开发板上:

连好线后的实物如下:

为了方便操作,把连接线压到了昉·星光开发板的下面。

之前我们使用的GPIO模块,较为基础,不适合复杂的GPIO操作。要完成矩阵模块的检测,我们需要使用更为高级的gpiod模块,通过如下指令安装:

# 安装系统gpiod工具

sudo apt install gpiod

# 安装Python的gpiod模块

sudo pip install gpiod

# 查看系统gpio处理设备:

sudo gpiodetect

# 查看gpio设备:

sudo gpioinfo

通过gpiodetect和gpioinfo,可以查看到昉·星光开发板上的GPIO设备情况。
gpiod比之前通过shell或者python3-gpio操作更方便,引脚与设备号之间,不需要转换,40Pin上的GPIO0,在gpiod里面,直接就对应0号,下面要编写的程序中就能体现。

安装完成后,编写如下的程序:

# -*- coding: utf-8 -*-
# file: ~/projects/key/keypad.py
import sys
from datetime import timedelta
from gpiod import chip, line_request, line_event
import time

# 设置按键键码
KEYPAD = [
        ["1","2","3","A"],
        ["4","5","6","B"],
        ["7","8","9","C"],
        ["*","0","#","D"]
]

# 设置行列对应的GPIO引脚
ROW_PINS = [0,1,2,3] # 控制行
COL_PINS = [4,6,8,9] # 控制列

# 使用gpiochip0,其为40Pin的gpio处理设备
c = chip(0)

# 设置控制行输出模式
config = line_request()
config.request_type = line_request.DIRECTION_OUTPUT

# 设置4个控制行
pins = [None, None, None, None]
for i in range(len(ROW_PINS)):
    # 设为输出口
    pins[i] = c.get_line(ROW_PINS[i])
    pins[i].request(config)
    pins[i].set_value(0)

# 设置控制列为输入:上升沿触发配置,下拉模式
buttons = c.get_lines(COL_PINS)
config = line_request()
config.request_type = line_request.EVENT_RISING_EDGE
config.flags = line_request.FLAG_BIAS_PULL_DOWN

# 设置按键输入
for i in range(buttons.size):
    config.consumer = "{}".format(i)
    buttons[i].request(config)

# 扫描按键
def scanLine(line,characters):
    # 检测开始,设置高电平
    line.set_value(1)

    # 检测按键
    lines = buttons.event_wait(timedelta(seconds=0.05))
    if not lines.empty:
        for it in lines:
            event = it.event_read()
            # 检测触发状态            
            if event.event_type == line_event.RISING_EDGE:
                # 输出对应的按键
                print("Key: ", characters[int(it.consumer)])

    # 检测完毕,设置低电平
    line.set_value(0)

try:
    print("\nKeyPad detector start:")
    while True:
        # 依次扫描
        for i in range(len(ROW_PINS)):
            scanLine(pins[i], KEYPAD[i])
except KeyboardInterrupt:
    print("\nKeyPad detector stopped!")

编写完程序后,运行该程序,并进行按键测试,即可收到如下的输出结果:

六、总结

在本次课程中,我们学习了通过GPIO来读取普通按键的状态,并进一步学习了矩阵按键的处理。

这两种按键,在IoT开发中,都很常用,用于使用者的基本输入处理。

在矩阵按键处理中,使用到了gpiod模块,其使用比gpio模块要复杂,但是可以做的操作更多,可以详细了解学习。

如果要进一步学习gpiod模块的用法,可以查看网址:Python gpiod | https://wiki.loliot.net/docs/lang/python/libraries/gpiod/python-gpiod-about/

七、课后作业

  • 了解gpiod的用法
  • 编写通过矩阵按键,来控制LED的程序
  • 长按键或者短按键,观察样例程序的输出,然后尝试编程实现判断长按键和短按键
4 Likes