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 <petr@zlosynth.com>
  • Loading branch information
phoracek committed Sep 18, 2023
1 parent 89b90ad commit 0c61690
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 11 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
8 changes: 2 additions & 6 deletions examples/sdmmc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,14 +159,10 @@ fn main() -> ! {
info!("Read 10 blocks at {} bytes/s", 5120. / duration);
info!("");

let write_buffer = [0x34; 512];
let write_buffer = [0x34; 512 * 10];
let start = pac::DWT::cycle_count();

for i in 0..10 {
if let Err(err) = sdmmc.write_block(i, &write_buffer) {
info!("Failed to write block {}: {:?}", i, err);
}
}
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;
Expand Down
74 changes: 70 additions & 4 deletions src/sdmmc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -738,7 +738,7 @@ macro_rules! sdmmc {
Ok(())
}

/// Write block to card. Buffer must be 512 bytes
/// Write block to card. Buffer must be 512 bytes
pub fn write_block(
&mut self,
address: u32,
Expand Down Expand Up @@ -793,6 +793,70 @@ 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;
}
}

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 Expand Up @@ -1265,10 +1329,12 @@ macro_rules! sdmmc {
blocks: &[embedded_sdmmc::Block],
start_block_idx: embedded_sdmmc::BlockIdx,
) -> Result<(), Self::Error> {
let start = start_block_idx.0;
let mut sdmmc = self.sdmmc.borrow_mut();
for block_idx in start..(start + blocks.len() as u32) {
sdmmc.write_block(block_idx, &blocks[(block_idx - start) as usize].contents)?;
if blocks.len() == 1 {
sdmmc.write_block(start_block_idx.0, &blocks[0].contents)?;
} else {
// TODO: flatten it into a single byte array
sdmmc.write_blocks(start_block_idx.0, &blocks[(block_idx - start) as usize].contents)?;
}
Ok(())
}
Expand Down

0 comments on commit 0c61690

Please sign in to comment.