mirror of
https://github.com/rtic-rs/rtic.git
synced 2024-11-29 23:14:34 +01:00
530 lines
32 KiB
HTML
530 lines
32 KiB
HTML
|
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="A Hardware Abstraction Layer (HAL) for embedded systems"><title>embedded_hal - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-46f98efaafac5295.ttf.woff2,FiraSans-Regular-018c141bf0843ffd.woff2,FiraSans-Medium-8f9a781e4970d388.woff2,SourceCodePro-Regular-562dcc5011b6de7d.ttf.woff2,SourceCodePro-Semibold-d899c5a5c4aeb14a.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2" crossorigin href="../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../static.files/normalize-76eba96aa4d2e634.css"><link rel="stylesheet" href="../static.files/rustdoc-492a78a4a87dcc01.css"><meta name="rustdoc-vars" data-root-path="../" data-static-root-path="../static.files/" data-current-crate="embedded_hal" data-themes="" data-resource-suffix="" data-rustdoc-version="1.82.0 (f6e511eec 2024-10-15)" data-channel="1.82.0" data-search-js="search-a99f1315e7cc5121.js" data-settings-js="settings-4313503d2e1961c2.js" ><script src="../static.files/storage-118b08c4c78b968e.js"></script><script defer src="../crates.js"></script><script defer src="../static.files/main-921df33f47b8780c.js"></script><noscript><link rel="stylesheet" href="../static.files/noscript-3b12f09e550e0385.css"></noscript><link rel="alternate icon" type="image/png" href="../static.files/favicon-32x32-422f7d1d52889060.png"><link rel="icon" type="image/svg+xml" href="../static.files/favicon-2c020d218678b618.svg"></head><body class="rustdoc mod crate"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="mobile-topbar"><button class="sidebar-menu-toggle" title="show sidebar"></button></nav><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../embedded_hal/index.html">embedded_<wbr>hal</a><span class="version">0.2.7</span></h2></div><div class="sidebar-elems"><ul class="block"><li><a id="all-types" href="all.html">All Items</a></li></ul><section><ul class="block"><li><a href="#modules">Modules</a></li><li><a href="#traits">Traits</a></li></ul></section></div></nav><div class="sidebar-resizer"></div><main><div class="width-limiter"><rustdoc-search></rustdoc-search><section id="main-content" class="content"><div class="main-heading"><h1>Crate <a class="mod" href="#">embedded_hal</a><button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><span class="out-of-band"><a class="src" href="../src/embedded_hal/lib.rs.html#1-999">source</a> · <button id="toggle-all-docs" title="collapse all docs">[<span>−</span>]</button></span></div><details class="toggle top-doc" open><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><p>A Hardware Abstraction Layer (HAL) for embedded systems</p>
|
|||
|
<p><strong>NOTE</strong> 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.</p>
|
|||
|
<h2 id="design-goals"><a class="doc-anchor" href="#design-goals">§</a>Design goals</h2>
|
|||
|
<p>The HAL</p>
|
|||
|
<ul>
|
|||
|
<li>
|
|||
|
<p>Must <em>erase</em> device specific details. Neither register, register blocks or magic values should
|
|||
|
appear in the API.</p>
|
|||
|
</li>
|
|||
|
<li>
|
|||
|
<p>Must be generic <em>within</em> a device and <em>across</em> 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.</p>
|
|||
|
</li>
|
|||
|
<li>
|
|||
|
<p>Where possible must <em>not</em> be tied to a specific asynchronous model. The API should be usable
|
|||
|
in blocking mode, with the <code>futures</code> model, with an async/await model or with a callback model.
|
|||
|
(cf. the <a href="https://crates.io/crates/nb"><code>nb</code></a> crate)</p>
|
|||
|
</li>
|
|||
|
<li>
|
|||
|
<p>Must be minimal, and thus easy to implement and zero cost, yet highly composable. People that
|
|||
|
want higher level abstraction should <em>prefer to use this HAL</em> rather than <em>re-implement</em>
|
|||
|
register manipulation code.</p>
|
|||
|
</li>
|
|||
|
<li>
|
|||
|
<p>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 <code>embedded-hal</code> 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 <code>embedded-hal</code> they can unlock all
|
|||
|
these drivers for their platform.</p>
|
|||
|
</li>
|
|||
|
</ul>
|
|||
|
<h2 id="out-of-scope"><a class="doc-anchor" href="#out-of-scope">§</a>Out of scope</h2>
|
|||
|
<ul>
|
|||
|
<li>Initialization and configuration stuff like “ensure this serial interface and that SPI
|
|||
|
interface are not using the same pins”. The HAL will focus on <em>doing I/O</em>.</li>
|
|||
|
</ul>
|
|||
|
<h2 id="reference-implementation"><a class="doc-anchor" href="#reference-implementation">§</a>Reference implementation</h2>
|
|||
|
<p>The <a href="https://crates.io/crates/stm32f30x-hal/0.1.0"><code>stm32f30x-hal</code></a> crate contains a reference implementation of this HAL.</p>
|
|||
|
<h2 id="platform-agnostic-drivers"><a class="doc-anchor" href="#platform-agnostic-drivers">§</a>Platform agnostic drivers</h2>
|
|||
|
<p>You can find platform agnostic drivers built on top of <code>embedded-hal</code> on crates.io by <a href="https://crates.io/keywords/embedded-hal">searching
|
|||
|
for the <em>embedded-hal</em> keyword</a>.</p>
|
|||
|
<p>If you writing a platform agnostic driver yourself you are highly encouraged to <a href="https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata">add the
|
|||
|
embedded-hal keyword</a>
|
|||
|
to your crate before publishing it!</p>
|
|||
|
<h2 id="detailed-design"><a class="doc-anchor" href="#detailed-design">§</a>Detailed design</h2><h3 id="traits"><a class="doc-anchor" href="#traits">§</a>Traits</h3>
|
|||
|
<p>The HAL is specified as traits to allow generic programming. These traits make use of the
|
|||
|
<a href="https://crates.io/crates/nb"><code>nb</code></a> crate (<em>please go read that crate documentation before continuing</em>) to abstract over
|
|||
|
the asynchronous model and to also provide a blocking operation mode.</p>
|
|||
|
<p>Here’s how a HAL trait may look like:</p>
|
|||
|
|
|||
|
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="kw">extern crate </span>nb;
|
|||
|
|
|||
|
<span class="doccomment">/// A serial interface
|
|||
|
</span><span class="kw">pub trait </span>Serial {
|
|||
|
<span class="doccomment">/// Error type associated to this serial interface
|
|||
|
</span><span class="kw">type </span>Error;
|
|||
|
|
|||
|
<span class="doccomment">/// Reads a single byte
|
|||
|
</span><span class="kw">fn </span>read(<span class="kw-2">&mut </span><span class="self">self</span>) -> nb::Result<u8, <span class="self">Self</span>::Error>;
|
|||
|
|
|||
|
<span class="doccomment">/// Writes a single byte
|
|||
|
</span><span class="kw">fn </span>write(<span class="kw-2">&mut </span><span class="self">self</span>, byte: u8) -> nb::Result<(), <span class="self">Self</span>::Error>;
|
|||
|
}</code></pre></div>
|
|||
|
<p>The <code>nb::Result</code> enum is used to add a <a href="https://docs.rs/nb/0.1.0/nb/enum.Error.html"><code>WouldBlock</code></a> variant to the errors
|
|||
|
of the serial interface. As explained in the documentation of the <code>nb</code> crate this single API,
|
|||
|
when paired with the macros in the <code>nb</code> crate, can operate in a blocking manner, or in a
|
|||
|
non-blocking manner compatible with <code>futures</code> and with the <code>await!</code> operator.</p>
|
|||
|
<p>Some traits, like the one shown below, may expose possibly blocking APIs that can’t fail. In
|
|||
|
those cases <code>nb::Result<_, Void></code> is used.</p>
|
|||
|
|
|||
|
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="kw">extern crate </span>nb;
|
|||
|
<span class="kw">extern crate </span>void;
|
|||
|
|
|||
|
<span class="kw">use </span>void::Void;
|
|||
|
|
|||
|
<span class="doccomment">/// A count down timer
|
|||
|
</span><span class="kw">pub trait </span>CountDown {
|
|||
|
<span class="comment">// ..
|
|||
|
|
|||
|
</span><span class="doccomment">/// "waits" until the count down is over
|
|||
|
</span><span class="kw">fn </span>wait(<span class="kw-2">&mut </span><span class="self">self</span>) -> nb::Result<(), Void>;
|
|||
|
}
|
|||
|
</code></pre></div>
|
|||
|
<h3 id="suggested-implementation"><a class="doc-anchor" href="#suggested-implementation">§</a>Suggested implementation</h3>
|
|||
|
<p>The HAL traits should be implemented for device crates generated via <a href="https://crates.io/crates/svd2rust"><code>svd2rust</code></a> to maximize
|
|||
|
code reuse.</p>
|
|||
|
<p>Shown below is an implementation of some of the HAL traits for the <a href="https://crates.io/crates/stm32f30x"><code>stm32f30x</code></a> crate. This
|
|||
|
single implementation will work for <em>any</em> microcontroller in the STM32F30x family.</p>
|
|||
|
|
|||
|
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="comment">// crate: stm32f30x-hal
|
|||
|
// An implementation of the `embedded-hal` traits for STM32F30x microcontrollers
|
|||
|
|
|||
|
</span><span class="kw">extern crate </span>embedded_hal <span class="kw">as </span>hal;
|
|||
|
<span class="kw">extern crate </span>nb;
|
|||
|
|
|||
|
<span class="comment">// device crate
|
|||
|
</span><span class="kw">extern crate </span>stm32f30x;
|
|||
|
|
|||
|
<span class="kw">use </span>stm32f30x::USART1;
|
|||
|
|
|||
|
<span class="doccomment">/// A serial interface
|
|||
|
</span><span class="comment">// NOTE generic over the USART peripheral
|
|||
|
</span><span class="kw">pub struct </span>Serial<USART> { usart: USART }
|
|||
|
|
|||
|
<span class="comment">// convenience type alias
|
|||
|
</span><span class="kw">pub type </span>Serial1 = Serial<USART1>;
|
|||
|
|
|||
|
<span class="doccomment">/// Serial interface error
|
|||
|
</span><span class="kw">pub enum </span>Error {
|
|||
|
<span class="doccomment">/// Buffer overrun
|
|||
|
</span>Overrun,
|
|||
|
<span class="comment">// omitted: other error variants
|
|||
|
</span>}
|
|||
|
|
|||
|
<span class="kw">impl </span>hal::serial::Read<u8> <span class="kw">for </span>Serial<USART1> {
|
|||
|
<span class="kw">type </span>Error = Error;
|
|||
|
|
|||
|
<span class="kw">fn </span>read(<span class="kw-2">&mut </span><span class="self">self</span>) -> nb::Result<u8, Error> {
|
|||
|
<span class="comment">// read the status register
|
|||
|
</span><span class="kw">let </span>isr = <span class="self">self</span>.usart.isr.read();
|
|||
|
|
|||
|
<span class="kw">if </span>isr.ore().bit_is_set() {
|
|||
|
<span class="comment">// Error: Buffer overrun
|
|||
|
</span><span class="prelude-val">Err</span>(nb::Error::Other(Error::Overrun))
|
|||
|
}
|
|||
|
<span class="comment">// omitted: checks for other errors
|
|||
|
</span><span class="kw">else if </span>isr.rxne().bit_is_set() {
|
|||
|
<span class="comment">// Data available: read the data register
|
|||
|
</span><span class="prelude-val">Ok</span>(<span class="self">self</span>.usart.rdr.read().bits() <span class="kw">as </span>u8)
|
|||
|
} <span class="kw">else </span>{
|
|||
|
<span class="comment">// No data available yet
|
|||
|
</span><span class="prelude-val">Err</span>(nb::Error::WouldBlock)
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
<span class="kw">impl </span>hal::serial::Write<u8> <span class="kw">for </span>Serial<USART1> {
|
|||
|
<span class="kw">type </span>Error = Error;
|
|||
|
|
|||
|
<span class="kw">fn </span>write(<span class="kw-2">&mut </span><span class="self">self</span>, byte: u8) -> nb::Result<(), Error> {
|
|||
|
<span class="comment">// Similar to the `read` implementation
|
|||
|
</span>}
|
|||
|
|
|||
|
<span class="kw">fn </span>flush(<span class="kw-2">&mut </span><span class="self">self</span>) -> nb::Result<(), Error> {
|
|||
|
<span class="comment">// Similar to the `read` implementation
|
|||
|
</span>}
|
|||
|
}
|
|||
|
</code></pre></div>
|
|||
|
<h3 id="intended-usage"><a class="doc-anchor" href="#intended-usage">§</a>Intended usage</h3>
|
|||
|
<p>Thanks to the <a href="https://crates.io/crates/nb"><code>nb</code></a> crate the HAL API can be used in a blocking manner,
|
|||
|
with <code>futures</code> or with the <code>await</code> operator using the <a href="https://docs.rs/nb/0.1.0/nb/macro.block.html"><code>block!</code></a>,
|
|||
|
<a href="https://docs.rs/nb/0.1.0/nb/index.html#how-to-use-this-crate"><code>try_nb!</code></a> and <a href="https://docs.rs/nb/0.1.0/nb/index.html#how-to-use-this-crate"><code>await!</code></a> macros respectively.</p>
|
|||
|
<h4 id="blocking-mode"><a class="doc-anchor" href="#blocking-mode">§</a>Blocking mode</h4>
|
|||
|
<p>An example of sending a string over the serial interface in a blocking
|
|||
|
fashion:</p>
|
|||
|
|
|||
|
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="kw">extern crate </span>embedded_hal;
|
|||
|
<span class="attr">#[macro_use(block)]
|
|||
|
</span><span class="kw">extern crate </span>nb;
|
|||
|
|
|||
|
<span class="kw">use </span>stm32f30x_hal::Serial1;
|
|||
|
<span class="kw">use </span>embedded_hal::serial::Write;
|
|||
|
|
|||
|
<span class="kw">let </span><span class="kw-2">mut </span>serial: Serial1 = {
|
|||
|
<span class="comment">// ..
|
|||
|
</span>};
|
|||
|
|
|||
|
<span class="kw">for </span>byte <span class="kw">in </span><span class="string">b"Hello, world!" </span>{
|
|||
|
<span class="comment">// NOTE `block!` blocks until `serial.write()` completes and returns
|
|||
|
// `Result<(), Error>`
|
|||
|
</span><span class="macro">block!</span>(serial.write(<span class="kw-2">*</span>byte)).unwrap();
|
|||
|
}
|
|||
|
</code></pre></div>
|
|||
|
<h4 id="futures"><a class="doc-anchor" href="#futures">§</a><code>futures</code></h4>
|
|||
|
<p>An example of running two tasks concurrently. First task: blink an LED every
|
|||
|
second. Second task: loop back data over the serial interface.</p>
|
|||
|
|
|||
|
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="kw">extern crate </span>embedded_hal <span class="kw">as </span>hal;
|
|||
|
<span class="kw">extern crate </span>futures;
|
|||
|
<span class="kw">extern crate </span>void;
|
|||
|
|
|||
|
<span class="attr">#[macro_use(try_nb)]
|
|||
|
</span><span class="kw">extern crate </span>nb;
|
|||
|
|
|||
|
<span class="kw">use </span>hal::prelude::<span class="kw-2">*</span>;
|
|||
|
<span class="kw">use </span>futures::{
|
|||
|
future,
|
|||
|
Async,
|
|||
|
Future,
|
|||
|
};
|
|||
|
<span class="kw">use </span>futures::future::Loop;
|
|||
|
<span class="kw">use </span>stm32f30x_hal::{Led, Serial1, Timer6};
|
|||
|
<span class="kw">use </span>void::Void;
|
|||
|
|
|||
|
<span class="doccomment">/// `futures` version of `CountDown.wait`
|
|||
|
///
|
|||
|
/// This returns a future that must be polled to completion
|
|||
|
</span><span class="kw">fn </span>wait<T>(<span class="kw-2">mut </span>timer: T) -> <span class="kw">impl </span>Future<Item = T, Error = Void>
|
|||
|
<span class="kw">where
|
|||
|
</span>T: hal::timer::CountDown,
|
|||
|
{
|
|||
|
<span class="kw">let </span><span class="kw-2">mut </span>timer = <span class="prelude-val">Some</span>(timer);
|
|||
|
future::poll_fn(<span class="kw">move </span>|| {
|
|||
|
<span class="macro">try_nb!</span>(timer.as_mut().unwrap().wait());
|
|||
|
|
|||
|
<span class="prelude-val">Ok</span>(Async::Ready(timer.take().unwrap()))
|
|||
|
})
|
|||
|
}
|
|||
|
|
|||
|
<span class="doccomment">/// `futures` version of `Serial.read`
|
|||
|
///
|
|||
|
/// This returns a future that must be polled to completion
|
|||
|
</span><span class="kw">fn </span>read<S>(<span class="kw-2">mut </span>serial: S) -> <span class="kw">impl </span>Future<Item = (S, u8), Error = S::Error>
|
|||
|
<span class="kw">where
|
|||
|
</span>S: hal::serial::Read<u8>,
|
|||
|
{
|
|||
|
<span class="kw">let </span><span class="kw-2">mut </span>serial = <span class="prelude-val">Some</span>(serial);
|
|||
|
future::poll_fn(<span class="kw">move </span>|| {
|
|||
|
<span class="kw">let </span>byte = <span class="macro">try_nb!</span>(serial.as_mut().unwrap().read());
|
|||
|
|
|||
|
<span class="prelude-val">Ok</span>(Async::Ready((serial.take().unwrap(), byte)))
|
|||
|
})
|
|||
|
}
|
|||
|
|
|||
|
<span class="doccomment">/// `futures` version of `Serial.write`
|
|||
|
///
|
|||
|
/// This returns a future that must be polled to completion
|
|||
|
</span><span class="kw">fn </span>write<S>(<span class="kw-2">mut </span>serial: S, byte: u8) -> <span class="kw">impl </span>Future<Item = S, Error = S::Error>
|
|||
|
<span class="kw">where
|
|||
|
</span>S: hal::serial::Write<u8>,
|
|||
|
{
|
|||
|
<span class="kw">let </span><span class="kw-2">mut </span>serial = <span class="prelude-val">Some</span>(serial);
|
|||
|
future::poll_fn(<span class="kw">move </span>|| {
|
|||
|
<span class="macro">try_nb!</span>(serial.as_mut().unwrap().write(byte));
|
|||
|
|
|||
|
<span class="prelude-val">Ok</span>(Async::Ready(serial.take().unwrap()))
|
|||
|
})
|
|||
|
}
|
|||
|
|
|||
|
<span class="kw">fn </span>main() {
|
|||
|
<span class="comment">// HAL implementers
|
|||
|
</span><span class="kw">let </span>timer: Timer6 = {
|
|||
|
<span class="comment">// ..
|
|||
|
</span>};
|
|||
|
<span class="kw">let </span>serial: Serial1 = {
|
|||
|
<span class="comment">// ..
|
|||
|
</span>};
|
|||
|
<span class="kw">let </span>led: Led = {
|
|||
|
<span class="comment">// ..
|
|||
|
</span>};
|
|||
|
|
|||
|
<span class="comment">// Tasks
|
|||
|
</span><span class="kw">let </span><span class="kw-2">mut </span>blinky = future::loop_fn::<<span class="kw">_</span>, (), <span class="kw">_</span>, <span class="kw">_</span>>(
|
|||
|
(led, timer, <span class="bool-val">true</span>),
|
|||
|
|(<span class="kw-2">mut </span>led, <span class="kw-2">mut </span>timer, state)| {
|
|||
|
wait(timer).map(<span class="kw">move </span>|timer| {
|
|||
|
<span class="kw">if </span>state {
|
|||
|
led.on();
|
|||
|
} <span class="kw">else </span>{
|
|||
|
led.off();
|
|||
|
}
|
|||
|
|
|||
|
Loop::Continue((led, timer, !state))
|
|||
|
})
|
|||
|
});
|
|||
|
|
|||
|
<span class="kw">let </span><span class="kw-2">mut </span>loopback = future::loop_fn::<<span class="kw">_</span>, (), <span class="kw">_</span>, <span class="kw">_</span>>(serial, |<span class="kw-2">mut </span>serial| {
|
|||
|
read(serial).and_then(|(serial, byte)| {
|
|||
|
write(serial, byte)
|
|||
|
}).map(|serial| {
|
|||
|
Loop::Continue(serial)
|
|||
|
})
|
|||
|
});
|
|||
|
|
|||
|
<span class="comment">// Event loop
|
|||
|
</span><span class="kw">loop </span>{
|
|||
|
blinky.poll().unwrap(); <span class="comment">// NOTE(unwrap) E = Void
|
|||
|
</span>loopback.poll().unwrap();
|
|||
|
}
|
|||
|
}
|
|||
|
</code></pre></div>
|
|||
|
<h4 id="await"><a class="doc-anchor" href="#await">§</a><code>await</code></h4>
|
|||
|
<p>Same example as above but using <code>await!</code> instead of <code>futures</code>.</p>
|
|||
|
|
|||
|
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="attr">#![feature(generator_trait)]
|
|||
|
#![feature(generators)]
|
|||
|
|
|||
|
</span><span class="kw">extern crate </span>embedded_hal <span class="kw">as </span>hal;
|
|||
|
|
|||
|
<span class="attr">#[macro_use(<span class="kw">await</span>)]
|
|||
|
</span><span class="kw">extern crate </span>nb;
|
|||
|
|
|||
|
<span class="kw">use </span>std::ops::Generator;
|
|||
|
<span class="kw">use </span>std::pin::Pin;
|
|||
|
|
|||
|
<span class="kw">use </span>hal::prelude::<span class="kw-2">*</span>;
|
|||
|
<span class="kw">use </span>stm32f30x_hal::{Led, Serial1, Timer6};
|
|||
|
|
|||
|
<span class="kw">fn </span>main() {
|
|||
|
<span class="comment">// HAL implementers
|
|||
|
</span><span class="kw">let </span><span class="kw-2">mut </span>timer: Timer6 = {
|
|||
|
<span class="comment">// ..
|
|||
|
</span>};
|
|||
|
<span class="kw">let </span><span class="kw-2">mut </span>serial: Serial1 = {
|
|||
|
<span class="comment">// ..
|
|||
|
</span>};
|
|||
|
<span class="kw">let </span><span class="kw-2">mut </span>led: Led = {
|
|||
|
<span class="comment">// ..
|
|||
|
</span>};
|
|||
|
|
|||
|
<span class="comment">// Tasks
|
|||
|
</span><span class="kw">let </span><span class="kw-2">mut </span>blinky = (<span class="kw">move </span>|| {
|
|||
|
<span class="kw">let </span><span class="kw-2">mut </span>state = <span class="bool-val">false</span>;
|
|||
|
<span class="kw">loop </span>{
|
|||
|
<span class="comment">// `await!` means "suspend / yield here" instead of "block until
|
|||
|
// completion"
|
|||
|
</span><span class="macro">await!</span>(timer.wait()).unwrap(); <span class="comment">// NOTE(unwrap) E = Void
|
|||
|
|
|||
|
</span>state = !state;
|
|||
|
|
|||
|
<span class="kw">if </span>state {
|
|||
|
led.on();
|
|||
|
} <span class="kw">else </span>{
|
|||
|
led.off();
|
|||
|
}
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
<span class="kw">let </span><span class="kw-2">mut </span>loopback = (<span class="kw">move </span>|| {
|
|||
|
<span class="kw">loop </span>{
|
|||
|
<span class="kw">let </span>byte = <span class="macro">await!</span>(serial.read()).unwrap();
|
|||
|
<span class="macro">await!</span>(serial.write(byte)).unwrap();
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
<span class="comment">// Event loop
|
|||
|
</span><span class="kw">loop </span>{
|
|||
|
Pin::new(<span class="kw-2">&mut </span>blinky).resume(());
|
|||
|
Pin::new(<span class="kw-2">&mut </span>loopback).resume(());
|
|||
|
}
|
|||
|
}
|
|||
|
</code></pre></div>
|
|||
|
<h3 id="generic-programming-and-higher-level-abstractions"><a class="doc-anchor" href="#generic-programming-and-higher-level-abstractions">§</a>Generic programming and higher level abstractions</h3>
|
|||
|
<p>The core of the HAL has been kept minimal on purpose to encourage building <strong>generic</strong> 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 <code>blocking</code> module and, in the future, in the <code>futures</code> and <code>async</code> modules.</p>
|
|||
|
<p>Some examples:</p>
|
|||
|
<p><strong>NOTE</strong> 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.</p>
|
|||
|
<ul>
|
|||
|
<li>Write a whole buffer to a serial device in blocking a fashion.</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="kw">extern crate </span>embedded_hal <span class="kw">as </span>hal;
|
|||
|
<span class="attr">#[macro_use(block)]
|
|||
|
</span><span class="kw">extern crate </span>nb;
|
|||
|
|
|||
|
<span class="kw">use </span>hal::prelude::<span class="kw-2">*</span>;
|
|||
|
|
|||
|
<span class="kw">fn </span>write_all<S>(serial: <span class="kw-2">&mut </span>S, buffer: <span class="kw-2">&</span>[u8]) -> <span class="prelude-ty">Result</span><(), S::Error>
|
|||
|
<span class="kw">where
|
|||
|
</span>S: hal::serial::Write<u8>
|
|||
|
{
|
|||
|
<span class="kw">for </span><span class="kw-2">&</span>byte <span class="kw">in </span>buffer {
|
|||
|
<span class="macro">block!</span>(serial.write(byte))<span class="question-mark">?</span>;
|
|||
|
}
|
|||
|
|
|||
|
<span class="prelude-val">Ok</span>(())
|
|||
|
}
|
|||
|
</code></pre></div>
|
|||
|
<ul>
|
|||
|
<li>Blocking serial read with timeout</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="kw">extern crate </span>embedded_hal <span class="kw">as </span>hal;
|
|||
|
<span class="kw">extern crate </span>nb;
|
|||
|
|
|||
|
<span class="kw">use </span>hal::prelude::<span class="kw-2">*</span>;
|
|||
|
|
|||
|
<span class="kw">enum </span>Error<E> {
|
|||
|
<span class="doccomment">/// Serial interface error
|
|||
|
</span>Serial(E),
|
|||
|
TimedOut,
|
|||
|
}
|
|||
|
|
|||
|
<span class="kw">fn </span>read_with_timeout<S, T>(
|
|||
|
serial: <span class="kw-2">&mut </span>S,
|
|||
|
timer: <span class="kw-2">&mut </span>T,
|
|||
|
timeout: T::Time,
|
|||
|
) -> <span class="prelude-ty">Result</span><u8, Error<S::Error>>
|
|||
|
<span class="kw">where
|
|||
|
</span>T: hal::timer::CountDown,
|
|||
|
S: hal::serial::Read<u8>,
|
|||
|
{
|
|||
|
timer.start(timeout);
|
|||
|
|
|||
|
<span class="kw">loop </span>{
|
|||
|
<span class="kw">match </span>serial.read() {
|
|||
|
<span class="comment">// raise error
|
|||
|
</span><span class="prelude-val">Err</span>(nb::Error::Other(e)) => <span class="kw">return </span><span class="prelude-val">Err</span>(Error::Serial(e)),
|
|||
|
<span class="prelude-val">Err</span>(nb::Error::WouldBlock) => {
|
|||
|
<span class="comment">// no data available yet, check the timer below
|
|||
|
</span>},
|
|||
|
<span class="prelude-val">Ok</span>(byte) => <span class="kw">return </span><span class="prelude-val">Ok</span>(byte),
|
|||
|
}
|
|||
|
|
|||
|
<span class="kw">match </span>timer.wait() {
|
|||
|
<span class="prelude-val">Err</span>(nb::Error::Other(e)) => {
|
|||
|
<span class="comment">// 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.
|
|||
|
</span><span class="macro">unreachable!</span>()
|
|||
|
},
|
|||
|
<span class="comment">// no timeout yet, try again
|
|||
|
</span><span class="prelude-val">Err</span>(nb::Error::WouldBlock) => <span class="kw">continue</span>,
|
|||
|
<span class="prelude-val">Ok</span>(()) => <span class="kw">return </span><span class="prelude-val">Err</span>(Error::TimedOut),
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
</code></pre></div>
|
|||
|
<ul>
|
|||
|
<li>Asynchronous SPI transfer</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="attr">#![feature(conservative_impl_trait)]
|
|||
|
#![feature(generators)]
|
|||
|
#![feature(generator_trait)]
|
|||
|
|
|||
|
</span><span class="kw">extern crate </span>embedded_hal <span class="kw">as </span>hal;
|
|||
|
<span class="attr">#[macro_use(<span class="kw">await</span>)]
|
|||
|
</span><span class="kw">extern crate </span>nb;
|
|||
|
|
|||
|
<span class="kw">use </span>std::ops::Generator;
|
|||
|
|
|||
|
<span class="doccomment">/// Transfers a byte buffer of size N
|
|||
|
///
|
|||
|
/// Returns the same byte buffer but filled with the data received from the
|
|||
|
/// slave device
|
|||
|
</span><span class="kw">fn </span>transfer<S, B>(
|
|||
|
<span class="kw-2">mut </span>spi: S,
|
|||
|
<span class="kw-2">mut </span>buffer: [u8; <span class="number">16</span>], <span class="comment">// NOTE this should be generic over the size of the array
|
|||
|
</span>) -> <span class="kw">impl </span>Generator<Return = <span class="prelude-ty">Result</span><(S, [u8; <span class="number">16</span>]), S::Error>, Yield = ()>
|
|||
|
<span class="kw">where
|
|||
|
</span>S: hal::spi::FullDuplex<u8>,
|
|||
|
{
|
|||
|
<span class="kw">move </span>|| {
|
|||
|
<span class="kw">let </span>n = buffer.len();
|
|||
|
<span class="kw">for </span>i <span class="kw">in </span><span class="number">0</span>..n {
|
|||
|
<span class="macro">await!</span>(spi.send(buffer[i]))<span class="question-mark">?</span>;
|
|||
|
buffer[i] = <span class="macro">await!</span>(spi.read())<span class="question-mark">?</span>;
|
|||
|
}
|
|||
|
|
|||
|
<span class="prelude-val">Ok</span>((spi, buffer))
|
|||
|
}
|
|||
|
}
|
|||
|
</code></pre></div>
|
|||
|
<ul>
|
|||
|
<li>Buffered serial interface with periodic flushing in interrupt handler</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="kw">extern crate </span>embedded_hal <span class="kw">as </span>hal;
|
|||
|
<span class="kw">extern crate </span>nb;
|
|||
|
<span class="kw">extern crate </span>void;
|
|||
|
|
|||
|
<span class="kw">use </span>hal::prelude::<span class="kw-2">*</span>;
|
|||
|
<span class="kw">use </span>void::Void;
|
|||
|
|
|||
|
<span class="kw">fn </span>flush<S>(serial: <span class="kw-2">&mut </span>S, cb: <span class="kw-2">&mut </span>CircularBuffer)
|
|||
|
<span class="kw">where
|
|||
|
</span>S: hal::serial::Write<u8, Error = Void>,
|
|||
|
{
|
|||
|
<span class="kw">loop </span>{
|
|||
|
<span class="kw">if let </span><span class="prelude-val">Some</span>(byte) = cb.peek() {
|
|||
|
<span class="kw">match </span>serial.write(<span class="kw-2">*</span>byte) {
|
|||
|
<span class="prelude-val">Err</span>(nb::Error::Other(<span class="kw">_</span>)) => <span class="macro">unreachable!</span>(),
|
|||
|
<span class="prelude-val">Err</span>(nb::Error::WouldBlock) => <span class="kw">return</span>,
|
|||
|
<span class="prelude-val">Ok</span>(()) => {}, <span class="comment">// keep flushing data
|
|||
|
</span>}
|
|||
|
}
|
|||
|
|
|||
|
cb.pop();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
<span class="comment">// The stuff below could be in some other crate
|
|||
|
|
|||
|
</span><span class="doccomment">/// Global singleton
|
|||
|
</span><span class="kw">pub struct </span>BufferedSerial1;
|
|||
|
|
|||
|
<span class="comment">// NOTE private
|
|||
|
</span><span class="kw">static </span>BUFFER1: Mutex<CircularBuffer> = {
|
|||
|
<span class="comment">// ..
|
|||
|
</span>};
|
|||
|
<span class="kw">static </span>SERIAL1: Mutex<Serial1> = {
|
|||
|
<span class="comment">// ..
|
|||
|
</span>};
|
|||
|
|
|||
|
<span class="kw">impl </span>BufferedSerial1 {
|
|||
|
<span class="kw">pub fn </span>write(<span class="kw-2">&</span><span class="self">self</span>, byte: u8) {
|
|||
|
<span class="self">self</span>.write_all(<span class="kw-2">&</span>[byte])
|
|||
|
}
|
|||
|
|
|||
|
<span class="kw">pub fn </span>write_all(<span class="kw-2">&</span><span class="self">self</span>, bytes: <span class="kw-2">&</span>[u8]) {
|
|||
|
<span class="kw">let </span><span class="kw-2">mut </span>buffer = BUFFER1.lock();
|
|||
|
<span class="kw">for </span>byte <span class="kw">in </span>bytes {
|
|||
|
buffer.push(<span class="kw-2">*</span>byte).expect(<span class="string">"buffer overrun"</span>);
|
|||
|
}
|
|||
|
<span class="comment">// omitted: pend / enable interrupt_handler
|
|||
|
</span>}
|
|||
|
}
|
|||
|
|
|||
|
<span class="kw">fn </span>interrupt_handler() {
|
|||
|
<span class="kw">let </span><span class="kw-2">mut </span>serial = SERIAL1.lock();
|
|||
|
<span class="kw">let </span><span class="kw-2">mut </span>buffer = BUFFER1.lock();
|
|||
|
|
|||
|
flush(<span class="kw-2">&mut *</span>serial, <span class="kw-2">&mut </span>buffer);
|
|||
|
}
|
|||
|
|
|||
|
</code></pre></div>
|
|||
|
</div></details><h2 id="modules" class="section-header">Modules<a href="#modules" class="anchor">§</a></h2><ul class="item-table"><li><div class="item-name"><a class="mod" href="adc/index.html" title="mod embedded_hal::adc">adc</a></div><div class="desc docblock-short">Analog-digital conversion traits</div></li><li><div class="item-name"><a class="mod" href="blocking/index.html" title="mod embedded_hal::blocking">blocking</a></div><div class="desc docblock-short">Blocking API</div></li><li><div class="item-name"><a class="mod" href="can/index.html" title="mod embedded_hal::can">can</a></div><div class="desc docblock-short">Controller Area Network</div></li><li><div class="item-name"><a class="mod" href="digital/index.html" title="mod embedded_hal::digital">digital</a></div><div class="desc docblock-short">Digital I/O</div></li><li><div class="item-name"><a class="mod" href="fmt/index.html" title="mod embedded_hal::fmt">fmt</a></div><div class="desc docblock-short">Implementation of <code>core::fmt::Write</code> for the HAL’s <code>serial::Write</code>.</div></li><li><div class="item-name"><a class="mod" href="prelude/index.html" title="mod embedded_hal::prelude">prelude</a></div><div class="desc docblock-short">The prelude is a collection of all the traits in this crate</div></li><li><div class="item-name"><a class="mod" href="serial/index.html" title="mod embedded_hal::serial">serial</a></div><div class="desc docblock-short">Serial interface</div></li><li><div class="item-name"><a class="mod" href="spi/index.html" title="mod embedded_hal::spi">spi</a></div><div class="desc docblock-short">Serial Peripheral Interface</div></li><li><div class="item-name"><a class="mod" href="timer/index.html" title="mod embedded_hal::timer">timer</a></div><div class="desc docblock-short">Timers</div></li><li><div class="item-name"><a class="mod" href="watchdog/index.html" title="mod embedded_hal::watchdog">watchdog</a></div><div class="desc docblock-short">Traits for interactions with a processors watchdog timer.</div></li></ul><h2 id="traits-1" class="section-header">Traits<a href="#traits-1" class="anchor">§</a></h2><ul class="item-table"><li><div class="item-name"><a class="trait" href="trait.PwmPin.html" title="trait embedded_hal::PwmPin">PwmPin</a></div><div class="desc docblock-short">A single PWM channel / pin</div></li></ul></section></div></main></body></html>
|