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(())
}

注意事项

  1. 运行程序需要root权限,因为直接访问硬件外设
  2. 确保没有其他程序正在使用相同的硬件资源
  3. 不同树莓派型号的GPIO引脚布局可能不同
  4. 使用中断时要注意去抖动处理

更多功能

rppal还提供了其他高级功能,如:

  • 软件PWM生成
  • 硬件定时器
  • 多线程安全访问
  • 自定义GPIO轮询间隔
回到顶部