Skip to content

Commit d32befd

Browse files
committed
spi: Enable SPI DMA transfers
1 parent 564a91b commit d32befd

File tree

5 files changed

+369
-0
lines changed

5 files changed

+369
-0
lines changed

examples/spi-dma.rs

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
#![deny(warnings)]
2+
#![no_main]
3+
#![no_std]
4+
5+
mod utilities;
6+
7+
use core::mem::MaybeUninit;
8+
9+
use cortex_m_rt::entry;
10+
use cortex_m_semihosting::debug;
11+
use stm32h5xx_hal::{
12+
pac,
13+
prelude::*,
14+
spi::{self, Config as SpiConfig, Spi, dma::DuplexDmaTransfer},
15+
};
16+
17+
static mut SOURCE_BYTES: MaybeUninit<[u8; 40]> = MaybeUninit::uninit();
18+
static mut DEST_BYTES: MaybeUninit<[u8; 40]> = MaybeUninit::zeroed();
19+
20+
fn u8_to_u8_sequential() -> (&'static [u8; 40], &'static mut [u8; 40]) {
21+
let buf: &mut [MaybeUninit<u8>; 40] = unsafe {
22+
&mut *(core::ptr::addr_of_mut!(SOURCE_BYTES)
23+
as *mut [MaybeUninit<u8>; 40])
24+
};
25+
26+
for (i, value) in buf.iter_mut().enumerate() {
27+
unsafe {
28+
value.as_mut_ptr().write(i as u8);
29+
}
30+
}
31+
#[allow(static_mut_refs)] // TODO: Fix this
32+
let src = unsafe { SOURCE_BYTES.assume_init_ref() };
33+
34+
let dest =
35+
unsafe { (*core::ptr::addr_of_mut!(DEST_BYTES)).assume_init_mut() };
36+
37+
dest.fill(0);
38+
39+
(src, dest)
40+
}
41+
42+
#[entry]
43+
fn main() -> ! {
44+
utilities::logger::init();
45+
46+
let dp = pac::Peripherals::take().unwrap();
47+
48+
// Constrain and Freeze power
49+
log::info!("Setup PWR... ");
50+
let pwr = dp.PWR.constrain();
51+
let pwrcfg = pwr.freeze();
52+
53+
// Constrain and Freeze clock
54+
log::info!("Setup RCC... ");
55+
let rcc = dp.RCC.constrain();
56+
let ccdr = rcc
57+
.sys_ck(192.MHz())
58+
.pll1_q_ck(64.MHz())
59+
.freeze(pwrcfg, &dp.SBS);
60+
61+
// Acquire the GPIOB peripheral. This also enables the clock for
62+
// GPIOB in the RCC register.
63+
let gpiob = dp.GPIOB.split(ccdr.peripheral.GPIOB);
64+
65+
let sck = gpiob.pb13.into_alternate();
66+
let miso = gpiob.pb14.into_alternate();
67+
let mosi = gpiob.pb15.into_alternate();
68+
69+
log::info!("stm32h5xx-hal example - SPI DMA");
70+
71+
// Initialise the SPI peripheral.
72+
let mut spi: Spi<_, u8> = dp.SPI2.spi(
73+
(sck, miso, mosi),
74+
SpiConfig::new(spi::MODE_0),
75+
1.MHz(),
76+
ccdr.peripheral.SPI2,
77+
&ccdr.clocks,
78+
);
79+
80+
let (source_buf, dest_buf) = u8_to_u8_sequential();
81+
82+
let channels = dp.GPDMA1.channels(ccdr.peripheral.GPDMA1);
83+
let tx_ch = channels.0;
84+
let rx_ch = channels.1;
85+
86+
let mut transfer = DuplexDmaTransfer::new(&mut spi, tx_ch, rx_ch, source_buf, dest_buf);
87+
transfer.start().unwrap();
88+
transfer.wait_for_complete().unwrap();
89+
let (_, _, source_buf, dest_buf) = transfer.free().unwrap();
90+
91+
assert_eq!(source_buf, dest_buf);
92+
93+
log::info!("Success!");
94+
loop {
95+
debug::exit(debug::EXIT_SUCCESS)
96+
}
97+
}

src/spi.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,13 +141,15 @@ pub use embedded_hal::spi::{
141141
Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3,
142142
};
143143

144+
use crate::gpdma::Error as DmaError;
144145
use crate::rcc::{CoreClocks, ResetEnable};
145146
use crate::stm32::spi1;
146147

147148
use crate::time::Hertz;
148149
use spi1::{cfg1::MBR, cfg2::LSBFRST, cfg2::SSIOP};
149150

150151
mod config;
152+
pub mod dma;
151153
mod hal;
152154
pub mod nonblocking;
153155
mod spi_def;
@@ -182,6 +184,14 @@ pub enum Error {
182184
/// Caller makes invalid call (e.g. write in SimplexReceiver mode, or read in
183185
/// SimplexTransmitter)
184186
InvalidOperation,
187+
/// A DMA error occurred during processing
188+
DmaError(DmaError)
189+
}
190+
191+
impl From<DmaError> for Error {
192+
fn from(error: DmaError) -> Self {
193+
Error::DmaError(error)
194+
}
185195
}
186196

187197
pub trait Pins<SPI> {
@@ -252,6 +262,12 @@ pub trait Instance:
252262

253263
#[doc(hidden)]
254264
fn rec() -> Self::Rec;
265+
266+
#[doc(hidden)]
267+
fn tx_dma_request() -> u8;
268+
269+
#[doc(hidden)]
270+
fn rx_dma_request() -> u8;
255271
}
256272

257273
pub trait Word: Copy + Default + 'static + crate::Sealed {
@@ -564,6 +580,29 @@ impl<SPI: Instance, W: Word> Inner<SPI, W> {
564580
let _ = self.spi.sr().read();
565581
}
566582

583+
/// Disable DMA for both Rx and Tx
584+
#[inline]
585+
pub fn enable_tx_dma(&self) {
586+
self.spi.cfg1().modify(|_, w| w.txdmaen().disabled());
587+
}
588+
589+
/// Disable DMA for both Rx and Tx
590+
#[inline]
591+
pub fn enable_rx_dma(&self) {
592+
self.spi.cfg1().modify(|_, w| w.rxdmaen().disabled());
593+
}
594+
595+
/// Disable DMA for both Rx and Tx
596+
#[inline]
597+
pub fn disable_dma(&self) {
598+
self.spi.cfg1().modify(|_, w| w.rxdmaen().disabled().txdmaen().disabled());
599+
}
600+
601+
#[inline]
602+
pub fn start_transfer(&self) {
603+
self.spi.cr1().modify(|_, w| w.cstart().started());
604+
}
605+
567606
/// Read a single word from the receive data register
568607
#[inline(always)]
569608
fn read_data_reg(&mut self) -> W {

src/spi/dma.rs

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
use core::marker::PhantomData;
2+
3+
use embedded_dma::{ReadBuffer, WriteBuffer};
4+
5+
use crate::gpdma::{
6+
config::{Config as DmaConfig, MemoryToPeripheral, PeripheralToMemory},
7+
Channel, Transfer as DmaTransfer, Word,
8+
};
9+
10+
use super::{Error, FrameSize, Instance, Spi};
11+
12+
pub struct DmaRx<SPI, W> {
13+
_spi: PhantomData<SPI>,
14+
_word: PhantomData<W>,
15+
}
16+
17+
impl<SPI, W> DmaRx<SPI, W> {
18+
fn new() -> Self {
19+
Self {
20+
_spi: PhantomData,
21+
_word: PhantomData,
22+
}
23+
}
24+
}
25+
26+
unsafe impl<SPI: Instance, W: FrameSize> ReadBuffer for DmaRx<SPI, W> {
27+
type Word = W;
28+
29+
unsafe fn read_buffer(&self) -> (*const Self::Word, usize) {
30+
((*SPI::ptr()).rxdr().as_ptr() as *const W, 1)
31+
}
32+
}
33+
34+
pub struct DmaTx<SPI, W> {
35+
_spi: PhantomData<SPI>,
36+
_word: PhantomData<W>,
37+
}
38+
39+
impl<SPI, W> DmaTx<SPI, W> {
40+
fn new() -> Self {
41+
Self {
42+
_spi: PhantomData,
43+
_word: PhantomData,
44+
}
45+
}
46+
}
47+
48+
unsafe impl<SPI: Instance, W: FrameSize> WriteBuffer for DmaTx<SPI, W> {
49+
type Word = W;
50+
51+
unsafe fn write_buffer(&mut self) -> (*mut Self::Word, usize) {
52+
((*SPI::ptr()).txdr().as_ptr() as *mut W, 1)
53+
}
54+
}
55+
56+
pub struct RxDmaTransfer<SPI, W: FrameSize, CH, D> {
57+
spi: Spi<SPI, W>,
58+
transfer: DmaTransfer<CH, DmaRx<SPI, W>, D, PeripheralToMemory>,
59+
}
60+
61+
impl<SPI, W, CH, D> RxDmaTransfer<SPI, W, CH, D>
62+
where
63+
SPI: Instance,
64+
W: FrameSize + Word,
65+
CH: Channel,
66+
D: WriteBuffer<Word = W>,
67+
{
68+
pub fn new(spi: Spi<SPI, W>, channel: CH, destination: D) -> Self {
69+
let config = DmaConfig::new().with_request(SPI::rx_dma_request());
70+
let source = DmaRx::new();
71+
let transfer = DmaTransfer::peripheral_to_memory(
72+
config,
73+
channel,
74+
source,
75+
destination,
76+
);
77+
Self { spi, transfer }
78+
}
79+
80+
pub fn start(&mut self) -> Result<(), crate::gpdma::Error> {
81+
self.spi.enable_rx_dma();
82+
self.transfer.start_with(|_, _| {
83+
self.spi.enable();
84+
self.spi.start_transfer();
85+
})
86+
}
87+
}
88+
89+
pub struct TxDmaTransfer<SPI, W: FrameSize, CH, S> {
90+
spi: Spi<SPI, W>,
91+
transfer: DmaTransfer<CH, S, DmaTx<SPI, W>, MemoryToPeripheral>,
92+
}
93+
94+
impl<SPI, W, CH, S> TxDmaTransfer<SPI, W, CH, S>
95+
where
96+
SPI: Instance,
97+
W: FrameSize + Word,
98+
CH: Channel,
99+
S: ReadBuffer<Word = W>,
100+
{
101+
pub fn new(spi: Spi<SPI, W>, channel: CH, source: S) -> Self {
102+
let config = DmaConfig::new().with_request(SPI::tx_dma_request());
103+
let destination = DmaTx::new();
104+
let transfer = DmaTransfer::memory_to_peripheral(
105+
config,
106+
channel,
107+
source,
108+
destination,
109+
);
110+
Self { spi, transfer }
111+
}
112+
113+
pub fn start(&mut self) -> Result<(), crate::gpdma::Error> {
114+
self.transfer.start_with(|_, _| {
115+
self.spi.enable_tx_dma();
116+
self.spi.enable();
117+
self.spi.start_transfer();
118+
})
119+
}
120+
}
121+
122+
pub struct DuplexDmaTransfer<'a, SPI, W: FrameSize, TX, RX, S, D> {
123+
spi: &'a mut Spi<SPI, W>,
124+
tx_transfer: DmaTransfer<TX, S, DmaTx<SPI, W>, MemoryToPeripheral>,
125+
rx_transfer: DmaTransfer<RX, DmaRx<SPI, W>, D, PeripheralToMemory>,
126+
}
127+
128+
impl<'a, SPI, W, RX, TX, S, D> DuplexDmaTransfer<'a, SPI, W, TX, RX, S, D>
129+
where
130+
SPI: Instance,
131+
W: FrameSize + Word,
132+
TX: Channel,
133+
RX: Channel,
134+
S: ReadBuffer<Word = W>,
135+
D: WriteBuffer<Word = W>,
136+
{
137+
pub fn new(
138+
spi: &'a mut Spi<SPI, W>,
139+
tx_channel: TX,
140+
rx_channel: RX,
141+
source: S,
142+
destination: D,
143+
) -> Self {
144+
let tx_config = DmaConfig::new().with_request(SPI::tx_dma_request());
145+
let tx_destination = DmaTx::new();
146+
let tx_transfer = DmaTransfer::memory_to_peripheral(
147+
tx_config,
148+
tx_channel,
149+
source,
150+
tx_destination,
151+
);
152+
let rx_source = DmaRx::new();
153+
let rx_config = DmaConfig::new().with_request(SPI::rx_dma_request());
154+
let rx_transfer = DmaTransfer::peripheral_to_memory(
155+
rx_config,
156+
rx_channel,
157+
rx_source,
158+
destination,
159+
);
160+
Self {
161+
spi,
162+
tx_transfer,
163+
rx_transfer,
164+
}
165+
}
166+
167+
pub fn start(&mut self) -> Result<(), Error> {
168+
self.spi.enable_rx_dma();
169+
self.rx_transfer.start()?;
170+
self.tx_transfer.start_with(|_, _| {
171+
self.spi.enable_tx_dma();
172+
self.spi.enable();
173+
self.spi.start_transfer();
174+
})?;
175+
Ok(())
176+
}
177+
178+
pub fn is_dma_complete(&self) -> Result<bool, Error> {
179+
let complete = self.tx_transfer.is_transfer_complete()?
180+
&& self.rx_transfer.is_transfer_complete()?;
181+
Ok(complete)
182+
}
183+
184+
pub fn wait_for_complete(&mut self) -> Result<(), Error> {
185+
while !self.is_dma_complete()? {}
186+
self.spi.end_transaction();
187+
self.spi.disable_dma();
188+
Ok(())
189+
}
190+
191+
pub fn end_transfer(&mut self) {
192+
self.spi.end_transaction();
193+
self.spi.disable_dma();
194+
}
195+
196+
pub fn free(self) -> Result<(TX, RX, S, D), Error> {
197+
let (tx, s, _) = self.tx_transfer.free()?;
198+
let (rx, _, d) = self.rx_transfer.free()?;
199+
Ok((tx, rx, s, d))
200+
}
201+
}
202+
203+
type DuplexInplaceDmaTransfer<'a, SPI, W, TX, RX> =
204+
DuplexDmaTransfer<'a, SPI, W, TX, RX, &'static [W], &'static mut [W]>;
205+
206+
impl<SPI: Instance, W: FrameSize + Word> Spi<SPI, W> {
207+
pub fn dma_transfer_inplace<TX: Channel, RX: Channel>(
208+
&mut self,
209+
buffer: &'static mut [W],
210+
tx_channel: TX,
211+
rx_channel: RX,
212+
) -> Result<DuplexInplaceDmaTransfer<SPI, W, TX, RX>, Error> {
213+
// Note (unsafe): Data will be read from the start of the buffer before data is written
214+
// to those locations just like for blocking non-DMA in-place transfers
215+
let source = unsafe { *(buffer.as_ptr() as *const &[W]) };
216+
let mut transfer = DuplexDmaTransfer::new(
217+
self, tx_channel, rx_channel, source, buffer,
218+
);
219+
transfer.start()?;
220+
Ok(transfer)
221+
}
222+
}

0 commit comments

Comments
 (0)