Skip to content

Commit 0bfeeca

Browse files
authored
Merge pull request #167 from quartiq/feature/dma-rtic-example
[DMA] Adding DMA+SPI+RTIC example
2 parents 624c87e + 002123c commit 0bfeeca

File tree

6 files changed

+426
-11
lines changed

6 files changed

+426
-11
lines changed

Cargo.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,4 +185,8 @@ required-features = ["rm0433"]
185185

186186
[[example]]
187187
name = "spi_dma"
188-
required-features = ["rm0433"]
188+
required-features = ["rm0433"]
189+
190+
[[example]]
191+
name = "spi-dma-rtic"
192+
required-features = ["rm0433","rt"]

examples/digital_read.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#![deny(warnings)]
2+
#![no_main]
3+
#![no_std]
4+
5+
use stm32h7xx_hal::hal::digital::v2::InputPin;
6+
7+
use cortex_m_rt::entry;
8+
use stm32h7xx_hal::{pac, prelude::*};
9+
10+
#[macro_use]
11+
mod utilities;
12+
13+
use log::info;
14+
15+
#[entry]
16+
fn main() -> ! {
17+
utilities::logger::init();
18+
info!("stm32h7xx-hal example - digitalRead");
19+
20+
let _cp = cortex_m::Peripherals::take().unwrap();
21+
let dp = pac::Peripherals::take().unwrap();
22+
23+
info!("Setup PWR... ");
24+
let pwr = dp.PWR.constrain();
25+
let pwrcfg = example_power!(pwr).freeze();
26+
27+
info!("Setup RCC... ");
28+
let rcc = dp.RCC.constrain();
29+
let ccdr = rcc.sys_ck(100.mhz()).freeze(pwrcfg, &dp.SYSCFG);
30+
31+
// Push button configuration
32+
let gpioc = dp.GPIOC.split(ccdr.peripheral.GPIOC);
33+
34+
let button1 = gpioc.pc5.into_pull_up_input();
35+
36+
loop {
37+
let result = button1.is_high().unwrap();
38+
info!("{}", result);
39+
cortex_m::asm::delay(10000000);
40+
}
41+
}

examples/exti_interrupt.rs

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
#![deny(warnings)]
2+
#![no_main]
3+
#![no_std]
4+
5+
use core::cell::{Cell, RefCell};
6+
use cortex_m::interrupt::{free, Mutex};
7+
use cortex_m::peripheral::NVIC;
8+
use cortex_m_rt::entry;
9+
use stm32h7xx_hal::gpio::{Edge, ExtiPin, Input, Output, PullUp, PushPull};
10+
use stm32h7xx_hal::hal::digital::v2::OutputPin;
11+
use stm32h7xx_hal::{interrupt, pac, prelude::*};
12+
13+
// LED pin
14+
use stm32h7xx_hal::gpio::gpioa::PA1;
15+
16+
// Button pins
17+
use stm32h7xx_hal::gpio::gpioc::PC5;
18+
use stm32h7xx_hal::gpio::gpioe::PE3;
19+
20+
#[macro_use]
21+
mod utilities;
22+
23+
use log::info;
24+
25+
// Semaphore for synchronization
26+
static SEMAPHORE: Mutex<Cell<bool>> = Mutex::new(Cell::new(true));
27+
28+
// Setup the sharing of pins between the main loop and the interrupts
29+
static BUTTON1_PIN: Mutex<RefCell<Option<PE3<Input<PullUp>>>>> =
30+
Mutex::new(RefCell::new(None));
31+
static BUTTON2_PIN: Mutex<RefCell<Option<PC5<Input<PullUp>>>>> =
32+
Mutex::new(RefCell::new(None));
33+
static LED: Mutex<RefCell<Option<PA1<Output<PushPull>>>>> =
34+
Mutex::new(RefCell::new(None));
35+
36+
#[entry]
37+
fn main() -> ! {
38+
utilities::logger::init();
39+
info!("stm32h7xx-hal example - EXTI Interrupt");
40+
41+
let mut cp = cortex_m::Peripherals::take().unwrap();
42+
let dp = pac::Peripherals::take().unwrap();
43+
44+
info!("Setup PWR...");
45+
let pwr = dp.PWR.constrain();
46+
let pwrcfg = example_power!(pwr).freeze();
47+
48+
info!("Setup RCC...");
49+
let rcc = dp.RCC.constrain();
50+
let ccdr = rcc.sys_ck(100.mhz()).freeze(pwrcfg, &dp.SYSCFG);
51+
52+
// Push button configuration
53+
let mut syscfg = dp.SYSCFG;
54+
let mut exti = dp.EXTI;
55+
56+
let gpioe = dp.GPIOE.split(ccdr.peripheral.GPIOE);
57+
let mut button1 = gpioe.pe3.into_pull_up_input();
58+
button1.make_interrupt_source(&mut syscfg);
59+
button1.trigger_on_edge(&mut exti, Edge::RISING);
60+
button1.enable_interrupt(&mut exti);
61+
62+
let gpioc = dp.GPIOC.split(ccdr.peripheral.GPIOC);
63+
let mut button2 = gpioc.pc5.into_pull_up_input();
64+
button2.make_interrupt_source(&mut syscfg);
65+
button2.trigger_on_edge(&mut exti, Edge::RISING);
66+
button2.enable_interrupt(&mut exti);
67+
68+
let gpioa = dp.GPIOA.split(ccdr.peripheral.GPIOA);
69+
let led = gpioa.pa1.into_push_pull_output();
70+
71+
// Save information needed by the interrupt handlers to the global variable
72+
free(|cs| {
73+
BUTTON1_PIN.borrow(cs).replace(Some(button1));
74+
BUTTON2_PIN.borrow(cs).replace(Some(button2));
75+
LED.borrow(cs).replace(Some(led));
76+
});
77+
78+
// Enable the button interrupts
79+
unsafe {
80+
cp.NVIC.set_priority(interrupt::EXTI3, 1);
81+
cp.NVIC.set_priority(interrupt::EXTI9_5, 1);
82+
NVIC::unmask::<interrupt>(interrupt::EXTI3);
83+
NVIC::unmask::<interrupt>(interrupt::EXTI9_5);
84+
}
85+
86+
loop {
87+
cortex_m::asm::nop();
88+
}
89+
}
90+
91+
fn toggle_led(on_or_off: bool) {
92+
free(|cs| {
93+
if let Some(b) = LED.borrow(cs).borrow_mut().as_mut() {
94+
if on_or_off {
95+
b.set_high().unwrap();
96+
} else {
97+
b.set_low().unwrap();
98+
}
99+
}
100+
});
101+
}
102+
103+
#[interrupt]
104+
fn EXTI9_5() {
105+
info!("EXTI9_5 fired!");
106+
toggle_led(true);
107+
free(|cs| {
108+
if let Some(b) = BUTTON2_PIN.borrow(cs).borrow_mut().as_mut() {
109+
b.clear_interrupt_pending_bit()
110+
}
111+
112+
// Signal that the interrupt fired
113+
SEMAPHORE.borrow(cs).set(false);
114+
});
115+
}
116+
117+
#[interrupt]
118+
fn EXTI3() {
119+
info!("EXTI3 fired!");
120+
toggle_led(false);
121+
free(|cs| {
122+
if let Some(b) = BUTTON1_PIN.borrow(cs).borrow_mut().as_mut() {
123+
b.clear_interrupt_pending_bit()
124+
}
125+
126+
// Signal that the interrupt fired
127+
SEMAPHORE.borrow(cs).set(false);
128+
});
129+
}

examples/spi-dma-rtic.rs

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

src/qspi.rs

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -341,9 +341,17 @@ impl Qspi {
341341
_ => panic!("Invalid QSPI frequency requested"),
342342
};
343343

344-
// Write the prescaler
345-
regs.cr
346-
.write(|w| unsafe { w.prescaler().bits(divisor as u8) });
344+
// Write the prescaler and the SSHIFT bit. Note that SSHIFT is required because it appears
345+
// that the QSPI may have signal contention issues when reading. SSHIFT forces the read to
346+
// occur on the falling edge instead of the rising edge. Refer to
347+
// https://github.com/quartiq/stabilizer/issues/101 for more information
348+
//
349+
// This is also noted in the docstring for the read() method.
350+
//
351+
// SSHIFT must not be set in DDR mode.
352+
regs.cr.write(|w| unsafe {
353+
w.prescaler().bits(divisor as u8).sshift().set_bit()
354+
});
347355

348356
match bank {
349357
Bank::One => regs.cr.modify(|_, w| w.fsel().clear_bit()),
@@ -447,10 +455,17 @@ impl Qspi {
447455
/// Read data over the QSPI interface.
448456
///
449457
/// # Note
450-
/// There is an issue where the QSPI peripheral will erroneously drive the output pins for an
451-
/// extra half clock cycle before IO is swapped from output to input. Refer to
458+
///
459+
/// Without any dummy cycles, the QSPI peripheral will erroneously drive the
460+
/// output pins for an extra half clock cycle before IO is swapped from
461+
/// output to input. Refer to
452462
/// https://github.com/quartiq/stabilizer/issues/101 for more information.
453463
///
464+
/// Although it doesn't stop the possible bus contention, this HAL sets the
465+
/// SSHIFT bit in the CR register. With this bit set, the QSPI receiver
466+
/// sampling point is delayed by an extra half cycle. Then the receiver
467+
/// sampling point is after the bus contention.
468+
///
454469
/// # Args
455470
/// * `addr` - The address to read data from.
456471
/// * `dest` - An array to store the result of the read into.

0 commit comments

Comments
 (0)