Skip to content

Commit 26ab9e2

Browse files
committed
Add unidirectional SPI DMA
1 parent 11c726f commit 26ab9e2

File tree

3 files changed

+144
-1
lines changed

3 files changed

+144
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1212
- RCC `Bus` trait + private `Enable` and `Reset` traits
1313
- Added `micros_since` and `reset` methods to timer
1414
- Added `select_frequency` method to RTC
15+
- Unidirectional DMA support for SPI (TX only)
1516

1617
### Breaking changes
1718

examples/spi-dma.rs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#![no_std]
2+
#![no_main]
3+
4+
/**
5+
Transmits data over an SPI port using DMA
6+
*/
7+
8+
use panic_halt as _;
9+
10+
use stm32f1xx_hal::{
11+
prelude::*,
12+
pac,
13+
spi::{Spi, Mode, Polarity, Phase},
14+
};
15+
use cortex_m_rt::entry;
16+
17+
#[entry]
18+
fn main() -> ! {
19+
// Get access to the device specific peripherals from the peripheral access crate
20+
let dp = pac::Peripherals::take().unwrap();
21+
22+
// Take ownership over the raw flash and rcc devices and convert them into the corresponding
23+
// HAL structs
24+
let mut flash = dp.FLASH.constrain();
25+
let mut rcc = dp.RCC.constrain();
26+
27+
// Freeze the configuration of all the clocks in the system and store the frozen frequencies in
28+
// `clocks`
29+
let clocks = rcc.cfgr.freeze(&mut flash.acr);
30+
31+
// Acquire the GPIOA peripheral
32+
let mut gpiob = dp.GPIOB.split(&mut rcc.apb2);
33+
34+
let pins = (
35+
gpiob.pb13.into_alternate_push_pull(&mut gpiob.crh),
36+
gpiob.pb14.into_floating_input(&mut gpiob.crh),
37+
gpiob.pb15.into_alternate_push_pull(&mut gpiob.crh),
38+
);
39+
40+
let spi_mode = Mode {
41+
polarity: Polarity::IdleLow,
42+
phase: Phase::CaptureOnFirstTransition
43+
};
44+
let mut spi = Spi::spi2(
45+
dp.SPI2,
46+
pins,
47+
spi_mode,
48+
100.khz(),
49+
clocks,
50+
&mut rcc.apb1
51+
);
52+
53+
// Set up the DMA device
54+
let dma = dp.DMA1.split(&mut rcc.ahb);
55+
56+
// Connect the SPI device to the DMA
57+
let spi_dma = spi.with_tx_dma(dma.5);
58+
59+
// Start a DMA transfer
60+
let transfer = spi_dma.write(b"hello, world");
61+
62+
// Wait for it to finnish. The transfer takes ownership over the SPI device
63+
// and the data being sent anb those things are returned by transfer.wait
64+
let (_spi_dma, _buffer) = transfer.wait();
65+
66+
loop {
67+
}
68+
}

src/spi.rs

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22
33
use core::ptr;
44

5-
pub use crate::hal::spi::{Mode, Phase, Polarity};
65
use nb;
6+
7+
pub use crate::hal::spi::{Mode, Phase, Polarity};
78
use crate::pac::{SPI1, SPI2};
89

910
use crate::afio::MAPR;
@@ -12,6 +13,12 @@ use crate::gpio::gpiob::{PB13, PB14, PB15, PB3, PB4, PB5};
1213
use crate::gpio::{Alternate, Floating, Input, PushPull};
1314
use crate::rcc::{RccBus, Clocks, Enable, Reset};
1415
use crate::time::Hertz;
16+
use crate::dma::dma1::{C3, C5};
17+
use crate::dma::{Transmit, TxDma, Transfer, R, Static, TransferPayload};
18+
19+
use core::sync::atomic::{self, Ordering};
20+
21+
use as_slice::AsSlice;
1522

1623
/// SPI error
1724
#[derive(Debug)]
@@ -226,3 +233,70 @@ hal! {
226233
SPI1: (_spi1),
227234
SPI2: (_spi2),
228235
}
236+
237+
// DMA
238+
239+
pub struct SpiPayload<SPI, PINS> {
240+
spi: Spi<SPI, PINS>
241+
}
242+
243+
pub type SpiTxDma<SPI, PINS, CHANNEL> = TxDma<SpiPayload<SPI, PINS>, CHANNEL>;
244+
245+
macro_rules! spi_dma {
246+
($SPIi:ident, $TCi:ident) => {
247+
impl<PINS> Transmit for SpiTxDma<$SPIi, PINS, $TCi> {
248+
type TxChannel = $TCi;
249+
type ReceivedWord = u8;
250+
}
251+
252+
impl<PINS> Spi<$SPIi, PINS> {
253+
pub fn with_tx_dma(self, channel: $TCi) -> SpiTxDma<$SPIi, PINS, $TCi> {
254+
let payload = SpiPayload{
255+
spi: self
256+
};
257+
SpiTxDma {payload, channel}
258+
}
259+
}
260+
261+
impl<PINS> TransferPayload for SpiTxDma<$SPIi, PINS, $TCi> {
262+
fn start(&mut self) {
263+
self.payload.spi.spi.cr2.modify(|_, w| w.txdmaen().set_bit());
264+
self.channel.start();
265+
}
266+
fn stop(&mut self) {
267+
self.payload.spi.spi.cr2.modify(|_, w| w.txdmaen().clear_bit());
268+
self.channel.stop();
269+
}
270+
}
271+
272+
impl<A, B, PIN> crate::dma::WriteDma<A, B, u8> for SpiTxDma<$SPIi, PIN, $TCi>
273+
where
274+
A: AsSlice<Element=u8>,
275+
B: Static<A>
276+
{
277+
fn write(mut self, buffer: B) -> Transfer<R, B, Self> {
278+
{
279+
let buffer = buffer.borrow().as_slice();
280+
self.channel.set_peripheral_address(unsafe{ &(*$SPIi::ptr()).dr as *const _ as u32 }, false);
281+
self.channel.set_memory_address(buffer.as_ptr() as u32, true);
282+
self.channel.set_transfer_length(buffer.len());
283+
}
284+
atomic::compiler_fence(Ordering::Release);
285+
self.channel.ch().cr.modify(|_, w| { w
286+
.mem2mem() .clear_bit()
287+
.pl() .medium()
288+
.msize() .bits8()
289+
.psize() .bits8()
290+
.circ() .clear_bit()
291+
.dir() .set_bit()
292+
});
293+
self.start();
294+
295+
Transfer::r(buffer, self)
296+
}
297+
}
298+
}
299+
}
300+
301+
spi_dma!(SPI1, C3);
302+
spi_dma!(SPI2, C5);

0 commit comments

Comments
 (0)