mirror of
https://github.com/rust-embedded/embedded-hal.git
synced 2026-03-19 05:40:32 +00:00
make tests functional
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,3 +1,4 @@
|
||||
/target/
|
||||
**/*.rs.bk
|
||||
.#*
|
||||
/target/
|
||||
Cargo.lock
|
||||
|
||||
11
Cargo.toml
11
Cargo.toml
@@ -3,11 +3,18 @@ authors = ["Jorge Aparicio <jorge@japaric.io>"]
|
||||
categories = ["asynchronous", "embedded", "hardware-support", "no-std"]
|
||||
description = "Minimal Hardware Abstraction Layer for embedded systems"
|
||||
documentation = "https://docs.rs/embedded-hal"
|
||||
keywords = ["hal", "I/O"]
|
||||
keywords = ["hal", "IO"]
|
||||
license = "MIT OR Apache-2.0"
|
||||
name = "embedded-hal"
|
||||
repository = "https://github.com/japaric/embedded-hal"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies.nb]
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
|
||||
[dev-dependencies]
|
||||
stm32f30x = { git = "https://github.com/japaric/stm32f30x" }
|
||||
futures = "0.1.17"
|
||||
|
||||
[features]
|
||||
unstable = ["nb/unstable"]
|
||||
|
||||
545
src/lib.rs
545
src/lib.rs
@@ -31,25 +31,24 @@
|
||||
//!
|
||||
//! # Reference implementation
|
||||
//!
|
||||
//! The [`blue-pill`] crate contains a reference implementation of this HAL.
|
||||
//! The [`f3`] crate contains a reference implementation of this HAL.
|
||||
//!
|
||||
//! [`blue-pill`]: https://github.com/japaric/blue-pill
|
||||
//! [`f3`]: https://crates.io/crates/f3/0.5.0
|
||||
//!
|
||||
//! # Detailed design
|
||||
//!
|
||||
//! ## Traits
|
||||
//!
|
||||
//! The HAL is specified using traits to allow generic programming. These traits
|
||||
//! traits will make use of the [`nb`][] [crate][] (*please go read that crate
|
||||
//! traits will 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://github.com/japaric/nb
|
||||
//! [crate]: https://japaric.github.io/nb/nb/
|
||||
//! [`nb`]: https://crates.io/crates/nb
|
||||
//!
|
||||
//! Here's how a HAL trait may look like:
|
||||
//!
|
||||
//! ``` rust
|
||||
//! ```
|
||||
//! extern crate nb;
|
||||
//!
|
||||
//! /// A serial interface
|
||||
@@ -58,24 +57,28 @@
|
||||
//! type Error;
|
||||
//!
|
||||
//! /// Reads a single byte
|
||||
//! fn read(&self) -> nb::Result<u8, Self::Error>;
|
||||
//! fn read(&mut self) -> nb::Result<u8, Self::Error>;
|
||||
//!
|
||||
//! /// Writes a single byte
|
||||
//! fn write(&self, byte: u8) -> nb::Result<(), Self::Error>;
|
||||
//! 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 we'll see below this extra error variant lets
|
||||
//! this single API operate in a blocking manner, or in a non-blocking manner
|
||||
//! compatible with `futures` and with the `await` operator.
|
||||
//! compatible with `futures` and with the `await!` operator.
|
||||
//!
|
||||
//! [`WouldBlock`]: https://japaric.github.io/nb/nb/enum.Error.html
|
||||
//! [`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<_, !>` should be used.
|
||||
//!
|
||||
//! ``` ignore
|
||||
//! ```
|
||||
//! #![feature(never_type)]
|
||||
//!
|
||||
//! extern crate nb;
|
||||
//!
|
||||
//! /// A timer used for timeouts
|
||||
//! pub trait Timer {
|
||||
//! /// A time unit that can be convert to a human time unit
|
||||
@@ -99,6 +102,8 @@
|
||||
//! /// "waits" until the timer times out
|
||||
//! fn wait(&self) -> nb::Result<(), !>;
|
||||
//! }
|
||||
//!
|
||||
//! # fn main() {}
|
||||
//! ```
|
||||
//!
|
||||
//! ## Implementation
|
||||
@@ -108,105 +113,74 @@
|
||||
//!
|
||||
//! [`svd2rust`]: https://crates.io/crates/svd2rust
|
||||
//!
|
||||
//! Shown below is an implementation of the HAL traits for the [`stm32f103xx`]
|
||||
//! Shown below is an implementation of the HAL traits for the [`stm32f30x`]
|
||||
//! crate. This single implementation will work for *any* microcontroller in the
|
||||
//! STM32F103 family.
|
||||
//! STM32F30x family.
|
||||
//!
|
||||
//! [`stm32f103xx`]: https://crates.io/crates/stm32f103xx
|
||||
//! [`stm32f30x`]: https://crates.io/crates/stm32f30x
|
||||
//!
|
||||
//! ``` ignore
|
||||
//! //! An implementation of the `embedded-hal` for STM32F103xx microcontrollers
|
||||
//! ```
|
||||
//! //! An implementation of the `embedded-hal` for STM32F30x microcontrollers
|
||||
//!
|
||||
//! extern crate core;
|
||||
//! extern crate embedded_hal as hal;
|
||||
//! extern crate nb;
|
||||
//!
|
||||
//! // device crate
|
||||
//! extern crate stm32f103xx;
|
||||
//! extern crate stm32f30x;
|
||||
//!
|
||||
//! use core::ops::Deref;
|
||||
//! use stm32f30x::USART1;
|
||||
//!
|
||||
//! /// A serial interface
|
||||
//! // NOTE generic over the USART peripheral. This works with USART1, USART2
|
||||
//! // and USART3
|
||||
//! pub struct Serial<'a, U>(pub &'a U)
|
||||
//! where
|
||||
//! U: Deref<Target=stm32f103xx::usart1::RegisterBlock> + 'static;
|
||||
//! // 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,
|
||||
//! // add more error variants here
|
||||
//! // omitted other error variants
|
||||
//! }
|
||||
//!
|
||||
//! impl<'a, U> hal::serial::Read<u8> for Serial<'a, U>
|
||||
//! where
|
||||
//! U: Deref<Target=stm32f103xx::usart1::RegisterBlock> + 'static
|
||||
//! {
|
||||
//! impl hal::serial::Read<u8> for Serial<USART1> {
|
||||
//! type Error = Error;
|
||||
//!
|
||||
//! fn read(&mut self) -> nb::Result<u8, Error> {
|
||||
//! // read the status register
|
||||
//! let sr = self.0.sr.read();
|
||||
//! let isr = self.usart.isr.read();
|
||||
//!
|
||||
//! if sr.ore().bit_is_set() {
|
||||
//! if isr.ore().bit_is_set() {
|
||||
//! // Error: Buffer overrun
|
||||
//! Err(nb::Error::Other(Error::Overrun))
|
||||
//! }
|
||||
//! // Add additional `else if` statements to check for other errors
|
||||
//! else if sr.rxne().bit_is_set() {
|
||||
//! // omitted: checks for other errors
|
||||
//! else if isr.rxne().bit_is_set() {
|
||||
//! // Data available: read the data register
|
||||
//! Ok(self.0.dr.read().bits() as u8)
|
||||
//! }
|
||||
//! else {
|
||||
//! Ok(self.usart.rdr.read().bits() as u8)
|
||||
//! } else {
|
||||
//! // No data available yet
|
||||
//! Err(nb::Error::WouldBlock)
|
||||
//! }
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! impl<'a, U> hal::serial::Write<u8> for Serial<'a, U>
|
||||
//! where
|
||||
//! U: Deref<Target=stm32f103xx::usart1::RegisterBlock> + 'static
|
||||
//! {
|
||||
//! impl hal::serial::Write<u8> for Serial<USART1> {
|
||||
//! type Error = Error;
|
||||
//!
|
||||
//! fn write(&mut self, byte: u8) -> nb::Result<(), Error> {
|
||||
//! // Very similar to the above implementation
|
||||
//! Ok(())
|
||||
//! // Similar to the `read` implementation
|
||||
//! # Ok(())
|
||||
//! }
|
||||
//!
|
||||
//! fn flush(&mut self) -> nb::Result<(), Error> {
|
||||
//! // Similar to the `read` implementation
|
||||
//! # Ok(())
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! Furthermore the above implementation of `hal::Serial` is generic over the
|
||||
//! USART peripheral instance and will work with peripherals USART1, USART2,
|
||||
//! USART3, etc.
|
||||
//!
|
||||
//! Note that the above implementation uses a newtype over a *reference* to the
|
||||
//! USART register block. This pushes the concern of synchronization to the user
|
||||
//! of the `Serial` abstraction. However it's also possible to *erase* that
|
||||
//! reference by handling the synchronization within the `hal::Serial`
|
||||
//! implementation:
|
||||
//!
|
||||
//! ``` ignore
|
||||
//! extern crate embedded_hal as hal;
|
||||
//! extern crate nb;
|
||||
//!
|
||||
//! /// A synchronized serial interface
|
||||
//! // NOTE This is a global singleton
|
||||
//! pub struct Serial1;
|
||||
//!
|
||||
//! // NOTE private
|
||||
//! static USART1: Mutex<_> = Mutex::new(..);
|
||||
//!
|
||||
//! impl hal::serial::Read<u8> for Serial1 {
|
||||
//! type Error = !;
|
||||
//!
|
||||
//! fn read(&mut self) -> Result<u8, nb::Error<Self::Error>> {
|
||||
//! hal::serial::Read::read(&Serial(&*USART1.lock()))
|
||||
//! }
|
||||
//! }
|
||||
//! # fn main() {}
|
||||
//! ```
|
||||
//!
|
||||
//! ## Intended usage
|
||||
@@ -215,32 +189,45 @@
|
||||
//! with `futures` or with the `await` operator using the [`block!`],
|
||||
//! [`try_nb!`] and [`await!`] macros respectively.
|
||||
//!
|
||||
//! [`block!`]: https://japaric.github.io/nb/nb/macro.block.html
|
||||
//! [`try_nb!`]: https://japaric.github.io/nb/nb/macro.try_nb.html
|
||||
//! [`await!`]: https://japaric.github.io/nb/nb/macro.await.html
|
||||
//! [`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:
|
||||
//!
|
||||
//! ``` ignore
|
||||
//! extern crate embedded_hal as hal;
|
||||
//!
|
||||
//! #[macro_use]
|
||||
//! ```
|
||||
//! # #![feature(never_type)]
|
||||
//! extern crate embedded_hal;
|
||||
//! #[macro_use(block)]
|
||||
//! extern crate nb;
|
||||
//!
|
||||
//! extern crate stm32f103xx_hal_impl;
|
||||
//! use stm32f30x_hal::Serial1;
|
||||
//! use embedded_hal::serial::Write;
|
||||
//!
|
||||
//! use stm32f103xx_hal_impl::Serial;
|
||||
//!
|
||||
//! let serial = Serial(usart1);
|
||||
//! # 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()).unwrap();
|
||||
//! block!(serial.write(*byte)).unwrap();
|
||||
//! }
|
||||
//! # }
|
||||
//!
|
||||
//! # mod stm32f30x_hal {
|
||||
//! # pub struct Serial1;
|
||||
//! # impl Serial1 {
|
||||
//! # pub fn write(&mut self, _: u8) -> ::nb::Result<(), !> {
|
||||
//! # Ok(())
|
||||
//! # }
|
||||
//! # }
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! ### `futures`
|
||||
@@ -248,13 +235,14 @@
|
||||
//! An example of running two tasks concurrently. First task: blink an LED every
|
||||
//! second. Second task: loop back data over the serial interface.
|
||||
//!
|
||||
//! ``` ignore
|
||||
//! ```
|
||||
//! #![feature(conservative_impl_trait)]
|
||||
//! #![feature(never_type)]
|
||||
//!
|
||||
//! extern crate embedded_hal as hal;
|
||||
//!
|
||||
//! extern crate futures;
|
||||
//! extern crate stm32f103xx_hal_impl;
|
||||
//!
|
||||
//! #[macro_use]
|
||||
//! #[macro_use(try_nb)]
|
||||
//! extern crate nb;
|
||||
//!
|
||||
//! use hal::prelude::*;
|
||||
@@ -264,139 +252,216 @@
|
||||
//! Future,
|
||||
//! };
|
||||
//! use futures::future::Loop;
|
||||
//! use stm32f103xx_hal_impl::{Serial, Timer};
|
||||
//! use stm32f30x_hal::{Led, Serial1, Timer6};
|
||||
//!
|
||||
//! /// `futures` version of `Timer.wait`
|
||||
//! ///
|
||||
//! /// This returns a future that must be polled to completion
|
||||
//! fn wait<T>(timer: T) -> impl Future<Item = T, Error = !>
|
||||
//! fn wait<T>(mut timer: T) -> impl Future<Item = T, Error = !>
|
||||
//! where
|
||||
//! T: hal::Timer,
|
||||
//! {
|
||||
//! future::loop_fn(timer, |timer| {
|
||||
//! match timer.wait() {
|
||||
//! Ok(()) => Ok(Loop::Break(timer)),
|
||||
//! Err(nb::Error::WouldBlock) => Ok(Loop::Continue(timer)),
|
||||
//! let mut timer = Some(timer);
|
||||
//! future::poll_fn(move || {
|
||||
//! if let Some(ref mut timer) = timer {
|
||||
//! try_nb!(timer.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>(serial: S) -> impl Future<Item = (S, u8), Error = S::Error>
|
||||
//! fn read<S>(mut serial: S) -> impl Future<Item = (S, u8), Error = S::Error>
|
||||
//! where
|
||||
//! S: hal::serial::Read<u8>,
|
||||
//! {
|
||||
//! future::loop_fn(serial, |mut serial| {
|
||||
//! match serial.read() {
|
||||
//! Ok(byte) => Ok(Loop::Break((serial, byte))),
|
||||
//! Err(nb::Error::WouldBlock) => Ok(Loop::Continue(serial)),
|
||||
//! Err(nb::Error::Other(error)) => Err(error),
|
||||
//! }
|
||||
//! 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>(serial: S, byte: u8) -> impl Future<Item = S, Error = S::Error>
|
||||
//! fn write<S>(mut serial: S, byte: u8) -> impl Future<Item = S, Error = S::Error>
|
||||
//! where
|
||||
//! S: hal::serial::Write<u8>,
|
||||
//! {
|
||||
//! future::loop_fn(serial, move |mut serial| {
|
||||
//! match serial.write(byte) {
|
||||
//! Ok(()) => Ok(Loop::Break(serial)),
|
||||
//! Err(nb::Error::WouldBlock) => Ok(Loop::Continue(serial)),
|
||||
//! Err(nb::Error::Other(error)) => Err(error),
|
||||
//! }
|
||||
//! 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 = Timer(tim3);
|
||||
//! let serial = Serial(usart1);
|
||||
//! let timer: Timer6 = {
|
||||
//! // ..
|
||||
//! # Timer6
|
||||
//! };
|
||||
//! let serial: Serial1 = {
|
||||
//! // ..
|
||||
//! # Serial1
|
||||
//! };
|
||||
//! let led: Led = {
|
||||
//! // ..
|
||||
//! # Led
|
||||
//! };
|
||||
//!
|
||||
//! // Tasks
|
||||
//! let mut blinky = future::loop_fn(true, |state| {
|
||||
//! wait(timer).map(|_| {
|
||||
//! if state {
|
||||
//! Led.on();
|
||||
//! } else {
|
||||
//! Led.off();
|
||||
//! }
|
||||
//! 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(!state)
|
||||
//! Loop::Continue((led, timer, !state))
|
||||
//! })
|
||||
//! });
|
||||
//! });
|
||||
//!
|
||||
//! let mut loopback = future::loop_fn((), |_| {
|
||||
//! read(serial).and_then(|byte| {
|
||||
//! let mut loopback = future::loop_fn::<_, (), _, _>(serial, |mut serial| {
|
||||
//! read(serial).and_then(|(serial, byte)| {
|
||||
//! write(serial, byte)
|
||||
//! }).map(|_| {
|
||||
//! Loop::Continue(())
|
||||
//! });
|
||||
//! }).map(|serial| {
|
||||
//! Loop::Continue(serial)
|
||||
//! })
|
||||
//! });
|
||||
//!
|
||||
//! // Event loop
|
||||
//! loop {
|
||||
//! blinky().poll().unwrap(); // NOTE(unwrap) E = !
|
||||
//! loopback().poll().unwrap();
|
||||
//! blinky.poll().unwrap(); // NOTE(unwrap) E = !
|
||||
//! loopback.poll().unwrap();
|
||||
//! break;
|
||||
//! }
|
||||
//! # }
|
||||
//!
|
||||
//! # mod stm32f30x_hal {
|
||||
//! # pub struct Timer6;
|
||||
//! # impl ::hal::Timer for Timer6 {
|
||||
//! # type Time = ();
|
||||
//! #
|
||||
//! # fn get_timeout(&self) {}
|
||||
//! # fn pause(&mut self) {}
|
||||
//! # fn restart(&mut self) {}
|
||||
//! # fn resume(&mut self) {}
|
||||
//! # fn set_timeout<T>(&mut self, _: T) where T: Into<()> {}
|
||||
//! # fn wait(&mut self) -> ::nb::Result<(), !> { Err(::nb::Error::WouldBlock) }
|
||||
//! # }
|
||||
//! #
|
||||
//! # pub struct Serial1;
|
||||
//! # impl ::hal::serial::Read<u8> for Serial1 {
|
||||
//! # type Error = !;
|
||||
//! # fn read(&mut self) -> ::nb::Result<u8, !> { Err(::nb::Error::WouldBlock) }
|
||||
//! # }
|
||||
//! # impl ::hal::serial::Write<u8> for Serial1 {
|
||||
//! # type Error = !;
|
||||
//! # fn flush(&mut self) -> ::nb::Result<(), !> { Err(::nb::Error::WouldBlock) }
|
||||
//! # fn write(&mut self, _: u8) -> ::nb::Result<(), !> { 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`.
|
||||
//!
|
||||
//! **NOTE** The `await!` macro requires language support for generators, which
|
||||
//! is not yet in the compiler.
|
||||
//! ```
|
||||
//! #![feature(generator_trait)]
|
||||
//! #![feature(generators)]
|
||||
//! # #![feature(never_type)]
|
||||
//!
|
||||
//! ``` ignore
|
||||
//! extern crate embedded_hal as hal;
|
||||
//!
|
||||
//! extern crate stm32f103xx_hal_impl;
|
||||
//!
|
||||
//! #[macro_use]
|
||||
//! #[macro_use(await)]
|
||||
//! extern crate nb;
|
||||
//!
|
||||
//! use std::ops::Generator;
|
||||
//!
|
||||
//! use hal::prelude::*;
|
||||
//! use stm32f103xx_hal_impl::{Serial, Timer};
|
||||
//! use stm32f30x_hal::{Led, Serial1, Timer6};
|
||||
//!
|
||||
//! // HAL implementers
|
||||
//! let timer = Timer(tim3);
|
||||
//! let serial = Serial(usart1);
|
||||
//! fn main() {
|
||||
//! // HAL implementers
|
||||
//! let mut timer: Timer6 = {
|
||||
//! // ..
|
||||
//! # Timer6
|
||||
//! };
|
||||
//! let mut serial: Serial1 = {
|
||||
//! // ..
|
||||
//! # Serial1
|
||||
//! };
|
||||
//! let mut led: Led = {
|
||||
//! // ..
|
||||
//! # Led
|
||||
//! };
|
||||
//!
|
||||
//! // Tasks
|
||||
//! let mut blinky = (|| {
|
||||
//! let mut state = false;
|
||||
//! loop {
|
||||
//! // `await!` means "suspend / yield here" instead of "block until
|
||||
//! // completion"
|
||||
//! await!(timer.wait()).unwrap(); // NOTE(unwrap) E = !
|
||||
//! // 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 = !
|
||||
//!
|
||||
//! state = !state;
|
||||
//! state = !state;
|
||||
//!
|
||||
//! if state {
|
||||
//! Led.on();
|
||||
//! } else {
|
||||
//! Led.off();
|
||||
//! if state {
|
||||
//! led.on();
|
||||
//! } else {
|
||||
//! led.off();
|
||||
//! }
|
||||
//! }
|
||||
//! }
|
||||
//! })();
|
||||
//! });
|
||||
//!
|
||||
//! let mut loopback = (|| {
|
||||
//! let mut loopback = (move || {
|
||||
//! loop {
|
||||
//! let byte = await!(serial.read()).unwrap();
|
||||
//! await!(serial.write(byte)).unwrap();
|
||||
//! }
|
||||
//! });
|
||||
//!
|
||||
//! // Event loop
|
||||
//! loop {
|
||||
//! let byte = await!(serial.read()).unwrap();
|
||||
//! await!(serial.write(byte)).unwrap();
|
||||
//! blinky.resume();
|
||||
//! loopback.resume();
|
||||
//! # break;
|
||||
//! }
|
||||
//! })();
|
||||
//!
|
||||
//! // Event loop
|
||||
//! loop {
|
||||
//! blinky.resume();
|
||||
//! serial.resume();
|
||||
//! }
|
||||
//!
|
||||
//! # mod stm32f30x_hal {
|
||||
//! # pub struct Serial1;
|
||||
//! # impl Serial1 {
|
||||
//! # pub fn read(&mut self) -> ::nb::Result<u8, !> { Err(::nb::Error::WouldBlock) }
|
||||
//! # pub fn write(&mut self, _: u8) -> ::nb::Result<(), !> { Err(::nb::Error::WouldBlock) }
|
||||
//! # }
|
||||
//! # pub struct Timer6;
|
||||
//! # impl Timer6 {
|
||||
//! # pub fn wait(&mut self) -> ::nb::Result<(), !> { 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
|
||||
@@ -410,11 +475,11 @@
|
||||
//! methods with default implementation to allow specialization, but they have
|
||||
//! been written as functions to keep things simple.
|
||||
//!
|
||||
//! - Write a whole buffer in blocking fashion.
|
||||
//! - Write a whole buffer in blocking a fashion.
|
||||
//!
|
||||
//! ``` ignore
|
||||
//! ```
|
||||
//! extern crate embedded_hal as hal;
|
||||
//! #[macro_use]
|
||||
//! #[macro_use(block)]
|
||||
//! extern crate nb;
|
||||
//!
|
||||
//! use hal::prelude::*;
|
||||
@@ -429,11 +494,13 @@
|
||||
//!
|
||||
//! Ok(())
|
||||
//! }
|
||||
//!
|
||||
//! # fn main() {}
|
||||
//! ```
|
||||
//!
|
||||
//! - Blocking read with timeout
|
||||
//!
|
||||
//! ``` ignore
|
||||
//! ```
|
||||
//! extern crate embedded_hal as hal;
|
||||
//! extern crate nb;
|
||||
//!
|
||||
@@ -461,9 +528,10 @@
|
||||
//!
|
||||
//! loop {
|
||||
//! match serial.read() {
|
||||
//! // raise error
|
||||
//! Err(nb::Error::Other(e)) => return Err(Error::Serial(e)),
|
||||
//! Err(nb::Error::WouldBlock) => {
|
||||
//! // no data available, check the timer below
|
||||
//! // no data available yet, check the timer below
|
||||
//! },
|
||||
//! Ok(byte) => return Ok(byte),
|
||||
//! }
|
||||
@@ -473,61 +541,74 @@
|
||||
//! // 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.
|
||||
//! e
|
||||
//! unreachable!()
|
||||
//! },
|
||||
//! // no timeout yet, try again
|
||||
//! Err(nb::Error::WouldBlock) => continue,
|
||||
//! Ok(()) => {
|
||||
//! timer.pause();
|
||||
//! return Err(Error::TimedOut);
|
||||
//! },
|
||||
//! Ok(()) => return Err(Error::TimedOut),
|
||||
//! }
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! # fn main() {}
|
||||
//! ```
|
||||
//!
|
||||
//! - Asynchronous SPI transfer
|
||||
//!
|
||||
//! ``` ignore
|
||||
//! ```
|
||||
//! #![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
|
||||
//! #[async]
|
||||
//! fn transfer<const N: usize, S>(
|
||||
//! spi: &S,
|
||||
//! mut buffer: [u8; N],
|
||||
//! ) -> Result<[u8; N], S::Error>
|
||||
//! 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,
|
||||
//! S: hal::spi::FullDuplex<u8>,
|
||||
//! {
|
||||
//! for byte in &mut buffer {
|
||||
//! await!(spi.send(byte))?;
|
||||
//! *byte = await!(spi.receive())?;
|
||||
//! }
|
||||
//! move || {
|
||||
//! let n = buffer.len();
|
||||
//! for i in 0..n {
|
||||
//! await!(spi.send(buffer[i]))?;
|
||||
//! buffer[i] = await!(spi.read())?;
|
||||
//! }
|
||||
//!
|
||||
//! buffer
|
||||
//! Ok((spi, buffer))
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! # fn main() {}
|
||||
//! ```
|
||||
//!
|
||||
//! - Buffered serial interface with periodic flushing in interrupt handler
|
||||
//!
|
||||
//! ``` ignore
|
||||
//! ```
|
||||
//! # #![feature(never_type)]
|
||||
//! extern crate embedded_hal as hal;
|
||||
//! extern crate nb;
|
||||
//!
|
||||
//! use hal::prelude::*;
|
||||
//!
|
||||
//! fn flush<S>(serial: &mut S, cb: &mut CircularBuffer) -> Result<(), S::Error>
|
||||
//! fn flush<S>(serial: &mut S, cb: &mut CircularBuffer)
|
||||
//! where
|
||||
//! S: hal::serial::Write<u8>,
|
||||
//! S: hal::serial::Write<u8, Error = !>,
|
||||
//! {
|
||||
//! loop {
|
||||
//! if let Some(byte) = cb.peek() {
|
||||
//! match serial.write(*byte) {
|
||||
//! Err(nb::Error::Other(e)) => return Err(e),
|
||||
//! Err(nb::Error::WouldBlock) => return Ok(()),
|
||||
//! Err(nb::Error::Other(_)) => unreachable!(),
|
||||
//! Err(nb::Error::WouldBlock) => return,
|
||||
//! Ok(()) => {}, // keep flushing data
|
||||
//! }
|
||||
//! }
|
||||
@@ -539,70 +620,66 @@
|
||||
//! // The stuff below could be in some other crate
|
||||
//!
|
||||
//! /// Global singleton
|
||||
//! pub struct BufferedSerial;
|
||||
//! pub struct BufferedSerial1;
|
||||
//!
|
||||
//! // NOTE private
|
||||
//! static BUFFER: Mutex<CircularBuffer> = ..;
|
||||
//! static SERIAL: Mutex<impl hal::serial::Write<u8>> = ..;
|
||||
//! static BUFFER1: Mutex<CircularBuffer> = {
|
||||
//! // ..
|
||||
//! # Mutex(CircularBuffer)
|
||||
//! };
|
||||
//! static SERIAL1: Mutex<Serial1> = {
|
||||
//! // ..
|
||||
//! # Mutex(Serial1)
|
||||
//! };
|
||||
//!
|
||||
//! impl BufferedSerial {
|
||||
//! pub fn write(&self, bytes: &[u8]) {
|
||||
//! let mut buffer = BUFFER.lock();
|
||||
//! for byte in bytes {
|
||||
//! buffer.push(*byte).unwrap();
|
||||
//! }
|
||||
//! impl BufferedSerial1 {
|
||||
//! pub fn write(&self, byte: u8) {
|
||||
//! self.write_all(&[byte])
|
||||
//! }
|
||||
//!
|
||||
//! pub fn write_all(&self, bytes: &[u8]) {
|
||||
//! let mut buffer = BUFFER.lock();
|
||||
//! let mut buffer = BUFFER1.lock();
|
||||
//! for byte in bytes {
|
||||
//! buffer.push(*byte).unwrap();
|
||||
//! buffer.push(*byte).expect("buffer overrun");
|
||||
//! }
|
||||
//! // omitted: pend / enable interrupt_handler
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! fn interrupt_handler() {
|
||||
//! let serial = SERIAL.lock();
|
||||
//! let buffer = BUFFER.lock();
|
||||
//! let mut serial = SERIAL1.lock();
|
||||
//! let mut buffer = BUFFER1.lock();
|
||||
//!
|
||||
//! flush(&mut serial, &mut buffer).unwrap();
|
||||
//! 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 = !;
|
||||
//! # fn write(&mut self, _: u8) -> nb::Result<(), !> { Err(::nb::Error::WouldBlock) }
|
||||
//! # fn flush(&mut self) -> nb::Result<(), !> { 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() {}
|
||||
//! ```
|
||||
//!
|
||||
//! # A note on time units
|
||||
//!
|
||||
//! Implementers of this HAL are encouraged to use *application agnostic* time
|
||||
//! units in their implementations. Is it usually the case that the application
|
||||
//! will want to pick the clock frequencies of the different peripherals so
|
||||
//! using a unit like `apb1::Ticks` instead of `Seconds` lets the application
|
||||
//! perform the conversions cheaply without register / memory accesses.
|
||||
//!
|
||||
//! For example: a `Timer` implementation uses the peripheral TIM1 which is
|
||||
//! connected to the APB1 bus. This implementation uses the time unit
|
||||
//! `apb1::Ticks` where 1 tick is equal to `1 / apb1::FREQUENCY` seconds where
|
||||
//! `apb1::FREQUENCY` is picked by the application.
|
||||
//!
|
||||
//! Now each application can declare the `apb1::FREQUENCY` using a macro that
|
||||
//! expands into conversions (implementations of [the `From` trait]) from human
|
||||
//! time units like `Milliseconds` to `apb1::Ticks`.
|
||||
//!
|
||||
//! [the `From` trait]: https://doc.rust-lang.org/core/convert/trait.From.html
|
||||
//!
|
||||
//! With this setup the application can use human time units with the `Timer`
|
||||
//! API:
|
||||
//!
|
||||
//! ``` ignore
|
||||
//! frequency!(apb1, 8_000_000); // Hz
|
||||
//!
|
||||
//! let timer: impl Timer = ..;
|
||||
//!
|
||||
//! // All these are equivalent
|
||||
//! timer.set_timeout(apb1::Ticks(8_000));
|
||||
//! timer.set_timeout(Milliseconds(1));
|
||||
//! timer.set_timeout(1.ms());
|
||||
//! ```
|
||||
//!
|
||||
//! See the [`blue-pill`] crate for an example implementation of this approach.
|
||||
|
||||
#![deny(missing_docs)]
|
||||
#![deny(warnings)]
|
||||
|
||||
Reference in New Issue
Block a user