Skip to content

Commit

Permalink
examples: Add DMA examples for SPI, I2C, and UART to Tier 1 BSPs
Browse files Browse the repository at this point in the history
  • Loading branch information
jbeaurivage committed Nov 7, 2024
1 parent 220d135 commit ebbe7d1
Show file tree
Hide file tree
Showing 20 changed files with 788 additions and 218 deletions.
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
83 changes: 83 additions & 0 deletions boards/feather_m0/examples/spi.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
//! 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
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

0 comments on commit ebbe7d1

Please sign in to comment.