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:
swaitsandClaude Opus 4.6 committed 2026-03-29 20:42:54 -06:00
1 parent 7064110293
commit 3102fc747a
17624 files changed
+4564928 -38

No files matched your search

+98
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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>;
}
+54
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
}
}
+18
View File
@@ -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(())
}
}
+999
View File
@@ -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
View File
@@ -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
View File
@@ -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>;
}
+78
View File
@@ -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
View File
@@ -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
View File
@@ -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);
}