rppal
repository·master·Indexed 23 days ago
https://github.com/golemparts/rppalA Raspberry Pi Peripheral Access Library providing a Rust interface to GPIO, I2C, PWM, SPI, and UART peripherals. It supports various Raspberry Pi models including the Raspberry Pi 5, integrates with embedded-hal traits, and supports USB to serial adapters. Version 0.22.1.
What's inside rppal
- Before running any examples that interface with external hardware components (such as LEDs, servos, or sensors), you must read the comments within the source code. Take all necessary precautions to prevent electrical damage to your Raspberry Pi or connected peripherals.
Install RPPAL via Cargo
masterTo use RPPAL in your Rust project, add it to your
Cargo.tomldependencies. You can do this usingcargo add rppalor by manually adding the dependency line.Note: RPPAL is no longer maintained as of July 1, 2025. It is compatible with all Raspberry Pi models released before that date, including Raspberry Pi 5.
[dependencies] rppal = "0.22.1"Cross-compile RPPAL for Raspberry Pi
masterIf you are developing on a non-Raspberry Pi machine, you must cross-compile for the target ARM architecture.
1. Install the target
For 32-bit Linux, use
armv7-unknown-linux-gnueabihf. For 64-bit, useaarch64-unknown-linux-gnu.rustup target install armv7-unknown-linux-gnueabihf2. Configure Cargo
Create a
.cargo/config.tomlfile in your project root to set the default target:[build] target = "armv7-unknown-linux-gnueabihf"3. Configure VS Code
To ensure
rust-analyzerworks correctly, create a.vscode/settings.jsonfile in your project root:{ "rust-analyzer.cargo.target": "armv7-unknown-linux-gnueabihf" }Access Raspberry Pi peripherals with RPPAL
masterRPPAL provides a user-friendly interface to access Raspberry Pi peripherals including GPIO, I2C, PWM, SPI, and UART. It also supports USB to serial adapters.
Key integration features:
embedded-halsupport: RPPAL implementsembedded-haltraits, allowing it to be used with a wide variety of platform-agnostic drivers. It supports bothembedded-halv0.2.7 and v1.- Platform Support: Compatible with Raspberry Pi A, A+, B, B+, 2B, 3A+, 3B, 3B+, 4B, 5, CM, CM 3, CM 3+, CM 4, CM 5, 400, Zero, Zero W, and Zero 2 W.
- OS Requirements: Requires a recent release of Raspberry Pi OS. Other Linux distributions are unsupported.
- Toolchain Support: Supports both
gnuandmusllibc targets.
Enable embedded-hal trait implementations
masterIf your project requires
embedded-haltrait implementations, you must specify either thehalorhal-unprovenfeature flag in yourCargo.toml.hal: Enablesembedded-haltrait implementations for all supported peripherals (excludesunproventraits).hal-unproven: Enables allembedded-haltrait implementations, including those marked asunproven. Note thatunproventraits do not follow semver rules and may introduce breaking changes in patch releases.
[dependencies] rppal = { version = "0.22.1", features = ["hal"] }How SPI segments work in multi-segment transfers
masterWhen performing a multi-segment transfer via
Spi::transfer_segments, eachSegmentallows you to fine-tune the SPI protocol parameters for that specific chunk of data.Key behaviors to keep in mind:
- Buffer Lengths: If you provide both a read and write buffer in a single segment, the transfer length is automatically determined by the shortest of the two buffers. You can check this length using
segment.len(). - Slave Select (SS) Toggling: By setting
ss_changetotrue, you can force the Slave Select line to toggle between segments. This is useful for protocols that require a chip-select pulse between different command/data phases. - Inheritance: If you do not explicitly set a parameter (like
clock_speedorbits_per_word) on a segment, it defaults to0, which instructs the driver to use the global configuration of theSpiinstance.
- Buffer Lengths: If you provide both a read and write buffer in a single segment, the transfer length is automatically determined by the shortest of the two buffers. You can check this length using
Implement embedded-hal SPI traits with RPPAL
masterRPPAL provides implementations for several
embedded-halversions to allow theSpistruct to be used in generic embedded drivers. Depending on which features you enable,Spiimplements the following:embedded-hal(Modern)When the
embedded-halfeature is enabled,Spiimplements:embedded_hal::spi::ErrorTypeembedded_hal::spi::Errorembedded_hal::spi::SpiBus<u8>(providingread,write,transfer,transfer_in_place, andflush)embedded_hal::spi::SpiDevice<u8>(viaSimpleHalSpiDevice)
embedded-hal-0(Legacy)When the
embedded-hal-0feature is enabled,Spiimplements:embedded_hal_0::blocking::spi::Transfer<u8>embedded_hal_0::blocking::spi::Write<u8>embedded_hal_0::spi::FullDuplex<u8>
embedded-hal-nb(Non-blocking)When the
embedded-hal-nbfeature is enabled,Spiimplements:embedded_hal_nb::spi::FullDuplex<u8>
PWM Channel mapping by model
masterPWM channel availability depends on the Raspberry Pi model being used:
Raspberry Pi 5 and later
Supports 4 hardware PWM channels:
Channel::Pwm0= GPIO12Channel::Pwm1= GPIO13Channel::Pwm2= GPIO18Channel::Pwm3= GPIO19
Older models (pre-Raspberry Pi 5)
Supports 2 hardware PWM channels:
Channel::Pwm0= GPIO12 or GPIO18Channel::Pwm1= GPIO13 or GPIO19
Note: Using analog audio output consumes both PWM channels on older models, which may cause conflicts if you attempt to access PWM simultaneously.
Use RPPAL with embedded-hal traits
masterRPPAL can be used as a hardware abstraction layer for the Rust ecosystem by enabling specific features. This allows you to use RPPAL-compatible hardware with any driver that implements the
embedded-haltraits.To use these traits, you must enable one of the following features in your
Cargo.toml:embedded-hal-0(forembedded-halv0.2.7)embedded-hal(forembedded-halv1)embedded-hal-nb(for non-blockingembedded-halsupport)
I2C, SPI, PWM, and UART Usage Patterns
masterThe following examples demonstrate communication with other peripherals and hardware modules:
- I2C (Real-Time Clock):
i2c_ds3231.rsshows how to set and retrieve time on a Maxim Integrated DS3231 RTC via the I2C bus. - Hardware PWM:
pwm_blinkled.rsblinks an LED using the Raspberry Pi's dedicated hardware PWM.pwm_servo.rsrotates a servo motor using hardware PWM.
- SPI (EEPROM):
spi_25aa1024.rsdemonstrates data transfers to a Microchip 25AA1024 serial EEPROM using the SPI bus. - UART (Serial):
uart_blocking_read.rsdemonstrates how to perform blocking reads while waiting for incoming serial data.
- I2C (Real-Time Clock):
GPIO Usage Patterns and Examples
masterThe following examples demonstrate various ways to interact with the Raspberry Pi GPIO pins:
- Basic LED Blinking:
gpio_blinkled.rsblinks an LED in a simple loop. - Signal Handling:
gpio_blinkled_signals.rsblinks an LED while handlingSIGINT(Ctrl+C) andSIGTERMsignals to ensure the pin state is reset before the application exits. - Concurrency with MPSC:
gpio_multithreaded_mpsc.rsblinks an LED on a separate thread using a Multi-Producer Single-Consumer (MPSC) channel. - Concurrency with Mutex:
gpio_multithreaded_mutex.rsdemonstrates blinking an LED controlled from multiple threads using a Mutex. - Software PWM for Servos:
gpio_servo_softpwm.rsrotates a servo motor using software-based Pulse Width Modulation (PWM). - Input Interrupts and Shared State:
gpio_shared_button_state.rsdemonstrates sharing state with an input interrupt and pausing program execution until a specific number of event changes occur. - GPIO Status Inspection:
gpio_status.rsretrieves and displays the mode and logic level for every pin on the 26-pin or 40-pin GPIO header in an ASCII table.
- Basic LED Blinking:
Blink an LED using GPIO
masterThis example demonstrates how to control a GPIO pin to blink an LED. This code uses BCM pin numbering (e.g., BCM 23 corresponds to physical pin 16).
Caution: Always use an appropriate resistor in series with an LED to prevent damage to the GPIO pin or the LED.
use std::error::Error; use std::thread; use std::time::Duration; use rppal::gpio::Gpio; use rppal::system::DeviceInfo; // Gpio uses BCM pin numbering. BCM GPIO 23 is tied to physical pin 16. const GPIO_LED: u8 = 23; fn main() -> Result<(), Box<dyn Error>> { println!("Blinking an LED on a {}.", DeviceInfo::new()?.model()); let mut pin = Gpio::new()?.get(GPIO_LED)?.into_output(); // Blink the LED by setting the pin's logic level high for 500 ms. pin.set_high(); thread::sleep(Duration::from_millis(500)); pin.set_low(); Ok() }