SD Card Interface class. Log raw data bytes to memory addresses of your choice, or format the card and use the FAT file system to write files.
SDCard.cpp@3:210eb67b260c, 2010-08-23 (annotated)
- Committer:
- Blaze513
- Date:
- Mon Aug 23 07:12:13 2010 +0000
- Revision:
- 3:210eb67b260c
- Parent:
- 1:94c648931f84
- Child:
- 4:9a5878d316d5
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
Blaze513 | 3:210eb67b260c | 1 | //mbed Microcontroller Library |
Blaze513 | 3:210eb67b260c | 2 | //SDCard Interface |
Blaze513 | 3:210eb67b260c | 3 | //Copyright 2010 |
Blaze513 | 3:210eb67b260c | 4 | //Thomas Hamilton |
Blaze513 | 3:210eb67b260c | 5 | |
Blaze513 | 0:f3870f76a890 | 6 | #include "SDCard.h" |
Blaze513 | 0:f3870f76a890 | 7 | |
Blaze513 | 1:94c648931f84 | 8 | SDCard::SDCard(PinName mosi, PinName miso, PinName sck, PinName cs, const char* DiskName) : |
Blaze513 | 3:210eb67b260c | 9 | FATFileSystem(DiskName), DataLines(mosi, miso, sck), ChipSelect(cs), CRCMode(1), Timeout(1024) |
Blaze513 | 0:f3870f76a890 | 10 | { |
Blaze513 | 0:f3870f76a890 | 11 | DataLines.frequency(100000); |
Blaze513 | 0:f3870f76a890 | 12 | //set universal speed |
Blaze513 | 0:f3870f76a890 | 13 | ChipSelect.write(1); |
Blaze513 | 0:f3870f76a890 | 14 | //chip select is active low |
Blaze513 | 0:f3870f76a890 | 15 | GenerateCRCTable(1, 137, CommandCRCTable); |
Blaze513 | 0:f3870f76a890 | 16 | //generate the command crc lookup table |
Blaze513 | 0:f3870f76a890 | 17 | //(generator polynomial x^7 + x^3 + 1 converts to decimal 137) |
Blaze513 | 0:f3870f76a890 | 18 | GenerateCRCTable(2, 69665, DataCRCTable); |
Blaze513 | 0:f3870f76a890 | 19 | //generate the command crc lookup table |
Blaze513 | 0:f3870f76a890 | 20 | //(generator polynomial x^16 + x^12 + x^5 + 1 converts to decimal 69665) |
Blaze513 | 0:f3870f76a890 | 21 | Initialize(); |
Blaze513 | 3:210eb67b260c | 22 | //run card setup operations |
Blaze513 | 3:210eb67b260c | 23 | } |
Blaze513 | 3:210eb67b260c | 24 | |
Blaze513 | 3:210eb67b260c | 25 | SDCard::~SDCard() |
Blaze513 | 3:210eb67b260c | 26 | { |
Blaze513 | 3:210eb67b260c | 27 | delete[] CommandCRCTable; |
Blaze513 | 3:210eb67b260c | 28 | delete[] DataCRCTable; |
Blaze513 | 3:210eb67b260c | 29 | delete[] OCR; |
Blaze513 | 3:210eb67b260c | 30 | delete[] CSD; |
Blaze513 | 3:210eb67b260c | 31 | delete[] FSR; |
Blaze513 | 3:210eb67b260c | 32 | delete[] Workspace; |
Blaze513 | 3:210eb67b260c | 33 | //delete all card data register copies and workspaces |
Blaze513 | 3:210eb67b260c | 34 | delete this; |
Blaze513 | 1:94c648931f84 | 35 | } |
Blaze513 | 1:94c648931f84 | 36 | |
Blaze513 | 1:94c648931f84 | 37 | unsigned char SDCard::disk_initialize() |
Blaze513 | 1:94c648931f84 | 38 | { return 0x00; } |
Blaze513 | 3:210eb67b260c | 39 | //disc is initialized during construction |
Blaze513 | 1:94c648931f84 | 40 | unsigned char SDCard::disk_status() |
Blaze513 | 1:94c648931f84 | 41 | { return 0x00; } |
Blaze513 | 3:210eb67b260c | 42 | //card is always initialized |
Blaze513 | 3:210eb67b260c | 43 | unsigned char SDCard::disk_read( |
Blaze513 | 3:210eb67b260c | 44 | unsigned char* buff, unsigned long sector, unsigned char count) |
Blaze513 | 1:94c648931f84 | 45 | { return Read((unsigned int)sector, count, buff); } |
Blaze513 | 3:210eb67b260c | 46 | //read operations must efficiently complete multiple sector transactions |
Blaze513 | 3:210eb67b260c | 47 | unsigned char SDCard::disk_write( |
Blaze513 | 3:210eb67b260c | 48 | const unsigned char* buff, unsigned long sector, unsigned char count) |
Blaze513 | 1:94c648931f84 | 49 | { return Write((unsigned int)sector, count, (unsigned char*)buff); } |
Blaze513 | 3:210eb67b260c | 50 | //write operations must efficiently complete multiple sector transactions |
Blaze513 | 1:94c648931f84 | 51 | unsigned char SDCard::disk_sync() |
Blaze513 | 1:94c648931f84 | 52 | { return 0x00; } |
Blaze513 | 3:210eb67b260c | 53 | //all disc functions are synchronous |
Blaze513 | 1:94c648931f84 | 54 | unsigned long SDCard::disk_sector_count() |
Blaze513 | 1:94c648931f84 | 55 | { |
Blaze513 | 1:94c648931f84 | 56 | switch (CSD[0] & 0xC0) |
Blaze513 | 1:94c648931f84 | 57 | { |
Blaze513 | 1:94c648931f84 | 58 | case 0x00: |
Blaze513 | 1:94c648931f84 | 59 | return ((((CSD[6] & 0x03) << 10) | (CSD[7] << 2) | ((CSD[8] & 0xC0) >> 6)) + 1) |
Blaze513 | 1:94c648931f84 | 60 | * (1 << ((((CSD[9] & 0x03) << 1) | ((CSD[10] & 0x80) >> 7)) + 2)); |
Blaze513 | 3:210eb67b260c | 61 | //calculate sector count as specified for version 1 cards |
Blaze513 | 1:94c648931f84 | 62 | case 0x40: |
Blaze513 | 1:94c648931f84 | 63 | return ((((CSD[7] & 0x3F) << 16) | (CSD[8] << 8) | CSD[9]) + 1) * 1024; |
Blaze513 | 3:210eb67b260c | 64 | //calculate sector count as specified for version 2 cards |
Blaze513 | 1:94c648931f84 | 65 | default: |
Blaze513 | 1:94c648931f84 | 66 | return 0; |
Blaze513 | 1:94c648931f84 | 67 | } |
Blaze513 | 1:94c648931f84 | 68 | } |
Blaze513 | 3:210eb67b260c | 69 | //return number of sectors on card |
Blaze513 | 1:94c648931f84 | 70 | unsigned short SDCard::disk_sector_size() |
Blaze513 | 1:94c648931f84 | 71 | { return 512; } |
Blaze513 | 3:210eb67b260c | 72 | //fix SD card sector size to 512 for all cards |
Blaze513 | 1:94c648931f84 | 73 | unsigned long SDCard::disk_block_size() |
Blaze513 | 1:94c648931f84 | 74 | { |
Blaze513 | 1:94c648931f84 | 75 | switch (CSD[0] & 0xC0) |
Blaze513 | 1:94c648931f84 | 76 | { |
Blaze513 | 1:94c648931f84 | 77 | case 0x00: |
Blaze513 | 1:94c648931f84 | 78 | return (CSD[10] << 1) | (CSD[11] >> 7) + 1; |
Blaze513 | 3:210eb67b260c | 79 | //calculate erase sector size for version 1 cards |
Blaze513 | 1:94c648931f84 | 80 | case 0x40: |
Blaze513 | 1:94c648931f84 | 81 | return 1; |
Blaze513 | 3:210eb67b260c | 82 | //erase sector size is given by allocation unit for version 2 cards |
Blaze513 | 1:94c648931f84 | 83 | default: |
Blaze513 | 1:94c648931f84 | 84 | return 0; |
Blaze513 | 1:94c648931f84 | 85 | } |
Blaze513 | 0:f3870f76a890 | 86 | } |
Blaze513 | 3:210eb67b260c | 87 | //return the number of sectors in an erase sector |
Blaze513 | 0:f3870f76a890 | 88 | |
Blaze513 | 1:94c648931f84 | 89 | unsigned char SDCard::Log(unsigned char Control, unsigned char Data) |
Blaze513 | 0:f3870f76a890 | 90 | { |
Blaze513 | 1:94c648931f84 | 91 | static unsigned char Mode = 0x00; |
Blaze513 | 3:210eb67b260c | 92 | //store previous operating mode to determine current behavior |
Blaze513 | 1:94c648931f84 | 93 | static unsigned short Index = 0; |
Blaze513 | 3:210eb67b260c | 94 | //store last written byte number of current memory block |
Blaze513 | 1:94c648931f84 | 95 | |
Blaze513 | 1:94c648931f84 | 96 | if (CRCMode) |
Blaze513 | 0:f3870f76a890 | 97 | { |
Blaze513 | 1:94c648931f84 | 98 | SelectCRCMode(0); |
Blaze513 | 0:f3870f76a890 | 99 | } |
Blaze513 | 3:210eb67b260c | 100 | //CRC's are not used in raw data mode |
Blaze513 | 3:210eb67b260c | 101 | |
Blaze513 | 1:94c648931f84 | 102 | switch (Control) |
Blaze513 | 0:f3870f76a890 | 103 | { |
Blaze513 | 1:94c648931f84 | 104 | case 0x00: |
Blaze513 | 1:94c648931f84 | 105 | if (Mode) |
Blaze513 | 1:94c648931f84 | 106 | { |
Blaze513 | 1:94c648931f84 | 107 | ChipSelect.write(0); |
Blaze513 | 1:94c648931f84 | 108 | for (; Index < 512; Index++) |
Blaze513 | 1:94c648931f84 | 109 | { |
Blaze513 | 1:94c648931f84 | 110 | DataLines.write(0xFF); |
Blaze513 | 1:94c648931f84 | 111 | } |
Blaze513 | 3:210eb67b260c | 112 | //get through left over space, filling with 0xFF for write blocks |
Blaze513 | 1:94c648931f84 | 113 | DataLines.write(0xFF); |
Blaze513 | 1:94c648931f84 | 114 | DataLines.write(0xFF); |
Blaze513 | 3:210eb67b260c | 115 | //get through CRC |
Blaze513 | 1:94c648931f84 | 116 | ChipSelect.write(1); |
Blaze513 | 1:94c648931f84 | 117 | if (Mode == 0x01) |
Blaze513 | 1:94c648931f84 | 118 | { |
Blaze513 | 1:94c648931f84 | 119 | ChipSelect.write(0); |
Blaze513 | 1:94c648931f84 | 120 | t = 0; |
Blaze513 | 1:94c648931f84 | 121 | do |
Blaze513 | 1:94c648931f84 | 122 | { |
Blaze513 | 1:94c648931f84 | 123 | t++; |
Blaze513 | 1:94c648931f84 | 124 | } while (((DataLines.write(0xFF) & 0x11) != 0x01) && (t < Timeout)); |
Blaze513 | 3:210eb67b260c | 125 | //get through data response token |
Blaze513 | 1:94c648931f84 | 126 | while (!DataLines.write(0xFF)); |
Blaze513 | 3:210eb67b260c | 127 | //get through busy signal |
Blaze513 | 1:94c648931f84 | 128 | DataLines.write(0xFD); |
Blaze513 | 1:94c648931f84 | 129 | DataLines.write(0xFF); |
Blaze513 | 3:210eb67b260c | 130 | //send stop transmission token |
Blaze513 | 1:94c648931f84 | 131 | while (!DataLines.write(0xFF)); |
Blaze513 | 3:210eb67b260c | 132 | //get through busy signal |
Blaze513 | 1:94c648931f84 | 133 | ChipSelect.write(1); |
Blaze513 | 1:94c648931f84 | 134 | DataLines.write(0xFF); |
Blaze513 | 1:94c648931f84 | 135 | } |
Blaze513 | 3:210eb67b260c | 136 | //finish write block |
Blaze513 | 1:94c648931f84 | 137 | else |
Blaze513 | 1:94c648931f84 | 138 | { |
Blaze513 | 1:94c648931f84 | 139 | Command(12, 0, Workspace); |
Blaze513 | 3:210eb67b260c | 140 | //send stop transmission command |
Blaze513 | 1:94c648931f84 | 141 | ChipSelect.write(0); |
Blaze513 | 1:94c648931f84 | 142 | while (!DataLines.write(0xFF)); |
Blaze513 | 3:210eb67b260c | 143 | //get through busy signal |
Blaze513 | 1:94c648931f84 | 144 | ChipSelect.write(1); |
Blaze513 | 1:94c648931f84 | 145 | DataLines.write(0xFF); |
Blaze513 | 1:94c648931f84 | 146 | } |
Blaze513 | 3:210eb67b260c | 147 | //finish read block |
Blaze513 | 1:94c648931f84 | 148 | Index = 0; |
Blaze513 | 1:94c648931f84 | 149 | Mode = 0x00; |
Blaze513 | 3:210eb67b260c | 150 | //reset index to start and mode to synced |
Blaze513 | 1:94c648931f84 | 151 | } |
Blaze513 | 1:94c648931f84 | 152 | return 0xFF; |
Blaze513 | 3:210eb67b260c | 153 | //control code 0 synchronizes the card |
Blaze513 | 3:210eb67b260c | 154 | |
Blaze513 | 1:94c648931f84 | 155 | case 0x01: |
Blaze513 | 1:94c648931f84 | 156 | if (Mode != 0x01) |
Blaze513 | 1:94c648931f84 | 157 | { |
Blaze513 | 1:94c648931f84 | 158 | Log(0, 0); |
Blaze513 | 1:94c648931f84 | 159 | Command(25, 0, Workspace); |
Blaze513 | 1:94c648931f84 | 160 | Mode = 0x01; |
Blaze513 | 1:94c648931f84 | 161 | } |
Blaze513 | 3:210eb67b260c | 162 | //if previous call was not a write operation, sync the card, start a new write |
Blaze513 | 3:210eb67b260c | 163 | //block, and set function to write mode |
Blaze513 | 1:94c648931f84 | 164 | if (Index == 0) |
Blaze513 | 1:94c648931f84 | 165 | { |
Blaze513 | 1:94c648931f84 | 166 | ChipSelect.write(0); |
Blaze513 | 1:94c648931f84 | 167 | DataLines.write(0xFC); |
Blaze513 | 1:94c648931f84 | 168 | DataLines.write(Data); |
Blaze513 | 1:94c648931f84 | 169 | ChipSelect.write(1); |
Blaze513 | 1:94c648931f84 | 170 | Index++; |
Blaze513 | 1:94c648931f84 | 171 | } |
Blaze513 | 3:210eb67b260c | 172 | //if the index is at the start, send the start block token before the byte |
Blaze513 | 1:94c648931f84 | 173 | else if (Index < 511) |
Blaze513 | 1:94c648931f84 | 174 | { |
Blaze513 | 1:94c648931f84 | 175 | ChipSelect.write(0); |
Blaze513 | 1:94c648931f84 | 176 | DataLines.write(Data); |
Blaze513 | 1:94c648931f84 | 177 | ChipSelect.write(1); |
Blaze513 | 1:94c648931f84 | 178 | Index++; |
Blaze513 | 1:94c648931f84 | 179 | } |
Blaze513 | 3:210eb67b260c | 180 | //if the index is between the boundaries, simply write the byte |
Blaze513 | 1:94c648931f84 | 181 | else |
Blaze513 | 1:94c648931f84 | 182 | { |
Blaze513 | 1:94c648931f84 | 183 | ChipSelect.write(0); |
Blaze513 | 1:94c648931f84 | 184 | DataLines.write(Data); |
Blaze513 | 1:94c648931f84 | 185 | DataLines.write(0xFF); |
Blaze513 | 1:94c648931f84 | 186 | DataLines.write(0xFF); |
Blaze513 | 1:94c648931f84 | 187 | t = 0; |
Blaze513 | 1:94c648931f84 | 188 | do |
Blaze513 | 1:94c648931f84 | 189 | { |
Blaze513 | 1:94c648931f84 | 190 | t++; |
Blaze513 | 1:94c648931f84 | 191 | } while (((DataLines.write(0xFF) & 0x11) != 0x01) && (t < Timeout)); |
Blaze513 | 1:94c648931f84 | 192 | while (!DataLines.write(0xFF)); |
Blaze513 | 1:94c648931f84 | 193 | ChipSelect.write(1); |
Blaze513 | 1:94c648931f84 | 194 | Index = 0; |
Blaze513 | 1:94c648931f84 | 195 | } |
Blaze513 | 3:210eb67b260c | 196 | //if the index is at the last address, get through CRC, Data response token, and |
Blaze513 | 3:210eb67b260c | 197 | //busy signal and reset the index |
Blaze513 | 1:94c648931f84 | 198 | return 0xFF; |
Blaze513 | 3:210eb67b260c | 199 | //return stuff bits; control code 1 writes a byte |
Blaze513 | 3:210eb67b260c | 200 | |
Blaze513 | 1:94c648931f84 | 201 | case 0x02: |
Blaze513 | 1:94c648931f84 | 202 | if (Mode != 0x02) |
Blaze513 | 1:94c648931f84 | 203 | { |
Blaze513 | 1:94c648931f84 | 204 | Log(0, 0); |
Blaze513 | 1:94c648931f84 | 205 | Command(18, 0, Workspace); |
Blaze513 | 1:94c648931f84 | 206 | Mode = 0x02; |
Blaze513 | 1:94c648931f84 | 207 | } |
Blaze513 | 3:210eb67b260c | 208 | //if previous call was not a read operation, sync the card, start a new read block, |
Blaze513 | 3:210eb67b260c | 209 | //and set function to read mode |
Blaze513 | 1:94c648931f84 | 210 | if (Index == 0) |
Blaze513 | 1:94c648931f84 | 211 | { |
Blaze513 | 1:94c648931f84 | 212 | ChipSelect.write(0); |
Blaze513 | 1:94c648931f84 | 213 | t = 0; |
Blaze513 | 1:94c648931f84 | 214 | do |
Blaze513 | 1:94c648931f84 | 215 | { |
Blaze513 | 1:94c648931f84 | 216 | t++; |
Blaze513 | 1:94c648931f84 | 217 | } while ((DataLines.write(0xFF) != 0xFE) && (t < Timeout)); |
Blaze513 | 1:94c648931f84 | 218 | Workspace[0] = DataLines.write(0xFF); |
Blaze513 | 1:94c648931f84 | 219 | ChipSelect.write(1); |
Blaze513 | 1:94c648931f84 | 220 | Index++; |
Blaze513 | 1:94c648931f84 | 221 | return Workspace[0]; |
Blaze513 | 1:94c648931f84 | 222 | } |
Blaze513 | 3:210eb67b260c | 223 | //if the index is at the start, get the start block token and read the first byte |
Blaze513 | 1:94c648931f84 | 224 | else if (Index < 511) |
Blaze513 | 1:94c648931f84 | 225 | { |
Blaze513 | 1:94c648931f84 | 226 | ChipSelect.write(0); |
Blaze513 | 1:94c648931f84 | 227 | Workspace[0] = DataLines.write(0xFF); |
Blaze513 | 1:94c648931f84 | 228 | ChipSelect.write(1); |
Blaze513 | 1:94c648931f84 | 229 | Index++; |
Blaze513 | 1:94c648931f84 | 230 | return Workspace[0]; |
Blaze513 | 1:94c648931f84 | 231 | } |
Blaze513 | 3:210eb67b260c | 232 | //if the index is between the boundaries, simply read the byte |
Blaze513 | 1:94c648931f84 | 233 | else |
Blaze513 | 1:94c648931f84 | 234 | { |
Blaze513 | 1:94c648931f84 | 235 | ChipSelect.write(0); |
Blaze513 | 1:94c648931f84 | 236 | Workspace[0] = DataLines.write(0xFF); |
Blaze513 | 1:94c648931f84 | 237 | DataLines.write(0xFF); |
Blaze513 | 1:94c648931f84 | 238 | DataLines.write(0xFF); |
Blaze513 | 1:94c648931f84 | 239 | ChipSelect.write(1); |
Blaze513 | 1:94c648931f84 | 240 | Index = 0; |
Blaze513 | 1:94c648931f84 | 241 | return Workspace[0]; |
Blaze513 | 1:94c648931f84 | 242 | } |
Blaze513 | 3:210eb67b260c | 243 | //if the index is at the last address, get through CRC and reset the index; |
Blaze513 | 3:210eb67b260c | 244 | //control code 2 reads a byte |
Blaze513 | 3:210eb67b260c | 245 | |
Blaze513 | 1:94c648931f84 | 246 | default: |
Blaze513 | 1:94c648931f84 | 247 | return 0xFF; |
Blaze513 | 3:210eb67b260c | 248 | //return stuff bits |
Blaze513 | 0:f3870f76a890 | 249 | } |
Blaze513 | 0:f3870f76a890 | 250 | } |
Blaze513 | 0:f3870f76a890 | 251 | |
Blaze513 | 1:94c648931f84 | 252 | unsigned char SDCard::Write(unsigned int Address, unsigned char* Data) |
Blaze513 | 0:f3870f76a890 | 253 | { |
Blaze513 | 1:94c648931f84 | 254 | if (!Capacity) |
Blaze513 | 0:f3870f76a890 | 255 | { |
Blaze513 | 1:94c648931f84 | 256 | Command(24, Address * 512, Workspace); |
Blaze513 | 0:f3870f76a890 | 257 | } |
Blaze513 | 0:f3870f76a890 | 258 | else |
Blaze513 | 0:f3870f76a890 | 259 | { |
Blaze513 | 1:94c648931f84 | 260 | Command(24, Address, Workspace); |
Blaze513 | 1:94c648931f84 | 261 | } |
Blaze513 | 3:210eb67b260c | 262 | //send single block write command; addressing depends on the card version |
Blaze513 | 3:210eb67b260c | 263 | if (Workspace[0]) |
Blaze513 | 3:210eb67b260c | 264 | { return 0x04; } |
Blaze513 | 3:210eb67b260c | 265 | //if a command error occurs, return parameter error |
Blaze513 | 1:94c648931f84 | 266 | DataCRC(512, Data, Workspace); |
Blaze513 | 3:210eb67b260c | 267 | //calculate the data CRC |
Blaze513 | 1:94c648931f84 | 268 | ChipSelect.write(0); |
Blaze513 | 1:94c648931f84 | 269 | DataLines.write(0xFE); |
Blaze513 | 3:210eb67b260c | 270 | //write start block token |
Blaze513 | 1:94c648931f84 | 271 | for (unsigned short i = 0; i < 512; i++) |
Blaze513 | 1:94c648931f84 | 272 | { |
Blaze513 | 1:94c648931f84 | 273 | DataLines.write(Data[i]); |
Blaze513 | 1:94c648931f84 | 274 | } |
Blaze513 | 3:210eb67b260c | 275 | //write the data to the addressed card sector |
Blaze513 | 1:94c648931f84 | 276 | DataLines.write(Workspace[0]); |
Blaze513 | 1:94c648931f84 | 277 | DataLines.write(Workspace[1]); |
Blaze513 | 3:210eb67b260c | 278 | //write the data CRC to the card |
Blaze513 | 1:94c648931f84 | 279 | t = 0; |
Blaze513 | 1:94c648931f84 | 280 | do |
Blaze513 | 1:94c648931f84 | 281 | { |
Blaze513 | 1:94c648931f84 | 282 | Workspace[0] = DataLines.write(0xFF); |
Blaze513 | 1:94c648931f84 | 283 | t++; |
Blaze513 | 1:94c648931f84 | 284 | } while (((Workspace[0] & 0x11) != 0x01) && (t < Timeout)); |
Blaze513 | 3:210eb67b260c | 285 | //gather the data block response token |
Blaze513 | 1:94c648931f84 | 286 | while (!DataLines.write(0xFF)); |
Blaze513 | 3:210eb67b260c | 287 | //get through the busy signal |
Blaze513 | 1:94c648931f84 | 288 | ChipSelect.write(1); |
Blaze513 | 1:94c648931f84 | 289 | DataLines.write(0xFF); |
Blaze513 | 1:94c648931f84 | 290 | if (((Workspace[0] & 0x1F) != 0x05) || (t == Timeout)) |
Blaze513 | 1:94c648931f84 | 291 | { return 0x01; } |
Blaze513 | 1:94c648931f84 | 292 | else |
Blaze513 | 1:94c648931f84 | 293 | { return 0x00; } |
Blaze513 | 3:210eb67b260c | 294 | //if data response token indicates error, return R/W error |
Blaze513 | 1:94c648931f84 | 295 | } |
Blaze513 | 1:94c648931f84 | 296 | unsigned char SDCard::Write(unsigned int Address, unsigned char SectorCount, unsigned char* Data) |
Blaze513 | 1:94c648931f84 | 297 | { |
Blaze513 | 1:94c648931f84 | 298 | static unsigned char CurrentSectorCount = 1; |
Blaze513 | 3:210eb67b260c | 299 | //store the last write sector count |
Blaze513 | 1:94c648931f84 | 300 | if (SectorCount != CurrentSectorCount) |
Blaze513 | 1:94c648931f84 | 301 | { |
Blaze513 | 1:94c648931f84 | 302 | Command(55, 0, Workspace); |
Blaze513 | 1:94c648931f84 | 303 | Command(23, SectorCount, Workspace); |
Blaze513 | 1:94c648931f84 | 304 | if (Workspace[0]) |
Blaze513 | 1:94c648931f84 | 305 | { return 0x04; } |
Blaze513 | 1:94c648931f84 | 306 | CurrentSectorCount = SectorCount; |
Blaze513 | 1:94c648931f84 | 307 | } |
Blaze513 | 3:210eb67b260c | 308 | //set the expected number of write blocks if different from previous operations |
Blaze513 | 1:94c648931f84 | 309 | if (!Capacity) |
Blaze513 | 1:94c648931f84 | 310 | { |
Blaze513 | 1:94c648931f84 | 311 | Command(25, Address * 512, Workspace); |
Blaze513 | 1:94c648931f84 | 312 | } |
Blaze513 | 1:94c648931f84 | 313 | else |
Blaze513 | 1:94c648931f84 | 314 | { |
Blaze513 | 1:94c648931f84 | 315 | Command(25, Address, Workspace); |
Blaze513 | 1:94c648931f84 | 316 | } |
Blaze513 | 3:210eb67b260c | 317 | if (Workspace[0]) |
Blaze513 | 3:210eb67b260c | 318 | { return 0x04; } |
Blaze513 | 1:94c648931f84 | 319 | Workspace[4] = 0x00; |
Blaze513 | 1:94c648931f84 | 320 | for (unsigned char i = 0; i < SectorCount; i++) |
Blaze513 | 1:94c648931f84 | 321 | { |
Blaze513 | 1:94c648931f84 | 322 | DataCRC(512, &Data[i * 512], Workspace); |
Blaze513 | 3:210eb67b260c | 323 | //calculate data crc for each passed write block |
Blaze513 | 1:94c648931f84 | 324 | ChipSelect.write(0); |
Blaze513 | 1:94c648931f84 | 325 | DataLines.write(0xFC); |
Blaze513 | 3:210eb67b260c | 326 | //send multiple write block start token |
Blaze513 | 1:94c648931f84 | 327 | for (unsigned int j = i * 512; j < (i + 1) * 512; j++) |
Blaze513 | 0:f3870f76a890 | 328 | { |
Blaze513 | 1:94c648931f84 | 329 | DataLines.write(Data[j]); |
Blaze513 | 1:94c648931f84 | 330 | } |
Blaze513 | 3:210eb67b260c | 331 | //write each data block |
Blaze513 | 1:94c648931f84 | 332 | DataLines.write(Workspace[0]); |
Blaze513 | 1:94c648931f84 | 333 | DataLines.write(Workspace[1]); |
Blaze513 | 1:94c648931f84 | 334 | t = 0; |
Blaze513 | 1:94c648931f84 | 335 | do |
Blaze513 | 1:94c648931f84 | 336 | { |
Blaze513 | 1:94c648931f84 | 337 | Workspace[0] = DataLines.write(0xFF); |
Blaze513 | 1:94c648931f84 | 338 | t++; |
Blaze513 | 1:94c648931f84 | 339 | } while (((Workspace[0] & 0x11) != 0x01) && (t < Timeout)); |
Blaze513 | 1:94c648931f84 | 340 | while (!DataLines.write(0xFF)); |
Blaze513 | 1:94c648931f84 | 341 | ChipSelect.write(1); |
Blaze513 | 1:94c648931f84 | 342 | Workspace[4] |= Workspace[0]; |
Blaze513 | 3:210eb67b260c | 343 | //record if any write errors are detected in the data response tokens |
Blaze513 | 1:94c648931f84 | 344 | if (t == Timeout) |
Blaze513 | 1:94c648931f84 | 345 | { |
Blaze513 | 1:94c648931f84 | 346 | ChipSelect.write(0); |
Blaze513 | 1:94c648931f84 | 347 | DataLines.write(0xFD); |
Blaze513 | 1:94c648931f84 | 348 | while (!DataLines.write(0xFF)); |
Blaze513 | 1:94c648931f84 | 349 | ChipSelect.write(1); |
Blaze513 | 1:94c648931f84 | 350 | DataLines.write(0xFF); |
Blaze513 | 1:94c648931f84 | 351 | return 0x01; |
Blaze513 | 0:f3870f76a890 | 352 | } |
Blaze513 | 3:210eb67b260c | 353 | //if a block write operation gets timed out, make sure the card is synced and exit |
Blaze513 | 0:f3870f76a890 | 354 | } |
Blaze513 | 0:f3870f76a890 | 355 | ChipSelect.write(0); |
Blaze513 | 1:94c648931f84 | 356 | DataLines.write(0xFD); |
Blaze513 | 1:94c648931f84 | 357 | DataLines.write(0xFF); |
Blaze513 | 1:94c648931f84 | 358 | while (!DataLines.write(0xFF)); |
Blaze513 | 1:94c648931f84 | 359 | ChipSelect.write(1); |
Blaze513 | 1:94c648931f84 | 360 | DataLines.write(0xFF); |
Blaze513 | 1:94c648931f84 | 361 | if ((Workspace[4] & 0x1F) != 0x05) |
Blaze513 | 1:94c648931f84 | 362 | { return 0x01; } |
Blaze513 | 1:94c648931f84 | 363 | else |
Blaze513 | 1:94c648931f84 | 364 | { return 0x00; } |
Blaze513 | 1:94c648931f84 | 365 | } |
Blaze513 | 1:94c648931f84 | 366 | |
Blaze513 | 1:94c648931f84 | 367 | unsigned char SDCard::Read(unsigned int Address, unsigned char* Data) |
Blaze513 | 1:94c648931f84 | 368 | { |
Blaze513 | 1:94c648931f84 | 369 | if (!Capacity) |
Blaze513 | 0:f3870f76a890 | 370 | { |
Blaze513 | 1:94c648931f84 | 371 | Command(17, Address * 512, Workspace); |
Blaze513 | 0:f3870f76a890 | 372 | } |
Blaze513 | 1:94c648931f84 | 373 | else |
Blaze513 | 1:94c648931f84 | 374 | { |
Blaze513 | 1:94c648931f84 | 375 | Command(17, Address, Workspace); |
Blaze513 | 1:94c648931f84 | 376 | } |
Blaze513 | 3:210eb67b260c | 377 | //send single block read command; addressing depends on the card version |
Blaze513 | 3:210eb67b260c | 378 | if (Workspace[0]) |
Blaze513 | 3:210eb67b260c | 379 | { return 0x04; } |
Blaze513 | 3:210eb67b260c | 380 | //if a command error occurs, return parameter error |
Blaze513 | 1:94c648931f84 | 381 | ChipSelect.write(0); |
Blaze513 | 1:94c648931f84 | 382 | t = 0; |
Blaze513 | 1:94c648931f84 | 383 | do |
Blaze513 | 0:f3870f76a890 | 384 | { |
Blaze513 | 1:94c648931f84 | 385 | t++; |
Blaze513 | 1:94c648931f84 | 386 | } while ((DataLines.write(0xFF) != 0xFE) && (t < Timeout)); |
Blaze513 | 1:94c648931f84 | 387 | if (t == Timeout) { ChipSelect.write(1); DataLines.write(0xFF); return 0x01; } |
Blaze513 | 3:210eb67b260c | 388 | //get to start block token |
Blaze513 | 1:94c648931f84 | 389 | for (unsigned short i = 0; i < 512; i++) |
Blaze513 | 1:94c648931f84 | 390 | { |
Blaze513 | 1:94c648931f84 | 391 | Data[i] = DataLines.write(0xFF); |
Blaze513 | 0:f3870f76a890 | 392 | } |
Blaze513 | 3:210eb67b260c | 393 | //read the data from the addressed card sector |
Blaze513 | 1:94c648931f84 | 394 | Workspace[2] = DataLines.write(0xFF); |
Blaze513 | 0:f3870f76a890 | 395 | Workspace[3] = DataLines.write(0xFF); |
Blaze513 | 3:210eb67b260c | 396 | //read the data CRC to the card |
Blaze513 | 0:f3870f76a890 | 397 | ChipSelect.write(1); |
Blaze513 | 0:f3870f76a890 | 398 | DataLines.write(0xFF); |
Blaze513 | 0:f3870f76a890 | 399 | DataCRC(512, Data, Workspace); |
Blaze513 | 3:210eb67b260c | 400 | //calculate the data CRC |
Blaze513 | 1:94c648931f84 | 401 | if (CRCMode && ((Workspace[0] != Workspace[2]) || (Workspace[1] != Workspace[3]))) |
Blaze513 | 1:94c648931f84 | 402 | { return 0x01; } |
Blaze513 | 1:94c648931f84 | 403 | else |
Blaze513 | 1:94c648931f84 | 404 | { return 0x00; } |
Blaze513 | 3:210eb67b260c | 405 | //if CRC is invalid, return R/W error |
Blaze513 | 1:94c648931f84 | 406 | } |
Blaze513 | 1:94c648931f84 | 407 | unsigned char SDCard::Read(unsigned int Address, unsigned char SectorCount, unsigned char* Data) |
Blaze513 | 1:94c648931f84 | 408 | { |
Blaze513 | 1:94c648931f84 | 409 | if (!Capacity) |
Blaze513 | 1:94c648931f84 | 410 | { |
Blaze513 | 1:94c648931f84 | 411 | Command(18, Address * 512, Workspace); |
Blaze513 | 1:94c648931f84 | 412 | } |
Blaze513 | 0:f3870f76a890 | 413 | else |
Blaze513 | 1:94c648931f84 | 414 | { |
Blaze513 | 1:94c648931f84 | 415 | Command(18, Address, Workspace); |
Blaze513 | 1:94c648931f84 | 416 | } |
Blaze513 | 3:210eb67b260c | 417 | if (Workspace[0]) |
Blaze513 | 3:210eb67b260c | 418 | { return 0; } |
Blaze513 | 1:94c648931f84 | 419 | Workspace[4] = 0x00; |
Blaze513 | 1:94c648931f84 | 420 | for (unsigned char i = 0; i < SectorCount; i++) |
Blaze513 | 1:94c648931f84 | 421 | { |
Blaze513 | 1:94c648931f84 | 422 | ChipSelect.write(0); |
Blaze513 | 1:94c648931f84 | 423 | t = 0; |
Blaze513 | 1:94c648931f84 | 424 | do |
Blaze513 | 1:94c648931f84 | 425 | { |
Blaze513 | 1:94c648931f84 | 426 | t++; |
Blaze513 | 1:94c648931f84 | 427 | } while ((DataLines.write(0xFF) != 0xFE) && (t < Timeout)); |
Blaze513 | 3:210eb67b260c | 428 | //get to each data block start token |
Blaze513 | 1:94c648931f84 | 429 | if (t == Timeout) |
Blaze513 | 1:94c648931f84 | 430 | { |
Blaze513 | 1:94c648931f84 | 431 | ChipSelect.write(1); |
Blaze513 | 1:94c648931f84 | 432 | Command(12, 0, Workspace); |
Blaze513 | 1:94c648931f84 | 433 | ChipSelect.write(0); |
Blaze513 | 1:94c648931f84 | 434 | while (!DataLines.write(0xFF)); |
Blaze513 | 1:94c648931f84 | 435 | ChipSelect.write(1); |
Blaze513 | 1:94c648931f84 | 436 | DataLines.write(0xFF); |
Blaze513 | 1:94c648931f84 | 437 | return 0x01; |
Blaze513 | 1:94c648931f84 | 438 | } |
Blaze513 | 3:210eb67b260c | 439 | //if a block read operation gets timed out, make sure the card is synced and exit |
Blaze513 | 1:94c648931f84 | 440 | for (unsigned int j = i * 512; j < (i + 1) * 512; j++) |
Blaze513 | 1:94c648931f84 | 441 | { |
Blaze513 | 1:94c648931f84 | 442 | Data[j] = DataLines.write(0xFF); |
Blaze513 | 1:94c648931f84 | 443 | } |
Blaze513 | 3:210eb67b260c | 444 | //read each data block |
Blaze513 | 1:94c648931f84 | 445 | Workspace[2] = DataLines.write(0xFF); |
Blaze513 | 1:94c648931f84 | 446 | Workspace[3] = DataLines.write(0xFF); |
Blaze513 | 1:94c648931f84 | 447 | ChipSelect.write(1); |
Blaze513 | 1:94c648931f84 | 448 | DataCRC(512, &Data[i * 512], Workspace); |
Blaze513 | 3:210eb67b260c | 449 | //calculate data crc for each read data block |
Blaze513 | 1:94c648931f84 | 450 | Workspace[4] |= ((Workspace[0] != Workspace[2]) || (Workspace[1] != Workspace[3])); |
Blaze513 | 3:210eb67b260c | 451 | //record if any invalid CRCs are detected during the transaction |
Blaze513 | 1:94c648931f84 | 452 | } |
Blaze513 | 1:94c648931f84 | 453 | Command(12, 0, Workspace); |
Blaze513 | 1:94c648931f84 | 454 | ChipSelect.write(0); |
Blaze513 | 1:94c648931f84 | 455 | while (!DataLines.write(0xFF)); |
Blaze513 | 1:94c648931f84 | 456 | ChipSelect.write(1); |
Blaze513 | 1:94c648931f84 | 457 | if (Workspace[4]) |
Blaze513 | 1:94c648931f84 | 458 | { return 0x01; } |
Blaze513 | 1:94c648931f84 | 459 | else |
Blaze513 | 1:94c648931f84 | 460 | { return 0x00; } |
Blaze513 | 0:f3870f76a890 | 461 | } |
Blaze513 | 0:f3870f76a890 | 462 | |
Blaze513 | 1:94c648931f84 | 463 | unsigned char SDCard::SelectCRCMode(bool Mode) |
Blaze513 | 0:f3870f76a890 | 464 | { |
Blaze513 | 1:94c648931f84 | 465 | t = 0; |
Blaze513 | 1:94c648931f84 | 466 | do |
Blaze513 | 1:94c648931f84 | 467 | { |
Blaze513 | 1:94c648931f84 | 468 | Command(59, Mode, Workspace); |
Blaze513 | 3:210eb67b260c | 469 | //command 59 sets card CRC mode |
Blaze513 | 1:94c648931f84 | 470 | t++; |
Blaze513 | 1:94c648931f84 | 471 | } while (Workspace[0] && (t < Timeout)); |
Blaze513 | 1:94c648931f84 | 472 | CRCMode = Mode; |
Blaze513 | 1:94c648931f84 | 473 | if (t == Timeout) |
Blaze513 | 1:94c648931f84 | 474 | { return 0x01; } |
Blaze513 | 1:94c648931f84 | 475 | else |
Blaze513 | 1:94c648931f84 | 476 | { return 0x00; } |
Blaze513 | 3:210eb67b260c | 477 | //if command times out, return error |
Blaze513 | 1:94c648931f84 | 478 | } |
Blaze513 | 1:94c648931f84 | 479 | |
Blaze513 | 3:210eb67b260c | 480 | void SDCard::SetTimeout(unsigned int Retries) |
Blaze513 | 3:210eb67b260c | 481 | { |
Blaze513 | 3:210eb67b260c | 482 | Timeout = Retries; |
Blaze513 | 3:210eb67b260c | 483 | } |
Blaze513 | 3:210eb67b260c | 484 | //set c=number of retries for card operations |
Blaze513 | 3:210eb67b260c | 485 | |
Blaze513 | 1:94c648931f84 | 486 | unsigned char SDCard::Initialize() |
Blaze513 | 1:94c648931f84 | 487 | { |
Blaze513 | 1:94c648931f84 | 488 | for (unsigned char i = 0; i < 16; i++) |
Blaze513 | 0:f3870f76a890 | 489 | { |
Blaze513 | 0:f3870f76a890 | 490 | DataLines.write(0xFF); |
Blaze513 | 1:94c648931f84 | 491 | //clock card at least 74 times to power up |
Blaze513 | 0:f3870f76a890 | 492 | } |
Blaze513 | 1:94c648931f84 | 493 | |
Blaze513 | 1:94c648931f84 | 494 | t = 0; |
Blaze513 | 1:94c648931f84 | 495 | do |
Blaze513 | 0:f3870f76a890 | 496 | { |
Blaze513 | 0:f3870f76a890 | 497 | Command(0, 0, Workspace); |
Blaze513 | 0:f3870f76a890 | 498 | //send command 0 to put the card into SPI mode |
Blaze513 | 1:94c648931f84 | 499 | t++; |
Blaze513 | 1:94c648931f84 | 500 | } while ((Workspace[0] != 0x01) && (t < Timeout)); |
Blaze513 | 1:94c648931f84 | 501 | if (t == Timeout) { return 0x01; } |
Blaze513 | 1:94c648931f84 | 502 | |
Blaze513 | 1:94c648931f84 | 503 | t = 0; |
Blaze513 | 1:94c648931f84 | 504 | do |
Blaze513 | 0:f3870f76a890 | 505 | { |
Blaze513 | 0:f3870f76a890 | 506 | Command(59, 1, Workspace); |
Blaze513 | 1:94c648931f84 | 507 | //turn on CRCs |
Blaze513 | 1:94c648931f84 | 508 | t++; |
Blaze513 | 1:94c648931f84 | 509 | } while ((Workspace[0] != 0x01) && (Workspace[0] != 0x05) && (t < Timeout)); |
Blaze513 | 1:94c648931f84 | 510 | //command 59 is not valid for all cards in idle state |
Blaze513 | 1:94c648931f84 | 511 | if (t == Timeout) { return 0x01; } |
Blaze513 | 1:94c648931f84 | 512 | |
Blaze513 | 1:94c648931f84 | 513 | t = 0; |
Blaze513 | 1:94c648931f84 | 514 | do |
Blaze513 | 0:f3870f76a890 | 515 | { |
Blaze513 | 0:f3870f76a890 | 516 | Command(8, 426, Workspace); |
Blaze513 | 0:f3870f76a890 | 517 | //voltage bits are 0x01 for 2.7V - 3.6V, |
Blaze513 | 0:f3870f76a890 | 518 | //check pattern 0xAA, [00,00,01,AA] = 426 |
Blaze513 | 1:94c648931f84 | 519 | t++; |
Blaze513 | 1:94c648931f84 | 520 | } while (((Workspace[0] != 0x01) || ((Workspace[3] & 0x0F) != 0x01) || |
Blaze513 | 1:94c648931f84 | 521 | (Workspace[4] != 0xAA)) && (Workspace[0] != 0x05) && (t < Timeout)); |
Blaze513 | 1:94c648931f84 | 522 | //check version, voltage acceptance, and check pattern |
Blaze513 | 1:94c648931f84 | 523 | if (t == Timeout) { return 0x01; } |
Blaze513 | 1:94c648931f84 | 524 | Version = Workspace[0] != 0x05; |
Blaze513 | 0:f3870f76a890 | 525 | //store card version |
Blaze513 | 1:94c648931f84 | 526 | |
Blaze513 | 1:94c648931f84 | 527 | t = 0; |
Blaze513 | 1:94c648931f84 | 528 | do |
Blaze513 | 0:f3870f76a890 | 529 | { |
Blaze513 | 0:f3870f76a890 | 530 | Command(58, 0, Workspace); |
Blaze513 | 0:f3870f76a890 | 531 | //check the OCR |
Blaze513 | 1:94c648931f84 | 532 | t++; |
Blaze513 | 1:94c648931f84 | 533 | } while (((Workspace[0] != 0x01) || |
Blaze513 | 1:94c648931f84 | 534 | !((Workspace[2] & 0x20) || (Workspace[2] & 0x10))) && (t < Timeout)); |
Blaze513 | 1:94c648931f84 | 535 | //check for correct operating voltage 3.3V |
Blaze513 | 1:94c648931f84 | 536 | if (t == Timeout) { return 0x01; } |
Blaze513 | 1:94c648931f84 | 537 | |
Blaze513 | 1:94c648931f84 | 538 | t = 0; |
Blaze513 | 1:94c648931f84 | 539 | do |
Blaze513 | 0:f3870f76a890 | 540 | { |
Blaze513 | 0:f3870f76a890 | 541 | Command(55, 0, Workspace); |
Blaze513 | 0:f3870f76a890 | 542 | Command(41, 1073741824, Workspace); |
Blaze513 | 1:94c648931f84 | 543 | //specify host supports high capacity |
Blaze513 | 1:94c648931f84 | 544 | //cards, [40,00,00,00] = 1073741824 |
Blaze513 | 1:94c648931f84 | 545 | t++; |
Blaze513 | 1:94c648931f84 | 546 | } while (Workspace[0] && (t < Timeout)); |
Blaze513 | 1:94c648931f84 | 547 | //check if card is ready |
Blaze513 | 1:94c648931f84 | 548 | if (t == Timeout) { return 0x01; } |
Blaze513 | 1:94c648931f84 | 549 | |
Blaze513 | 1:94c648931f84 | 550 | if (SelectCRCMode(1)) |
Blaze513 | 1:94c648931f84 | 551 | { return 0x01; } |
Blaze513 | 1:94c648931f84 | 552 | //turn on CRCs for all cards |
Blaze513 | 1:94c648931f84 | 553 | |
Blaze513 | 1:94c648931f84 | 554 | t = 0; |
Blaze513 | 1:94c648931f84 | 555 | do |
Blaze513 | 0:f3870f76a890 | 556 | { |
Blaze513 | 0:f3870f76a890 | 557 | Command(58, 0, Workspace); |
Blaze513 | 0:f3870f76a890 | 558 | //check the OCR again |
Blaze513 | 1:94c648931f84 | 559 | t++; |
Blaze513 | 1:94c648931f84 | 560 | } while ((Workspace[0] || !(Workspace[1] & 0x80)) && (t < Timeout)); |
Blaze513 | 1:94c648931f84 | 561 | //check power up status |
Blaze513 | 1:94c648931f84 | 562 | if (t == Timeout) { return 0x01; } |
Blaze513 | 1:94c648931f84 | 563 | for (unsigned char i = 0; i < 4; i++) |
Blaze513 | 1:94c648931f84 | 564 | { |
Blaze513 | 1:94c648931f84 | 565 | OCR[i] = Workspace[i + 1]; |
Blaze513 | 1:94c648931f84 | 566 | //record OCR |
Blaze513 | 0:f3870f76a890 | 567 | } |
Blaze513 | 0:f3870f76a890 | 568 | Capacity = (OCR[0] & 0x40) == 0x40; |
Blaze513 | 0:f3870f76a890 | 569 | //record capacity |
Blaze513 | 1:94c648931f84 | 570 | |
Blaze513 | 1:94c648931f84 | 571 | t = 0; |
Blaze513 | 1:94c648931f84 | 572 | do |
Blaze513 | 0:f3870f76a890 | 573 | { |
Blaze513 | 1:94c648931f84 | 574 | do |
Blaze513 | 0:f3870f76a890 | 575 | { |
Blaze513 | 1:94c648931f84 | 576 | Command(9, 0, Workspace); |
Blaze513 | 1:94c648931f84 | 577 | //read the card-specific-data register |
Blaze513 | 1:94c648931f84 | 578 | t++; |
Blaze513 | 1:94c648931f84 | 579 | } while (Workspace[0] && (t < Timeout)); |
Blaze513 | 1:94c648931f84 | 580 | if (t == Timeout) { return 0x01; } |
Blaze513 | 1:94c648931f84 | 581 | ChipSelect.write(0); |
Blaze513 | 1:94c648931f84 | 582 | do |
Blaze513 | 1:94c648931f84 | 583 | { |
Blaze513 | 1:94c648931f84 | 584 | t++; |
Blaze513 | 1:94c648931f84 | 585 | } while ((DataLines.write(0xFF) != 0xFE) && (t < Timeout)); |
Blaze513 | 0:f3870f76a890 | 586 | //get to the start-data-block token |
Blaze513 | 1:94c648931f84 | 587 | if (t == Timeout) { ChipSelect.write(1); DataLines.write(0xFF); return 0x01; } |
Blaze513 | 1:94c648931f84 | 588 | for (unsigned char i = 0; i < 16; i++) |
Blaze513 | 0:f3870f76a890 | 589 | { |
Blaze513 | 1:94c648931f84 | 590 | CSD[i] = DataLines.write(0xFF); |
Blaze513 | 1:94c648931f84 | 591 | //gather CSD |
Blaze513 | 0:f3870f76a890 | 592 | } |
Blaze513 | 1:94c648931f84 | 593 | Workspace[2] = DataLines.write(0xFF); |
Blaze513 | 0:f3870f76a890 | 594 | Workspace[3] = DataLines.write(0xFF); |
Blaze513 | 1:94c648931f84 | 595 | //save CSD CRC |
Blaze513 | 0:f3870f76a890 | 596 | ChipSelect.write(1); |
Blaze513 | 0:f3870f76a890 | 597 | DataLines.write(0xFF); |
Blaze513 | 0:f3870f76a890 | 598 | DataCRC(16, CSD, Workspace); |
Blaze513 | 0:f3870f76a890 | 599 | //calculate the CSD data CRC |
Blaze513 | 1:94c648931f84 | 600 | Workspace[4] = 0; |
Blaze513 | 1:94c648931f84 | 601 | for (unsigned char i = 0; i < 15; i++) |
Blaze513 | 0:f3870f76a890 | 602 | { |
Blaze513 | 1:94c648931f84 | 603 | Workspace[4] = CommandCRCTable[Workspace[4]] ^ CSD[i]; |
Blaze513 | 0:f3870f76a890 | 604 | } |
Blaze513 | 1:94c648931f84 | 605 | Workspace[4] = CommandCRCTable[Workspace[4]] | 0x01; |
Blaze513 | 1:94c648931f84 | 606 | //calculate the CSD table CRC |
Blaze513 | 1:94c648931f84 | 607 | t++; |
Blaze513 | 1:94c648931f84 | 608 | } while (((Workspace[0] != Workspace[2]) || (Workspace[1] != Workspace[3]) || |
Blaze513 | 1:94c648931f84 | 609 | (Workspace[4] != CSD[15])) && (t < Timeout)); |
Blaze513 | 1:94c648931f84 | 610 | //check all CSD CRCs |
Blaze513 | 1:94c648931f84 | 611 | if (t == Timeout) { return 0x01; } |
Blaze513 | 1:94c648931f84 | 612 | |
Blaze513 | 1:94c648931f84 | 613 | if (((CSD[3] & 0x07) > 0x02) || |
Blaze513 | 1:94c648931f84 | 614 | (((CSD[3] & 0x78) > 0x30) && ((CSD[3] & 0x07) > 0x01))) |
Blaze513 | 0:f3870f76a890 | 615 | { |
Blaze513 | 0:f3870f76a890 | 616 | DataLines.frequency(25000000); |
Blaze513 | 1:94c648931f84 | 617 | //maximum speed is 25MHz |
Blaze513 | 0:f3870f76a890 | 618 | } |
Blaze513 | 0:f3870f76a890 | 619 | else |
Blaze513 | 0:f3870f76a890 | 620 | { |
Blaze513 | 0:f3870f76a890 | 621 | Workspace[0] = 1; |
Blaze513 | 1:94c648931f84 | 622 | for (unsigned char i = 0; i < (CSD[3] & 0x07); i++) |
Blaze513 | 0:f3870f76a890 | 623 | { |
Blaze513 | 0:f3870f76a890 | 624 | Workspace[0] *= 10; |
Blaze513 | 0:f3870f76a890 | 625 | //the first three bits are a power of ten multiplier for speed |
Blaze513 | 0:f3870f76a890 | 626 | } |
Blaze513 | 0:f3870f76a890 | 627 | switch (CSD[3] & 0x78) |
Blaze513 | 0:f3870f76a890 | 628 | { |
Blaze513 | 0:f3870f76a890 | 629 | case 0x08: DataLines.frequency(Workspace[0] * 100000); break; |
Blaze513 | 0:f3870f76a890 | 630 | case 0x10: DataLines.frequency(Workspace[0] * 120000); break; |
Blaze513 | 0:f3870f76a890 | 631 | case 0x18: DataLines.frequency(Workspace[0] * 140000); break; |
Blaze513 | 0:f3870f76a890 | 632 | case 0x20: DataLines.frequency(Workspace[0] * 150000); break; |
Blaze513 | 0:f3870f76a890 | 633 | case 0x28: DataLines.frequency(Workspace[0] * 200000); break; |
Blaze513 | 0:f3870f76a890 | 634 | case 0x30: DataLines.frequency(Workspace[0] * 250000); break; |
Blaze513 | 0:f3870f76a890 | 635 | case 0x38: DataLines.frequency(Workspace[0] * 300000); break; |
Blaze513 | 0:f3870f76a890 | 636 | case 0x40: DataLines.frequency(Workspace[0] * 350000); break; |
Blaze513 | 0:f3870f76a890 | 637 | case 0x48: DataLines.frequency(Workspace[0] * 400000); break; |
Blaze513 | 0:f3870f76a890 | 638 | case 0x50: DataLines.frequency(Workspace[0] * 450000); break; |
Blaze513 | 0:f3870f76a890 | 639 | case 0x58: DataLines.frequency(Workspace[0] * 500000); break; |
Blaze513 | 0:f3870f76a890 | 640 | case 0x60: DataLines.frequency(Workspace[0] * 550000); break; |
Blaze513 | 0:f3870f76a890 | 641 | case 0x68: DataLines.frequency(Workspace[0] * 600000); break; |
Blaze513 | 0:f3870f76a890 | 642 | case 0x70: DataLines.frequency(Workspace[0] * 700000); break; |
Blaze513 | 0:f3870f76a890 | 643 | case 0x78: DataLines.frequency(Workspace[0] * 800000); break; |
Blaze513 | 0:f3870f76a890 | 644 | default: break; |
Blaze513 | 0:f3870f76a890 | 645 | //read the CSD card speed bits and speed up card operations |
Blaze513 | 0:f3870f76a890 | 646 | } |
Blaze513 | 0:f3870f76a890 | 647 | } |
Blaze513 | 1:94c648931f84 | 648 | |
Blaze513 | 1:94c648931f84 | 649 | if (CSD[4] & 0x40) |
Blaze513 | 1:94c648931f84 | 650 | //check for switch command class support |
Blaze513 | 1:94c648931f84 | 651 | { |
Blaze513 | 1:94c648931f84 | 652 | t = 0; |
Blaze513 | 1:94c648931f84 | 653 | do |
Blaze513 | 1:94c648931f84 | 654 | { |
Blaze513 | 1:94c648931f84 | 655 | Command(6, 2147483649, Workspace); |
Blaze513 | 1:94c648931f84 | 656 | //switch to high-speed mode (SDR25, 50MHz) |
Blaze513 | 1:94c648931f84 | 657 | t++; |
Blaze513 | 1:94c648931f84 | 658 | } while (Workspace[0] && (Workspace[0] != 0x04) && (t < Timeout)); |
Blaze513 | 1:94c648931f84 | 659 | //some cards that support switch class commands respond with illegal command |
Blaze513 | 1:94c648931f84 | 660 | if (t == Timeout) { return 0x01; } |
Blaze513 | 1:94c648931f84 | 661 | if (!Workspace[0]) |
Blaze513 | 1:94c648931f84 | 662 | { |
Blaze513 | 1:94c648931f84 | 663 | do |
Blaze513 | 1:94c648931f84 | 664 | { |
Blaze513 | 1:94c648931f84 | 665 | ChipSelect.write(0); |
Blaze513 | 1:94c648931f84 | 666 | do |
Blaze513 | 1:94c648931f84 | 667 | { |
Blaze513 | 1:94c648931f84 | 668 | t++; |
Blaze513 | 1:94c648931f84 | 669 | } while ((DataLines.write(0xFF) != 0xFE) && (t < Timeout)); |
Blaze513 | 1:94c648931f84 | 670 | //get to the start-data-block token |
Blaze513 | 1:94c648931f84 | 671 | if (t == Timeout) { ChipSelect.write(1); DataLines.write(0xFF); return 0x01; } |
Blaze513 | 1:94c648931f84 | 672 | for (unsigned char i = 0; i < 64; i++) |
Blaze513 | 1:94c648931f84 | 673 | { |
Blaze513 | 1:94c648931f84 | 674 | FSR[i] = DataLines.write(0xFF); |
Blaze513 | 1:94c648931f84 | 675 | //gather function-status register |
Blaze513 | 1:94c648931f84 | 676 | } |
Blaze513 | 1:94c648931f84 | 677 | Workspace[2] = DataLines.write(0xFF); |
Blaze513 | 1:94c648931f84 | 678 | Workspace[3] = DataLines.write(0xFF); |
Blaze513 | 1:94c648931f84 | 679 | //record data CRC |
Blaze513 | 1:94c648931f84 | 680 | ChipSelect.write(1); |
Blaze513 | 1:94c648931f84 | 681 | DataLines.write(0xFF); |
Blaze513 | 1:94c648931f84 | 682 | DataCRC(64, FSR, Workspace); |
Blaze513 | 1:94c648931f84 | 683 | //calculate CRC |
Blaze513 | 1:94c648931f84 | 684 | t++; |
Blaze513 | 1:94c648931f84 | 685 | } while (((Workspace[0] != Workspace[2]) || (Workspace[1] != Workspace[3])) && |
Blaze513 | 1:94c648931f84 | 686 | (t < Timeout)); |
Blaze513 | 1:94c648931f84 | 687 | //complete CRC |
Blaze513 | 1:94c648931f84 | 688 | if (t == Timeout) { return 0x01; } |
Blaze513 | 1:94c648931f84 | 689 | if ((FSR[13] & 0x02) && ((FSR[16] & 0x0F) == 0x01)) |
Blaze513 | 1:94c648931f84 | 690 | { |
Blaze513 | 1:94c648931f84 | 691 | DataLines.frequency(50000000); |
Blaze513 | 1:94c648931f84 | 692 | //increase speed if function switch was successful |
Blaze513 | 1:94c648931f84 | 693 | } |
Blaze513 | 1:94c648931f84 | 694 | } |
Blaze513 | 1:94c648931f84 | 695 | } |
Blaze513 | 1:94c648931f84 | 696 | |
Blaze513 | 0:f3870f76a890 | 697 | if (!Version) |
Blaze513 | 0:f3870f76a890 | 698 | { |
Blaze513 | 1:94c648931f84 | 699 | t = 0; |
Blaze513 | 1:94c648931f84 | 700 | do |
Blaze513 | 0:f3870f76a890 | 701 | { |
Blaze513 | 0:f3870f76a890 | 702 | Command(16, 512, Workspace); |
Blaze513 | 0:f3870f76a890 | 703 | //set data-block length to 512 bytes |
Blaze513 | 1:94c648931f84 | 704 | t++; |
Blaze513 | 1:94c648931f84 | 705 | } while (Workspace[0] && (t < Timeout)); |
Blaze513 | 1:94c648931f84 | 706 | if (t == Timeout) { return 0x01; } |
Blaze513 | 0:f3870f76a890 | 707 | } |
Blaze513 | 1:94c648931f84 | 708 | |
Blaze513 | 1:94c648931f84 | 709 | if (SelectCRCMode(0)) |
Blaze513 | 1:94c648931f84 | 710 | { return 0x01; } |
Blaze513 | 1:94c648931f84 | 711 | //turn off CRCs |
Blaze513 | 1:94c648931f84 | 712 | |
Blaze513 | 1:94c648931f84 | 713 | return 0x00; |
Blaze513 | 0:f3870f76a890 | 714 | } |
Blaze513 | 0:f3870f76a890 | 715 | |
Blaze513 | 0:f3870f76a890 | 716 | void SDCard::Command(unsigned char Index, unsigned int Argument, unsigned char* Response) |
Blaze513 | 0:f3870f76a890 | 717 | { |
Blaze513 | 1:94c648931f84 | 718 | CommandCRC(&Index, &Argument, Workspace); |
Blaze513 | 1:94c648931f84 | 719 | //calculate command CRC |
Blaze513 | 0:f3870f76a890 | 720 | ChipSelect.write(0); |
Blaze513 | 0:f3870f76a890 | 721 | //assert chip select low to synchronize command |
Blaze513 | 0:f3870f76a890 | 722 | DataLines.write(0x40 | Index); |
Blaze513 | 0:f3870f76a890 | 723 | //the index is assumed valid, commands start with "01b" |
Blaze513 | 0:f3870f76a890 | 724 | DataLines.write(((char*)&Argument)[3]); |
Blaze513 | 0:f3870f76a890 | 725 | DataLines.write(((char*)&Argument)[2]); |
Blaze513 | 0:f3870f76a890 | 726 | DataLines.write(((char*)&Argument)[1]); |
Blaze513 | 0:f3870f76a890 | 727 | DataLines.write(((char*)&Argument)[0]); |
Blaze513 | 0:f3870f76a890 | 728 | //send the argument bytes in order from MSB to LSB (mbed is little endian) |
Blaze513 | 1:94c648931f84 | 729 | DataLines.write(Workspace[0]); |
Blaze513 | 0:f3870f76a890 | 730 | //send the command CRC |
Blaze513 | 1:94c648931f84 | 731 | t = 0; |
Blaze513 | 1:94c648931f84 | 732 | do |
Blaze513 | 0:f3870f76a890 | 733 | { |
Blaze513 | 0:f3870f76a890 | 734 | Response[0] = DataLines.write(0xFF); |
Blaze513 | 0:f3870f76a890 | 735 | //clock the card high to let it run operations, the first byte will be |
Blaze513 | 0:f3870f76a890 | 736 | //busy (all high), the response will be sent some time later |
Blaze513 | 1:94c648931f84 | 737 | t++; |
Blaze513 | 1:94c648931f84 | 738 | } while ((Response[0] & 0x80) && (t < Timeout)); |
Blaze513 | 1:94c648931f84 | 739 | //check for a response by testing if the first bit is low |
Blaze513 | 0:f3870f76a890 | 740 | if ((Index == 8) || (Index == 13) || (Index == 58)) |
Blaze513 | 0:f3870f76a890 | 741 | { |
Blaze513 | 1:94c648931f84 | 742 | for (unsigned char i = 1; i < 5; i++) |
Blaze513 | 0:f3870f76a890 | 743 | { |
Blaze513 | 1:94c648931f84 | 744 | Response[i] = DataLines.write(0xFF); |
Blaze513 | 0:f3870f76a890 | 745 | } |
Blaze513 | 0:f3870f76a890 | 746 | //get the rest of the response |
Blaze513 | 0:f3870f76a890 | 747 | } |
Blaze513 | 0:f3870f76a890 | 748 | ChipSelect.write(1); |
Blaze513 | 0:f3870f76a890 | 749 | //assert chip select high to synchronize command |
Blaze513 | 0:f3870f76a890 | 750 | DataLines.write(0xFF); |
Blaze513 | 0:f3870f76a890 | 751 | //clock the deselected card high to complete processing for some cards |
Blaze513 | 0:f3870f76a890 | 752 | } |
Blaze513 | 0:f3870f76a890 | 753 | |
Blaze513 | 1:94c648931f84 | 754 | void SDCard::CommandCRC(unsigned char* IndexPtr, unsigned int* ArgumentPtr, unsigned char* Result) |
Blaze513 | 0:f3870f76a890 | 755 | { |
Blaze513 | 1:94c648931f84 | 756 | if (CRCMode) |
Blaze513 | 1:94c648931f84 | 757 | { |
Blaze513 | 1:94c648931f84 | 758 | Result[0] = |
Blaze513 | 0:f3870f76a890 | 759 | CommandCRCTable[ |
Blaze513 | 0:f3870f76a890 | 760 | CommandCRCTable[ |
Blaze513 | 0:f3870f76a890 | 761 | CommandCRCTable[ |
Blaze513 | 0:f3870f76a890 | 762 | CommandCRCTable[ |
Blaze513 | 1:94c648931f84 | 763 | CommandCRCTable[ |
Blaze513 | 1:94c648931f84 | 764 | *IndexPtr | 0x40 |
Blaze513 | 1:94c648931f84 | 765 | ] ^ ((char*)ArgumentPtr)[3] |
Blaze513 | 1:94c648931f84 | 766 | ] ^ ((char*)ArgumentPtr)[2] |
Blaze513 | 1:94c648931f84 | 767 | ] ^ ((char*)ArgumentPtr)[1] |
Blaze513 | 1:94c648931f84 | 768 | ] ^ ((char*)ArgumentPtr)[0] |
Blaze513 | 1:94c648931f84 | 769 | ] | 0x01; |
Blaze513 | 1:94c648931f84 | 770 | } |
Blaze513 | 1:94c648931f84 | 771 | else |
Blaze513 | 1:94c648931f84 | 772 | { |
Blaze513 | 1:94c648931f84 | 773 | Result[0] = 0xFF; |
Blaze513 | 1:94c648931f84 | 774 | } |
Blaze513 | 1:94c648931f84 | 775 | //using a CRC table, the CRC result of a byte is equal to the byte |
Blaze513 | 1:94c648931f84 | 776 | //in the table at the address equal to the input byte, a message CRC |
Blaze513 | 1:94c648931f84 | 777 | //is obtained by successively XORing these with the message bytes |
Blaze513 | 0:f3870f76a890 | 778 | } |
Blaze513 | 0:f3870f76a890 | 779 | |
Blaze513 | 0:f3870f76a890 | 780 | void SDCard::DataCRC(unsigned short Length, unsigned char* Data, unsigned char* Result) |
Blaze513 | 0:f3870f76a890 | 781 | { |
Blaze513 | 1:94c648931f84 | 782 | if (CRCMode) |
Blaze513 | 0:f3870f76a890 | 783 | { |
Blaze513 | 1:94c648931f84 | 784 | unsigned char Reference; |
Blaze513 | 1:94c648931f84 | 785 | //store the current CRC lookup value |
Blaze513 | 1:94c648931f84 | 786 | Result[0] = 0x00; |
Blaze513 | 1:94c648931f84 | 787 | Result[1] = 0x00; |
Blaze513 | 1:94c648931f84 | 788 | //initialize result carrier |
Blaze513 | 1:94c648931f84 | 789 | for (unsigned short i = 0; i < Length; i++) |
Blaze513 | 1:94c648931f84 | 790 | //step through each byte of the data to be checked |
Blaze513 | 1:94c648931f84 | 791 | { |
Blaze513 | 1:94c648931f84 | 792 | Reference = Result[0]; |
Blaze513 | 1:94c648931f84 | 793 | //record current crc lookup for both bytes |
Blaze513 | 1:94c648931f84 | 794 | Result[0] = DataCRCTable[2 * Reference] ^ Result[1]; |
Blaze513 | 1:94c648931f84 | 795 | //new fist byte result is XORed with old second byte result |
Blaze513 | 1:94c648931f84 | 796 | Result[1] = DataCRCTable[(2 * Reference) + 1] ^ Data[i]; |
Blaze513 | 1:94c648931f84 | 797 | //new second byte result is XORed with new data byte |
Blaze513 | 1:94c648931f84 | 798 | } |
Blaze513 | 1:94c648931f84 | 799 | for (unsigned char i = 0; i < 2; i++) |
Blaze513 | 1:94c648931f84 | 800 | //the final result must be XORed with two 0x00 bytes. |
Blaze513 | 1:94c648931f84 | 801 | { |
Blaze513 | 1:94c648931f84 | 802 | Reference = Result[0]; |
Blaze513 | 1:94c648931f84 | 803 | Result[0] = DataCRCTable[2 * Reference] ^ Result[1]; |
Blaze513 | 1:94c648931f84 | 804 | Result[1] = DataCRCTable[(2 * Reference) + 1]; |
Blaze513 | 1:94c648931f84 | 805 | } |
Blaze513 | 0:f3870f76a890 | 806 | } |
Blaze513 | 1:94c648931f84 | 807 | else |
Blaze513 | 0:f3870f76a890 | 808 | { |
Blaze513 | 1:94c648931f84 | 809 | Result[0] = 0xFF; |
Blaze513 | 1:94c648931f84 | 810 | Result[1] = 0xFF; |
Blaze513 | 0:f3870f76a890 | 811 | } |
Blaze513 | 0:f3870f76a890 | 812 | } |
Blaze513 | 0:f3870f76a890 | 813 | |
Blaze513 | 0:f3870f76a890 | 814 | void SDCard::GenerateCRCTable(unsigned char Size, unsigned long long Generator, unsigned char* Table) |
Blaze513 | 0:f3870f76a890 | 815 | { |
Blaze513 | 0:f3870f76a890 | 816 | unsigned char Index[9] = {0, 0, 0, 0, 0, 0, 0, 0, 0}; |
Blaze513 | 1:94c648931f84 | 817 | //this will hold information from the generator; the position indicates |
Blaze513 | 1:94c648931f84 | 818 | //the order of the encountered 1, the value indicates its position in |
Blaze513 | 1:94c648931f84 | 819 | //the generator, the 9th entry indicates the number of 1's encountered |
Blaze513 | 1:94c648931f84 | 820 | for (unsigned char i = 0; i < 64; i++) |
Blaze513 | 0:f3870f76a890 | 821 | { |
Blaze513 | 0:f3870f76a890 | 822 | if (((char*)&Generator)[7] & 0x80) |
Blaze513 | 0:f3870f76a890 | 823 | { break; } |
Blaze513 | 0:f3870f76a890 | 824 | Generator = Generator << 1; |
Blaze513 | 0:f3870f76a890 | 825 | //shift generator so that the first bit is high |
Blaze513 | 0:f3870f76a890 | 826 | } |
Blaze513 | 1:94c648931f84 | 827 | for (unsigned char i = 0; i < Size; i++) |
Blaze513 | 0:f3870f76a890 | 828 | { |
Blaze513 | 1:94c648931f84 | 829 | Table[i] = 0x00; |
Blaze513 | 1:94c648931f84 | 830 | //initialize table |
Blaze513 | 0:f3870f76a890 | 831 | } |
Blaze513 | 0:f3870f76a890 | 832 | for (unsigned char i = 0; i < 8; i++) |
Blaze513 | 0:f3870f76a890 | 833 | //increment through each generator bit |
Blaze513 | 0:f3870f76a890 | 834 | { |
Blaze513 | 0:f3870f76a890 | 835 | if ((0x80 >> i) & ((unsigned char*)&Generator)[7]) |
Blaze513 | 0:f3870f76a890 | 836 | //if a 1 is encountered in the generator |
Blaze513 | 0:f3870f76a890 | 837 | { |
Blaze513 | 0:f3870f76a890 | 838 | Index[Index[8]] = i; |
Blaze513 | 0:f3870f76a890 | 839 | Index[8]++; |
Blaze513 | 0:f3870f76a890 | 840 | //record its order and location and increment the counter |
Blaze513 | 0:f3870f76a890 | 841 | } |
Blaze513 | 0:f3870f76a890 | 842 | for (unsigned char j = 0; j < (0x01 << i); j++) |
Blaze513 | 0:f3870f76a890 | 843 | //each bit increases the number of xor operations by a power of 2 |
Blaze513 | 0:f3870f76a890 | 844 | { |
Blaze513 | 0:f3870f76a890 | 845 | for (unsigned char k = 0; k < Size; k++) |
Blaze513 | 0:f3870f76a890 | 846 | //we need to perform operations for each byte in the CRC result |
Blaze513 | 0:f3870f76a890 | 847 | { |
Blaze513 | 0:f3870f76a890 | 848 | Table[(Size * ((0x01 << i) + j)) + k] = Table[(Size * j) + k]; |
Blaze513 | 1:94c648931f84 | 849 | //each new power is equal to all previous entries with an added |
Blaze513 | 1:94c648931f84 | 850 | //xor on the leftmost bit and each succeeding 1 on the generator |
Blaze513 | 0:f3870f76a890 | 851 | for (unsigned char l = 0; l < Index[8]; l++) |
Blaze513 | 0:f3870f76a890 | 852 | //increment through the encountered generator 1s |
Blaze513 | 0:f3870f76a890 | 853 | { |
Blaze513 | 0:f3870f76a890 | 854 | Table[(Size * ((0x01 << i) + j)) + k] ^= (((unsigned char*)&Generator)[7-k] << (i + 1 - Index[l])); |
Blaze513 | 0:f3870f76a890 | 855 | Table[(Size * ((0x01 << i) + j)) + k] ^= (((unsigned char*)&Generator)[6-k] >> (7 - i + Index[l])); |
Blaze513 | 0:f3870f76a890 | 856 | //xor the new bit and the new generator 1s |
Blaze513 | 0:f3870f76a890 | 857 | } |
Blaze513 | 0:f3870f76a890 | 858 | } |
Blaze513 | 0:f3870f76a890 | 859 | } |
Blaze513 | 0:f3870f76a890 | 860 | } |
Blaze513 | 0:f3870f76a890 | 861 | } |