forked from stm32-rs/stm32h7xx-hal
-
Notifications
You must be signed in to change notification settings - Fork 1
/
spi-dma-rtic.rs
172 lines (145 loc) · 5.62 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
//! 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;
use embedded_hal::digital::v2::OutputPin;
use rtic::app;
#[macro_use]
#[allow(unused)]
mod utilities;
use hal::prelude::*;
use stm32h7xx_hal as hal;
// 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();
#[app(device = stm32h7xx_hal::stm32, peripherals = true)]
const APP: () = {
struct Resources {
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,
>,
}
#[init]
fn init(ctx: init::Context) -> init::LateResources {
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_af5()
.set_speed(hal::gpio::Speed::VeryHigh);
let sck = gpiob
.pb10
.into_alternate_af5()
.set_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()
.set_speed(hal::gpio::Speed::VeryHigh);
cs.set_high().unwrap();
// Initialize our transmit buffer.
let buffer: &'static mut [u8; BUFFER_SIZE] = {
let buf: &mut [MaybeUninit<u8>; BUFFER_SIZE] = unsafe {
&mut *(&mut BUFFER as *mut MaybeUninit<[u8; BUFFER_SIZE]>
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 {
&mut *(buf as *mut [MaybeUninit<u8>; BUFFER_SIZE]
as *mut [u8; BUFFER_SIZE])
}
};
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);
init::LateResources { cs, transfer }
}
#[task(binds=DMA1_STR1, resources=[transfer, cs], priority=2)]
fn dma_complete(mut ctx: dma_complete::Context) {
// If desired, the transfer can scheduled again here to continue transmitting.
let cs = &mut ctx.resources.cs;
ctx.resources.transfer.clear_transfer_complete_interrupt();
ctx.resources.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().unwrap();
});
}
#[idle(resources=[transfer, cs])]
fn idle(mut ctx: idle::Context) -> ! {
// Start the DMA transfer over SPI.
let mut cs = ctx.resources.cs;
ctx.resources.transfer.lock(|transfer| {
cs.lock(|cs| {
transfer.start(|spi| {
// Set CS low for the transfer.
cs.set_low().unwrap();
// 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();
}
}
};