8个版本

0.2.2 2022年10月23日
0.2.1 2022年10月23日
0.1.4 2018年9月26日

#236 in 嵌入式开发

46 每月下载次数
2 crates 中使用

MIT/Apache

37KB
331 代码行

Latest version Documentation

keypad

针对键盘矩阵电路的平台无关驱动程序

此驱动程序允许您读取键盘矩阵中任何按键的状态,就像它连接到一个单输入引脚一样。它支持任何尺寸的键盘,以及实现Rust embedded-hal 特性的任何嵌入式平台。

动机

使用微控制器读取按键的最简单方法是每个按键连接到一个输入引脚。然而,如果你有的按键比可用的引脚多,那么这种方法就不可行。一个解决方案是使用键盘矩阵电路,它允许你使用N+M个引脚读取N*M个按键。

matrix

在这个电路中,每一行是一个带有上拉电阻的输入引脚,每一列是一个开漏输出引脚。你通过驱动特定按键的列引脚低电平并读取其行引脚来读取该按键的状态。

这种方法的缺点是它增加了代码的复杂性。你不再只需读取单个输入引脚以检查按键是否被按下,而是需要通过驱动列引脚低电平、读取行引脚并再次将列引脚设置为高电平/浮空来主动扫描矩阵。

此驱动程序的目的就是使用embedded-hal特性来隐藏这种复杂性。它通过提供一组虚拟的KeyInput引脚来实现,每个引脚代表键盘矩阵中的一个按键。由于它们实现了InputPin特性,您可以像处理单个输入引脚一样处理每个引脚,而无需担心底层发生的矩阵扫描。

这种方法受到了shift-register-driver crate的启发,该crate使用虚拟输出引脚来控制移位寄存器。

限制

  • 读取按键状态不是可重入的。

  • 这不是为了尽可能快地扫描整个键盘而优化的。这是将每个按键作为独立输入处理的权衡。

示例

本例使用模拟类型实现 embeddded-hal 特性,而不使用任何实际硬件。它可以在您的宿主计算机上编译和运行,但由于没有实际按钮可以按下,因此它不会做任何有趣的事情。

有关在真实微控制器上运行的示例,请参阅 keypad-bluepill-example

use core::convert::Infallible;
use embedded_hal::digital::v2::InputPin;
use keypad::mock_hal::{self, GpioExt, Input, OpenDrain, Output, PullUp, GPIOA};
use keypad::{keypad_new, keypad_struct};

// Define the struct that represents your keypad matrix circuit,
// picking the row and column pin numbers.
keypad_struct! {
    pub struct ExampleKeypad<Error = Infallible> {
        rows: (
            mock_hal::gpioa::PA0<Input<PullUp>>,
            mock_hal::gpioa::PA1<Input<PullUp>>,
            mock_hal::gpioa::PA2<Input<PullUp>>,
            mock_hal::gpioa::PA3<Input<PullUp>>,
        ),
        columns: (
            mock_hal::gpioa::PA4<Output<OpenDrain>>,
            mock_hal::gpioa::PA5<Output<OpenDrain>>,
            mock_hal::gpioa::PA6<Output<OpenDrain>>,
            mock_hal::gpioa::PA7<Output<OpenDrain>>,
            mock_hal::gpioa::PA8<Output<OpenDrain>>,
        ),
    }
}

fn main() {
    // Get access to (mock) general-purpose input/output pins.
    let pins = GPIOA::split();

    // Create an instance of the keypad struct you defined above.
    let keypad = keypad_new!(ExampleKeypad {
        rows: (
            pins.pa0.into_pull_up_input(),
            pins.pa1.into_pull_up_input(),
            pins.pa2.into_pull_up_input(),
            pins.pa3.into_pull_up_input(),
        ),
        columns: (
            pins.pa4.into_open_drain_output(),
            pins.pa5.into_open_drain_output(),
            pins.pa6.into_open_drain_output(),
            pins.pa7.into_open_drain_output(),
            pins.pa8.into_open_drain_output(),
        ),
    });

    // Create a 2d array of virtual `KeypadInput` pins, each representing 1 key
    // in the matrix. They implement the `InputPin` trait and can (mostly) be
    // used just like any other embedded-hal input pins.
    let keys = keypad.decompose();

    let first_key = &keys[0][0];
    println!("Is first key pressed? {:?}\n", first_key.is_low());

    // Print a table of which keys are pressed. 
    for (row_index, row) in keys.iter().enumerate() {
        print!("row {}: ", row_index);
        for key in row.iter() {
            let is_pressed = if key.is_low().unwrap() { 1 } else { 0 };
            print!(" {} ", is_pressed);
        }
        println!();
    }

    // Give up ownership of the row and column pins.
    let ((_r0, _r1, _r2, _r3), (_c0, _c1, _c2, _c3, _c4)) = keypad.release();
}

许可证

许可协议为以下之一

由您选择。

依赖项

~71KB