#hal #cortex-m #async #nxp #micro-controller #arm

nightly no-std lpc11xx-async-hal

Asynchronous HAL for the NXP LPC111x/LPC11Cxx/LPC11xxL/LPC11xxXL family of Cortex-M0 microcontrollers

4 releases

0.1.3 Jul 30, 2019
0.1.2 Jul 29, 2019
0.1.1 Jul 29, 2019
0.1.0 Jul 27, 2019

#1943 in Embedded development

MIT license

66KB
1K SLoC

LPC11xx Async HAL

Documentation Crates.io

Asynchronous HAL for the NXP LPC111x/LPC11Cxx/LPC11xxL/LPC11xxXL family of Cortex-M0 microcontrollers using async-await syntax.

Example

Below is a minimal echo server using the UART driver. The microcontroller will wake up from sleep only to send/receive data in the UART interrupt and to issue additional UART commands from the async fn inside PendSV and will be sleeping the rest of the time.

use lpc11xx_async_hal::uart;

// Initialize the UART driver ahead of time, e.g. 9600 baud at 12MHz
// The UART peripheral must have power, a clock, configured pins etc

let uart = uart::Driver::initialize(
    device.UART, // < lpc11xx::UART
    uart::Options {
        clock_divisor: 71,
        fractional_div: 0,
        fractional_mul: 1,
        flow_control: false,
        rx_threshold: uart::RxThreshold::default(),
    },
);

// The actual logic, to be polled from the PendSV interrupt handler

async fn echo_server(mut uart: uart::Driver) -> ! {
    let (mut sender, mut receiver) = uart.split();

    sender.write(b"Type some text: ").await;

    loop {
        let mut buffer = [0; 20];

        let bytes_read = receiver.read(&mut buffer).await;

        sender.write(&buffer[..bytes_read]).await;
    }
}

Status

Async drivers for the following peripherals are currently available:

Peripheral Missing features Won't implement
UART Autobaud, RS-485
I2C Slave Mode Monitor Mode
ADC Timer Match
SysTick

More are in development, contributions and suggestions welcome.

Caveats

The Rust async-await feature is still under heavy development to say nothing of its #![no_std] integration so this crate should be viewed as a proof of concept and ongoing experiment in writing automatically generated asynchronous state machines on LPC11xx devices. It works fine and does useful things though.

No-std support

An additional problem is brought about when using async-await in #![no_std] crates; essentially, it doesn't work because the compiler machinery to generate the resulting futures depends on thread-local storage which is in std. You can work around this by using the core-futures-stateless helper crate which will patch libcore to make #![feature(async_await)] work:

[dependencies.core]
package = "core-futures-stateless"
version = "0.1.0"

This can be removed once fixed in libcore. Note that this will (unfortunately) change the crate name of all types in the core crate in compiler messages.

Futures behaviour

This crate is built exclusively around the PendSV facility in Cortex-M processors. That is, all futures depend on being polled inside the PendSV exception handler, and all peripheral interrupt handlers will raise PendSV to progress the futures. As a result the task::Context associated with your main async task is irrelevant and you should create a dummy Waker that does absolutely nothing. The core-futures-stateless crate provides a convenience method task::stateless_waker() which does just that.

use core::future::Future;
use core::task::{stateless_waker, Context};

// your executor somewhere inside the PendSV exception handler

match Future::poll(your_pinned_future, &mut Context::from_waker(stateless_waker())) {
    Poll::Ready(x) => { /* future resolved */ },
    Poll::Pending => { /* go back to sleep */ },
}

In the future the crate might become context-aware but in the meantime this is the way it works for simplicity and because using PendSV is quite a natural approach to polling futures.

Memory safety

There is currently a minor soundness hole in this crate in that interrupt handlers reference stack-like memory from currently running futures, which means that Drop impls for Future types must run to ensure memory safety. I am not sure how to fix this without significant compromises in performance or ergonomics and don't really consider this a major issue from a pragmatic standpoint. Leak futures at your own peril.

Dependencies

~3MB
~75K SLoC