diff options
| author | Mateusz Butkiewicz <[email protected]> | 2023-03-22 08:44:58 +0100 |
|---|---|---|
| committer | Mateusz Butkiewicz <[email protected]> | 2023-03-27 13:20:00 +0200 |
| commit | 6a802c47083e4f6bb7b7c6c06fd2ef3e291a5212 (patch) | |
| tree | 96ba93e9e9c942710bc2c722bf351ccc184260a9 | |
| parent | 732614579b86c2a63856b6e8e2622e09322600a7 (diff) | |
feat(stm32:qspi): add support for QSPI in stm32
Implemented with help of Tomasz GrzeĊ <[email protected]>.
| -rw-r--r-- | embassy-stm32/build.rs | 7 | ||||
| -rw-r--r-- | embassy-stm32/src/lib.rs | 3 | ||||
| -rw-r--r-- | embassy-stm32/src/qspi/mod.rs | 342 |
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 | ))] |
| 49 | pub mod flash; | 49 | pub mod flash; |
| 50 | pub mod pwm; | 50 | pub mod pwm; |
| 51 | #[cfg(quadspi)] | ||
| 52 | pub mod qspi; | ||
| 51 | #[cfg(rng)] | 53 | #[cfg(rng)] |
| 52 | pub mod rng; | 54 | pub mod rng; |
| 53 | #[cfg(sdmmc)] | 55 | #[cfg(sdmmc)] |
| @@ -60,7 +62,6 @@ pub mod usart; | |||
| 60 | pub mod usb; | 62 | pub mod usb; |
| 61 | #[cfg(otg)] | 63 | #[cfg(otg)] |
| 62 | pub mod usb_otg; | 64 | pub mod usb_otg; |
| 63 | |||
| 64 | #[cfg(iwdg)] | 65 | #[cfg(iwdg)] |
| 65 | pub mod wdg; | 66 | pub 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 | |||
| 3 | use embassy_hal_common::{into_ref, PeripheralRef}; | ||
| 4 | |||
| 5 | use crate::dma::TransferOptions; | ||
| 6 | use crate::gpio::sealed::AFType; | ||
| 7 | use crate::gpio::AnyPin; | ||
| 8 | use crate::pac::quadspi::Quadspi as Regs; | ||
| 9 | use crate::rcc::RccPeripheral; | ||
| 10 | use crate::{peripherals, Peripheral}; | ||
| 11 | |||
| 12 | pub struct QspiWidth; | ||
| 13 | |||
| 14 | #[allow(dead_code)] | ||
| 15 | impl 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 | |||
| 22 | struct QspiMode; | ||
| 23 | |||
| 24 | #[allow(dead_code)] | ||
| 25 | impl 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 | |||
| 32 | pub 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 | |||
| 42 | impl 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 | |||
| 56 | pub 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 | |||
| 64 | impl 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)] | ||
| 77 | pub 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 | |||
| 89 | impl<'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 | |||
| 315 | pub(crate) mod sealed { | ||
| 316 | use super::*; | ||
| 317 | |||
| 318 | pub trait Instance { | ||
| 319 | const REGS: Regs; | ||
| 320 | } | ||
| 321 | } | ||
| 322 | |||
| 323 | pub trait Instance: Peripheral<P = Self> + sealed::Instance + RccPeripheral {} | ||
| 324 | |||
| 325 | pin_trait!(SckPin, Instance); | ||
| 326 | pin_trait!(D0Pin, Instance); | ||
| 327 | pin_trait!(D1Pin, Instance); | ||
| 328 | pin_trait!(D2Pin, Instance); | ||
| 329 | pin_trait!(D3Pin, Instance); | ||
| 330 | pin_trait!(NSSPin, Instance); | ||
| 331 | |||
| 332 | dma_trait!(QuadDma, Instance); | ||
| 333 | |||
| 334 | foreach_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 | ); | ||
