如下是來自RVBoards開源小組liangdi關于「哪吒開發板」的技術文章。
用 Rust 探索RISC-V 主板?全志D1芯片之GPIO
gpio 是單片機或者單板機和外部硬件溝通的橋梁,通過它可以控制外部硬件,可以建立通訊,可以獲取傳感器數據等
D1 開發板和樹莓派一樣,對外引出了 40pin 引腳, 這些引腳包含3.3v,5v供電, GND , 以及幾個未使用(NC)引腳, 然后就是我們要講到的 GPIO 引腳。
輔助利器
開發 gpio 應用離不開幾個利器
1. 原理圖
已經開放原理圖下載, 下載地址: https://www.rvboards.org/forum/cn/assets/uploads/files/1620265818082-d1哪吒開發板原理圖20210224.pdf
根據原理圖,我們可以看到 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 文件
里面內容顯示了,板子上有兩部分 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等)的對應關系
3. SYSFS
sysfs 和 debugfs 一樣,通過文件系統,用戶不僅可以查看信息,還可以操作硬件。
在 /sys/class/gpio 目錄下有四個文件,分別是 export,gpiochip0 ,gpiochip2020,unexport
其中 gpiochip0,gpiochip2020 鏈接的是芯片兩個gpio主控
export 和 unexport 用來控制 gpio 的開啟與關閉
# 啟用 2020 號 gpio 端口, 根據上面的信息,可以知道 2020 對應擴展 IO PP0 , 也就是 40pin 引腳中的 GPIO8echo 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!( " line {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!(" Error 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 在這方面支持也比較完善。
評論
查看更多