4x4 矩阵键盘

罗大富 BigRich大约 6 分钟ESP32Python

今天我们来学习 4x4 矩阵键盘,并最终实现一个密码开锁的功能。

实验原理

薄膜按键,是一块带触点的 PET 薄片,用在 PCB、FPC 等线路上作为开关使用,在使用者与仪器之间起到一个重要的触感型开关的作用。与传统的硅胶按键相比,薄膜按键具有更好的手感、更长的寿命,可以间接的提高使用导电膜的各类型开关的生产效率。

薄膜按键的工作原理很好理解,薄膜上的触点位于 PCB 板上的导电部位,当按键受到外力按压时,触点的中心点下凹,接触到 PCB 上的线路,从而形成回路,电流通过,整个产品就得正常工作。

这个键盘有 16 个按键,如果 16 个按键均为独立按键的话,需要占用 16 个 IO 口,对于我们的开发板来说还是可以接受的,但是如果有 64 个按键,那单片机的 IO 口就完全不能能满足我们的需求,因此,就出现了矩阵键盘将这 8 根线连接到单片机的 8 个 IO 口上,通过程序扫描键盘就可检测 16 个键,如果我们想实现 64 个按键的话就只需要用到 16 个 IO口,可以参考 LED 点阵屏

无论是独立键盘还是矩阵键盘,单片机检测其是否被按下的依据都一样,即检测与该键对应的 IO 口是否为低电平,独立键盘有一端固定为低电平,此种方式编程比较简单。而矩阵键盘两端都与单片机 IO 口相连,因此在检测时需编程通过单片机1/0口送出低电平,检测方法有多种,最常用的是 行列扫描线翻转法

  1. 行列扫描法:检测时,先送一列为低电平,其余几列全为高电平(确定列数),然后立即轮流检测一次各行是否有低电平,若检测到某一行为低电平(确定行数),则便可确认当前被按下的键是哪一行哪一列的,用同样方法轮流送各列一次低电平,再轮流检测一次各行是否变为低电平,这样即可检测完所有的按键,当有键被按下时便可判断出按下的键是哪一个键。当然,也可以将行线置低电平,扫描列是否有低电平,从而达到整个键盘的检测;
  2. 线翻转法:使所有行线为低电平时,检测所有列线是否有低电平,如果有,就记录列线值:然后再翻转,使所有列线都为低电平,检测所有行线的值,由于有按键按下,行线的值也会有变化,记录行线的值。从而就可以检测到全部按键

硬件电路设计

物料清单(BOM 表):

材料名称数量
4*4 矩阵键盘1
LCD1602 液晶屏1
杜邦线(跳线)若干
面包板1

矩阵键盘从左到右依次接 D19、D18、D5、D17、D16、D4、D2、D15。

LCD 1602 接 5V 电源,SCL 接 D27,SDA 接 D14。

软件程序设计

这里我们需要用到 Python 的一个内置函数 enumerate()

enumerate() 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中。

以下是 enumerate() 方法的语法:enumerate(sequence, [start=0]),其中的参数:

  • sequence:一个序列、迭代器或其他支持迭代对象;
  • start:下标起始位置。

用法如下:

seasons = ['Spring', 'Summer', 'Fall', 'Winter']

for i, ele in enumerate(seasons):
    print(i, ele)

这样,我们就能既获取到索引值 i,也能获取到元素 ele

因此,我们可以先把按键值打印在 Shell 命令行中,代码如下:

import time
from machine import Pin


# 行引脚设置为输入
row_pins = [Pin(19, Pin.IN, Pin.PULL_UP),
            Pin(18, Pin.IN, Pin.PULL_UP),
            Pin(5, Pin.IN, Pin.PULL_UP),
            Pin(17, Pin.IN, Pin.PULL_UP)]

# 列引脚设置为输出
col_pins = [Pin(16, Pin.OUT),
            Pin(4, Pin.OUT),
            Pin(2, Pin.OUT),
            Pin(15, Pin.OUT)]


def read_keypad():
    keys = [
        ['1', '2', '3', 'A'],
        ['4', '5', '6', 'B'],
        ['7', '8', '9', 'C'],
        ['*', '0', '#', 'D']
    ]

    for j, col_pin in enumerate(col_pins):
        col_pin.value(0)  # 将当前列设置为低电平
        for i, row_pin in enumerate(row_pins):
            if row_pin.value() == 0:  # 检测行引脚的状态
                # 将当前列恢复为高电平
                col_pin.value(1)	
                return keys[i][j]  # 返回按下的按键
        col_pin.value(1)  # 将当前列恢复为高电平

    return None  # 没有按键被按下


# 循环读取键盘状态
while True:
    key = read_keypad()
    if key is not None:
        print("按下的按键:", key)
    time.sleep(0.1)  # 短暂延迟

最后,我们就可以再使用 LCD1602 屏幕显示我们输入的内容,并且校验输入的密码是否正确,代码如下:

import time
from machine import Pin, SoftI2C
from libs.i2c_lcd import I2cLcd


# 行引脚设置为输入
row_pins = [Pin(19, Pin.IN, Pin.PULL_UP),
            Pin(18, Pin.IN, Pin.PULL_UP),
            Pin(5, Pin.IN, Pin.PULL_UP),
            Pin(17, Pin.IN, Pin.PULL_UP)]

# 列引脚设置为输出
col_pins = [Pin(16, Pin.OUT),
            Pin(4, Pin.OUT),
            Pin(2, Pin.OUT),
            Pin(15, Pin.OUT)]


# 定义硬件 I2C 控制对象
i2c = SoftI2C(sda=Pin(14), scl=Pin(27), freq=100000)

# 获取 I2C 设备地址
address = i2c.scan()[0]

# 定义 I2cLcd 对象
i2c_lcd = I2cLcd(i2c, address, 2, 16)

def read_keypad():
    keys = [
        ['1', '2', '3', 'A'],
        ['4', '5', '6', 'B'],
        ['7', '8', '9', 'C'],
        ['*', '0', '#', 'D']
    ]

    for j, col_pin in enumerate(col_pins):
        col_pin.value(0)  # 将当前列设置为低电平
        for i, row_pin in enumerate(row_pins):
            if row_pin.value() == 0:  # 检测行引脚的状态
                # 将当前列恢复为高电平
                col_pin.value(1)
                return keys[i][j]  # 返回按下的按键
        col_pin.value(1)  # 将当前列恢复为高电平

    return None  # 没有按键被按下

# 正确密码
password = '123456'
# 显示的密码
pwd_input = ''

i2c_lcd.putstr('Enter Password:\n')

# 循环读取键盘状态
while True:
    
    key = read_keypad()
    if key is not None:
        # 重置屏幕内容
        i2c_lcd.clear()
        i2c_lcd.putstr('Enter Password:\n')
        if key == 'D' and pwd_input != '':
            pwd_input = pwd_input[:-1]
        elif key == '*':
            # 校验密码
            i2c_lcd.clear()
            i2c_lcd.putstr('Enter Password:\n')
            print(f'pwd_input: {pwd_input}, pwd: {password}')
            if pwd_input == password:
                i2c_lcd.putstr('Correct!')
            else:
                i2c_lcd.putstr('Wrong!')
            # 重置 pwd_input
            pwd_input = ''
        # 非特殊按键则显示在屏幕上
        elif key != 'D' and key !='*' and len(pwd_input) < 15:
            pwd_input += key
        i2c_lcd.putstr(pwd_input)
    time.sleep(0.2)  # 短暂延迟
上次编辑于:
贡献者: Luo