use std::error;
use std::fmt;
use std::io;
use std::result;
use std::time::Duration;
#[cfg(any(
feature = "embedded-hal-0",
feature = "embedded-hal",
feature = "embedded-hal-nb"
))]
mod hal;
#[cfg(feature = "hal-unproven")]
mod hal_unproven;
mod sysfs;
use crate::system::DeviceInfo;
const NANOS_PER_SEC: f64 = 1_000_000_000.0;
#[derive(Debug)]
pub enum Error {
Io(io::Error),
UnknownModel,
InvalidChannel,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Error::Io(ref err) => write!(f, "I/O error: {}", err),
Error::UnknownModel => write!(f, "Unknown Raspberry Pi model"),
Error::InvalidChannel => write!(f, "Invalid PWM channel"),
}
}
}
impl error::Error for Error {}
impl From<io::Error> for Error {
fn from(err: io::Error) -> Error {
Error::Io(err)
}
}
pub type Result<T> = result::Result<T, Error>;
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum Channel {
Pwm0 = 0,
Pwm1 = 1,
Pwm2 = 2,
Pwm3 = 3,
}
impl fmt::Display for Channel {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Channel::Pwm0 => write!(f, "Pwm0"),
Channel::Pwm1 => write!(f, "Pwm1"),
Channel::Pwm2 => write!(f, "Pwm2"),
Channel::Pwm3 => write!(f, "Pwm3"),
}
}
}
impl TryFrom<u8> for Channel {
type Error = Error;
fn try_from(value: u8) -> result::Result<Self, Self::Error> {
match value {
0 => Ok(Channel::Pwm0),
1 => Ok(Channel::Pwm1),
2 => Ok(Channel::Pwm2),
3 => Ok(Channel::Pwm3),
_ => Err(Error::InvalidChannel),
}
}
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum Polarity {
Normal,
Inverse,
}
impl fmt::Display for Polarity {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Polarity::Normal => write!(f, "Normal"),
Polarity::Inverse => write!(f, "Inverse"),
}
}
}
#[derive(Debug)]
pub struct Pwm {
chip: u8,
channel: u8,
reset_on_drop: bool,
}
impl Pwm {
pub fn new(channel: Channel) -> Result<Pwm> {
let device_info = DeviceInfo::new().map_err(|_| Error::UnknownModel)?;
let pwmchip = device_info.pwm_chip();
let index = channel as u8;
Self::with_pwmchip(pwmchip, index)
}
pub fn with_pwmchip(pwmchip: u8, index: u8) -> Result<Pwm> {
sysfs::export(pwmchip, index)?;
let pwm = Pwm {
chip: pwmchip,
channel: index,
reset_on_drop: true,
};
let _ = pwm.disable();
Ok(pwm)
}
pub fn with_period(
channel: Channel,
period: Duration,
pulse_width: Duration,
polarity: Polarity,
enabled: bool,
) -> Result<Pwm> {
let pwm = Pwm::new(channel)?;
let _ = sysfs::set_pulse_width(pwm.chip, pwm.channel, 0);
pwm.set_period(period)?;
pwm.set_pulse_width(pulse_width)?;
pwm.set_polarity(polarity)?;
if enabled {
pwm.enable()?;
}
Ok(pwm)
}
pub fn with_frequency(
channel: Channel,
frequency: f64,
duty_cycle: f64,
polarity: Polarity,
enabled: bool,
) -> Result<Pwm> {
let pwm = Pwm::new(channel)?;
let _ = sysfs::set_pulse_width(pwm.chip, pwm.channel, 0);
let period = if frequency == 0.0 {
0.0
} else {
(1.0 / frequency) * NANOS_PER_SEC
};
let pulse_width = period * duty_cycle.clamp(0.0, 1.0);
sysfs::set_period(pwm.chip, pwm.channel, period as u64)?;
sysfs::set_pulse_width(pwm.chip, pwm.channel, pulse_width as u64)?;
pwm.set_polarity(polarity)?;
if enabled {
pwm.enable()?;
}
Ok(pwm)
}
pub fn period(&self) -> Result<Duration> {
Ok(Duration::from_nanos(sysfs::period(
self.chip,
self.channel,
)?))
}
pub fn set_period(&self, period: Duration) -> Result<()> {
sysfs::set_period(
self.chip,
self.channel,
u64::from(period.subsec_nanos())
.saturating_add(period.as_secs().saturating_mul(NANOS_PER_SEC as u64)),
)?;
Ok(())
}
pub fn pulse_width(&self) -> Result<Duration> {
Ok(Duration::from_nanos(sysfs::pulse_width(
self.chip,
self.channel,
)?))
}
pub fn set_pulse_width(&self, pulse_width: Duration) -> Result<()> {
sysfs::set_pulse_width(
self.chip,
self.channel,
u64::from(pulse_width.subsec_nanos())
.saturating_add(pulse_width.as_secs().saturating_mul(NANOS_PER_SEC as u64)),
)?;
Ok(())
}
pub fn frequency(&self) -> Result<f64> {
let period = sysfs::period(self.chip, self.channel)? as f64;
Ok(if period == 0.0 {
0.0
} else {
1.0 / (period / NANOS_PER_SEC)
})
}
pub fn set_frequency(&self, frequency: f64, duty_cycle: f64) -> Result<()> {
let _ = sysfs::set_pulse_width(self.chip, self.channel, 0);
let period = if frequency == 0.0 {
0.0
} else {
(1.0 / frequency) * NANOS_PER_SEC
};
let pulse_width = period * duty_cycle.clamp(0.0, 1.0);
sysfs::set_period(self.chip, self.channel, period as u64)?;
sysfs::set_pulse_width(self.chip, self.channel, pulse_width as u64)?;
Ok(())
}
pub fn duty_cycle(&self) -> Result<f64> {
let period = sysfs::period(self.chip, self.channel)? as f64;
let pulse_width = sysfs::pulse_width(self.chip, self.channel)? as f64;
Ok(if period == 0.0 {
0.0
} else {
(pulse_width / period).clamp(0.0, 1.0)
})
}
pub fn set_duty_cycle(&self, duty_cycle: f64) -> Result<()> {
let period = sysfs::period(self.chip, self.channel)? as f64;
let pulse_width = period * duty_cycle.clamp(0.0, 1.0);
sysfs::set_pulse_width(self.chip, self.channel, pulse_width as u64)?;
Ok(())
}
pub fn polarity(&self) -> Result<Polarity> {
Ok(sysfs::polarity(self.chip, self.channel)?)
}
pub fn set_polarity(&self, polarity: Polarity) -> Result<()> {
sysfs::set_polarity(self.chip, self.channel, polarity)?;
Ok(())
}
pub fn is_enabled(&self) -> Result<bool> {
Ok(sysfs::enabled(self.chip, self.channel)?)
}
pub fn enable(&self) -> Result<()> {
sysfs::set_enabled(self.chip, self.channel, true)?;
Ok(())
}
pub fn disable(&self) -> Result<()> {
sysfs::set_enabled(self.chip, self.channel, false)?;
Ok(())
}
pub fn reset_on_drop(&self) -> bool {
self.reset_on_drop
}
pub fn set_reset_on_drop(&mut self, reset_on_drop: bool) {
self.reset_on_drop = reset_on_drop;
}
}
impl Drop for Pwm {
fn drop(&mut self) {
if self.reset_on_drop {
let _ = sysfs::set_enabled(self.chip, self.channel, false);
let _ = sysfs::unexport(self.chip, self.channel);
}
}
}