aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMateusz Butkiewicz <[email protected]>2023-03-22 08:44:58 +0100
committerMateusz Butkiewicz <[email protected]>2023-03-27 13:20:00 +0200
commit6a802c47083e4f6bb7b7c6c06fd2ef3e291a5212 (patch)
tree96ba93e9e9c942710bc2c722bf351ccc184260a9
parent732614579b86c2a63856b6e8e2622e09322600a7 (diff)
feat(stm32:qspi): add support for QSPI in stm32
Implemented with help of Tomasz GrzeĊ› <[email protected]>.
-rw-r--r--embassy-stm32/build.rs7
-rw-r--r--embassy-stm32/src/lib.rs3
-rw-r--r--embassy-stm32/src/qspi/mod.rs342
3 files changed, 351 insertions, 1 deletions
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs
index dbfc1370d..3780c5a40 100644
--- a/embassy-stm32/build.rs
+++ b/embassy-stm32/build.rs
@@ -427,6 +427,12 @@ fn main() {
427 (("sdmmc", "D6"), quote!(crate::sdmmc::D6Pin)), 427 (("sdmmc", "D6"), quote!(crate::sdmmc::D6Pin)),
428 (("sdmmc", "D6"), quote!(crate::sdmmc::D7Pin)), 428 (("sdmmc", "D6"), quote!(crate::sdmmc::D7Pin)),
429 (("sdmmc", "D8"), quote!(crate::sdmmc::D8Pin)), 429 (("sdmmc", "D8"), quote!(crate::sdmmc::D8Pin)),
430 (("quadspi", "BK1_IO0"), quote!(crate::qspi::D0Pin)),
431 (("quadspi", "BK1_IO1"), quote!(crate::qspi::D1Pin)),
432 (("quadspi", "BK1_IO2"), quote!(crate::qspi::D2Pin)),
433 (("quadspi", "BK1_IO3"), quote!(crate::qspi::D3Pin)),
434 (("quadspi", "CLK"), quote!(crate::qspi::SckPin)),
435 (("quadspi", "BK1_NCS"), quote!(crate::qspi::NSSPin)),
430 ].into(); 436 ].into();
431 437
432 for p in METADATA.peripherals { 438 for p in METADATA.peripherals {
@@ -507,6 +513,7 @@ fn main() {
507 (("dcmi", "PSSI"), quote!(crate::dcmi::FrameDma)), 513 (("dcmi", "PSSI"), quote!(crate::dcmi::FrameDma)),
508 // SDMMCv1 uses the same channel for both directions, so just implement for RX 514 // SDMMCv1 uses the same channel for both directions, so just implement for RX
509 (("sdmmc", "RX"), quote!(crate::sdmmc::SdmmcDma)), 515 (("sdmmc", "RX"), quote!(crate::sdmmc::SdmmcDma)),
516 (("quadspi", "QUADSPI"), quote!(crate::qspi::QuadDma)),
510 ] 517 ]
511 .into(); 518 .into();
512 519
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs
index eeaa04f67..8dc4df2dc 100644
--- a/embassy-stm32/src/lib.rs
+++ b/embassy-stm32/src/lib.rs
@@ -48,6 +48,8 @@ pub mod crc;
48))] 48))]
49pub mod flash; 49pub mod flash;
50pub mod pwm; 50pub mod pwm;
51#[cfg(quadspi)]
52pub mod qspi;
51#[cfg(rng)] 53#[cfg(rng)]
52pub mod rng; 54pub mod rng;
53#[cfg(sdmmc)] 55#[cfg(sdmmc)]
@@ -60,7 +62,6 @@ pub mod usart;
60pub mod usb; 62pub mod usb;
61#[cfg(otg)] 63#[cfg(otg)]
62pub mod usb_otg; 64pub mod usb_otg;
63
64#[cfg(iwdg)] 65#[cfg(iwdg)]
65pub mod wdg; 66pub mod wdg;
66 67
diff --git a/embassy-stm32/src/qspi/mod.rs b/embassy-stm32/src/qspi/mod.rs
new file mode 100644
index 000000000..f375d1b46
--- /dev/null
+++ b/embassy-stm32/src/qspi/mod.rs
@@ -0,0 +1,342 @@
1#![macro_use]
2
3use embassy_hal_common::{into_ref, PeripheralRef};
4
5use crate::dma::TransferOptions;
6use crate::gpio::sealed::AFType;
7use crate::gpio::AnyPin;
8use crate::pac::quadspi::Quadspi as Regs;
9use crate::rcc::RccPeripheral;
10use crate::{peripherals, Peripheral};
11
12pub struct QspiWidth;
13
14#[allow(dead_code)]
15impl QspiWidth {
16 pub const NONE: u8 = 0b00;
17 pub const SING: u8 = 0b01;
18 pub const DUAL: u8 = 0b10;
19 pub const QUAD: u8 = 0b11;
20}
21
22struct QspiMode;
23
24#[allow(dead_code)]
25impl QspiMode {
26 pub const INDIRECT_WRITE: u8 = 0b00;
27 pub const INDIRECT_READ: u8 = 0b01;
28 pub const AUTO_POLLING: u8 = 0b10;
29 pub const MEMORY_MAPPED: u8 = 0b11;
30}
31
32pub struct QspiTransaction {
33 pub iwidth: u8,
34 pub awidth: u8,
35 pub dwidth: u8,
36 pub instruction: u8,
37 pub address: Option<u32>,
38 pub dummy: u8,
39 pub data_len: Option<usize>,
40}
41
42impl Default for QspiTransaction {
43 fn default() -> Self {
44 Self {
45 iwidth: QspiWidth::NONE,
46 awidth: QspiWidth::NONE,
47 dwidth: QspiWidth::NONE,
48 instruction: 0,
49 address: None,
50 dummy: 0,
51 data_len: None,
52 }
53 }
54}
55
56pub struct Config {
57 pub memory_size: u8,
58 pub address_size: u8,
59 pub prescaler: u8,
60 pub fifo_threshold: u8,
61 pub cs_high_time: u8,
62}
63
64impl Default for Config {
65 fn default() -> Self {
66 Self {
67 memory_size: 0,
68 address_size: 2,
69 prescaler: 128,
70 fifo_threshold: 16,
71 cs_high_time: 4,
72 }
73 }
74}
75
76#[allow(dead_code)]
77pub struct Qspi<'d, T: Instance, Dma> {
78 _peri: PeripheralRef<'d, T>,
79 sck: Option<PeripheralRef<'d, AnyPin>>,
80 d0: Option<PeripheralRef<'d, AnyPin>>,
81 d1: Option<PeripheralRef<'d, AnyPin>>,
82 d2: Option<PeripheralRef<'d, AnyPin>>,
83 d3: Option<PeripheralRef<'d, AnyPin>>,
84 nss: Option<PeripheralRef<'d, AnyPin>>,
85 dma: PeripheralRef<'d, Dma>,
86 config: Config,
87}
88
89impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> {
90 pub fn new(
91 peri: impl Peripheral<P = T> + 'd,
92 d0: impl Peripheral<P = impl D0Pin<T>> + 'd,
93 d1: impl Peripheral<P = impl D1Pin<T>> + 'd,
94 d2: impl Peripheral<P = impl D2Pin<T>> + 'd,
95 d3: impl Peripheral<P = impl D3Pin<T>> + 'd,
96 sck: impl Peripheral<P = impl SckPin<T>> + 'd,
97 nss: impl Peripheral<P = impl NSSPin<T>> + 'd,
98 dma: impl Peripheral<P = Dma> + 'd,
99 config: Config,
100 ) -> Self {
101 into_ref!(peri, d0, d1, d2, d3, sck, nss);
102
103 unsafe {
104 sck.set_as_af(sck.af_num(), AFType::OutputPushPull);
105 sck.set_speed(crate::gpio::Speed::VeryHigh);
106 nss.set_as_af(nss.af_num(), AFType::OutputPushPull);
107 nss.set_speed(crate::gpio::Speed::VeryHigh);
108 d0.set_as_af(d0.af_num(), AFType::OutputPushPull);
109 d0.set_speed(crate::gpio::Speed::VeryHigh);
110 d1.set_as_af(d1.af_num(), AFType::OutputPushPull);
111 d1.set_speed(crate::gpio::Speed::VeryHigh);
112 d2.set_as_af(d2.af_num(), AFType::OutputPushPull);
113 d2.set_speed(crate::gpio::Speed::VeryHigh);
114 d3.set_as_af(d3.af_num(), AFType::OutputPushPull);
115 d3.set_speed(crate::gpio::Speed::VeryHigh);
116 }
117
118 Self::new_inner(
119 peri,
120 Some(d0.map_into()),
121 Some(d1.map_into()),
122 Some(d2.map_into()),
123 Some(d3.map_into()),
124 Some(sck.map_into()),
125 Some(nss.map_into()),
126 dma,
127 config,
128 )
129 }
130
131 fn new_inner(
132 peri: impl Peripheral<P = T> + 'd,
133 d0: Option<PeripheralRef<'d, AnyPin>>,
134 d1: Option<PeripheralRef<'d, AnyPin>>,
135 d2: Option<PeripheralRef<'d, AnyPin>>,
136 d3: Option<PeripheralRef<'d, AnyPin>>,
137 sck: Option<PeripheralRef<'d, AnyPin>>,
138 nss: Option<PeripheralRef<'d, AnyPin>>,
139 dma: impl Peripheral<P = Dma> + 'd,
140 config: Config,
141 ) -> Self {
142 into_ref!(peri, dma);
143
144 T::enable();
145 unsafe {
146 T::REGS.cr().write(|w| w.set_fthres(config.fifo_threshold));
147
148 while T::REGS.sr().read().busy() {}
149
150 T::REGS.cr().write(|w| {
151 w.set_prescaler(config.prescaler);
152 w.set_en(true);
153 });
154 T::REGS.dcr().write(|w| {
155 w.set_fsize(config.memory_size);
156 w.set_csht(config.cs_high_time);
157 w.set_ckmode(false);
158 });
159 }
160
161 Self {
162 _peri: peri,
163 sck,
164 d0,
165 d1,
166 d2,
167 d3,
168 nss,
169 dma,
170 config,
171 }
172 }
173
174 pub fn command(&mut self, transaction: QspiTransaction) {
175 unsafe {
176 T::REGS.cr().modify(|v| v.set_dmaen(false));
177 self.setup_transaction(QspiMode::INDIRECT_WRITE, &transaction);
178
179 while !T::REGS.sr().read().tcf() {}
180 T::REGS.fcr().modify(|v| v.set_ctcf(true));
181 }
182 }
183
184 pub fn read(&mut self, buf: &mut [u8], transaction: QspiTransaction) {
185 unsafe {
186 T::REGS.cr().modify(|v| v.set_dmaen(false));
187 self.setup_transaction(QspiMode::INDIRECT_WRITE, &transaction);
188
189 if let Some(len) = transaction.data_len {
190 let current_ar = T::REGS.ar().read().address();
191 T::REGS.ccr().modify(|v| {
192 v.set_fmode(QspiMode::INDIRECT_READ);
193 });
194 T::REGS.ar().write(|v| {
195 v.set_address(current_ar);
196 });
197
198 for idx in 0..len {
199 while !T::REGS.sr().read().tcf() && !T::REGS.sr().read().ftf() {}
200 buf[idx] = *(T::REGS.dr().ptr() as *mut u8);
201 }
202 }
203
204 while !T::REGS.sr().read().tcf() {}
205 T::REGS.fcr().modify(|v| v.set_ctcf(true));
206 }
207 }
208
209 pub fn write(&mut self, buf: &[u8], transaction: QspiTransaction) {
210 unsafe {
211 T::REGS.cr().modify(|v| v.set_dmaen(false));
212 self.setup_transaction(QspiMode::INDIRECT_WRITE, &transaction);
213
214 if let Some(len) = transaction.data_len {
215 T::REGS.ccr().modify(|v| {
216 v.set_fmode(QspiMode::INDIRECT_WRITE);
217 });
218
219 for idx in 0..len {
220 while !T::REGS.sr().read().ftf() {}
221 *(T::REGS.dr().ptr() as *mut u8) = buf[idx];
222 }
223 }
224
225 while !T::REGS.sr().read().tcf() {}
226 T::REGS.fcr().modify(|v| v.set_ctcf(true));
227 }
228 }
229
230 pub fn read_dma(&mut self, buf: &mut [u8], transaction: QspiTransaction)
231 where
232 Dma: QuadDma<T>,
233 {
234 unsafe {
235 self.setup_transaction(QspiMode::INDIRECT_WRITE, &transaction);
236
237 let request = self.dma.request();
238 let options = TransferOptions::default();
239
240 T::REGS.ccr().modify(|v| {
241 v.set_fmode(QspiMode::INDIRECT_READ);
242 });
243 let current_ar = T::REGS.ar().read().address();
244 T::REGS.ar().write(|v| {
245 v.set_address(current_ar);
246 });
247
248 self.dma
249 .start_read(request, T::REGS.dr().ptr() as *mut u8, buf, options);
250
251 T::REGS.cr().modify(|v| v.set_dmaen(true));
252
253 while self.dma.is_running() {}
254 }
255 }
256
257 pub fn write_dma(&mut self, buf: &[u8], transaction: QspiTransaction)
258 where
259 Dma: QuadDma<T>,
260 {
261 unsafe {
262 self.setup_transaction(QspiMode::INDIRECT_WRITE, &transaction);
263
264 let request = self.dma.request();
265 let options = TransferOptions::default();
266
267 T::REGS.ccr().modify(|v| {
268 v.set_fmode(QspiMode::INDIRECT_WRITE);
269 });
270
271 self.dma
272 .start_write(request, buf, T::REGS.dr().ptr() as *mut u8, options);
273
274 T::REGS.cr().modify(|v| v.set_dmaen(true));
275
276 while self.dma.is_running() {}
277 }
278 }
279
280 fn setup_transaction(&mut self, fmode: u8, transaction: &QspiTransaction) {
281 unsafe {
282 T::REGS.fcr().modify(|v| {
283 v.set_csmf(true);
284 v.set_ctcf(true);
285 v.set_ctef(true);
286 v.set_ctof(true);
287 });
288
289 while T::REGS.sr().read().busy() {}
290
291 if let Some(len) = transaction.data_len {
292 T::REGS.dlr().write(|v| v.set_dl(len as u32 - 1));
293 }
294
295 T::REGS.ccr().write(|v| {
296 v.set_fmode(fmode);
297 v.set_imode(transaction.iwidth);
298 v.set_instruction(transaction.instruction);
299 v.set_admode(transaction.awidth);
300 v.set_adsize(self.config.address_size);
301 v.set_dmode(transaction.dwidth);
302 v.set_abmode(QspiWidth::NONE);
303 v.set_dcyc(transaction.dummy);
304 });
305
306 if let Some(addr) = transaction.address {
307 T::REGS.ar().write(|v| {
308 v.set_address(addr);
309 });
310 }
311 }
312 }
313}
314
315pub(crate) mod sealed {
316 use super::*;
317
318 pub trait Instance {
319 const REGS: Regs;
320 }
321}
322
323pub trait Instance: Peripheral<P = Self> + sealed::Instance + RccPeripheral {}
324
325pin_trait!(SckPin, Instance);
326pin_trait!(D0Pin, Instance);
327pin_trait!(D1Pin, Instance);
328pin_trait!(D2Pin, Instance);
329pin_trait!(D3Pin, Instance);
330pin_trait!(NSSPin, Instance);
331
332dma_trait!(QuadDma, Instance);
333
334foreach_peripheral!(
335 (quadspi, $inst:ident) => {
336 impl sealed::Instance for peripherals::$inst {
337 const REGS: Regs = crate::pac::$inst;
338 }
339
340 impl Instance for peripherals::$inst {}
341 };
342);