单片机/MCUwilliam hill官网
直播中

jf_56450220

未满1年用户 14经验值
擅长:嵌入式技术
私信 关注

【xG24 Matter开发套件试用体验】物联网密码柜之驱动矩阵键盘和OLED显示器

简介

笔者在提交试用申请时填写的项目计划是制作一个物联网密码柜,本阶段的主要目标是驱动矩阵键盘和Oled显示器,为后续完整的物联网密码柜项目打下基础。采用Thonny编辑器+circuitpython进行开发,矩阵键盘驱动为自行编写,Oled驱动基于Adafruit SSD_1306库。

到本阶段为止已实现的功能:

  • 矩阵键盘的输入与识别
  • 密码校验
  • 边缘检测及错误处理
  • SSD1306显示器的驱动

后续需要进行完善的功能:

  • 物联网密码柜其它外设的驱动
  • 蓝牙服务的驱动,实现物联网

circuitpython固件的刷入及编辑器的设置

本次开发使用circuitpython进行开发,circuitpython开发便捷并且有众多开源库可以参考。

首先在circuitpython官网找到EFR32xG24 Explorer Kit的固件进行下载
Snipaste_2024-08-04_22-17-49.jpg


下载完成后,再使用Silicon Labs的官方软件Simplicity Commander将circuitpython固件刷入开发板中,刷固件前记得先擦除整片flash
Snipaste_2024-08-04_22-22-21.jpg


circuitpython固件刷写完成之后,保持开发板与电脑的连接,打开Thonny编辑器,选择circuitpython为解释器并选择开发板连接的串口
Snipaste_2024-08-04_22-27-00.jpg


然后将有关代码提示的设置打开,方便后续开发
Snipaste_2024-08-04_22-28-30.jpg


矩阵键盘的驱动

矩阵键盘的实现原理并不难,就是逐行扫描,由于硬件和成本的局限性和对实际密码输入场景的考量,这里仅考虑单一按键同时按下的情况(如果有多个按键同时按下,则按扫描顺序返回第一个按键)。

核心业务代码如下:

def scan_keyboard(self):
        """扫描矩阵键盘并返回按下的按键(row, col),如果按键已被按下则不重复返回"""
        # 逐行扫描
        for i, row in enumerate(self.rows):
            # 当前行IO拉低
            row.value = False
            # 逐列检查
            for j, col in enumerate(self.cols):
                current_key = (i, j)
                # 按键按下
                if not col.value:
                    time.sleep(0.01) #延迟10ms消抖
                    if not col.value:
                        # 重新拉高行IO
                        row.value = True
                        if current_key != self.pressed_key:
                            self.pressed_key = current_key
                            return current_key
                # 按键释放
                else:
                    # 若当前按键在记录中,清空已按下的按键记录
                    if current_key == self.pressed_key:
                        self.pressed_key = None 
            # 重新拉高行IO
            row.value = True
        # 没有按键按下
        return None

Oled屏幕的驱动

笔者所购0.96英寸Oled屏幕为SSD1315,对应的驱动实在难找,好在SSD1306与SSD1315十分相似,于是选择使用Adafruit SSD_1306驱动库进行驱动。

首先进入到circuitpython官网的文档库,找到SSD_1306有关的驱动
Snipaste_2024-08-04_22-40-48.jpg


笔者这里使用基于framebuf的驱动库,可以看到这个驱动库需要先安装framebuf驱动库和bus device驱动库(circuitpython固件已内置),点击左侧的“Download from Github”进入下载链接
Snipaste_2024-08-04_22-42-13.jpg


同理找到Framebuf Module驱动库,按照上述流程进行下载
Snipaste_2024-08-04_22-45-41.jpg


下载完成后解压压缩包,将压缩包内lib文件夹下的py文件上传至开发板的lib文件夹
Snipaste_2024-08-04_22-48-16.jpg


framebuf驱动库下的examples文件夹下有一个font5x8.bin,这是framebuf依赖的字符库,需要一同上传到lib文件夹下(在哪里调用就上传到哪)

示例代码如下:

def text(self, string, x, y, color, *, font_name="font5x8.bin", size=1):
        """Place text on the screen in variables sizes. Breaks on \\\\\\\\n to next line.

        Does not break on line going off screen.
        """
        # determine our effective width/height, taking rotation into account
        frame_width = self.width
        frame_height = self.height
        if self.rotation in (1, 3):
            frame_width, frame_height = frame_height, frame_width

        for chunk in string.split("\\\\\\\\n"):
            if not self._font or self._font.font_name != font_name:
                # load the font!
                self._font = BitmapFont(font_name)
            width = self._font.font_width
            height = self._font.font_height
            for i, char in enumerate(chunk):
                char_x = x + (i * (width + 1)) * size
                if (
                    char_x + (width * size) > 0
                    and char_x < frame_width
                    and y + (height * size) > 0
                    and y < frame_height
                ):
                    self._font.draw_char(char, char_x, y, self, color, size=size)
            y += height * size

依赖库准备完成后,定义oled显示方法,方便主程序调用

import board
import busio
import adafruit_ssd1306

class OledManager:
    # 初始化方法
    def __init__(self, width, height, i2c):
        self.oled = adafruit_ssd1306.SSD1306_I2C(width, height, i2c)
        self.bgColor = 0 #黑色背景
        
    # 定义清空屏幕方法
    def clear(self):
        self.oled.fill(self.bgColor)
    
    # 定义显示文本方法(第一行)
    def show_text(self, text):
        self.oled.text(text, 0, 0, not self.bgColor, font_name='font5x8.bin', size=1)
        self.oled.show()

    # 定义显示密码方法(第二行)
    def show_star(self, num):
        self.oled.text("*"*num, 0, 8, not self.bgColor, font_name='font5x8.bin', size=1)
        self.oled.show()
        
    # 定义显示结果方法(第三行)
    def show_result(self, result=False):
        if(result):
            self.oled.text("Right!", 0, 16, not self.bgColor, font_name='font5x8.bin', size=2)
        else:
            self.oled.text("Wrong!", 0, 16, not self.bgColor, font_name='font5x8.bin', size=2)
        self.oled.show()

由于笔者对oled的使用不太了解,加上又是第一次使用circuitpython进行开发,所以这里将密码校验的相关部分显示在哪个位置全部定死了,后续可能会考虑进行优化


密码校验核心业务代码

# 检查密码是否正确并返回结果
def check_pwd(password):
    return password == saved_password

def handle_input(key, password):
    """处理密码输入逻辑"""  
    if key==(3, 1):  # 回退键
        password = password[:-1]  # 删除最后一位
        return password
    # 根据键盘布局添加适当的字符
    if len(password) < PASSWORD_MAX_LENGTH:
        password += keyboard_char_map.get(key, '')
    return password

def display_tip(oled, tip="Password:"):
    oled.show_text(tip)

def display_password(oled, password):
    oled.clear()
    display_tip(oled)
    oled.show_star(len(password))

def display_result(oled, result):
    oled.clear()
    display_tip(oled)
    oled.show_result(result)

主程序核心业务代码

def main():
    global password
    
    # 初始化显示
    display_tip(oled)
    
    while True:
        # 扫描矩阵键盘
        key = keyboard.scan_keyboard()
        # 如果有按键按下
        if key:
            # 确认键
            if key==(3,2):
                display_result(oled, result=check_pwd(password))
                password = ""  # 清空密码
            # 处理按下的按键
            else:
                password = handle_input(key, password)
                display_password(oled, password)   
        time.sleep(0.01)  # 防止CPU占用过高

阶段性总结

本次使用circuitpython实现了对矩阵键盘和Oled显示屏的驱动以及密码校验部分,为后续物联网密码柜的制作打下了基础。

最后放一个密码输入演示视频。

密码输入演示

更多回帖

发帖
×
20
完善资料,
赚取积分