Skip to content

Commit fb254b5

Browse files
committed
examples: Add DMA examples for SPI, I2C, and UART to Tier 1 BSPs
1 parent e46f094 commit fb254b5

File tree

20 files changed

+792
-218
lines changed

20 files changed

+792
-218
lines changed

boards/atsame54_xpro/Cargo.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,17 @@ rtt-target = { version = "0.3", features = ["cortex-m"] }
3939

4040
[features]
4141
default = ["rt", "atsamd-hal/same54p"]
42+
dma = ["atsamd-hal/dma"]
4243
rt = ["cortex-m-rt", "atsamd-hal/same54p-rt"]
4344
usb = ["atsamd-hal/usb", "usb-device"]
4445
can = ["atsamd-hal/can"]
4546

47+
[[example]]
48+
name = "blinky_basic"
49+
50+
[[example]]
51+
name = "blinky_rtic"
52+
4653
[[example]]
4754
name = "mcan"
4855
required-features = ["can"]

boards/feather_m0/Cargo.toml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,19 @@ required-features = ["rtic"]
127127

128128
[[example]]
129129
name = "uart"
130+
131+
[[example]]
132+
name = "uart_dma_nonblocking"
133+
required-features = ["dma"]
134+
135+
[[example]]
136+
name = "uart_dma_blocking"
130137
required-features = ["dma"]
131138

132139
[[example]]
133140
name = "i2c"
134141
required-features = ["dma"]
142+
143+
[[example]]
144+
name = "spi"
145+
required-features = ["dma"]

boards/feather_m0/examples/i2c.rs

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
//! This example showcases the i2c module.
1+
//! This example showcases the i2c module, and uses DMA to perform I2C
2+
//! transactions.
23
34
#![no_std]
45
#![no_main]
@@ -23,8 +24,9 @@ use hal::ehal::i2c::I2c;
2324
use hal::fugit::RateExtU32;
2425
use hal::sercom::i2c;
2526

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

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

51-
let buf_src: &'static mut [u8; LENGTH] =
52-
cortex_m::singleton!(: [u8; LENGTH] = [0x00; LENGTH]).unwrap();
53-
let buf_dest: &'static mut [u8; LENGTH] =
54-
cortex_m::singleton!(: [u8; LENGTH] = [0x00; LENGTH]).unwrap();
55-
5653
let gclk0 = clocks.gclk0();
5754
let sercom3_clock = &clocks.sercom3_core(&gclk0).unwrap();
5855
let pads = i2c::Pads::new(sda, scl);
5956
let mut i2c = i2c::Config::new(&pm, peripherals.sercom3, pads, sercom3_clock.freq())
6057
.baud(100.kHz())
61-
.enable();
58+
.enable()
59+
.with_dma_channel(chan0);
6260

63-
let mut buffer = [0x00; 1];
61+
let mut received = [0x00; 1];
6462

6563
// Test writing then reading from an I2C chip
66-
i2c.write_read(ADDRESS, &[0x00], &mut buffer).unwrap();
67-
68-
// Test writing then reading using DMA
69-
let init_token = i2c.init_dma_transfer().unwrap();
70-
let xfer = i2c.send_with_dma(ADDRESS, init_token, buf_src, chan0, |_| {});
71-
let (chan0, _buf_src, mut i2c) = xfer.wait();
72-
73-
let init_token = i2c.init_dma_transfer().unwrap();
74-
let xfer = i2c.receive_with_dma(ADDRESS, init_token, buf_dest, chan0, |_| {});
75-
let (_chan0, _i2c, _buf_dest) = xfer.wait();
64+
i2c.write_read(ADDRESS, &[0x00; 8], &mut received).unwrap();
7665

7766
loop {
7867
// Go to sleep

boards/feather_m0/examples/spi.rs

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
//! This example showcases the spi module, and uses DMA to perform SPI
2+
//! transactions.
3+
4+
#![no_std]
5+
#![no_main]
6+
7+
#[cfg(not(feature = "use_semihosting"))]
8+
use panic_halt as _;
9+
#[cfg(feature = "use_semihosting")]
10+
use panic_semihosting as _;
11+
12+
use feather_m0 as bsp;
13+
14+
use bsp::entry;
15+
use bsp::hal;
16+
use bsp::pac;
17+
18+
use pac::Peripherals;
19+
20+
use hal::clock::GenericClockController;
21+
use hal::dmac::{DmaController, PriorityLevel};
22+
use hal::ehal::spi::SpiBus;
23+
use hal::fugit::RateExtU32;
24+
25+
#[entry]
26+
fn main() -> ! {
27+
let mut peripherals = Peripherals::take().unwrap();
28+
let mut clocks = GenericClockController::with_internal_32kosc(
29+
peripherals.gclk,
30+
&mut peripherals.pm,
31+
&mut peripherals.sysctrl,
32+
&mut peripherals.nvmctrl,
33+
);
34+
35+
let mut pm = peripherals.pm;
36+
let dmac = peripherals.dmac;
37+
let pins = bsp::Pins::new(peripherals.port);
38+
39+
// Take SPI pins
40+
let (miso, mosi, sclk) = (pins.miso, pins.mosi, pins.sclk);
41+
42+
// Setup DMA channels for later use
43+
let mut dmac = DmaController::init(dmac, &mut pm);
44+
let channels = dmac.split();
45+
let chan0 = channels.0.init(PriorityLevel::Lvl0);
46+
let chan1 = channels.1.init(PriorityLevel::Lvl0);
47+
48+
// Create a Spi with DMA enabled
49+
let mut spi = bsp::spi_master(
50+
&mut clocks,
51+
100.kHz(),
52+
peripherals.sercom4,
53+
&mut pm,
54+
sclk,
55+
mosi,
56+
miso,
57+
)
58+
.with_dma_channels(chan0, chan1);
59+
60+
loop {
61+
let mut source = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
62+
let mut dest = [0xff; 16];
63+
64+
// Read words into a buffer. The words sent will be be NOP word
65+
// (by default, 0x00).
66+
spi.read(&mut dest).unwrap();
67+
68+
// Send words from a buffer
69+
spi.write(&source).unwrap();
70+
71+
// Simultaneously read and write from different buffers.
72+
//
73+
// If the source is longer than the destination, the words read
74+
// in excess will be discarded.
75+
//
76+
// If the destination is longer than the source, the excess words
77+
// sent will be the NOP word (by default, 0x00).
78+
spi.transfer(&mut dest, &source).unwrap();
79+
80+
// Simultaneously read and write from the same buffer
81+
// Cannot use DMA for this method, so it reverts to word by word transfers.
82+
spi.transfer_in_place(&mut source).unwrap();
83+
}
84+
}

boards/feather_m0/examples/uart.rs

Lines changed: 17 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
//! This example showcases the uart module.
1+
//! This example shows how to use the UART to perform transfers using the
2+
//! embedded-hal-nb traits.
23
34
#![no_std]
45
#![no_main]
56

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

1717
use bsp::{entry, periph_alias, pin_alias};
1818
use hal::clock::GenericClockController;
19-
use hal::dmac::{DmaController, PriorityLevel};
2019
use hal::ehal_nb::serial::{Read, Write};
2120
use hal::fugit::RateExtU32;
2221

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

3534
let mut pm = peripherals.pm;
36-
let dmac = peripherals.dmac;
3735
let pins = bsp::Pins::new(peripherals.port);
3836

39-
// Setup DMA channels for later use
40-
let mut dmac = DmaController::init(dmac, &mut pm);
41-
let channels = dmac.split();
42-
43-
let chan0 = channels.0.init(PriorityLevel::Lvl0);
44-
let chan1 = channels.1.init(PriorityLevel::Lvl0);
45-
4637
// Take peripheral and pins
4738
let uart_sercom = periph_alias!(peripherals.uart_sercom);
4839
let uart_rx = pin_alias!(pins.uart_rx);
@@ -61,47 +52,27 @@ fn main() -> ! {
6152
// Split uart in rx + tx halves
6253
let (mut rx, mut tx) = uart.split();
6354

64-
// Get a 50 byte buffer to store data to send/receive
65-
const LENGTH: usize = 50;
66-
let rx_buffer: &'static mut [u8; LENGTH] =
67-
cortex_m::singleton!(: [u8; LENGTH] = [0x00; LENGTH]).unwrap();
68-
let tx_buffer: &'static mut [u8; LENGTH] =
69-
cortex_m::singleton!(: [u8; LENGTH] = [0x00; LENGTH]).unwrap();
55+
// Make buffers to store data to send/receive
56+
let mut rx_buffer = [0x00; 50];
57+
let mut tx_buffer = [0x00; 50];
7058

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

76-
// Send data in a blocking way
77-
for c in tx_buffer.iter() {
78-
nb::block!(tx.write(*c)).unwrap();
79-
}
80-
81-
// We'll now receive data in a blocking way
82-
rx.flush_rx_buffer();
83-
for c in rx_buffer.iter_mut() {
84-
*c = nb::block!(rx.read()).unwrap();
85-
}
86-
87-
// Finally, we'll receive AND send data at the same time with DMA
88-
89-
// Setup a DMA transfer to send our data asynchronously.
90-
// We'll set the waker to be a no-op
91-
let tx_dma = tx.send_with_dma(tx_buffer, chan0, |_| {});
92-
93-
// Setup a DMA transfer to receive our data asynchronously.
94-
// Again, we'll set the waker to be a no-op
95-
let rx_dma = rx.receive_with_dma(rx_buffer, chan1, |_| {});
96-
97-
// Wait for transmit DMA transfer to complete
98-
let (_chan0, _tx_buffer, _tx) = tx_dma.wait();
99-
100-
// Wait for receive DMA transfer to complete
101-
let (_chan1, _rx_buffer, _rx) = rx_dma.wait();
102-
10364
loop {
104-
// Go to sleep
105-
asm::wfi();
65+
// Send data. We block on each byte, but we could also perform some tasks while
66+
// waiting for the byte to finish sending.
67+
for c in tx_buffer.iter() {
68+
nb::block!(tx.write(*c)).unwrap();
69+
}
70+
71+
// Receive data. We block on each byte, but we could also perform some tasks
72+
// while waiting for the byte to finish sending.
73+
rx.flush_rx_buffer();
74+
for c in rx_buffer.iter_mut() {
75+
*c = nb::block!(rx.read()).unwrap();
76+
}
10677
}
10778
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
//! This example shows how to use the UART to perform blocking transfers using
2+
//! DMA and the embedded-io traits.
3+
4+
#![no_std]
5+
#![no_main]
6+
7+
#[cfg(not(feature = "use_semihosting"))]
8+
use panic_halt as _;
9+
#[cfg(feature = "use_semihosting")]
10+
use panic_semihosting as _;
11+
12+
use bsp::hal;
13+
use bsp::pac;
14+
use feather_m0 as bsp;
15+
16+
use bsp::{entry, periph_alias, pin_alias};
17+
use hal::clock::GenericClockController;
18+
use hal::dmac::{DmaController, PriorityLevel};
19+
use hal::embedded_io::{Read, Write};
20+
use hal::fugit::RateExtU32;
21+
22+
use pac::Peripherals;
23+
24+
#[entry]
25+
fn main() -> ! {
26+
let mut peripherals = Peripherals::take().unwrap();
27+
let mut clocks = GenericClockController::with_internal_32kosc(
28+
peripherals.gclk,
29+
&mut peripherals.pm,
30+
&mut peripherals.sysctrl,
31+
&mut peripherals.nvmctrl,
32+
);
33+
34+
let mut pm = peripherals.pm;
35+
let dmac = peripherals.dmac;
36+
let pins = bsp::Pins::new(peripherals.port);
37+
38+
// Setup DMA channels for later use
39+
let mut dmac = DmaController::init(dmac, &mut pm);
40+
let channels = dmac.split();
41+
42+
let chan0 = channels.0.init(PriorityLevel::Lvl0);
43+
let chan1 = channels.1.init(PriorityLevel::Lvl0);
44+
45+
// Take peripheral and pins
46+
let uart_sercom = periph_alias!(peripherals.uart_sercom);
47+
let uart_rx = pin_alias!(pins.uart_rx);
48+
let uart_tx = pin_alias!(pins.uart_tx);
49+
50+
// Setup UART peripheral and attach DMA channels
51+
let uart = bsp::uart(
52+
&mut clocks,
53+
9600.Hz(),
54+
uart_sercom,
55+
&mut pm,
56+
uart_rx,
57+
uart_tx,
58+
)
59+
.with_rx_channel(chan0)
60+
.with_tx_channel(chan1);
61+
62+
// Split uart in rx + tx halves
63+
let (mut rx, mut tx) = uart.split();
64+
65+
loop {
66+
// Make buffers to store data to send/receive
67+
let mut rx_buffer = [0x00; 50];
68+
let mut tx_buffer = [0x00; 50];
69+
70+
// For fun, store numbers from 0 to 49 in buffer
71+
for (i, c) in tx_buffer.iter_mut().enumerate() {
72+
*c = i as u8;
73+
}
74+
75+
// Send data using DMA...
76+
tx.write(&tx_buffer).unwrap();
77+
78+
//...and receive using DMA
79+
rx.flush_rx_buffer();
80+
rx.read(&mut rx_buffer).unwrap();
81+
}
82+
}

0 commit comments

Comments
 (0)