Skip to content

Commit 835b2e4

Browse files
authored
Write commands and read response in single transfer
1 parent 5f270ac commit 835b2e4

File tree

1 file changed

+55
-44
lines changed

1 file changed

+55
-44
lines changed

src/inner/sdcard/mod.rs

Lines changed: 55 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ use crate::{debug, trace, warn};
2121
// Types and Implementations
2222
// ****************************************************************************
2323

24+
/// The max number of bytes in a command response.
25+
const COMMAND_RESPONSE_BYTES: usize = 5;
26+
2427
/// Driver for an SD Card on an SPI bus.
2528
///
2629
/// Built from an [`SpiDevice`] implementation and a Chip Select pin.
@@ -254,10 +257,8 @@ where
254257
self.write_data(DATA_START_BLOCK, &blocks[0].contents)
255258
.await?;
256259
self.wait_not_busy(Delay::new_write()).await?;
257-
if self.card_command(CMD13, 0).await? != 0x00 {
258-
return Err(Error::WriteError);
259-
}
260-
if self.read_byte().await? != 0x00 {
260+
let response = self.card_command(CMD13, 0).await?;
261+
if response[0] != 0x00 || response[1] != 0x00 {
261262
return Err(Error::WriteError);
262263
}
263264
} else {
@@ -317,15 +318,17 @@ where
317318
match self.card_type {
318319
Some(CardType::SD1) => {
319320
let mut csd = CsdV1::new();
320-
if self.card_command(CMD9, 0).await? != 0 {
321+
let cmd_response = self.card_command(CMD9, 0).await?;
322+
if cmd_response[0] != 0 {
321323
return Err(Error::RegisterReadError);
322324
}
323325
self.read_data(&mut csd.data).await?;
324326
Ok(Csd::V1(csd))
325327
}
326328
Some(CardType::SD2 | CardType::SDHC) => {
327329
let mut csd = CsdV2::new();
328-
if self.card_command(CMD9, 0).await? != 0 {
330+
let cmd_response = self.card_command(CMD9, 0).await?;
331+
if cmd_response[0] != 0 {
329332
return Err(Error::RegisterReadError);
330333
}
331334
self.read_data(&mut csd.data).await?;
@@ -429,12 +432,12 @@ where
429432
Err(e) => {
430433
return Err(e);
431434
}
432-
Ok(R1_IDLE_STATE) => {
435+
Ok([R1_IDLE_STATE, ..]) => {
433436
break;
434437
}
435438
Ok(_r) => {
436439
// Try again
437-
warn!("Got response: {:x}, trying again..", _r);
440+
warn!("Got response: {:x}, trying again..", _r[0]);
438441
}
439442
}
440443

@@ -444,19 +447,18 @@ where
444447
debug!("Enable CRC: {}", self.options.use_crc);
445448
// "The SPI interface is initialized in the CRC OFF mode in default"
446449
// -- SD Part 1 Physical Layer Specification v9.00, Section 7.2.2 Bus Transfer Protection
447-
if self.options.use_crc && self.card_command(CMD59, 1).await? != R1_IDLE_STATE {
450+
if self.options.use_crc && self.card_command(CMD59, 1).await?[0] != R1_IDLE_STATE {
448451
return Err(Error::CantEnableCRC);
449452
}
450453
// Check card version
451454
let mut delay = Delay::new_command();
452455
let arg = loop {
453-
if self.card_command(CMD8, 0x1AA).await? == (R1_ILLEGAL_COMMAND | R1_IDLE_STATE) {
456+
let cmd_response = self.card_command(CMD8, 0x1AA).await?;
457+
if cmd_response[0] == (R1_ILLEGAL_COMMAND | R1_IDLE_STATE) {
454458
card_type = CardType::SD1;
455459
break 0;
456460
}
457-
let mut buffer = [0xFF; 4];
458-
self.transfer_bytes(&mut buffer).await?;
459-
let status = buffer[3];
461+
let status = cmd_response[4];
460462
if status == 0xAA {
461463
card_type = CardType::SD2;
462464
break 0x4000_0000;
@@ -474,12 +476,11 @@ where
474476
}
475477

476478
if card_type == CardType::SD2 {
477-
if self.card_command(CMD58, 0).await? != 0 {
479+
let cmd_response = self.card_command(CMD58, 0).await?;
480+
if cmd_response[0] != 0 {
478481
return Err(Error::Cmd58Error);
479482
}
480-
let mut buffer = [0xFF; 4];
481-
self.transfer_bytes(&mut buffer).await?;
482-
if (buffer[0] & 0xC0) == 0xC0 {
483+
if (cmd_response[1] & 0xC0) == 0xC0 {
483484
card_type = CardType::SDHC;
484485
}
485486
// Ignore the other three bytes
@@ -496,42 +497,50 @@ where
496497
/// Perform an application-specific command.
497498
async fn card_acmd(&mut self, command: u8, arg: u32) -> Result<u8, Error> {
498499
self.card_command(CMD55, 0).await?;
499-
self.card_command(command, arg).await
500+
self.card_command(command, arg).await.map(|r| r[0])
500501
}
501502

502503
/// Perform a command.
503-
async fn card_command(&mut self, command: u8, arg: u32) -> Result<u8, Error> {
504+
async fn card_command(
505+
&mut self,
506+
command: u8,
507+
arg: u32,
508+
) -> Result<[u8; COMMAND_RESPONSE_BYTES], Error> {
504509
if command != CMD0 && command != CMD12 {
505510
self.wait_not_busy(Delay::new_command()).await?;
506511
}
507512

508-
let mut buf = [
509-
0x40 | command,
510-
(arg >> 24) as u8,
511-
(arg >> 16) as u8,
512-
(arg >> 8) as u8,
513-
arg as u8,
514-
0,
515-
];
513+
const COMMAND_BYTES: usize = 6;
514+
// The maximum number of bytes before the card should start sending the response. Based on
515+
// http://elm-chan.org/docs/mmc/mmc_e.html
516+
const MAX_WAIT_BYTES: usize = 8;
517+
const TRANSFER_BYTES: usize = COMMAND_BYTES + MAX_WAIT_BYTES + COMMAND_RESPONSE_BYTES;
518+
519+
let mut buf = [0xFF; TRANSFER_BYTES];
520+
buf[0] = 0x40 | command;
521+
buf[1] = (arg >> 24) as u8;
522+
buf[2] = (arg >> 16) as u8;
523+
buf[3] = (arg >> 8) as u8;
524+
buf[4] = arg as u8;
516525
buf[5] = crc7(&buf[0..5]);
517526

518-
self.write_bytes(&buf).await?;
519-
520-
// skip stuff byte for stop read
521-
if command == CMD12 {
522-
let _result = self.read_byte().await?;
523-
}
524-
525-
let mut delay = Delay::new_command();
526-
loop {
527-
let result = self.read_byte().await?;
528-
if (result & 0x80) == ERROR_OK {
529-
return Ok(result);
530-
}
531-
delay
532-
.delay(&mut self.delayer, Error::TimeoutCommand(command))
533-
.await?;
534-
}
527+
// Write the command and read the response in a single SPI transfer. In the async case
528+
// this allows performing the command without CPU attention and it removes the risk of
529+
// timeouts on the SD card caused by scheduling delays between sending the command and
530+
// reading the response.
531+
self.transfer_bytes(&mut buf).await?;
532+
533+
// Find the response in the buffer
534+
buf.iter()
535+
.skip(COMMAND_BYTES)
536+
.position(|&b| (b & 0x80) == ERROR_OK)
537+
.and_then(|pos| {
538+
let start = pos + COMMAND_BYTES;
539+
let end = start + COMMAND_RESPONSE_BYTES;
540+
buf.get(start..end)
541+
})
542+
.and_then(|bytes| bytes.try_into().ok())
543+
.ok_or(Error::UnexpectedResponse(command))
535544
}
536545

537546
/// Receive a byte from the SPI bus by clocking out an 0xFF byte.
@@ -627,6 +636,8 @@ pub enum Error {
627636
TimeoutWaitNotBusy,
628637
/// We didn't get a response when executing this command
629638
TimeoutCommand(u8),
639+
/// We didn't get the expected response when executing this command
640+
UnexpectedResponse(u8),
630641
/// We didn't get a response when executing this application-specific command
631642
TimeoutACommand(u8),
632643
/// We got a bad response from Command 58

0 commit comments

Comments
 (0)