use std::error;
use std::fmt;
use std::io;
use std::result;
use std::time::Duration;
#[cfg(feature = "hal")]
mod hal;
mod sysfs;
#[derive(Debug)]
pub enum Error {
Io(io::Error),
}
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),
}
}
}
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, Copy, Clone)]
pub enum Channel {
Pwm0 = 0,
Pwm1 = 1,
}
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Polarity {
Normal,
Inverse,
}
pub struct Pwm {
channel: Channel,
reset_on_drop: bool,
}
impl Pwm {
pub fn new(channel: Channel) -> Result<Pwm> {
sysfs::export(channel as u8)?;
let pwm = Pwm {
channel,
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> {
sysfs::export(channel as u8)?;
let pwm = Pwm {
channel,
reset_on_drop: true,
};
let _ = pwm.disable();
let _ = sysfs::set_pulse_width(channel as u8, 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> {
sysfs::export(channel as u8)?;
let pwm = Pwm {
channel,
reset_on_drop: true,
};
let _ = pwm.disable();
let _ = sysfs::set_pulse_width(channel as u8, 0);
let period = if frequency == 0.0 {
0.0
} else {
(1.0 / frequency) * 1_000_000_000.0
};
let pulse_width = period * duty_cycle.max(0.0).min(1.0);
sysfs::set_period(channel as u8, period as u64)?;
sysfs::set_pulse_width(channel as u8, 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.channel as u8)?))
}
pub fn set_period(&self, period: Duration) -> Result<()> {
sysfs::set_period(
self.channel as u8,
u64::from(period.subsec_nanos())
.saturating_add(period.as_secs().saturating_mul(1_000_000_000)),
)?;
Ok(())
}
pub fn pulse_width(&self) -> Result<Duration> {
Ok(Duration::from_nanos(sysfs::pulse_width(
self.channel as u8,
)?))
}
pub fn set_pulse_width(&self, pulse_width: Duration) -> Result<()> {
sysfs::set_pulse_width(
self.channel as u8,
u64::from(pulse_width.subsec_nanos())
.saturating_add(pulse_width.as_secs().saturating_mul(1_000_000_000)),
)?;
Ok(())
}
pub fn frequency(&self) -> Result<f64> {
let period = sysfs::period(self.channel as u8)? as f64;
Ok(if period == 0.0 {
0.0
} else {
1.0 / (period / 1_000_000_000.0)
})
}
pub fn set_frequency(&self, frequency: f64, duty_cycle: f64) -> Result<()> {
let _ = sysfs::set_pulse_width(self.channel as u8, 0);
let period = if frequency == 0.0 {
0.0
} else {
(1.0 / frequency) * 1_000_000_000.0
};
let pulse_width = period * duty_cycle.max(0.0).min(1.0);
sysfs::set_period(self.channel as u8, period as u64)?;
sysfs::set_pulse_width(self.channel as u8, pulse_width as u64)?;
Ok(())
}
pub fn duty_cycle(&self) -> Result<f64> {
let period = sysfs::period(self.channel as u8)? as f64;
let pulse_width = sysfs::pulse_width(self.channel as u8)? as f64;
Ok(if period == 0.0 {
0.0
} else {
(pulse_width / period).max(0.0).min(1.0)
})
}
pub fn set_duty_cycle(&self, duty_cycle: f64) -> Result<()> {
let period = sysfs::period(self.channel as u8)? as f64;
let pulse_width = period * duty_cycle.max(0.0).min(1.0);
sysfs::set_pulse_width(self.channel as u8, pulse_width as u64)?;
Ok(())
}
pub fn polarity(&self) -> Result<Polarity> {
Ok(sysfs::polarity(self.channel as u8)?)
}
pub fn set_polarity(&self, polarity: Polarity) -> Result<()> {
sysfs::set_polarity(self.channel as u8, polarity)?;
Ok(())
}
pub fn is_enabled(&self) -> Result<bool> {
Ok(sysfs::enabled(self.channel as u8)?)
}
pub fn enable(&self) -> Result<()> {
sysfs::set_enabled(self.channel as u8, true)?;
Ok(())
}
pub fn disable(&self) -> Result<()> {
sysfs::set_enabled(self.channel as u8, 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.channel as u8, false);
let _ = sysfs::unexport(self.channel as u8);
}
}
}