Skip to content

Commit

Permalink
Introduce SDMMC write_blocks
Browse files Browse the repository at this point in the history
`write_block` needs to be called for every 512 bytes long block that is
to be written to the SD card. Each of these calls requires the overhead
of negotiating the write command.

With this patch, `write_blocks` is introduced, mirroring the behavior
of `read_block` and `read_blocks`. This new method helped me to improve
write speed of the original 9492 kB/s to 66252 kB/s.

Signed-off-by: Petr Horacek <[email protected]>
  • Loading branch information
phoracek committed Sep 18, 2023
1 parent 89b90ad commit 117f43a
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 35 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
58 changes: 24 additions & 34 deletions examples/sdmmc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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!");

Expand Down
74 changes: 74 additions & 0 deletions src/sdmmc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ pub enum Error {
Crc,
DataCrcFail,
RxOverFlow,
TxUnderFlow,
NoCard,
BadClock,
InvalidConfiguration,
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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<CardStatus<P>, Error> {
Expand Down

0 comments on commit 117f43a

Please sign in to comment.