diff --git a/CHANGELOG.md b/CHANGELOG.md index d6620a558..3ba38bd5c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,9 +15,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Changed - Swap PWM channel arguments to references - All trait methods have been renamed to remove the `try_` prefix (i.e. `try_send` -> `send`) for consistency. -- Moved all traits into two modules depending on the execution model: `blocking` and `nb` (non-blocking). +- Moved all traits into two sub modules for each feature depending on the execution model: `blocking` and `nb` (non-blocking). For example, the spi traits can now be found under `embedded_hal::spi::blocking` or `embedded_hal::spi::nb`. +- Execution-model-independent definitions have been moved into the feature module. For example, SPI `Phase` is now defined in `embedded_hal::spi::Phase`. For convenience, these definitions are reexported in both of its blocking and non-blocking submodules. - Re-export `nb::{block!, Error, Result}` to avoid version mismatches. These should be used instead of - importing the `nb` crate directly in dependendent crates. + importing the `nb` crate directly in dependent crates. - `blocking::Serial`: renamed `bwrite_all` to `write`, `bflush` to `flush. - Removed `prelude` to avoid method name conflicts between different flavors (blocking, nb) of the same trait. Traits must now be manually imported. - Removed the various `Default` marker traits. diff --git a/src/adc.rs b/src/adc.rs new file mode 100644 index 000000000..3e5567287 --- /dev/null +++ b/src/adc.rs @@ -0,0 +1,95 @@ +//! Analog-digital conversion traits + +/// Non-blocking ADC traits +pub mod nb { + /// A marker trait to identify MCU pins that can be used as inputs to an ADC channel. + /// + /// This marker trait denotes an object, i.e. a GPIO pin, that is ready for use as an input to the + /// ADC. As ADCs channels can be supplied by multiple pins, this trait defines the relationship + /// between the physical interface and the ADC sampling buffer. + /// + /// ``` + /// # use core::marker::PhantomData; + /// # use embedded_hal::adc::nb::Channel; + /// + /// struct Adc1; // Example ADC with single bank of 8 channels + /// struct Gpio1Pin1(PhantomData); + /// struct Analog(()); // marker type to denote a pin in "analog" mode + /// + /// // GPIO 1 pin 1 can supply an ADC channel when it is configured in Analog mode + /// impl Channel for Gpio1Pin1 { + /// type ID = u8; // ADC channels are identified numerically + /// + /// fn channel(&self) -> Self::ID { + /// 7_u8 // GPIO pin 1 is connected to ADC channel 7 + /// } + /// } + /// + /// struct Adc2; // ADC with two banks of 16 channels + /// struct Gpio2PinA(PhantomData); + /// struct AltFun(()); // marker type to denote some alternate function mode for the pin + /// + /// // GPIO 2 pin A can supply an ADC channel when it's configured in some alternate function mode + /// impl Channel for Gpio2PinA { + /// type ID = (u8, u8); // ADC channels are identified by bank number and channel number + /// + /// fn channel(&self) -> Self::ID { + /// (0, 3) // bank 0 channel 3 + /// } + /// } + /// ``` + pub trait Channel { + /// Channel ID type + /// + /// A type used to identify this ADC channel. For example, if the ADC has eight channels, this + /// might be a `u8`. If the ADC has multiple banks of channels, it could be a tuple, like + /// `(u8: bank_id, u8: channel_id)`. + type ID: Copy; + + /// Get the specific ID that identifies this channel, for example `0_u8` for the first ADC + /// channel, if Self::ID is u8. + fn channel(&self) -> Self::ID; + } + + /// ADCs that sample on single channels per request, and do so at the time of the request. + /// + /// This trait is the interface to an ADC that is configured to read a specific channel at the time + /// of the request (in contrast to continuous asynchronous sampling). + /// + /// ``` + /// use embedded_hal::adc::nb::{Channel, OneShot}; + /// + /// struct MyAdc; // 10-bit ADC, with 5 channels + /// # impl MyAdc { + /// # pub fn power_up(&mut self) {} + /// # pub fn power_down(&mut self) {} + /// # pub fn do_conversion(&mut self, chan: u8) -> u16 { 0xAA55_u16 } + /// # } + /// + /// impl OneShot for MyAdc + /// where + /// WORD: From, + /// PIN: Channel, + /// { + /// type Error = (); + /// + /// fn read(&mut self, pin: &mut PIN) -> nb::Result { + /// let chan = 1 << pin.channel(); + /// self.power_up(); + /// let result = self.do_conversion(chan); + /// self.power_down(); + /// Ok(result.into()) + /// } + /// } + /// ``` + pub trait OneShot> { + /// Error type returned by ADC methods + type Error; + + /// Request that the ADC begin a conversion on the specified pin + /// + /// This method takes a `Pin` reference, as it is expected that the ADC will be able to sample + /// whatever channel underlies the pin. + fn read(&mut self, pin: &mut Pin) -> nb::Result; + } +} diff --git a/src/blocking/delay.rs b/src/blocking/delay.rs deleted file mode 100644 index e5a5eea81..000000000 --- a/src/blocking/delay.rs +++ /dev/null @@ -1,32 +0,0 @@ -//! Delays -//! -//! # What's the difference between these traits and the `timer::CountDown` trait? -//! -//! The `Timer` trait provides a *non-blocking* timer abstraction and it's meant to be used to build -//! higher level abstractions like I/O operations with timeouts. OTOH, these delays traits only -//! provide *blocking* functionality. Note that you can also use the `timer::CountDown` trait to -//! implement blocking delays. - -/// Millisecond delay -/// -/// `UXX` denotes the range type of the delay time. `UXX` can be `u8`, `u16`, etc. A single type can -/// implement this trait for different types of `UXX`. -pub trait DelayMs { - /// Enumeration of `DelayMs` errors - type Error; - - /// Pauses execution for `ms` milliseconds - fn delay_ms(&mut self, ms: UXX) -> Result<(), Self::Error>; -} - -/// Microsecond delay -/// -/// `UXX` denotes the range type of the delay time. `UXX` can be `u8`, `u16`, etc. A single type can -/// implement this trait for different types of `UXX`. -pub trait DelayUs { - /// Enumeration of `DelayMs` errors - type Error; - - /// Pauses execution for `us` microseconds - fn delay_us(&mut self, us: UXX) -> Result<(), Self::Error>; -} diff --git a/src/blocking/digital.rs b/src/blocking/digital.rs deleted file mode 100644 index f97c34061..000000000 --- a/src/blocking/digital.rs +++ /dev/null @@ -1,155 +0,0 @@ -//! Digital I/O - -use core::{convert::From, ops::Not}; - -/// Digital output pin state -/// -/// Conversion from `bool` and logical negation are also implemented -/// for this type. -/// ```rust -/// # use embedded_hal::blocking::digital::PinState; -/// let state = PinState::from(false); -/// assert_eq!(state, PinState::Low); -/// assert_eq!(!state, PinState::High); -/// ``` -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub enum PinState { - /// Low pin state - Low, - /// High pin state - High, -} - -impl From for PinState { - fn from(value: bool) -> Self { - match value { - false => PinState::Low, - true => PinState::High, - } - } -} - -impl Not for PinState { - type Output = PinState; - - fn not(self) -> Self::Output { - match self { - PinState::High => PinState::Low, - PinState::Low => PinState::High, - } - } -} - -/// Single digital push-pull output pin -pub trait OutputPin { - /// Error type - type Error; - - /// Drives the pin low - /// - /// *NOTE* the actual electrical state of the pin may not actually be low, e.g. due to external - /// electrical sources - fn set_low(&mut self) -> Result<(), Self::Error>; - - /// Drives the pin high - /// - /// *NOTE* the actual electrical state of the pin may not actually be high, e.g. due to external - /// electrical sources - fn set_high(&mut self) -> Result<(), Self::Error>; - - /// Drives the pin high or low depending on the provided value - /// - /// *NOTE* the actual electrical state of the pin may not actually be high or low, e.g. due to external - /// electrical sources - fn set_state(&mut self, state: PinState) -> Result<(), Self::Error> { - match state { - PinState::Low => self.set_low(), - PinState::High => self.set_high(), - } - } -} - -/// Push-pull output pin that can read its output state -pub trait StatefulOutputPin: OutputPin { - /// Is the pin in drive high mode? - /// - /// *NOTE* this does *not* read the electrical state of the pin - fn is_set_high(&self) -> Result; - - /// Is the pin in drive low mode? - /// - /// *NOTE* this does *not* read the electrical state of the pin - fn is_set_low(&self) -> Result; -} - -/// Output pin that can be toggled -/// -/// See [toggleable](toggleable) to use a software implementation if -/// both [OutputPin](trait.OutputPin.html) and -/// [StatefulOutputPin](trait.StatefulOutputPin.html) are -/// implemented. Otherwise, implement this using hardware mechanisms. -pub trait ToggleableOutputPin { - /// Error type - type Error; - - /// Toggle pin output. - fn toggle(&mut self) -> Result<(), Self::Error>; -} - -/// Single digital input pin -pub trait InputPin { - /// Error type - type Error; - - /// Is the input pin high? - fn is_high(&self) -> Result; - - /// Is the input pin low? - fn is_low(&self) -> Result; -} - -/// Single pin that can switch from input to output mode, and vice-versa. -/// -/// Example use (assumes the `Error` type is the same for the `IoPin`, -/// `InputPin`, and `OutputPin`): -/// -/// ``` -/// use core::time::Duration; -/// use embedded_hal::blocking::digital::{IoPin, InputPin, OutputPin}; -/// -/// pub fn ping_and_read( -/// mut pin: TOutputPin, delay_fn: &dyn Fn(Duration) -> ()) -> Result -/// where -/// TInputPin : InputPin + IoPin, -/// TOutputPin : OutputPin + IoPin, -/// { -/// // Ping -/// pin.set_low()?; -/// delay_fn(Duration::from_millis(10)); -/// pin.set_high()?; -/// -/// // Read -/// let pin = pin.into_input_pin()?; -/// delay_fn(Duration::from_millis(10)); -/// pin.is_high() -/// } -/// ``` -pub trait IoPin -where - TInput: InputPin + IoPin, - TOutput: OutputPin + IoPin, -{ - /// Error type. - type Error; - - /// Tries to convert this pin to input mode. - /// - /// If the pin is already in input mode, this method should succeed. - fn into_input_pin(self) -> Result; - - /// Tries to convert this pin to output mode with the given initial state. - /// - /// If the pin is already in the requested state, this method should - /// succeed. - fn into_output_pin(self, state: PinState) -> Result; -} diff --git a/src/blocking/i2c.rs b/src/blocking/i2c.rs deleted file mode 100644 index a893bad6c..000000000 --- a/src/blocking/i2c.rs +++ /dev/null @@ -1,294 +0,0 @@ -//! Blocking I2C API -//! -//! This API supports 7-bit and 10-bit addresses. Traits feature an `AddressMode` -//! marker type parameter. Two implementation of the `AddressMode` exist: -//! `SevenBitAddress` and `TenBitAddress`. -//! -//! Through this marker types it is possible to implement each address mode for -//! the traits independently in `embedded-hal` implementations and device drivers -//! can depend only on the mode that they support. -//! -//! Additionally, the I2C 10-bit address mode has been developed to be fully -//! backwards compatible with the 7-bit address mode. This allows for a -//! software-emulated 10-bit addressing implementation if the address mode -//! is not supported by the hardware. -//! -//! Since 7-bit addressing is the mode of the majority of I2C devices, -//! `SevenBitAddress` has been set as default mode and thus can be omitted if desired. -//! -//! ## Examples -//! -//! ### `embedded-hal` implementation for an MCU -//! Here is an example of an embedded-hal implementation of the `Write` trait -//! for both modes: -//! ``` -//! # use embedded_hal::blocking::i2c::{SevenBitAddress, TenBitAddress, Write}; -//! /// I2C0 hardware peripheral which supports both 7-bit and 10-bit addressing. -//! pub struct I2c0; -//! -//! impl Write for I2c0 -//! { -//! # type Error = (); -//! # -//! fn write(&mut self, addr: u8, output: &[u8]) -> Result<(), Self::Error> { -//! // ... -//! # Ok(()) -//! } -//! } -//! -//! impl Write for I2c0 -//! { -//! # type Error = (); -//! # -//! fn write(&mut self, addr: u16, output: &[u8]) -> Result<(), Self::Error> { -//! // ... -//! # Ok(()) -//! } -//! } -//! ``` -//! -//! ### Device driver compatible only with 7-bit addresses -//! -//! For demonstration purposes the address mode parameter has been omitted in this example. -//! -//! ``` -//! # use embedded_hal::blocking::i2c::WriteRead; -//! const ADDR: u8 = 0x15; -//! # const TEMP_REGISTER: u8 = 0x1; -//! pub struct TemperatureSensorDriver { -//! i2c: I2C, -//! } -//! -//! impl TemperatureSensorDriver -//! where -//! I2C: WriteRead, -//! { -//! pub fn read_temperature(&mut self) -> Result { -//! let mut temp = [0]; -//! self.i2c -//! .write_read(ADDR, &[TEMP_REGISTER], &mut temp) -//! .and(Ok(temp[0])) -//! } -//! } -//! ``` -//! -//! ### Device driver compatible only with 10-bit addresses -//! -//! ``` -//! # use embedded_hal::blocking::i2c::{TenBitAddress, WriteRead}; -//! const ADDR: u16 = 0x158; -//! # const TEMP_REGISTER: u8 = 0x1; -//! pub struct TemperatureSensorDriver { -//! i2c: I2C, -//! } -//! -//! impl TemperatureSensorDriver -//! where -//! I2C: WriteRead, -//! { -//! pub fn read_temperature(&mut self) -> Result { -//! let mut temp = [0]; -//! self.i2c -//! .write_read(ADDR, &[TEMP_REGISTER], &mut temp) -//! .and(Ok(temp[0])) -//! } -//! } -//! ``` - -use crate::private; - -/// Address mode (7-bit / 10-bit) -/// -/// Note: This trait is sealed and should not be implemented outside of this crate. -pub trait AddressMode: private::Sealed {} - -/// 7-bit address mode type -pub type SevenBitAddress = u8; - -/// 10-bit address mode type -pub type TenBitAddress = u16; - -impl AddressMode for SevenBitAddress {} - -impl AddressMode for TenBitAddress {} - -/// Blocking read -pub trait Read { - /// Error type - type Error; - - /// Reads enough bytes from slave with `address` to fill `buffer` - /// - /// # I2C Events (contract) - /// - /// ``` text - /// Master: ST SAD+R MAK MAK ... NMAK SP - /// Slave: SAK B0 B1 ... BN - /// ``` - /// - /// Where - /// - /// - `ST` = start condition - /// - `SAD+R` = slave address followed by bit 1 to indicate reading - /// - `SAK` = slave acknowledge - /// - `Bi` = ith byte of data - /// - `MAK` = master acknowledge - /// - `NMAK` = master no acknowledge - /// - `SP` = stop condition - fn read(&mut self, address: A, buffer: &mut [u8]) -> Result<(), Self::Error>; -} - -/// Blocking write -pub trait Write { - /// Error type - type Error; - - /// Writes bytes to slave with address `address` - /// - /// # I2C Events (contract) - /// - /// ``` text - /// Master: ST SAD+W B0 B1 ... BN SP - /// Slave: SAK SAK SAK ... SAK - /// ``` - /// - /// Where - /// - /// - `ST` = start condition - /// - `SAD+W` = slave address followed by bit 0 to indicate writing - /// - `SAK` = slave acknowledge - /// - `Bi` = ith byte of data - /// - `SP` = stop condition - fn write(&mut self, address: A, bytes: &[u8]) -> Result<(), Self::Error>; -} - -/// Blocking write (iterator version) -pub trait WriteIter { - /// Error type - type Error; - - /// Writes bytes to slave with address `address` - /// - /// # I2C Events (contract) - /// - /// Same as `Write` - fn write_iter(&mut self, address: A, bytes: B) -> Result<(), Self::Error> - where - B: IntoIterator; -} - -/// Blocking write + read -pub trait WriteRead { - /// Error type - type Error; - - /// Writes bytes to slave with address `address` and then reads enough bytes to fill `buffer` *in a - /// single transaction* - /// - /// # I2C Events (contract) - /// - /// ``` text - /// Master: ST SAD+W O0 O1 ... OM SR SAD+R MAK MAK ... NMAK SP - /// Slave: SAK SAK SAK ... SAK SAK I0 I1 ... IN - /// ``` - /// - /// Where - /// - /// - `ST` = start condition - /// - `SAD+W` = slave address followed by bit 0 to indicate writing - /// - `SAK` = slave acknowledge - /// - `Oi` = ith outgoing byte of data - /// - `SR` = repeated start condition - /// - `SAD+R` = slave address followed by bit 1 to indicate reading - /// - `Ii` = ith incoming byte of data - /// - `MAK` = master acknowledge - /// - `NMAK` = master no acknowledge - /// - `SP` = stop condition - fn write_read( - &mut self, - address: A, - bytes: &[u8], - buffer: &mut [u8], - ) -> Result<(), Self::Error>; -} - -/// Blocking write (iterator version) + read -pub trait WriteIterRead { - /// Error type - type Error; - - /// Writes bytes to slave with address `address` and then reads enough bytes to fill `buffer` *in a - /// single transaction* - /// - /// # I2C Events (contract) - /// - /// Same as the `WriteRead` trait - fn write_iter_read( - &mut self, - address: A, - bytes: B, - buffer: &mut [u8], - ) -> Result<(), Self::Error> - where - B: IntoIterator; -} - -/// Transactional I2C operation. -/// -/// Several operations can be combined as part of a transaction. -#[derive(Debug, PartialEq)] -pub enum Operation<'a> { - /// Read data into the provided buffer - Read(&'a mut [u8]), - /// Write data from the provided buffer - Write(&'a [u8]), -} - -/// Transactional I2C interface. -/// -/// This allows combining operations within an I2C transaction. -pub trait Transactional { - /// Error type - type Error; - - /// Execute the provided operations on the I2C bus. - /// - /// Transaction contract: - /// - Before executing the first operation an ST is sent automatically. This is followed by SAD+R/W as appropriate. - /// - Data from adjacent operations of the same type are sent after each other without an SP or SR. - /// - Between adjacent operations of a different type an SR and SAD+R/W is sent. - /// - After executing the last operation an SP is sent automatically. - /// - If the last operation is a `Read` the master does not send an acknowledge for the last byte. - /// - /// - `ST` = start condition - /// - `SAD+R/W` = slave address followed by bit 1 to indicate reading or 0 to indicate writing - /// - `SR` = repeated start condition - /// - `SP` = stop condition - fn exec<'a>(&mut self, address: A, operations: &mut [Operation<'a>]) - -> Result<(), Self::Error>; -} - -/// Transactional I2C interface (iterator version). -/// -/// This allows combining operation within an I2C transaction. -pub trait TransactionalIter { - /// Error type - type Error; - - /// Execute the provided operations on the I2C bus (iterator version). - /// - /// Transaction contract: - /// - Before executing the first operation an ST is sent automatically. This is followed by SAD+R/W as appropriate. - /// - Data from adjacent operations of the same type are sent after each other without an SP or SR. - /// - Between adjacent operations of a different type an SR and SAD+R/W is sent. - /// - After executing the last operation an SP is sent automatically. - /// - If the last operation is a `Read` the master does not send an acknowledge for the last byte. - /// - /// - `ST` = start condition - /// - `SAD+R/W` = slave address followed by bit 1 to indicate reading or 0 to indicate writing - /// - `SR` = repeated start condition - /// - `SP` = stop condition - fn exec_iter<'a, O>(&mut self, address: A, operations: O) -> Result<(), Self::Error> - where - O: IntoIterator>; -} diff --git a/src/blocking/mod.rs b/src/blocking/mod.rs deleted file mode 100644 index ac0132dc1..000000000 --- a/src/blocking/mod.rs +++ /dev/null @@ -1,14 +0,0 @@ -//! Blocking API -//! -//! In some cases it's possible to implement these blocking traits on top of one of the core HAL -//! traits. To save boilerplate when that's the case a `Default` marker trait may be provided. -//! Implementing that marker trait will opt in your type into a blanket implementation. - -pub mod delay; -pub mod digital; -pub mod i2c; -pub mod pwm; -pub mod qei; -pub mod serial; -pub mod spi; -pub mod watchdog; diff --git a/src/blocking/pwm.rs b/src/blocking/pwm.rs deleted file mode 100644 index 3dee5d770..000000000 --- a/src/blocking/pwm.rs +++ /dev/null @@ -1,129 +0,0 @@ -//! Pulse Width Modulation - -/// Pulse Width Modulation -/// -/// # Examples -/// -/// Use this interface to control the power output of some actuator -/// -/// ``` -/// extern crate embedded_hal as hal; -/// -/// use hal::blocking::pwm::Pwm; -/// -/// fn main() { -/// let mut pwm: Pwm1 = { -/// // .. -/// # Pwm1 -/// }; -/// -/// pwm.set_period(1.khz()).unwrap(); -/// -/// let max_duty = pwm.get_max_duty().unwrap(); -/// -/// // brightest LED -/// pwm.set_duty(&Channel::_1, max_duty).unwrap(); -/// -/// // dimmer LED -/// pwm.set_duty(&Channel::_2, max_duty / 4).unwrap(); -/// } -/// -/// # use core::convert::Infallible; -/// # struct KiloHertz(u32); -/// # trait U32Ext { fn khz(self) -> KiloHertz; } -/// # impl U32Ext for u32 { fn khz(self) -> KiloHertz { KiloHertz(self) } } -/// # enum Channel { _1, _2 } -/// # struct Pwm1; -/// # impl hal::blocking::pwm::Pwm for Pwm1 { -/// # type Error = Infallible; -/// # type Channel = Channel; -/// # type Time = KiloHertz; -/// # type Duty = u16; -/// # fn disable(&mut self, _: &Channel) -> Result<(), Self::Error> { unimplemented!() } -/// # fn enable(&mut self, _: &Channel) -> Result<(), Self::Error> { unimplemented!() } -/// # fn get_duty(&self, _: &Channel) -> Result { unimplemented!() } -/// # fn get_max_duty(&self) -> Result { Ok(0) } -/// # fn set_duty(&mut self, _: &Channel, _: u16) -> Result<(), Self::Error> { Ok(()) } -/// # fn get_period(&self) -> Result { unimplemented!() } -/// # fn set_period(&mut self, _: T) -> Result<(), Self::Error> where T: Into { Ok(()) } -/// # } -/// ``` -// unproven reason: pre-singletons API. The `PwmPin` trait seems more useful because it models independent -// PWM channels. Here a certain number of channels are multiplexed in a single implementer. -pub trait Pwm { - /// Enumeration of `Pwm` errors - type Error; - - /// Enumeration of channels that can be used with this `Pwm` interface - /// - /// If your `Pwm` interface has no channels you can use the type `()` - /// here - type Channel; - - /// A time unit that can be converted into a human time unit (e.g. seconds) - type Time; - - /// Type for the `duty` methods - /// - /// The implementer is free to choose a float / percentage representation - /// (e.g. `0.0 .. 1.0`) or an integer representation (e.g. `0 .. 65535`) - type Duty; - - /// Disables a PWM `channel` - fn disable(&mut self, channel: &Self::Channel) -> Result<(), Self::Error>; - - /// Enables a PWM `channel` - fn enable(&mut self, channel: &Self::Channel) -> Result<(), Self::Error>; - - /// Returns the current PWM period - fn get_period(&self) -> Result; - - /// Returns the current duty cycle - /// - /// While the pin is transitioning to the new duty cycle after a `set_duty` call, this may - /// return the old or the new duty cycle depending on the implementation. - fn get_duty(&self, channel: &Self::Channel) -> Result; - - /// Returns the maximum duty cycle value - fn get_max_duty(&self) -> Result; - - /// Sets a new duty cycle - fn set_duty(&mut self, channel: &Self::Channel, duty: Self::Duty) -> Result<(), Self::Error>; - - /// Sets a new PWM period - fn set_period

(&mut self, period: P) -> Result<(), Self::Error> - where - P: Into; -} - -/// A single PWM channel / pin -/// -/// See `Pwm` for details -pub trait PwmPin { - /// Enumeration of `PwmPin` errors - type Error; - - /// Type for the `duty` methods - /// - /// The implementer is free to choose a float / percentage representation - /// (e.g. `0.0 .. 1.0`) or an integer representation (e.g. `0 .. 65535`) - type Duty; - - /// Disables a PWM `channel` - fn disable(&mut self) -> Result<(), Self::Error>; - - /// Enables a PWM `channel` - fn enable(&mut self) -> Result<(), Self::Error>; - - /// Returns the current duty cycle - /// - /// While the pin is transitioning to the new duty cycle after a `set_duty` call, this may - /// return the old or the new duty cycle depending on the implementation. - fn get_duty(&self) -> Result; - - /// Returns the maximum duty cycle value - fn get_max_duty(&self) -> Result; - - /// Sets a new duty cycle - fn set_duty(&mut self, duty: Self::Duty) -> Result<(), Self::Error>; -} diff --git a/src/blocking/qei.rs b/src/blocking/qei.rs deleted file mode 100644 index 2fdc1733a..000000000 --- a/src/blocking/qei.rs +++ /dev/null @@ -1,79 +0,0 @@ -//! Quadrature encoder interface - -/// Quadrature encoder interface -/// -/// # Examples -/// -/// You can use this interface to measure the speed of a motor -/// -/// ``` -/// extern crate embedded_hal as hal; -/// #[macro_use(block)] -/// extern crate nb; -/// -/// use hal::blocking::qei::Qei; -/// use hal::nb::timer::CountDown; -/// -/// fn main() { -/// let mut qei: Qei1 = { -/// // .. -/// # Qei1 -/// }; -/// let mut timer: Timer6 = { -/// // .. -/// # Timer6 -/// }; -/// -/// -/// let before = qei.count().unwrap(); -/// timer.start(1.s()).unwrap(); -/// block!(timer.wait()); -/// let after = qei.count().unwrap(); -/// -/// let speed = after.wrapping_sub(before); -/// println!("Speed: {} pulses per second", speed); -/// } -/// -/// # use core::convert::Infallible; -/// # struct Seconds(u32); -/// # trait U32Ext { fn s(self) -> Seconds; } -/// # impl U32Ext for u32 { fn s(self) -> Seconds { Seconds(self) } } -/// # struct Qei1; -/// # impl hal::blocking::qei::Qei for Qei1 { -/// # type Error = Infallible; -/// # type Count = u16; -/// # fn count(&self) -> Result { Ok(0) } -/// # fn direction(&self) -> Result<::hal::blocking::qei::Direction, Self::Error> { unimplemented!() } -/// # } -/// # struct Timer6; -/// # impl hal::nb::timer::CountDown for Timer6 { -/// # type Error = Infallible; -/// # type Time = Seconds; -/// # fn start(&mut self, _: T) -> Result<(), Infallible> where T: Into { Ok(()) } -/// # fn wait(&mut self) -> ::nb::Result<(), Infallible> { Ok(()) } -/// # } -/// ``` -// unproven reason: needs to be re-evaluated in the new singletons world. At the very least this needs a -// reference implementation -pub trait Qei { - /// Enumeration of `Qei` errors - type Error; - - /// The type of the value returned by `count` - type Count; - - /// Returns the current pulse count of the encoder - fn count(&self) -> Result; - - /// Returns the count direction - fn direction(&self) -> Result; -} - -/// Count direction -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub enum Direction { - /// 3, 2, 1 - Downcounting, - /// 1, 2, 3 - Upcounting, -} diff --git a/src/blocking/watchdog.rs b/src/blocking/watchdog.rs deleted file mode 100644 index 19fd30e64..000000000 --- a/src/blocking/watchdog.rs +++ /dev/null @@ -1,60 +0,0 @@ -//! Traits for interactions with a processors watchdog timer. - -/// Feeds an existing watchdog to ensure the processor isn't reset. Sometimes -/// the "feeding" operation is also referred to as "refreshing". -pub trait Watchdog { - /// An enumeration of `Watchdog` errors. - /// - /// For infallible implementations, will be `Infallible` - type Error; - - /// Triggers the watchdog. This must be done once the watchdog is started - /// to prevent the processor being reset. - fn feed(&mut self) -> Result<(), Self::Error>; -} - -/// Enables A watchdog timer to reset the processor if software is frozen or -/// stalled. -pub trait Enable { - /// An enumeration of `Enable` errors. - /// - /// For infallible implementations, will be `Infallible` - type Error; - - /// Unit of time used by the watchdog. - type Time; - - /// The started watchdog that should be `feed()`. - type Target: Watchdog; - - /// Starts the watchdog with a given period, typically once this is done - /// the watchdog needs to be `feed()` periodically, or the processor would be - /// reset. - /// - /// This consumes the value and returns the `Watchdog` trait that you must - /// `feed()`. - fn start(self, period: T) -> Result - where - T: Into; -} - -/// Disables a running watchdog timer so the processor won't be reset. -/// -/// Not all watchdog timers support disable operation after they've been enabled. -/// In this case, hardware support libraries would not implement this trait -/// and hardware-agnostic libraries should consider not requiring it. -pub trait Disable { - /// An enumeration of `Disable` errors. - /// - /// For infallible implementations, will be `Infallible` - type Error; - - /// Disabled watchdog instance that can be enabled. - type Target: Enable; - - /// Disables the watchdog. - /// - /// This stops the watchdog and returns an instance implementing the - /// `Enable` trait so that it can be started again. - fn disable(self) -> Result; -} diff --git a/src/nb/capture.rs b/src/capture.rs similarity index 98% rename from src/nb/capture.rs rename to src/capture.rs index 3e1cea29e..428fbf635 100644 --- a/src/nb/capture.rs +++ b/src/capture.rs @@ -1,5 +1,7 @@ //! Input capture +/// Non-blocking input capture traits +pub mod nb { /// Input capture /// /// # Examples @@ -92,3 +94,4 @@ pub trait Capture { where R: Into; } +} diff --git a/src/delay.rs b/src/delay.rs new file mode 100644 index 000000000..5c1c856b7 --- /dev/null +++ b/src/delay.rs @@ -0,0 +1,35 @@ +//! Delays +//! +//! # What's the difference between these traits and the `timer::CountDown` trait? +//! +//! The `Timer` trait provides a *non-blocking* timer abstraction and it's meant to be used to build +//! higher level abstractions like I/O operations with timeouts. OTOH, these delays traits only +//! provide *blocking* functionality. Note that you can also use the `timer::CountDown` trait to +//! implement blocking delays. + +/// Blocking delay traits +pub mod blocking { + /// Millisecond delay + /// + /// `UXX` denotes the range type of the delay time. `UXX` can be `u8`, `u16`, etc. A single type can + /// implement this trait for different types of `UXX`. + pub trait DelayMs { + /// Enumeration of `DelayMs` errors + type Error; + + /// Pauses execution for `ms` milliseconds + fn delay_ms(&mut self, ms: UXX) -> Result<(), Self::Error>; + } + + /// Microsecond delay + /// + /// `UXX` denotes the range type of the delay time. `UXX` can be `u8`, `u16`, etc. A single type can + /// implement this trait for different types of `UXX`. + pub trait DelayUs { + /// Enumeration of `DelayMs` errors + type Error; + + /// Pauses execution for `us` microseconds + fn delay_us(&mut self, us: UXX) -> Result<(), Self::Error>; + } +} diff --git a/src/digital.rs b/src/digital.rs new file mode 100644 index 000000000..606a00bef --- /dev/null +++ b/src/digital.rs @@ -0,0 +1,164 @@ +//! Digital I/O +//! +//! In some cases it's possible to implement these blocking traits on top of one of the core HAL +//! traits. To save boilerplate when that's the case a `Default` marker trait may be provided. +//! Implementing that marker trait will opt in your type into a blanket implementation. + +use core::{convert::From, ops::Not}; + +/// Digital output pin state +/// +/// Conversion from `bool` and logical negation are also implemented +/// for this type. +/// ```rust +/// # use embedded_hal::digital::PinState; +/// let state = PinState::from(false); +/// assert_eq!(state, PinState::Low); +/// assert_eq!(!state, PinState::High); +/// ``` +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum PinState { + /// Low pin state + Low, + /// High pin state + High, +} + +impl From for PinState { + fn from(value: bool) -> Self { + match value { + false => PinState::Low, + true => PinState::High, + } + } +} + +impl Not for PinState { + type Output = PinState; + + fn not(self) -> Self::Output { + match self { + PinState::High => PinState::Low, + PinState::Low => PinState::High, + } + } +} + +/// Blocking digital I/O traits +pub mod blocking { + use super::PinState; + + /// Single digital push-pull output pin + pub trait OutputPin { + /// Error type + type Error; + + /// Drives the pin low + /// + /// *NOTE* the actual electrical state of the pin may not actually be low, e.g. due to external + /// electrical sources + fn set_low(&mut self) -> Result<(), Self::Error>; + + /// Drives the pin high + /// + /// *NOTE* the actual electrical state of the pin may not actually be high, e.g. due to external + /// electrical sources + fn set_high(&mut self) -> Result<(), Self::Error>; + + /// Drives the pin high or low depending on the provided value + /// + /// *NOTE* the actual electrical state of the pin may not actually be high or low, e.g. due to external + /// electrical sources + fn set_state(&mut self, state: PinState) -> Result<(), Self::Error> { + match state { + PinState::Low => self.set_low(), + PinState::High => self.set_high(), + } + } + } + + /// Push-pull output pin that can read its output state + pub trait StatefulOutputPin: OutputPin { + /// Is the pin in drive high mode? + /// + /// *NOTE* this does *not* read the electrical state of the pin + fn is_set_high(&self) -> Result; + + /// Is the pin in drive low mode? + /// + /// *NOTE* this does *not* read the electrical state of the pin + fn is_set_low(&self) -> Result; + } + + /// Output pin that can be toggled + /// + /// See [toggleable](toggleable) to use a software implementation if + /// both [OutputPin](trait.OutputPin.html) and + /// [StatefulOutputPin](trait.StatefulOutputPin.html) are + /// implemented. Otherwise, implement this using hardware mechanisms. + pub trait ToggleableOutputPin { + /// Error type + type Error; + + /// Toggle pin output. + fn toggle(&mut self) -> Result<(), Self::Error>; + } + + /// Single digital input pin + pub trait InputPin { + /// Error type + type Error; + + /// Is the input pin high? + fn is_high(&self) -> Result; + + /// Is the input pin low? + fn is_low(&self) -> Result; + } + + /// Single pin that can switch from input to output mode, and vice-versa. + /// + /// Example use (assumes the `Error` type is the same for the `IoPin`, + /// `InputPin`, and `OutputPin`): + /// + /// ``` + /// use core::time::Duration; + /// use embedded_hal::digital::blocking::{IoPin, InputPin, OutputPin}; + /// + /// pub fn ping_and_read( + /// mut pin: TOutputPin, delay_fn: &dyn Fn(Duration) -> ()) -> Result + /// where + /// TInputPin : InputPin + IoPin, + /// TOutputPin : OutputPin + IoPin, + /// { + /// // Ping + /// pin.set_low()?; + /// delay_fn(Duration::from_millis(10)); + /// pin.set_high()?; + /// + /// // Read + /// let pin = pin.into_input_pin()?; + /// delay_fn(Duration::from_millis(10)); + /// pin.is_high() + /// } + /// ``` + pub trait IoPin + where + TInput: InputPin + IoPin, + TOutput: OutputPin + IoPin, + { + /// Error type. + type Error; + + /// Tries to convert this pin to input mode. + /// + /// If the pin is already in input mode, this method should succeed. + fn into_input_pin(self) -> Result; + + /// Tries to convert this pin to output mode with the given initial state. + /// + /// If the pin is already in the requested state, this method should + /// succeed. + fn into_output_pin(self, state: PinState) -> Result; + } +} diff --git a/src/fmt.rs b/src/fmt.rs index 81e23cef5..324ffd294 100644 --- a/src/fmt.rs +++ b/src/fmt.rs @@ -3,7 +3,7 @@ //! TODO write example of usage use core::fmt::{Result, Write}; -impl Write for dyn crate::nb::serial::Write + '_ +impl Write for dyn crate::serial::nb::Write + '_ where Word: From, { diff --git a/src/i2c.rs b/src/i2c.rs new file mode 100644 index 000000000..16d29c58b --- /dev/null +++ b/src/i2c.rs @@ -0,0 +1,307 @@ +//! Blocking I2C API +//! +//! This API supports 7-bit and 10-bit addresses. Traits feature an `AddressMode` +//! marker type parameter. Two implementation of the `AddressMode` exist: +//! `SevenBitAddress` and `TenBitAddress`. +//! +//! Through this marker types it is possible to implement each address mode for +//! the traits independently in `embedded-hal` implementations and device drivers +//! can depend only on the mode that they support. +//! +//! Additionally, the I2C 10-bit address mode has been developed to be fully +//! backwards compatible with the 7-bit address mode. This allows for a +//! software-emulated 10-bit addressing implementation if the address mode +//! is not supported by the hardware. +//! +//! Since 7-bit addressing is the mode of the majority of I2C devices, +//! `SevenBitAddress` has been set as default mode and thus can be omitted if desired. +//! +//! In some cases it's possible to implement these blocking traits on top of one of the core HAL +//! traits. To save boilerplate when that's the case a `Default` marker trait may be provided. +//! Implementing that marker trait will opt in your type into a blanket implementation. +//! +//! ## Examples +//! +//! ### `embedded-hal` implementation for an MCU +//! Here is an example of an embedded-hal implementation of the `Write` trait +//! for both modes: +//! ``` +//! # use embedded_hal::i2c::{SevenBitAddress, TenBitAddress, blocking::Write}; +//! /// I2C0 hardware peripheral which supports both 7-bit and 10-bit addressing. +//! pub struct I2c0; +//! +//! impl Write for I2c0 +//! { +//! # type Error = (); +//! # +//! fn write(&mut self, addr: u8, output: &[u8]) -> Result<(), Self::Error> { +//! // ... +//! # Ok(()) +//! } +//! } +//! +//! impl Write for I2c0 +//! { +//! # type Error = (); +//! # +//! fn write(&mut self, addr: u16, output: &[u8]) -> Result<(), Self::Error> { +//! // ... +//! # Ok(()) +//! } +//! } +//! ``` +//! +//! ### Device driver compatible only with 7-bit addresses +//! +//! For demonstration purposes the address mode parameter has been omitted in this example. +//! +//! ``` +//! # use embedded_hal::i2c::blocking::WriteRead; +//! const ADDR: u8 = 0x15; +//! # const TEMP_REGISTER: u8 = 0x1; +//! pub struct TemperatureSensorDriver { +//! i2c: I2C, +//! } +//! +//! impl TemperatureSensorDriver +//! where +//! I2C: WriteRead, +//! { +//! pub fn read_temperature(&mut self) -> Result { +//! let mut temp = [0]; +//! self.i2c +//! .write_read(ADDR, &[TEMP_REGISTER], &mut temp) +//! .and(Ok(temp[0])) +//! } +//! } +//! ``` +//! +//! ### Device driver compatible only with 10-bit addresses +//! +//! ``` +//! # use embedded_hal::i2c::{TenBitAddress, blocking::WriteRead}; +//! const ADDR: u16 = 0x158; +//! # const TEMP_REGISTER: u8 = 0x1; +//! pub struct TemperatureSensorDriver { +//! i2c: I2C, +//! } +//! +//! impl TemperatureSensorDriver +//! where +//! I2C: WriteRead, +//! { +//! pub fn read_temperature(&mut self) -> Result { +//! let mut temp = [0]; +//! self.i2c +//! .write_read(ADDR, &[TEMP_REGISTER], &mut temp) +//! .and(Ok(temp[0])) +//! } +//! } +//! ``` + +use crate::private; + +/// Address mode (7-bit / 10-bit) +/// +/// Note: This trait is sealed and should not be implemented outside of this crate. +pub trait AddressMode: private::Sealed {} + +/// 7-bit address mode type +pub type SevenBitAddress = u8; + +/// 10-bit address mode type +pub type TenBitAddress = u16; + +impl AddressMode for SevenBitAddress {} + +impl AddressMode for TenBitAddress {} + +/// Blocking I2C traits +pub mod blocking { + + use super::{AddressMode, SevenBitAddress}; + + /// Blocking read + pub trait Read { + /// Error type + type Error; + + /// Reads enough bytes from slave with `address` to fill `buffer` + /// + /// # I2C Events (contract) + /// + /// ``` text + /// Master: ST SAD+R MAK MAK ... NMAK SP + /// Slave: SAK B0 B1 ... BN + /// ``` + /// + /// Where + /// + /// - `ST` = start condition + /// - `SAD+R` = slave address followed by bit 1 to indicate reading + /// - `SAK` = slave acknowledge + /// - `Bi` = ith byte of data + /// - `MAK` = master acknowledge + /// - `NMAK` = master no acknowledge + /// - `SP` = stop condition + fn read(&mut self, address: A, buffer: &mut [u8]) -> Result<(), Self::Error>; + } + + /// Blocking write + pub trait Write { + /// Error type + type Error; + + /// Writes bytes to slave with address `address` + /// + /// # I2C Events (contract) + /// + /// ``` text + /// Master: ST SAD+W B0 B1 ... BN SP + /// Slave: SAK SAK SAK ... SAK + /// ``` + /// + /// Where + /// + /// - `ST` = start condition + /// - `SAD+W` = slave address followed by bit 0 to indicate writing + /// - `SAK` = slave acknowledge + /// - `Bi` = ith byte of data + /// - `SP` = stop condition + fn write(&mut self, address: A, bytes: &[u8]) -> Result<(), Self::Error>; + } + + /// Blocking write (iterator version) + pub trait WriteIter { + /// Error type + type Error; + + /// Writes bytes to slave with address `address` + /// + /// # I2C Events (contract) + /// + /// Same as `Write` + fn write_iter(&mut self, address: A, bytes: B) -> Result<(), Self::Error> + where + B: IntoIterator; + } + + /// Blocking write + read + pub trait WriteRead { + /// Error type + type Error; + + /// Writes bytes to slave with address `address` and then reads enough bytes to fill `buffer` *in a + /// single transaction* + /// + /// # I2C Events (contract) + /// + /// ``` text + /// Master: ST SAD+W O0 O1 ... OM SR SAD+R MAK MAK ... NMAK SP + /// Slave: SAK SAK SAK ... SAK SAK I0 I1 ... IN + /// ``` + /// + /// Where + /// + /// - `ST` = start condition + /// - `SAD+W` = slave address followed by bit 0 to indicate writing + /// - `SAK` = slave acknowledge + /// - `Oi` = ith outgoing byte of data + /// - `SR` = repeated start condition + /// - `SAD+R` = slave address followed by bit 1 to indicate reading + /// - `Ii` = ith incoming byte of data + /// - `MAK` = master acknowledge + /// - `NMAK` = master no acknowledge + /// - `SP` = stop condition + fn write_read( + &mut self, + address: A, + bytes: &[u8], + buffer: &mut [u8], + ) -> Result<(), Self::Error>; + } + + /// Blocking write (iterator version) + read + pub trait WriteIterRead { + /// Error type + type Error; + + /// Writes bytes to slave with address `address` and then reads enough bytes to fill `buffer` *in a + /// single transaction* + /// + /// # I2C Events (contract) + /// + /// Same as the `WriteRead` trait + fn write_iter_read( + &mut self, + address: A, + bytes: B, + buffer: &mut [u8], + ) -> Result<(), Self::Error> + where + B: IntoIterator; + } + + /// Transactional I2C operation. + /// + /// Several operations can be combined as part of a transaction. + #[derive(Debug, PartialEq)] + pub enum Operation<'a> { + /// Read data into the provided buffer + Read(&'a mut [u8]), + /// Write data from the provided buffer + Write(&'a [u8]), + } + + /// Transactional I2C interface. + /// + /// This allows combining operations within an I2C transaction. + pub trait Transactional { + /// Error type + type Error; + + /// Execute the provided operations on the I2C bus. + /// + /// Transaction contract: + /// - Before executing the first operation an ST is sent automatically. This is followed by SAD+R/W as appropriate. + /// - Data from adjacent operations of the same type are sent after each other without an SP or SR. + /// - Between adjacent operations of a different type an SR and SAD+R/W is sent. + /// - After executing the last operation an SP is sent automatically. + /// - If the last operation is a `Read` the master does not send an acknowledge for the last byte. + /// + /// - `ST` = start condition + /// - `SAD+R/W` = slave address followed by bit 1 to indicate reading or 0 to indicate writing + /// - `SR` = repeated start condition + /// - `SP` = stop condition + fn exec<'a>( + &mut self, + address: A, + operations: &mut [Operation<'a>], + ) -> Result<(), Self::Error>; + } + + /// Transactional I2C interface (iterator version). + /// + /// This allows combining operation within an I2C transaction. + pub trait TransactionalIter { + /// Error type + type Error; + + /// Execute the provided operations on the I2C bus (iterator version). + /// + /// Transaction contract: + /// - Before executing the first operation an ST is sent automatically. This is followed by SAD+R/W as appropriate. + /// - Data from adjacent operations of the same type are sent after each other without an SP or SR. + /// - Between adjacent operations of a different type an SR and SAD+R/W is sent. + /// - After executing the last operation an SP is sent automatically. + /// - If the last operation is a `Read` the master does not send an acknowledge for the last byte. + /// + /// - `ST` = start condition + /// - `SAD+R/W` = slave address followed by bit 1 to indicate reading or 0 to indicate writing + /// - `SR` = repeated start condition + /// - `SP` = stop condition + fn exec_iter<'a, O>(&mut self, address: A, operations: O) -> Result<(), Self::Error> + where + O: IntoIterator>; + } +} diff --git a/src/lib.rs b/src/lib.rs index ddf5b6564..438f3f3c3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -72,7 +72,7 @@ //! Here's how a HAL trait may look like: //! //! ``` -//! use nb; +//! use embedded_hal::nb; //! //! /// A serial interface //! pub trait Serial { @@ -98,7 +98,7 @@ //! those cases `nb::Result<_, Infallible>` is used. //! //! ``` -//! use nb; +//! use embedded_hal::nb; //! //! # use std as core; //! use ::core::convert::Infallible; @@ -131,7 +131,7 @@ //! // An implementation of the `embedded-hal` traits for STM32F1xx microcontrollers //! //! use embedded_hal as hal; -//! use nb; +//! use hal::nb; //! //! // device crate //! use stm32f1::stm32f103::USART1; @@ -150,7 +150,7 @@ //! // omitted: other error variants //! } //! -//! impl hal::nb::serial::Read for Serial { +//! impl hal::serial::nb::Read for Serial { //! type Error = Error; //! //! fn read(&mut self) -> nb::Result { @@ -172,7 +172,7 @@ //! } //! } //! -//! impl hal::nb::serial::Write for Serial { +//! impl hal::serial::nb::Write for Serial { //! type Error = Error; //! //! fn write(&mut self, byte: u8) -> nb::Result<(), Error> { @@ -203,8 +203,8 @@ //! //! ``` //! use crate::stm32f1xx_hal::Serial1; -//! use embedded_hal::nb::serial::Write; -//! use nb::block; +//! use embedded_hal::serial::nb::Write; +//! use embedded_hal::nb::block; //! //! # fn main() { //! let mut serial: Serial1 = { @@ -220,7 +220,7 @@ //! # } //! //! # mod stm32f1xx_hal { -//! # use nb; +//! # use embedded_hal::nb; //! # use core::convert::Infallible; //! # pub struct Serial1; //! # impl Serial1 { @@ -248,12 +248,12 @@ //! //! ``` //! use embedded_hal as hal; -//! use nb::block; -//! use hal::nb::serial::Write; +//! use hal::nb::block; +//! use hal::serial::nb::Write; //! //! fn write_all(serial: &mut S, buffer: &[u8]) -> Result<(), S::Error> //! where -//! S: hal::nb::serial::Write +//! S: hal::serial::nb::Write //! { //! for &byte in buffer { //! block!(serial.write(byte))?; @@ -269,10 +269,10 @@ //! //! ``` //! use embedded_hal as hal; -//! use nb; +//! use hal::nb; //! -//! use hal::nb::serial::Write; -//! use hal::nb::timer::CountDown; +//! use hal::serial::nb::Write; +//! use hal::timer::nb::CountDown; //! //! enum Error { //! /// Serial interface error @@ -287,8 +287,8 @@ //! timeout: T::Time, //! ) -> Result> //! where -//! T: hal::nb::timer::CountDown, -//! S: hal::nb::serial::Read, +//! T: hal::timer::nb::CountDown, +//! S: hal::serial::nb::Read, //! { //! timer.start(timeout).map_err(Error::TimedOut)?; //! @@ -324,14 +324,14 @@ //! ``` //! # use std as core; //! use embedded_hal as hal; -//! use nb; +//! use hal::nb; //! -//! use hal::nb::serial::Write; +//! use hal::serial::nb::Write; //! use ::core::convert::Infallible; //! //! fn flush(serial: &mut S, cb: &mut CircularBuffer) //! where -//! S: hal::nb::serial::Write, +//! S: hal::serial::nb::Write, //! { //! loop { //! if let Some(byte) = cb.peek() { @@ -395,7 +395,7 @@ //! # fn deref_mut(&mut self) -> &mut T { self.0 } //! # } //! # struct Serial1; -//! # impl hal::nb::serial::Write for Serial1 { +//! # impl hal::serial::nb::Write for Serial1 { //! # type Error = Infallible; //! # fn write(&mut self, _: u8) -> nb::Result<(), Infallible> { Err(::nb::Error::WouldBlock) } //! # fn flush(&mut self) -> nb::Result<(), Infallible> { Err(::nb::Error::WouldBlock) } @@ -414,12 +414,21 @@ #![deny(missing_docs)] #![no_std] -pub mod blocking; pub mod fmt; -pub mod nb; +pub use nb; +pub mod adc; +pub mod delay; +pub mod digital; +pub mod i2c; +pub mod pwm; +pub mod qei; +pub mod serial; +pub mod spi; +pub mod timer; +pub mod watchdog; mod private { - use crate::blocking::i2c::{SevenBitAddress, TenBitAddress}; + use crate::i2c::{SevenBitAddress, TenBitAddress}; pub trait Sealed {} impl Sealed for SevenBitAddress {} diff --git a/src/nb/adc.rs b/src/nb/adc.rs deleted file mode 100644 index 17de484dd..000000000 --- a/src/nb/adc.rs +++ /dev/null @@ -1,92 +0,0 @@ -//! Analog-digital conversion traits - -/// A marker trait to identify MCU pins that can be used as inputs to an ADC channel. -/// -/// This marker trait denotes an object, i.e. a GPIO pin, that is ready for use as an input to the -/// ADC. As ADCs channels can be supplied by multiple pins, this trait defines the relationship -/// between the physical interface and the ADC sampling buffer. -/// -/// ``` -/// # use core::marker::PhantomData; -/// # use embedded_hal::nb::adc::Channel; -/// -/// struct Adc1; // Example ADC with single bank of 8 channels -/// struct Gpio1Pin1(PhantomData); -/// struct Analog(()); // marker type to denote a pin in "analog" mode -/// -/// // GPIO 1 pin 1 can supply an ADC channel when it is configured in Analog mode -/// impl Channel for Gpio1Pin1 { -/// type ID = u8; // ADC channels are identified numerically -/// -/// fn channel(&self) -> Self::ID { -/// 7_u8 // GPIO pin 1 is connected to ADC channel 7 -/// } -/// } -/// -/// struct Adc2; // ADC with two banks of 16 channels -/// struct Gpio2PinA(PhantomData); -/// struct AltFun(()); // marker type to denote some alternate function mode for the pin -/// -/// // GPIO 2 pin A can supply an ADC channel when it's configured in some alternate function mode -/// impl Channel for Gpio2PinA { -/// type ID = (u8, u8); // ADC channels are identified by bank number and channel number -/// -/// fn channel(&self) -> Self::ID { -/// (0, 3) // bank 0 channel 3 -/// } -/// } -/// ``` -pub trait Channel { - /// Channel ID type - /// - /// A type used to identify this ADC channel. For example, if the ADC has eight channels, this - /// might be a `u8`. If the ADC has multiple banks of channels, it could be a tuple, like - /// `(u8: bank_id, u8: channel_id)`. - type ID: Copy; - - /// Get the specific ID that identifies this channel, for example `0_u8` for the first ADC - /// channel, if Self::ID is u8. - fn channel(&self) -> Self::ID; -} - -/// ADCs that sample on single channels per request, and do so at the time of the request. -/// -/// This trait is the interface to an ADC that is configured to read a specific channel at the time -/// of the request (in contrast to continuous asynchronous sampling). -/// -/// ``` -/// use embedded_hal::nb::adc::{Channel, OneShot}; -/// -/// struct MyAdc; // 10-bit ADC, with 5 channels -/// # impl MyAdc { -/// # pub fn power_up(&mut self) {} -/// # pub fn power_down(&mut self) {} -/// # pub fn do_conversion(&mut self, chan: u8) -> u16 { 0xAA55_u16 } -/// # } -/// -/// impl OneShot for MyAdc -/// where -/// WORD: From, -/// PIN: Channel, -/// { -/// type Error = (); -/// -/// fn read(&mut self, pin: &mut PIN) -> nb::Result { -/// let chan = 1 << pin.channel(); -/// self.power_up(); -/// let result = self.do_conversion(chan); -/// self.power_down(); -/// Ok(result.into()) -/// } -/// } -/// ``` -pub trait OneShot> { - /// Error type returned by ADC methods - type Error; - - /// Request that the ADC begin a conversion on the specified pin - /// - /// This method takes a `Pin` reference, as it is expected that the ADC will be able to sample - /// whatever channel underlies the pin. - fn read(&mut self, pin: &mut Pin) -> nb::Result; -} diff --git a/src/nb/mod.rs b/src/nb/mod.rs deleted file mode 100644 index c687bdad5..000000000 --- a/src/nb/mod.rs +++ /dev/null @@ -1,22 +0,0 @@ -//! Non-blocking API -//! -//! These traits make use of the [`nb`] crate -//! (*please go read that crate documentation before continuing*) to abstract over -//! the execution model and to also provide an optional blocking operation mode. -//! -//! The `nb::Result` enum is used to add an [`Error::WouldBlock`] variant to the errors -//! of the traits. Using this it is possible to execute actions in a non-blocking -//! way. -//! -//! `block!`, `Result` and `Error` from the [`nb`] crate are re-exported here to avoid -//! crate version mismatches. These should be used instead of importing the `nb` crate -//! directly again in dependent crates. -//! -//! [`nb`]: https://crates.io/crates/nb - -pub use nb::{block, Error, Result}; -pub mod adc; -pub mod capture; -pub mod serial; -pub mod spi; -pub mod timer; diff --git a/src/nb/spi.rs b/src/nb/spi.rs deleted file mode 100644 index c6dbab4bd..000000000 --- a/src/nb/spi.rs +++ /dev/null @@ -1,82 +0,0 @@ -//! Serial Peripheral Interface - -/// Full duplex (master mode) -/// -/// # Notes -/// -/// - It's the task of the user of this interface to manage the slave select lines -/// -/// - Due to how full duplex SPI works each `read` call must be preceded by a `write` call. -/// -/// - `read` calls only return the data received with the last `write` call. -/// Previously received data is discarded -/// -/// - Data is only guaranteed to be clocked out when the `read` call succeeds. -/// The slave select line shouldn't be released before that. -/// -/// - Some SPIs can work with 8-bit *and* 16-bit words. You can overload this trait with different -/// `Word` types to allow operation in both modes. -pub trait FullDuplex { - /// An enumeration of SPI errors - type Error; - - /// Reads the word stored in the shift register - /// - /// **NOTE** A word must be sent to the slave before attempting to call this - /// method. - fn read(&mut self) -> nb::Result; - - /// Writes a word to the slave - fn write(&mut self, word: Word) -> nb::Result<(), Self::Error>; -} - -/// Clock polarity -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum Polarity { - /// Clock signal low when idle - IdleLow, - /// Clock signal high when idle - IdleHigh, -} - -/// Clock phase -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum Phase { - /// Data in "captured" on the first clock transition - CaptureOnFirstTransition, - /// Data in "captured" on the second clock transition - CaptureOnSecondTransition, -} - -/// SPI mode -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct Mode { - /// Clock polarity - pub polarity: Polarity, - /// Clock phase - pub phase: Phase, -} - -/// Helper for CPOL = 0, CPHA = 0 -pub const MODE_0: Mode = Mode { - polarity: Polarity::IdleLow, - phase: Phase::CaptureOnFirstTransition, -}; - -/// Helper for CPOL = 0, CPHA = 1 -pub const MODE_1: Mode = Mode { - polarity: Polarity::IdleLow, - phase: Phase::CaptureOnSecondTransition, -}; - -/// Helper for CPOL = 1, CPHA = 0 -pub const MODE_2: Mode = Mode { - polarity: Polarity::IdleHigh, - phase: Phase::CaptureOnFirstTransition, -}; - -/// Helper for CPOL = 1, CPHA = 1 -pub const MODE_3: Mode = Mode { - polarity: Polarity::IdleHigh, - phase: Phase::CaptureOnSecondTransition, -}; diff --git a/src/nb/timer.rs b/src/nb/timer.rs deleted file mode 100644 index 07a1b02de..000000000 --- a/src/nb/timer.rs +++ /dev/null @@ -1,94 +0,0 @@ -//! Timers - -/// A count down timer -/// -/// # Contract -/// -/// - `self.start(count); block!(self.wait());` MUST block for AT LEAST the time specified by -/// `count`. -/// -/// *Note* that the implementer doesn't necessarily have to be a *downcounting* timer; it could also -/// be an *upcounting* timer as long as the above contract is upheld. -/// -/// # Examples -/// -/// You can use this timer to create delays -/// -/// ``` -/// extern crate embedded_hal as hal; -/// #[macro_use(block)] -/// extern crate nb; -/// -/// use hal::nb::timer::CountDown; -/// -/// fn main() { -/// let mut led: Led = { -/// // .. -/// # Led -/// }; -/// let mut timer: Timer6 = { -/// // .. -/// # Timer6 -/// }; -/// -/// Led.on(); -/// timer.start(1.s()).unwrap(); -/// block!(timer.wait()); // blocks for 1 second -/// Led.off(); -/// } -/// -/// # use core::convert::Infallible; -/// # struct Seconds(u32); -/// # trait U32Ext { fn s(self) -> Seconds; } -/// # impl U32Ext for u32 { fn s(self) -> Seconds { Seconds(self) } } -/// # struct Led; -/// # impl Led { -/// # pub fn off(&mut self) {} -/// # pub fn on(&mut self) {} -/// # } -/// # struct Timer6; -/// # impl hal::nb::timer::CountDown for Timer6 { -/// # type Error = Infallible; -/// # type Time = Seconds; -/// # fn start(&mut self, _: T) -> Result<(), Self::Error> where T: Into { Ok(()) } -/// # fn wait(&mut self) -> ::nb::Result<(), Infallible> { Ok(()) } -/// # } -/// ``` -pub trait CountDown { - /// An enumeration of `CountDown` errors. - /// - /// For infallible implementations, will be `Infallible` - type Error; - - /// The unit of time used by this timer - type Time; - - /// Starts a new count down - fn start(&mut self, count: T) -> Result<(), Self::Error> - where - T: Into; - - /// Non-blockingly "waits" until the count down finishes - /// - /// # Contract - /// - /// - If `Self: Periodic`, the timer will start a new count down right after the last one - /// finishes. - /// - Otherwise the behavior of calling `wait` after the last call returned `Ok` is UNSPECIFIED. - /// Implementers are suggested to panic on this scenario to signal a programmer error. - fn wait(&mut self) -> nb::Result<(), Self::Error>; -} - -/// Marker trait that indicates that a timer is periodic -pub trait Periodic {} - -/// Trait for cancelable countdowns. -pub trait Cancel: CountDown { - /// Tries to cancel this countdown. - /// - /// # Errors - /// - /// An error will be returned if the countdown has already been canceled or was never started. - /// An error is also returned if the countdown is not `Periodic` and has already expired. - fn cancel(&mut self) -> Result<(), Self::Error>; -} diff --git a/src/pwm.rs b/src/pwm.rs new file mode 100644 index 000000000..aad090bc0 --- /dev/null +++ b/src/pwm.rs @@ -0,0 +1,136 @@ +//! Pulse Width Modulation + +/// Blocking pulse width modulation traits +pub mod blocking { + /// Pulse Width Modulation + /// + /// # Examples + /// + /// Use this interface to control the power output of some actuator + /// + /// ``` + /// extern crate embedded_hal as hal; + /// + /// use hal::pwm::blocking::Pwm; + /// + /// fn main() { + /// let mut pwm: Pwm1 = { + /// // .. + /// # Pwm1 + /// }; + /// + /// pwm.set_period(1.khz()).unwrap(); + /// + /// let max_duty = pwm.get_max_duty().unwrap(); + /// + /// // brightest LED + /// pwm.set_duty(&Channel::_1, max_duty).unwrap(); + /// + /// // dimmer LED + /// pwm.set_duty(&Channel::_2, max_duty / 4).unwrap(); + /// } + /// + /// # use core::convert::Infallible; + /// # struct KiloHertz(u32); + /// # trait U32Ext { fn khz(self) -> KiloHertz; } + /// # impl U32Ext for u32 { fn khz(self) -> KiloHertz { KiloHertz(self) } } + /// # enum Channel { _1, _2 } + /// # struct Pwm1; + /// # impl hal::pwm::blocking::Pwm for Pwm1 { + /// # type Error = Infallible; + /// # type Channel = Channel; + /// # type Time = KiloHertz; + /// # type Duty = u16; + /// # fn disable(&mut self, _: &Channel) -> Result<(), Self::Error> { unimplemented!() } + /// # fn enable(&mut self, _: &Channel) -> Result<(), Self::Error> { unimplemented!() } + /// # fn get_duty(&self, _: &Channel) -> Result { unimplemented!() } + /// # fn get_max_duty(&self) -> Result { Ok(0) } + /// # fn set_duty(&mut self, _: &Channel, _: u16) -> Result<(), Self::Error> { Ok(()) } + /// # fn get_period(&self) -> Result { unimplemented!() } + /// # fn set_period(&mut self, _: T) -> Result<(), Self::Error> where T: Into { Ok(()) } + /// # } + /// ``` + // unproven reason: pre-singletons API. The `PwmPin` trait seems more useful because it models independent + // PWM channels. Here a certain number of channels are multiplexed in a single implementer. + pub trait Pwm { + /// Enumeration of `Pwm` errors + type Error; + + /// Enumeration of channels that can be used with this `Pwm` interface + /// + /// If your `Pwm` interface has no channels you can use the type `()` + /// here + type Channel; + + /// A time unit that can be converted into a human time unit (e.g. seconds) + type Time; + + /// Type for the `duty` methods + /// + /// The implementer is free to choose a float / percentage representation + /// (e.g. `0.0 .. 1.0`) or an integer representation (e.g. `0 .. 65535`) + type Duty; + + /// Disables a PWM `channel` + fn disable(&mut self, channel: &Self::Channel) -> Result<(), Self::Error>; + + /// Enables a PWM `channel` + fn enable(&mut self, channel: &Self::Channel) -> Result<(), Self::Error>; + + /// Returns the current PWM period + fn get_period(&self) -> Result; + + /// Returns the current duty cycle + /// + /// While the pin is transitioning to the new duty cycle after a `set_duty` call, this may + /// return the old or the new duty cycle depending on the implementation. + fn get_duty(&self, channel: &Self::Channel) -> Result; + + /// Returns the maximum duty cycle value + fn get_max_duty(&self) -> Result; + + /// Sets a new duty cycle + fn set_duty( + &mut self, + channel: &Self::Channel, + duty: Self::Duty, + ) -> Result<(), Self::Error>; + + /// Sets a new PWM period + fn set_period

(&mut self, period: P) -> Result<(), Self::Error> + where + P: Into; + } + + /// A single PWM channel / pin + /// + /// See `Pwm` for details + pub trait PwmPin { + /// Enumeration of `PwmPin` errors + type Error; + + /// Type for the `duty` methods + /// + /// The implementer is free to choose a float / percentage representation + /// (e.g. `0.0 .. 1.0`) or an integer representation (e.g. `0 .. 65535`) + type Duty; + + /// Disables a PWM `channel` + fn disable(&mut self) -> Result<(), Self::Error>; + + /// Enables a PWM `channel` + fn enable(&mut self) -> Result<(), Self::Error>; + + /// Returns the current duty cycle + /// + /// While the pin is transitioning to the new duty cycle after a `set_duty` call, this may + /// return the old or the new duty cycle depending on the implementation. + fn get_duty(&self) -> Result; + + /// Returns the maximum duty cycle value + fn get_max_duty(&self) -> Result; + + /// Sets a new duty cycle + fn set_duty(&mut self, duty: Self::Duty) -> Result<(), Self::Error>; + } +} diff --git a/src/qei.rs b/src/qei.rs new file mode 100644 index 000000000..f96bf565a --- /dev/null +++ b/src/qei.rs @@ -0,0 +1,84 @@ +//! Quadrature encoder interface traits + +/// Count direction +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum Direction { + /// 3, 2, 1 + Downcounting, + /// 1, 2, 3 + Upcounting, +} + +/// Blocking quadrature encoder interface traits +pub mod blocking { + use super::Direction; + + /// Quadrature encoder interface + /// + /// # Examples + /// + /// You can use this interface to measure the speed of a motor + /// + /// ``` + /// extern crate embedded_hal as hal; + /// #[macro_use(block)] + /// extern crate nb; + /// + /// use hal::qei::blocking::Qei; + /// use hal::timer::nb::CountDown; + /// + /// fn main() { + /// let mut qei: Qei1 = { + /// // .. + /// # Qei1 + /// }; + /// let mut timer: Timer6 = { + /// // .. + /// # Timer6 + /// }; + /// + /// + /// let before = qei.count().unwrap(); + /// timer.start(1.s()).unwrap(); + /// block!(timer.wait()); + /// let after = qei.count().unwrap(); + /// + /// let speed = after.wrapping_sub(before); + /// println!("Speed: {} pulses per second", speed); + /// } + /// + /// # use core::convert::Infallible; + /// # struct Seconds(u32); + /// # trait U32Ext { fn s(self) -> Seconds; } + /// # impl U32Ext for u32 { fn s(self) -> Seconds { Seconds(self) } } + /// # struct Qei1; + /// # impl hal::qei::blocking::Qei for Qei1 { + /// # type Error = Infallible; + /// # type Count = u16; + /// # fn count(&self) -> Result { Ok(0) } + /// # fn direction(&self) -> Result<::hal::qei::Direction, Self::Error> { unimplemented!() } + /// # } + /// # struct Timer6; + /// # impl hal::timer::nb::CountDown for Timer6 { + /// # type Error = Infallible; + /// # type Time = Seconds; + /// # fn start(&mut self, _: T) -> Result<(), Infallible> where T: Into { Ok(()) } + /// # fn wait(&mut self) -> ::nb::Result<(), Infallible> { Ok(()) } + /// # } + /// ``` + // unproven reason: needs to be re-evaluated in the new singletons world. At the very least this needs a + // reference implementation + pub trait Qei { + /// Enumeration of `Qei` errors + type Error; + + /// The type of the value returned by `count` + type Count; + + /// Returns the current pulse count of the encoder + fn count(&self) -> Result; + + /// Returns the count direction + fn direction(&self) -> Result; + } +} diff --git a/src/blocking/serial.rs b/src/serial/blocking.rs similarity index 73% rename from src/blocking/serial.rs rename to src/serial/blocking.rs index 892ac0381..4f199bf06 100644 --- a/src/blocking/serial.rs +++ b/src/serial/blocking.rs @@ -1,4 +1,8 @@ //! Blocking serial API +//! +//! In some cases it's possible to implement these blocking traits on top of one of the core HAL +//! traits. To save boilerplate when that's the case a `Default` marker trait may be provided. +//! Implementing that marker trait will opt in your type into a blanket implementation. /// Write half of a serial interface (blocking variant) pub trait Write { diff --git a/src/serial/mod.rs b/src/serial/mod.rs new file mode 100644 index 000000000..cdb6e5f1c --- /dev/null +++ b/src/serial/mod.rs @@ -0,0 +1,4 @@ +//! Serial traits + +pub mod blocking; +pub mod nb; diff --git a/src/nb/serial.rs b/src/serial/nb.rs similarity index 100% rename from src/nb/serial.rs rename to src/serial/nb.rs diff --git a/src/blocking/spi.rs b/src/spi/blocking.rs similarity index 84% rename from src/blocking/spi.rs rename to src/spi/blocking.rs index 36fa90e2a..0aa95c750 100644 --- a/src/blocking/spi.rs +++ b/src/spi/blocking.rs @@ -1,4 +1,8 @@ //! Blocking SPI API +//! +//! In some cases it's possible to implement these blocking traits on top of one of the core HAL +//! traits. To save boilerplate when that's the case a `Default` marker trait may be provided. +//! Implementing that marker trait will opt in your type into a blanket implementation. /// Blocking transfer pub trait Transfer { diff --git a/src/spi/mod.rs b/src/spi/mod.rs new file mode 100644 index 000000000..0a99e9379 --- /dev/null +++ b/src/spi/mod.rs @@ -0,0 +1,55 @@ +//! SPI traits + +pub mod blocking; +pub mod nb; + +/// Clock polarity +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Polarity { + /// Clock signal low when idle + IdleLow, + /// Clock signal high when idle + IdleHigh, +} + +/// Clock phase +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Phase { + /// Data in "captured" on the first clock transition + CaptureOnFirstTransition, + /// Data in "captured" on the second clock transition + CaptureOnSecondTransition, +} + +/// SPI mode +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Mode { + /// Clock polarity + pub polarity: Polarity, + /// Clock phase + pub phase: Phase, +} + +/// Helper for CPOL = 0, CPHA = 0 +pub const MODE_0: Mode = Mode { + polarity: Polarity::IdleLow, + phase: Phase::CaptureOnFirstTransition, +}; + +/// Helper for CPOL = 0, CPHA = 1 +pub const MODE_1: Mode = Mode { + polarity: Polarity::IdleLow, + phase: Phase::CaptureOnSecondTransition, +}; + +/// Helper for CPOL = 1, CPHA = 0 +pub const MODE_2: Mode = Mode { + polarity: Polarity::IdleHigh, + phase: Phase::CaptureOnFirstTransition, +}; + +/// Helper for CPOL = 1, CPHA = 1 +pub const MODE_3: Mode = Mode { + polarity: Polarity::IdleHigh, + phase: Phase::CaptureOnSecondTransition, +}; diff --git a/src/spi/nb.rs b/src/spi/nb.rs new file mode 100644 index 000000000..67921971d --- /dev/null +++ b/src/spi/nb.rs @@ -0,0 +1,31 @@ +//! Serial Peripheral Interface + +/// Full duplex (master mode) +/// +/// # Notes +/// +/// - It's the task of the user of this interface to manage the slave select lines +/// +/// - Due to how full duplex SPI works each `read` call must be preceded by a `write` call. +/// +/// - `read` calls only return the data received with the last `write` call. +/// Previously received data is discarded +/// +/// - Data is only guaranteed to be clocked out when the `read` call succeeds. +/// The slave select line shouldn't be released before that. +/// +/// - Some SPIs can work with 8-bit *and* 16-bit words. You can overload this trait with different +/// `Word` types to allow operation in both modes. +pub trait FullDuplex { + /// An enumeration of SPI errors + type Error; + + /// Reads the word stored in the shift register + /// + /// **NOTE** A word must be sent to the slave before attempting to call this + /// method. + fn read(&mut self) -> nb::Result; + + /// Writes a word to the slave + fn write(&mut self, word: Word) -> nb::Result<(), Self::Error>; +} diff --git a/src/timer.rs b/src/timer.rs new file mode 100644 index 000000000..ed6552af9 --- /dev/null +++ b/src/timer.rs @@ -0,0 +1,98 @@ +//! Timer traits + +/// Marker trait that indicates that a timer is periodic +pub trait Periodic {} + +/// Non-blocking timer traits +pub mod nb { + + /// A count down timer + /// + /// # Contract + /// + /// - `self.start(count); block!(self.wait());` MUST block for AT LEAST the time specified by + /// `count`. + /// + /// *Note* that the implementer doesn't necessarily have to be a *downcounting* timer; it could also + /// be an *upcounting* timer as long as the above contract is upheld. + /// + /// # Examples + /// + /// You can use this timer to create delays + /// + /// ``` + /// extern crate embedded_hal as hal; + /// #[macro_use(block)] + /// extern crate nb; + /// + /// use hal::timer::nb::CountDown; + /// + /// fn main() { + /// let mut led: Led = { + /// // .. + /// # Led + /// }; + /// let mut timer: Timer6 = { + /// // .. + /// # Timer6 + /// }; + /// + /// Led.on(); + /// timer.start(1.s()).unwrap(); + /// block!(timer.wait()); // blocks for 1 second + /// Led.off(); + /// } + /// + /// # use core::convert::Infallible; + /// # struct Seconds(u32); + /// # trait U32Ext { fn s(self) -> Seconds; } + /// # impl U32Ext for u32 { fn s(self) -> Seconds { Seconds(self) } } + /// # struct Led; + /// # impl Led { + /// # pub fn off(&mut self) {} + /// # pub fn on(&mut self) {} + /// # } + /// # struct Timer6; + /// # impl hal::timer::nb::CountDown for Timer6 { + /// # type Error = Infallible; + /// # type Time = Seconds; + /// # fn start(&mut self, _: T) -> Result<(), Self::Error> where T: Into { Ok(()) } + /// # fn wait(&mut self) -> ::nb::Result<(), Infallible> { Ok(()) } + /// # } + /// ``` + pub trait CountDown { + /// An enumeration of `CountDown` errors. + /// + /// For infallible implementations, will be `Infallible` + type Error; + + /// The unit of time used by this timer + type Time; + + /// Starts a new count down + fn start(&mut self, count: T) -> Result<(), Self::Error> + where + T: Into; + + /// Non-blockingly "waits" until the count down finishes + /// + /// # Contract + /// + /// - If `Self: Periodic`, the timer will start a new count down right after the last one + /// finishes. + /// - Otherwise the behavior of calling `wait` after the last call returned `Ok` is UNSPECIFIED. + /// Implementers are suggested to panic on this scenario to signal a programmer error. + fn wait(&mut self) -> nb::Result<(), Self::Error>; + } + + /// Trait for cancelable countdowns. + pub trait Cancel: CountDown { + /// Tries to cancel this countdown. + /// + /// # Errors + /// + /// An error will be returned if the countdown has already been canceled or was never started. + /// An error is also returned if the countdown is not `Periodic` and has already expired. + fn cancel(&mut self) -> Result<(), Self::Error>; + } +} diff --git a/src/watchdog.rs b/src/watchdog.rs new file mode 100644 index 000000000..d75a5d5fa --- /dev/null +++ b/src/watchdog.rs @@ -0,0 +1,64 @@ +//! Traits for interactions with a processors watchdog timer. + +/// Blocking processor watchdog traits + +pub mod blocking { + /// Feeds an existing watchdog to ensure the processor isn't reset. Sometimes + /// the "feeding" operation is also referred to as "refreshing". + pub trait Watchdog { + /// An enumeration of `Watchdog` errors. + /// + /// For infallible implementations, will be `Infallible` + type Error; + + /// Triggers the watchdog. This must be done once the watchdog is started + /// to prevent the processor being reset. + fn feed(&mut self) -> Result<(), Self::Error>; + } + + /// Enables A watchdog timer to reset the processor if software is frozen or + /// stalled. + pub trait Enable { + /// An enumeration of `Enable` errors. + /// + /// For infallible implementations, will be `Infallible` + type Error; + + /// Unit of time used by the watchdog. + type Time; + + /// The started watchdog that should be `feed()`. + type Target: Watchdog; + + /// Starts the watchdog with a given period, typically once this is done + /// the watchdog needs to be `feed()` periodically, or the processor would be + /// reset. + /// + /// This consumes the value and returns the `Watchdog` trait that you must + /// `feed()`. + fn start(self, period: T) -> Result + where + T: Into; + } + + /// Disables a running watchdog timer so the processor won't be reset. + /// + /// Not all watchdog timers support disable operation after they've been enabled. + /// In this case, hardware support libraries would not implement this trait + /// and hardware-agnostic libraries should consider not requiring it. + pub trait Disable { + /// An enumeration of `Disable` errors. + /// + /// For infallible implementations, will be `Infallible` + type Error; + + /// Disabled watchdog instance that can be enabled. + type Target: Enable; + + /// Disables the watchdog. + /// + /// This stops the watchdog and returns an instance implementing the + /// `Enable` trait so that it can be started again. + fn disable(self) -> Result; + } +}