diff --git a/CHANGELOG.md b/CHANGELOG.md index d6db015b..966a6520 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,8 @@ * Update `smoltcp` dependency to `0.9.0` * MSRV increased to 1.65.0 * add `IntoAf` trait to restrict `into_alternate` [#346] -* sdmmc: Fix read speed test. +* sdmmc: Introduce `write_blocks` +* sdmmc: Fix read speed test ## [v0.14.0] 2023-03-22 diff --git a/examples/sdmmc.rs b/examples/sdmmc.rs index 900cc002..73dd3417 100644 --- a/examples/sdmmc.rs +++ b/examples/sdmmc.rs @@ -144,55 +144,45 @@ fn main() -> ! { info!("----------------------"); info!(""); - // Read test - let mut buffer = [0u8; 512 * 10]; - cp.DWT.enable_cycle_counter(); - let start = pac::DWT::cycle_count(); - - // Read 10 blocks - sdmmc.read_blocks(0, &mut buffer).unwrap(); + // Write single block test + let write_buffer = [0x34; 512]; + let start = pac::DWT::cycle_count(); + sdmmc.write_block(0, &write_buffer).unwrap(); let end = pac::DWT::cycle_count(); let duration = (end - start) as f32 / ccdr.clocks.c_ck().raw() as f32; + info!("Wrote single block at {} bytes/s", 512.0 / duration); - info!("Read 10 blocks at {} bytes/s", 5120. / duration); - info!(""); - - let write_buffer = [0x34; 512]; + // Write multiple blocks test + let write_buffer = [0x34; 512 * 16]; let start = pac::DWT::cycle_count(); + sdmmc.write_blocks(0, &write_buffer).unwrap(); + let end = pac::DWT::cycle_count(); + let duration = (end - start) as f32 / ccdr.clocks.c_ck().raw() as f32; + info!("Wrote 16 blocks at {} bytes/s", (512.0 * 16.0) / duration); - for i in 0..10 { - if let Err(err) = sdmmc.write_block(i, &write_buffer) { - info!("Failed to write block {}: {:?}", i, err); - } - } - + // Read single block test + let mut buffer = [0u8; 512]; + let start = pac::DWT::cycle_count(); + sdmmc.read_block(0, &mut buffer).unwrap(); let end = pac::DWT::cycle_count(); let duration = (end - start) as f32 / ccdr.clocks.c_ck().raw() as f32; + info!("Read single block at {} bytes/s", 512.0 / duration); - info!("Wrote 10 blocks at {} bytes/s", 5120. / duration); - info!(""); + // Read multiple blocks test + let mut buffer = [0u8; 512 * 16]; + let start = pac::DWT::cycle_count(); + sdmmc.read_blocks(0, &mut buffer).unwrap(); + let end = pac::DWT::cycle_count(); + let duration = (end - start) as f32 / ccdr.clocks.c_ck().raw() as f32; + info!("Read 16 blocks at {} bytes/s", (512.0 * 16.0) / duration); info!("Verification test..."); - // Write 10 blocks - for i in 0..10 { - if let Err(err) = sdmmc.write_block(i, &write_buffer) { - info!("Failed to write block {}: {:?}", i, err); - } else { - info!("Wrote block {}", i); - } - - // Read back - sdmmc.read_blocks(0, &mut buffer).unwrap(); - } - - // Check the read for byte in buffer.iter() { assert_eq!(*byte, 0x34); } - info!("Verified 10 blocks"); - info!(""); + info!("Verified all blocks"); info!("Done!"); diff --git a/src/sdmmc.rs b/src/sdmmc.rs index 8027ff87..847e327a 100644 --- a/src/sdmmc.rs +++ b/src/sdmmc.rs @@ -295,6 +295,7 @@ pub enum Error { Crc, DataCrcFail, RxOverFlow, + TxUnderFlow, NoCard, BadClock, InvalidConfiguration, @@ -342,6 +343,8 @@ macro_rules! err_from_datapath_sm { return Err(Error::DataCrcFail); } else if $status.rxoverr().bit() { return Err(Error::RxOverFlow); + } else if $status.txunderr().bit() { + return Err(Error::TxUnderFlow); } else if $status.dtimeout().bit() { return Err(Error::Timeout); } @@ -793,6 +796,77 @@ macro_rules! sdmmc { Err(Error::SoftwareTimeout) } + /// Write multiple blocks to card. The length of the buffer + /// must be multiple of 512. + /// + /// `address` is the block address. + pub fn write_blocks( + &mut self, + address: u32, + buffer: &[u8] + ) -> Result<(), Error> { + let _card = self.card()?; + + assert!(buffer.len() % 512 == 0, + "Buffer length must be a multiple of 512"); + let n_blocks = buffer.len() / 512; + + if !self.cmd16_illegal { + self.cmd(common_cmd::set_block_length(512))?; // CMD16 + } + + // Setup write command + self.start_datapath_transfer(512 * n_blocks as u32, 9, Dir::HostToCard); + self.cmd(common_cmd::write_multiple_blocks(address))?; // CMD25 + + let mut i = 0; + let mut status; + while { + status = self.sdmmc.star.read(); + !(status.txunderr().bit() + || status.dcrcfail().bit() + || status.dtimeout().bit() + || status.dataend().bit()) + } { + if status.txfifohe().bit() { + for _ in 0..8 { + let mut wb = [0u8; 4]; + wb.copy_from_slice(&buffer[i..i + 4]); + let word = u32::from_le_bytes(wb); + self.sdmmc.fifor.write(|w| unsafe { w.bits(word) }); + i += 4; + } + } + + if i >= buffer.len() { + break + } + } + + while { + status = self.sdmmc.star.read(); + !(status.txunderr().bit() + || status.dcrcfail().bit() + || status.dtimeout().bit() + || status.dataend().bit()) + } {} + self.cmd(common_cmd::stop_transmission())?; // CMD12 + + err_from_datapath_sm!(status); + self.clear_static_interrupt_flags(); + + let mut timeout: u32 = 0xFFFF_FFFF; + + // Try to read card status (CMD13) + while timeout > 0 { + if self.card_ready()? { + return Ok(()); + } + timeout -= 1; + } + Err(Error::SoftwareTimeout) + } + /// Query the card status (CMD13, returns R1) /// fn read_status(&self) -> Result, Error> {