diff options
| author | chemicstry <[email protected]> | 2023-02-18 01:35:35 +0200 |
|---|---|---|
| committer | chemicstry <[email protected]> | 2023-02-18 01:37:06 +0200 |
| commit | a53f525f510de07e8c35d38ecc575cb8ea929dd9 (patch) | |
| tree | cda725bead4d8504d15d68e2b275fa529e5531d8 | |
| parent | e3f8020c3bdf726dfa451b5b190f27191507a18f (diff) | |
stm32/sdmmc: Fix SDIOv1 writes
| -rw-r--r-- | embassy-stm32/src/dma/bdma.rs | 1 | ||||
| -rw-r--r-- | embassy-stm32/src/dma/dma.rs | 23 | ||||
| -rw-r--r-- | embassy-stm32/src/dma/gpdma.rs | 10 | ||||
| -rw-r--r-- | embassy-stm32/src/dma/mod.rs | 16 | ||||
| -rw-r--r-- | embassy-stm32/src/sdmmc/mod.rs | 40 | ||||
| -rw-r--r-- | examples/stm32f4/src/bin/sdmmc.rs | 39 |
6 files changed, 124 insertions, 5 deletions
diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index 6160b9f40..5a7a408bb 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs | |||
| @@ -192,6 +192,7 @@ mod low_level_api { | |||
| 192 | options.flow_ctrl == crate::dma::FlowControl::Dma, | 192 | options.flow_ctrl == crate::dma::FlowControl::Dma, |
| 193 | "Peripheral flow control not supported" | 193 | "Peripheral flow control not supported" |
| 194 | ); | 194 | ); |
| 195 | assert!(options.fifo_threshold.is_none(), "FIFO mode not supported"); | ||
| 195 | 196 | ||
| 196 | let ch = dma.ch(channel_number as _); | 197 | let ch = dma.ch(channel_number as _); |
| 197 | 198 | ||
diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index fec60f708..385a833f7 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs | |||
| @@ -4,7 +4,7 @@ use core::task::Waker; | |||
| 4 | use embassy_cortex_m::interrupt::Priority; | 4 | use embassy_cortex_m::interrupt::Priority; |
| 5 | use embassy_sync::waitqueue::AtomicWaker; | 5 | use embassy_sync::waitqueue::AtomicWaker; |
| 6 | 6 | ||
| 7 | use super::{Burst, FlowControl, Request, TransferOptions, Word, WordSize}; | 7 | use super::{Burst, FifoThreshold, FlowControl, Request, TransferOptions, Word, WordSize}; |
| 8 | use crate::_generated::DMA_CHANNEL_COUNT; | 8 | use crate::_generated::DMA_CHANNEL_COUNT; |
| 9 | use crate::interrupt::{Interrupt, InterruptExt}; | 9 | use crate::interrupt::{Interrupt, InterruptExt}; |
| 10 | use crate::pac::dma::{regs, vals}; | 10 | use crate::pac::dma::{regs, vals}; |
| @@ -40,6 +40,17 @@ impl From<FlowControl> for vals::Pfctrl { | |||
| 40 | } | 40 | } |
| 41 | } | 41 | } |
| 42 | 42 | ||
| 43 | impl From<FifoThreshold> for vals::Fth { | ||
| 44 | fn from(value: FifoThreshold) -> Self { | ||
| 45 | match value { | ||
| 46 | FifoThreshold::Quarter => vals::Fth::QUARTER, | ||
| 47 | FifoThreshold::Half => vals::Fth::HALF, | ||
| 48 | FifoThreshold::ThreeQuarters => vals::Fth::THREEQUARTERS, | ||
| 49 | FifoThreshold::Full => vals::Fth::FULL, | ||
| 50 | } | ||
| 51 | } | ||
| 52 | } | ||
| 53 | |||
| 43 | struct ChannelState { | 54 | struct ChannelState { |
| 44 | waker: AtomicWaker, | 55 | waker: AtomicWaker, |
| 45 | } | 56 | } |
| @@ -236,6 +247,16 @@ mod low_level_api { | |||
| 236 | ch.par().write_value(peri_addr as u32); | 247 | ch.par().write_value(peri_addr as u32); |
| 237 | ch.m0ar().write_value(mem_addr as u32); | 248 | ch.m0ar().write_value(mem_addr as u32); |
| 238 | ch.ndtr().write_value(regs::Ndtr(mem_len as _)); | 249 | ch.ndtr().write_value(regs::Ndtr(mem_len as _)); |
| 250 | ch.fcr().write(|w| { | ||
| 251 | if let Some(fth) = options.fifo_threshold { | ||
| 252 | // FIFO mode | ||
| 253 | w.set_dmdis(vals::Dmdis::DISABLED); | ||
| 254 | w.set_fth(fth.into()); | ||
| 255 | } else { | ||
| 256 | // Direct mode | ||
| 257 | w.set_dmdis(vals::Dmdis::ENABLED); | ||
| 258 | } | ||
| 259 | }); | ||
| 239 | ch.cr().write(|w| { | 260 | ch.cr().write(|w| { |
| 240 | w.set_dir(dir); | 261 | w.set_dir(dir); |
| 241 | w.set_msize(data_size); | 262 | w.set_msize(data_size); |
diff --git a/embassy-stm32/src/dma/gpdma.rs b/embassy-stm32/src/dma/gpdma.rs index d252cef3e..442fee48e 100644 --- a/embassy-stm32/src/dma/gpdma.rs +++ b/embassy-stm32/src/dma/gpdma.rs | |||
| @@ -176,8 +176,16 @@ mod low_level_api { | |||
| 176 | mem_len: usize, | 176 | mem_len: usize, |
| 177 | incr_mem: bool, | 177 | incr_mem: bool, |
| 178 | data_size: WordSize, | 178 | data_size: WordSize, |
| 179 | _options: TransferOptions, | 179 | options: TransferOptions, |
| 180 | ) { | 180 | ) { |
| 181 | assert!(options.mburst == crate::dma::Burst::Single, "Burst mode not supported"); | ||
| 182 | assert!(options.pburst == crate::dma::Burst::Single, "Burst mode not supported"); | ||
| 183 | assert!( | ||
| 184 | options.flow_ctrl == crate::dma::FlowControl::Dma, | ||
| 185 | "Peripheral flow control not supported" | ||
| 186 | ); | ||
| 187 | assert!(options.fifo_threshold.is_none(), "FIFO mode not supported"); | ||
| 188 | |||
| 181 | // "Preceding reads and writes cannot be moved past subsequent writes." | 189 | // "Preceding reads and writes cannot be moved past subsequent writes." |
| 182 | fence(Ordering::SeqCst); | 190 | fence(Ordering::SeqCst); |
| 183 | 191 | ||
diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs index b51e0d40b..f5a82fb7a 100644 --- a/embassy-stm32/src/dma/mod.rs +++ b/embassy-stm32/src/dma/mod.rs | |||
| @@ -188,6 +188,19 @@ pub enum FlowControl { | |||
| 188 | 188 | ||
| 189 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] | 189 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] |
| 190 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 190 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 191 | pub enum FifoThreshold { | ||
| 192 | /// 1/4 full FIFO | ||
| 193 | Quarter, | ||
| 194 | /// 1/2 full FIFO | ||
| 195 | Half, | ||
| 196 | /// 3/4 full FIFO | ||
| 197 | ThreeQuarters, | ||
| 198 | /// Full FIFO | ||
| 199 | Full, | ||
| 200 | } | ||
| 201 | |||
| 202 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] | ||
| 203 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 191 | pub struct TransferOptions { | 204 | pub struct TransferOptions { |
| 192 | /// Peripheral burst transfer configuration | 205 | /// Peripheral burst transfer configuration |
| 193 | pub pburst: Burst, | 206 | pub pburst: Burst, |
| @@ -195,6 +208,8 @@ pub struct TransferOptions { | |||
| 195 | pub mburst: Burst, | 208 | pub mburst: Burst, |
| 196 | /// Flow control configuration | 209 | /// Flow control configuration |
| 197 | pub flow_ctrl: FlowControl, | 210 | pub flow_ctrl: FlowControl, |
| 211 | /// FIFO threshold for DMA FIFO mode. If none, direct mode is used. | ||
| 212 | pub fifo_threshold: Option<FifoThreshold>, | ||
| 198 | } | 213 | } |
| 199 | 214 | ||
| 200 | impl Default for TransferOptions { | 215 | impl Default for TransferOptions { |
| @@ -203,6 +218,7 @@ impl Default for TransferOptions { | |||
| 203 | pburst: Burst::Single, | 218 | pburst: Burst::Single, |
| 204 | mburst: Burst::Single, | 219 | mburst: Burst::Single, |
| 205 | flow_ctrl: FlowControl::Dma, | 220 | flow_ctrl: FlowControl::Dma, |
| 221 | fifo_threshold: None, | ||
| 206 | } | 222 | } |
| 207 | } | 223 | } |
| 208 | } | 224 | } |
diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index 3f07e0c07..3c99a0b6c 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | 2 | ||
| 3 | use core::default::Default; | 3 | use core::default::Default; |
| 4 | use core::future::poll_fn; | 4 | use core::future::poll_fn; |
| 5 | use core::ops::{Deref, DerefMut}; | ||
| 5 | use core::task::Poll; | 6 | use core::task::Poll; |
| 6 | 7 | ||
| 7 | use embassy_hal_common::drop::OnDrop; | 8 | use embassy_hal_common::drop::OnDrop; |
| @@ -40,7 +41,23 @@ impl Default for Signalling { | |||
| 40 | } | 41 | } |
| 41 | 42 | ||
| 42 | #[repr(align(4))] | 43 | #[repr(align(4))] |
| 43 | pub struct DataBlock([u8; 512]); | 44 | #[derive(Debug, Clone)] |
| 45 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 46 | pub struct DataBlock(pub [u8; 512]); | ||
| 47 | |||
| 48 | impl Deref for DataBlock { | ||
| 49 | type Target = [u8; 512]; | ||
| 50 | |||
| 51 | fn deref(&self) -> &Self::Target { | ||
| 52 | &self.0 | ||
| 53 | } | ||
| 54 | } | ||
| 55 | |||
| 56 | impl DerefMut for DataBlock { | ||
| 57 | fn deref_mut(&mut self) -> &mut Self::Target { | ||
| 58 | &mut self.0 | ||
| 59 | } | ||
| 60 | } | ||
| 44 | 61 | ||
| 45 | /// Errors | 62 | /// Errors |
| 46 | #[non_exhaustive] | 63 | #[non_exhaustive] |
| @@ -570,6 +587,12 @@ impl SdmmcInner { | |||
| 570 | regs.clkcr().write(|w| { | 587 | regs.clkcr().write(|w| { |
| 571 | w.set_pwrsav(false); | 588 | w.set_pwrsav(false); |
| 572 | w.set_negedge(false); | 589 | w.set_negedge(false); |
| 590 | |||
| 591 | // Hardware flow control is broken on SDIOv1 and causes clock glitches, which result in CRC errors. | ||
| 592 | // See chip erratas for more details. | ||
| 593 | #[cfg(sdmmc_v1)] | ||
| 594 | w.set_hwfc_en(false); | ||
| 595 | #[cfg(sdmmc_v2)] | ||
| 573 | w.set_hwfc_en(true); | 596 | w.set_hwfc_en(true); |
| 574 | 597 | ||
| 575 | #[cfg(sdmmc_v1)] | 598 | #[cfg(sdmmc_v1)] |
| @@ -807,10 +830,16 @@ impl SdmmcInner { | |||
| 807 | let regs = self.0; | 830 | let regs = self.0; |
| 808 | let on_drop = OnDrop::new(|| unsafe { self.on_drop() }); | 831 | let on_drop = OnDrop::new(|| unsafe { self.on_drop() }); |
| 809 | 832 | ||
| 833 | // sdmmc_v1 uses different cmd/dma order than v2, but only for writes | ||
| 834 | #[cfg(sdmmc_v1)] | ||
| 835 | self.cmd(Cmd::write_single_block(address), true)?; | ||
| 836 | |||
| 810 | unsafe { | 837 | unsafe { |
| 811 | self.prepare_datapath_write(buffer as *const [u32; 128], 512, 9, data_transfer_timeout, dma); | 838 | self.prepare_datapath_write(buffer as *const [u32; 128], 512, 9, data_transfer_timeout, dma); |
| 812 | self.data_interrupts(true); | 839 | self.data_interrupts(true); |
| 813 | } | 840 | } |
| 841 | |||
| 842 | #[cfg(sdmmc_v2)] | ||
| 814 | self.cmd(Cmd::write_single_block(address), true)?; | 843 | self.cmd(Cmd::write_single_block(address), true)?; |
| 815 | 844 | ||
| 816 | let res = poll_fn(|cx| { | 845 | let res = poll_fn(|cx| { |
| @@ -922,7 +951,9 @@ impl SdmmcInner { | |||
| 922 | let request = dma.request(); | 951 | let request = dma.request(); |
| 923 | dma.start_read(request, regs.fifor().ptr() as *const u32, buffer, crate::dma::TransferOptions { | 952 | dma.start_read(request, regs.fifor().ptr() as *const u32, buffer, crate::dma::TransferOptions { |
| 924 | pburst: crate::dma::Burst::Incr4, | 953 | pburst: crate::dma::Burst::Incr4, |
| 954 | mburst: crate::dma::Burst::Incr4, | ||
| 925 | flow_ctrl: crate::dma::FlowControl::Peripheral, | 955 | flow_ctrl: crate::dma::FlowControl::Peripheral, |
| 956 | fifo_threshold: Some(crate::dma::FifoThreshold::Full), | ||
| 926 | ..Default::default() | 957 | ..Default::default() |
| 927 | }); | 958 | }); |
| 928 | } else if #[cfg(sdmmc_v2)] { | 959 | } else if #[cfg(sdmmc_v2)] { |
| @@ -970,7 +1001,9 @@ impl SdmmcInner { | |||
| 970 | let request = dma.request(); | 1001 | let request = dma.request(); |
| 971 | dma.start_write(request, buffer, regs.fifor().ptr() as *mut u32, crate::dma::TransferOptions { | 1002 | dma.start_write(request, buffer, regs.fifor().ptr() as *mut u32, crate::dma::TransferOptions { |
| 972 | pburst: crate::dma::Burst::Incr4, | 1003 | pburst: crate::dma::Burst::Incr4, |
| 1004 | mburst: crate::dma::Burst::Incr4, | ||
| 973 | flow_ctrl: crate::dma::FlowControl::Peripheral, | 1005 | flow_ctrl: crate::dma::FlowControl::Peripheral, |
| 1006 | fifo_threshold: Some(crate::dma::FifoThreshold::Full), | ||
| 974 | ..Default::default() | 1007 | ..Default::default() |
| 975 | }); | 1008 | }); |
| 976 | } else if #[cfg(sdmmc_v2)] { | 1009 | } else if #[cfg(sdmmc_v2)] { |
| @@ -982,6 +1015,11 @@ impl SdmmcInner { | |||
| 982 | regs.dctrl().modify(|w| { | 1015 | regs.dctrl().modify(|w| { |
| 983 | w.set_dblocksize(block_size); | 1016 | w.set_dblocksize(block_size); |
| 984 | w.set_dtdir(false); | 1017 | w.set_dtdir(false); |
| 1018 | #[cfg(sdmmc_v1)] | ||
| 1019 | { | ||
| 1020 | w.set_dmaen(true); | ||
| 1021 | w.set_dten(true); | ||
| 1022 | } | ||
| 985 | }); | 1023 | }); |
| 986 | } | 1024 | } |
| 987 | 1025 | ||
diff --git a/examples/stm32f4/src/bin/sdmmc.rs b/examples/stm32f4/src/bin/sdmmc.rs index 0edd8a61a..b57e955f6 100644 --- a/examples/stm32f4/src/bin/sdmmc.rs +++ b/examples/stm32f4/src/bin/sdmmc.rs | |||
| @@ -4,11 +4,15 @@ | |||
| 4 | 4 | ||
| 5 | use defmt::*; | 5 | use defmt::*; |
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_stm32::sdmmc::Sdmmc; | 7 | use embassy_stm32::sdmmc::{DataBlock, Sdmmc}; |
| 8 | use embassy_stm32::time::mhz; | 8 | use embassy_stm32::time::mhz; |
| 9 | use embassy_stm32::{interrupt, Config}; | 9 | use embassy_stm32::{interrupt, Config}; |
| 10 | use {defmt_rtt as _, panic_probe as _}; | 10 | use {defmt_rtt as _, panic_probe as _}; |
| 11 | 11 | ||
| 12 | /// This is a safeguard to not overwrite any data on the SD card. | ||
| 13 | /// If you don't care about SD card contents, set this to `true` to test writes. | ||
| 14 | const ALLOW_WRITES: bool = false; | ||
| 15 | |||
| 12 | #[embassy_executor::main] | 16 | #[embassy_executor::main] |
| 13 | async fn main(_spawner: Spawner) -> ! { | 17 | async fn main(_spawner: Spawner) -> ! { |
| 14 | let mut config = Config::default(); | 18 | let mut config = Config::default(); |
| @@ -34,11 +38,42 @@ async fn main(_spawner: Spawner) -> ! { | |||
| 34 | // Should print 400kHz for initialization | 38 | // Should print 400kHz for initialization |
| 35 | info!("Configured clock: {}", sdmmc.clock().0); | 39 | info!("Configured clock: {}", sdmmc.clock().0); |
| 36 | 40 | ||
| 37 | unwrap!(sdmmc.init_card(mhz(25)).await); | 41 | unwrap!(sdmmc.init_card(mhz(24)).await); |
| 38 | 42 | ||
| 39 | let card = unwrap!(sdmmc.card()); | 43 | let card = unwrap!(sdmmc.card()); |
| 40 | 44 | ||
| 41 | info!("Card: {:#?}", Debug2Format(card)); | 45 | info!("Card: {:#?}", Debug2Format(card)); |
| 46 | info!("Clock: {}", sdmmc.clock()); | ||
| 47 | |||
| 48 | // Arbitrary block index | ||
| 49 | let block_idx = 16; | ||
| 50 | |||
| 51 | // SDMMC uses `DataBlock` instead of `&[u8]` to ensure 4 byte alignment required by the hardware. | ||
| 52 | let mut block = DataBlock([0u8; 512]); | ||
| 53 | |||
| 54 | sdmmc.read_block(block_idx, &mut block).await.unwrap(); | ||
| 55 | info!("Read: {=[u8]:X}...{=[u8]:X}", block[..8], block[512 - 8..]); | ||
| 56 | |||
| 57 | if !ALLOW_WRITES { | ||
| 58 | info!("Writing is disabled."); | ||
| 59 | loop {} | ||
| 60 | } | ||
| 61 | |||
| 62 | info!("Filling block with 0x55"); | ||
| 63 | block.fill(0x55); | ||
| 64 | sdmmc.write_block(block_idx, &block).await.unwrap(); | ||
| 65 | info!("Write done"); | ||
| 66 | |||
| 67 | sdmmc.read_block(block_idx, &mut block).await.unwrap(); | ||
| 68 | info!("Read: {=[u8]:X}...{=[u8]:X}", block[..8], block[512 - 8..]); | ||
| 69 | |||
| 70 | info!("Filling block with 0xAA"); | ||
| 71 | block.fill(0xAA); | ||
| 72 | sdmmc.write_block(block_idx, &block).await.unwrap(); | ||
| 73 | info!("Write done"); | ||
| 74 | |||
| 75 | sdmmc.read_block(block_idx, &mut block).await.unwrap(); | ||
| 76 | info!("Read: {=[u8]:X}...{=[u8]:X}", block[..8], block[512 - 8..]); | ||
| 42 | 77 | ||
| 43 | loop {} | 78 | loop {} |
| 44 | } | 79 | } |
