From e8199411d5246b6eca312f07bd7f860c17772d1f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 20 Dec 2022 21:28:59 +0100 Subject: [PATCH 1/5] Add PWM SetDuty trait. --- embedded-hal/src/lib.rs | 1 + embedded-hal/src/pwm.rs | 77 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 embedded-hal/src/pwm.rs diff --git a/embedded-hal/src/lib.rs b/embedded-hal/src/lib.rs index 8b65ded47..410305ac4 100644 --- a/embedded-hal/src/lib.rs +++ b/embedded-hal/src/lib.rs @@ -80,6 +80,7 @@ pub mod delay; pub mod digital; pub mod i2c; +pub mod pwm; pub mod serial; pub mod spi; diff --git a/embedded-hal/src/pwm.rs b/embedded-hal/src/pwm.rs new file mode 100644 index 000000000..d76127ecd --- /dev/null +++ b/embedded-hal/src/pwm.rs @@ -0,0 +1,77 @@ +//! Pulse Width Modulation (PWM) traits + +/// Error +pub trait Error: core::fmt::Debug { + /// Convert error to a generic error kind + /// + /// By using this method, errors freely defined by HAL implementations + /// can be converted to a set of generic errors upon which generic + /// code can act. + fn kind(&self) -> ErrorKind; +} + +impl Error for core::convert::Infallible { + fn kind(&self) -> ErrorKind { + match *self {} + } +} + +/// Error kind +/// +/// This represents a common set of operation errors. HAL implementations are +/// free to define more specific or additional error types. However, by providing +/// a mapping to these common errors, generic code can still react to them. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +#[non_exhaustive] +pub enum ErrorKind { + /// A different error occurred. The original error may contain more information. + Other, +} + +impl Error for ErrorKind { + fn kind(&self) -> ErrorKind { + *self + } +} + +impl core::fmt::Display for ErrorKind { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Self::Other => write!( + f, + "A different error occurred. The original error may contain more information" + ), + } + } +} + +/// Error type trait +/// +/// This just defines the error type, to be used by the other traits. +pub trait ErrorType { + /// Error type + type Error: Error; +} + +impl ErrorType for &mut T { + type Error = T::Error; +} + +/// Single PWM channel / pin +pub trait SetDuty: ErrorType { + /// Set the duty cycle. + /// + /// `duty` is the duty cycle. Valid values span the entire `u16` range: + /// + /// - `duty = 0` is considered 0% duty, which makes the pin permanently low. + /// - `duty = u16::MAX` is considered 100% duty, which makes the pin permanently high. + /// + /// Implementations must scale the duty value linearly to the range required by the hardware. + fn set_duty(&mut self, duty: u16) -> Self::Error; +} + +impl SetDuty for &mut T { + fn set_duty(&mut self, duty: u16) -> Self::Error { + T::set_duty(self, duty) + } +} From ff0b1ced6462c59d26b6b2d35bfc04ed9b34d3b9 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Sat, 21 Jan 2023 15:30:52 -0600 Subject: [PATCH 2/5] Add `get_max_duty()` and convenience methods --- embedded-hal/src/pwm.rs | 57 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 50 insertions(+), 7 deletions(-) diff --git a/embedded-hal/src/pwm.rs b/embedded-hal/src/pwm.rs index d76127ecd..ce289cac9 100644 --- a/embedded-hal/src/pwm.rs +++ b/embedded-hal/src/pwm.rs @@ -59,19 +59,62 @@ impl ErrorType for &mut T { /// Single PWM channel / pin pub trait SetDuty: ErrorType { - /// Set the duty cycle. + /// Get the maximum duty cycle value. /// - /// `duty` is the duty cycle. Valid values span the entire `u16` range: + /// This value corresponds to a 100% duty cycle. + fn get_max_duty(&self) -> u16; + + /// Set the duty cycle to `duty / max_duty`. + /// + /// The caller is responsible for ensuring that the duty cycle value is less than or equal to the maximum duty cycle value, + /// as reported by `get_max_duty`. + fn set_duty(&mut self, duty: u16) -> Result<(), Self::Error>; + + /// Set the duty cycle to 0%, or always inactive. + fn set_off(&mut self) -> Result<(), Self::Error> { + self.set_duty(0) + } + + /// Set the duty cycle to 100%, or always active. + fn set_on(&mut self) -> Result<(), Self::Error> { + self.set_duty(self.get_max_duty()) + } + + /// Set the duty cycle to `num / denom`. /// - /// - `duty = 0` is considered 0% duty, which makes the pin permanently low. - /// - `duty = u16::MAX` is considered 100% duty, which makes the pin permanently high. + /// The caller is responsible for ensuring that `num` is less than or equal to `denom`, + /// and that `denom` is not zero. + fn set_fraction(&mut self, num: u16, denom: u16) -> Result<(), Self::Error> { + let duty = num as u32 * self.get_max_duty() as u32 / denom as u32; + self.set_duty(duty as u16) + } + + /// Set the duty cycle to `percent / 100` /// - /// Implementations must scale the duty value linearly to the range required by the hardware. - fn set_duty(&mut self, duty: u16) -> Self::Error; + /// The caller is responsible for ensuring that `percent` is less than or equal to 100. + fn set_percent(&mut self, percent: u8) -> Result<(), Self::Error> { + self.set_fraction(percent as u16, 100) + } } impl SetDuty for &mut T { - fn set_duty(&mut self, duty: u16) -> Self::Error { + fn get_max_duty(&self) -> u16 { + T::get_max_duty(self) + } + fn set_duty(&mut self, duty: u16) -> Result<(), Self::Error> { T::set_duty(self, duty) } + + fn set_off(&mut self) -> Result<(), Self::Error> { + T::set_off(self) + } + fn set_on(&mut self) -> Result<(), Self::Error> { + T::set_on(self) + } + fn set_fraction(&mut self, num: u16, denom: u16) -> Result<(), Self::Error> { + T::set_fraction(self, num, denom) + } + fn set_percent(&mut self, percent: u8) -> Result<(), Self::Error> { + T::set_percent(self, percent) + } } From 3f53ffc9dda88a16778d88e200b641483ad06597 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Sat, 21 Jan 2023 15:37:19 -0600 Subject: [PATCH 3/5] Add `#[inline]` to provided methods --- embedded-hal/src/pwm.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/embedded-hal/src/pwm.rs b/embedded-hal/src/pwm.rs index ce289cac9..39ee88ac9 100644 --- a/embedded-hal/src/pwm.rs +++ b/embedded-hal/src/pwm.rs @@ -71,11 +71,13 @@ pub trait SetDuty: ErrorType { fn set_duty(&mut self, duty: u16) -> Result<(), Self::Error>; /// Set the duty cycle to 0%, or always inactive. + #[inline] fn set_off(&mut self) -> Result<(), Self::Error> { self.set_duty(0) } /// Set the duty cycle to 100%, or always active. + #[inline] fn set_on(&mut self) -> Result<(), Self::Error> { self.set_duty(self.get_max_duty()) } @@ -84,6 +86,7 @@ pub trait SetDuty: ErrorType { /// /// The caller is responsible for ensuring that `num` is less than or equal to `denom`, /// and that `denom` is not zero. + #[inline] fn set_fraction(&mut self, num: u16, denom: u16) -> Result<(), Self::Error> { let duty = num as u32 * self.get_max_duty() as u32 / denom as u32; self.set_duty(duty as u16) @@ -92,6 +95,7 @@ pub trait SetDuty: ErrorType { /// Set the duty cycle to `percent / 100` /// /// The caller is responsible for ensuring that `percent` is less than or equal to 100. + #[inline] fn set_percent(&mut self, percent: u8) -> Result<(), Self::Error> { self.set_fraction(percent as u16, 100) } From d5f9747a060ca4109d215f512aa80a89ccb90df0 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 28 Feb 2023 20:40:33 +0100 Subject: [PATCH 4/5] pwm: Rename "duty" to "duty cycle". --- embedded-hal/src/pwm.rs | 54 ++++++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/embedded-hal/src/pwm.rs b/embedded-hal/src/pwm.rs index 39ee88ac9..1ba9c4df0 100644 --- a/embedded-hal/src/pwm.rs +++ b/embedded-hal/src/pwm.rs @@ -58,28 +58,28 @@ impl ErrorType for &mut T { } /// Single PWM channel / pin -pub trait SetDuty: ErrorType { +pub trait SetDutyCycle: ErrorType { /// Get the maximum duty cycle value. /// /// This value corresponds to a 100% duty cycle. - fn get_max_duty(&self) -> u16; + fn get_max_duty_cycle(&self) -> u16; /// Set the duty cycle to `duty / max_duty`. /// /// The caller is responsible for ensuring that the duty cycle value is less than or equal to the maximum duty cycle value, /// as reported by `get_max_duty`. - fn set_duty(&mut self, duty: u16) -> Result<(), Self::Error>; + fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error>; /// Set the duty cycle to 0%, or always inactive. #[inline] - fn set_off(&mut self) -> Result<(), Self::Error> { - self.set_duty(0) + fn set_duty_cycle_fully_off(&mut self) -> Result<(), Self::Error> { + self.set_duty_cycle(0) } /// Set the duty cycle to 100%, or always active. #[inline] - fn set_on(&mut self) -> Result<(), Self::Error> { - self.set_duty(self.get_max_duty()) + fn set_duty_cycle_fully_on(&mut self) -> Result<(), Self::Error> { + self.set_duty_cycle(self.get_max_duty_cycle()) } /// Set the duty cycle to `num / denom`. @@ -87,38 +87,42 @@ pub trait SetDuty: ErrorType { /// The caller is responsible for ensuring that `num` is less than or equal to `denom`, /// and that `denom` is not zero. #[inline] - fn set_fraction(&mut self, num: u16, denom: u16) -> Result<(), Self::Error> { - let duty = num as u32 * self.get_max_duty() as u32 / denom as u32; - self.set_duty(duty as u16) + fn set_duty_cycle_fraction(&mut self, num: u16, denom: u16) -> Result<(), Self::Error> { + let duty = num as u32 * self.get_max_duty_cycle() as u32 / denom as u32; + self.set_duty_cycle(duty as u16) } /// Set the duty cycle to `percent / 100` /// /// The caller is responsible for ensuring that `percent` is less than or equal to 100. #[inline] - fn set_percent(&mut self, percent: u8) -> Result<(), Self::Error> { - self.set_fraction(percent as u16, 100) + fn set_duty_cycle_percent(&mut self, percent: u8) -> Result<(), Self::Error> { + self.set_duty_cycle_fraction(percent as u16, 100) } } -impl SetDuty for &mut T { - fn get_max_duty(&self) -> u16 { - T::get_max_duty(self) +impl SetDutyCycle for &mut T { + fn get_max_duty_cycle(&self) -> u16 { + T::get_max_duty_cycle(self) } - fn set_duty(&mut self, duty: u16) -> Result<(), Self::Error> { - T::set_duty(self, duty) + + fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> { + T::set_duty_cycle(self, duty) } - fn set_off(&mut self) -> Result<(), Self::Error> { - T::set_off(self) + fn set_duty_cycle_fully_off(&mut self) -> Result<(), Self::Error> { + T::set_duty_cycle_fully_off(self) } - fn set_on(&mut self) -> Result<(), Self::Error> { - T::set_on(self) + + fn set_duty_cycle_fully_on(&mut self) -> Result<(), Self::Error> { + T::set_duty_cycle_fully_on(self) } - fn set_fraction(&mut self, num: u16, denom: u16) -> Result<(), Self::Error> { - T::set_fraction(self, num, denom) + + fn set_duty_cycle_fraction(&mut self, num: u16, denom: u16) -> Result<(), Self::Error> { + T::set_duty_cycle_fraction(self, num, denom) } - fn set_percent(&mut self, percent: u8) -> Result<(), Self::Error> { - T::set_percent(self, percent) + + fn set_duty_cycle_percent(&mut self, percent: u8) -> Result<(), Self::Error> { + T::set_duty_cycle_percent(self, percent) } } From 78a6f06322b8e649ce7762068b7f3236f1de3947 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 28 Feb 2023 20:40:48 +0100 Subject: [PATCH 5/5] Add changelog. --- embedded-hal/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/embedded-hal/CHANGELOG.md b/embedded-hal/CHANGELOG.md index 3bba75f17..57f177103 100644 --- a/embedded-hal/CHANGELOG.md +++ b/embedded-hal/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### Added +- Added `pwm::SetDutyCycle` trait. + ### Changed - gpio: add `ErrorKind` enum for consistency with other traits and for future extensibility. No kinds are defined for now. - delay: make infallible.