| 1 | //! Non-blocking Hardware Abstraction Layer (HAL) traits for embedded systems, using the `nb` crate. |
| 2 | //! |
| 3 | //! The `embedded-hal-nb` traits make use of the |
| 4 | //! [`nb`][] crate (*please go read that crate documentation before continuing*) to abstract over |
| 5 | //! the asynchronous model and to also provide a blocking operation mode. |
| 6 | //! |
| 7 | //! [`nb`]: https://crates.io/crates/nb |
| 8 | //! |
| 9 | //! Here's how a HAL trait may look like: |
| 10 | //! |
| 11 | //! ``` |
| 12 | //! use embedded_hal_nb; |
| 13 | //! |
| 14 | //! /// A serial interface |
| 15 | //! pub trait Serial { |
| 16 | //! /// Error type associated to this serial interface |
| 17 | //! type Error: core::fmt::Debug; |
| 18 | //! |
| 19 | //! /// Reads a single byte |
| 20 | //! fn read(&mut self) -> nb::Result<u8, Self::Error>; |
| 21 | //! |
| 22 | //! /// Writes a single byte |
| 23 | //! fn write(&mut self, byte: u8) -> nb::Result<(), Self::Error>; |
| 24 | //! } |
| 25 | //! ``` |
| 26 | //! |
| 27 | //! The `nb::Result` enum is used to add a [`WouldBlock`] variant to the errors |
| 28 | //! of the serial interface. As explained in the documentation of the `nb` crate this single API, |
| 29 | //! when paired with the macros in the `nb` crate, can operate in a blocking manner, or be adapted |
| 30 | //! to other asynchronous execution schemes. |
| 31 | //! |
| 32 | //! [`WouldBlock`]: https://docs.rs/nb/1.0.0/nb/enum.Error.html |
| 33 | //! |
| 34 | //! Some traits, like the one shown below, may expose possibly blocking APIs that can't fail. In |
| 35 | //! those cases `nb::Result<_, Infallible>` is used. |
| 36 | //! |
| 37 | //! ``` |
| 38 | //! # use std as core; |
| 39 | //! use ::core::convert::Infallible; |
| 40 | //! |
| 41 | //! /// A count down timer |
| 42 | //! pub trait CountDown { |
| 43 | //! // .. |
| 44 | //! |
| 45 | //! /// "waits" until the count down is over |
| 46 | //! fn wait(&mut self) -> nb::Result<(), Infallible>; |
| 47 | //! } |
| 48 | //! |
| 49 | //! # fn main() {} |
| 50 | //! ``` |
| 51 | //! |
| 52 | //! ## Suggested implementation |
| 53 | //! |
| 54 | //! The HAL traits should be implemented for device crates generated via [`svd2rust`] to maximize |
| 55 | //! code reuse. |
| 56 | //! |
| 57 | //! [`svd2rust`]: https://crates.io/crates/svd2rust |
| 58 | //! |
| 59 | //! Shown below is an implementation of some of the HAL traits for the [`stm32f1xx-hal`] crate. This |
| 60 | //! single implementation will work for *any* microcontroller in the `STM32F1xx` family. |
| 61 | //! |
| 62 | //! [`stm32f1`]: https://crates.io/crates/stm32f1 |
| 63 | //! |
| 64 | //! ```no_run |
| 65 | //! // crate: stm32f1xx-hal |
| 66 | //! // An implementation of the `embedded-hal` traits for STM32F1xx microcontrollers |
| 67 | //! |
| 68 | //! use embedded_hal_nb::serial; |
| 69 | //! use nb; |
| 70 | //! |
| 71 | //! // device crate |
| 72 | //! use stm32f1::stm32f103::USART1; |
| 73 | //! |
| 74 | //! /// A serial interface |
| 75 | //! // NOTE generic over the USART peripheral |
| 76 | //! pub struct Serial<USART> { usart: USART } |
| 77 | //! |
| 78 | //! // convenience type alias |
| 79 | //! pub type Serial1 = Serial<USART1>; |
| 80 | //! |
| 81 | //! impl serial::ErrorType for Serial<USART1> { |
| 82 | //! type Error = serial::ErrorKind; |
| 83 | //! } |
| 84 | //! |
| 85 | //! impl embedded_hal_nb::serial::Read<u8> for Serial<USART1> { |
| 86 | //! fn read(&mut self) -> nb::Result<u8, Self::Error> { |
| 87 | //! // read the status register |
| 88 | //! let isr = self.usart.sr.read(); |
| 89 | //! |
| 90 | //! if isr.ore().bit_is_set() { |
| 91 | //! // Error: Buffer overrun |
| 92 | //! Err(nb::Error::Other(Self::Error::Overrun)) |
| 93 | //! } |
| 94 | //! // omitted: checks for other errors |
| 95 | //! else if isr.rxne().bit_is_set() { |
| 96 | //! // Data available: read the data register |
| 97 | //! Ok(self.usart.dr.read().bits() as u8) |
| 98 | //! } else { |
| 99 | //! // No data available yet |
| 100 | //! Err(nb::Error::WouldBlock) |
| 101 | //! } |
| 102 | //! } |
| 103 | //! } |
| 104 | //! |
| 105 | //! impl embedded_hal_nb::serial::Write<u8> for Serial<USART1> { |
| 106 | //! fn write(&mut self, byte: u8) -> nb::Result<(), Self::Error> { |
| 107 | //! // Similar to the `read` implementation |
| 108 | //! # Ok(()) |
| 109 | //! } |
| 110 | //! |
| 111 | //! fn flush(&mut self) -> nb::Result<(), Self::Error> { |
| 112 | //! // Similar to the `read` implementation |
| 113 | //! # Ok(()) |
| 114 | //! } |
| 115 | //! } |
| 116 | //! |
| 117 | //! # fn main() {} |
| 118 | //! ``` |
| 119 | //! |
| 120 | //! ## Intended usage |
| 121 | //! |
| 122 | //! Thanks to the [`nb`] crate the HAL API can be used in a blocking manner |
| 123 | //! with the [`block!`] macro or with `futures`. |
| 124 | //! |
| 125 | //! [`block!`]: https://docs.rs/nb/1.0.0/nb/macro.block.html |
| 126 | //! |
| 127 | //! ### Blocking mode |
| 128 | //! |
| 129 | //! An example of writing a string over the serial interface in a blocking |
| 130 | //! fashion: |
| 131 | //! |
| 132 | //! ``` |
| 133 | //! use stm32f1xx_hal::Serial1; |
| 134 | //! use embedded_hal_nb::serial::Write; |
| 135 | //! use nb::block; |
| 136 | //! |
| 137 | //! # fn main() { |
| 138 | //! let mut serial: Serial1 = { |
| 139 | //! // .. |
| 140 | //! # Serial1 |
| 141 | //! }; |
| 142 | //! |
| 143 | //! for byte in b"Hello, world!" { |
| 144 | //! // NOTE `block!` blocks until `serial.write()` completes and returns |
| 145 | //! // `Result<(), Error>` |
| 146 | //! block!(serial.write(*byte)).unwrap(); |
| 147 | //! } |
| 148 | //! # } |
| 149 | //! |
| 150 | //! # mod stm32f1xx_hal { |
| 151 | //! # use embedded_hal_nb; |
| 152 | //! # use core::convert::Infallible; |
| 153 | //! # pub struct Serial1; |
| 154 | //! # impl Serial1 { |
| 155 | //! # pub fn write(&mut self, _: u8) -> nb::Result<(), Infallible> { |
| 156 | //! # Ok(()) |
| 157 | //! # } |
| 158 | //! # } |
| 159 | //! # } |
| 160 | //! ``` |
| 161 | //! |
| 162 | //! ## Generic programming and higher level abstractions |
| 163 | //! |
| 164 | //! The core of the HAL has been kept minimal on purpose to encourage building **generic** higher |
| 165 | //! level abstractions on top of it. Some higher level abstractions that pick an asynchronous model |
| 166 | //! or that have blocking behavior and that are deemed useful to build other abstractions can be |
| 167 | //! found in the `blocking` module. |
| 168 | //! |
| 169 | //! Some examples: |
| 170 | //! |
| 171 | //! **NOTE** All the functions shown below could have been written as trait |
| 172 | //! methods with default implementation to allow specialization, but they have |
| 173 | //! been written as functions to keep things simple. |
| 174 | //! |
| 175 | //! - Write a whole buffer to a serial device in blocking a fashion. |
| 176 | //! |
| 177 | //! ``` |
| 178 | //! use embedded_hal_nb::serial::Write; |
| 179 | //! use nb::block; |
| 180 | //! |
| 181 | //! fn write_all<S>(serial: &mut S, buffer: &[u8]) -> Result<(), S::Error> |
| 182 | //! where |
| 183 | //! S: Write<u8> |
| 184 | //! { |
| 185 | //! for &byte in buffer { |
| 186 | //! block!(serial.write(byte))?; |
| 187 | //! } |
| 188 | //! |
| 189 | //! Ok(()) |
| 190 | //! } |
| 191 | //! |
| 192 | //! # fn main() {} |
| 193 | //! ``` |
| 194 | //! |
| 195 | //! - Buffered serial interface with periodic flushing in interrupt handler |
| 196 | //! |
| 197 | //! ``` |
| 198 | //! # use std as core; |
| 199 | //! use embedded_hal_nb::serial::{ErrorKind, Write}; |
| 200 | //! use nb::block; |
| 201 | //! |
| 202 | //! fn flush<S>(serial: &mut S, cb: &mut CircularBuffer) |
| 203 | //! where |
| 204 | //! S: Write<u8, Error = ErrorKind>, |
| 205 | //! { |
| 206 | //! loop { |
| 207 | //! if let Some(byte) = cb.peek() { |
| 208 | //! match serial.write(*byte) { |
| 209 | //! Err(nb::Error::Other(_)) => unreachable!(), |
| 210 | //! Err(nb::Error::WouldBlock) => return, |
| 211 | //! Ok(()) => {}, // keep flushing data |
| 212 | //! } |
| 213 | //! } |
| 214 | //! |
| 215 | //! cb.pop(); |
| 216 | //! } |
| 217 | //! } |
| 218 | //! |
| 219 | //! // The stuff below could be in some other crate |
| 220 | //! |
| 221 | //! /// Global singleton |
| 222 | //! pub struct BufferedSerial1; |
| 223 | //! |
| 224 | //! // NOTE private |
| 225 | //! static BUFFER1: Mutex<CircularBuffer> = { |
| 226 | //! // .. |
| 227 | //! # Mutex(CircularBuffer) |
| 228 | //! }; |
| 229 | //! static SERIAL1: Mutex<Serial1> = { |
| 230 | //! // .. |
| 231 | //! # Mutex(Serial1) |
| 232 | //! }; |
| 233 | //! |
| 234 | //! impl BufferedSerial1 { |
| 235 | //! pub fn write(&self, byte: u8) { |
| 236 | //! self.write_all(&[byte]) |
| 237 | //! } |
| 238 | //! |
| 239 | //! pub fn write_all(&self, bytes: &[u8]) { |
| 240 | //! let mut buffer = BUFFER1.lock(); |
| 241 | //! for byte in bytes { |
| 242 | //! buffer.push(*byte).expect("buffer overrun" ); |
| 243 | //! } |
| 244 | //! // omitted: pend / enable interrupt_handler |
| 245 | //! } |
| 246 | //! } |
| 247 | //! |
| 248 | //! fn interrupt_handler() { |
| 249 | //! let mut serial = SERIAL1.lock(); |
| 250 | //! let mut buffer = BUFFER1.lock(); |
| 251 | //! |
| 252 | //! flush(&mut *serial, &mut buffer); |
| 253 | //! } |
| 254 | //! |
| 255 | //! # struct Mutex<T>(T); |
| 256 | //! # impl<T> Mutex<T> { |
| 257 | //! # fn lock(&self) -> RefMut<T> { unimplemented!() } |
| 258 | //! # } |
| 259 | //! # struct RefMut<'a, T>(&'a mut T) where T: 'a; |
| 260 | //! # impl<'a, T> ::core::ops::Deref for RefMut<'a, T> { |
| 261 | //! # type Target = T; |
| 262 | //! # fn deref(&self) -> &T { self.0 } |
| 263 | //! # } |
| 264 | //! # impl<'a, T> ::core::ops::DerefMut for RefMut<'a, T> { |
| 265 | //! # fn deref_mut(&mut self) -> &mut T { self.0 } |
| 266 | //! # } |
| 267 | //! # struct Serial1; |
| 268 | //! # impl embedded_hal_nb::serial::ErrorType for Serial1 { |
| 269 | //! # type Error = ErrorKind; |
| 270 | //! # } |
| 271 | //! # impl embedded_hal_nb::serial::Write<u8> for Serial1 { |
| 272 | //! # fn write(&mut self, _: u8) -> nb::Result<(), Self::Error> { Err(nb::Error::WouldBlock) } |
| 273 | //! # fn flush(&mut self) -> nb::Result<(), Self::Error> { Err(nb::Error::WouldBlock) } |
| 274 | //! # } |
| 275 | //! # struct CircularBuffer; |
| 276 | //! # impl CircularBuffer { |
| 277 | //! # pub fn peek(&mut self) -> Option<&u8> { None } |
| 278 | //! # pub fn pop(&mut self) -> Option<u8> { None } |
| 279 | //! # pub fn push(&mut self, _: u8) -> Result<(), ()> { Ok(()) } |
| 280 | //! # } |
| 281 | //! |
| 282 | //! # fn main() {} |
| 283 | //! ``` |
| 284 | |
| 285 | #![warn (missing_docs)] |
| 286 | #![no_std ] |
| 287 | |
| 288 | pub use nb; |
| 289 | |
| 290 | pub mod serial; |
| 291 | pub mod spi; |
| 292 | |