Reputation: 7431
I'm struggling to utilize the PWM peripheral hardware on a Raspberry Pi 4 (Model B Rev 1.5) with Rust code. I can bit bash the pin using python code (see below), but I can't get the rppal
crate to work in Rust.
For the Rust/PWM peripheral, I've configured the device tree overlay with a line in /boot/config.txt
:
dtoverlay=pwm,pin=12,func=4
And I've also commented out the audio in the same file with:
#dtparam=audio=on
These adjustments to /boot/config.txt
allows the Pi to boot without error and allows me run both code snippets below without run time errors. The Python code is able to control the fan speed (connected to the GPIO/PWM pin) but the Rust implementation is unable to make fan spin at all.
What should I be looking at to figure this out?
Rust implementation:
use crossterm::{
cursor,
event::{self, Event, KeyCode, KeyEvent},
execute, terminal,
};
use rppal::pwm::{Channel, Error, Pwm};
use std::io::{stdout, Write};
// Function to decode PWM errors
fn decode_pwm_error(err: Error, prefix: &str) {
match err {
Error::Io(e) => {
println!("'{}': OS IO Error: '{}'\r", prefix, e)
}
Error::UnknownModel => {
println!("'{}': Unknown model? Programming error?\r", prefix)
}
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Configure the PWM channel (Channel 0 corresponds to GPIO12)
let pwm = match Pwm::new(Channel::Pwm0) {
Ok(p) => {
println!("Instantiated object\r");
p
}
Err(err) => {
decode_pwm_error(err, "Instantiating");
return Ok(());
}
};
// Initialize the duty cycle to 50%
let mut duty_cycle = 0.5;
let frequency = 100.0;
// Set the PWM frequency to 100 Hz and the duty cycle to 0% initially
match pwm.set_frequency(frequency, duty_cycle) {
Ok(_) => println!("Initial frequency set.\r"),
Err(err) => {
decode_pwm_error(err, "Setting frequency");
return Ok(());
}
};
// Set up the terminal
let mut stdout = stdout();
terminal::enable_raw_mode()?;
execute!(stdout, terminal::EnterAlternateScreen, cursor::Hide)?;
println!("Use arrow keys to change the duty cycle. ESC Quits\r");
loop {
// Display the current duty cycle
println!("Current duty cycle: {:.1}%\r", duty_cycle * 100.0);
// Wait for an event
if let Event::Key(KeyEvent { code, .. }) = event::read()? {
match code {
KeyCode::Up => {
if duty_cycle < 1.0 {
duty_cycle += 0.01;
}
if duty_cycle > 1.0 {
duty_cycle = 1.0;
}
}
KeyCode::Down => {
if duty_cycle > 0.0 {
duty_cycle -= 0.01;
}
if duty_cycle < 0.0 {
duty_cycle = 0.0;
}
}
KeyCode::Esc => break,
_ => {}
}
// Set the PWM duty cycle
match pwm.set_duty_cycle(duty_cycle) {
Ok(_) => {}
Err(err) => {
decode_pwm_error(
err,
format!("Setting duty cycle to '{:.1}%'", duty_cycle * 100.0).as_str(),
);
return Ok(());
}
};
}
// Flush the output
stdout.flush()?;
}
// Stop the PWM
match pwm.disable() {
Ok(_) => println!("PWM Stopped\r"),
Err(err) => {
decode_pwm_error(err, "Stopping PWM");
return Ok(());
}
};
// Restore the terminal
execute!(stdout, terminal::LeaveAlternateScreen, cursor::Show)?;
terminal::disable_raw_mode()?;
Ok(())
}
In python, I can bit bash the output with the following code:
import RPi.GPIO as GPIO
import time
import curses
# Setup GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setup(12, GPIO.OUT)
GPIO.setwarnings(False)
# Initialize PWM on GPIO 12 at 100 Hz
pwm = GPIO.PWM(12, 100)
pwm.start(0) # Start with 0% duty cycle
def main(stdscr):
# Clear screen and initialize curses
stdscr.clear()
curses.curs_set(0)
stdscr.nodelay(1) # Don't block on getch()
stdscr.timeout(100) # Refresh every 100 milliseconds
duty_cycle = 0.0
while True:
stdscr.clear()
stdscr.addstr(0, 0, "Use arrow keys to change the duty cycle.")
stdscr.addstr(1, 0, "Current duty cycle: {:.1f}%".format(duty_cycle * 100))
stdscr.addstr(2, 0, "Press ESC to exit.")
stdscr.refresh()
key = stdscr.getch()
if key == curses.KEY_UP:
if duty_cycle < 1.0:
duty_cycle += 0.01
if duty_cycle > 1.0:
duty_cycle = 1.0
pwm.ChangeDutyCycle(duty_cycle * 100)
elif key == curses.KEY_DOWN:
if duty_cycle > 0.0:
duty_cycle -= 0.01
if duty_cycle < 0.0:
duty_cycle = 0.0
pwm.ChangeDutyCycle(duty_cycle * 100)
elif key == 27: # ESC key
break
time.sleep(0.1) # Small delay to prevent high CPU usage
pwm.stop()
GPIO.cleanup()
if __name__ == "__main__":
curses.wrapper(main)
Upvotes: 0
Views: 110