Skip to content

Commit 5213c1e

Browse files
jbeaurivagesajattack
authored andcommitted
feat!: Add async support for many peripherals (#635)
Adds support for async APIs for the following peripherals: * SPI * I2C * UART * DMAC * EIC (external GPIO interrupts) * Timer/counters BREAKING CHANGE: Removes support for wakers in sync `dmac::Transfers`.
1 parent 62e3411 commit 5213c1e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

71 files changed

+5915
-471
lines changed

.github/workflows/build-bsp.yml

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ jobs:
3030

3131
- name: Install Rust
3232
run: |
33+
rustup update
3334
rustup set profile minimal
3435
rustup override set ${{ matrix.toolchain }}
3536
target=$(cat ./crates.json | jq -Mr --arg board '${{ matrix.bsp.name }}' -c '.boards | .[$board] | .target')

.github/workflows/build-hal.yml

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ jobs:
2424

2525
- name: Install Rust
2626
run: |
27+
rustup update
2728
rustup set profile minimal
2829
rustup override set ${{ matrix.toolchain }}
2930
target=$(cat ./crates.json | jq -Mr --arg pac "${{matrix.pac}}" -c '.hal_build_variants["${{matrix.pac}}"].target')

README.md

+19
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,25 @@ The Peripheral Access Crates (PACs) are automatically generated from Microchip S
9191
[wio_terminal]: https://github.com/atsamd-rs/atsamd/tree/master/boards/wio_terminal
9292
[xiao_m0]: https://github.com/atsamd-rs/atsamd/tree/master/boards/xiao_m0
9393

94+
95+
### `async` APIs
96+
97+
[`atsamd_hal`](https://crates.io/crate/atsamd-hal) provides APIs for using `async`/`await` constructs with some of its peripherals. To enable `async` support, use the `async` Cargo feature.
98+
Detailed documentation is provided in the `atsamd_hal::async_hal` module. The [metro_m4](https://github.com/atsamd-rs/atsamd/tree/master/boards/metro_m4/examples) and
99+
[feather_m0](https://github.com/atsamd-rs/atsamd/tree/master/boards/feather_m0/examples) feature complete examples showing how to use async APIs.
100+
101+
Please note that you must bring your own executor runtime such as [`embassy-executor`](https://crates.io/crates/embassy-executor) or [`rtic`](https://crates.io/crates/rtic) to be able to
102+
use the async APIs.
103+
104+
#### Supported peripherals
105+
106+
* SPI
107+
* I2C
108+
* USART
109+
* DMAC
110+
* EIC (GPIO interrupts)
111+
* Timers
112+
94113
### Examples
95114

96115
The BSPs include examples to quickly get up and running with the board. Building the examples

boards/atsame54_xpro/Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ rtt-target = { version = "0.3", features = ["cortex-m"] }
4040
[features]
4141
default = ["rt", "atsamd-hal/same54p"]
4242
dma = ["atsamd-hal/dma"]
43+
max-channels = ["dma", "atsamd-hal/max-channels"]
44+
# Enable async support from atsamd-hal
45+
async = ["atsamd-hal/async"]
4346
rt = ["cortex-m-rt", "atsamd-hal/same54p-rt"]
4447
usb = ["atsamd-hal/usb", "usb-device"]
4548
can = ["atsamd-hal/can"]

boards/atsame54_xpro/examples/mcan.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -140,12 +140,12 @@ mod app {
140140

141141
let (pclk_eic, gclk0) = clock::pclk::Pclk::enable(tokens.pclks.eic, clocks.gclk0);
142142

143-
let mut eic = hal::eic::init_with_ulp32k(&mut mclk, pclk_eic.into(), ctx.device.eic);
144-
let mut button = bsp::pin_alias!(pins.button).into_pull_up_ei();
145-
eic.button_debounce_pins(&[button.id()]);
146-
button.sense(&mut eic, Sense::Fall);
147-
button.enable_interrupt(&mut eic);
148-
eic.finalize();
143+
let mut eic =
144+
hal::eic::init_with_ulp32k(&mut mclk, pclk_eic.into(), ctx.device.eic).finalize();
145+
let mut button = bsp::pin_alias!(pins.button).into_pull_up_ei(&mut eic);
146+
button.sense(Sense::Fall);
147+
button.debounce();
148+
button.enable_interrupt();
149149

150150
let can1_rx = bsp::pin_alias!(pins.ata6561_rx).into_mode();
151151
let can1_tx = bsp::pin_alias!(pins.ata6561_tx).into_mode();

boards/feather_m0/.cargo/config.toml

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ rustflags = [
77
# See https://github.com/rust-embedded/cortex-m-quickstart/pull/95
88
"-C", "link-arg=--nmagic",
99
"-C", "link-arg=-Tlink.x",
10+
"-C", "link-arg=-Tdefmt.x" # uncomment if using defmt
1011
]
1112

1213
[target.thumbv6m-none-eabi]

boards/feather_m0/Cargo.toml

+40-5
Original file line numberDiff line numberDiff line change
@@ -37,17 +37,23 @@ version = "0.3"
3737
optional = true
3838

3939
[dev-dependencies]
40-
cortex-m-rtic = "1.0"
40+
embassy-executor = { version = "0.6.2", features = ["arch-cortex-m", "executor-thread", "task-arena-size-64"] }
41+
rtic = { version = "2.1.1", features = ["thumbv6-backend"] }
42+
rtic-monotonics = { version = "1.3.0", features = ["cortex-m-systick", "systick-10khz"] }
43+
fugit = "0.3.6"
4144
cortex-m = "0.7"
4245
usbd-serial = "0.2"
4346
cortex-m-semihosting = "0.3"
4447
ssd1306 = "0.7"
4548
embedded-graphics = "0.7.1"
4649
drogue-nom-utils = "0.1"
4750
nom = { version = "5", default-features = false }
48-
heapless = "0.7"
51+
heapless = "0.8"
4952
panic-halt = "0.2"
50-
panic-semihosting = "0.5"
53+
panic-semihosting = "0.6"
54+
defmt = "0.3"
55+
defmt-rtt = "0.4"
56+
panic-probe = "0.3"
5157

5258
[features]
5359
# ask the HAL to enable atsamd21g support
@@ -61,6 +67,8 @@ rfm = []
6167
express = []
6268
dma = ["atsamd-hal/dma"]
6369
max-channels = ["dma", "atsamd-hal/max-channels"]
70+
# Enable async support from atsamd-hal
71+
async = ["atsamd-hal/async"]
6472
# Enable pins for the adalogger SD card reader
6573
adalogger = []
6674
# Enable pins for Feather with WINC1500 wifi
@@ -72,6 +80,10 @@ use_semihosting = []
7280
[[example]]
7381
name = "blinky_basic"
7482

83+
[[example]]
84+
name = "blinky_rtic"
85+
required-features = ["rtic"]
86+
7587
[[example]]
7688
name = "timers"
7789

@@ -122,8 +134,7 @@ name = "adalogger"
122134
required-features = ["adalogger", "usb", "sdmmc"]
123135

124136
[[example]]
125-
name = "blinky_rtic"
126-
required-features = ["rtic"]
137+
name = "blinky_monotonic"
127138

128139
[[example]]
129140
name = "uart"
@@ -143,3 +154,27 @@ required-features = ["dma"]
143154
[[example]]
144155
name = "spi"
145156
required-features = ["dma"]
157+
158+
[[example]]
159+
name = "async_dmac"
160+
required-features = ["dma", "async"]
161+
162+
[[example]]
163+
name = "async_timer"
164+
required-features = ["async"]
165+
166+
[[example]]
167+
name = "async_eic"
168+
required-features = ["async"]
169+
170+
[[example]]
171+
name = "async_i2c"
172+
required-features = ["dma", "async"]
173+
174+
[[example]]
175+
name = "async_spi"
176+
required-features = ["dma", "async"]
177+
178+
[[example]]
179+
name = "async_uart"
180+
required-features = ["dma", "async"]
+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
//! This example shows a safe API to
2+
//! execute a memory-to-memory DMA transfer
3+
4+
#![no_std]
5+
#![no_main]
6+
7+
use defmt_rtt as _;
8+
use panic_probe as _;
9+
10+
atsamd_hal::bind_interrupts!(struct Irqs {
11+
DMAC => atsamd_hal::dmac::InterruptHandler;
12+
});
13+
14+
use bsp::hal;
15+
use bsp::pac;
16+
use feather_m0 as bsp;
17+
use hal::{
18+
clock::GenericClockController,
19+
dmac::{DmaController, PriorityLevel, TriggerAction, TriggerSource},
20+
};
21+
22+
#[embassy_executor::main]
23+
async fn main(_s: embassy_executor::Spawner) {
24+
let mut peripherals = pac::Peripherals::take().unwrap();
25+
let _core = pac::CorePeripherals::take().unwrap();
26+
27+
let _clocks = GenericClockController::with_external_32kosc(
28+
peripherals.gclk,
29+
&mut peripherals.pm,
30+
&mut peripherals.sysctrl,
31+
&mut peripherals.nvmctrl,
32+
);
33+
34+
// Initialize DMA Controller
35+
let dmac = DmaController::init(peripherals.dmac, &mut peripherals.pm);
36+
37+
// Turn dmac into an async controller
38+
let mut dmac = dmac.into_future(crate::Irqs);
39+
// Get individual handles to DMA channels
40+
let channels = dmac.split();
41+
42+
// Initialize DMA Channel 0
43+
let mut channel = channels.0.init(PriorityLevel::Lvl0);
44+
45+
let mut source = [0xff; 100];
46+
let mut dest = [0x0; 100];
47+
48+
defmt::info!(
49+
"Launching a DMA transfer.\n\tSource: {:#x}\n\tDestination: {:#x}",
50+
&source,
51+
&dest
52+
);
53+
54+
channel
55+
.transfer_future(
56+
&mut source,
57+
&mut dest,
58+
TriggerSource::Disable,
59+
TriggerAction::Block,
60+
)
61+
.await
62+
.unwrap();
63+
64+
defmt::info!(
65+
"Finished DMA transfer.\n\tSource: {:#x}\n\tDestination: {:#x}",
66+
&source,
67+
&dest
68+
);
69+
70+
loop {
71+
cortex_m::asm::wfi();
72+
}
73+
}
+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#![no_std]
2+
#![no_main]
3+
4+
use defmt_rtt as _;
5+
use panic_probe as _;
6+
7+
use bsp::pac;
8+
use bsp::{hal, pin_alias};
9+
use feather_m0 as bsp;
10+
use hal::{
11+
clock::{enable_internal_32kosc, ClockGenId, ClockSource, GenericClockController},
12+
ehal::digital::StatefulOutputPin,
13+
eic::{
14+
pin::{ExtInt2, Sense},
15+
EIC,
16+
},
17+
gpio::{Pin, PullUpInterrupt},
18+
};
19+
20+
atsamd_hal::bind_interrupts!(struct Irqs {
21+
EIC => atsamd_hal::eic::InterruptHandler;
22+
});
23+
24+
#[embassy_executor::main]
25+
async fn main(_s: embassy_executor::Spawner) {
26+
let mut peripherals = pac::Peripherals::take().unwrap();
27+
let _core = pac::CorePeripherals::take().unwrap();
28+
29+
let mut clocks = GenericClockController::with_external_32kosc(
30+
peripherals.gclk,
31+
&mut peripherals.pm,
32+
&mut peripherals.sysctrl,
33+
&mut peripherals.nvmctrl,
34+
);
35+
let pins = bsp::Pins::new(peripherals.port);
36+
let mut red_led: bsp::RedLed = pin_alias!(pins.red_led).into();
37+
38+
let _internal_clock = clocks
39+
.configure_gclk_divider_and_source(ClockGenId::Gclk2, 1, ClockSource::Osc32k, false)
40+
.unwrap();
41+
clocks.configure_standby(ClockGenId::Gclk2, true);
42+
43+
enable_internal_32kosc(&mut peripherals.sysctrl);
44+
45+
// Configure a clock for the EIC peripheral
46+
let gclk2 = clocks.get_gclk(ClockGenId::Gclk2).unwrap();
47+
let eic_clock = clocks.eic(&gclk2).unwrap();
48+
49+
let mut eic = EIC::init(&mut peripherals.pm, eic_clock, peripherals.eic).into_future(Irqs);
50+
let button: Pin<_, PullUpInterrupt> = pins.d10.into();
51+
let mut extint = ExtInt2::new(button, &mut eic);
52+
extint.enable_interrupt_wake();
53+
54+
loop {
55+
// Here we show straight falling edge detection without
56+
extint.wait(Sense::Fall).await;
57+
defmt::info!("Falling edge detected");
58+
red_led.toggle().unwrap();
59+
}
60+
}
+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#![no_std]
2+
#![no_main]
3+
4+
use defmt_rtt as _;
5+
use panic_probe as _;
6+
7+
use bsp::hal;
8+
use bsp::pac;
9+
use feather_m0 as bsp;
10+
use fugit::MillisDuration;
11+
use hal::ehal_async::i2c::I2c;
12+
use hal::{
13+
clock::GenericClockController,
14+
dmac::{DmaController, PriorityLevel},
15+
prelude::*,
16+
sercom::{i2c, Sercom3},
17+
};
18+
use rtic_monotonics::systick::Systick;
19+
20+
atsamd_hal::bind_interrupts!(struct Irqs {
21+
SERCOM3 => atsamd_hal::sercom::i2c::InterruptHandler<Sercom3>;
22+
DMAC => atsamd_hal::dmac::InterruptHandler;
23+
});
24+
25+
#[embassy_executor::main]
26+
async fn main(_s: embassy_executor::Spawner) {
27+
let mut peripherals = pac::Peripherals::take().unwrap();
28+
let _core = pac::CorePeripherals::take().unwrap();
29+
30+
let mut clocks = GenericClockController::with_external_32kosc(
31+
peripherals.gclk,
32+
&mut peripherals.pm,
33+
&mut peripherals.sysctrl,
34+
&mut peripherals.nvmctrl,
35+
);
36+
37+
let pins = bsp::Pins::new(peripherals.port);
38+
39+
// Take SDA and SCL
40+
let (sda, scl) = (pins.sda, pins.scl);
41+
let i2c_sercom = bsp::periph_alias!(peripherals.i2c_sercom);
42+
43+
// Initialize DMA Controller
44+
let dmac = DmaController::init(peripherals.dmac, &mut peripherals.pm);
45+
46+
// Turn dmac into an async controller
47+
let mut dmac = dmac.into_future(Irqs);
48+
// Get individual handles to DMA channels
49+
let channels = dmac.split();
50+
51+
// Initialize DMA Channel 0
52+
let channel0 = channels.0.init(PriorityLevel::Lvl0);
53+
54+
let gclk0 = clocks.gclk0();
55+
let sercom3_clock = &clocks.sercom3_core(&gclk0).unwrap();
56+
let pads = i2c::Pads::new(sda, scl);
57+
let mut i2c = i2c::Config::new(&peripherals.pm, i2c_sercom, pads, sercom3_clock.freq())
58+
.baud(100.kHz())
59+
.enable()
60+
.into_future(Irqs)
61+
.with_dma_channel(channel0);
62+
63+
loop {
64+
defmt::info!("Sending 0x00 to I2C device...");
65+
// This test is based on the BMP388 barometer. Feel free to use any I2C
66+
// peripheral you have on hand.
67+
i2c.write(0x76, &[0x00]).await.unwrap();
68+
69+
let mut buffer = [0xff; 4];
70+
i2c.read(0x76, &mut buffer).await.unwrap();
71+
defmt::info!("Read buffer: {:#x}", buffer);
72+
Systick::delay(MillisDuration::<u32>::from_ticks(500).convert()).await;
73+
}
74+
}

0 commit comments

Comments
 (0)