Skip to content

Commit

Permalink
Merge pull request ARMmbed#5 from simonqhughes/master
Browse files Browse the repository at this point in the history
Fix for armmbed/sd-driver issue 4: SDCard Initialisation fails for ES…
  • Loading branch information
sg- authored Apr 6, 2017
2 parents 3b0529a + 5830844 commit 37b5043
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 13 deletions.
97 changes: 84 additions & 13 deletions features/filesystem/sd/SDBlockDevice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,37 @@
*
* SPI Startup
* -----------
* The SD card powers up in SD mode. The SPI interface mode is selected by
* asserting CS low and sending the reset command (CMD0). The card will
* respond with a (R1) response.
* The SD card powers up in SD mode. The start-up procedure is complicated
* by the requirement to support older SDCards in a backwards compatible
* way with the new higher capacity variants SDHC and SDHC.
*
* The following figures from the specification with associated text describe
* the SPI mode initialisation process:
* - Figure 7-1: SD Memory Card State Diagram (SPI mode)
* - Figure 7-2: SPI Mode Initialization Flow
*
* Firstly, a low initial clock should be selected (in the range of 100-
* 400kHZ). After initialisation has been completed, the switch to a
* higher clock speed can be made (e.g. 1MHz). Newer cards will support
* higher speeds than the default _transfer_sck defined here.
*
* Next, note the following from the SDCard specification (note to
* Figure 7-1):
*
* In any of the cases CMD1 is not recommended because it may be difficult for the host
* to distinguish between MultiMediaCard and SD Memory Card
*
* Hence CMD1 is not used for the initialisation sequence.
*
* The SPI interface mode is selected by asserting CS low and sending the
* reset command (CMD0). The card will respond with a (R1) response.
* In practice many cards initially respond with 0xff or invalid data
* which is ignored. Data is read until a valid response is received
* or the number of re-reads has exceeded a maximim count. If a valid
* response is not received then the CMD0 can be retried. This
* has been found to successfully initialise cards where the SPI master
* (on MCU) has been reset but the SDCard has not, so the first
* CMD0 may be lost.
*
* CMD8 is optionally sent to determine the voltage range supported, and
* indirectly determine whether it is a version 1.x SD/non-SD card or
Expand Down Expand Up @@ -119,9 +147,11 @@
#include "SDBlockDevice.h"
#include "mbed_debug.h"

#define SD_COMMAND_TIMEOUT 5000

#define SD_DBG 0
#define SD_COMMAND_TIMEOUT 5000 /*!< Number of times to query card for correct result */
#define SD_CMD0_GO_IDLE_STATE_RETRIES 3 /*!< Number of retries for sending CMDO*/
#define SD_CMD0_GO_IDLE_STATE 0x00 /*!< CMD0 code value */
#define SD_CMD0_INVALID_RESPONSE_TIMEOUT -1 /*!< CMD0 received invalid responses and timed out */
#define SD_DBG 0

#define SD_BLOCK_DEVICE_ERROR_WOULD_BLOCK -5001 /*!< operation would block */
#define SD_BLOCK_DEVICE_ERROR_UNSUPPORTED -5002 /*!< unsupported operation */
Expand All @@ -130,6 +160,7 @@
#define SD_BLOCK_DEVICE_ERROR_NO_DEVICE -5005 /*!< device is missing or not connected */
#define SD_BLOCK_DEVICE_ERROR_WRITE_PROTECTED -5006 /*!< write protected */


SDBlockDevice::SDBlockDevice(PinName mosi, PinName miso, PinName sclk, PinName cs)
: _spi(mosi, miso, sclk), _cs(cs), _is_initialized(0)
{
Expand Down Expand Up @@ -177,13 +208,13 @@ int SDBlockDevice::_initialise_card()
}
_spi.unlock();

// send CMD0, should return with all zeros except IDLE STATE set (bit 0)
if (_cmd(0, 0) != R1_IDLE_STATE) {
/* Transition from SD Card mode to SPI mode by sending CMD0 GO_IDLE_STATE command */
if (_go_idle_state() != R1_IDLE_STATE) {
debug_if(_dbg, "No disk, or could not put SD card in to SPI idle state\n");
return SD_BLOCK_DEVICE_ERROR_NO_DEVICE;
}

// send CMD8 to determine whther it is ver 2.x
// send CMD8 to determine whether it is ver 2.x
int r = _cmd8();
if (r == R1_IDLE_STATE) {
return _initialise_card_v2();
Expand Down Expand Up @@ -344,9 +375,11 @@ bd_size_t SDBlockDevice::get_erase_size() const
bd_size_t SDBlockDevice::size() const
{
bd_size_t sectors = 0;
_lock.lock();
if(_is_initialized) {
sectors = _sectors;
}
_lock.unlock();
return 512*sectors;
}

Expand All @@ -369,7 +402,7 @@ int SDBlockDevice::_cmd(int cmd, int arg) {
_spi.write(arg >> 0);
_spi.write(0x95);

// wait for the repsonse (response[7] == 0)
// wait for the response (response[7] == 0)
for (int i = 0; i < SD_COMMAND_TIMEOUT; i++) {
int response = _spi.write(0xFF);
if (!(response & 0x80)) {
Expand All @@ -396,7 +429,7 @@ int SDBlockDevice::_cmdx(int cmd, int arg) {
_spi.write(arg >> 0);
_spi.write(0x95);

// wait for the repsonse (response[7] == 0)
// wait for the response (response[7] == 0)
for (int i = 0; i < SD_COMMAND_TIMEOUT; i++) {
int response = _spi.write(0xFF);
if (!(response & 0x80)) {
Expand Down Expand Up @@ -425,7 +458,7 @@ int SDBlockDevice::_cmd58() {
_spi.write(arg >> 0);
_spi.write(0x95);

// wait for the repsonse (response[7] == 0)
// wait for the response (response[7] == 0)
for (int i = 0; i < SD_COMMAND_TIMEOUT; i++) {
int response = _spi.write(0xFF);
if (!(response & 0x80)) {
Expand Down Expand Up @@ -457,7 +490,7 @@ int SDBlockDevice::_cmd8() {
_spi.write(0xAA); // check pattern
_spi.write(0x87); // crc

// wait for the repsonse (response[7] == 0)
// wait for the response (response[7] == 0)
for (int i = 0; i < SD_COMMAND_TIMEOUT * 1000; i++) {
char response[5];
response[0] = _spi.write(0xFF);
Expand All @@ -477,6 +510,44 @@ int SDBlockDevice::_cmd8() {
return -1; // timeout
}

int SDBlockDevice::_go_idle_state() {
_spi.lock();
_cs = 0;
int cmd_arg = 0; /* CMD0 argument is just "stuff bits" */
int ret = SD_CMD0_INVALID_RESPONSE_TIMEOUT;

/* Reseting the MCU SPI master may not reset the on-board SDCard, in which
* case when MCU power-on occurs the SDCard will resume operations as
* though there was no reset. In this scenario the first CMD0 will
* not be interpreted as a command and get lost. For some cards retrying
* the command overcomes this situation. */
for (int num_retries = 0; ret != R1_IDLE_STATE && (num_retries < SD_CMD0_GO_IDLE_STATE_RETRIES); num_retries++) {
/* send a CMD0 */
_spi.write(0x40 | SD_CMD0_GO_IDLE_STATE);
_spi.write(cmd_arg >> 24);
_spi.write(cmd_arg >> 16);
_spi.write(cmd_arg >> 8);
_spi.write(cmd_arg >> 0);
_spi.write(0x95);

/* wait for the R1_IDLE_STATE response */
for (int i = 0; i < SD_COMMAND_TIMEOUT; i++) {
int response = _spi.write(0xFF);
/* Explicitly check for the R1_IDLE_STATE response rather that most significant bit
* being 0 because invalid data can be returned. */
if ((response == R1_IDLE_STATE)) {
ret = response;
break;
}
wait_ms(1);
}
}
_cs = 1;
_spi.write(0xFF);
_spi.unlock();
return ret;
}

int SDBlockDevice::_read(uint8_t *buffer, uint32_t length) {
_spi.lock();
_cs = 0;
Expand Down
13 changes: 13 additions & 0 deletions features/filesystem/sd/SDBlockDevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,19 @@ class SDBlockDevice : public BlockDevice {
int _cmdx(int cmd, int arg);
int _cmd8();
int _cmd58();

/* Move the SDCard into the SPI Mode idle state
*
* The card is transitioned from SDCard mode to SPI mode by sending the
* CMD0 (GO_IDLE_STATE) command with CS asserted. See the notes in the
* "SPI Startup" section of the comments at the head of the
* implementation file for further details and specification references.
*
* @return -1 if an error occurred e.g. a valid response was not
* received. Otherwise R1_IDLE_STATE (0x1), the successful
* response from CMD0.
*/
int _go_idle_state();
int _initialise_card();
int _initialise_card_v1();
int _initialise_card_v2();
Expand Down

0 comments on commit 37b5043

Please sign in to comment.