aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2025-01-07 21:15:54 +0000
committerGitHub <[email protected]>2025-01-07 21:15:54 +0000
commit2a06eb2459163ce5b11d1c82171f87a4b98f99cb (patch)
treea31d02a6ccb49af7041d5b4ec9626df3dd830333
parent92fa6536722368b8cb8cabdb42640dc017fb26cb (diff)
parentd08116da49161e1f0db2b104553d50035c499be8 (diff)
Merge pull request #3667 from williams-one/stm32u5-add-hspi-support
STM32U5: add HSPI support
-rw-r--r--embassy-stm32/build.rs22
-rw-r--r--embassy-stm32/src/hspi/enums.rs411
-rw-r--r--embassy-stm32/src/hspi/mod.rs1008
-rw-r--r--embassy-stm32/src/lib.rs2
-rw-r--r--embassy-stm32/src/ospi/mod.rs2
-rw-r--r--examples/stm32u5/src/bin/hspi_memory_mapped.rs455
6 files changed, 1899 insertions, 1 deletions
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs
index 30c525521..4abbf8d69 100644
--- a/embassy-stm32/build.rs
+++ b/embassy-stm32/build.rs
@@ -1091,6 +1091,27 @@ fn main() {
1091 (("octospim", "P2_NCS"), quote!(crate::ospi::NSSPin)), 1091 (("octospim", "P2_NCS"), quote!(crate::ospi::NSSPin)),
1092 (("octospim", "P2_CLK"), quote!(crate::ospi::SckPin)), 1092 (("octospim", "P2_CLK"), quote!(crate::ospi::SckPin)),
1093 (("octospim", "P2_NCLK"), quote!(crate::ospi::NckPin)), 1093 (("octospim", "P2_NCLK"), quote!(crate::ospi::NckPin)),
1094 (("hspi", "IO0"), quote!(crate::hspi::D0Pin)),
1095 (("hspi", "IO1"), quote!(crate::hspi::D1Pin)),
1096 (("hspi", "IO2"), quote!(crate::hspi::D2Pin)),
1097 (("hspi", "IO3"), quote!(crate::hspi::D3Pin)),
1098 (("hspi", "IO4"), quote!(crate::hspi::D4Pin)),
1099 (("hspi", "IO5"), quote!(crate::hspi::D5Pin)),
1100 (("hspi", "IO6"), quote!(crate::hspi::D6Pin)),
1101 (("hspi", "IO7"), quote!(crate::hspi::D7Pin)),
1102 (("hspi", "IO8"), quote!(crate::hspi::D8Pin)),
1103 (("hspi", "IO9"), quote!(crate::hspi::D9Pin)),
1104 (("hspi", "IO10"), quote!(crate::hspi::D10Pin)),
1105 (("hspi", "IO11"), quote!(crate::hspi::D11Pin)),
1106 (("hspi", "IO12"), quote!(crate::hspi::D12Pin)),
1107 (("hspi", "IO13"), quote!(crate::hspi::D13Pin)),
1108 (("hspi", "IO14"), quote!(crate::hspi::D14Pin)),
1109 (("hspi", "IO15"), quote!(crate::hspi::D15Pin)),
1110 (("hspi", "DQS0"), quote!(crate::hspi::DQS0Pin)),
1111 (("hspi", "DQS1"), quote!(crate::hspi::DQS1Pin)),
1112 (("hspi", "NCS"), quote!(crate::hspi::NSSPin)),
1113 (("hspi", "CLK"), quote!(crate::hspi::SckPin)),
1114 (("hspi", "NCLK"), quote!(crate::hspi::NckPin)),
1094 (("tsc", "G1_IO1"), quote!(crate::tsc::G1IO1Pin)), 1115 (("tsc", "G1_IO1"), quote!(crate::tsc::G1IO1Pin)),
1095 (("tsc", "G1_IO2"), quote!(crate::tsc::G1IO2Pin)), 1116 (("tsc", "G1_IO2"), quote!(crate::tsc::G1IO2Pin)),
1096 (("tsc", "G1_IO3"), quote!(crate::tsc::G1IO3Pin)), 1117 (("tsc", "G1_IO3"), quote!(crate::tsc::G1IO3Pin)),
@@ -1275,6 +1296,7 @@ fn main() {
1275 (("sdmmc", "RX"), quote!(crate::sdmmc::SdmmcDma)), 1296 (("sdmmc", "RX"), quote!(crate::sdmmc::SdmmcDma)),
1276 (("quadspi", "QUADSPI"), quote!(crate::qspi::QuadDma)), 1297 (("quadspi", "QUADSPI"), quote!(crate::qspi::QuadDma)),
1277 (("octospi", "OCTOSPI1"), quote!(crate::ospi::OctoDma)), 1298 (("octospi", "OCTOSPI1"), quote!(crate::ospi::OctoDma)),
1299 (("hspi", "HSPI1"), quote!(crate::hspi::HspiDma)),
1278 (("dac", "CH1"), quote!(crate::dac::DacDma1)), 1300 (("dac", "CH1"), quote!(crate::dac::DacDma1)),
1279 (("dac", "CH2"), quote!(crate::dac::DacDma2)), 1301 (("dac", "CH2"), quote!(crate::dac::DacDma2)),
1280 (("timer", "UP"), quote!(crate::timer::UpDma)), 1302 (("timer", "UP"), quote!(crate::timer::UpDma)),
diff --git a/embassy-stm32/src/hspi/enums.rs b/embassy-stm32/src/hspi/enums.rs
new file mode 100644
index 000000000..351fc9ec6
--- /dev/null
+++ b/embassy-stm32/src/hspi/enums.rs
@@ -0,0 +1,411 @@
1//! Enums used in Hspi configuration.
2
3#[allow(dead_code)]
4#[derive(Copy, Clone, defmt::Format)]
5pub(crate) enum HspiMode {
6 IndirectWrite,
7 IndirectRead,
8 AutoPolling,
9 MemoryMapped,
10}
11
12impl Into<u8> for HspiMode {
13 fn into(self) -> u8 {
14 match self {
15 HspiMode::IndirectWrite => 0b00,
16 HspiMode::IndirectRead => 0b01,
17 HspiMode::AutoPolling => 0b10,
18 HspiMode::MemoryMapped => 0b11,
19 }
20 }
21}
22
23/// Hspi lane width
24#[allow(dead_code)]
25#[derive(Copy, Clone, defmt::Format)]
26pub enum HspiWidth {
27 /// None
28 NONE,
29 /// Single lane
30 SING,
31 /// Dual lanes
32 DUAL,
33 /// Quad lanes
34 QUAD,
35 /// Eight lanes
36 OCTO,
37 /// Sixteen lanes
38 HEXADECA,
39}
40
41impl Into<u8> for HspiWidth {
42 fn into(self) -> u8 {
43 match self {
44 HspiWidth::NONE => 0b00,
45 HspiWidth::SING => 0b01,
46 HspiWidth::DUAL => 0b10,
47 HspiWidth::QUAD => 0b11,
48 HspiWidth::OCTO => 0b100,
49 HspiWidth::HEXADECA => 0b101,
50 }
51 }
52}
53
54/// Flash bank selection
55#[allow(dead_code)]
56#[derive(Copy, Clone, defmt::Format)]
57pub enum FlashSelection {
58 /// Bank 1
59 Flash1,
60 /// Bank 2
61 Flash2,
62}
63
64impl Into<bool> for FlashSelection {
65 fn into(self) -> bool {
66 match self {
67 FlashSelection::Flash1 => false,
68 FlashSelection::Flash2 => true,
69 }
70 }
71}
72
73/// Wrap Size
74#[allow(dead_code)]
75#[allow(missing_docs)]
76#[derive(Copy, Clone, defmt::Format)]
77pub enum WrapSize {
78 None,
79 _16Bytes,
80 _32Bytes,
81 _64Bytes,
82 _128Bytes,
83}
84
85impl Into<u8> for WrapSize {
86 fn into(self) -> u8 {
87 match self {
88 WrapSize::None => 0x00,
89 WrapSize::_16Bytes => 0x02,
90 WrapSize::_32Bytes => 0x03,
91 WrapSize::_64Bytes => 0x04,
92 WrapSize::_128Bytes => 0x05,
93 }
94 }
95}
96
97/// Memory Type
98#[allow(missing_docs)]
99#[allow(dead_code)]
100#[derive(Copy, Clone, defmt::Format)]
101pub enum MemoryType {
102 Micron,
103 Macronix,
104 Standard,
105 MacronixRam,
106 HyperBusMemory,
107 HyperBusRegister,
108}
109
110impl Into<u8> for MemoryType {
111 fn into(self) -> u8 {
112 match self {
113 MemoryType::Micron => 0x00,
114 MemoryType::Macronix => 0x01,
115 MemoryType::Standard => 0x02,
116 MemoryType::MacronixRam => 0x03,
117 MemoryType::HyperBusMemory => 0x04,
118 MemoryType::HyperBusRegister => 0x04,
119 }
120 }
121}
122
123/// Hspi memory size.
124#[allow(missing_docs)]
125#[derive(Copy, Clone, defmt::Format)]
126pub enum MemorySize {
127 _1KiB,
128 _2KiB,
129 _4KiB,
130 _8KiB,
131 _16KiB,
132 _32KiB,
133 _64KiB,
134 _128KiB,
135 _256KiB,
136 _512KiB,
137 _1MiB,
138 _2MiB,
139 _4MiB,
140 _8MiB,
141 _16MiB,
142 _32MiB,
143 _64MiB,
144 _128MiB,
145 _256MiB,
146 _512MiB,
147 _1GiB,
148 _2GiB,
149 _4GiB,
150 Other(u8),
151}
152
153impl Into<u8> for MemorySize {
154 fn into(self) -> u8 {
155 match self {
156 MemorySize::_1KiB => 6,
157 MemorySize::_2KiB => 7,
158 MemorySize::_4KiB => 8,
159 MemorySize::_8KiB => 9,
160 MemorySize::_16KiB => 10,
161 MemorySize::_32KiB => 11,
162 MemorySize::_64KiB => 12,
163 MemorySize::_128KiB => 13,
164 MemorySize::_256KiB => 14,
165 MemorySize::_512KiB => 15,
166 MemorySize::_1MiB => 16,
167 MemorySize::_2MiB => 17,
168 MemorySize::_4MiB => 18,
169 MemorySize::_8MiB => 19,
170 MemorySize::_16MiB => 20,
171 MemorySize::_32MiB => 21,
172 MemorySize::_64MiB => 22,
173 MemorySize::_128MiB => 23,
174 MemorySize::_256MiB => 24,
175 MemorySize::_512MiB => 25,
176 MemorySize::_1GiB => 26,
177 MemorySize::_2GiB => 27,
178 MemorySize::_4GiB => 28,
179 MemorySize::Other(val) => val,
180 }
181 }
182}
183
184/// Hspi Address size
185#[derive(Copy, Clone, defmt::Format)]
186pub enum AddressSize {
187 /// 8-bit address
188 _8Bit,
189 /// 16-bit address
190 _16Bit,
191 /// 24-bit address
192 _24Bit,
193 /// 32-bit address
194 _32Bit,
195}
196
197impl Into<u8> for AddressSize {
198 fn into(self) -> u8 {
199 match self {
200 AddressSize::_8Bit => 0b00,
201 AddressSize::_16Bit => 0b01,
202 AddressSize::_24Bit => 0b10,
203 AddressSize::_32Bit => 0b11,
204 }
205 }
206}
207
208/// Time the Chip Select line stays high.
209#[allow(missing_docs)]
210#[derive(Copy, Clone, defmt::Format)]
211pub enum ChipSelectHighTime {
212 _1Cycle,
213 _2Cycle,
214 _3Cycle,
215 _4Cycle,
216 _5Cycle,
217 _6Cycle,
218 _7Cycle,
219 _8Cycle,
220}
221
222impl Into<u8> for ChipSelectHighTime {
223 fn into(self) -> u8 {
224 match self {
225 ChipSelectHighTime::_1Cycle => 0,
226 ChipSelectHighTime::_2Cycle => 1,
227 ChipSelectHighTime::_3Cycle => 2,
228 ChipSelectHighTime::_4Cycle => 3,
229 ChipSelectHighTime::_5Cycle => 4,
230 ChipSelectHighTime::_6Cycle => 5,
231 ChipSelectHighTime::_7Cycle => 6,
232 ChipSelectHighTime::_8Cycle => 7,
233 }
234 }
235}
236
237/// FIFO threshold.
238#[allow(missing_docs)]
239#[derive(Copy, Clone, defmt::Format)]
240pub enum FIFOThresholdLevel {
241 _1Bytes,
242 _2Bytes,
243 _3Bytes,
244 _4Bytes,
245 _5Bytes,
246 _6Bytes,
247 _7Bytes,
248 _8Bytes,
249 _9Bytes,
250 _10Bytes,
251 _11Bytes,
252 _12Bytes,
253 _13Bytes,
254 _14Bytes,
255 _15Bytes,
256 _16Bytes,
257 _17Bytes,
258 _18Bytes,
259 _19Bytes,
260 _20Bytes,
261 _21Bytes,
262 _22Bytes,
263 _23Bytes,
264 _24Bytes,
265 _25Bytes,
266 _26Bytes,
267 _27Bytes,
268 _28Bytes,
269 _29Bytes,
270 _30Bytes,
271 _31Bytes,
272 _32Bytes,
273}
274
275impl Into<u8> for FIFOThresholdLevel {
276 fn into(self) -> u8 {
277 match self {
278 FIFOThresholdLevel::_1Bytes => 0,
279 FIFOThresholdLevel::_2Bytes => 1,
280 FIFOThresholdLevel::_3Bytes => 2,
281 FIFOThresholdLevel::_4Bytes => 3,
282 FIFOThresholdLevel::_5Bytes => 4,
283 FIFOThresholdLevel::_6Bytes => 5,
284 FIFOThresholdLevel::_7Bytes => 6,
285 FIFOThresholdLevel::_8Bytes => 7,
286 FIFOThresholdLevel::_9Bytes => 8,
287 FIFOThresholdLevel::_10Bytes => 9,
288 FIFOThresholdLevel::_11Bytes => 10,
289 FIFOThresholdLevel::_12Bytes => 11,
290 FIFOThresholdLevel::_13Bytes => 12,
291 FIFOThresholdLevel::_14Bytes => 13,
292 FIFOThresholdLevel::_15Bytes => 14,
293 FIFOThresholdLevel::_16Bytes => 15,
294 FIFOThresholdLevel::_17Bytes => 16,
295 FIFOThresholdLevel::_18Bytes => 17,
296 FIFOThresholdLevel::_19Bytes => 18,
297 FIFOThresholdLevel::_20Bytes => 19,
298 FIFOThresholdLevel::_21Bytes => 20,
299 FIFOThresholdLevel::_22Bytes => 21,
300 FIFOThresholdLevel::_23Bytes => 22,
301 FIFOThresholdLevel::_24Bytes => 23,
302 FIFOThresholdLevel::_25Bytes => 24,
303 FIFOThresholdLevel::_26Bytes => 25,
304 FIFOThresholdLevel::_27Bytes => 26,
305 FIFOThresholdLevel::_28Bytes => 27,
306 FIFOThresholdLevel::_29Bytes => 28,
307 FIFOThresholdLevel::_30Bytes => 29,
308 FIFOThresholdLevel::_31Bytes => 30,
309 FIFOThresholdLevel::_32Bytes => 31,
310 }
311 }
312}
313
314/// Dummy cycle count
315#[allow(missing_docs)]
316#[derive(Copy, Clone, defmt::Format)]
317pub enum DummyCycles {
318 _0,
319 _1,
320 _2,
321 _3,
322 _4,
323 _5,
324 _6,
325 _7,
326 _8,
327 _9,
328 _10,
329 _11,
330 _12,
331 _13,
332 _14,
333 _15,
334 _16,
335 _17,
336 _18,
337 _19,
338 _20,
339 _21,
340 _22,
341 _23,
342 _24,
343 _25,
344 _26,
345 _27,
346 _28,
347 _29,
348 _30,
349 _31,
350}
351
352impl Into<u8> for DummyCycles {
353 fn into(self) -> u8 {
354 match self {
355 DummyCycles::_0 => 0,
356 DummyCycles::_1 => 1,
357 DummyCycles::_2 => 2,
358 DummyCycles::_3 => 3,
359 DummyCycles::_4 => 4,
360 DummyCycles::_5 => 5,
361 DummyCycles::_6 => 6,
362 DummyCycles::_7 => 7,
363 DummyCycles::_8 => 8,
364 DummyCycles::_9 => 9,
365 DummyCycles::_10 => 10,
366 DummyCycles::_11 => 11,
367 DummyCycles::_12 => 12,
368 DummyCycles::_13 => 13,
369 DummyCycles::_14 => 14,
370 DummyCycles::_15 => 15,
371 DummyCycles::_16 => 16,
372 DummyCycles::_17 => 17,
373 DummyCycles::_18 => 18,
374 DummyCycles::_19 => 19,
375 DummyCycles::_20 => 20,
376 DummyCycles::_21 => 21,
377 DummyCycles::_22 => 22,
378 DummyCycles::_23 => 23,
379 DummyCycles::_24 => 24,
380 DummyCycles::_25 => 25,
381 DummyCycles::_26 => 26,
382 DummyCycles::_27 => 27,
383 DummyCycles::_28 => 28,
384 DummyCycles::_29 => 29,
385 DummyCycles::_30 => 30,
386 DummyCycles::_31 => 31,
387 }
388 }
389}
390
391/// Functional mode
392#[allow(missing_docs)]
393#[allow(dead_code)]
394#[derive(Copy, Clone, defmt::Format)]
395pub enum FunctionalMode {
396 IndirectWrite,
397 IndirectRead,
398 AutoStatusPolling,
399 MemoryMapped,
400}
401
402impl Into<u8> for FunctionalMode {
403 fn into(self) -> u8 {
404 match self {
405 FunctionalMode::IndirectWrite => 0x00,
406 FunctionalMode::IndirectRead => 0x01,
407 FunctionalMode::AutoStatusPolling => 0x02,
408 FunctionalMode::MemoryMapped => 0x03,
409 }
410 }
411}
diff --git a/embassy-stm32/src/hspi/mod.rs b/embassy-stm32/src/hspi/mod.rs
new file mode 100644
index 000000000..97e1993dd
--- /dev/null
+++ b/embassy-stm32/src/hspi/mod.rs
@@ -0,0 +1,1008 @@
1//! HSPI Serial Peripheral Interface
2//!
3
4// NOTE: This is a partial implementation of the HSPI driver.
5// It implements only Single and Octal SPI modes, but additional
6// modes can be added as needed following the same pattern and
7// using ospi/mod.rs as a reference.
8
9#![macro_use]
10
11pub mod enums;
12
13use core::marker::PhantomData;
14
15use embassy_embedded_hal::{GetConfig, SetConfig};
16use embassy_hal_internal::{into_ref, PeripheralRef};
17pub use enums::*;
18
19use crate::dma::{word, ChannelAndRequest};
20use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed};
21use crate::mode::{Async, Blocking, Mode as PeriMode};
22use crate::pac::hspi::Hspi as Regs;
23use crate::rcc::{self, RccPeripheral};
24use crate::{peripherals, Peripheral};
25
26/// HSPI driver config.
27#[derive(Clone, Copy, defmt::Format)]
28pub struct Config {
29 /// Fifo threshold used by the peripheral to generate the interrupt indicating data
30 /// or space is available in the FIFO
31 pub fifo_threshold: FIFOThresholdLevel,
32 /// Indicates the type of external device connected
33 pub memory_type: MemoryType, // Need to add an additional enum to provide this public interface
34 /// Defines the size of the external device connected to the HSPI corresponding
35 /// to the number of address bits required to access the device
36 pub device_size: MemorySize,
37 /// Sets the minimum number of clock cycles that the chip select signal must be held high
38 /// between commands
39 pub chip_select_high_time: ChipSelectHighTime,
40 /// Enables the free running clock
41 pub free_running_clock: bool,
42 /// Sets the clock level when the device is not selected
43 pub clock_mode: bool,
44 /// Indicates the wrap size corresponding to the external device configuration
45 pub wrap_size: WrapSize,
46 /// Specified the prescaler factor used for generating the external clock based
47 /// on the AHB clock
48 pub clock_prescaler: u8,
49 /// Allows the delay of 1/2 cycle the data sampling to account for external
50 /// signal delays
51 pub sample_shifting: bool,
52 /// Allows hold to 1/4 cycle the data
53 pub delay_hold_quarter_cycle: bool,
54 /// Enables the transaction boundary feature and defines the boundary to release
55 /// the chip select
56 pub chip_select_boundary: u8,
57 /// Enables the delay block bypass so the sampling is not affected by the delay block
58 pub delay_block_bypass: bool,
59 /// Enables communication regulation feature. Chip select is released when the other
60 /// HSPI requests access to the bus
61 pub max_transfer: u8,
62 /// Enables the refresh feature, chip select is released every refresh + 1 clock cycles
63 pub refresh: u32,
64}
65
66impl Default for Config {
67 fn default() -> Self {
68 Self {
69 fifo_threshold: FIFOThresholdLevel::_16Bytes,
70 memory_type: MemoryType::Micron,
71 device_size: MemorySize::Other(0),
72 chip_select_high_time: ChipSelectHighTime::_5Cycle,
73 free_running_clock: false,
74 clock_mode: false,
75 wrap_size: WrapSize::None,
76 clock_prescaler: 0,
77 sample_shifting: false,
78 delay_hold_quarter_cycle: false,
79 chip_select_boundary: 0, // Acceptable range 0 to 31
80 delay_block_bypass: true,
81 max_transfer: 0,
82 refresh: 0,
83 }
84 }
85}
86
87/// HSPI transfer configuration.
88pub struct TransferConfig {
89 /// Instruction width (IMODE)
90 pub iwidth: HspiWidth,
91 /// Instruction Id
92 pub instruction: Option<u32>,
93 /// Number of Instruction Bytes
94 pub isize: AddressSize,
95 /// Instruction Double Transfer rate enable
96 pub idtr: bool,
97
98 /// Address width (ADMODE)
99 pub adwidth: HspiWidth,
100 /// Device memory address
101 pub address: Option<u32>,
102 /// Number of Address Bytes
103 pub adsize: AddressSize,
104 /// Address Double Transfer rate enable
105 pub addtr: bool,
106
107 /// Alternate bytes width (ABMODE)
108 pub abwidth: HspiWidth,
109 /// Alternate Bytes
110 pub alternate_bytes: Option<u32>,
111 /// Number of Alternate Bytes
112 pub absize: AddressSize,
113 /// Alternate Bytes Double Transfer rate enable
114 pub abdtr: bool,
115
116 /// Data width (DMODE)
117 pub dwidth: HspiWidth,
118 /// Data buffer
119 pub ddtr: bool,
120
121 /// Number of dummy cycles (DCYC)
122 pub dummy: DummyCycles,
123}
124
125impl Default for TransferConfig {
126 fn default() -> Self {
127 Self {
128 iwidth: HspiWidth::NONE,
129 instruction: None,
130 isize: AddressSize::_8Bit,
131 idtr: false,
132
133 adwidth: HspiWidth::NONE,
134 address: None,
135 adsize: AddressSize::_8Bit,
136 addtr: false,
137
138 abwidth: HspiWidth::NONE,
139 alternate_bytes: None,
140 absize: AddressSize::_8Bit,
141 abdtr: false,
142
143 dwidth: HspiWidth::NONE,
144 ddtr: false,
145
146 dummy: DummyCycles::_0,
147 }
148 }
149}
150
151/// Error used for HSPI implementation
152#[derive(Debug)]
153#[cfg_attr(feature = "defmt", derive(defmt::Format))]
154pub enum HspiError {
155 /// Peripheral configuration is invalid
156 InvalidConfiguration,
157 /// Operation configuration is invalid
158 InvalidCommand,
159 /// Size zero buffer passed to instruction
160 EmptyBuffer,
161}
162
163/// HSPI driver.
164pub struct Hspi<'d, T: Instance, M: PeriMode> {
165 _peri: PeripheralRef<'d, T>,
166 sck: Option<PeripheralRef<'d, AnyPin>>,
167 d0: Option<PeripheralRef<'d, AnyPin>>,
168 d1: Option<PeripheralRef<'d, AnyPin>>,
169 d2: Option<PeripheralRef<'d, AnyPin>>,
170 d3: Option<PeripheralRef<'d, AnyPin>>,
171 d4: Option<PeripheralRef<'d, AnyPin>>,
172 d5: Option<PeripheralRef<'d, AnyPin>>,
173 d6: Option<PeripheralRef<'d, AnyPin>>,
174 d7: Option<PeripheralRef<'d, AnyPin>>,
175 d8: Option<PeripheralRef<'d, AnyPin>>,
176 d9: Option<PeripheralRef<'d, AnyPin>>,
177 d10: Option<PeripheralRef<'d, AnyPin>>,
178 d11: Option<PeripheralRef<'d, AnyPin>>,
179 d12: Option<PeripheralRef<'d, AnyPin>>,
180 d13: Option<PeripheralRef<'d, AnyPin>>,
181 d14: Option<PeripheralRef<'d, AnyPin>>,
182 d15: Option<PeripheralRef<'d, AnyPin>>,
183 nss: Option<PeripheralRef<'d, AnyPin>>,
184 dqs0: Option<PeripheralRef<'d, AnyPin>>,
185 dqs1: Option<PeripheralRef<'d, AnyPin>>,
186 dma: Option<ChannelAndRequest<'d>>,
187 _phantom: PhantomData<M>,
188 config: Config,
189 width: HspiWidth,
190}
191
192impl<'d, T: Instance, M: PeriMode> Hspi<'d, T, M> {
193 /// Enter memory mode.
194 /// The Input `read_config` is used to configure the read operation in memory mode
195 pub fn enable_memory_mapped_mode(
196 &mut self,
197 read_config: TransferConfig,
198 write_config: TransferConfig,
199 ) -> Result<(), HspiError> {
200 // Use configure command to set read config
201 self.configure_command(&read_config, None)?;
202
203 // Set writing configurations, there are separate registers for write configurations in memory mapped mode
204 T::REGS.wccr().modify(|w| {
205 w.set_imode(write_config.iwidth.into());
206 w.set_idtr(write_config.idtr);
207 w.set_isize(write_config.isize.into());
208
209 w.set_admode(write_config.adwidth.into());
210 w.set_addtr(write_config.idtr);
211 w.set_adsize(write_config.adsize.into());
212
213 w.set_dmode(write_config.dwidth.into());
214 w.set_ddtr(write_config.ddtr);
215
216 w.set_abmode(write_config.abwidth.into());
217 w.set_dqse(true);
218 });
219
220 T::REGS.wtcr().modify(|w| w.set_dcyc(write_config.dummy.into()));
221
222 // Enable memory mapped mode
223 T::REGS.cr().modify(|r| {
224 r.set_fmode(FunctionalMode::MemoryMapped.into());
225 r.set_tcen(false);
226 });
227 Ok(())
228 }
229
230 /// Quit from memory mapped mode
231 pub fn disable_memory_mapped_mode(&mut self) {
232 T::REGS.cr().modify(|r| {
233 r.set_fmode(FunctionalMode::IndirectWrite.into());
234 r.set_abort(true);
235 r.set_dmaen(false);
236 r.set_en(false);
237 });
238
239 // Clear transfer complete flag
240 T::REGS.fcr().write(|w| w.set_ctcf(true));
241
242 // Re-enable HSPI
243 T::REGS.cr().modify(|r| {
244 r.set_en(true);
245 });
246 }
247
248 fn new_inner(
249 peri: impl Peripheral<P = T> + 'd,
250 d0: Option<PeripheralRef<'d, AnyPin>>,
251 d1: Option<PeripheralRef<'d, AnyPin>>,
252 d2: Option<PeripheralRef<'d, AnyPin>>,
253 d3: Option<PeripheralRef<'d, AnyPin>>,
254 d4: Option<PeripheralRef<'d, AnyPin>>,
255 d5: Option<PeripheralRef<'d, AnyPin>>,
256 d6: Option<PeripheralRef<'d, AnyPin>>,
257 d7: Option<PeripheralRef<'d, AnyPin>>,
258 d8: Option<PeripheralRef<'d, AnyPin>>,
259 d9: Option<PeripheralRef<'d, AnyPin>>,
260 d10: Option<PeripheralRef<'d, AnyPin>>,
261 d11: Option<PeripheralRef<'d, AnyPin>>,
262 d12: Option<PeripheralRef<'d, AnyPin>>,
263 d13: Option<PeripheralRef<'d, AnyPin>>,
264 d14: Option<PeripheralRef<'d, AnyPin>>,
265 d15: Option<PeripheralRef<'d, AnyPin>>,
266 sck: Option<PeripheralRef<'d, AnyPin>>,
267 nss: Option<PeripheralRef<'d, AnyPin>>,
268 dqs0: Option<PeripheralRef<'d, AnyPin>>,
269 dqs1: Option<PeripheralRef<'d, AnyPin>>,
270 dma: Option<ChannelAndRequest<'d>>,
271 config: Config,
272 width: HspiWidth,
273 dual_memory_mode: bool,
274 ) -> Self {
275 into_ref!(peri);
276
277 // System configuration
278 rcc::enable_and_reset::<T>();
279
280 // Call this function just to check that the clock for HSPI1 is properly setup
281 let _ = T::frequency();
282
283 while T::REGS.sr().read().busy() {}
284
285 Self::configure_registers(&config, Some(dual_memory_mode));
286
287 Self {
288 _peri: peri,
289 sck,
290 d0,
291 d1,
292 d2,
293 d3,
294 d4,
295 d5,
296 d6,
297 d7,
298 d8,
299 d9,
300 d10,
301 d11,
302 d12,
303 d13,
304 d14,
305 d15,
306 nss,
307 dqs0,
308 dqs1,
309 dma,
310 _phantom: PhantomData,
311 config,
312 width,
313 }
314 }
315
316 fn configure_registers(config: &Config, dual_memory_mode: Option<bool>) {
317 // Device configuration
318 T::REGS.dcr1().modify(|w| {
319 w.set_mtyp(config.memory_type.into());
320 w.set_devsize(config.device_size.into());
321 w.set_csht(config.chip_select_high_time.into());
322 w.set_frck(false);
323 w.set_ckmode(config.clock_mode);
324 w.set_dlybyp(config.delay_block_bypass);
325 });
326
327 T::REGS.dcr2().modify(|w| {
328 w.set_wrapsize(config.wrap_size.into());
329 });
330
331 T::REGS.dcr3().modify(|w| {
332 w.set_csbound(config.chip_select_boundary);
333 w.set_maxtran(config.max_transfer);
334 });
335
336 T::REGS.dcr4().modify(|w| {
337 w.set_refresh(config.refresh);
338 });
339
340 T::REGS.cr().modify(|w| {
341 w.set_fthres(config.fifo_threshold.into());
342 });
343
344 // Wait for busy flag to clear
345 while T::REGS.sr().read().busy() {}
346
347 T::REGS.dcr2().modify(|w| {
348 w.set_prescaler(config.clock_prescaler);
349 });
350
351 // The configuration of clock prescaler trigger automatically a calibration process
352 // So it is necessary to wait the calibration is complete
353 while T::REGS.sr().read().busy() {}
354
355 if let Some(dual_memory_mode) = dual_memory_mode {
356 T::REGS.cr().modify(|w| {
357 w.set_dmm(dual_memory_mode);
358 });
359 }
360
361 T::REGS.tcr().modify(|w| {
362 w.set_sshift(config.sample_shifting);
363 w.set_dhqc(config.delay_hold_quarter_cycle);
364 });
365
366 // Enable peripheral
367 T::REGS.cr().modify(|w| {
368 w.set_en(true);
369 });
370
371 // Free running clock needs to be set after peripheral enable
372 if config.free_running_clock {
373 T::REGS.dcr1().modify(|w| {
374 w.set_frck(config.free_running_clock);
375 });
376 }
377 }
378
379 // Function to configure the peripheral for the requested command
380 fn configure_command(&mut self, command: &TransferConfig, data_len: Option<usize>) -> Result<(), HspiError> {
381 // Check that transaction doesn't use more than hardware initialized pins
382 if <enums::HspiWidth as Into<u8>>::into(command.iwidth) > <enums::HspiWidth as Into<u8>>::into(self.width)
383 || <enums::HspiWidth as Into<u8>>::into(command.adwidth) > <enums::HspiWidth as Into<u8>>::into(self.width)
384 || <enums::HspiWidth as Into<u8>>::into(command.abwidth) > <enums::HspiWidth as Into<u8>>::into(self.width)
385 || <enums::HspiWidth as Into<u8>>::into(command.dwidth) > <enums::HspiWidth as Into<u8>>::into(self.width)
386 {
387 return Err(HspiError::InvalidCommand);
388 }
389
390 while T::REGS.sr().read().busy() {}
391
392 T::REGS.cr().modify(|w| {
393 w.set_fmode(0.into());
394 });
395
396 // Configure alternate bytes
397 if let Some(ab) = command.alternate_bytes {
398 T::REGS.abr().write(|v| v.set_alternate(ab));
399 T::REGS.ccr().modify(|w| {
400 w.set_abmode(command.abwidth.into());
401 w.set_abdtr(command.abdtr);
402 w.set_absize(command.absize.into());
403 })
404 }
405
406 // Configure dummy cycles
407 T::REGS.tcr().modify(|w| {
408 w.set_dcyc(command.dummy.into());
409 });
410
411 // Configure data
412 if let Some(data_length) = data_len {
413 T::REGS.dlr().write(|v| {
414 v.set_dl((data_length - 1) as u32);
415 })
416 } else {
417 T::REGS.dlr().write(|v| {
418 v.set_dl((0) as u32);
419 })
420 }
421
422 // Configure instruction/address/data modes
423 T::REGS.ccr().modify(|w| {
424 w.set_imode(command.iwidth.into());
425 w.set_idtr(command.idtr);
426 w.set_isize(command.isize.into());
427
428 w.set_admode(command.adwidth.into());
429 w.set_addtr(command.addtr);
430 w.set_adsize(command.adsize.into());
431
432 w.set_dmode(command.dwidth.into());
433 w.set_ddtr(command.ddtr);
434 });
435
436 // Configure DQS
437 T::REGS.ccr().modify(|w| {
438 w.set_dqse(command.ddtr && command.instruction.unwrap_or(0) != 0x12ED);
439 });
440
441 // Set information required to initiate transaction
442 if let Some(instruction) = command.instruction {
443 if let Some(address) = command.address {
444 T::REGS.ir().write(|v| {
445 v.set_instruction(instruction);
446 });
447
448 T::REGS.ar().write(|v| {
449 v.set_address(address);
450 });
451 } else {
452 T::REGS.ir().write(|v| {
453 v.set_instruction(instruction);
454 });
455 }
456 } else {
457 if let Some(address) = command.address {
458 T::REGS.ar().write(|v| {
459 v.set_address(address);
460 });
461 } else {
462 // The only single phase transaction supported is instruction only
463 return Err(HspiError::InvalidCommand);
464 }
465 }
466
467 Ok(())
468 }
469
470 /// Function used to control or configure the target device without data transfer
471 pub fn blocking_command(&mut self, command: &TransferConfig) -> Result<(), HspiError> {
472 // Wait for peripheral to be free
473 while T::REGS.sr().read().busy() {}
474
475 // Need additional validation that command configuration doesn't have data set
476 self.configure_command(command, None)?;
477
478 // Transaction initiated by setting final configuration, i.e the instruction register
479 while !T::REGS.sr().read().tcf() {}
480 T::REGS.fcr().write(|w| {
481 w.set_ctcf(true);
482 });
483
484 Ok(())
485 }
486
487 /// Blocking read with byte by byte data transfer
488 pub fn blocking_read<W: Word>(&mut self, buf: &mut [W], transaction: TransferConfig) -> Result<(), HspiError> {
489 if buf.is_empty() {
490 return Err(HspiError::EmptyBuffer);
491 }
492
493 // Wait for peripheral to be free
494 while T::REGS.sr().read().busy() {}
495
496 // Ensure DMA is not enabled for this transaction
497 T::REGS.cr().modify(|w| {
498 w.set_dmaen(false);
499 });
500
501 self.configure_command(&transaction, Some(buf.len()))?;
502
503 let current_address = T::REGS.ar().read().address();
504 let current_instruction = T::REGS.ir().read().instruction();
505
506 // For a indirect read transaction, the transaction begins when the instruction/address is set
507 T::REGS
508 .cr()
509 .modify(|v| v.set_fmode(FunctionalMode::IndirectRead.into()));
510 if T::REGS.ccr().read().admode() == HspiWidth::NONE.into() {
511 T::REGS.ir().write(|v| v.set_instruction(current_instruction));
512 } else {
513 T::REGS.ar().write(|v| v.set_address(current_address));
514 }
515
516 for idx in 0..buf.len() {
517 while !T::REGS.sr().read().tcf() && !T::REGS.sr().read().ftf() {}
518 buf[idx] = unsafe { (T::REGS.dr().as_ptr() as *mut W).read_volatile() };
519 }
520
521 while !T::REGS.sr().read().tcf() {}
522 T::REGS.fcr().write(|v| v.set_ctcf(true));
523
524 Ok(())
525 }
526
527 /// Blocking write with byte by byte data transfer
528 pub fn blocking_write<W: Word>(&mut self, buf: &[W], transaction: TransferConfig) -> Result<(), HspiError> {
529 if buf.is_empty() {
530 return Err(HspiError::EmptyBuffer);
531 }
532
533 // Wait for peripheral to be free
534 while T::REGS.sr().read().busy() {}
535
536 T::REGS.cr().modify(|w| {
537 w.set_dmaen(false);
538 });
539
540 self.configure_command(&transaction, Some(buf.len()))?;
541
542 T::REGS
543 .cr()
544 .modify(|v| v.set_fmode(FunctionalMode::IndirectWrite.into()));
545
546 for idx in 0..buf.len() {
547 while !T::REGS.sr().read().ftf() {}
548 unsafe { (T::REGS.dr().as_ptr() as *mut W).write_volatile(buf[idx]) };
549 }
550
551 while !T::REGS.sr().read().tcf() {}
552 T::REGS.fcr().write(|v| v.set_ctcf(true));
553
554 Ok(())
555 }
556
557 /// Set new bus configuration
558 pub fn set_config(&mut self, config: &Config) {
559 // Wait for busy flag to clear
560 while T::REGS.sr().read().busy() {}
561
562 // Disable DMA channel while configuring the peripheral
563 T::REGS.cr().modify(|w| {
564 w.set_dmaen(false);
565 });
566
567 Self::configure_registers(config, None);
568
569 self.config = *config;
570 }
571
572 /// Get current configuration
573 pub fn get_config(&self) -> Config {
574 self.config
575 }
576}
577
578impl<'d, T: Instance> Hspi<'d, T, Blocking> {
579 /// Create new blocking HSPI driver for single spi external chip
580 pub fn new_blocking_singlespi(
581 peri: impl Peripheral<P = T> + 'd,
582 sck: impl Peripheral<P = impl SckPin<T>> + 'd,
583 d0: impl Peripheral<P = impl D0Pin<T>> + 'd,
584 d1: impl Peripheral<P = impl D1Pin<T>> + 'd,
585 nss: impl Peripheral<P = impl NSSPin<T>> + 'd,
586 config: Config,
587 ) -> Self {
588 Self::new_inner(
589 peri,
590 new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
591 new_pin!(d1, AfType::input(Pull::None)),
592 None,
593 None,
594 None,
595 None,
596 None,
597 None,
598 None,
599 None,
600 None,
601 None,
602 None,
603 None,
604 None,
605 None,
606 new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
607 new_pin!(
608 nss,
609 AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)
610 ),
611 None,
612 None,
613 None,
614 config,
615 HspiWidth::SING,
616 false,
617 )
618 }
619
620 /// Create new blocking HSPI driver for octospi external chip
621 pub fn new_blocking_octospi(
622 peri: impl Peripheral<P = T> + 'd,
623 sck: impl Peripheral<P = impl SckPin<T>> + 'd,
624 d0: impl Peripheral<P = impl D0Pin<T>> + 'd,
625 d1: impl Peripheral<P = impl D1Pin<T>> + 'd,
626 d2: impl Peripheral<P = impl D2Pin<T>> + 'd,
627 d3: impl Peripheral<P = impl D3Pin<T>> + 'd,
628 d4: impl Peripheral<P = impl D4Pin<T>> + 'd,
629 d5: impl Peripheral<P = impl D5Pin<T>> + 'd,
630 d6: impl Peripheral<P = impl D6Pin<T>> + 'd,
631 d7: impl Peripheral<P = impl D7Pin<T>> + 'd,
632 nss: impl Peripheral<P = impl NSSPin<T>> + 'd,
633 dqs0: impl Peripheral<P = impl DQS0Pin<T>> + 'd,
634 config: Config,
635 ) -> Self {
636 Self::new_inner(
637 peri,
638 new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
639 new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
640 new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
641 new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
642 new_pin!(d4, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
643 new_pin!(d5, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
644 new_pin!(d6, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
645 new_pin!(d7, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
646 None,
647 None,
648 None,
649 None,
650 None,
651 None,
652 None,
653 None,
654 new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
655 new_pin!(
656 nss,
657 AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)
658 ),
659 new_pin!(dqs0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
660 None,
661 None,
662 config,
663 HspiWidth::OCTO,
664 false,
665 )
666 }
667}
668
669impl<'d, T: Instance> Hspi<'d, T, Async> {
670 /// Create new HSPI driver for a single spi external chip
671 pub fn new_singlespi(
672 peri: impl Peripheral<P = T> + 'd,
673 sck: impl Peripheral<P = impl SckPin<T>> + 'd,
674 d0: impl Peripheral<P = impl D0Pin<T>> + 'd,
675 d1: impl Peripheral<P = impl D1Pin<T>> + 'd,
676 nss: impl Peripheral<P = impl NSSPin<T>> + 'd,
677 dma: impl Peripheral<P = impl HspiDma<T>> + 'd,
678 config: Config,
679 ) -> Self {
680 Self::new_inner(
681 peri,
682 new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
683 new_pin!(d1, AfType::input(Pull::None)),
684 None,
685 None,
686 None,
687 None,
688 None,
689 None,
690 None,
691 None,
692 None,
693 None,
694 None,
695 None,
696 None,
697 None,
698 new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
699 new_pin!(
700 nss,
701 AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)
702 ),
703 None,
704 None,
705 new_dma!(dma),
706 config,
707 HspiWidth::SING,
708 false,
709 )
710 }
711
712 /// Create new HSPI driver for octospi external chip
713 pub fn new_octospi(
714 peri: impl Peripheral<P = T> + 'd,
715 sck: impl Peripheral<P = impl SckPin<T>> + 'd,
716 d0: impl Peripheral<P = impl D0Pin<T>> + 'd,
717 d1: impl Peripheral<P = impl D1Pin<T>> + 'd,
718 d2: impl Peripheral<P = impl D2Pin<T>> + 'd,
719 d3: impl Peripheral<P = impl D3Pin<T>> + 'd,
720 d4: impl Peripheral<P = impl D4Pin<T>> + 'd,
721 d5: impl Peripheral<P = impl D5Pin<T>> + 'd,
722 d6: impl Peripheral<P = impl D6Pin<T>> + 'd,
723 d7: impl Peripheral<P = impl D7Pin<T>> + 'd,
724 nss: impl Peripheral<P = impl NSSPin<T>> + 'd,
725 dqs0: impl Peripheral<P = impl DQS0Pin<T>> + 'd,
726 dma: impl Peripheral<P = impl HspiDma<T>> + 'd,
727 config: Config,
728 ) -> Self {
729 Self::new_inner(
730 peri,
731 new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
732 new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
733 new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
734 new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
735 new_pin!(d4, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
736 new_pin!(d5, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
737 new_pin!(d6, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
738 new_pin!(d7, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
739 new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
740 None,
741 None,
742 None,
743 None,
744 None,
745 None,
746 None,
747 None,
748 new_pin!(
749 nss,
750 AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)
751 ),
752 new_pin!(dqs0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
753 None,
754 new_dma!(dma),
755 config,
756 HspiWidth::OCTO,
757 false,
758 )
759 }
760
761 /// Blocking read with DMA transfer
762 pub fn blocking_read_dma<W: Word>(&mut self, buf: &mut [W], transaction: TransferConfig) -> Result<(), HspiError> {
763 if buf.is_empty() {
764 return Err(HspiError::EmptyBuffer);
765 }
766
767 // Wait for peripheral to be free
768 while T::REGS.sr().read().busy() {}
769
770 self.configure_command(&transaction, Some(buf.len()))?;
771
772 let current_address = T::REGS.ar().read().address();
773 let current_instruction = T::REGS.ir().read().instruction();
774
775 // For a indirect read transaction, the transaction begins when the instruction/address is set
776 T::REGS
777 .cr()
778 .modify(|v| v.set_fmode(FunctionalMode::IndirectRead.into()));
779 if T::REGS.ccr().read().admode() == HspiWidth::NONE.into() {
780 T::REGS.ir().write(|v| v.set_instruction(current_instruction));
781 } else {
782 T::REGS.ar().write(|v| v.set_address(current_address));
783 }
784
785 let transfer = unsafe {
786 self.dma
787 .as_mut()
788 .unwrap()
789 .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default())
790 };
791
792 T::REGS.cr().modify(|w| w.set_dmaen(true));
793
794 transfer.blocking_wait();
795
796 finish_dma(T::REGS);
797
798 Ok(())
799 }
800
801 /// Blocking write with DMA transfer
802 pub fn blocking_write_dma<W: Word>(&mut self, buf: &[W], transaction: TransferConfig) -> Result<(), HspiError> {
803 if buf.is_empty() {
804 return Err(HspiError::EmptyBuffer);
805 }
806
807 // Wait for peripheral to be free
808 while T::REGS.sr().read().busy() {}
809
810 self.configure_command(&transaction, Some(buf.len()))?;
811 T::REGS
812 .cr()
813 .modify(|v| v.set_fmode(FunctionalMode::IndirectWrite.into()));
814
815 let transfer = unsafe {
816 self.dma
817 .as_mut()
818 .unwrap()
819 .write(buf, T::REGS.dr().as_ptr() as *mut W, Default::default())
820 };
821
822 T::REGS.cr().modify(|w| w.set_dmaen(true));
823
824 transfer.blocking_wait();
825
826 finish_dma(T::REGS);
827
828 Ok(())
829 }
830
831 /// Asynchronous read from external device
832 pub async fn read<W: Word>(&mut self, buf: &mut [W], transaction: TransferConfig) -> Result<(), HspiError> {
833 if buf.is_empty() {
834 return Err(HspiError::EmptyBuffer);
835 }
836
837 // Wait for peripheral to be free
838 while T::REGS.sr().read().busy() {}
839
840 self.configure_command(&transaction, Some(buf.len()))?;
841
842 let current_address = T::REGS.ar().read().address();
843 let current_instruction = T::REGS.ir().read().instruction();
844
845 // For a indirect read transaction, the transaction begins when the instruction/address is set
846 T::REGS
847 .cr()
848 .modify(|v| v.set_fmode(FunctionalMode::IndirectRead.into()));
849 if T::REGS.ccr().read().admode() == HspiWidth::NONE.into() {
850 T::REGS.ir().write(|v| v.set_instruction(current_instruction));
851 } else {
852 T::REGS.ar().write(|v| v.set_address(current_address));
853 }
854
855 let transfer = unsafe {
856 self.dma
857 .as_mut()
858 .unwrap()
859 .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default())
860 };
861
862 T::REGS.cr().modify(|w| w.set_dmaen(true));
863
864 transfer.await;
865
866 finish_dma(T::REGS);
867
868 Ok(())
869 }
870
871 /// Asynchronous write to external device
872 pub async fn write<W: Word>(&mut self, buf: &[W], transaction: TransferConfig) -> Result<(), HspiError> {
873 if buf.is_empty() {
874 return Err(HspiError::EmptyBuffer);
875 }
876
877 // Wait for peripheral to be free
878 while T::REGS.sr().read().busy() {}
879
880 self.configure_command(&transaction, Some(buf.len()))?;
881 T::REGS
882 .cr()
883 .modify(|v| v.set_fmode(FunctionalMode::IndirectWrite.into()));
884
885 let transfer = unsafe {
886 self.dma
887 .as_mut()
888 .unwrap()
889 .write(buf, T::REGS.dr().as_ptr() as *mut W, Default::default())
890 };
891
892 T::REGS.cr().modify(|w| w.set_dmaen(true));
893
894 transfer.await;
895
896 finish_dma(T::REGS);
897
898 Ok(())
899 }
900}
901
902impl<'d, T: Instance, M: PeriMode> Drop for Hspi<'d, T, M> {
903 fn drop(&mut self) {
904 self.sck.as_ref().map(|x| x.set_as_disconnected());
905 self.d0.as_ref().map(|x| x.set_as_disconnected());
906 self.d1.as_ref().map(|x| x.set_as_disconnected());
907 self.d2.as_ref().map(|x| x.set_as_disconnected());
908 self.d3.as_ref().map(|x| x.set_as_disconnected());
909 self.d4.as_ref().map(|x| x.set_as_disconnected());
910 self.d5.as_ref().map(|x| x.set_as_disconnected());
911 self.d6.as_ref().map(|x| x.set_as_disconnected());
912 self.d7.as_ref().map(|x| x.set_as_disconnected());
913 self.d8.as_ref().map(|x| x.set_as_disconnected());
914 self.d9.as_ref().map(|x| x.set_as_disconnected());
915 self.d10.as_ref().map(|x| x.set_as_disconnected());
916 self.d11.as_ref().map(|x| x.set_as_disconnected());
917 self.d12.as_ref().map(|x| x.set_as_disconnected());
918 self.d13.as_ref().map(|x| x.set_as_disconnected());
919 self.d14.as_ref().map(|x| x.set_as_disconnected());
920 self.d15.as_ref().map(|x| x.set_as_disconnected());
921 self.nss.as_ref().map(|x| x.set_as_disconnected());
922 self.dqs0.as_ref().map(|x| x.set_as_disconnected());
923 self.dqs1.as_ref().map(|x| x.set_as_disconnected());
924
925 rcc::disable::<T>();
926 }
927}
928
929fn finish_dma(regs: Regs) {
930 while !regs.sr().read().tcf() {}
931 regs.fcr().write(|v| v.set_ctcf(true));
932
933 regs.cr().modify(|w| {
934 w.set_dmaen(false);
935 });
936}
937
938/// HSPI instance trait.
939pub(crate) trait SealedInstance {
940 const REGS: Regs;
941}
942
943/// HSPI instance trait.
944#[allow(private_bounds)]
945pub trait Instance: Peripheral<P = Self> + SealedInstance + RccPeripheral {}
946
947pin_trait!(SckPin, Instance);
948pin_trait!(NckPin, Instance);
949pin_trait!(D0Pin, Instance);
950pin_trait!(D1Pin, Instance);
951pin_trait!(D2Pin, Instance);
952pin_trait!(D3Pin, Instance);
953pin_trait!(D4Pin, Instance);
954pin_trait!(D5Pin, Instance);
955pin_trait!(D6Pin, Instance);
956pin_trait!(D7Pin, Instance);
957pin_trait!(D8Pin, Instance);
958pin_trait!(D9Pin, Instance);
959pin_trait!(D10Pin, Instance);
960pin_trait!(D11Pin, Instance);
961pin_trait!(D12Pin, Instance);
962pin_trait!(D13Pin, Instance);
963pin_trait!(D14Pin, Instance);
964pin_trait!(D15Pin, Instance);
965pin_trait!(DQS0Pin, Instance);
966pin_trait!(DQS1Pin, Instance);
967pin_trait!(NSSPin, Instance);
968dma_trait!(HspiDma, Instance);
969
970foreach_peripheral!(
971 (hspi, $inst:ident) => {
972 impl SealedInstance for peripherals::$inst {
973 const REGS: Regs = crate::pac::$inst;
974 }
975
976 impl Instance for peripherals::$inst {}
977 };
978);
979
980impl<'d, T: Instance, M: PeriMode> SetConfig for Hspi<'d, T, M> {
981 type Config = Config;
982 type ConfigError = ();
983 fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> {
984 self.set_config(config);
985 Ok(())
986 }
987}
988
989impl<'d, T: Instance, M: PeriMode> GetConfig for Hspi<'d, T, M> {
990 type Config = Config;
991 fn get_config(&self) -> Self::Config {
992 self.get_config()
993 }
994}
995
996/// Word sizes usable for HSPI.
997#[allow(private_bounds)]
998pub trait Word: word::Word {}
999
1000macro_rules! impl_word {
1001 ($T:ty) => {
1002 impl Word for $T {}
1003 };
1004}
1005
1006impl_word!(u8);
1007impl_word!(u16);
1008impl_word!(u32);
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs
index 61da754c3..c37908dbc 100644
--- a/embassy-stm32/src/lib.rs
+++ b/embassy-stm32/src/lib.rs
@@ -83,6 +83,8 @@ pub mod hash;
83pub mod hrtim; 83pub mod hrtim;
84#[cfg(hsem)] 84#[cfg(hsem)]
85pub mod hsem; 85pub mod hsem;
86#[cfg(hspi)]
87pub mod hspi;
86#[cfg(i2c)] 88#[cfg(i2c)]
87pub mod i2c; 89pub mod i2c;
88#[cfg(any(all(spi_v1, rcc_f4), spi_v3))] 90#[cfg(any(all(spi_v1, rcc_f4), spi_v3))]
diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs
index 33e19f4f8..e35d51c91 100644
--- a/embassy-stm32/src/ospi/mod.rs
+++ b/embassy-stm32/src/ospi/mod.rs
@@ -52,7 +52,7 @@ pub struct Config {
52 /// Enables the transaction boundary feature and defines the boundary to release 52 /// Enables the transaction boundary feature and defines the boundary to release
53 /// the chip select 53 /// the chip select
54 pub chip_select_boundary: u8, 54 pub chip_select_boundary: u8,
55 /// Enbales the delay block bypass so the sampling is not affected by the delay block 55 /// Enables the delay block bypass so the sampling is not affected by the delay block
56 pub delay_block_bypass: bool, 56 pub delay_block_bypass: bool,
57 /// Enables communication regulation feature. Chip select is released when the other 57 /// Enables communication regulation feature. Chip select is released when the other
58 /// OctoSpi requests access to the bus 58 /// OctoSpi requests access to the bus
diff --git a/examples/stm32u5/src/bin/hspi_memory_mapped.rs b/examples/stm32u5/src/bin/hspi_memory_mapped.rs
new file mode 100644
index 000000000..9fef4855e
--- /dev/null
+++ b/examples/stm32u5/src/bin/hspi_memory_mapped.rs
@@ -0,0 +1,455 @@
1#![no_main]
2#![no_std]
3
4// Tested on an STM32U5G9J-DK2 demo board using the on-board MX66LM1G45G flash memory
5// The flash is connected to the HSPI1 port as an OCTA-DTR device
6//
7// Use embassy-stm32 feature "stm32u5g9zj" and probe-rs chip "STM32U5G9ZJTxQ"
8
9use defmt::info;
10use embassy_executor::Spawner;
11use embassy_stm32::hspi::{
12 AddressSize, ChipSelectHighTime, DummyCycles, FIFOThresholdLevel, Hspi, HspiWidth, Instance, MemorySize,
13 MemoryType, TransferConfig, WrapSize,
14};
15use embassy_stm32::mode::Async;
16use embassy_stm32::rcc;
17use embassy_stm32::time::Hertz;
18use {defmt_rtt as _, panic_probe as _};
19
20#[embassy_executor::main]
21async fn main(_spawner: Spawner) {
22 info!("Start hspi_memory_mapped");
23
24 // RCC config
25 let mut config = embassy_stm32::Config::default();
26 config.rcc.hse = Some(rcc::Hse {
27 freq: Hertz(16_000_000),
28 mode: rcc::HseMode::Oscillator,
29 });
30 config.rcc.pll1 = Some(rcc::Pll {
31 source: rcc::PllSource::HSE,
32 prediv: rcc::PllPreDiv::DIV1,
33 mul: rcc::PllMul::MUL10,
34 divp: None,
35 divq: None,
36 divr: Some(rcc::PllDiv::DIV1),
37 });
38 config.rcc.sys = rcc::Sysclk::PLL1_R; // 160 Mhz
39 config.rcc.pll2 = Some(rcc::Pll {
40 source: rcc::PllSource::HSE,
41 prediv: rcc::PllPreDiv::DIV4,
42 mul: rcc::PllMul::MUL66,
43 divp: None,
44 divq: Some(rcc::PllDiv::DIV2),
45 divr: None,
46 });
47 config.rcc.mux.hspi1sel = rcc::mux::Hspisel::PLL2_Q; // 132 MHz
48
49 // Initialize peripherals
50 let p = embassy_stm32::init(config);
51
52 let flash_config = embassy_stm32::hspi::Config {
53 fifo_threshold: FIFOThresholdLevel::_4Bytes,
54 memory_type: MemoryType::Macronix,
55 device_size: MemorySize::_1GiB,
56 chip_select_high_time: ChipSelectHighTime::_2Cycle,
57 free_running_clock: false,
58 clock_mode: false,
59 wrap_size: WrapSize::None,
60 clock_prescaler: 0,
61 sample_shifting: false,
62 delay_hold_quarter_cycle: false,
63 chip_select_boundary: 0,
64 delay_block_bypass: false,
65 max_transfer: 0,
66 refresh: 0,
67 };
68
69 let use_dma = true;
70
71 info!("Testing flash in OCTA DTR mode and memory mapped mode");
72
73 let hspi = Hspi::new_octospi(
74 p.HSPI1,
75 p.PI3,
76 p.PH10,
77 p.PH11,
78 p.PH12,
79 p.PH13,
80 p.PH14,
81 p.PH15,
82 p.PI0,
83 p.PI1,
84 p.PH9,
85 p.PI2,
86 p.GPDMA1_CH7,
87 flash_config,
88 );
89
90 let mut flash = OctaDtrFlashMemory::new(hspi).await;
91
92 let flash_id = flash.read_id();
93 info!("FLASH ID: {=[u8]:x}", flash_id);
94
95 let mut rd_buf = [0u8; 16];
96 flash.read_memory(0, &mut rd_buf, use_dma).await;
97 info!("READ BUF: {=[u8]:#X}", rd_buf);
98
99 flash.erase_sector(0).await;
100 flash.read_memory(0, &mut rd_buf, use_dma).await;
101 info!("READ BUF: {=[u8]:#X}", rd_buf);
102 assert_eq!(rd_buf[0], 0xFF);
103 assert_eq!(rd_buf[15], 0xFF);
104
105 let mut wr_buf = [0u8; 16];
106 for i in 0..wr_buf.len() {
107 wr_buf[i] = i as u8;
108 }
109 info!("WRITE BUF: {=[u8]:#X}", wr_buf);
110 flash.write_memory(0, &wr_buf, use_dma).await;
111 flash.read_memory(0, &mut rd_buf, use_dma).await;
112 info!("READ BUF: {=[u8]:#X}", rd_buf);
113 assert_eq!(rd_buf[0], 0x00);
114 assert_eq!(rd_buf[15], 0x0F);
115
116 flash.enable_mm().await;
117 info!("Enabled memory mapped mode");
118
119 let first_u32 = unsafe { *(0xA0000000 as *const u32) };
120 info!("first_u32: 0x{=u32:X}", first_u32);
121 assert_eq!(first_u32, 0x03020100);
122
123 let second_u32 = unsafe { *(0xA0000004 as *const u32) };
124 assert_eq!(second_u32, 0x07060504);
125 info!("second_u32: 0x{=u32:X}", second_u32);
126
127 let first_u8 = unsafe { *(0xA0000000 as *const u8) };
128 assert_eq!(first_u8, 00);
129 info!("first_u8: 0x{=u8:X}", first_u8);
130
131 let second_u8 = unsafe { *(0xA0000001 as *const u8) };
132 assert_eq!(second_u8, 0x01);
133 info!("second_u8: 0x{=u8:X}", second_u8);
134
135 let third_u8 = unsafe { *(0xA0000002 as *const u8) };
136 assert_eq!(third_u8, 0x02);
137 info!("third_u8: 0x{=u8:X}", third_u8);
138
139 let fourth_u8 = unsafe { *(0xA0000003 as *const u8) };
140 assert_eq!(fourth_u8, 0x03);
141 info!("fourth_u8: 0x{=u8:X}", fourth_u8);
142
143 info!("DONE");
144}
145
146// Custom implementation for MX66UW1G45G NOR flash memory from Macronix.
147// Chip commands are hardcoded as they depend on the chip used.
148// This implementation enables Octa I/O (OPI) and Double Transfer Rate (DTR)
149
150pub struct OctaDtrFlashMemory<'d, I: Instance> {
151 hspi: Hspi<'d, I, Async>,
152}
153
154impl<'d, I: Instance> OctaDtrFlashMemory<'d, I> {
155 const MEMORY_PAGE_SIZE: usize = 256;
156
157 const CMD_READ_OCTA_DTR: u16 = 0xEE11;
158 const CMD_PAGE_PROGRAM_OCTA_DTR: u16 = 0x12ED;
159
160 const CMD_READ_ID_OCTA_DTR: u16 = 0x9F60;
161
162 const CMD_RESET_ENABLE: u8 = 0x66;
163 const CMD_RESET_ENABLE_OCTA_DTR: u16 = 0x6699;
164 const CMD_RESET: u8 = 0x99;
165 const CMD_RESET_OCTA_DTR: u16 = 0x9966;
166
167 const CMD_WRITE_ENABLE: u8 = 0x06;
168 const CMD_WRITE_ENABLE_OCTA_DTR: u16 = 0x06F9;
169
170 const CMD_SECTOR_ERASE_OCTA_DTR: u16 = 0x21DE;
171 const CMD_BLOCK_ERASE_OCTA_DTR: u16 = 0xDC23;
172
173 const CMD_READ_SR: u8 = 0x05;
174 const CMD_READ_SR_OCTA_DTR: u16 = 0x05FA;
175
176 const CMD_READ_CR2: u8 = 0x71;
177 const CMD_WRITE_CR2: u8 = 0x72;
178
179 const CR2_REG1_ADDR: u32 = 0x00000000;
180 const CR2_OCTA_DTR: u8 = 0x02;
181
182 const CR2_REG3_ADDR: u32 = 0x00000300;
183 const CR2_DC_6_CYCLES: u8 = 0x07;
184
185 pub async fn new(hspi: Hspi<'d, I, Async>) -> Self {
186 let mut memory = Self { hspi };
187
188 memory.reset_memory().await;
189 memory.enable_octa_dtr().await;
190 memory
191 }
192
193 async fn enable_octa_dtr(&mut self) {
194 self.write_enable_spi().await;
195 self.write_cr2_spi(Self::CR2_REG3_ADDR, Self::CR2_DC_6_CYCLES);
196 self.write_enable_spi().await;
197 self.write_cr2_spi(Self::CR2_REG1_ADDR, Self::CR2_OCTA_DTR);
198 }
199
200 pub async fn enable_mm(&mut self) {
201 let read_config = TransferConfig {
202 iwidth: HspiWidth::OCTO,
203 instruction: Some(Self::CMD_READ_OCTA_DTR as u32),
204 isize: AddressSize::_16Bit,
205 idtr: true,
206 adwidth: HspiWidth::OCTO,
207 adsize: AddressSize::_32Bit,
208 addtr: true,
209 dwidth: HspiWidth::OCTO,
210 ddtr: true,
211 dummy: DummyCycles::_6,
212 ..Default::default()
213 };
214
215 let write_config = TransferConfig {
216 iwidth: HspiWidth::OCTO,
217 isize: AddressSize::_16Bit,
218 idtr: true,
219 adwidth: HspiWidth::OCTO,
220 adsize: AddressSize::_32Bit,
221 addtr: true,
222 dwidth: HspiWidth::OCTO,
223 ddtr: true,
224 ..Default::default()
225 };
226 self.hspi.enable_memory_mapped_mode(read_config, write_config).unwrap();
227 }
228
229 async fn exec_command_spi(&mut self, cmd: u8) {
230 let transaction = TransferConfig {
231 iwidth: HspiWidth::SING,
232 instruction: Some(cmd as u32),
233 ..Default::default()
234 };
235 info!("Excuting command: 0x{:X}", transaction.instruction.unwrap());
236 self.hspi.blocking_command(&transaction).unwrap();
237 }
238
239 async fn exec_command_octa_dtr(&mut self, cmd: u16) {
240 let transaction = TransferConfig {
241 iwidth: HspiWidth::OCTO,
242 instruction: Some(cmd as u32),
243 isize: AddressSize::_16Bit,
244 idtr: true,
245 ..Default::default()
246 };
247 info!("Excuting command: 0x{:X}", transaction.instruction.unwrap());
248 self.hspi.blocking_command(&transaction).unwrap();
249 }
250
251 fn wait_write_finish_spi(&mut self) {
252 while (self.read_sr_spi() & 0x01) != 0 {}
253 }
254
255 fn wait_write_finish_octa_dtr(&mut self) {
256 while (self.read_sr_octa_dtr() & 0x01) != 0 {}
257 }
258
259 pub async fn reset_memory(&mut self) {
260 // servono entrambi i comandi?
261 self.exec_command_octa_dtr(Self::CMD_RESET_ENABLE_OCTA_DTR).await;
262 self.exec_command_octa_dtr(Self::CMD_RESET_OCTA_DTR).await;
263 self.exec_command_spi(Self::CMD_RESET_ENABLE).await;
264 self.exec_command_spi(Self::CMD_RESET).await;
265 self.wait_write_finish_spi();
266 }
267
268 async fn write_enable_spi(&mut self) {
269 self.exec_command_spi(Self::CMD_WRITE_ENABLE).await;
270 }
271
272 async fn write_enable_octa_dtr(&mut self) {
273 self.exec_command_octa_dtr(Self::CMD_WRITE_ENABLE_OCTA_DTR).await;
274 }
275
276 pub fn read_id(&mut self) -> [u8; 3] {
277 let mut buffer = [0; 6];
278 let transaction: TransferConfig = TransferConfig {
279 iwidth: HspiWidth::OCTO,
280 instruction: Some(Self::CMD_READ_ID_OCTA_DTR as u32),
281 isize: AddressSize::_16Bit,
282 idtr: true,
283 adwidth: HspiWidth::OCTO,
284 address: Some(0),
285 adsize: AddressSize::_32Bit,
286 addtr: true,
287 dwidth: HspiWidth::OCTO,
288 ddtr: true,
289 dummy: DummyCycles::_5,
290 ..Default::default()
291 };
292 info!("Reading flash id: 0x{:X}", transaction.instruction.unwrap());
293 self.hspi.blocking_read(&mut buffer, transaction).unwrap();
294 [buffer[0], buffer[2], buffer[4]]
295 }
296
297 pub async fn read_memory(&mut self, addr: u32, buffer: &mut [u8], use_dma: bool) {
298 let transaction = TransferConfig {
299 iwidth: HspiWidth::OCTO,
300 instruction: Some(Self::CMD_READ_OCTA_DTR as u32),
301 isize: AddressSize::_16Bit,
302 idtr: true,
303 adwidth: HspiWidth::OCTO,
304 address: Some(addr),
305 adsize: AddressSize::_32Bit,
306 addtr: true,
307 dwidth: HspiWidth::OCTO,
308 ddtr: true,
309 dummy: DummyCycles::_6,
310 ..Default::default()
311 };
312 if use_dma {
313 self.hspi.read(buffer, transaction).await.unwrap();
314 } else {
315 self.hspi.blocking_read(buffer, transaction).unwrap();
316 }
317 }
318
319 async fn perform_erase_octa_dtr(&mut self, addr: u32, cmd: u16) {
320 let transaction = TransferConfig {
321 iwidth: HspiWidth::OCTO,
322 instruction: Some(cmd as u32),
323 isize: AddressSize::_16Bit,
324 idtr: true,
325 adwidth: HspiWidth::OCTO,
326 address: Some(addr),
327 adsize: AddressSize::_32Bit,
328 addtr: true,
329 ..Default::default()
330 };
331 self.write_enable_octa_dtr().await;
332 self.hspi.blocking_command(&transaction).unwrap();
333 self.wait_write_finish_octa_dtr();
334 }
335
336 pub async fn erase_sector(&mut self, addr: u32) {
337 info!("Erasing 4K sector at address: 0x{:X}", addr);
338 self.perform_erase_octa_dtr(addr, Self::CMD_SECTOR_ERASE_OCTA_DTR).await;
339 }
340
341 pub async fn erase_block(&mut self, addr: u32) {
342 info!("Erasing 64K block at address: 0x{:X}", addr);
343 self.perform_erase_octa_dtr(addr, Self::CMD_BLOCK_ERASE_OCTA_DTR).await;
344 }
345
346 async fn write_page_octa_dtr(&mut self, addr: u32, buffer: &[u8], len: usize, use_dma: bool) {
347 assert!(
348 (len as u32 + (addr & 0x000000ff)) <= Self::MEMORY_PAGE_SIZE as u32,
349 "write_page(): page write length exceeds page boundary (len = {}, addr = {:X}",
350 len,
351 addr
352 );
353
354 let transaction = TransferConfig {
355 iwidth: HspiWidth::OCTO,
356 instruction: Some(Self::CMD_PAGE_PROGRAM_OCTA_DTR as u32),
357 isize: AddressSize::_16Bit,
358 idtr: true,
359 adwidth: HspiWidth::OCTO,
360 address: Some(addr),
361 adsize: AddressSize::_32Bit,
362 addtr: true,
363 dwidth: HspiWidth::OCTO,
364 ddtr: true,
365 ..Default::default()
366 };
367 self.write_enable_octa_dtr().await;
368 if use_dma {
369 self.hspi.write(buffer, transaction).await.unwrap();
370 } else {
371 self.hspi.blocking_write(buffer, transaction).unwrap();
372 }
373 self.wait_write_finish_octa_dtr();
374 }
375
376 pub async fn write_memory(&mut self, addr: u32, buffer: &[u8], use_dma: bool) {
377 let mut left = buffer.len();
378 let mut place = addr;
379 let mut chunk_start = 0;
380
381 while left > 0 {
382 let max_chunk_size = Self::MEMORY_PAGE_SIZE - (place & 0x000000ff) as usize;
383 let chunk_size = if left >= max_chunk_size { max_chunk_size } else { left };
384 let chunk = &buffer[chunk_start..(chunk_start + chunk_size)];
385 self.write_page_octa_dtr(place, chunk, chunk_size, use_dma).await;
386 place += chunk_size as u32;
387 left -= chunk_size;
388 chunk_start += chunk_size;
389 }
390 }
391
392 pub fn read_sr_spi(&mut self) -> u8 {
393 let mut buffer = [0; 1];
394 let transaction: TransferConfig = TransferConfig {
395 iwidth: HspiWidth::SING,
396 instruction: Some(Self::CMD_READ_SR as u32),
397 dwidth: HspiWidth::SING,
398 ..Default::default()
399 };
400 self.hspi.blocking_read(&mut buffer, transaction).unwrap();
401 // info!("Read MX66LM1G45G SR register: 0x{:x}", buffer[0]);
402 buffer[0]
403 }
404
405 pub fn read_sr_octa_dtr(&mut self) -> u8 {
406 let mut buffer = [0; 2];
407 let transaction: TransferConfig = TransferConfig {
408 iwidth: HspiWidth::OCTO,
409 instruction: Some(Self::CMD_READ_SR_OCTA_DTR as u32),
410 isize: AddressSize::_16Bit,
411 idtr: true,
412 adwidth: HspiWidth::OCTO,
413 address: Some(0),
414 adsize: AddressSize::_32Bit,
415 addtr: true,
416 dwidth: HspiWidth::OCTO,
417 ddtr: true,
418 dummy: DummyCycles::_5,
419 ..Default::default()
420 };
421 self.hspi.blocking_read(&mut buffer, transaction).unwrap();
422 // info!("Read MX66LM1G45G SR register: 0x{:x}", buffer[0]);
423 buffer[0]
424 }
425
426 pub fn read_cr2_spi(&mut self, addr: u32) -> u8 {
427 let mut buffer = [0; 1];
428 let transaction: TransferConfig = TransferConfig {
429 iwidth: HspiWidth::SING,
430 instruction: Some(Self::CMD_READ_CR2 as u32),
431 adwidth: HspiWidth::SING,
432 address: Some(addr),
433 adsize: AddressSize::_32Bit,
434 dwidth: HspiWidth::SING,
435 ..Default::default()
436 };
437 self.hspi.blocking_read(&mut buffer, transaction).unwrap();
438 // info!("Read MX66LM1G45G CR2[0x{:X}] register: 0x{:x}", addr, buffer[0]);
439 buffer[0]
440 }
441
442 pub fn write_cr2_spi(&mut self, addr: u32, value: u8) {
443 let buffer = [value; 1];
444 let transaction: TransferConfig = TransferConfig {
445 iwidth: HspiWidth::SING,
446 instruction: Some(Self::CMD_WRITE_CR2 as u32),
447 adwidth: HspiWidth::SING,
448 address: Some(addr),
449 adsize: AddressSize::_32Bit,
450 dwidth: HspiWidth::SING,
451 ..Default::default()
452 };
453 self.hspi.blocking_write(&buffer, transaction).unwrap();
454 }
455}