forked from stm32-rs/stm32h7xx-hal
-
Notifications
You must be signed in to change notification settings - Fork 0
/
spi-dma-rtic.rs
174 lines (147 loc) · 5.53 KB
/
spi-dma-rtic.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
//! Demo for STM32H747I-NUCLEO eval board using the Real Time Interrupt-driven Concurrency (RTIC)
//! framework.
//!
//! This example demonstrates using DMA to write data over a TX-only SPI interface.
#![deny(warnings)]
#![allow(clippy::type_complexity)]
#![no_main]
#![no_std]
use core::mem::MaybeUninit;
#[macro_use]
mod utilities;
// The number of bytes to transfer.
const BUFFER_SIZE: usize = 100;
// DMA1/DMA2 cannot interact with our stack. Instead, buffers for use with the
// DMA must be placed somewhere that DMA1/DMA2 can access. In this case we use
// AXI SRAM.
//
// The runtime does not initialise these SRAM banks
#[link_section = ".axisram.buffers"]
static mut BUFFER: MaybeUninit<[u8; BUFFER_SIZE]> = MaybeUninit::uninit();
#[rtic::app(device = stm32h7xx_hal::stm32, peripherals = true)]
mod app {
use hal::prelude::*;
use stm32h7xx_hal as hal;
use super::*;
#[shared]
struct SharedResources {
cs: hal::gpio::gpiob::PB12<hal::gpio::Output<hal::gpio::PushPull>>,
transfer: hal::dma::Transfer<
hal::dma::dma::Stream1<hal::stm32::DMA1>,
hal::spi::Spi<hal::stm32::SPI2, hal::spi::Disabled, u8>,
hal::dma::MemoryToPeripheral,
&'static mut [u8; BUFFER_SIZE],
hal::dma::DBTransfer,
>,
}
#[local]
struct LocalResources {}
#[init]
fn init(
ctx: init::Context,
) -> (SharedResources, LocalResources, init::Monotonics) {
utilities::logger::init();
// Initialise power...
let pwr = ctx.device.PWR.constrain();
let pwrcfg = example_power!(pwr).freeze();
// Initialise clocks...
let rcc = ctx.device.RCC.constrain();
let ccdr = rcc
.sys_ck(200.MHz())
.hclk(200.MHz())
.pll1_q_ck(200.MHz())
.freeze(pwrcfg, &ctx.device.SYSCFG);
let gpiob = ctx.device.GPIOB.split(ccdr.peripheral.GPIOB);
// Initialize a SPI transmitter on SPI2.
let spi = {
let mosi = gpiob
.pb15
.into_alternate()
.speed(hal::gpio::Speed::VeryHigh);
let sck = gpiob
.pb10
.into_alternate()
.speed(hal::gpio::Speed::VeryHigh);
let config = hal::spi::Config::new(hal::spi::MODE_0)
.communication_mode(hal::spi::CommunicationMode::Transmitter);
let spi: hal::spi::Spi<_, _, u8> = ctx.device.SPI2.spi(
(sck, hal::spi::NoMiso, mosi),
config,
3.MHz(),
ccdr.peripheral.SPI2,
&ccdr.clocks,
);
spi.disable()
};
let mut cs = gpiob
.pb12
.into_push_pull_output()
.speed(hal::gpio::Speed::VeryHigh);
cs.set_high();
// Initialize our transmit buffer.
let buffer: &'static mut [u8; BUFFER_SIZE] = {
let buf: &mut [MaybeUninit<u8>; BUFFER_SIZE] = unsafe {
&mut *(core::ptr::addr_of_mut!(BUFFER)
as *mut [MaybeUninit<u8>; BUFFER_SIZE])
};
for (i, value) in buf.iter_mut().enumerate() {
unsafe {
value.as_mut_ptr().write(i as u8 + 0x60); // 0x60, 0x61, 0x62...
}
}
unsafe { BUFFER.assume_init_mut() }
};
let streams = hal::dma::dma::StreamsTuple::new(
ctx.device.DMA1,
ccdr.peripheral.DMA1,
);
// Configure the DMA stream to increment the memory address and generate a transfer complete
// interrupt so we know when transmission is done.
let config = hal::dma::dma::DmaConfig::default()
.memory_increment(true)
.transfer_complete_interrupt(true);
let transfer: hal::dma::Transfer<
_,
_,
hal::dma::MemoryToPeripheral,
_,
_,
> = hal::dma::Transfer::init(streams.1, spi, buffer, None, config);
(
SharedResources { cs, transfer },
LocalResources {},
init::Monotonics(),
)
}
#[task(binds=DMA1_STR1, shared = [transfer, cs], priority=2)]
fn dma_complete(ctx: dma_complete::Context) {
// If desired, the transfer can scheduled again here to continue transmitting.
(ctx.shared.transfer, ctx.shared.cs).lock(|transfer, cs| {
transfer.clear_transfer_complete_interrupt();
transfer.pause(|spi| {
// At this point, the DMA transfer is done, but the data is still in the SPI output
// FIFO. Wait for it to complete before disabling CS.
while spi.inner().sr.read().txc().bit_is_clear() {}
cs.set_high();
});
});
}
#[idle(shared = [transfer, cs])]
fn idle(ctx: idle::Context) -> ! {
// Start the DMA transfer over SPI.
(ctx.shared.transfer, ctx.shared.cs).lock(|transfer, cs| {
transfer.start(|spi| {
// Set CS low for the transfer.
cs.set_low();
// Enable TX DMA support, enable the SPI peripheral, and start the transaction.
spi.enable_dma_tx();
spi.inner_mut().cr1.modify(|_, w| w.spe().enabled());
spi.inner_mut().cr1.modify(|_, w| w.cstart().started());
// The transaction immediately begins as the TX FIFO is now being filled by DMA.
});
});
loop {
cortex_m::asm::nop();
}
}
}