forked from LonelyWolf/stm32
-
Notifications
You must be signed in to change notification settings - Fork 0
/
sdcard.c
564 lines (475 loc) · 15.6 KB
/
sdcard.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
#include <stm32l1xx_rcc.h>
#include <stm32l1xx_gpio.h>
#include <misc.h>
#include <spi.h>
#include <sdcard.h>
SDCard_TypeDef SDCard; // SD card parameters
// Calculate CRC7
// It's a 7 bit CRC with polynomial x^7 + x^3 + 1
// input:
// crcIn - the CRC before (0 for first step)
// data - byte for CRC calculation
// return: the new CRC7
uint8_t CRC7_one(uint8_t crcIn, uint8_t data) {
const uint8_t g = 0x89;
uint8_t i;
crcIn ^= data;
for (i = 0; i < 8; i++) {
if (crcIn & 0x80) crcIn ^= g;
crcIn <<= 1;
}
return crcIn;
}
// Calculate CRC7 value of the buffer
// input:
// pBuf - pointer to the buffer
// len - length of the buffer
// return: the CRC7 value
uint8_t CRC7_buf(uint8_t *pBuf, uint8_t len) {
uint8_t crc = 0;
while (len--) crc = CRC7_one(crc,*pBuf++);
return crc;
}
// Calculate CRC16 CCITT
// It's a 16 bit CRC with polynomial x^16 + x^12 + x^5 + 1
// input:
// crcIn - the CRC before (0 for rist step)
// data - byte for CRC calculation
// return: the CRC16 value
uint16_t CRC16_one(uint16_t crcIn, uint8_t data) {
crcIn = (uint8_t)(crcIn >> 8)|(crcIn << 8);
crcIn ^= data;
crcIn ^= (uint8_t)(crcIn & 0xff) >> 4;
crcIn ^= (crcIn << 8) << 4;
crcIn ^= ((crcIn & 0xff) << 4) << 1;
return crcIn;
}
// Calculate CRC16 CCITT value of the buffer
// input:
// pBuf - pointer to the buffer
// len - length of the buffer
// return: the CRC16 value
uint16_t CRC16_buf(const uint8_t * pBuf, uint16_t len) {
uint16_t crc = 0;
while (len--) crc = CRC16_one(crc,*pBuf++);
return crc;
}
// Send buffer to the SD card
// input:
// pBuf - pointer to the buffer
// len - length of the buffer
// return: last response from SD card
void SD_WriteBuf(uint8_t *pBuf, uint16_t len) {
while (len--) SPIx_SendRecv(SDCARD_SPI_PORT,*pBuf++);
}
// Receive buffer from the SD card
// input:
// pBuf - pointer to the buffer
// len - length of the buffer
void SD_ReadBuf(uint8_t *pBuf, uint16_t len) {
while (len--) *pBuf++ = SPIx_SendRecv(SDCARD_SPI_PORT,0xff);
}
// Send command to the SD card and get response
// input:
// cmd - SD card command
// arg - 32-bit argument for SD card command
// return: SDR_xxx
SDResult_TypeDef SD_Cmd(uint8_t cmd, uint32_t arg, SDCmdResp_TypeDef resp_type, uint8_t *resp) {
uint8_t buf[6];
uint32_t wait = 2000; // response timeout
uint8_t rdLen = 0; // response length
uint8_t response;
// Determine response length
switch (resp_type) {
case SD_R1:
case SD_R1b:
rdLen = 1;
break;
case SD_R2:
rdLen = 2;
break;
case SD_R3:
case SD_R7:
rdLen = 5;
break;
default:
return SDR_BadResponse;
break;
}
// '01' start bits -> [6b]command -> [32b]argument -> [7b]CRC -> '1' end bit
buf[0] = cmd | 0x40; // command
buf[1] = (arg >> 24); // argument is packed big-endian
buf[2] = (arg >> 16) & 0xff;
buf[3] = (arg >> 8) & 0xff;
buf[4] = arg & 0xff;
buf[5] = CRC7_buf(&buf[0],5) | 0x01; // CRC (last bit always '1')
// Select SD card
SDCARD_CS_L();
// Send CMD#
SD_WriteBuf(&buf[0],6);
// Wait for a valid response
do {
response = SPIx_SendRecv(SDCARD_SPI_PORT,0xff);
} while ((response & 0x80) && --wait);
// Timeout
if (wait == 0) {
SPIx_SendRecv(SDCARD_SPI_PORT,0xff);
SDCARD_CS_H();
return SDR_Timeout;
}
// Read the response
while (rdLen--) {
*resp++ = response;
response = SPIx_SendRecv(SDCARD_SPI_PORT,0xff);
}
// Release SD card
SDCARD_CS_H();
return SDR_Success;
}
// Initialize the SD card
// return: SDR_xxx
// note: SPI peripheral must be initialized before
SDResult_TypeDef SD_Init(void) {
uint32_t wait;
uint8_t resp[5]; // buffer for card response
GPIO_InitTypeDef PORT;
// Populate SDCard structure with default values
SDCard.CardCapacity = 0;
SDCard.CardMaxBusClkFreq = 0;
SDCard.CardBlockSize = 0;
SDCard.CardCSDVer = 0;
SDCard.CardType = SDCT_UNKNOWN;
// Configure SDCARD CS control line as push-pull output with pullup
RCC_AHBPeriphClockCmd(SDCARD_PORT_PERIPH,ENABLE);
PORT.GPIO_Mode = GPIO_Mode_OUT;
PORT.GPIO_Speed = GPIO_Speed_40MHz;
PORT.GPIO_OType = GPIO_OType_PP;
PORT.GPIO_PuPd = GPIO_PuPd_UP;
PORT.GPIO_Pin = SDCARD_CS_PIN;
GPIO_Init(SDCARD_CS_PORT,&PORT);
// Set low SPI speed (32MHz -> 125kHz)
SPIx_SetSpeed(SDCARD_SPI_PORT,SPI_BR_256);
SDCard.CardType = SDCT_UNKNOWN;
// Must wait at least 74 clock ticks after reset
wait = 10;
while (wait--) SPIx_SendRecv(SDCARD_SPI_PORT,0xff);
// Software SD card reset (wait for SD card to enter IDLE state)
// Some cards requires many IDLE commands, so do it several times
wait = 100;
do {
SD_Cmd(SD_CMD_GO_IDLE_STATE,0x00,SD_R1,resp);
} while (!(resp[0] & SD_R1_IDLE) && --wait);
if (!wait) return SDR_NoResponse;
// CMD8: SEND_IF_COND. Send this command to verify SD card interface operating condition
// Argument: - [31:12]: Reserved (shall be set to '0')
// - [11:8]: Supply Voltage (VHS) 0x1 (Range: 2.7-3.6 V)
// - [7:0]: Check Pattern (recommended 0xAA)
SD_Cmd(SD_CMD_HS_SEND_EXT_CSD,SD_CHECK_PATTERN,SD_R7,resp); // CMD8
if (resp[0] == 0x05) {
// SDv1 or MMC
// Issue ACMD41 with zero argument while card returns idle state
wait = 100;
do {
SD_Cmd(SD_CMD_APP_CMD,0,SD_R1,resp); // CMD55
if (resp[0] & SD_R1_ILLEGAL_CMD) break; // MMC will respond with R1 'illegal command'
SD_Cmd(SD_CMD_SD_APP_OP_COND,0,SD_R3,resp); // ACMD41
} while (resp[0] != 0 && --wait);
if (!wait || resp[0] & SD_R1_ILLEGAL_CMD) {
// MMC or bad card
// Issue CMD1: initiate initialization process.
wait = 20;
do {
SD_Cmd(SD_CMD_SEND_OP_COND,0,SD_R1,resp); // CMD1
} while (resp[0] != 0 && --wait);
if (!wait) {
return SDR_UnknownCard;
} else {
SDCard.CardType = SDCT_MMC; // MMC
}
} else {
// SDv1 card
SDCard.CardType = SDCT_SDSC_V1; // SDv1
}
} else {
// SDv2 or later
// Check for SDv2 pattern
wait = resp[1] << 24;
wait |= resp[2] << 16;
wait |= resp[3] << 8;
wait |= resp[4];
if ((wait & 0x01ff) != (SD_CHECK_PATTERN & 0x01ff)) return SDR_Unsupported;
// Issue ACMD41 (this can be up to one second long)
wait = 0x2710;
do {
// CMD55: Send leading command for ACMD<n> command.
SD_Cmd(SD_CMD_APP_CMD,0,SD_R1,resp); // CMD55
// ACMD41 - initiate initialization process.
// Set HCS bit (Host Capacity Support) to inform card what
// host support high capacity
SD_Cmd(SD_CMD_SD_APP_OP_COND,1 << 30,SD_R3,resp); // ACMD41
} while (resp[0] && --wait);
if (!wait) return SDR_Timeout;
// This is SDv2
SDCard.CardType = SDCT_SDSC_V2;
// Read the OCR register
SD_Cmd(SD_CMD_READ_OCR,0,SD_R3,resp); // CMD58
if (resp[0] == 0x00) {
// R3 response
wait = resp[1] << 24;
wait |= resp[2] << 16;
wait |= resp[3] << 8;
wait |= resp[4];
} else {
SDCard.CardType = SDCT_UNKNOWN;
return SDR_BadResponse; // bad CMD58 response
}
// If CCS (Card Capacity Status) bit set -> this is SDHC or SDXC card
if (wait & (1 << 30)) SDCard.CardType = SDCT_SDHC; // SDHC or SDXC
}
// Unknown or bad card
if (SDCard.CardType == SDCT_UNKNOWN) return SDR_UnknownCard;
// Set SPI to higher speed (32MHz -> 16MHz)
SPIx_SetSpeed(SDCARD_SPI_PORT,SPI_BR_2);
// Turn off CRC checks
// SD_Cmd(SD_CMD_CRC_ON_OFF,0,SD_R1,resp); // CMD59
// Turn on CRC checks
// SD_Cmd(SD_CMD_CRC_ON_OFF,1,SD_R1,resp); // CMD59
// Must set block size for SDv1,SDv2 and MMC
// SDHC and SDXC always have fixed size 512
if ((SDCard.CardType == SDCT_SDSC_V1) || (SDCard.CardType == SDCT_SDSC_V2) ||
(SDCard.CardType == SDCT_MMC)) {
SD_Cmd(SD_CMD_SET_BLOCKLEN,512,SD_R1,resp); // CMD16
if (resp[0] != 0x00) return SDR_SetBlockSizeFailed;
}
return SDR_Success;
}
// Read register data after sending register read request
// input:
// buf - pointer to the buffer to store the register value
// length - length of the register (bytes)
// return: SDR_xxx
SDResult_TypeDef SD_ReadReg(uint8_t *buf, uint16_t length) {
uint32_t wait;
uint8_t cmdres;
uint16_t CRC_rcv; // Received CRC16 value of the register
// uint16_t CRC_loc; // Calculated CRC16 value of the register
// Select SD card
SDCARD_CS_L();
// Waiting for a start block token
wait = 0xfff; // Recommended timeout is 100ms FIXME: 0xfff is set by sight, need calculate more adequate value
do {
cmdres = SPIx_SendRecv(SDCARD_SPI_PORT,0xff);
} while (cmdres == 0xff && --wait);
if (!wait) {
// It was timeout
SPIx_SendRecv(SDCARD_SPI_PORT,0xff);
SDCARD_CS_H();
return SDR_Timeout;
}
if (cmdres != SD_TOKEN_START_BLOCK) {
// The card respond is not a start token
SPIx_SendRecv(SDCARD_SPI_PORT,0xff);
SDCARD_CS_H();
return SDR_ReadError;
}
// Receive the register value
SD_ReadBuf(buf,length);
// 16-bit CRC (some cards demand to receive this)
// Since some SD cards give wrong CRC values, we don't check if they are correct
CRC_rcv = SPIx_SendRecv(SDCARD_SPI_PORT,0xff) << 8;
CRC_rcv |= SPIx_SendRecv(SDCARD_SPI_PORT,0xff);
// CRC_loc = CRC16_buf(buf,16);
// if (CRC_rcv != CRC_loc) return SD_CRCErorr;
// Provide extra 8 clocks for the card (from SanDisk specification)
SPIx_SendRecv(SDCARD_SPI_PORT,0xff);
// Release SD card
SDCARD_CS_H();
return SDR_Success;
}
// Read the CSD register from the SD card
// return: SDR_xxx
SDResult_TypeDef SD_ReadCSD(void) {
uint8_t cmdres; // result of SD_Cmd
uint8_t resp[5]; // buffer for card response
uint32_t dev_size, dev_size_mul, rd_block_len;
cmdres = SD_Cmd(SD_CMD_SEND_CSD,0,SD_R1,resp); // CMD9
if (cmdres != SDR_Success) return cmdres;
if (resp[0] != 0x00) return SDR_BadResponse;
cmdres = SD_ReadReg(SDCard.CSD,16);
if (cmdres != SDR_Success) return cmdres;
// Parse the CSD register
SDCard.CardCSDVer = SDCard.CSD[0] >> 6; // CSD version
if (SDCard.CardType != SDCT_MMC) {
// SD
SDCard.CardMaxBusClkFreq = SDCard.CSD[3];
rd_block_len = SDCard.CSD[5] & 0x0f; // Max. read data block length
if (SDCard.CardCSDVer == 0) {
// CSD v1.00 (SDSCv1, SDSCv2)
dev_size = (uint32_t)(SDCard.CSD[6] & 0x03) << 10; // Device size
dev_size |= (uint32_t)SDCard.CSD[7] << 2;
dev_size |= (SDCard.CSD[8] & 0xc0) >> 6;
dev_size_mul = (SDCard.CSD[9] & 0x03) << 1; // Device size multiplier
dev_size_mul |= (SDCard.CSD[10] & 0x80) >> 7;
SDCard.CardCapacity = (dev_size + 1);
SDCard.CardCapacity *= (1 << (dev_size_mul + 2));
SDCard.CardBlockSize = 1 << (rd_block_len);
SDCard.CardCapacity *= SDCard.CardBlockSize;
} else {
// CSD v2.00 (SDHC, SDXC)
dev_size = (SDCard.CSD[7] & 0x3f) << 16;
dev_size |= SDCard.CSD[8] << 8;
dev_size |= SDCard.CSD[9];
SDCard.CardBlockSize = 512;
SDCard.CardCapacity = (dev_size + 1) * SDCard.CardBlockSize;
}
} else {
// MMC
SDCard.CardMaxBusClkFreq = SDCard.CSD[3];
dev_size = (uint32_t)(SDCard.CSD[6] & 0x03) << 8; // C_SIZE
dev_size += (uint32_t)SDCard.CSD[7];
dev_size <<= 2;
dev_size += SDCard.CSD[8] >> 6;
SDCard.CardBlockSize = 1 << (SDCard.CSD[5] & 0x0f); // MMC read block length
dev_size_mul = ((SDCard.CSD[9] & 0x03) << 1) + ((SDCard.CSD[10] & 0x80) >> 7);
SDCard.CardCapacity = (dev_size + 1) * (1 << (dev_size_mul + 2)) * SDCard.CardBlockSize;
}
return SDR_Success;
}
// Read the CID register from the SD card
// return: SDR_xxx
SDResult_TypeDef SD_ReadCID(void) {
uint8_t cmdres; // result of SD_Cmd
uint8_t resp[5]; // buffer for card response
cmdres = SD_Cmd(SD_CMD_SEND_CID,0,SD_R1,resp); // CMD10
if (cmdres != SDR_Success) return cmdres;
if (resp[0] != 0x00) return SDR_BadResponse;
cmdres = SD_ReadReg(SDCard.CID,16);
if (cmdres != SDR_Success) return cmdres;
// Parse CID register
// ...
return SDR_Success;
}
// Read block of data from the SD card
// input:
// addr - start address of the block (must be power of two)
// pBuf - pointer to the buffer for received data
// len - buffer length
// return: SDR_xxx
SDResult_TypeDef SD_ReadBlock(uint32_t addr, uint8_t *pBuf, uint32_t len) {
uint32_t wait;
uint8_t cmdres;
uint8_t resp[5];
uint16_t CRC_rcv; // Received CRC16 of the block
uint16_t CRC_loc; // Calculated CRC16 of the block
// SDSC card uses byte unit address and
// SDHC/SDXC cards use block unit address (1 unit = 512 bytes)
// For SDHC/SDXC card addr must be converted to block address
if (SDCard.CardType == SDCT_SDHC) addr >>= 9;
cmdres = SD_Cmd(SD_CMD_READ_SINGLE_BLOCK,addr,SD_R1,resp); // CMD17
if (cmdres != SDR_Success) return cmdres;
if (resp[0] == 0x00) {
// Select SD card
SDCARD_CS_L();
// Waiting for a start block token
wait = 0xfff; // Recommended timeout is 100ms FIXME: 0xfff is set by sight, need calculate more adequate value
do {
cmdres = SPIx_SendRecv(SDCARD_SPI_PORT,0xff);
} while (cmdres == 0xff && --wait);
if (!wait) {
// It was timeout
SPIx_SendRecv(SDCARD_SPI_PORT,0xff);
SDCARD_CS_H();
return SDR_Timeout;
}
if (cmdres != SD_TOKEN_START_BLOCK) {
// The card respond is not a start token
SPIx_SendRecv(SDCARD_SPI_PORT,0xff);
SDCARD_CS_H();
if (cmdres & SD_TOKEN_READ_RANGE_ERROR) {
// Specified address out of range
return SDR_AddrError;
}
return SDR_ReadError;
}
// Receive data block
SD_ReadBuf(pBuf,len);
// 16-bit CRC (it must be received even if CRC is off)
CRC_rcv = SPIx_SendRecv(SDCARD_SPI_PORT,0xff) << 8;
CRC_rcv |= SPIx_SendRecv(SDCARD_SPI_PORT,0xff);
CRC_loc = CRC16_buf(pBuf,len);
// Provide extra 8 clocks for the card (from SanDisk specification)
SPIx_SendRecv(SDCARD_SPI_PORT,0xff);
// Release SD card
SDCARD_CS_H();
} else {
// CMD17 has been rejected
if (resp[0] & SD_R1_ADDR_ERROR) {
// Given address is misaligned
return SDR_AddrError;
}
if (resp[0] & SD_R1_PARAM_ERROR) {
// Given address out of bounds
return SDR_AddrError;
}
// Some unknown error
return SDR_ReadError;
}
return (CRC_rcv == CRC_loc) ? SDR_Success : SDR_CRCError;
}
// Write block of data to the SD card
// input:
// addr - start address of the block (must be power of two)
// pBuf - pointer to the buffer with data
// len - buffer length
// return: SDR_xxx
SDResult_TypeDef SD_WriteBlock(uint32_t addr, uint8_t *pBuf, uint32_t len) {
uint32_t wait;
uint8_t cmdres;
uint8_t resp[5];
uint16_t CRC_loc; // Calculated CRC16 of the block
// Calculate 16-bit CRC
CRC_loc = CRC16_buf(pBuf,len);
// SDSC card uses byte unit address and
// SDHC/SDXC cards use block unit address (1 unit = 512 bytes)
// For SDHC/SDXC card addr must be converted to block address
if (SDCard.CardType == SDCT_SDHC) addr >>= 9;
cmdres = SD_Cmd(SD_CMD_WRITE_SINGLE_BLOCK,addr,SD_R1,resp); // CMD24
if (cmdres != SDR_Success) return cmdres;
if (resp[0] == 0x00) {
// Select SD card
SDCARD_CS_L();
// Send start block token
SPIx_SendRecv(SDCARD_SPI_PORT,SD_TOKEN_START_BLOCK);
// Send data block
SD_WriteBuf(pBuf,len);
// Send CRC
SPIx_SendRecv(SDCARD_SPI_PORT,CRC_loc >> 8);
SPIx_SendRecv(SDCARD_SPI_PORT,(uint8_t)CRC_loc);
// Get response from the SD card
cmdres = SPIx_SendRecv(SDCARD_SPI_PORT,0xff);
cmdres &= 0x1f;
if (cmdres != SD_TOKEN_DATA_ACCEPTED) {
// Data block rejected by SD card for some reason
// Release SD card
SPIx_SendRecv(SDCARD_SPI_PORT,0xff);
SDCARD_CS_H();
if (cmdres & SD_TOKEN_WRITE_CRC_ERROR) return SDR_WriteCRCError;
if (cmdres & SD_TOKEN_WRITE_ERROR) return SDR_WriteErrorInternal;
return SDR_WriteError;
}
// Wait while the SD card is busy by data programming
wait = 0x7fff; // Recommended timeout is 250ms (500ms for SDXC) FIXME: 0x7fff is set by sight, need calculate more adequate value
do {
cmdres = SPIx_SendRecv(SDCARD_SPI_PORT,0xff);
} while (cmdres == 0 && --wait);
// Provide extra 8 clocks for the card (from SanDisk specification)
SPIx_SendRecv(SDCARD_SPI_PORT,0xff);
// Release SD card
SDCARD_CS_H();
} else {
// Card rejected CMD24
if ((resp[0] & SD_R1_ADDR_ERROR) || (resp[0] & SD_R1_PARAM_ERROR)) return SDR_AddrError;
return SDR_WriteError;
}
return SDR_Success;
}