Skip to content

Commit

Permalink
feat!: Add async support for many peripherals (atsamd-rs#635)
Browse files Browse the repository at this point in the history
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`.
  • Loading branch information
jbeaurivage committed Nov 21, 2024
1 parent f222d42 commit 672d534
Show file tree
Hide file tree
Showing 71 changed files with 5,993 additions and 515 deletions.
1 change: 1 addition & 0 deletions .github/workflows/build-bsp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ jobs:

- name: Install Rust
run: |
rustup update
rustup set profile minimal
rustup override set ${{ matrix.toolchain }}
target=$(cat ./crates.json | jq -Mr --arg board '${{ matrix.bsp.name }}' -c '.boards | .[$board] | .target')
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/build-hal.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ jobs:

- name: Install Rust
run: |
rustup update
rustup set profile minimal
rustup override set ${{ matrix.toolchain }}
target=$(cat ./crates.json | jq -Mr --arg pac "${{matrix.pac}}" -c '.hal_build_variants["${{matrix.pac}}"].target')
Expand Down
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,25 @@ The Peripheral Access Crates (PACs) are automatically generated from Microchip S
[wio_terminal]: https://github.com/atsamd-rs/atsamd/tree/master/boards/wio_terminal
[xiao_m0]: https://github.com/atsamd-rs/atsamd/tree/master/boards/xiao_m0


### `async` APIs

[`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.
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
[feather_m0](https://github.com/atsamd-rs/atsamd/tree/master/boards/feather_m0/examples) feature complete examples showing how to use async APIs.

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
use the async APIs.

#### Supported peripherals

* SPI
* I2C
* USART
* DMAC
* EIC (GPIO interrupts)
* Timers

### Examples

The BSPs include examples to quickly get up and running with the board. Building the examples
Expand Down
3 changes: 3 additions & 0 deletions boards/atsame54_xpro/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ rtt-target = { version = "0.3", features = ["cortex-m"] }
[features]
default = ["rt", "atsamd-hal/same54p"]
dma = ["atsamd-hal/dma"]
max-channels = ["dma", "atsamd-hal/max-channels"]
# Enable async support from atsamd-hal
async = ["atsamd-hal/async"]
rt = ["cortex-m-rt", "atsamd-hal/same54p-rt"]
usb = ["atsamd-hal/usb", "usb-device"]
can = ["atsamd-hal/can"]
Expand Down
12 changes: 6 additions & 6 deletions boards/atsame54_xpro/examples/mcan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,12 +140,12 @@ mod app {

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

let mut eic = hal::eic::init_with_ulp32k(&mut mclk, pclk_eic.into(), ctx.device.eic);
let mut button = bsp::pin_alias!(pins.button).into_pull_up_ei();
eic.button_debounce_pins(&[button.id()]);
button.sense(&mut eic, Sense::Fall);
button.enable_interrupt(&mut eic);
eic.finalize();
let mut eic =
hal::eic::init_with_ulp32k(&mut mclk, pclk_eic.into(), ctx.device.eic).finalize();
let mut button = bsp::pin_alias!(pins.button).into_pull_up_ei(&mut eic);
button.sense(Sense::Fall);
button.debounce();
button.enable_interrupt();

let can1_rx = bsp::pin_alias!(pins.ata6561_rx).into_mode();
let can1_tx = bsp::pin_alias!(pins.ata6561_tx).into_mode();
Expand Down
1 change: 1 addition & 0 deletions boards/feather_m0/.cargo/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ rustflags = [
# See https://github.com/rust-embedded/cortex-m-quickstart/pull/95
"-C", "link-arg=--nmagic",
"-C", "link-arg=-Tlink.x",
"-C", "link-arg=-Tdefmt.x" # uncomment if using defmt
]

[target.thumbv6m-none-eabi]
Expand Down
45 changes: 40 additions & 5 deletions boards/feather_m0/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,23 @@ version = "0.3"
optional = true

[dev-dependencies]
cortex-m-rtic = "1.0"
embassy-executor = { version = "0.6.2", features = ["arch-cortex-m", "executor-thread", "task-arena-size-64"] }
rtic = { version = "2.1.1", features = ["thumbv6-backend"] }
rtic-monotonics = { version = "1.3.0", features = ["cortex-m-systick", "systick-10khz"] }
fugit = "0.3.6"
cortex-m = "0.7"
usbd-serial = "0.2"
cortex-m-semihosting = "0.3"
ssd1306 = "0.7"
embedded-graphics = "0.7.1"
drogue-nom-utils = "0.1"
nom = { version = "5", default-features = false }
heapless = "0.7"
heapless = "0.8"
panic-halt = "0.2"
panic-semihosting = "0.5"
panic-semihosting = "0.6"
defmt = "0.3"
defmt-rtt = "0.4"
panic-probe = "0.3"

[features]
# ask the HAL to enable atsamd21g support
Expand All @@ -61,6 +67,8 @@ rfm = []
express = []
dma = ["atsamd-hal/dma"]
max-channels = ["dma", "atsamd-hal/max-channels"]
# Enable async support from atsamd-hal
async = ["atsamd-hal/async"]
# Enable pins for the adalogger SD card reader
adalogger = []
# Enable pins for Feather with WINC1500 wifi
Expand All @@ -72,6 +80,10 @@ use_semihosting = []
[[example]]
name = "blinky_basic"

[[example]]
name = "blinky_rtic"
required-features = ["rtic"]

[[example]]
name = "timers"

Expand Down Expand Up @@ -122,8 +134,7 @@ name = "adalogger"
required-features = ["adalogger", "usb", "sdmmc"]

[[example]]
name = "blinky_rtic"
required-features = ["rtic"]
name = "blinky_monotonic"

[[example]]
name = "uart"
Expand All @@ -143,3 +154,27 @@ required-features = ["dma"]
[[example]]
name = "spi"
required-features = ["dma"]

[[example]]
name = "async_dmac"
required-features = ["dma", "async"]

[[example]]
name = "async_timer"
required-features = ["async"]

[[example]]
name = "async_eic"
required-features = ["async"]

[[example]]
name = "async_i2c"
required-features = ["dma", "async"]

[[example]]
name = "async_spi"
required-features = ["dma", "async"]

[[example]]
name = "async_uart"
required-features = ["dma", "async"]
73 changes: 73 additions & 0 deletions boards/feather_m0/examples/async_dmac.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
//! This example shows a safe API to
//! execute a memory-to-memory DMA transfer
#![no_std]
#![no_main]

use defmt_rtt as _;
use panic_probe as _;

atsamd_hal::bind_interrupts!(struct Irqs {
DMAC => atsamd_hal::dmac::InterruptHandler;
});

use bsp::hal;
use bsp::pac;
use feather_m0 as bsp;
use hal::{
clock::GenericClockController,
dmac::{DmaController, PriorityLevel, TriggerAction, TriggerSource},
};

#[embassy_executor::main]
async fn main(_s: embassy_executor::Spawner) {
let mut peripherals = pac::Peripherals::take().unwrap();
let _core = pac::CorePeripherals::take().unwrap();

let _clocks = GenericClockController::with_external_32kosc(
peripherals.gclk,
&mut peripherals.pm,
&mut peripherals.sysctrl,
&mut peripherals.nvmctrl,
);

// Initialize DMA Controller
let dmac = DmaController::init(peripherals.dmac, &mut peripherals.pm);

// Turn dmac into an async controller
let mut dmac = dmac.into_future(crate::Irqs);
// Get individual handles to DMA channels
let channels = dmac.split();

// Initialize DMA Channel 0
let mut channel = channels.0.init(PriorityLevel::Lvl0);

let mut source = [0xff; 100];
let mut dest = [0x0; 100];

defmt::info!(
"Launching a DMA transfer.\n\tSource: {:#x}\n\tDestination: {:#x}",
&source,
&dest
);

channel
.transfer_future(
&mut source,
&mut dest,
TriggerSource::Disable,
TriggerAction::Block,
)
.await
.unwrap();

defmt::info!(
"Finished DMA transfer.\n\tSource: {:#x}\n\tDestination: {:#x}",
&source,
&dest
);

loop {
cortex_m::asm::wfi();
}
}
60 changes: 60 additions & 0 deletions boards/feather_m0/examples/async_eic.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#![no_std]
#![no_main]

use defmt_rtt as _;
use panic_probe as _;

use bsp::pac;
use bsp::{hal, pin_alias};
use feather_m0 as bsp;
use hal::{
clock::{enable_internal_32kosc, ClockGenId, ClockSource, GenericClockController},
ehal::digital::StatefulOutputPin,
eic::{
pin::{ExtInt2, Sense},
EIC,
},
gpio::{Pin, PullUpInterrupt},
};

atsamd_hal::bind_interrupts!(struct Irqs {
EIC => atsamd_hal::eic::InterruptHandler;
});

#[embassy_executor::main]
async fn main(_s: embassy_executor::Spawner) {
let mut peripherals = pac::Peripherals::take().unwrap();
let _core = pac::CorePeripherals::take().unwrap();

let mut clocks = GenericClockController::with_external_32kosc(
peripherals.gclk,
&mut peripherals.pm,
&mut peripherals.sysctrl,
&mut peripherals.nvmctrl,
);
let pins = bsp::Pins::new(peripherals.port);
let mut red_led: bsp::RedLed = pin_alias!(pins.red_led).into();

let _internal_clock = clocks
.configure_gclk_divider_and_source(ClockGenId::Gclk2, 1, ClockSource::Osc32k, false)
.unwrap();
clocks.configure_standby(ClockGenId::Gclk2, true);

enable_internal_32kosc(&mut peripherals.sysctrl);

// Configure a clock for the EIC peripheral
let gclk2 = clocks.get_gclk(ClockGenId::Gclk2).unwrap();
let eic_clock = clocks.eic(&gclk2).unwrap();

let mut eic = EIC::init(&mut peripherals.pm, eic_clock, peripherals.eic).into_future(Irqs);
let button: Pin<_, PullUpInterrupt> = pins.d10.into();
let mut extint = ExtInt2::new(button, &mut eic);
extint.enable_interrupt_wake();

loop {
// Here we show straight falling edge detection without
extint.wait(Sense::Fall).await;
defmt::info!("Falling edge detected");
red_led.toggle().unwrap();
}
}
74 changes: 74 additions & 0 deletions boards/feather_m0/examples/async_i2c.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#![no_std]
#![no_main]

use defmt_rtt as _;
use panic_probe as _;

use bsp::hal;
use bsp::pac;
use feather_m0 as bsp;
use fugit::MillisDuration;
use hal::ehal_async::i2c::I2c;
use hal::{
clock::GenericClockController,
dmac::{DmaController, PriorityLevel},
prelude::*,
sercom::{i2c, Sercom3},
};
use rtic_monotonics::systick::Systick;

atsamd_hal::bind_interrupts!(struct Irqs {
SERCOM3 => atsamd_hal::sercom::i2c::InterruptHandler<Sercom3>;
DMAC => atsamd_hal::dmac::InterruptHandler;
});

#[embassy_executor::main]
async fn main(_s: embassy_executor::Spawner) {
let mut peripherals = pac::Peripherals::take().unwrap();
let _core = pac::CorePeripherals::take().unwrap();

let mut clocks = GenericClockController::with_external_32kosc(
peripherals.gclk,
&mut peripherals.pm,
&mut peripherals.sysctrl,
&mut peripherals.nvmctrl,
);

let pins = bsp::Pins::new(peripherals.port);

// Take SDA and SCL
let (sda, scl) = (pins.sda, pins.scl);
let i2c_sercom = bsp::periph_alias!(peripherals.i2c_sercom);

// Initialize DMA Controller
let dmac = DmaController::init(peripherals.dmac, &mut peripherals.pm);

// Turn dmac into an async controller
let mut dmac = dmac.into_future(Irqs);
// Get individual handles to DMA channels
let channels = dmac.split();

// Initialize DMA Channel 0
let channel0 = channels.0.init(PriorityLevel::Lvl0);

let gclk0 = clocks.gclk0();
let sercom3_clock = &clocks.sercom3_core(&gclk0).unwrap();
let pads = i2c::Pads::new(sda, scl);
let mut i2c = i2c::Config::new(&peripherals.pm, i2c_sercom, pads, sercom3_clock.freq())
.baud(100.kHz())
.enable()
.into_future(Irqs)
.with_dma_channel(channel0);

loop {
defmt::info!("Sending 0x00 to I2C device...");
// This test is based on the BMP388 barometer. Feel free to use any I2C
// peripheral you have on hand.
i2c.write(0x76, &[0x00]).await.unwrap();

let mut buffer = [0xff; 4];
i2c.read(0x76, &mut buffer).await.unwrap();
defmt::info!("Read buffer: {:#x}", buffer);
Systick::delay(MillisDuration::<u32>::from_ticks(500).convert()).await;
}
}
Loading

0 comments on commit 672d534

Please sign in to comment.