*
This commit is contained in:
@@ -0,0 +1,560 @@
|
||||
/* vim: set ai et ts=4 sw=4: */
|
||||
|
||||
#include "sdcard.h"
|
||||
|
||||
extern SPI_HandleTypeDef SDCARD_SPI_PORT;
|
||||
|
||||
static void SDCARD_Select() {
|
||||
HAL_GPIO_WritePin(SDCARD_CS_GPIO_Port, SDCARD_CS_Pin, GPIO_PIN_RESET);
|
||||
}
|
||||
|
||||
void SDCARD_Unselect() {
|
||||
HAL_GPIO_WritePin(SDCARD_CS_GPIO_Port, SDCARD_CS_Pin, GPIO_PIN_SET);
|
||||
}
|
||||
|
||||
/*
|
||||
R1: 0abcdefg
|
||||
||||||`- 1th bit (g): card is in idle state
|
||||
|||||`-- 2th bit (f): erase sequence cleared
|
||||
||||`--- 3th bit (e): illigal command detected
|
||||
|||`---- 4th bit (d): crc check error
|
||||
||`----- 5th bit (c): error in the sequence of erase commands
|
||||
|`------ 6th bit (b): misaligned addres used in command
|
||||
`------- 7th bit (a): command argument outside allowed range
|
||||
(8th bit is always zero)
|
||||
*/
|
||||
static uint8_t SDCARD_ReadR1() {
|
||||
uint8_t r1;
|
||||
// make sure FF is transmitted during receive
|
||||
uint8_t tx = 0xFF;
|
||||
for(;;) {
|
||||
HAL_SPI_TransmitReceive(&SDCARD_SPI_PORT, &tx, &r1, sizeof(r1), HAL_MAX_DELAY);
|
||||
if((r1 & 0x80) == 0) // 8th bit alwyas zero, r1 recevied
|
||||
break;
|
||||
}
|
||||
return r1;
|
||||
}
|
||||
|
||||
// data token for CMD9, CMD17, CMD18 and CMD24 are the same
|
||||
#define DATA_TOKEN_CMD9 0xFE
|
||||
#define DATA_TOKEN_CMD17 0xFE
|
||||
#define DATA_TOKEN_CMD18 0xFE
|
||||
#define DATA_TOKEN_CMD24 0xFE
|
||||
#define DATA_TOKEN_CMD25 0xFC
|
||||
|
||||
static int SDCARD_WaitDataToken(uint8_t token) {
|
||||
uint8_t fb;
|
||||
// make sure FF is transmitted during receive
|
||||
uint8_t tx = 0xFF;
|
||||
for(;;) {
|
||||
HAL_SPI_TransmitReceive(&SDCARD_SPI_PORT, &tx, &fb, sizeof(fb), HAL_MAX_DELAY);
|
||||
if(fb == token)
|
||||
break;
|
||||
|
||||
if(fb != 0xFF)
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int SDCARD_ReadBytes(uint8_t* buff, size_t buff_size) {
|
||||
// make sure FF is transmitted during receive
|
||||
uint8_t tx = 0xFF;
|
||||
while(buff_size > 0) {
|
||||
HAL_SPI_TransmitReceive(&SDCARD_SPI_PORT, &tx, buff, 1, HAL_MAX_DELAY);
|
||||
buff++;
|
||||
buff_size--;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int SDCARD_WaitNotBusy() {
|
||||
uint8_t busy;
|
||||
do {
|
||||
if(SDCARD_ReadBytes(&busy, sizeof(busy)) < 0) {
|
||||
return -1;
|
||||
}
|
||||
} while(busy != 0xFF);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SDCARD_Init() {
|
||||
/*
|
||||
Step 1.
|
||||
|
||||
Set DI and CS high and apply 74 or more clock pulses to SCLK. Without this
|
||||
step under certain circumstances SD-card will not work. For instance, when
|
||||
multiple SPI devices are sharing the same bus (i.e. MISO, MOSI, CS).
|
||||
*/
|
||||
SDCARD_Unselect();
|
||||
|
||||
uint8_t high = 0xFF;
|
||||
for(int i = 0; i < 10; i++) { // 80 clock pulses
|
||||
HAL_SPI_Transmit(&SDCARD_SPI_PORT, &high, sizeof(high), HAL_MAX_DELAY);
|
||||
}
|
||||
|
||||
SDCARD_Select();
|
||||
|
||||
/*
|
||||
Step 2.
|
||||
|
||||
Send CMD0 (GO_IDLE_STATE): Reset the SD card.
|
||||
*/
|
||||
if(SDCARD_WaitNotBusy() < 0) { // keep this!
|
||||
SDCARD_Unselect();
|
||||
return -1;
|
||||
}
|
||||
|
||||
{
|
||||
static const uint8_t cmd[] =
|
||||
{ 0x40 | 0x00 /* CMD0 */, 0x00, 0x00, 0x00, 0x00 /* ARG = 0 */, (0x4A << 1) | 1 /* CRC7 + end bit */ };
|
||||
HAL_SPI_Transmit(&SDCARD_SPI_PORT, (uint8_t*)cmd, sizeof(cmd), HAL_MAX_DELAY);
|
||||
}
|
||||
|
||||
if(SDCARD_ReadR1() != 0x01) {
|
||||
SDCARD_Unselect();
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
Step 3.
|
||||
|
||||
After the card enters idle state with a CMD0, send a CMD8 with argument of
|
||||
0x000001AA and correct CRC prior to initialization process. If the CMD8 is
|
||||
rejected with illigal command error (0x05), the card is SDC version 1 or
|
||||
MMC version 3. If accepted, R7 response (R1(0x01) + 32-bit return value)
|
||||
will be returned. The lower 12 bits in the return value 0x1AA means that
|
||||
the card is SDC version 2 and it can work at voltage range of 2.7 to 3.6
|
||||
volts. If not the case, the card should be rejected.
|
||||
*/
|
||||
if(SDCARD_WaitNotBusy() < 0) { // keep this!
|
||||
SDCARD_Unselect();
|
||||
return -1;
|
||||
}
|
||||
|
||||
{
|
||||
static const uint8_t cmd[] =
|
||||
{ 0x40 | 0x08 /* CMD8 */, 0x00, 0x00, 0x01, 0xAA /* ARG */, (0x43 << 1) | 1 /* CRC7 + end bit */ };
|
||||
HAL_SPI_Transmit(&SDCARD_SPI_PORT, (uint8_t*)cmd, sizeof(cmd), HAL_MAX_DELAY);
|
||||
}
|
||||
|
||||
if(SDCARD_ReadR1() != 0x01) {
|
||||
SDCARD_Unselect();
|
||||
return -2; // not an SDHC/SDXC card (i.e. SDSC, not supported)
|
||||
}
|
||||
|
||||
{
|
||||
uint8_t resp[4];
|
||||
if(SDCARD_ReadBytes(resp, sizeof(resp)) < 0) {
|
||||
SDCARD_Unselect();
|
||||
return -3;
|
||||
}
|
||||
|
||||
if(((resp[2] & 0x01) != 1) || (resp[3] != 0xAA)) {
|
||||
SDCARD_Unselect();
|
||||
return -4;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Step 4.
|
||||
|
||||
And then initiate initialization with ACMD41 with HCS flag (bit 30).
|
||||
*/
|
||||
for(;;) {
|
||||
if(SDCARD_WaitNotBusy() < 0) { // keep this!
|
||||
SDCARD_Unselect();
|
||||
return -1;
|
||||
}
|
||||
|
||||
{
|
||||
static const uint8_t cmd[] =
|
||||
{ 0x40 | 0x37 /* CMD55 */, 0x00, 0x00, 0x00, 0x00 /* ARG */, (0x7F << 1) | 1 /* CRC7 + end bit */ };
|
||||
HAL_SPI_Transmit(&SDCARD_SPI_PORT, (uint8_t*)cmd, sizeof(cmd), HAL_MAX_DELAY);
|
||||
}
|
||||
|
||||
if(SDCARD_ReadR1() != 0x01) {
|
||||
SDCARD_Unselect();
|
||||
return -5;
|
||||
}
|
||||
|
||||
if(SDCARD_WaitNotBusy() < 0) { // keep this!
|
||||
SDCARD_Unselect();
|
||||
return -1;
|
||||
}
|
||||
|
||||
{
|
||||
static const uint8_t cmd[] =
|
||||
{ 0x40 | 0x29 /* ACMD41 */, 0x40, 0x00, 0x00, 0x00 /* ARG */, (0x7F << 1) | 1 /* CRC7 + end bit */ };
|
||||
HAL_SPI_Transmit(&SDCARD_SPI_PORT, (uint8_t*)cmd, sizeof(cmd), HAL_MAX_DELAY);
|
||||
}
|
||||
|
||||
uint8_t r1 = SDCARD_ReadR1();
|
||||
if(r1 == 0x00) {
|
||||
break;
|
||||
}
|
||||
|
||||
if(r1 != 0x01) {
|
||||
SDCARD_Unselect();
|
||||
return -6;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Step 5.
|
||||
|
||||
After the initialization completed, read OCR register with CMD58 and check
|
||||
CCS flag (bit 30). When it is set, the card is a high-capacity card known
|
||||
as SDHC/SDXC.
|
||||
*/
|
||||
if(SDCARD_WaitNotBusy() < 0) { // keep this!
|
||||
SDCARD_Unselect();
|
||||
return -1;
|
||||
}
|
||||
|
||||
{
|
||||
static const uint8_t cmd[] =
|
||||
{ 0x40 | 0x3A /* CMD58 */, 0x00, 0x00, 0x00, 0x00 /* ARG */, (0x7F << 1) | 1 /* CRC7 + end bit */ };
|
||||
HAL_SPI_Transmit(&SDCARD_SPI_PORT, (uint8_t*)cmd, sizeof(cmd), HAL_MAX_DELAY);
|
||||
}
|
||||
|
||||
if(SDCARD_ReadR1() != 0x00) {
|
||||
SDCARD_Unselect();
|
||||
return -7;
|
||||
}
|
||||
|
||||
{
|
||||
uint8_t resp[4];
|
||||
if(SDCARD_ReadBytes(resp, sizeof(resp)) < 0) {
|
||||
SDCARD_Unselect();
|
||||
return -8;
|
||||
}
|
||||
|
||||
if((resp[0] & 0xC0) != 0xC0) {
|
||||
SDCARD_Unselect();
|
||||
return -9;
|
||||
}
|
||||
}
|
||||
|
||||
SDCARD_Unselect();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int SDCARD_GetBlocksNumber(uint32_t* num) {
|
||||
uint8_t csd[16];
|
||||
uint8_t crc[2];
|
||||
|
||||
SDCARD_Select();
|
||||
|
||||
if(SDCARD_WaitNotBusy() < 0) { // keep this!
|
||||
SDCARD_Unselect();
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* CMD9 (SEND_CSD) command */
|
||||
{
|
||||
static const uint8_t cmd[] =
|
||||
{ 0x40 | 0x09 /* CMD9 */, 0x00, 0x00, 0x00, 0x00 /* ARG */, (0x7F << 1) | 1 /* CRC7 + end bit */ };
|
||||
HAL_SPI_Transmit(&SDCARD_SPI_PORT, (uint8_t*)cmd, sizeof(cmd), HAL_MAX_DELAY);
|
||||
}
|
||||
|
||||
if(SDCARD_ReadR1() != 0x00) {
|
||||
SDCARD_Unselect();
|
||||
return -2;
|
||||
}
|
||||
|
||||
if(SDCARD_WaitDataToken(DATA_TOKEN_CMD9) < 0) {
|
||||
SDCARD_Unselect();
|
||||
return -3;
|
||||
}
|
||||
|
||||
if(SDCARD_ReadBytes(csd, sizeof(csd)) < 0) {
|
||||
SDCARD_Unselect();
|
||||
return -4;
|
||||
}
|
||||
|
||||
if(SDCARD_ReadBytes(crc, sizeof(crc)) < 0) {
|
||||
SDCARD_Unselect();
|
||||
return -5;
|
||||
}
|
||||
|
||||
SDCARD_Unselect();
|
||||
|
||||
// first byte is VVxxxxxxxx where VV is csd.version
|
||||
if((csd[0] & 0xC0) != 0x40) // csd.version != 1
|
||||
return -6;
|
||||
|
||||
uint32_t tmp = csd[7] & 0x3F; // two bits are reserved
|
||||
tmp = (tmp << 8) | csd[8];
|
||||
tmp = (tmp << 8) | csd[9];
|
||||
// Full volume: (C_SIZE+1)*512KByte == (C_SIZE+1)<<19
|
||||
// Block size: 512Byte == 1<<9
|
||||
// Blocks number: CARD_SIZE/BLOCK_SIZE = (C_SIZE+1)*(1<<19) / (1<<9) = (C_SIZE+1)*(1<<10)
|
||||
tmp = (tmp + 1) << 10;
|
||||
*num = tmp;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SDCARD_ReadSingleBlock(uint32_t blockNum, uint8_t* buff) {
|
||||
uint8_t crc[2];
|
||||
|
||||
SDCARD_Select();
|
||||
|
||||
if(SDCARD_WaitNotBusy() < 0) { // keep this!
|
||||
SDCARD_Unselect();
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* CMD17 (SEND_SINGLE_BLOCK) command */
|
||||
uint8_t cmd[] = {
|
||||
0x40 | 0x11 /* CMD17 */,
|
||||
(blockNum >> 24) & 0xFF, /* ARG */
|
||||
(blockNum >> 16) & 0xFF,
|
||||
(blockNum >> 8) & 0xFF,
|
||||
blockNum & 0xFF,
|
||||
(0x7F << 1) | 1 /* CRC7 + end bit */
|
||||
};
|
||||
HAL_SPI_Transmit(&SDCARD_SPI_PORT, (uint8_t*)cmd, sizeof(cmd), HAL_MAX_DELAY);
|
||||
|
||||
if(SDCARD_ReadR1() != 0x00) {
|
||||
SDCARD_Unselect();
|
||||
return -2;
|
||||
}
|
||||
|
||||
if(SDCARD_WaitDataToken(DATA_TOKEN_CMD17) < 0) {
|
||||
SDCARD_Unselect();
|
||||
return -3;
|
||||
}
|
||||
|
||||
if(SDCARD_ReadBytes(buff, 512) < 0) {
|
||||
SDCARD_Unselect();
|
||||
return -4;
|
||||
}
|
||||
|
||||
if(SDCARD_ReadBytes(crc, 2) < 0) {
|
||||
SDCARD_Unselect();
|
||||
return -5;
|
||||
}
|
||||
|
||||
SDCARD_Unselect();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int SDCARD_WriteSingleBlock(uint32_t blockNum, const uint8_t* buff) {
|
||||
SDCARD_Select();
|
||||
|
||||
if(SDCARD_WaitNotBusy() < 0) { // keep this!
|
||||
SDCARD_Unselect();
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* CMD24 (WRITE_BLOCK) command */
|
||||
uint8_t cmd[] = {
|
||||
0x40 | 0x18 /* CMD24 */,
|
||||
(blockNum >> 24) & 0xFF, /* ARG */
|
||||
(blockNum >> 16) & 0xFF,
|
||||
(blockNum >> 8) & 0xFF,
|
||||
blockNum & 0xFF,
|
||||
(0x7F << 1) | 1 /* CRC7 + end bit */
|
||||
};
|
||||
HAL_SPI_Transmit(&SDCARD_SPI_PORT, (uint8_t*)cmd, sizeof(cmd), HAL_MAX_DELAY);
|
||||
|
||||
if(SDCARD_ReadR1() != 0x00) {
|
||||
SDCARD_Unselect();
|
||||
return -2;
|
||||
}
|
||||
|
||||
uint8_t dataToken = DATA_TOKEN_CMD24;
|
||||
uint8_t crc[2] = { 0xFF, 0xFF };
|
||||
HAL_SPI_Transmit(&SDCARD_SPI_PORT, &dataToken, sizeof(dataToken), HAL_MAX_DELAY);
|
||||
HAL_SPI_Transmit(&SDCARD_SPI_PORT, (uint8_t*)buff, 512, HAL_MAX_DELAY);
|
||||
HAL_SPI_Transmit(&SDCARD_SPI_PORT, crc, sizeof(crc), HAL_MAX_DELAY);
|
||||
|
||||
/*
|
||||
dataResp:
|
||||
xxx0abc1
|
||||
010 - Data accepted
|
||||
101 - Data rejected due to CRC error
|
||||
110 - Data rejected due to write error
|
||||
*/
|
||||
uint8_t dataResp;
|
||||
SDCARD_ReadBytes(&dataResp, sizeof(dataResp));
|
||||
if((dataResp & 0x1F) != 0x05) { // data rejected
|
||||
SDCARD_Unselect();
|
||||
return -3;
|
||||
}
|
||||
|
||||
if(SDCARD_WaitNotBusy() < 0) {
|
||||
SDCARD_Unselect();
|
||||
return -4;
|
||||
}
|
||||
|
||||
SDCARD_Unselect();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SDCARD_ReadBegin(uint32_t blockNum) {
|
||||
SDCARD_Select();
|
||||
|
||||
if(SDCARD_WaitNotBusy() < 0) { // keep this!
|
||||
SDCARD_Unselect();
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* CMD18 (READ_MULTIPLE_BLOCK) command */
|
||||
uint8_t cmd[] = {
|
||||
0x40 | 0x12 /* CMD18 */,
|
||||
(blockNum >> 24) & 0xFF, /* ARG */
|
||||
(blockNum >> 16) & 0xFF,
|
||||
(blockNum >> 8) & 0xFF,
|
||||
blockNum & 0xFF,
|
||||
(0x7F << 1) | 1 /* CRC7 + end bit */
|
||||
};
|
||||
HAL_SPI_Transmit(&SDCARD_SPI_PORT, (uint8_t*)cmd, sizeof(cmd), HAL_MAX_DELAY);
|
||||
|
||||
if(SDCARD_ReadR1() != 0x00) {
|
||||
SDCARD_Unselect();
|
||||
return -2;
|
||||
}
|
||||
|
||||
SDCARD_Unselect();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SDCARD_ReadData(uint8_t* buff) {
|
||||
uint8_t crc[2];
|
||||
SDCARD_Select();
|
||||
|
||||
if(SDCARD_WaitDataToken(DATA_TOKEN_CMD18) < 0) {
|
||||
SDCARD_Unselect();
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(SDCARD_ReadBytes(buff, 512) < 0) {
|
||||
SDCARD_Unselect();
|
||||
return -2;
|
||||
}
|
||||
|
||||
if(SDCARD_ReadBytes(crc, 2) < 0) {
|
||||
SDCARD_Unselect();
|
||||
return -3;
|
||||
}
|
||||
|
||||
SDCARD_Unselect();
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int SDCARD_ReadEnd() {
|
||||
SDCARD_Select();
|
||||
|
||||
/* CMD12 (STOP_TRANSMISSION) */
|
||||
{
|
||||
static const uint8_t cmd[] = { 0x40 | 0x0C /* CMD12 */, 0x00, 0x00, 0x00, 0x00 /* ARG */, (0x7F << 1) | 1 };
|
||||
HAL_SPI_Transmit(&SDCARD_SPI_PORT, (uint8_t*)cmd, sizeof(cmd), HAL_MAX_DELAY);
|
||||
}
|
||||
|
||||
/*
|
||||
The received byte immediataly following CMD12 is a stuff byte, it should be
|
||||
discarded before receive the response of the CMD12
|
||||
*/
|
||||
uint8_t stuffByte;
|
||||
if(SDCARD_ReadBytes(&stuffByte, sizeof(stuffByte)) < 0) {
|
||||
SDCARD_Unselect();
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(SDCARD_ReadR1() != 0x00) {
|
||||
SDCARD_Unselect();
|
||||
return -2;
|
||||
}
|
||||
|
||||
SDCARD_Unselect();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int SDCARD_WriteBegin(uint32_t blockNum) {
|
||||
SDCARD_Select();
|
||||
|
||||
if(SDCARD_WaitNotBusy() < 0) { // keep this!
|
||||
SDCARD_Unselect();
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* CMD25 (WRITE_MULTIPLE_BLOCK) command */
|
||||
uint8_t cmd[] = {
|
||||
0x40 | 0x19 /* CMD25 */,
|
||||
(blockNum >> 24) & 0xFF, /* ARG */
|
||||
(blockNum >> 16) & 0xFF,
|
||||
(blockNum >> 8) & 0xFF,
|
||||
blockNum & 0xFF,
|
||||
(0x7F << 1) | 1 /* CRC7 + end bit */
|
||||
};
|
||||
HAL_SPI_Transmit(&SDCARD_SPI_PORT, (uint8_t*)cmd, sizeof(cmd), HAL_MAX_DELAY);
|
||||
|
||||
if(SDCARD_ReadR1() != 0x00) {
|
||||
SDCARD_Unselect();
|
||||
return -2;
|
||||
}
|
||||
|
||||
SDCARD_Unselect();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SDCARD_WriteData(const uint8_t* buff) {
|
||||
SDCARD_Select();
|
||||
|
||||
uint8_t dataToken = DATA_TOKEN_CMD25;
|
||||
uint8_t crc[2] = { 0xFF, 0xFF };
|
||||
HAL_SPI_Transmit(&SDCARD_SPI_PORT, &dataToken, sizeof(dataToken), HAL_MAX_DELAY);
|
||||
HAL_SPI_Transmit(&SDCARD_SPI_PORT, (uint8_t*)buff, 512, HAL_MAX_DELAY);
|
||||
HAL_SPI_Transmit(&SDCARD_SPI_PORT, crc, sizeof(crc), HAL_MAX_DELAY);
|
||||
|
||||
/*
|
||||
dataResp:
|
||||
xxx0abc1
|
||||
010 - Data accepted
|
||||
101 - Data rejected due to CRC error
|
||||
110 - Data rejected due to write error
|
||||
*/
|
||||
uint8_t dataResp;
|
||||
SDCARD_ReadBytes(&dataResp, sizeof(dataResp));
|
||||
if((dataResp & 0x1F) != 0x05) { // data rejected
|
||||
SDCARD_Unselect();
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(SDCARD_WaitNotBusy() < 0) {
|
||||
SDCARD_Unselect();
|
||||
return -2;
|
||||
}
|
||||
|
||||
SDCARD_Unselect();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SDCARD_WriteEnd() {
|
||||
SDCARD_Select();
|
||||
|
||||
uint8_t stopTran = 0xFD; // stop transaction token for CMD25
|
||||
HAL_SPI_Transmit(&SDCARD_SPI_PORT, &stopTran, sizeof(stopTran), HAL_MAX_DELAY);
|
||||
|
||||
// skip one byte before readyng "busy"
|
||||
// this is required by the spec and is necessary for some real SD-cards!
|
||||
uint8_t skipByte;
|
||||
SDCARD_ReadBytes(&skipByte, sizeof(skipByte));
|
||||
|
||||
if(SDCARD_WaitNotBusy() < 0) {
|
||||
SDCARD_Unselect();
|
||||
return -1;
|
||||
}
|
||||
|
||||
SDCARD_Unselect();
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user