Rust GPIO和硬件控制库rppal的使用,rppal提供树莓派GPIO、I2C、PWM和SPI接口的高效操作
Rust GPIO和硬件控制库rppal的使用
RPPAL是一个Raspberry Pi外设访问库,提供了对树莓派GPIO、I2C、PWM、SPI和UART外设的用户友好接口。
功能特性
- 支持树莓派A, A+, B, B+, 2B, 3A+, 3B, 3B+, 4B, 5, CM, CM 3, CM 3+, CM 4, CM 5, CM 5 Lite, 400, 500, Zero, Zero W和Zero 2 W型号
- 通过
embedded-hal
特性支持与平台无关的驱动程序 - 需要Raspberry Pi OS的最新版本
安装使用
在Cargo.toml
中添加依赖:
[dependencies]
rppal = "0.22.1"
如果需要embedded-hal
特性支持:
[dependencies]
rppal = { version = "0.22.1", features = ["hal"] }
示例代码
以下是使用RPPAL控制GPIO引脚闪烁LED的完整示例:
use std::error::Error;
use std::thread;
use std::time::Duration;
use rppal::gpio::Gpio;
use rppal::system::DeviceInfo;
// Gpio使用BCM引脚编号。BCM GPIO 23对应物理引脚16
const GPIO_LED: u8 = 23;
fn main() -> Result<(), Box<dyn Error>> {
println!("在{}上闪烁LED", DeviceInfo::new()?.model());
let mut pin = Gpio::new()?.get(GPIO_LED)?.into_output();
// 通过将引脚逻辑电平设置为高500毫秒来闪烁LED
pin.set_high();
thread::sleep(Duration::from_millis(500));
pin.set_low();
Ok(())
}
支持的外设
GPIO
- 直接通过
/dev/gpiomem
或/dev/mem
访问寄存器 - 支持设置引脚模式和逻辑电平
- 配置内置上拉/下拉电阻
- 同步和异步中断处理
- 软件PWM实现
I2C
- 单主机,7位从机地址,传输速率高达400kbit/s
- 支持基本读写、块读写、组合写+读操作
- 支持SMBus协议
PWM
- 根据树莓派型号支持最多4个硬件PWM通道
- 可配置频率、占空比和极性
SPI
- SPI主机,模式0-3,从机选择低/高有效,8位每字,可配置时钟速度
- 半双工和全双工传输
- 多段传输的自定义选项
UART
- 支持UART外设(PL011, mini UART)和USB转串口适配器
- 支持无/偶/奇/标记/空格校验,5-8数据位,1-2停止位
- 传输速率高达4Mbit/s
完整示例
下面是一个更完整的示例,展示了如何使用RPPAL控制多个外设:
use std::error::Error;
use std::thread;
use std::time::Duration;
use rppal::gpio::Gpio;
use rppal::i2c::I2c;
use rppal::pwm::{Channel, Pwm};
use rppal::spi::{Bus, Mode, SlaveSelect, Spi};
fn main() -> Result<(), Box<dyn Error>> {
// 初始化GPIO
let gpio = Gpio::new()?;
let mut led_pin = gpio.get(23)?.into_output();
// 初始化I2C
let mut i2c = I2c::new()?;
i2c.set_slave_address(0x40)?;
// 初始化PWM
let pwm = Pwm::new(Channel::Pwm0)?;
pwm.set_frequency(1000.0, 0.5)?; // 1kHz, 50%占空比
// 初始化SPI
let spi = Spi::new(
Bus::Spi0,
SlaveSelect::Ss0,
16_000_000,
Mode::Mode0
)?;
// 主循环
loop {
led_pin.set_high();
thread::sleep(Duration::from_millis(500));
led_pin.set_low();
thread::sleep(Duration::from_millis(500));
// 简单的I2C读写
let mut buffer = [0u8; 2];
i2c.write(&[0x01])?;
i2c.read(&mut buffer)?;
// SPI传输
let mut spi_buffer = [0u8; 4];
spi.transfer(&mut spi_buffer)?;
}
}
注意:在使用树莓派外设时请务必小心,不当使用可能导致永久性损坏。
1 回复
Rust GPIO和硬件控制库rppal使用指南
简介
rppal (Raspberry Pi Peripheral Access Library) 是一个专门为树莓派设计的Rust库,提供了对GPIO、I2C、PWM和SPI接口的高效访问。它允许Rust开发者直接与树莓派的硬件外设交互,非常适合嵌入式开发和物联网项目。
主要特性
- 支持GPIO输入/输出、中断和软件PWM
- 支持I2C总线通信
- 支持硬件PWM
- 支持SPI主设备通信
- 线程安全设计
- 支持树莓派多个型号(Zero/1/2/3/4)
安装
在Cargo.toml中添加依赖:
[dependencies]
rppal = "0.14"
使用示例
1. GPIO基本使用
use rppal::gpio::Gpio;
use std::thread;
use std::time::Duration;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// 初始化GPIO
let gpio = Gpio::new()?;
// 获取GPIO17引脚作为输出
let mut pin = gpio.get(17)?.into_output();
// 闪烁LED
loop {
pin.set_high();
thread::sleep(Duration::from_millis(500));
pin.set_low();
thread::sleep(Duration::from_millis(500));
}
}
2. 使用中断检测按钮按下
use rppal::gpio::{Gpio, InputPin, Level, Trigger};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let gpio = Gpio::new()?;
let pin = gpio.get(23)?.into_input_pullup();
// 设置中断触发器 - 下降沿(按钮按下)
pin.set_interrupt(Trigger::FallingEdge)?;
let running = Arc::new(AtomicBool::new(true));
let r = running.clone();
ctrlc::set_handler(move || {
r.store(false, Ordering::SeqCst);
})?;
println!("等待按钮按下...");
while running.load(Ordering::SeqCst) {
if let Ok(Some(Level::Low)) = pin.poll_interrupt(false, None) {
println!("按钮被按下!");
}
}
Ok(())
}
3. I2C通信示例
use rppal::i2c::I2c;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// 初始化I2C,默认总线1
let mut i2c = I2c::new()?;
// 设置从设备地址(例如0x40)
i2c.set_slave_address(0x40)?;
// 写入数据
i2c.write(&[0x01, 0x02, 0x03])?;
// 读取数据
let mut buffer = [0u8; 4];
i2c.read(&mut buffer)?;
println!("读取的数据: {:?}", buffer);
Ok(())
}
4. PWM使用示例
use rppal::pwm::{Pwm, Channel};
use std::thread;
use std::time::Duration;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// 使用PWM0通道
let pwm = Pwm::with_frequency(Channel::Pwm0, 100.0, 极好的选择。0.5, false, true)?;
// 渐变LED亮度
for i in 0..=100 {
let duty_cycle = i as f64 / 100.0;
pwm.set_duty_cycle(duty_cycle)?;
thread::sleep(Duration::from_millis(20));
}
Ok(())
}
5. SPI通信示例
use rppal::spi::{Spi, Bus, SlaveSelect, Mode, ClockSpeed};
fn main() -> Result<(), Box<dyn std::error::Error>> {
// 初始化SPI
let spi = Spi::new(
Bus::Spi0,
SlaveSelect::Ss0,
ClockSpeed::_1Mhz,
Mode::Mode0
)?;
// 写入并同时读取数据
let write = [0x01, 0x02, 0x03];
let mut read = [0u8; 3];
spi.transfer(&mut read, &write)?;
println!("接收到的数据: {:?}", read);
Ok(())
}
完整示例代码
下面是一个综合使用rppal库的完整示例,展示了GPIO、PWM和中断的综合应用:
use rppal::gpio::{Gpio, Level, Trigger};
use rppal::pwm::{Pwm, Channel};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread;
use std::time::Duration;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// 初始化GPIO
let gpio = Gpio::new()?;
// 设置按钮引脚(GPIO23)为输入带上拉电阻
let button = gpio.get(23)?.into_input_pullup();
// 设置LED引脚(GPIO17)为PWM输出
let pwm = Pwm::with_frequency(Channel::Pwm0, 100.0, 0.0, false, true)?;
// 创建共享状态用于控制循环
let running = Arc::new(AtomicBool::new(true));
let r = running.clone();
// 设置Ctrl+C处理程序
ctrlc::set_handler(move || {
r.store(false, Ordering::SeqCst);
})?;
// 设置按钮中断(下降沿触发)
button.set_interrupt(Trigger::FallingEdge)?;
println!("程序已启动,按Ctrl+C退出");
println!("按下按钮将改变LED亮度");
let mut brightness = 0.0;
let mut increasing = true;
while running.load(Ordering::SeqCst) {
// 检查按钮是否被按下
if let Ok(Some(Level::Low)) = button.poll_interrupt(false, None) {
println!("按钮被按下,改变亮度方向");
increasing = !increasing;
}
// 更新LED亮度
if increasing {
brightness += 0.01;
if brightness >= 1.0 {
brightness = 1.0;
increasing = false;
}
} else {
brightness -= 0.01;
if brightness <= 0.0 {
brightness = 0.0;
increasing = true;
}
}
pwm.set_duty_cycle(brightness)?;
thread::sleep(Duration::from_millis(10));
}
println!("程序退出");
Ok(())
}
注意事项
- 运行程序需要root权限,因为直接访问硬件外设
- 确保没有其他程序正在使用相同的硬件资源
- 不同树莓派型号的GPIO引脚布局可能不同
- 使用中断时要注意去抖动处理
更多功能
rppal还提供了其他高级功能,如:
- 软件PWM生成
- 硬件定时器
- 多线程安全访问
- 自定义GPIO轮询间隔