feat: add display UI with splash screen, dashboard, and RSSI sparkline
Implement full SSD1306 OLED display support: - Splash screen on boot (1.5s) with project name, version, board - Dashboard showing radio state, config, counters, RSSI/SNR - RSSI history sparkline (64-sample ring buffer, 2px-wide bars) - DisplayOn/DisplayOff host commands routed via dedicated channel - Pure render functions against generic DrawTarget (no hardware coupling) - Display on/off control bypasses radio task entirely Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
17624 files changed
+4564928
-38
No files matched your search
Vendored
+98
@@ -0,0 +1,98 @@
|
||||
//! Analog-digital conversion traits
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
use 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 std::marker::PhantomData;
|
||||
/// # use embedded_hal::adc::Channel;
|
||||
///
|
||||
/// struct Adc1; // Example ADC with single bank of 8 channels
|
||||
/// struct Gpio1Pin1<MODE>(PhantomData<MODE>);
|
||||
/// 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<Adc1> for Gpio1Pin1<Analog> {
|
||||
/// type ID = u8; // ADC channels are identified numerically
|
||||
///
|
||||
/// fn channel() -> u8 { 7_u8 } // GPIO pin 1 is connected to ADC channel 7
|
||||
/// }
|
||||
///
|
||||
/// struct Adc2; // ADC with two banks of 16 channels
|
||||
/// struct Gpio2PinA<MODE>(PhantomData<MODE>);
|
||||
/// 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<Adc2> for Gpio2PinA<AltFun> {
|
||||
/// type ID = (u8, u8); // ADC channels are identified by bank number and channel number
|
||||
///
|
||||
/// fn channel() -> (u8, u8) { (0, 3) } // bank 0 channel 3
|
||||
/// }
|
||||
/// ```
|
||||
#[cfg(feature = "unproven")]
|
||||
pub trait Channel<ADC> {
|
||||
/// 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;
|
||||
|
||||
/// 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::ID;
|
||||
|
||||
// `channel` is a function due to [this reported
|
||||
// issue](https://github.com/rust-lang/rust/issues/54973). Something about blanket impls
|
||||
// combined with `type ID; const CHANNEL: Self::ID;` causes problems.
|
||||
//const CHANNEL: 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::{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<WORD, PIN> OneShot<MyAdc, WORD, PIN> for MyAdc
|
||||
/// where
|
||||
/// WORD: From<u16>,
|
||||
/// PIN: Channel<MyAdc, ID=u8>,
|
||||
/// {
|
||||
/// type Error = ();
|
||||
///
|
||||
/// fn read(&mut self, _pin: &mut PIN) -> nb::Result<WORD, Self::Error> {
|
||||
/// let chan = 1 << PIN::channel();
|
||||
/// self.power_up();
|
||||
/// let result = self.do_conversion(chan);
|
||||
/// self.power_down();
|
||||
/// Ok(result.into())
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[cfg(feature = "unproven")]
|
||||
pub trait OneShot<ADC, Word, Pin: Channel<ADC>> {
|
||||
/// 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<Word, Self::Error>;
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
//! Blocking CAN API
|
||||
|
||||
/// A blocking CAN interface that is able to transmit and receive frames.
|
||||
pub trait Can {
|
||||
/// Associated frame type.
|
||||
type Frame: crate::can::Frame;
|
||||
|
||||
/// Associated error type.
|
||||
type Error: crate::can::Error;
|
||||
|
||||
/// Puts a frame in the transmit buffer. Blocks until space is available in
|
||||
/// the transmit buffer.
|
||||
fn transmit(&mut self, frame: &Self::Frame) -> Result<(), Self::Error>;
|
||||
|
||||
/// Blocks until a frame was received or an error occured.
|
||||
fn receive(&mut self) -> Result<Self::Frame, Self::Error>;
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
//! 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<UXX> {
|
||||
/// Pauses execution for `ms` milliseconds
|
||||
fn delay_ms(&mut self, ms: UXX);
|
||||
}
|
||||
|
||||
/// 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<UXX> {
|
||||
/// Pauses execution for `us` microseconds
|
||||
fn delay_us(&mut self, us: UXX);
|
||||
}
|
||||
+297
@@ -0,0 +1,297 @@
|
||||
//! 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<SevenBitAddress> for I2c0
|
||||
//! {
|
||||
//! # type Error = ();
|
||||
//! #
|
||||
//! fn write(&mut self, addr: u8, output: &[u8]) -> Result<(), Self::Error> {
|
||||
//! // ...
|
||||
//! # Ok(())
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! impl Write<TenBitAddress> 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: I2C,
|
||||
//! }
|
||||
//!
|
||||
//! impl<I2C, E> TemperatureSensorDriver<I2C>
|
||||
//! where
|
||||
//! I2C: WriteRead<Error = E>,
|
||||
//! {
|
||||
//! pub fn read_temperature(&mut self) -> Result<u8, E> {
|
||||
//! 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: I2C,
|
||||
//! }
|
||||
//!
|
||||
//! impl<I2C, E> TemperatureSensorDriver<I2C>
|
||||
//! where
|
||||
//! I2C: WriteRead<TenBitAddress, Error = E>,
|
||||
//! {
|
||||
//! pub fn read_temperature(&mut self) -> Result<u8, E> {
|
||||
//! let mut temp = [0];
|
||||
//! self.i2c
|
||||
//! .write_read(ADDR, &[TEMP_REGISTER], &mut temp)
|
||||
//! .and(Ok(temp[0]))
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
use crate::private;
|
||||
|
||||
impl private::Sealed for SevenBitAddress {}
|
||||
impl private::Sealed for TenBitAddress {}
|
||||
|
||||
/// 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<A: AddressMode = SevenBitAddress> {
|
||||
/// 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<A: AddressMode = SevenBitAddress> {
|
||||
/// 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<A: AddressMode = SevenBitAddress> {
|
||||
/// Error type
|
||||
type Error;
|
||||
|
||||
/// Writes bytes to slave with address `address`
|
||||
///
|
||||
/// # I2C Events (contract)
|
||||
///
|
||||
/// Same as `Write`
|
||||
fn write<B>(&mut self, address: A, bytes: B) -> Result<(), Self::Error>
|
||||
where
|
||||
B: IntoIterator<Item = u8>;
|
||||
}
|
||||
|
||||
/// Blocking write + read
|
||||
pub trait WriteRead<A: AddressMode = SevenBitAddress> {
|
||||
/// 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<A: AddressMode = SevenBitAddress> {
|
||||
/// 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<B>(
|
||||
&mut self,
|
||||
address: A,
|
||||
bytes: B,
|
||||
buffer: &mut [u8],
|
||||
) -> Result<(), Self::Error>
|
||||
where
|
||||
B: IntoIterator<Item = u8>;
|
||||
}
|
||||
|
||||
/// 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<A: AddressMode = SevenBitAddress> {
|
||||
/// 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<A: AddressMode = SevenBitAddress> {
|
||||
/// 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<Item = Operation<'a>>;
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
//! 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 can;
|
||||
pub mod delay;
|
||||
pub mod i2c;
|
||||
pub mod rng;
|
||||
pub mod serial;
|
||||
pub mod spi;
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
//! Blocking hardware random number generator
|
||||
|
||||
/// Blocking read
|
||||
///
|
||||
/// *This trait is available if embedded-hal is built with the `"unproven"` feature.*
|
||||
#[cfg(feature = "unproven")]
|
||||
pub trait Read {
|
||||
/// Error type
|
||||
type Error;
|
||||
|
||||
/// Reads enough bytes from hardware random number generator to fill `buffer`
|
||||
///
|
||||
/// If any error is encountered then this function immediately returns. The contents of buf are
|
||||
/// unspecified in this case.
|
||||
///
|
||||
/// If this function returns an error, it is unspecified how many bytes it has read, but it
|
||||
/// will never read more than would be necessary to completely fill the buffer.
|
||||
fn read(&mut self, buffer: &mut [u8]) -> Result<(), Self::Error>;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
//! Blocking serial API
|
||||
|
||||
/// Write half of a serial interface (blocking variant)
|
||||
pub trait Write<Word> {
|
||||
/// The type of error that can occur when writing
|
||||
type Error;
|
||||
|
||||
/// Writes a slice, blocking until everything has been written
|
||||
///
|
||||
/// An implementation can choose to buffer the write, returning `Ok(())`
|
||||
/// after the complete slice has been written to a buffer, but before all
|
||||
/// words have been sent via the serial interface. To make sure that
|
||||
/// everything has been sent, call [`bflush`] after this function returns.
|
||||
///
|
||||
/// [`bflush`]: #tymethod.bflush
|
||||
fn bwrite_all(&mut self, buffer: &[Word]) -> Result<(), Self::Error>;
|
||||
|
||||
/// Block until the serial interface has sent all buffered words
|
||||
fn bflush(&mut self) -> Result<(), Self::Error>;
|
||||
}
|
||||
|
||||
/// Blocking serial write
|
||||
pub mod write {
|
||||
/// Marker trait to opt into default blocking write implementation
|
||||
///
|
||||
/// Implementers of [`serial::Write`] can implement this marker trait
|
||||
/// for their type. Doing so will automatically provide the default
|
||||
/// implementation of [`blocking::serial::Write`] for the type.
|
||||
///
|
||||
/// [`serial::Write`]: ../../serial/trait.Write.html
|
||||
/// [`blocking::serial::Write`]: ../trait.Write.html
|
||||
pub trait Default<Word>: ::serial::Write<Word> {}
|
||||
|
||||
impl<S, Word> ::blocking::serial::Write<Word> for S
|
||||
where
|
||||
S: Default<Word>,
|
||||
Word: Clone,
|
||||
{
|
||||
type Error = S::Error;
|
||||
|
||||
fn bwrite_all(&mut self, buffer: &[Word]) -> Result<(), Self::Error> {
|
||||
for word in buffer {
|
||||
block!(self.write(word.clone()))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn bflush(&mut self) -> Result<(), Self::Error> {
|
||||
block!(self.flush())?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
//! Blocking SPI API
|
||||
|
||||
/// Blocking transfer
|
||||
pub trait Transfer<W> {
|
||||
/// Error type
|
||||
type Error;
|
||||
|
||||
/// Sends `words` to the slave. Returns the `words` received from the slave
|
||||
fn transfer<'w>(&mut self, words: &'w mut [W]) -> Result<&'w [W], Self::Error>;
|
||||
}
|
||||
|
||||
/// Blocking write
|
||||
pub trait Write<W> {
|
||||
/// Error type
|
||||
type Error;
|
||||
|
||||
/// Sends `words` to the slave, ignoring all the incoming words
|
||||
fn write(&mut self, words: &[W]) -> Result<(), Self::Error>;
|
||||
}
|
||||
|
||||
/// Blocking write (iterator version)
|
||||
#[cfg(feature = "unproven")]
|
||||
pub trait WriteIter<W> {
|
||||
/// Error type
|
||||
type Error;
|
||||
|
||||
/// Sends `words` to the slave, ignoring all the incoming words
|
||||
fn write_iter<WI>(&mut self, words: WI) -> Result<(), Self::Error>
|
||||
where
|
||||
WI: IntoIterator<Item = W>;
|
||||
}
|
||||
|
||||
/// Blocking transfer
|
||||
pub mod transfer {
|
||||
/// Default implementation of `blocking::spi::Transfer<W>` for implementers of
|
||||
/// `spi::FullDuplex<W>`
|
||||
pub trait Default<W>: ::spi::FullDuplex<W> {}
|
||||
|
||||
impl<W, S> ::blocking::spi::Transfer<W> for S
|
||||
where
|
||||
S: Default<W>,
|
||||
W: Clone,
|
||||
{
|
||||
type Error = S::Error;
|
||||
|
||||
fn transfer<'w>(&mut self, words: &'w mut [W]) -> Result<&'w [W], S::Error> {
|
||||
for word in words.iter_mut() {
|
||||
block!(self.send(word.clone()))?;
|
||||
*word = block!(self.read())?;
|
||||
}
|
||||
|
||||
Ok(words)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Blocking write
|
||||
pub mod write {
|
||||
/// Default implementation of `blocking::spi::Write<W>` for implementers of `spi::FullDuplex<W>`
|
||||
pub trait Default<W>: ::spi::FullDuplex<W> {}
|
||||
|
||||
impl<W, S> ::blocking::spi::Write<W> for S
|
||||
where
|
||||
S: Default<W>,
|
||||
W: Clone,
|
||||
{
|
||||
type Error = S::Error;
|
||||
|
||||
fn write(&mut self, words: &[W]) -> Result<(), S::Error> {
|
||||
for word in words {
|
||||
block!(self.send(word.clone()))?;
|
||||
block!(self.read())?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Blocking write (iterator version)
|
||||
#[cfg(feature = "unproven")]
|
||||
pub mod write_iter {
|
||||
/// Default implementation of `blocking::spi::WriteIter<W>` for implementers of
|
||||
/// `spi::FullDuplex<W>`
|
||||
pub trait Default<W>: ::spi::FullDuplex<W> {}
|
||||
|
||||
impl<W, S> ::blocking::spi::WriteIter<W> for S
|
||||
where
|
||||
S: Default<W>,
|
||||
W: Clone,
|
||||
{
|
||||
type Error = S::Error;
|
||||
|
||||
fn write_iter<WI>(&mut self, words: WI) -> Result<(), S::Error>
|
||||
where
|
||||
WI: IntoIterator<Item = W>,
|
||||
{
|
||||
for word in words.into_iter() {
|
||||
block!(self.send(word.clone()))?;
|
||||
block!(self.read())?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Operation for transactional SPI trait
|
||||
///
|
||||
/// This allows composition of SPI operations into a single bus transaction
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Operation<'a, W: 'static> {
|
||||
/// Write data from the provided buffer, discarding read data
|
||||
Write(&'a [W]),
|
||||
/// Write data out while reading data into the provided buffer
|
||||
Transfer(&'a mut [W]),
|
||||
}
|
||||
|
||||
/// Transactional trait allows multiple actions to be executed
|
||||
/// as part of a single SPI transaction
|
||||
pub trait Transactional<W: 'static> {
|
||||
/// Associated error type
|
||||
type Error;
|
||||
|
||||
/// Execute the provided transactions
|
||||
fn exec<'a>(&mut self, operations: &mut [Operation<'a, W>]) -> Result<(), Self::Error>;
|
||||
}
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
//! CAN Identifiers.
|
||||
|
||||
/// Standard 11-bit CAN Identifier (`0..=0x7FF`).
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub struct StandardId(u16);
|
||||
|
||||
impl StandardId {
|
||||
/// CAN ID `0`, the highest priority.
|
||||
pub const ZERO: Self = StandardId(0);
|
||||
|
||||
/// CAN ID `0x7FF`, the lowest priority.
|
||||
pub const MAX: Self = StandardId(0x7FF);
|
||||
|
||||
/// Tries to create a `StandardId` from a raw 16-bit integer.
|
||||
///
|
||||
/// This will return `None` if `raw` is out of range of an 11-bit integer (`> 0x7FF`).
|
||||
#[inline]
|
||||
pub fn new(raw: u16) -> Option<Self> {
|
||||
if raw <= 0x7FF {
|
||||
Some(StandardId(raw))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new `StandardId` without checking if it is inside the valid range.
|
||||
///
|
||||
/// # Safety
|
||||
/// Using this method can create an invalid ID and is thus marked as unsafe.
|
||||
#[inline]
|
||||
pub const unsafe fn new_unchecked(raw: u16) -> Self {
|
||||
StandardId(raw)
|
||||
}
|
||||
|
||||
/// Returns this CAN Identifier as a raw 16-bit integer.
|
||||
#[inline]
|
||||
pub fn as_raw(&self) -> u16 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// Extended 29-bit CAN Identifier (`0..=1FFF_FFFF`).
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub struct ExtendedId(u32);
|
||||
|
||||
impl ExtendedId {
|
||||
/// CAN ID `0`, the highest priority.
|
||||
pub const ZERO: Self = ExtendedId(0);
|
||||
|
||||
/// CAN ID `0x1FFFFFFF`, the lowest priority.
|
||||
pub const MAX: Self = ExtendedId(0x1FFF_FFFF);
|
||||
|
||||
/// Tries to create a `ExtendedId` from a raw 32-bit integer.
|
||||
///
|
||||
/// This will return `None` if `raw` is out of range of an 29-bit integer (`> 0x1FFF_FFFF`).
|
||||
#[inline]
|
||||
pub fn new(raw: u32) -> Option<Self> {
|
||||
if raw <= 0x1FFF_FFFF {
|
||||
Some(ExtendedId(raw))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new `ExtendedId` without checking if it is inside the valid range.
|
||||
///
|
||||
/// # Safety
|
||||
/// Using this method can create an invalid ID and is thus marked as unsafe.
|
||||
#[inline]
|
||||
pub const unsafe fn new_unchecked(raw: u32) -> Self {
|
||||
ExtendedId(raw)
|
||||
}
|
||||
|
||||
/// Returns this CAN Identifier as a raw 32-bit integer.
|
||||
#[inline]
|
||||
pub fn as_raw(&self) -> u32 {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Returns the Base ID part of this extended identifier.
|
||||
pub fn standard_id(&self) -> StandardId {
|
||||
// ID-28 to ID-18
|
||||
StandardId((self.0 >> 18) as u16)
|
||||
}
|
||||
}
|
||||
|
||||
/// A CAN Identifier (standard or extended).
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum Id {
|
||||
/// Standard 11-bit Identifier (`0..=0x7FF`).
|
||||
Standard(StandardId),
|
||||
|
||||
/// Extended 29-bit Identifier (`0..=0x1FFF_FFFF`).
|
||||
Extended(ExtendedId),
|
||||
}
|
||||
|
||||
impl From<StandardId> for Id {
|
||||
#[inline]
|
||||
fn from(id: StandardId) -> Self {
|
||||
Id::Standard(id)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ExtendedId> for Id {
|
||||
#[inline]
|
||||
fn from(id: ExtendedId) -> Self {
|
||||
Id::Extended(id)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn standard_id_new() {
|
||||
assert_eq!(
|
||||
StandardId::new(StandardId::MAX.as_raw()),
|
||||
Some(StandardId::MAX)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn standard_id_new_out_of_range() {
|
||||
assert_eq!(StandardId::new(StandardId::MAX.as_raw() + 1), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn standard_id_new_unchecked_out_of_range() {
|
||||
let id = StandardId::MAX.as_raw() + 1;
|
||||
assert_eq!(unsafe { StandardId::new_unchecked(id) }, StandardId(id));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extended_id_new() {
|
||||
assert_eq!(
|
||||
ExtendedId::new(ExtendedId::MAX.as_raw()),
|
||||
Some(ExtendedId::MAX)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extended_id_new_out_of_range() {
|
||||
assert_eq!(ExtendedId::new(ExtendedId::MAX.as_raw() + 1), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extended_id_new_unchecked_out_of_range() {
|
||||
let id = ExtendedId::MAX.as_raw() + 1;
|
||||
assert_eq!(unsafe { ExtendedId::new_unchecked(id) }, ExtendedId(id));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_standard_id_from_extended_id() {
|
||||
assert_eq!(
|
||||
Some(ExtendedId::MAX.standard_id()),
|
||||
StandardId::new((ExtendedId::MAX.0 >> 18) as u16)
|
||||
);
|
||||
}
|
||||
}
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
//! Controller Area Network
|
||||
|
||||
pub mod nb;
|
||||
|
||||
mod id;
|
||||
|
||||
pub use self::id::*;
|
||||
pub use self::nb::*;
|
||||
|
||||
/// A CAN2.0 Frame
|
||||
pub trait Frame: Sized {
|
||||
/// Creates a new frame.
|
||||
///
|
||||
/// This will return `None` if the data slice is too long.
|
||||
fn new(id: impl Into<Id>, data: &[u8]) -> Option<Self>;
|
||||
|
||||
/// Creates a new remote frame (RTR bit set).
|
||||
///
|
||||
/// This will return `None` if the data length code (DLC) is not valid.
|
||||
fn new_remote(id: impl Into<Id>, dlc: usize) -> Option<Self>;
|
||||
|
||||
/// Returns true if this frame is a extended frame.
|
||||
fn is_extended(&self) -> bool;
|
||||
|
||||
/// Returns true if this frame is a standard frame.
|
||||
fn is_standard(&self) -> bool {
|
||||
!self.is_extended()
|
||||
}
|
||||
|
||||
/// Returns true if this frame is a remote frame.
|
||||
fn is_remote_frame(&self) -> bool;
|
||||
|
||||
/// Returns true if this frame is a data frame.
|
||||
fn is_data_frame(&self) -> bool {
|
||||
!self.is_remote_frame()
|
||||
}
|
||||
|
||||
/// Returns the frame identifier.
|
||||
fn id(&self) -> Id;
|
||||
|
||||
/// Returns the data length code (DLC) which is in the range 0..8.
|
||||
///
|
||||
/// For data frames the DLC value always matches the length of the data.
|
||||
/// Remote frames do not carry any data, yet the DLC can be greater than 0.
|
||||
fn dlc(&self) -> usize;
|
||||
|
||||
/// Returns the frame data (0..8 bytes in length).
|
||||
fn data(&self) -> &[u8];
|
||||
}
|
||||
|
||||
/// CAN error
|
||||
pub trait Error: core::fmt::Debug {
|
||||
/// Convert error to a generic CAN error kind
|
||||
///
|
||||
/// By using this method, CAN errors freely defined by HAL implementations
|
||||
/// can be converted to a set of generic serial errors upon which generic
|
||||
/// code can act.
|
||||
fn kind(&self) -> ErrorKind;
|
||||
}
|
||||
|
||||
/// CAN error kind
|
||||
///
|
||||
/// This represents a common set of CAN operation errors. HAL implementations are
|
||||
/// free to define more specific or additional error types. However, by providing
|
||||
/// a mapping to these common CAN errors, generic code can still react to them.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub enum ErrorKind {
|
||||
/// The peripheral receive buffer was overrun.
|
||||
Overrun,
|
||||
|
||||
// MAC sublayer errors
|
||||
/// A bit error is detected at that bit time when the bit value that is
|
||||
/// monitored differs from the bit value sent.
|
||||
Bit,
|
||||
|
||||
/// A stuff error is detected at the bit time of the sixth consecutive
|
||||
/// equal bit level in a frame field that shall be coded by the method
|
||||
/// of bit stuffing.
|
||||
Stuff,
|
||||
|
||||
/// Calculated CRC sequence does not equal the received one.
|
||||
Crc,
|
||||
|
||||
/// A form error shall be detected when a fixed-form bit field contains
|
||||
/// one or more illegal bits.
|
||||
Form,
|
||||
|
||||
/// An ACK error shall be detected by a transmitter whenever it does not
|
||||
/// monitor a dominant bit during the ACK slot.
|
||||
Acknowledge,
|
||||
|
||||
/// 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 {
|
||||
ErrorKind::Overrun => write!(f, "The peripheral receive buffer was overrun"),
|
||||
ErrorKind::Bit => write!(
|
||||
f,
|
||||
"Bit value that is monitored differs from the bit value sent"
|
||||
),
|
||||
ErrorKind::Stuff => write!(f, "Sixth consecutive equal bits detected"),
|
||||
ErrorKind::Crc => write!(f, "Calculated CRC sequence does not equal the received one"),
|
||||
ErrorKind::Form => write!(
|
||||
f,
|
||||
"A fixed-form bit field contains one or more illegal bits"
|
||||
),
|
||||
ErrorKind::Acknowledge => write!(f, "Transmitted frame was not acknowledged"),
|
||||
ErrorKind::Other => write!(
|
||||
f,
|
||||
"A different error occurred. The original error may contain more information"
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
//! Non-blocking CAN API
|
||||
|
||||
/// A CAN interface that is able to transmit and receive frames.
|
||||
pub trait Can {
|
||||
/// Associated frame type.
|
||||
type Frame: crate::can::Frame;
|
||||
|
||||
/// Associated error type.
|
||||
type Error: crate::can::Error;
|
||||
|
||||
/// Puts a frame in the transmit buffer to be sent on the bus.
|
||||
///
|
||||
/// If the transmit buffer is full, this function will try to replace a pending
|
||||
/// lower priority frame and return the frame that was replaced.
|
||||
/// Returns `Err(WouldBlock)` if the transmit buffer is full and no frame can be
|
||||
/// replaced.
|
||||
///
|
||||
/// # Notes for implementers
|
||||
///
|
||||
/// * Frames of equal identifier shall be transmited in FIFO fashion when more
|
||||
/// than one transmit buffer is available.
|
||||
/// * When replacing pending frames make sure the frame is not in the process of
|
||||
/// being send to the bus.
|
||||
fn transmit(&mut self, frame: &Self::Frame) -> nb::Result<Option<Self::Frame>, Self::Error>;
|
||||
|
||||
/// Returns a received frame if available.
|
||||
fn receive(&mut self) -> nb::Result<Self::Frame, Self::Error>;
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
//! Digital I/O
|
||||
//!
|
||||
//!
|
||||
//!
|
||||
|
||||
// Deprecated / infallible traits
|
||||
#[deprecated(
|
||||
since = "0.2.2",
|
||||
note = "Deprecated because the methods cannot return errors. \
|
||||
Users should use the traits in digital::v2."
|
||||
)]
|
||||
pub mod v1;
|
||||
|
||||
// New / fallible traits
|
||||
pub mod v2;
|
||||
|
||||
// v2 -> v1 compatibility wrappers
|
||||
// These require explicit casts from v2 -> v1
|
||||
pub mod v1_compat;
|
||||
|
||||
// v1 -> v2 compatibility shims
|
||||
// These are implicit over v1 implementations
|
||||
pub mod v2_compat;
|
||||
|
||||
// Re-export old traits so this isn't a breaking change
|
||||
#[allow(deprecated)]
|
||||
pub use self::v1::*;
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
//! Digital I/O
|
||||
//!
|
||||
//! The traits in this module are now deprecated. Please use the new versions included
|
||||
//! in `digital::v2`.
|
||||
|
||||
#![allow(deprecated)]
|
||||
|
||||
/// Single digital push-pull output pin
|
||||
///
|
||||
/// *This version of the trait is now deprecated. Please use the new `OutputPin` trait in
|
||||
/// `digital::v2::OutputPin`*.
|
||||
|
||||
pub trait OutputPin {
|
||||
/// 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);
|
||||
|
||||
/// 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);
|
||||
}
|
||||
|
||||
/// Push-pull output pin that can read its output state
|
||||
///
|
||||
/// *This trait is available if embedded-hal is built with the `"unproven"` feature.*
|
||||
///
|
||||
/// *This version of the trait is now deprecated. Please use the new `StatefulOutputPin` trait in
|
||||
/// `digital::v2::StatefulOutputPin`*.
|
||||
#[cfg(feature = "unproven")]
|
||||
pub trait StatefulOutputPin {
|
||||
/// Is the pin in drive high mode?
|
||||
///
|
||||
/// *NOTE* this does *not* read the electrical state of the pin
|
||||
fn is_set_high(&self) -> bool;
|
||||
|
||||
/// Is the pin in drive low mode?
|
||||
///
|
||||
/// *NOTE* this does *not* read the electrical state of the pin
|
||||
fn is_set_low(&self) -> bool;
|
||||
}
|
||||
|
||||
/// Output pin that can be toggled
|
||||
///
|
||||
/// *This trait is available if embedded-hal is built with the `"unproven"` feature.*
|
||||
///
|
||||
/// *This version of the trait is now deprecated. Please use the new `ToggleableOutputPin`
|
||||
/// trait in `digital::v2::ToggleableOutputPin`*.
|
||||
///
|
||||
/// 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.
|
||||
#[cfg(feature = "unproven")]
|
||||
pub trait ToggleableOutputPin {
|
||||
/// Toggle pin output.
|
||||
fn toggle(&mut self);
|
||||
}
|
||||
|
||||
/// If you can read **and** write the output state, a pin is
|
||||
/// toggleable by software.
|
||||
///
|
||||
/// *This version of the module is now deprecated. Please use the new `toggleable` module in
|
||||
/// `digital::v2::toggleable`*.
|
||||
///
|
||||
/// ```
|
||||
/// use embedded_hal::digital::{OutputPin, StatefulOutputPin, ToggleableOutputPin};
|
||||
/// use embedded_hal::digital::toggleable;
|
||||
///
|
||||
/// /// A virtual output pin that exists purely in software
|
||||
/// struct MyPin {
|
||||
/// state: bool
|
||||
/// }
|
||||
///
|
||||
/// impl OutputPin for MyPin {
|
||||
/// fn set_low(&mut self) {
|
||||
/// self.state = false;
|
||||
/// }
|
||||
/// fn set_high(&mut self) {
|
||||
/// self.state = true;
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// impl StatefulOutputPin for MyPin {
|
||||
/// fn is_set_low(&self) -> bool {
|
||||
/// !self.state
|
||||
/// }
|
||||
/// fn is_set_high(&self) -> bool {
|
||||
/// self.state
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// /// Opt-in to the software implementation.
|
||||
/// impl toggleable::Default for MyPin {}
|
||||
///
|
||||
/// let mut pin = MyPin { state: false };
|
||||
/// pin.toggle();
|
||||
/// assert!(pin.is_set_high());
|
||||
/// pin.toggle();
|
||||
/// assert!(pin.is_set_low());
|
||||
/// ```
|
||||
#[cfg(feature = "unproven")]
|
||||
pub mod toggleable {
|
||||
#[allow(deprecated)]
|
||||
use super::{OutputPin, StatefulOutputPin, ToggleableOutputPin};
|
||||
|
||||
/// Software-driven `toggle()` implementation.
|
||||
///
|
||||
/// *This trait is available if embedded-hal is built with the `"unproven"` feature.*
|
||||
#[allow(deprecated)]
|
||||
pub trait Default: OutputPin + StatefulOutputPin {}
|
||||
|
||||
#[allow(deprecated)]
|
||||
impl<P> ToggleableOutputPin for P
|
||||
where
|
||||
P: Default,
|
||||
{
|
||||
/// Toggle pin output
|
||||
fn toggle(&mut self) {
|
||||
if self.is_set_low() {
|
||||
self.set_high();
|
||||
} else {
|
||||
self.set_low();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Single digital input pin
|
||||
///
|
||||
/// *This trait is available if embedded-hal is built with the `"unproven"` feature.*
|
||||
///
|
||||
/// *This version of the trait is now deprecated. Please use the new `InputPin` trait in
|
||||
/// `digital::v2::InputPin`*.
|
||||
#[cfg(feature = "unproven")]
|
||||
pub trait InputPin {
|
||||
/// Is the input pin high?
|
||||
fn is_high(&self) -> bool;
|
||||
|
||||
/// Is the input pin low?
|
||||
fn is_low(&self) -> bool;
|
||||
}
|
||||
+298
@@ -0,0 +1,298 @@
|
||||
//! v1 compatibility wrappers
|
||||
//!
|
||||
//! This module provides wrappers to support use of v2 implementations with
|
||||
//! v1 consumers. v2 traits must be explicitly cast to the v1 version using
|
||||
//! `.into()`, and will panic on internal errors
|
||||
//!
|
||||
//! ```
|
||||
//! extern crate embedded_hal;
|
||||
//! use embedded_hal::digital::{v1, v2, v1_compat::OldOutputPin};
|
||||
//!
|
||||
//! struct NewOutputPinImpl {}
|
||||
//!
|
||||
//! impl v2::OutputPin for NewOutputPinImpl {
|
||||
//! type Error = ();
|
||||
//! fn set_low(&mut self) -> Result<(), Self::Error> { Ok(()) }
|
||||
//! fn set_high(&mut self) -> Result<(), Self::Error>{ Ok(()) }
|
||||
//! }
|
||||
//!
|
||||
//! struct OldOutputPinConsumer<T: v1::OutputPin> {
|
||||
//! _pin: T,
|
||||
//! }
|
||||
//!
|
||||
//! impl <T>OldOutputPinConsumer<T>
|
||||
//! where T: v1::OutputPin {
|
||||
//! pub fn new(pin: T) -> OldOutputPinConsumer<T> {
|
||||
//! OldOutputPinConsumer{ _pin: pin }
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! fn main() {
|
||||
//! let pin = NewOutputPinImpl{};
|
||||
//! let _consumer: OldOutputPinConsumer<OldOutputPin<_>> = OldOutputPinConsumer::new(pin.into());
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
|
||||
#[allow(deprecated)]
|
||||
use super::v1;
|
||||
use super::v2;
|
||||
|
||||
/// Wrapper to allow fallible `v2::OutputPin` traits to be converted to `v1::OutputPin` traits
|
||||
pub struct OldOutputPin<T> {
|
||||
pin: T,
|
||||
}
|
||||
|
||||
impl<T, E> OldOutputPin<T>
|
||||
where
|
||||
T: v2::OutputPin<Error = E>,
|
||||
E: core::fmt::Debug,
|
||||
{
|
||||
/// Create a new OldOutputPin wrapper around a `v2::OutputPin`
|
||||
pub fn new(pin: T) -> Self {
|
||||
Self { pin }
|
||||
}
|
||||
|
||||
/// Fetch a reference to the inner `v2::OutputPin` impl
|
||||
#[cfg(test)]
|
||||
fn inner(&self) -> &T {
|
||||
&self.pin
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> From<T> for OldOutputPin<T>
|
||||
where
|
||||
T: v2::OutputPin<Error = E>,
|
||||
E: core::fmt::Debug,
|
||||
{
|
||||
fn from(pin: T) -> Self {
|
||||
OldOutputPin { pin }
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation of `v1::OutputPin` trait for fallible `v2::OutputPin` output pins
|
||||
/// where errors will panic.
|
||||
#[allow(deprecated)]
|
||||
impl<T, E> v1::OutputPin for OldOutputPin<T>
|
||||
where
|
||||
T: v2::OutputPin<Error = E>,
|
||||
E: core::fmt::Debug,
|
||||
{
|
||||
fn set_low(&mut self) {
|
||||
self.pin.set_low().unwrap()
|
||||
}
|
||||
|
||||
fn set_high(&mut self) {
|
||||
self.pin.set_high().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation of `v1::StatefulOutputPin` trait for `v2::StatefulOutputPin` fallible pins
|
||||
/// where errors will panic.
|
||||
#[cfg(feature = "unproven")]
|
||||
#[allow(deprecated)]
|
||||
impl<T, E> v1::StatefulOutputPin for OldOutputPin<T>
|
||||
where
|
||||
T: v2::StatefulOutputPin<Error = E>,
|
||||
E: core::fmt::Debug,
|
||||
{
|
||||
fn is_set_low(&self) -> bool {
|
||||
self.pin.is_set_low().unwrap()
|
||||
}
|
||||
|
||||
fn is_set_high(&self) -> bool {
|
||||
self.pin.is_set_high().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper to allow fallible `v2::InputPin` traits to be converted to `v1::InputPin` traits
|
||||
/// where errors will panic.
|
||||
#[cfg(feature = "unproven")]
|
||||
pub struct OldInputPin<T> {
|
||||
pin: T,
|
||||
}
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
impl<T, E> OldInputPin<T>
|
||||
where
|
||||
T: v2::InputPin<Error = E>,
|
||||
E: core::fmt::Debug,
|
||||
{
|
||||
/// Create an `OldInputPin` wrapper around a `v2::InputPin`.
|
||||
pub fn new(pin: T) -> Self {
|
||||
Self { pin }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
impl<T, E> From<T> for OldInputPin<T>
|
||||
where
|
||||
T: v2::InputPin<Error = E>,
|
||||
E: core::fmt::Debug,
|
||||
{
|
||||
fn from(pin: T) -> Self {
|
||||
OldInputPin { pin }
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation of `v1::InputPin` trait for `v2::InputPin` fallible pins
|
||||
/// where errors will panic.
|
||||
#[cfg(feature = "unproven")]
|
||||
#[allow(deprecated)]
|
||||
impl<T, E> v1::InputPin for OldInputPin<T>
|
||||
where
|
||||
T: v2::InputPin<Error = E>,
|
||||
E: core::fmt::Debug,
|
||||
{
|
||||
fn is_low(&self) -> bool {
|
||||
self.pin.is_low().unwrap()
|
||||
}
|
||||
|
||||
fn is_high(&self) -> bool {
|
||||
self.pin.is_high().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(deprecated)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[allow(deprecated)]
|
||||
use crate::digital::v1;
|
||||
use crate::digital::v2;
|
||||
|
||||
use crate::digital::v1::OutputPin;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct NewOutputPinImpl {
|
||||
state: bool,
|
||||
res: Result<(), ()>,
|
||||
}
|
||||
|
||||
impl v2::OutputPin for NewOutputPinImpl {
|
||||
type Error = ();
|
||||
|
||||
fn set_low(&mut self) -> Result<(), Self::Error> {
|
||||
self.state = false;
|
||||
self.res
|
||||
}
|
||||
fn set_high(&mut self) -> Result<(), Self::Error> {
|
||||
self.state = true;
|
||||
self.res
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
struct OldOutputPinConsumer<T: v1::OutputPin> {
|
||||
_pin: T,
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
impl<T> OldOutputPinConsumer<T>
|
||||
where
|
||||
T: v1::OutputPin,
|
||||
{
|
||||
pub fn new(pin: T) -> OldOutputPinConsumer<T> {
|
||||
OldOutputPinConsumer { _pin: pin }
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn v1_v2_output_explicit() {
|
||||
let i = NewOutputPinImpl {
|
||||
state: false,
|
||||
res: Ok(()),
|
||||
};
|
||||
let _c: OldOutputPinConsumer<OldOutputPin<_>> = OldOutputPinConsumer::new(i.into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn v1_v2_output_state() {
|
||||
let mut o: OldOutputPin<_> = NewOutputPinImpl {
|
||||
state: false,
|
||||
res: Ok(()),
|
||||
}
|
||||
.into();
|
||||
|
||||
o.set_high();
|
||||
assert_eq!(o.inner().state, true);
|
||||
|
||||
o.set_low();
|
||||
assert_eq!(o.inner().state, false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn v1_v2_output_panic() {
|
||||
let mut o: OldOutputPin<_> = NewOutputPinImpl {
|
||||
state: false,
|
||||
res: Err(()),
|
||||
}
|
||||
.into();
|
||||
|
||||
o.set_high();
|
||||
}
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
use crate::digital::v1::InputPin;
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
struct NewInputPinImpl {
|
||||
state: Result<bool, ()>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
impl v2::InputPin for NewInputPinImpl {
|
||||
type Error = ();
|
||||
|
||||
fn is_low(&self) -> Result<bool, Self::Error> {
|
||||
self.state.map(|v| v == false)
|
||||
}
|
||||
fn is_high(&self) -> Result<bool, Self::Error> {
|
||||
self.state.map(|v| v == true)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
#[allow(deprecated)]
|
||||
struct OldInputPinConsumer<T: v1::InputPin> {
|
||||
_pin: T,
|
||||
}
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
#[allow(deprecated)]
|
||||
impl<T> OldInputPinConsumer<T>
|
||||
where
|
||||
T: v1::InputPin,
|
||||
{
|
||||
pub fn new(pin: T) -> OldInputPinConsumer<T> {
|
||||
OldInputPinConsumer { _pin: pin }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
#[test]
|
||||
fn v1_v2_input_explicit() {
|
||||
let i = NewInputPinImpl { state: Ok(false) };
|
||||
let _c: OldInputPinConsumer<OldInputPin<_>> = OldInputPinConsumer::new(i.into());
|
||||
}
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
#[test]
|
||||
fn v1_v2_input_state() {
|
||||
let i: OldInputPin<_> = NewInputPinImpl { state: Ok(false) }.into();
|
||||
|
||||
assert_eq!(i.is_low(), true);
|
||||
assert_eq!(i.is_high(), false);
|
||||
}
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn v1_v2_input_panic() {
|
||||
let i: OldInputPin<_> = NewInputPinImpl { state: Err(()) }.into();
|
||||
|
||||
i.is_low();
|
||||
}
|
||||
}
|
||||
+216
@@ -0,0 +1,216 @@
|
||||
//! Digital I/O
|
||||
//!
|
||||
//! Version 2 / fallible traits. Infallible implementations should set Error to `!`.
|
||||
|
||||
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::v2::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<bool> 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
|
||||
///
|
||||
/// *This trait is available if embedded-hal is built with the `"unproven"` feature.*
|
||||
#[cfg(feature = "unproven")]
|
||||
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<bool, Self::Error>;
|
||||
|
||||
/// Is the pin in drive low mode?
|
||||
///
|
||||
/// *NOTE* this does *not* read the electrical state of the pin
|
||||
fn is_set_low(&self) -> Result<bool, Self::Error>;
|
||||
}
|
||||
|
||||
/// Output pin that can be toggled
|
||||
///
|
||||
/// *This trait is available if embedded-hal is built with the `"unproven"` feature.*
|
||||
///
|
||||
/// 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.
|
||||
#[cfg(feature = "unproven")]
|
||||
pub trait ToggleableOutputPin {
|
||||
/// Error type
|
||||
type Error;
|
||||
|
||||
/// Toggle pin output.
|
||||
fn toggle(&mut self) -> Result<(), Self::Error>;
|
||||
}
|
||||
|
||||
/// If you can read **and** write the output state, a pin is
|
||||
/// toggleable by software.
|
||||
///
|
||||
/// ```
|
||||
/// use embedded_hal::digital::v2::{OutputPin, StatefulOutputPin, ToggleableOutputPin};
|
||||
/// use embedded_hal::digital::v2::toggleable;
|
||||
///
|
||||
/// /// A virtual output pin that exists purely in software
|
||||
/// struct MyPin {
|
||||
/// state: bool
|
||||
/// }
|
||||
///
|
||||
/// impl OutputPin for MyPin {
|
||||
/// type Error = void::Void;
|
||||
///
|
||||
/// fn set_low(&mut self) -> Result<(), Self::Error> {
|
||||
/// self.state = false;
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// fn set_high(&mut self) -> Result<(), Self::Error> {
|
||||
/// self.state = true;
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// impl StatefulOutputPin for MyPin {
|
||||
/// fn is_set_low(&self) -> Result<bool, Self::Error> {
|
||||
/// Ok(!self.state)
|
||||
/// }
|
||||
/// fn is_set_high(&self) -> Result<bool, Self::Error> {
|
||||
/// Ok(self.state)
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// /// Opt-in to the software implementation.
|
||||
/// impl toggleable::Default for MyPin {}
|
||||
///
|
||||
/// let mut pin = MyPin { state: false };
|
||||
/// pin.toggle().unwrap();
|
||||
/// assert!(pin.is_set_high().unwrap());
|
||||
/// pin.toggle().unwrap();
|
||||
/// assert!(pin.is_set_low().unwrap());
|
||||
/// ```
|
||||
#[cfg(feature = "unproven")]
|
||||
pub mod toggleable {
|
||||
use super::{OutputPin, StatefulOutputPin, ToggleableOutputPin};
|
||||
|
||||
/// Software-driven `toggle()` implementation.
|
||||
///
|
||||
/// *This trait is available if embedded-hal is built with the `"unproven"` feature.*
|
||||
pub trait Default: OutputPin + StatefulOutputPin {}
|
||||
|
||||
impl<P> ToggleableOutputPin for P
|
||||
where
|
||||
P: Default,
|
||||
{
|
||||
type Error = P::Error;
|
||||
|
||||
/// Toggle pin output
|
||||
fn toggle(&mut self) -> Result<(), Self::Error> {
|
||||
if self.is_set_low()? {
|
||||
self.set_high()
|
||||
} else {
|
||||
self.set_low()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Single digital input pin
|
||||
///
|
||||
/// *This trait is available if embedded-hal is built with the `"unproven"` feature.*
|
||||
#[cfg(feature = "unproven")]
|
||||
pub trait InputPin {
|
||||
/// Error type
|
||||
type Error;
|
||||
|
||||
/// Is the input pin high?
|
||||
fn is_high(&self) -> Result<bool, Self::Error>;
|
||||
|
||||
/// Is the input pin low?
|
||||
fn is_low(&self) -> Result<bool, Self::Error>;
|
||||
}
|
||||
|
||||
/// 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`):
|
||||
///
|
||||
/// *This trait is available if embedded-hal is built with the `"unproven"` feature.*
|
||||
#[cfg(feature = "unproven")]
|
||||
pub trait IoPin<TInput, TOutput>
|
||||
where
|
||||
TInput: InputPin + IoPin<TInput, TOutput>,
|
||||
TOutput: OutputPin + IoPin<TInput, TOutput>,
|
||||
{
|
||||
/// 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<TInput, Self::Error>;
|
||||
|
||||
/// 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<TOutput, Self::Error>;
|
||||
}
|
||||
+234
@@ -0,0 +1,234 @@
|
||||
//! v2 compatibility shims
|
||||
//!
|
||||
//! This module adds implicit forward support to v1 digital traits,
|
||||
//! allowing v1 implementations to be directly used with v2 consumers.
|
||||
//!
|
||||
//! ```
|
||||
//! extern crate embedded_hal;
|
||||
//! use embedded_hal::digital::{v1, v2};
|
||||
//!
|
||||
//! struct OldOutputPinImpl { }
|
||||
//!
|
||||
//! impl v1::OutputPin for OldOutputPinImpl {
|
||||
//! fn set_low(&mut self) { }
|
||||
//! fn set_high(&mut self) { }
|
||||
//! }
|
||||
//!
|
||||
//! struct NewOutputPinConsumer<T: v2::OutputPin> {
|
||||
//! _pin: T,
|
||||
//! }
|
||||
//!
|
||||
//! impl <T>NewOutputPinConsumer<T>
|
||||
//! where T: v2::OutputPin {
|
||||
//! pub fn new(pin: T) -> NewOutputPinConsumer<T> {
|
||||
//! NewOutputPinConsumer{ _pin: pin }
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! fn main() {
|
||||
//! let pin = OldOutputPinImpl{};
|
||||
//! let _consumer = NewOutputPinConsumer::new(pin);
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
|
||||
#[allow(deprecated)]
|
||||
use super::v1;
|
||||
use super::v2;
|
||||
|
||||
/// Implementation of fallible `v2::OutputPin` for `v1::OutputPin` traits
|
||||
#[allow(deprecated)]
|
||||
impl<T> v2::OutputPin for T
|
||||
where
|
||||
T: v1::OutputPin,
|
||||
{
|
||||
// TODO: update to ! when never_type is stabilized
|
||||
type Error = ();
|
||||
|
||||
fn set_low(&mut self) -> Result<(), Self::Error> {
|
||||
Ok(self.set_low())
|
||||
}
|
||||
|
||||
fn set_high(&mut self) -> Result<(), Self::Error> {
|
||||
Ok(self.set_high())
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation of fallible `v2::StatefulOutputPin` for `v1::StatefulOutputPin` digital traits
|
||||
#[cfg(feature = "unproven")]
|
||||
#[allow(deprecated)]
|
||||
impl<T> v2::StatefulOutputPin for T
|
||||
where
|
||||
T: v1::StatefulOutputPin + v1::OutputPin,
|
||||
{
|
||||
fn is_set_low(&self) -> Result<bool, Self::Error> {
|
||||
Ok(self.is_set_low())
|
||||
}
|
||||
|
||||
fn is_set_high(&self) -> Result<bool, Self::Error> {
|
||||
Ok(self.is_set_high())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
#[allow(deprecated)]
|
||||
impl<T> v2::toggleable::Default for T where T: v1::toggleable::Default {}
|
||||
|
||||
/// Implementation of fallible `v2::InputPin` for `v1::InputPin` digital traits
|
||||
#[cfg(feature = "unproven")]
|
||||
#[allow(deprecated)]
|
||||
impl<T> v2::InputPin for T
|
||||
where
|
||||
T: v1::InputPin,
|
||||
{
|
||||
// TODO: update to ! when never_type is stabilized
|
||||
type Error = ();
|
||||
|
||||
fn is_low(&self) -> Result<bool, Self::Error> {
|
||||
Ok(self.is_low())
|
||||
}
|
||||
|
||||
fn is_high(&self) -> Result<bool, Self::Error> {
|
||||
Ok(self.is_high())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
#[allow(deprecated)]
|
||||
use crate::digital::v1;
|
||||
use crate::digital::v2;
|
||||
|
||||
#[allow(deprecated)]
|
||||
struct OldOutputPinImpl {
|
||||
state: bool,
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
impl v1::OutputPin for OldOutputPinImpl {
|
||||
fn set_low(&mut self) {
|
||||
self.state = false;
|
||||
}
|
||||
fn set_high(&mut self) {
|
||||
self.state = true;
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
#[cfg(feature = "unproven")]
|
||||
impl v1::StatefulOutputPin for OldOutputPinImpl {
|
||||
fn is_set_low(&self) -> bool {
|
||||
self.state == false
|
||||
}
|
||||
|
||||
fn is_set_high(&self) -> bool {
|
||||
self.state == true
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
#[cfg(feature = "unproven")]
|
||||
impl v1::toggleable::Default for OldOutputPinImpl {}
|
||||
|
||||
struct NewOutputPinConsumer<T: v2::OutputPin> {
|
||||
_pin: T,
|
||||
}
|
||||
|
||||
impl<T> NewOutputPinConsumer<T>
|
||||
where
|
||||
T: v2::OutputPin,
|
||||
{
|
||||
pub fn new(pin: T) -> NewOutputPinConsumer<T> {
|
||||
NewOutputPinConsumer { _pin: pin }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
struct NewToggleablePinConsumer<T: v2::ToggleableOutputPin> {
|
||||
_pin: T,
|
||||
}
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
impl<T> NewToggleablePinConsumer<T>
|
||||
where
|
||||
T: v2::ToggleableOutputPin,
|
||||
{
|
||||
pub fn new(pin: T) -> NewToggleablePinConsumer<T> {
|
||||
NewToggleablePinConsumer { _pin: pin }
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "unproven")]
|
||||
fn v2_v1_toggleable_implicit() {
|
||||
let i = OldOutputPinImpl { state: false };
|
||||
let _c = NewToggleablePinConsumer::new(i);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn v2_v1_output_implicit() {
|
||||
let i = OldOutputPinImpl { state: false };
|
||||
let _c = NewOutputPinConsumer::new(i);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn v2_v1_output_state() {
|
||||
let mut o = OldOutputPinImpl { state: false };
|
||||
|
||||
v2::OutputPin::set_high(&mut o).unwrap();
|
||||
assert_eq!(o.state, true);
|
||||
|
||||
v2::OutputPin::set_low(&mut o).unwrap();
|
||||
assert_eq!(o.state, false);
|
||||
}
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
#[allow(deprecated)]
|
||||
struct OldInputPinImpl {
|
||||
state: bool,
|
||||
}
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
#[allow(deprecated)]
|
||||
impl v1::InputPin for OldInputPinImpl {
|
||||
fn is_low(&self) -> bool {
|
||||
!self.state
|
||||
}
|
||||
fn is_high(&self) -> bool {
|
||||
self.state
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
struct NewInputPinConsumer<T: v2::InputPin> {
|
||||
_pin: T,
|
||||
}
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
impl<T> NewInputPinConsumer<T>
|
||||
where
|
||||
T: v2::InputPin,
|
||||
{
|
||||
pub fn new(pin: T) -> NewInputPinConsumer<T> {
|
||||
NewInputPinConsumer { _pin: pin }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
#[test]
|
||||
#[cfg(feature = "unproven")]
|
||||
fn v2_v1_input_implicit() {
|
||||
let i = OldInputPinImpl { state: false };
|
||||
let _c = NewInputPinConsumer::new(i);
|
||||
}
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
#[test]
|
||||
fn v2_v1_input_state() {
|
||||
let mut i = OldInputPinImpl { state: false };
|
||||
|
||||
assert_eq!(v2::InputPin::is_high(&mut i).unwrap(), false);
|
||||
assert_eq!(v2::InputPin::is_low(&mut i).unwrap(), true);
|
||||
}
|
||||
}
|
||||
Vendored
+18
@@ -0,0 +1,18 @@
|
||||
//! Implementation of `core::fmt::Write` for the HAL's `serial::Write`.
|
||||
//!
|
||||
//! TODO write example of usage
|
||||
use core::fmt::{Result, Write};
|
||||
|
||||
impl<Word, Error> Write for dyn (::serial::Write<Word, Error = Error>)
|
||||
where
|
||||
Word: From<u8>,
|
||||
{
|
||||
fn write_str(&mut self, s: &str) -> Result {
|
||||
let _ = s
|
||||
.as_bytes()
|
||||
.into_iter()
|
||||
.map(|c| block!(self.write(Word::from(*c))))
|
||||
.last();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Vendored
+999
@@ -0,0 +1,999 @@
|
||||
//! A Hardware Abstraction Layer (HAL) for embedded systems
|
||||
//!
|
||||
//! **NOTE** This HAL is still is active development. Expect the traits presented here to be
|
||||
//! tweaked, split or be replaced wholesale before being stabilized, i.e. before hitting the 1.0.0
|
||||
//! release. That being said there's a part of the HAL that's currently considered unproven and is
|
||||
//! hidden behind an "unproven" Cargo feature. This API is even more volatile and it's exempt from
|
||||
//! semver rules: it can change in a non-backward compatible fashion or even disappear in between
|
||||
//! patch releases.
|
||||
//!
|
||||
//! # Design goals
|
||||
//!
|
||||
//! The HAL
|
||||
//!
|
||||
//! - Must *erase* device specific details. Neither register, register blocks or magic values should
|
||||
//! appear in the API.
|
||||
//!
|
||||
//! - Must be generic *within* a device and *across* devices. The API to use a serial interface must
|
||||
//! be the same regardless of whether the implementation uses the USART1 or UART4 peripheral of a
|
||||
//! device or the UART0 peripheral of another device.
|
||||
//!
|
||||
//! - Where possible must *not* be tied to a specific asynchronous model. The API should be usable
|
||||
//! in blocking mode, with the `futures` model, with an async/await model or with a callback model.
|
||||
//! (cf. the [`nb`] crate)
|
||||
//!
|
||||
//! - Must be minimal, and thus easy to implement and zero cost, yet highly composable. People that
|
||||
//! want higher level abstraction should *prefer to use this HAL* rather than *re-implement*
|
||||
//! register manipulation code.
|
||||
//!
|
||||
//! - Serve as a foundation for building an ecosystem of platform agnostic drivers. Here driver
|
||||
//! means a library crate that lets a target platform interface an external device like a digital
|
||||
//! sensor or a wireless transceiver. The advantage of this system is that by writing the driver as
|
||||
//! a generic library on top of `embedded-hal` driver authors can support any number of target
|
||||
//! platforms (e.g. Cortex-M microcontrollers, AVR microcontrollers, embedded Linux, etc.). The
|
||||
//! advantage for application developers is that by adopting `embedded-hal` they can unlock all
|
||||
//! these drivers for their platform.
|
||||
//!
|
||||
//! # Out of scope
|
||||
//!
|
||||
//! - Initialization and configuration stuff like "ensure this serial interface and that SPI
|
||||
//! interface are not using the same pins". The HAL will focus on *doing I/O*.
|
||||
//!
|
||||
//! # Reference implementation
|
||||
//!
|
||||
//! The [`stm32f30x-hal`] crate contains a reference implementation of this HAL.
|
||||
//!
|
||||
//! [`stm32f30x-hal`]: https://crates.io/crates/stm32f30x-hal/0.1.0
|
||||
//!
|
||||
//! # Platform agnostic drivers
|
||||
//!
|
||||
//! You can find platform agnostic drivers built on top of `embedded-hal` on crates.io by [searching
|
||||
//! for the *embedded-hal* keyword](https://crates.io/keywords/embedded-hal).
|
||||
//!
|
||||
//! If you writing a platform agnostic driver yourself you are highly encouraged to [add the
|
||||
//! embedded-hal keyword](https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata)
|
||||
//! to your crate before publishing it!
|
||||
//!
|
||||
//! # Detailed design
|
||||
//!
|
||||
//! ## Traits
|
||||
//!
|
||||
//! The HAL is specified as traits to allow generic programming. These traits make use of the
|
||||
//! [`nb`][] crate (*please go read that crate documentation before continuing*) to abstract over
|
||||
//! the asynchronous model and to also provide a blocking operation mode.
|
||||
//!
|
||||
//! [`nb`]: https://crates.io/crates/nb
|
||||
//!
|
||||
//! Here's how a HAL trait may look like:
|
||||
//!
|
||||
//! ```
|
||||
//! extern crate nb;
|
||||
//!
|
||||
//! /// A serial interface
|
||||
//! pub trait Serial {
|
||||
//! /// Error type associated to this serial interface
|
||||
//! type Error;
|
||||
//!
|
||||
//! /// Reads a single byte
|
||||
//! fn read(&mut self) -> nb::Result<u8, Self::Error>;
|
||||
//!
|
||||
//! /// Writes a single byte
|
||||
//! fn write(&mut self, byte: u8) -> nb::Result<(), Self::Error>;
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! The `nb::Result` enum is used to add a [`WouldBlock`] variant to the errors
|
||||
//! of the serial interface. As explained in the documentation of the `nb` crate this single API,
|
||||
//! when paired with the macros in the `nb` crate, can operate in a blocking manner, or in a
|
||||
//! non-blocking manner compatible with `futures` and with the `await!` operator.
|
||||
//!
|
||||
//! [`WouldBlock`]: https://docs.rs/nb/0.1.0/nb/enum.Error.html
|
||||
//!
|
||||
//! Some traits, like the one shown below, may expose possibly blocking APIs that can't fail. In
|
||||
//! those cases `nb::Result<_, Void>` is used.
|
||||
//!
|
||||
//! ```
|
||||
//! extern crate nb;
|
||||
//! extern crate void;
|
||||
//!
|
||||
//! use void::Void;
|
||||
//!
|
||||
//! /// A count down timer
|
||||
//! pub trait CountDown {
|
||||
//! // ..
|
||||
//!
|
||||
//! /// "waits" until the count down is over
|
||||
//! fn wait(&mut self) -> nb::Result<(), Void>;
|
||||
//! }
|
||||
//!
|
||||
//! # fn main() {}
|
||||
//! ```
|
||||
//!
|
||||
//! ## Suggested implementation
|
||||
//!
|
||||
//! The HAL traits should be implemented for device crates generated via [`svd2rust`] to maximize
|
||||
//! code reuse.
|
||||
//!
|
||||
//! [`svd2rust`]: https://crates.io/crates/svd2rust
|
||||
//!
|
||||
//! Shown below is an implementation of some of the HAL traits for the [`stm32f30x`] crate. This
|
||||
//! single implementation will work for *any* microcontroller in the STM32F30x family.
|
||||
//!
|
||||
//! [`stm32f30x`]: https://crates.io/crates/stm32f30x
|
||||
//!
|
||||
//! ```
|
||||
//! // crate: stm32f30x-hal
|
||||
//! // An implementation of the `embedded-hal` traits for STM32F30x microcontrollers
|
||||
//!
|
||||
//! extern crate embedded_hal as hal;
|
||||
//! extern crate nb;
|
||||
//!
|
||||
//! // device crate
|
||||
//! extern crate stm32f30x;
|
||||
//!
|
||||
//! use stm32f30x::USART1;
|
||||
//!
|
||||
//! /// A serial interface
|
||||
//! // NOTE generic over the USART peripheral
|
||||
//! pub struct Serial<USART> { usart: USART }
|
||||
//!
|
||||
//! // convenience type alias
|
||||
//! pub type Serial1 = Serial<USART1>;
|
||||
//!
|
||||
//! /// Serial interface error
|
||||
//! pub enum Error {
|
||||
//! /// Buffer overrun
|
||||
//! Overrun,
|
||||
//! // omitted: other error variants
|
||||
//! }
|
||||
//!
|
||||
//! impl hal::serial::Read<u8> for Serial<USART1> {
|
||||
//! type Error = Error;
|
||||
//!
|
||||
//! fn read(&mut self) -> nb::Result<u8, Error> {
|
||||
//! // read the status register
|
||||
//! let isr = self.usart.isr.read();
|
||||
//!
|
||||
//! if isr.ore().bit_is_set() {
|
||||
//! // Error: Buffer overrun
|
||||
//! Err(nb::Error::Other(Error::Overrun))
|
||||
//! }
|
||||
//! // omitted: checks for other errors
|
||||
//! else if isr.rxne().bit_is_set() {
|
||||
//! // Data available: read the data register
|
||||
//! Ok(self.usart.rdr.read().bits() as u8)
|
||||
//! } else {
|
||||
//! // No data available yet
|
||||
//! Err(nb::Error::WouldBlock)
|
||||
//! }
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! impl hal::serial::Write<u8> for Serial<USART1> {
|
||||
//! type Error = Error;
|
||||
//!
|
||||
//! fn write(&mut self, byte: u8) -> nb::Result<(), Error> {
|
||||
//! // Similar to the `read` implementation
|
||||
//! # Ok(())
|
||||
//! }
|
||||
//!
|
||||
//! fn flush(&mut self) -> nb::Result<(), Error> {
|
||||
//! // Similar to the `read` implementation
|
||||
//! # Ok(())
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! # fn main() {}
|
||||
//! ```
|
||||
//!
|
||||
//! ## Intended usage
|
||||
//!
|
||||
//! Thanks to the [`nb`] crate the HAL API can be used in a blocking manner,
|
||||
//! with `futures` or with the `await` operator using the [`block!`],
|
||||
//! [`try_nb!`] and [`await!`] macros respectively.
|
||||
//!
|
||||
//! [`block!`]: https://docs.rs/nb/0.1.0/nb/macro.block.html
|
||||
//! [`try_nb!`]: https://docs.rs/nb/0.1.0/nb/index.html#how-to-use-this-crate
|
||||
//! [`await!`]: https://docs.rs/nb/0.1.0/nb/index.html#how-to-use-this-crate
|
||||
//!
|
||||
//! ### Blocking mode
|
||||
//!
|
||||
//! An example of sending a string over the serial interface in a blocking
|
||||
//! fashion:
|
||||
//!
|
||||
//! ```
|
||||
//! extern crate embedded_hal;
|
||||
//! #[macro_use(block)]
|
||||
//! extern crate nb;
|
||||
//!
|
||||
//! use stm32f30x_hal::Serial1;
|
||||
//! use embedded_hal::serial::Write;
|
||||
//!
|
||||
//! # fn main() {
|
||||
//! let mut serial: Serial1 = {
|
||||
//! // ..
|
||||
//! # Serial1
|
||||
//! };
|
||||
//!
|
||||
//! for byte in b"Hello, world!" {
|
||||
//! // NOTE `block!` blocks until `serial.write()` completes and returns
|
||||
//! // `Result<(), Error>`
|
||||
//! block!(serial.write(*byte)).unwrap();
|
||||
//! }
|
||||
//! # }
|
||||
//!
|
||||
//! # mod stm32f30x_hal {
|
||||
//! # extern crate void;
|
||||
//! # use self::void::Void;
|
||||
//! # pub struct Serial1;
|
||||
//! # impl Serial1 {
|
||||
//! # pub fn write(&mut self, _: u8) -> ::nb::Result<(), Void> {
|
||||
//! # Ok(())
|
||||
//! # }
|
||||
//! # }
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! ### `futures`
|
||||
//!
|
||||
//! An example of running two tasks concurrently. First task: blink an LED every
|
||||
//! second. Second task: loop back data over the serial interface.
|
||||
//!
|
||||
//! ```
|
||||
//! extern crate embedded_hal as hal;
|
||||
//! extern crate futures;
|
||||
//! extern crate void;
|
||||
//!
|
||||
//! #[macro_use(try_nb)]
|
||||
//! extern crate nb;
|
||||
//!
|
||||
//! use hal::prelude::*;
|
||||
//! use futures::{
|
||||
//! future,
|
||||
//! Async,
|
||||
//! Future,
|
||||
//! };
|
||||
//! use futures::future::Loop;
|
||||
//! use stm32f30x_hal::{Led, Serial1, Timer6};
|
||||
//! use void::Void;
|
||||
//!
|
||||
//! /// `futures` version of `CountDown.wait`
|
||||
//! ///
|
||||
//! /// This returns a future that must be polled to completion
|
||||
//! fn wait<T>(mut timer: T) -> impl Future<Item = T, Error = Void>
|
||||
//! where
|
||||
//! T: hal::timer::CountDown,
|
||||
//! {
|
||||
//! let mut timer = Some(timer);
|
||||
//! future::poll_fn(move || {
|
||||
//! try_nb!(timer.as_mut().unwrap().wait());
|
||||
//!
|
||||
//! Ok(Async::Ready(timer.take().unwrap()))
|
||||
//! })
|
||||
//! }
|
||||
//!
|
||||
//! /// `futures` version of `Serial.read`
|
||||
//! ///
|
||||
//! /// This returns a future that must be polled to completion
|
||||
//! fn read<S>(mut serial: S) -> impl Future<Item = (S, u8), Error = S::Error>
|
||||
//! where
|
||||
//! S: hal::serial::Read<u8>,
|
||||
//! {
|
||||
//! let mut serial = Some(serial);
|
||||
//! future::poll_fn(move || {
|
||||
//! let byte = try_nb!(serial.as_mut().unwrap().read());
|
||||
//!
|
||||
//! Ok(Async::Ready((serial.take().unwrap(), byte)))
|
||||
//! })
|
||||
//! }
|
||||
//!
|
||||
//! /// `futures` version of `Serial.write`
|
||||
//! ///
|
||||
//! /// This returns a future that must be polled to completion
|
||||
//! fn write<S>(mut serial: S, byte: u8) -> impl Future<Item = S, Error = S::Error>
|
||||
//! where
|
||||
//! S: hal::serial::Write<u8>,
|
||||
//! {
|
||||
//! let mut serial = Some(serial);
|
||||
//! future::poll_fn(move || {
|
||||
//! try_nb!(serial.as_mut().unwrap().write(byte));
|
||||
//!
|
||||
//! Ok(Async::Ready(serial.take().unwrap()))
|
||||
//! })
|
||||
//! }
|
||||
//!
|
||||
//! fn main() {
|
||||
//! // HAL implementers
|
||||
//! let timer: Timer6 = {
|
||||
//! // ..
|
||||
//! # Timer6
|
||||
//! };
|
||||
//! let serial: Serial1 = {
|
||||
//! // ..
|
||||
//! # Serial1
|
||||
//! };
|
||||
//! let led: Led = {
|
||||
//! // ..
|
||||
//! # Led
|
||||
//! };
|
||||
//!
|
||||
//! // Tasks
|
||||
//! let mut blinky = future::loop_fn::<_, (), _, _>(
|
||||
//! (led, timer, true),
|
||||
//! |(mut led, mut timer, state)| {
|
||||
//! wait(timer).map(move |timer| {
|
||||
//! if state {
|
||||
//! led.on();
|
||||
//! } else {
|
||||
//! led.off();
|
||||
//! }
|
||||
//!
|
||||
//! Loop::Continue((led, timer, !state))
|
||||
//! })
|
||||
//! });
|
||||
//!
|
||||
//! let mut loopback = future::loop_fn::<_, (), _, _>(serial, |mut serial| {
|
||||
//! read(serial).and_then(|(serial, byte)| {
|
||||
//! write(serial, byte)
|
||||
//! }).map(|serial| {
|
||||
//! Loop::Continue(serial)
|
||||
//! })
|
||||
//! });
|
||||
//!
|
||||
//! // Event loop
|
||||
//! loop {
|
||||
//! blinky.poll().unwrap(); // NOTE(unwrap) E = Void
|
||||
//! loopback.poll().unwrap();
|
||||
//! # break;
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! # mod stm32f30x_hal {
|
||||
//! # extern crate void;
|
||||
//! # use self::void::Void;
|
||||
//! # pub struct Timer6;
|
||||
//! # impl ::hal::timer::CountDown for Timer6 {
|
||||
//! # type Time = ();
|
||||
//! #
|
||||
//! # fn start<T>(&mut self, _: T) where T: Into<()> {}
|
||||
//! # fn wait(&mut self) -> ::nb::Result<(), Void> { Err(::nb::Error::WouldBlock) }
|
||||
//! # }
|
||||
//! #
|
||||
//! # pub struct Serial1;
|
||||
//! # impl ::hal::serial::Read<u8> for Serial1 {
|
||||
//! # type Error = Void;
|
||||
//! # fn read(&mut self) -> ::nb::Result<u8, Void> { Err(::nb::Error::WouldBlock) }
|
||||
//! # }
|
||||
//! # impl ::hal::serial::Write<u8> for Serial1 {
|
||||
//! # type Error = Void;
|
||||
//! # fn flush(&mut self) -> ::nb::Result<(), Void> { Err(::nb::Error::WouldBlock) }
|
||||
//! # fn write(&mut self, _: u8) -> ::nb::Result<(), Void> { Err(::nb::Error::WouldBlock) }
|
||||
//! # }
|
||||
//! #
|
||||
//! # pub struct Led;
|
||||
//! # impl Led {
|
||||
//! # pub fn off(&mut self) {}
|
||||
//! # pub fn on(&mut self) {}
|
||||
//! # }
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! ### `await`
|
||||
//!
|
||||
//! Same example as above but using `await!` instead of `futures`.
|
||||
//!
|
||||
//! ```
|
||||
//! #![feature(generator_trait)]
|
||||
//! #![feature(generators)]
|
||||
//!
|
||||
//! extern crate embedded_hal as hal;
|
||||
//!
|
||||
//! #[macro_use(await)]
|
||||
//! extern crate nb;
|
||||
//!
|
||||
//! use std::ops::Generator;
|
||||
//! use std::pin::Pin;
|
||||
//!
|
||||
//! use hal::prelude::*;
|
||||
//! use stm32f30x_hal::{Led, Serial1, Timer6};
|
||||
//!
|
||||
//! fn main() {
|
||||
//! // HAL implementers
|
||||
//! let mut timer: Timer6 = {
|
||||
//! // ..
|
||||
//! # Timer6
|
||||
//! };
|
||||
//! let mut serial: Serial1 = {
|
||||
//! // ..
|
||||
//! # Serial1
|
||||
//! };
|
||||
//! let mut led: Led = {
|
||||
//! // ..
|
||||
//! # Led
|
||||
//! };
|
||||
//!
|
||||
//! // Tasks
|
||||
//! let mut blinky = (move || {
|
||||
//! let mut state = false;
|
||||
//! loop {
|
||||
//! // `await!` means "suspend / yield here" instead of "block until
|
||||
//! // completion"
|
||||
//! await!(timer.wait()).unwrap(); // NOTE(unwrap) E = Void
|
||||
//!
|
||||
//! state = !state;
|
||||
//!
|
||||
//! if state {
|
||||
//! led.on();
|
||||
//! } else {
|
||||
//! led.off();
|
||||
//! }
|
||||
//! }
|
||||
//! });
|
||||
//!
|
||||
//! let mut loopback = (move || {
|
||||
//! loop {
|
||||
//! let byte = await!(serial.read()).unwrap();
|
||||
//! await!(serial.write(byte)).unwrap();
|
||||
//! }
|
||||
//! });
|
||||
//!
|
||||
//! // Event loop
|
||||
//! loop {
|
||||
//! Pin::new(&mut blinky).resume(());
|
||||
//! Pin::new(&mut loopback).resume(());
|
||||
//! # break;
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! # mod stm32f30x_hal {
|
||||
//! # extern crate void;
|
||||
//! # use self::void::Void;
|
||||
//! # pub struct Serial1;
|
||||
//! # impl Serial1 {
|
||||
//! # pub fn read(&mut self) -> ::nb::Result<u8, Void> { Err(::nb::Error::WouldBlock) }
|
||||
//! # pub fn write(&mut self, _: u8) -> ::nb::Result<(), Void> { Err(::nb::Error::WouldBlock) }
|
||||
//! # }
|
||||
//! # pub struct Timer6;
|
||||
//! # impl Timer6 {
|
||||
//! # pub fn wait(&mut self) -> ::nb::Result<(), Void> { Err(::nb::Error::WouldBlock) }
|
||||
//! # }
|
||||
//! # pub struct Led;
|
||||
//! # impl Led {
|
||||
//! # pub fn off(&mut self) {}
|
||||
//! # pub fn on(&mut self) {}
|
||||
//! # }
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! ## Generic programming and higher level abstractions
|
||||
//!
|
||||
//! The core of the HAL has been kept minimal on purpose to encourage building **generic** higher
|
||||
//! level abstractions on top of it. Some higher level abstractions that pick an asynchronous model
|
||||
//! or that have blocking behavior and that are deemed useful to build other abstractions can be
|
||||
//! found in the `blocking` module and, in the future, in the `futures` and `async` modules.
|
||||
//!
|
||||
//! Some examples:
|
||||
//!
|
||||
//! **NOTE** All the functions shown below could have been written as trait
|
||||
//! methods with default implementation to allow specialization, but they have
|
||||
//! been written as functions to keep things simple.
|
||||
//!
|
||||
//! - Write a whole buffer to a serial device in blocking a fashion.
|
||||
//!
|
||||
//! ```
|
||||
//! extern crate embedded_hal as hal;
|
||||
//! #[macro_use(block)]
|
||||
//! extern crate nb;
|
||||
//!
|
||||
//! use hal::prelude::*;
|
||||
//!
|
||||
//! fn write_all<S>(serial: &mut S, buffer: &[u8]) -> Result<(), S::Error>
|
||||
//! where
|
||||
//! S: hal::serial::Write<u8>
|
||||
//! {
|
||||
//! for &byte in buffer {
|
||||
//! block!(serial.write(byte))?;
|
||||
//! }
|
||||
//!
|
||||
//! Ok(())
|
||||
//! }
|
||||
//!
|
||||
//! # fn main() {}
|
||||
//! ```
|
||||
//!
|
||||
//! - Blocking serial read with timeout
|
||||
//!
|
||||
//! ```
|
||||
//! extern crate embedded_hal as hal;
|
||||
//! extern crate nb;
|
||||
//!
|
||||
//! use hal::prelude::*;
|
||||
//!
|
||||
//! enum Error<E> {
|
||||
//! /// Serial interface error
|
||||
//! Serial(E),
|
||||
//! TimedOut,
|
||||
//! }
|
||||
//!
|
||||
//! fn read_with_timeout<S, T>(
|
||||
//! serial: &mut S,
|
||||
//! timer: &mut T,
|
||||
//! timeout: T::Time,
|
||||
//! ) -> Result<u8, Error<S::Error>>
|
||||
//! where
|
||||
//! T: hal::timer::CountDown,
|
||||
//! S: hal::serial::Read<u8>,
|
||||
//! {
|
||||
//! timer.start(timeout);
|
||||
//!
|
||||
//! loop {
|
||||
//! match serial.read() {
|
||||
//! // raise error
|
||||
//! Err(nb::Error::Other(e)) => return Err(Error::Serial(e)),
|
||||
//! Err(nb::Error::WouldBlock) => {
|
||||
//! // no data available yet, check the timer below
|
||||
//! },
|
||||
//! Ok(byte) => return Ok(byte),
|
||||
//! }
|
||||
//!
|
||||
//! match timer.wait() {
|
||||
//! Err(nb::Error::Other(e)) => {
|
||||
//! // The error type specified by `timer.wait()` is `!`, which
|
||||
//! // means no error can actually occur. The Rust compiler
|
||||
//! // still forces us to provide this match arm, though.
|
||||
//! unreachable!()
|
||||
//! },
|
||||
//! // no timeout yet, try again
|
||||
//! Err(nb::Error::WouldBlock) => continue,
|
||||
//! Ok(()) => return Err(Error::TimedOut),
|
||||
//! }
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! # fn main() {}
|
||||
//! ```
|
||||
//!
|
||||
//! - Asynchronous SPI transfer
|
||||
//!
|
||||
//! ```
|
||||
//! #![feature(conservative_impl_trait)]
|
||||
//! #![feature(generators)]
|
||||
//! #![feature(generator_trait)]
|
||||
//!
|
||||
//! extern crate embedded_hal as hal;
|
||||
//! #[macro_use(await)]
|
||||
//! extern crate nb;
|
||||
//!
|
||||
//! use std::ops::Generator;
|
||||
//!
|
||||
//! /// Transfers a byte buffer of size N
|
||||
//! ///
|
||||
//! /// Returns the same byte buffer but filled with the data received from the
|
||||
//! /// slave device
|
||||
//! fn transfer<S, B>(
|
||||
//! mut spi: S,
|
||||
//! mut buffer: [u8; 16], // NOTE this should be generic over the size of the array
|
||||
//! ) -> impl Generator<Return = Result<(S, [u8; 16]), S::Error>, Yield = ()>
|
||||
//! where
|
||||
//! S: hal::spi::FullDuplex<u8>,
|
||||
//! {
|
||||
//! move || {
|
||||
//! let n = buffer.len();
|
||||
//! for i in 0..n {
|
||||
//! await!(spi.send(buffer[i]))?;
|
||||
//! buffer[i] = await!(spi.read())?;
|
||||
//! }
|
||||
//!
|
||||
//! Ok((spi, buffer))
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! # fn main() {}
|
||||
//! ```
|
||||
//!
|
||||
//! - Buffered serial interface with periodic flushing in interrupt handler
|
||||
//!
|
||||
//! ```
|
||||
//! extern crate embedded_hal as hal;
|
||||
//! extern crate nb;
|
||||
//! extern crate void;
|
||||
//!
|
||||
//! use hal::prelude::*;
|
||||
//! use void::Void;
|
||||
//!
|
||||
//! fn flush<S>(serial: &mut S, cb: &mut CircularBuffer)
|
||||
//! where
|
||||
//! S: hal::serial::Write<u8, Error = Void>,
|
||||
//! {
|
||||
//! loop {
|
||||
//! if let Some(byte) = cb.peek() {
|
||||
//! match serial.write(*byte) {
|
||||
//! Err(nb::Error::Other(_)) => unreachable!(),
|
||||
//! Err(nb::Error::WouldBlock) => return,
|
||||
//! Ok(()) => {}, // keep flushing data
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! cb.pop();
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! // The stuff below could be in some other crate
|
||||
//!
|
||||
//! /// Global singleton
|
||||
//! pub struct BufferedSerial1;
|
||||
//!
|
||||
//! // NOTE private
|
||||
//! static BUFFER1: Mutex<CircularBuffer> = {
|
||||
//! // ..
|
||||
//! # Mutex(CircularBuffer)
|
||||
//! };
|
||||
//! static SERIAL1: Mutex<Serial1> = {
|
||||
//! // ..
|
||||
//! # Mutex(Serial1)
|
||||
//! };
|
||||
//!
|
||||
//! impl BufferedSerial1 {
|
||||
//! pub fn write(&self, byte: u8) {
|
||||
//! self.write_all(&[byte])
|
||||
//! }
|
||||
//!
|
||||
//! pub fn write_all(&self, bytes: &[u8]) {
|
||||
//! let mut buffer = BUFFER1.lock();
|
||||
//! for byte in bytes {
|
||||
//! buffer.push(*byte).expect("buffer overrun");
|
||||
//! }
|
||||
//! // omitted: pend / enable interrupt_handler
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! fn interrupt_handler() {
|
||||
//! let mut serial = SERIAL1.lock();
|
||||
//! let mut buffer = BUFFER1.lock();
|
||||
//!
|
||||
//! flush(&mut *serial, &mut buffer);
|
||||
//! }
|
||||
//!
|
||||
//! # struct Mutex<T>(T);
|
||||
//! # impl<T> Mutex<T> {
|
||||
//! # fn lock(&self) -> RefMut<T> { unimplemented!() }
|
||||
//! # }
|
||||
//! # struct RefMut<'a, T>(&'a mut T) where T: 'a;
|
||||
//! # impl<'a, T> ::std::ops::Deref for RefMut<'a, T> {
|
||||
//! # type Target = T;
|
||||
//! # fn deref(&self) -> &T { self.0 }
|
||||
//! # }
|
||||
//! # impl<'a, T> ::std::ops::DerefMut for RefMut<'a, T> {
|
||||
//! # fn deref_mut(&mut self) -> &mut T { self.0 }
|
||||
//! # }
|
||||
//! # struct Serial1;
|
||||
//! # impl ::hal::serial::Write<u8> for Serial1 {
|
||||
//! # type Error = Void;
|
||||
//! # fn write(&mut self, _: u8) -> nb::Result<(), Void> { Err(::nb::Error::WouldBlock) }
|
||||
//! # fn flush(&mut self) -> nb::Result<(), Void> { Err(::nb::Error::WouldBlock) }
|
||||
//! # }
|
||||
//! # struct CircularBuffer;
|
||||
//! # impl CircularBuffer {
|
||||
//! # pub fn peek(&mut self) -> Option<&u8> { None }
|
||||
//! # pub fn pop(&mut self) -> Option<u8> { None }
|
||||
//! # pub fn push(&mut self, _: u8) -> Result<(), ()> { Ok(()) }
|
||||
//! # }
|
||||
//!
|
||||
//! # fn main() {}
|
||||
//! ```
|
||||
|
||||
#![deny(missing_docs)]
|
||||
#![no_std]
|
||||
|
||||
#[macro_use]
|
||||
extern crate nb;
|
||||
extern crate void;
|
||||
|
||||
pub mod adc;
|
||||
pub mod blocking;
|
||||
pub mod can;
|
||||
pub mod digital;
|
||||
pub mod fmt;
|
||||
pub mod prelude;
|
||||
pub mod serial;
|
||||
pub mod spi;
|
||||
pub mod timer;
|
||||
pub mod watchdog;
|
||||
|
||||
/// Input capture
|
||||
///
|
||||
/// *This trait is available if embedded-hal is built with the `"unproven"` feature.*
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// You can use this interface to measure the period of (quasi) periodic signals
|
||||
/// / events
|
||||
///
|
||||
/// ```
|
||||
/// extern crate embedded_hal as hal;
|
||||
/// #[macro_use(block)]
|
||||
/// extern crate nb;
|
||||
///
|
||||
/// use hal::prelude::*;
|
||||
///
|
||||
/// fn main() {
|
||||
/// let mut capture: Capture1 = {
|
||||
/// // ..
|
||||
/// # Capture1
|
||||
/// };
|
||||
///
|
||||
/// capture.set_resolution(1.ms());
|
||||
///
|
||||
/// let before = block!(capture.capture(Channel::_1)).unwrap();
|
||||
/// let after = block!(capture.capture(Channel::_1)).unwrap();
|
||||
///
|
||||
/// let period = after.wrapping_sub(before);
|
||||
///
|
||||
/// println!("Period: {} ms", period);
|
||||
/// }
|
||||
///
|
||||
/// # extern crate void;
|
||||
/// # use void::Void;
|
||||
/// # struct MilliSeconds(u32);
|
||||
/// # trait U32Ext { fn ms(self) -> MilliSeconds; }
|
||||
/// # impl U32Ext for u32 { fn ms(self) -> MilliSeconds { MilliSeconds(self) } }
|
||||
/// # struct Capture1;
|
||||
/// # enum Channel { _1 }
|
||||
/// # impl hal::Capture for Capture1 {
|
||||
/// # type Capture = u16;
|
||||
/// # type Channel = Channel;
|
||||
/// # type Error = Void;
|
||||
/// # type Time = MilliSeconds;
|
||||
/// # fn capture(&mut self, _: Channel) -> ::nb::Result<u16, Void> { Ok(0) }
|
||||
/// # fn disable(&mut self, _: Channel) { unimplemented!() }
|
||||
/// # fn enable(&mut self, _: Channel) { unimplemented!() }
|
||||
/// # fn get_resolution(&self) -> MilliSeconds { unimplemented!() }
|
||||
/// # fn set_resolution<T>(&mut self, _: T) where T: Into<MilliSeconds> {}
|
||||
/// # }
|
||||
/// ```
|
||||
#[cfg(feature = "unproven")]
|
||||
// reason: pre-singletons API. With singletons a `CapturePin` (cf. `PwmPin`) trait seems more
|
||||
// appropriate
|
||||
pub trait Capture {
|
||||
/// Enumeration of `Capture` errors
|
||||
///
|
||||
/// Possible errors:
|
||||
///
|
||||
/// - *overcapture*, the previous capture value was overwritten because it
|
||||
/// was not read in a timely manner
|
||||
type Error;
|
||||
|
||||
/// Enumeration of channels that can be used with this `Capture` interface
|
||||
///
|
||||
/// If your `Capture` 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;
|
||||
|
||||
/// The type of the value returned by `capture`
|
||||
type Capture;
|
||||
|
||||
/// "Waits" for a transition in the capture `channel` and returns the value
|
||||
/// of counter at that instant
|
||||
///
|
||||
/// NOTE that you must multiply the returned value by the *resolution* of
|
||||
/// this `Capture` interface to get a human time unit (e.g. seconds)
|
||||
fn capture(&mut self, channel: Self::Channel) -> nb::Result<Self::Capture, Self::Error>;
|
||||
|
||||
/// Disables a capture `channel`
|
||||
fn disable(&mut self, channel: Self::Channel);
|
||||
|
||||
/// Enables a capture `channel`
|
||||
fn enable(&mut self, channel: Self::Channel);
|
||||
|
||||
/// Returns the current resolution
|
||||
fn get_resolution(&self) -> Self::Time;
|
||||
|
||||
/// Sets the resolution of the capture timer
|
||||
fn set_resolution<R>(&mut self, resolution: R)
|
||||
where
|
||||
R: Into<Self::Time>;
|
||||
}
|
||||
|
||||
/// Pulse Width Modulation
|
||||
///
|
||||
/// *This trait is available if embedded-hal is built with the `"unproven"` feature.*
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Use this interface to control the power output of some actuator
|
||||
///
|
||||
/// ```
|
||||
/// extern crate embedded_hal as hal;
|
||||
///
|
||||
/// use hal::prelude::*;
|
||||
///
|
||||
/// fn main() {
|
||||
/// let mut pwm: Pwm1 = {
|
||||
/// // ..
|
||||
/// # Pwm1
|
||||
/// };
|
||||
///
|
||||
/// pwm.set_period(1.khz());
|
||||
///
|
||||
/// let max_duty = pwm.get_max_duty();
|
||||
///
|
||||
/// // brightest LED
|
||||
/// pwm.set_duty(Channel::_1, max_duty);
|
||||
///
|
||||
/// // dimmer LED
|
||||
/// pwm.set_duty(Channel::_2, max_duty / 4);
|
||||
/// }
|
||||
///
|
||||
/// # 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 for Pwm1 {
|
||||
/// # type Channel = Channel;
|
||||
/// # type Time = KiloHertz;
|
||||
/// # type Duty = u16;
|
||||
/// # fn disable(&mut self, _: Channel) { unimplemented!() }
|
||||
/// # fn enable(&mut self, _: Channel) { unimplemented!() }
|
||||
/// # fn get_duty(&self, _: Channel) -> u16 { unimplemented!() }
|
||||
/// # fn get_max_duty(&self) -> u16 { 0 }
|
||||
/// # fn set_duty(&mut self, _: Channel, _: u16) {}
|
||||
/// # fn get_period(&self) -> KiloHertz { unimplemented!() }
|
||||
/// # fn set_period<T>(&mut self, _: T) where T: Into<KiloHertz> {}
|
||||
/// # }
|
||||
/// ```
|
||||
#[cfg(feature = "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 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);
|
||||
|
||||
/// Enables a PWM `channel`
|
||||
fn enable(&mut self, channel: Self::Channel);
|
||||
|
||||
/// Returns the current PWM period
|
||||
fn get_period(&self) -> Self::Time;
|
||||
|
||||
/// Returns the current duty cycle
|
||||
fn get_duty(&self, channel: Self::Channel) -> Self::Duty;
|
||||
|
||||
/// Returns the maximum duty cycle value
|
||||
fn get_max_duty(&self) -> Self::Duty;
|
||||
|
||||
/// Sets a new duty cycle
|
||||
fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty);
|
||||
|
||||
/// Sets a new PWM period
|
||||
fn set_period<P>(&mut self, period: P)
|
||||
where
|
||||
P: Into<Self::Time>;
|
||||
}
|
||||
|
||||
/// A single PWM channel / pin
|
||||
///
|
||||
/// See `Pwm` for details
|
||||
pub trait PwmPin {
|
||||
/// 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);
|
||||
|
||||
/// Enables a PWM `channel`
|
||||
fn enable(&mut self);
|
||||
|
||||
/// Returns the current duty cycle
|
||||
fn get_duty(&self) -> Self::Duty;
|
||||
|
||||
/// Returns the maximum duty cycle value
|
||||
fn get_max_duty(&self) -> Self::Duty;
|
||||
|
||||
/// Sets a new duty cycle
|
||||
fn set_duty(&mut self, duty: Self::Duty);
|
||||
}
|
||||
|
||||
/// Quadrature encoder interface
|
||||
///
|
||||
/// *This trait is available if embedded-hal is built with the `"unproven"` feature.*
|
||||
///
|
||||
/// # 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::prelude::*;
|
||||
///
|
||||
/// fn main() {
|
||||
/// let mut qei: Qei1 = {
|
||||
/// // ..
|
||||
/// # Qei1
|
||||
/// };
|
||||
/// let mut timer: Timer6 = {
|
||||
/// // ..
|
||||
/// # Timer6
|
||||
/// };
|
||||
///
|
||||
///
|
||||
/// let before = qei.count();
|
||||
/// timer.start(1.s());
|
||||
/// block!(timer.wait());
|
||||
/// let after = qei.count();
|
||||
///
|
||||
/// let speed = after.wrapping_sub(before);
|
||||
/// println!("Speed: {} pulses per second", speed);
|
||||
/// }
|
||||
///
|
||||
/// # extern crate void;
|
||||
/// # use void::Void;
|
||||
/// # struct Seconds(u32);
|
||||
/// # trait U32Ext { fn s(self) -> Seconds; }
|
||||
/// # impl U32Ext for u32 { fn s(self) -> Seconds { Seconds(self) } }
|
||||
/// # struct Qei1;
|
||||
/// # impl hal::Qei for Qei1 {
|
||||
/// # type Count = u16;
|
||||
/// # fn count(&self) -> u16 { 0 }
|
||||
/// # fn direction(&self) -> ::hal::Direction { unimplemented!() }
|
||||
/// # }
|
||||
/// # struct Timer6;
|
||||
/// # impl hal::timer::CountDown for Timer6 {
|
||||
/// # type Time = Seconds;
|
||||
/// # fn start<T>(&mut self, _: T) where T: Into<Seconds> {}
|
||||
/// # fn wait(&mut self) -> ::nb::Result<(), Void> { Ok(()) }
|
||||
/// # }
|
||||
/// ```
|
||||
#[cfg(feature = "unproven")]
|
||||
// reason: needs to be re-evaluated in the new singletons world. At the very least this needs a
|
||||
// reference implementation
|
||||
pub trait Qei {
|
||||
/// The type of the value returned by `count`
|
||||
type Count;
|
||||
|
||||
/// Returns the current pulse count of the encoder
|
||||
fn count(&self) -> Self::Count;
|
||||
|
||||
/// Returns the count direction
|
||||
fn direction(&self) -> Direction;
|
||||
}
|
||||
|
||||
/// Count direction
|
||||
///
|
||||
/// *This enumeration is available if embedded-hal is built with the `"unproven"` feature.*
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
#[cfg(feature = "unproven")]
|
||||
// reason: part of the unproven `Qei` interface
|
||||
pub enum Direction {
|
||||
/// 3, 2, 1
|
||||
Downcounting,
|
||||
/// 1, 2, 3
|
||||
Upcounting,
|
||||
}
|
||||
|
||||
mod private {
|
||||
pub trait Sealed {}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
//! The prelude is a collection of all the traits in this crate
|
||||
//!
|
||||
//! The traits have been renamed to avoid collisions with other items when
|
||||
//! performing a glob import.
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
pub use adc::OneShot as _embedded_hal_adc_OneShot;
|
||||
pub use blocking::delay::DelayMs as _embedded_hal_blocking_delay_DelayMs;
|
||||
pub use blocking::delay::DelayUs as _embedded_hal_blocking_delay_DelayUs;
|
||||
pub use blocking::i2c::{
|
||||
Read as _embedded_hal_blocking_i2c_Read, Write as _embedded_hal_blocking_i2c_Write,
|
||||
WriteRead as _embedded_hal_blocking_i2c_WriteRead,
|
||||
};
|
||||
#[cfg(feature = "unproven")]
|
||||
pub use blocking::rng::Read as _embedded_hal_blocking_rng_Read;
|
||||
pub use blocking::serial::Write as _embedded_hal_blocking_serial_Write;
|
||||
pub use blocking::spi::{
|
||||
Transfer as _embedded_hal_blocking_spi_Transfer, Write as _embedded_hal_blocking_spi_Write,
|
||||
};
|
||||
#[allow(deprecated)]
|
||||
#[cfg(feature = "unproven")]
|
||||
pub use digital::InputPin as _embedded_hal_digital_InputPin;
|
||||
#[allow(deprecated)]
|
||||
pub use digital::OutputPin as _embedded_hal_digital_OutputPin;
|
||||
#[cfg(feature = "unproven")]
|
||||
#[allow(deprecated)]
|
||||
pub use digital::ToggleableOutputPin as _embedded_hal_digital_ToggleableOutputPin;
|
||||
pub use serial::Read as _embedded_hal_serial_Read;
|
||||
pub use serial::Write as _embedded_hal_serial_Write;
|
||||
pub use spi::FullDuplex as _embedded_hal_spi_FullDuplex;
|
||||
pub use timer::CountDown as _embedded_hal_timer_CountDown;
|
||||
#[cfg(feature = "unproven")]
|
||||
pub use watchdog::Watchdog as _embedded_hal_watchdog_Watchdog;
|
||||
#[cfg(feature = "unproven")]
|
||||
pub use watchdog::WatchdogDisable as _embedded_hal_watchdog_WatchdogDisable;
|
||||
#[cfg(feature = "unproven")]
|
||||
pub use watchdog::WatchdogEnable as _embedded_hal_watchdog_WatchdogEnable;
|
||||
#[cfg(feature = "unproven")]
|
||||
pub use Capture as _embedded_hal_Capture;
|
||||
#[cfg(feature = "unproven")]
|
||||
pub use Pwm as _embedded_hal_Pwm;
|
||||
pub use PwmPin as _embedded_hal_PwmPin;
|
||||
#[cfg(feature = "unproven")]
|
||||
pub use Qei as _embedded_hal_Qei;
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
//! Serial interface
|
||||
|
||||
use nb;
|
||||
|
||||
/// Read half of a serial interface
|
||||
///
|
||||
/// Some serial interfaces support different data sizes (8 bits, 9 bits, etc.);
|
||||
/// This can be encoded in this trait via the `Word` type parameter.
|
||||
pub trait Read<Word> {
|
||||
/// Read error
|
||||
type Error;
|
||||
|
||||
/// Reads a single word from the serial interface
|
||||
fn read(&mut self) -> nb::Result<Word, Self::Error>;
|
||||
}
|
||||
|
||||
/// Write half of a serial interface
|
||||
pub trait Write<Word> {
|
||||
/// Write error
|
||||
type Error;
|
||||
|
||||
/// Writes a single word to the serial interface
|
||||
fn write(&mut self, word: Word) -> nb::Result<(), Self::Error>;
|
||||
|
||||
/// Ensures that none of the previously written words are still buffered
|
||||
fn flush(&mut self) -> nb::Result<(), Self::Error>;
|
||||
}
|
||||
Vendored
+78
@@ -0,0 +1,78 @@
|
||||
//! Serial Peripheral Interface
|
||||
|
||||
use nb;
|
||||
|
||||
/// 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 `send` call.
|
||||
///
|
||||
/// - 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<Word> {
|
||||
/// 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<Word, Self::Error>;
|
||||
|
||||
/// Sends a word to the slave
|
||||
fn send(&mut self, word: Word) -> nb::Result<(), Self::Error>;
|
||||
}
|
||||
|
||||
/// Clock polarity
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Polarity {
|
||||
/// Clock signal low when idle
|
||||
IdleLow,
|
||||
/// Clock signal high when idle
|
||||
IdleHigh,
|
||||
}
|
||||
|
||||
/// Clock phase
|
||||
#[derive(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(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,
|
||||
};
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
//! Timers
|
||||
|
||||
use nb;
|
||||
use void::Void;
|
||||
|
||||
/// 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::prelude::*;
|
||||
///
|
||||
/// fn main() {
|
||||
/// let mut led: Led = {
|
||||
/// // ..
|
||||
/// # Led
|
||||
/// };
|
||||
/// let mut timer: Timer6 = {
|
||||
/// // ..
|
||||
/// # Timer6
|
||||
/// };
|
||||
///
|
||||
/// Led.on();
|
||||
/// timer.start(1.s());
|
||||
/// block!(timer.wait()); // blocks for 1 second
|
||||
/// Led.off();
|
||||
/// }
|
||||
///
|
||||
/// # extern crate void;
|
||||
/// # use void::Void;
|
||||
/// # 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::CountDown for Timer6 {
|
||||
/// # type Time = Seconds;
|
||||
/// # fn start<T>(&mut self, _: T) where T: Into<Seconds> {}
|
||||
/// # fn wait(&mut self) -> ::nb::Result<(), Void> { Ok(()) }
|
||||
/// # }
|
||||
/// ```
|
||||
pub trait CountDown {
|
||||
/// The unit of time used by this timer
|
||||
type Time;
|
||||
|
||||
/// Starts a new count down
|
||||
fn start<T>(&mut self, count: T)
|
||||
where
|
||||
T: Into<Self::Time>;
|
||||
|
||||
/// 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<(), Void>;
|
||||
}
|
||||
|
||||
/// Marker trait that indicates that a timer is periodic
|
||||
pub trait Periodic {}
|
||||
|
||||
/// Trait for cancelable countdowns.
|
||||
pub trait Cancel: CountDown {
|
||||
/// Error returned when a countdown can't be canceled.
|
||||
type Error;
|
||||
|
||||
/// 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>;
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
//! Traits for interactions with a processors watchdog timer.
|
||||
|
||||
/// Feeds an existing watchdog to ensure the processor isn't reset. Sometimes
|
||||
/// commonly referred to as "kicking" or "refreshing".
|
||||
#[cfg(feature = "unproven")]
|
||||
pub trait Watchdog {
|
||||
/// Triggers the watchdog. This must be done once the watchdog is started
|
||||
/// to prevent the processor being reset.
|
||||
fn feed(&mut self);
|
||||
}
|
||||
|
||||
/// Enables A watchdog timer to reset the processor if software is frozen or
|
||||
/// stalled.
|
||||
#[cfg(feature = "unproven")]
|
||||
pub trait WatchdogEnable {
|
||||
/// Unit of time used by the watchdog
|
||||
type Time;
|
||||
/// Starts the watchdog with a given period, typically once this is done
|
||||
/// the watchdog needs to be kicked periodically or the processor is reset.
|
||||
fn start<T>(&mut self, period: T)
|
||||
where
|
||||
T: Into<Self::Time>;
|
||||
}
|
||||
|
||||
/// Disables a running watchdog timer so the processor won't be reset.
|
||||
#[cfg(feature = "unproven")]
|
||||
pub trait WatchdogDisable {
|
||||
/// Disables the watchdog
|
||||
fn disable(&mut self);
|
||||
}
|
||||
Reference in new issue
Block a user