Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DMA-enabled embedded-hal blocking implementations #772

Merged
merged 7 commits into from
Nov 7, 2024
7 changes: 7 additions & 0 deletions boards/atsame54_xpro/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,17 @@ rtt-target = { version = "0.3", features = ["cortex-m"] }

[features]
default = ["rt", "atsamd-hal/same54p"]
dma = ["atsamd-hal/dma"]
rt = ["cortex-m-rt", "atsamd-hal/same54p-rt"]
usb = ["atsamd-hal/usb", "usb-device"]
can = ["atsamd-hal/can"]

[[example]]
name = "blinky_basic"

[[example]]
name = "blinky_rtic"

[[example]]
name = "mcan"
required-features = ["can"]
11 changes: 11 additions & 0 deletions boards/feather_m0/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,19 @@ required-features = ["rtic"]

[[example]]
name = "uart"

[[example]]
name = "uart_dma_nonblocking"
required-features = ["dma"]

[[example]]
name = "uart_dma_blocking"
required-features = ["dma"]

[[example]]
name = "i2c"
required-features = ["dma"]

[[example]]
name = "spi"
required-features = ["dma"]
29 changes: 9 additions & 20 deletions boards/feather_m0/examples/i2c.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
//! This example showcases the i2c module.
//! This example showcases the i2c module, and uses DMA to perform I2C
//! transactions.

#![no_std]
#![no_main]
Expand All @@ -23,8 +24,9 @@ use hal::ehal::i2c::I2c;
use hal::fugit::RateExtU32;
use hal::sercom::i2c;

const LENGTH: usize = 1;
const ADDRESS: u8 = 0x77;
// This example is based on the BMP388 pressure sensor. Adjust the device and
// register addresses to your liking
const ADDRESS: u8 = 0x76;

#[entry]
fn main() -> ! {
Expand All @@ -48,31 +50,18 @@ fn main() -> ! {
let channels = dmac.split();
let chan0 = channels.0.init(PriorityLevel::Lvl0);

let buf_src: &'static mut [u8; LENGTH] =
cortex_m::singleton!(: [u8; LENGTH] = [0x00; LENGTH]).unwrap();
let buf_dest: &'static mut [u8; LENGTH] =
cortex_m::singleton!(: [u8; LENGTH] = [0x00; LENGTH]).unwrap();

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(&pm, peripherals.sercom3, pads, sercom3_clock.freq())
.baud(100.kHz())
.enable();
.enable()
.with_dma_channel(chan0);

let mut buffer = [0x00; 1];
let mut received = [0x00; 1];

// Test writing then reading from an I2C chip
i2c.write_read(ADDRESS, &[0x00], &mut buffer).unwrap();

// Test writing then reading using DMA
let init_token = i2c.init_dma_transfer().unwrap();
let xfer = i2c.send_with_dma(ADDRESS, init_token, buf_src, chan0, |_| {});
let (chan0, _buf_src, mut i2c) = xfer.wait();

let init_token = i2c.init_dma_transfer().unwrap();
let xfer = i2c.receive_with_dma(ADDRESS, init_token, buf_dest, chan0, |_| {});
let (_chan0, _i2c, _buf_dest) = xfer.wait();
i2c.write_read(ADDRESS, &[0x00; 8], &mut received).unwrap();

loop {
// Go to sleep
Expand Down
84 changes: 84 additions & 0 deletions boards/feather_m0/examples/spi.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
//! This example showcases the spi module, and uses DMA to perform SPI
//! transactions.

#![no_std]
#![no_main]

#[cfg(not(feature = "use_semihosting"))]
use panic_halt as _;
#[cfg(feature = "use_semihosting")]
use panic_semihosting as _;

use feather_m0 as bsp;

use bsp::entry;
use bsp::hal;
use bsp::pac;

use pac::Peripherals;

use hal::clock::GenericClockController;
use hal::dmac::{DmaController, PriorityLevel};
use hal::ehal::spi::SpiBus;
use hal::fugit::RateExtU32;

#[entry]
fn main() -> ! {
let mut peripherals = Peripherals::take().unwrap();
let mut clocks = GenericClockController::with_internal_32kosc(
peripherals.gclk,
&mut peripherals.pm,
&mut peripherals.sysctrl,
&mut peripherals.nvmctrl,
);

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

// Take SPI pins
let (miso, mosi, sclk) = (pins.miso, pins.mosi, pins.sclk);

// Setup DMA channels for later use
let mut dmac = DmaController::init(dmac, &mut pm);
let channels = dmac.split();
let chan0 = channels.0.init(PriorityLevel::Lvl0);
let chan1 = channels.1.init(PriorityLevel::Lvl0);

// Create a Spi with DMA enabled
let mut spi = bsp::spi_master(
&mut clocks,
100.kHz(),
peripherals.sercom4,
&mut pm,
sclk,
mosi,
miso,
)
.with_dma_channels(chan0, chan1);

loop {
let mut source = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
let mut dest = [0xff; 16];

// Read words into a buffer. The words sent will be be NOP word
// (by default, 0x00).
spi.read(&mut dest).unwrap();

// Send words from a buffer
spi.write(&source).unwrap();

// Simultaneously read and write from different buffers.
//
// If the source is longer than the destination, the words read
// in excess will be discarded.
//
// If the destination is longer than the source, the excess words
// sent will be the NOP word (by default, 0x00).
spi.transfer(&mut dest, &source).unwrap();

// Simultaneously read and write from the same buffer
// Cannot use DMA for this method, so it reverts to word by word transfers.
spi.transfer_in_place(&mut source).unwrap();
}
}
63 changes: 17 additions & 46 deletions boards/feather_m0/examples/uart.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
//! This example showcases the uart module.
//! This example shows how to use the UART to perform transfers using the
//! embedded-hal-nb traits.

#![no_std]
#![no_main]

use cortex_m::asm;
#[cfg(not(feature = "use_semihosting"))]
use panic_halt as _;
#[cfg(feature = "use_semihosting")]
Expand All @@ -16,7 +16,6 @@ use hal::nb;

use bsp::{entry, periph_alias, pin_alias};
use hal::clock::GenericClockController;
use hal::dmac::{DmaController, PriorityLevel};
use hal::ehal_nb::serial::{Read, Write};
use hal::fugit::RateExtU32;

Expand All @@ -33,16 +32,8 @@ fn main() -> ! {
);

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

// Setup DMA channels for later use
let mut dmac = DmaController::init(dmac, &mut pm);
let channels = dmac.split();

let chan0 = channels.0.init(PriorityLevel::Lvl0);
let chan1 = channels.1.init(PriorityLevel::Lvl0);

// Take peripheral and pins
let uart_sercom = periph_alias!(peripherals.uart_sercom);
let uart_rx = pin_alias!(pins.uart_rx);
Expand All @@ -61,47 +52,27 @@ fn main() -> ! {
// Split uart in rx + tx halves
let (mut rx, mut tx) = uart.split();

// Get a 50 byte buffer to store data to send/receive
const LENGTH: usize = 50;
let rx_buffer: &'static mut [u8; LENGTH] =
cortex_m::singleton!(: [u8; LENGTH] = [0x00; LENGTH]).unwrap();
let tx_buffer: &'static mut [u8; LENGTH] =
cortex_m::singleton!(: [u8; LENGTH] = [0x00; LENGTH]).unwrap();
// Make buffers to store data to send/receive
let mut rx_buffer = [0x00; 50];
let mut tx_buffer = [0x00; 50];

// For fun, store numbers from 0 to 49 in buffer
for (i, c) in tx_buffer.iter_mut().enumerate() {
*c = i as u8;
}

// Send data in a blocking way
for c in tx_buffer.iter() {
nb::block!(tx.write(*c)).unwrap();
}

// We'll now receive data in a blocking way
rx.flush_rx_buffer();
for c in rx_buffer.iter_mut() {
*c = nb::block!(rx.read()).unwrap();
}

// Finally, we'll receive AND send data at the same time with DMA

// Setup a DMA transfer to send our data asynchronously.
// We'll set the waker to be a no-op
let tx_dma = tx.send_with_dma(tx_buffer, chan0, |_| {});

// Setup a DMA transfer to receive our data asynchronously.
// Again, we'll set the waker to be a no-op
let rx_dma = rx.receive_with_dma(rx_buffer, chan1, |_| {});

// Wait for transmit DMA transfer to complete
let (_chan0, _tx_buffer, _tx) = tx_dma.wait();

// Wait for receive DMA transfer to complete
let (_chan1, _rx_buffer, _rx) = rx_dma.wait();

loop {
// Go to sleep
asm::wfi();
// Send data. We block on each byte, but we could also perform some tasks while
// waiting for the byte to finish sending.
for c in tx_buffer.iter() {
nb::block!(tx.write(*c)).unwrap();
}

// Receive data. We block on each byte, but we could also perform some tasks
// while waiting for the byte to finish sending.
rx.flush_rx_buffer();
for c in rx_buffer.iter_mut() {
*c = nb::block!(rx.read()).unwrap();
}
}
}
82 changes: 82 additions & 0 deletions boards/feather_m0/examples/uart_dma_blocking.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
//! This example shows how to use the UART to perform blocking transfers using
//! DMA and the embedded-io traits.

#![no_std]
#![no_main]

#[cfg(not(feature = "use_semihosting"))]
use panic_halt as _;
#[cfg(feature = "use_semihosting")]
use panic_semihosting as _;

use bsp::hal;
use bsp::pac;
use feather_m0 as bsp;

use bsp::{entry, periph_alias, pin_alias};
use hal::clock::GenericClockController;
use hal::dmac::{DmaController, PriorityLevel};
use hal::embedded_io::{Read, Write};
use hal::fugit::RateExtU32;

use pac::Peripherals;

#[entry]
fn main() -> ! {
let mut peripherals = Peripherals::take().unwrap();
let mut clocks = GenericClockController::with_internal_32kosc(
peripherals.gclk,
&mut peripherals.pm,
&mut peripherals.sysctrl,
&mut peripherals.nvmctrl,
);

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

// Setup DMA channels for later use
let mut dmac = DmaController::init(dmac, &mut pm);
let channels = dmac.split();

let chan0 = channels.0.init(PriorityLevel::Lvl0);
let chan1 = channels.1.init(PriorityLevel::Lvl0);

// Take peripheral and pins
let uart_sercom = periph_alias!(peripherals.uart_sercom);
let uart_rx = pin_alias!(pins.uart_rx);
let uart_tx = pin_alias!(pins.uart_tx);

// Setup UART peripheral and attach DMA channels
let uart = bsp::uart(
&mut clocks,
9600.Hz(),
uart_sercom,
&mut pm,
uart_rx,
uart_tx,
)
.with_rx_channel(chan0)
.with_tx_channel(chan1);

// Split uart in rx + tx halves
let (mut rx, mut tx) = uart.split();

loop {
// Make buffers to store data to send/receive
let mut rx_buffer = [0x00; 50];
let mut tx_buffer = [0x00; 50];

// For fun, store numbers from 0 to 49 in buffer
for (i, c) in tx_buffer.iter_mut().enumerate() {
*c = i as u8;
}

// Send data using DMA...
tx.write(&tx_buffer).unwrap();

//...and receive using DMA
rx.flush_rx_buffer();
rx.read(&mut rx_buffer).unwrap();
}
}
Loading