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

nightly no-std lpc11xx-async-hal

NXP LPC111x/LPC11Cxx/LPC11xxL/LPC11xxXL系列Cortex-M0微控制器的异步HAL

4个版本

0.1.3 2019年7月30日
0.1.2 2019年7月29日
0.1.1 2019年7月29日
0.1.0 2019年7月27日

1929 in 嵌入式开发

MIT许可

66KB
1K SLoC

LPC11xx Async HAL

Documentation Crates.io

使用async-await语法为NXP LPC111x/LPC11Cxx/LPC11xxL/LPC11xxXL系列Cortex-M0微控制器提供的异步HAL。

示例

以下是一个使用UART驱动器的最小echo服务器示例。微控制器仅在UART中断中发送/接收数据以及从async fn中的PendSV发出附加UART命令时从睡眠状态唤醒,其余时间处于睡眠状态。

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;
    }
}

状态

以下外设的异步驱动程序目前可用

外设 缺失的功能 不会实现
UART 自动波特率,RS-485
I2C 从模式 监视模式
ADC 定时器匹配
SysTick

更多功能正在开发中,欢迎贡献和建议。

注意事项

由于Rust的async-await特性仍在积极开发中,更不用说其#![no_std]集成,因此此crate应被视为一个证明概念和持续实验,用于在LPC11xx设备上编写自动生成的异步状态机。尽管如此,它确实工作良好并做了有用的事情。

无std支持

在使用#![no_std]crate中的async-await时,会产生一个额外的问题;基本上,它不起作用,因为生成结果的future所需的编译器机制依赖于线程局部存储,而线程局部存储位于std中。您可以通过使用core-futures-stateless辅助crate来解决此问题,该crate将修补libcore以使#![feature(async_await)]工作

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

这将在libcore修复后删除。请注意,这将在编译器消息中更改corecrate中所有类型的crate名称。

未来行为

此crate专门针对Cortex-M处理器中的PendSV功能构建。也就是说,所有未来都依赖于在PendSV异常处理程序中进行轮询,所有外设中断处理程序都会引发PendSV以推进未来。因此,与您的main异步任务关联的task::Context是无关紧要的,您应该创建一个什么也不做的虚拟Waker。The core-futures-stateless crate提供了一个方便的方法task::stateless_waker(),它正是这样做的。

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 */ },
}

未来,这个crate可能会变得具有上下文感知能力,但与此同时,这是出于简化和因为使用PendSV是轮询未来的相当自然的方法。

内存安全

目前,在这个crate中存在一个小的健全性问题,即中断处理程序从当前运行的未来引用类似于堆栈的内存,这意味着Future类型的Drop实现必须运行以确保内存安全。我不确定如何在不牺牲性能或易用性的情况下解决这个问题,并且并不认为这是一个从实用主义角度来看的重大问题。泄露未来自负其责。

依赖关系

~3MB
~75K SLoC