用 Rust 探索 RISC-V 主板 D1 之 GPIO
-
gpio 是单片机或者单板机和外部硬件沟通的桥梁,通过它可以控制外部硬件,可以建立通讯,可以获取传感器数据等
D1 开发板和树莓派一样,对外引出了 40pin 引脚, 这些引脚包含3.3v,5v供电, GND , 以及几个未使用(NC)引脚, 然后就是我们要讲到的 GPIO 引脚.
辅助利器
开发 gpio 应用离不开几个利器
1. 原理图
全志已经开放原理图下载, 下载地址: https://developer.allwinnertech.com/downloads/resources/24
根据原理图,我们可以看到 40pin 引脚与之对应的芯片端口
这里要说明 D1 板子用 PCF8574 扩展了 8 个 IO 分别是 PP0-PP7 ,其他引出的 IO 来至 D1 这颗芯片, 并且由于 IO 端口不足, 40 Pin 里面物理 32pin 和 38pin 为未启用(NC), 树莓派中是两个 GPIO 端口
2. debugfs
第二个利器就是 debugfs Wiki: Debugfs
debugfs 传承了 Linux 一切皆文件的理念,把内核更多信息通过文件系统展现给开发者,这里当然就包括了我们要的 gpio 信息
debugfs 默认挂载在
/sys/kernel/debug
, 如果没有挂载,可以执行mount -t debugfs none /sys/kernel/debug
挂载在 /sys/kernel/debug 目录下有个 gpio 文件
gpiochip0: GPIOs 0-223, parent: platform/2000000.pinctrl, 2000000.pinctrl: gpio-115 ( |usb1-vbus ) out lo gpio-116 ( |otg_det ) in lo gpio-117 ( |otg_id ) in hi gpio-144 ( |phy-rst ) out hi gpio-166 ( |cd ) in hi IRQ gpio-202 ( |wlan_hostwake ) in hi gpio-204 ( |wlan_regon ) out lo gpio-208 ( |bt_wake ) out lo gpio-209 ( |bt_hostwake ) in hi gpio-210 ( |bt_rst ) out lo gpiochip1: GPIOs 2020-2027, parent: i2c/2-0038, pcf8574, can sleep:
里面内容显示了,板子上有两部分 gpio 组成,第一部分(gpiochip0)有 0-223 ,共224 个 gpio 端口,来自 D1 芯片, 第二部分(gpiochip1) 2020-2027 , 共8个 gpio 端口来自 PCF8574, 并且 8574 是通过 i2c 连接的 , 同时又显示了gpiochip0 中已经做了配置的 GPIO 接口
D1 芯片中的 gpio 信息可以在
/sys/kernel/debug/pinctrl/2000000.pinctrl/pins
找到编号和芯片引脚名称(PA1,PB2等)的对应关系registered pins: 88 pin 32 (PB0) pin 33 (PB1) pin 34 (PB2) pin 35 (PB3) pin 36 (PB4) pin 37 (PB5) pin 38 (PB6) pin 39 (PB7) pin 40 (PB8) pin 41 (PB9) pin 42 (PB10) pin 43 (PB11) pin 44 (PB12) pin 64 (PC0) pin 65 (PC1) pin 66 (PC2) pin 67 (PC3) ...
3. sysfs
sysfs 和 debugfs 一样,通过文件系统,用户不仅可以查看信息,还可以操作硬件.
在
/sys/class/gpio
目录下有四个文件,分别是 export,gpiochip0 ,gpiochip2020,unexport其中 gpiochip0,gpiochip2020 链接的是芯片两个gpio主控
export 和 unexport 用来控制 gpio 的开启与关闭
# 启用 2020 号 gpio 端口, 根据上面的信息,可以知道 2020 对应扩展 IO PP0 , 也就是 40pin 引脚中的 GPIO8 echo 2020 > export # 执行完 echo 2020 > export 后, 会在 /sys/class/gpio 中创建一个目录 /sys/class/gpio/gpio202 , 在这个目录里面就可以设置 gpio 的in 和 out 以及读取或者输出高低电平 cd /sys/class/gpio/gpio2020 # 设置为输出 echo out > direction # 设置高电平 echo 1 > value # 设置低电平 echo 0 > value # 执行代码后, 如果接了 LED 灯, 灯就会亮了又灭了
Rust 在 gpio 方面的支持情况
rust 有以下一些 crate
1. linux-embedded-hal
Implementation of the embedded-hal traits for Linux devices
2. gpio-cdev
基于 GPIO character device ABI 的库
3. sysfs-gpio
基于 sysfs 操作 gpio 的库, 原理如上面手动操作 sysfs 是一样的
4. gpio-utils
操作 gpio 的小工具程序, 基于
sysfs_gpio
Rust Demo (使用
cdev-gpio
)- list gpios
extern crate gpio_cdev; use gpio_cdev::*; fn main() { let chip_iterator = match chips() { Ok(chips) => chips, Err(e) => { println!("Failed to get chip iterator: {:?}", e); return; } }; for chip in chip_iterator { let chip = match chip { Ok(chip) => chip, Err(err) => panic!("Failed to open the chip: {:?}", err) }; println!( "GPIO chip: {}, \"{}\", \"{}\", {} GPIO Lines", chip.path().to_string_lossy(), chip.name(), chip.label(), chip.num_lines() ); for line in chip.lines() { match line.info() { Ok(info) => { let mut flags = vec![]; if info.is_kernel() { flags.push("kernel"); } if info.direction() == LineDirection::Out { flags.push("output"); } if info.is_active_low() { flags.push("active-low"); } if info.is_open_drain() { flags.push("open-drain"); } if info.is_open_source() { flags.push("open-source"); } let usage = if !flags.is_empty() { format!("[{}]", flags.join(" ")) } else { "".to_owned() }; println!( "\tline {lineno:>3}: {name} {consumer} {usage}", lineno = info.line().offset(), name = info.name().unwrap_or("unused"), consumer = info.consumer().unwrap_or("unused"), usage = usage, ); } Err(e) => println!("\tError getting line info: {:?}", e), } } println!(); } }
cdev-gpio 的接口中, 通过 chips() 获取 gpio控制器列表, D1 中的/dev/gpiochip0 和 /dev/gpiochip1
每个 chip 中有 lines 列表,就是控制器下的 gpio 列表 line 就是 gpio 对象, 可以 进行 set_value , get_value ,以及设定输入输出等操作
总体来说, Linux 对 gpio 封装已经很简单, rust 在这方面支持也比较完善. 后续文章还有 i2c, spi , uart 等方面的操作,欢迎关注,欢迎拍砖.