|
| 1 | +/* |
| 2 | + These are low level functions to aid in detecting whether a card is present or not. |
| 3 | + Because of ESP32 v2 core, SdFat can only operate using Shared SPI. This makes the sd.begin test take over 1s |
| 4 | + which causes the RTK product to boot slowly. To circumvent this, we will ping the SD card directly to see if it responds. |
| 5 | + Failures take 2ms, successes take 1ms. |
| 6 | +
|
| 7 | + From Prototype puzzle: https://github.com/sparkfunX/ThePrototype/blob/master/Firmware/TestSketches/sdLocker/sdLocker.ino |
| 8 | + License: Public domain. This code is based on Karl Lunt's work: https://www.seanet.com/~karllunt/sdlocker2.html |
| 9 | +*/ |
| 10 | + |
| 11 | +//Define commands for the SD card |
| 12 | +#define SD_GO_IDLE (0x40 + 0) // CMD0 - go to idle state |
| 13 | +#define SD_INIT (0x40 + 1) // CMD1 - start initialization |
| 14 | +#define SD_SEND_IF_COND (0x40 + 8) // CMD8 - send interface (conditional), works for SDHC only |
| 15 | +#define SD_SEND_STATUS (0x40 + 13) // CMD13 - send card status |
| 16 | +#define SD_SET_BLK_LEN (0x40 + 16) // CMD16 - set length of block in bytes |
| 17 | +#define SD_LOCK_UNLOCK (0x40 + 42) // CMD42 - lock/unlock card |
| 18 | +#define CMD55 (0x40 + 55) // multi-byte preface command |
| 19 | +#define SD_READ_OCR (0x40 + 58) // read OCR |
| 20 | +#define SD_ADV_INIT (0xc0 + 41) // ACMD41, for SDHC cards - advanced start initialization |
| 21 | + |
| 22 | +//Define options for accessing the SD card's PWD (CMD42) |
| 23 | +#define MASK_ERASE 0x08 //erase the entire card |
| 24 | +#define MASK_LOCK_UNLOCK 0x04 //lock or unlock the card with password |
| 25 | +#define MASK_CLR_PWD 0x02 //clear password |
| 26 | +#define MASK_SET_PWD 0x01 //set password |
| 27 | + |
| 28 | +//Define bit masks for fields in the lock/unlock command (CMD42) data structure |
| 29 | +#define SET_PWD_MASK (1<<0) |
| 30 | +#define CLR_PWD_MASK (1<<1) |
| 31 | +#define LOCK_UNLOCK_MASK (1<<2) |
| 32 | +#define ERASE_MASK (1<<3) |
| 33 | + |
| 34 | +//Begin initialization by sending CMD0 and waiting until SD card |
| 35 | +//responds with In Idle Mode (0x01). If the response is not 0x01 |
| 36 | +//within a reasonable amount of time, there is no SD card on the bus. |
| 37 | +//Returns false if not card is detected |
| 38 | +//Returns true if a card responds |
| 39 | +bool sdPresent(void) |
| 40 | +{ |
| 41 | + byte response = 0; |
| 42 | + |
| 43 | + SPI.begin(); |
| 44 | + SPI.setClockDivider(SPI_CLOCK_DIV2); |
| 45 | + SPI.setDataMode(SPI_MODE0); |
| 46 | + SPI.setBitOrder(MSBFIRST); |
| 47 | + pinMode(pin_microSD_CS, OUTPUT); |
| 48 | + |
| 49 | + //Sending clocks while card power stabilizes... |
| 50 | + deselectCard(); // always make sure |
| 51 | + for (byte i = 0; i < 30; i++) // send several clocks while card power stabilizes |
| 52 | + xchg(0xff); |
| 53 | + |
| 54 | + //Sending CMD0 - GO IDLE... |
| 55 | + for (byte i = 0; i < 0x10; i++) //Attempt to go idle |
| 56 | + { |
| 57 | + response = sdSendCommand(SD_GO_IDLE, 0); // send CMD0 - go to idle state |
| 58 | + if (response == 1) break; |
| 59 | + } |
| 60 | + if (response != 1) return (false); //Card failed to respond to idle |
| 61 | + |
| 62 | + return (true); |
| 63 | +} |
| 64 | + |
| 65 | +/* |
| 66 | + sdSendCommand send raw command to SD card, return response |
| 67 | +
|
| 68 | + This routine accepts a single SD command and a 4-byte argument. It sends |
| 69 | + the command plus argument, adding the appropriate CRC. It then returns |
| 70 | + the one-byte response from the SD card. |
| 71 | +
|
| 72 | + For advanced commands (those with a command byte having bit 7 set), this |
| 73 | + routine automatically sends the required preface command (CMD55) before |
| 74 | + sending the requested command. |
| 75 | +
|
| 76 | + Upon exit, this routine returns the response byte from the SD card. |
| 77 | + Possible responses are: |
| 78 | + 0xff No response from card; card might actually be missing |
| 79 | + 0x01 SD card returned 0x01, which is OK for most commands |
| 80 | + 0x?? other responses are command-specific |
| 81 | +*/ |
| 82 | +byte sdSendCommand(byte command, unsigned long arg) |
| 83 | +{ |
| 84 | + byte response; |
| 85 | + |
| 86 | + if (command & 0x80) // special case, ACMD(n) is sent as CMD55 and CMDn |
| 87 | + { |
| 88 | + command &= 0x7f; // strip high bit for later |
| 89 | + response = sdSendCommand(CMD55, 0); // send first part (recursion) |
| 90 | + if (response > 1) return (response); |
| 91 | + } |
| 92 | + |
| 93 | + deselectCard(); |
| 94 | + xchg(0xFF); |
| 95 | + selectCard(); // enable CS |
| 96 | + xchg(0xFF); |
| 97 | + |
| 98 | + xchg(command | 0x40); // command always has bit 6 set! |
| 99 | + xchg((byte)(arg >> 24)); // send data, starting with top byte |
| 100 | + xchg((byte)(arg >> 16)); |
| 101 | + xchg((byte)(arg >> 8)); |
| 102 | + xchg((byte)(arg & 0xFF)); |
| 103 | + |
| 104 | + byte crc = 0x01; // good for most cases |
| 105 | + if (command == SD_GO_IDLE) crc = 0x95; // this will be good enough for most commands |
| 106 | + if (command == SD_SEND_IF_COND) crc = 0x87; // special case, have to use different CRC |
| 107 | + xchg(crc); // send final byte |
| 108 | + |
| 109 | + for (int i = 0; i < 30; i++) // loop until timeout or response |
| 110 | + { |
| 111 | + response = xchg(0xFF); |
| 112 | + if ((response & 0x80) == 0) break; // high bit cleared means we got a response |
| 113 | + } |
| 114 | + |
| 115 | + /* |
| 116 | + We have issued the command but the SD card is still selected. We |
| 117 | + only deselectCard the card if the command we just sent is NOT a command |
| 118 | + that requires additional data exchange, such as reading or writing |
| 119 | + a block. |
| 120 | + */ |
| 121 | + if ((command != SD_READ_OCR) && |
| 122 | + (command != SD_SEND_STATUS) && |
| 123 | + (command != SD_SEND_IF_COND) && |
| 124 | + (command != SD_LOCK_UNLOCK)) |
| 125 | + { |
| 126 | + deselectCard(); // all done |
| 127 | + xchg(0xFF); // close with eight more clocks |
| 128 | + } |
| 129 | + |
| 130 | + return (response); // let the caller sort it out |
| 131 | +} |
| 132 | + |
| 133 | +//Select (enable) the SD card |
| 134 | +void selectCard(void) |
| 135 | +{ |
| 136 | + digitalWrite(pin_microSD_CS, LOW); |
| 137 | +} |
| 138 | + |
| 139 | +//Deselect (disable) the SD card |
| 140 | +void deselectCard(void) |
| 141 | +{ |
| 142 | + digitalWrite(pin_microSD_CS, HIGH); |
| 143 | +} |
| 144 | + |
| 145 | +//Exchange a byte of data with the SD card via host's SPI bus |
| 146 | +byte xchg(byte val) |
| 147 | +{ |
| 148 | + byte receivedVal = SPI.transfer(val); |
| 149 | + return receivedVal; |
| 150 | +} |
0 commit comments