Hi, I am a beginner in rust and I scoured the internet but cannot find a tutorial or a step by step set of instructions on how to create custom libraries in rust to control the GPIO pins on the raspberry pi 4B, I am trying to build a robot and am doing this specifically on purpose to learn more about creating libraries for the raspberry pi.
Any advice, help or instructions are very much appreciated.
If you want to start from nothing and build up for the experience then perhaps have a look at the example C code for handling GPIO here:RPi GPIO Code Samples - eLinux.org and have a go at converting that to Rust.
Yes I have, and if I were on another project I would probably use that instead of writing my own , but right now I am trying to learn how to write these crates that can access and control GPIO on the raspberry pi. Thanks
Thanks so much. I have never done c/c++ but I will have a go, do you know of any rust documentation that goes through creating libraries for GPIO on the raspberry pi.
I have seen the rust bindings for libgpiod but have no idea how to implement them, I have only started learning rust a few days ago (quite ambitious to embark on such a complex project after just 2 days) so any advice is much appreciated with regards to it.
Thanks, I have asked in the raspberry pi forum. I just wish the developers of the crates or even raspberry pi could give like basic documentation on writing crates/ package/libraries to control GPIO on the raspberry pi.
It's not really clear to me why they should. Instruction on writing crates and such interfaces is not GPIO or even Pi specific, it's general Rust knowledge. This is something you will learn when learning Rust and its libraries/ecosystem/idioms/patterns in general. Two days on Rust so far is likely too early to be conversant with what is going on in such crates.
Just for fun here is a translation of the C GPIO code I linked to above in Rust:
use std::fs::OpenOptions;
use std::io;
use std::os::unix::io::AsRawFd;
use std::ptr;
use std::thread::sleep;
use std::time::Duration;
use libc::{self, c_void, mmap, munmap, off_t, PROT_READ, PROT_WRITE, MAP_SHARED, O_RDWR, O_SYNC};
const BCM2708_PERI_BASE: usize = 0x2000_0000;
const GPIO_BASE: usize = BCM2708_PERI_BASE + 0x200000;
const PAGE_SIZE: usize = 4 * 1024;
const BLOCK_SIZE: usize = 4 * 1024;
struct Gpio {
gpio: *mut u32,
gpio_map: *mut c_void,
}
impl Gpio {
fn setup_io() -> io::Result<Gpio> {
let file = OpenOptions::new().read(true).write(true).open("/dev/mem")?;
let fd = file.as_raw_fd();
unsafe {
let gpio_map = mmap(
ptr::null_mut(),
BLOCK_SIZE,
PROT_READ | PROT_WRITE,
MAP_SHARED,
fd,
GPIO_BASE as off_t,
);
if gpio_map == libc::MAP_FAILED {
Err(io::Error::last_os_error())
} else {
Ok(Gpio {
gpio: gpio_map as *mut u32,
gpio_map,
})
}
}
}
fn inp_gpio(&self, g: usize) {
unsafe {
let ptr = self.gpio.add(g / 10);
let val = ptr.read_volatile() & !(7 << ((g % 10) * 3));
ptr.write_volatile(val);
}
}
fn out_gpio(&self, g: usize) {
unsafe {
let ptr = self.gpio.add(g / 10);
let val = ptr.read_volatile() | (1 << ((g % 10) * 3));
ptr.write_volatile(val);
}
}
fn gpio_set(&self, g: usize) {
unsafe {
let ptr = self.gpio.add(7);
ptr.write_volatile(1 << g);
}
}
fn gpio_clr(&self, g: usize) {
unsafe {
let ptr = self.gpio.add(10);
ptr.write_volatile(1 << g);
}
}
fn get_gpio(&self, g: usize) -> bool {
unsafe {
let val = self.gpio.add(13).read_volatile();
(val & (1 << g)) != 0
}
}
}
impl Drop for Gpio {
fn drop(&mut self) {
unsafe {
munmap(self.gpio_map, BLOCK_SIZE);
}
}
}
fn print_button(gpio: &Gpio, g: usize) {
if gpio.get_gpio(g) {
println!("Button pressed!");
} else {
println!("Button released!");
}
}
fn main() -> io::Result<()> {
let gpio = Gpio::setup_io()?;
// Set GPIO pins 7-11 to output
for g in 7..=11 {
gpio.inp_gpio(g);
gpio.out_gpio(g);
}
for _ in 0..10 {
for g in 7..=11 {
gpio.gpio_set(g);
sleep(Duration::from_secs(1));
}
for g in 7..=11 {
gpio.gpio_clr(g);
sleep(Duration::from_secs(1));
}
}
Ok(())
}
It's about as minimal as such a GPIO interface can be. Lacking all the nice features more complete implementations might have, like sharing GPIO between threads etc.
Note:
This is completely untested. It is left to the reader to review, test, and determine its correctness or otherwise. Likely start with checking against the original C code.
The I/O base addresses of the GPIO (GPIO_BASE) tend to change with different Pi versions, you will have to check that at least.