I'm trying to interface a microSD card with a STM32L152-DISCOVERY board (STM32L152RCT6 ARM Cortex-M3), using SPI. The final goal is to use FatFs (http://elm-chan.org/fsw/ff/00index_e.html) to store sensor data on a SDcard that can be read also on a Decktop PC. On Chan's website there is a very helpful flowchart describing the initialization process (http://elm-chan.org/docs/mmc/im/sdinit.png).
Unfortunately, I have a problem in my code that prevents the SDCard to initialize. Specifically, when I send the command ACMD41 (CMD55+CMD41), the SD Card returns always 0x01.
I've modified my code many times, following some posts here on stack overflow, and in particular SDHC microSD card and SPI initialization, but the problem still persists.
Following are the HW and SW setup of my system:
Hardware setup
- The micro SDcard has interface UHS-I, capacity 16GB
- The SDcard adapter is connected to the SPI2 port on the discovery board
- For MISO, MOSI, CLK pins I've enabled the pull-up resistor. According to the mcu datasheet, the internal pull-up resistor should be around 45 Kohm.
- The power for the microSD Card is provided by an arduino board, using the 5V pin (I know it could sound crazy, but I don't have any other power supply right now, and I've read that the 5V pin of the arduino can deliver up to 400 mA @ 5V)
- The arduino GND, the discovery GND and the SDcard adapter GND are all connected togheter.
Software setup - SPI initialization
The SPI frequency is initially set between 125 KHz (I've read that must be in the range 100 KHz - 400 KHz).
/* SPI2 init function */
void MX_SPI2_Init(void)
{
hspi2.Instance = SPI2;
hspi2.Init.Mode = SPI_MODE_MASTER;
hspi2.Init.Direction = SPI_DIRECTION_2LINES;
hspi2.Init.DataSize = SPI_DATASIZE_8BIT;
hspi2.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi2.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi2.Init.NSS = SPI_NSS_SOFT;
hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_128;
hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi2.Init.TIMode = SPI_TIMODE_DISABLE;
hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi2.Init.CRCPolynomial = 10;
if (HAL_SPI_Init(&hspi2) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
}
void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle)
{
GPIO_InitTypeDef GPIO_InitStruct;
if(spiHandle->Instance==SPI2)
{
/* SPI2 clock enable */
__HAL_RCC_SPI2_CLK_ENABLE();
/**SPI2 GPIO Configuration
PB13 ------> SPI2_SCK
PB14 ------> SPI2_MISO
PB15 ------> SPI2_MOSI
*/
GPIO_InitStruct.Pin = GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
}
Software setup - SDCARD initialization
#include <string.h>
#include "ff_gen_drv.h"
#define CS_HIGH() HAL_GPIO_WritePin(GPIOB,GPIO_PIN_12,GPIO_PIN_SET);
#define CS_LOW() HAL_GPIO_WritePin(GPIOB,GPIO_PIN_12,GPIO_PIN_RESET);
#define DEFAULT_TIMEOUT 10
uint8_t dummy_clocks[] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
/* Definitions for MMC/SDC command */
#define CMD0 (0) /* GO_IDLE_STATE */
#define CMD1 (1) /* SEND_OP_COND (MMC) */
#define ACMD41 (41) /* SEND_OP_COND (SDC) */
#define CMD8 (8) /* SEND_IF_COND */
#define CMD9 (9) /* SEND_CSD */
#define CMD10 (10) /* SEND_CID */
#define CMD12 (12) /* STOP_TRANSMISSION */
#define ACMD13 (13) /* SD_STATUS (SDC) */
#define CMD16 (16) /* SET_BLOCKLEN */
#define CMD17 (17) /* READ_SINGLE_BLOCK */
#define CMD18 (18) /* READ_MULTIPLE_BLOCK */
#define CMD23 (23) /* SET_BLOCK_COUNT (MMC) */
#define ACMD23 (23) /* SET_WR_BLK_ERASE_COUNT (SDC) */
#define CMD24 (24) /* WRITE_BLOCK */
#define CMD25 (25) /* WRITE_MULTIPLE_BLOCK */
#define CMD55 (55) /* APP_CMD */
#define CMD58 (58) /* READ_OCR */
static volatile DSTATUS Stat = STA_NOINIT;
extern SPI_HandleTypeDef hspi2;
DSTATUS USER_initialize (BYTE pdrv);
DSTATUS USER_status (BYTE pdrv);
DRESULT USER_read (BYTE pdrv, BYTE *buff, DWORD sector, UINT count);
#if _USE_WRITE == 1
DRESULT USER_write (BYTE pdrv, const BYTE *buff, DWORD sector, UINT count);
#endif /* _USE_WRITE == 1 */
#if _USE_IOCTL == 1
DRESULT USER_ioctl (BYTE pdrv, BYTE cmd, void *buff);
#endif /* _USE_IOCTL == 1 */
Diskio_drvTypeDef USER_Driver =
{
USER_initialize,
USER_status,
USER_read,
#if _USE_WRITE
USER_write,
#endif /* _USE_WRITE == 1 */
#if _USE_IOCTL == 1
USER_ioctl,
#endif /* _USE_IOCTL == 1 */
};
void spi_init(void);
uint8_t send_cmd(BYTE cmd, DWORD arg);
DSTATUS USER_initialize (BYTE pdrv)
{
/* USER CODE BEGIN INIT */
Stat = STA_NOINIT;
enum initialization_state
{
SD_POWER_CYCLE = 0,
SD_SEND_CMD0,
SD_WAIT_CMD0_ANSWER,
SD_SEND_CMD8,
SD_SEND_CMD55,
SD_SEND_ACMD41,
SD_SEND_CMD1,
SD_SEND_CMD58,
SD_SEND_CMD16,
SD_SUCCESS,
SD_ERROR,
} init_phase;
uint8_t response = 0x00;
DWORD arg = 0;
init_phase = SD_POWER_CYCLE;
spi_init();
while(init_phase < SD_SUCCESS)
{
switch(init_phase)
{
case SD_POWER_CYCLE:
// Wait 1 ms
HAL_Delay(1);
HAL_SPI_Transmit(&hspi2, dummy_clocks, sizeof(dummy_clocks), 10);
init_phase = SD_SEND_CMD0;
break;
case SD_SEND_CMD0:
CS_LOW();
response = send_cmd(CMD0,arg);
if(response == 0x01)
init_phase = SD_SEND_CMD8;
else
init_phase = SD_ERROR;
break;
case SD_SEND_CMD8:
arg = 0x000001AA;
response = send_cmd(CMD8,arg);
if(response == 0x01)
init_phase = SD_SEND_CMD55;
else
init_phase = SD_ERROR;
break;
case SD_SEND_CMD55:
arg = 0x00000000;
response = send_cmd(CMD55,arg);
if(response == 0x01)
init_phase = SD_SEND_ACMD41;
else
init_phase = SD_ERROR;
break;
case SD_SEND_ACMD41:
arg = 0x40000000;
response = send_cmd(ACMD41,arg);
if(response == 0x00)
init_phase = SD_SEND_CMD58;
else
{
// HAL_Delay(1000);
init_phase = SD_SEND_CMD55;
}
break;
case SD_SEND_CMD58:
arg = 0x00000000;
response = send_cmd(CMD58,arg);
break;
case SD_ERROR:
CS_HIGH();
Stat = STA_NODISK;
break;
default:
// Something went wrong - Try to re-init
init_phase = SD_POWER_CYCLE;
spi_init();
break;
}
}
return Stat;
/* USER CODE END INIT */
}
...
...
void spi_init(void)
{
CS_HIGH();
HAL_Delay(10);
}
uint8_t send_cmd(BYTE cmd, DWORD arg)
{
// cmd packet is of fixed lenght
uint8_t cmd_packet[6] = {0};
// Response
uint8_t cmd_response = 0xFF;
// R1 is 1 byte only and it is used for most commands
uint8_t r1 = 0xFF;
// Commands R3 and R7 are 5 bytes long, (R1 + trailing 32-bit data)
uint8_t r3_7[5] = {0};
// First byte is the command
// The cmd_packet must start with 01, therefore we add 0x40 to the cmd byte
cmd_packet[0] = 0x40 | cmd;
// Four bytes for the argument
for(uint8_t i = 1; i<=4; i++)
cmd_packet[i] = (uint8_t)(arg >> (4-i)*8);
// Add crc: it must be correct for CMD0 and CMD 8 only; for other commands, we use a dummy crc (0x01)
if(cmd == CMD0)
cmd_packet[5] = 0x95;
else if(cmd == CMD8)
cmd_packet[5] = 0x87;
else if(cmd == ACMD41)
cmd_packet[5] = 0x95;
else
cmd_packet[5] = 0x01;
// Send the command
HAL_SPI_Transmit(&hspi2, cmd_packet, sizeof(cmd_packet), DEFAULT_TIMEOUT);
// Receive the answer from SDcard
switch(cmd)
{
case CMD0:
// Try 3 times to get the answer
for(uint8_t j = 0; j<3; j++)
{
HAL_SPI_Transmit(&hspi2, (uint8_t*)&cmd_response, sizeof(cmd_response), DEFAULT_TIMEOUT);
HAL_SPI_Receive(&hspi2,&r1,sizeof(r1),DEFAULT_TIMEOUT);
if(r1 != 0xFF)
return r1;
}
break;
case CMD8:
HAL_SPI_Transmit(&hspi2, (uint8_t*)&cmd_response, sizeof(cmd_response), DEFAULT_TIMEOUT);
HAL_SPI_Receive(&hspi2,r3_7,sizeof(r3_7),DEFAULT_TIMEOUT);
if( r3_7[3] == 0x01 && r3_7[4] == 0xAA)
return 0x01;
break;
case CMD55:
HAL_SPI_Transmit(&hspi2, (uint8_t*)&cmd_response, sizeof(cmd_response), DEFAULT_TIMEOUT);
HAL_SPI_Receive(&hspi2,&r1,sizeof(r1),DEFAULT_TIMEOUT);
if(r1 != 0xFF)
return r1;
break;
case ACMD41:
for(int i = 0; i<150; i++)
{
HAL_SPI_Transmit(&hspi2, (uint8_t*)&cmd_response, sizeof(cmd_response), DEFAULT_TIMEOUT);
HAL_SPI_Receive(&hspi2,&r1,sizeof(r1),DEFAULT_TIMEOUT);
if(r1 == 0x00)
return r1;
else
HAL_Delay(10);
}
return 0xFF;
break;
case CMD58:
HAL_SPI_Transmit(&hspi2, (uint8_t*)&cmd_response, sizeof(cmd_response), DEFAULT_TIMEOUT);
HAL_SPI_Receive(&hspi2,r3_7,sizeof(r3_7),DEFAULT_TIMEOUT);
if( r3_7[1] & (1<<7))
return 0x01;
else
return 0x00;
break;
}
}
I have the same problem. I interfaced a SDHC card with a STM32F107VC board.And I succeed in using FatFs. Actually I never find any clues about this problem.I forwent using SD module and attached SD with Dupont Line.Like this(连接)
It worked then...I also interfaced a mircoSD,also succeed.
My code:
SD_SPI_Mode.c
SD_SPI_Mode.h
Additionly,my file operation code:
FileOperation.c
Just for test.May be helpful...
Sorry for my poor English,It’s not my mother tongue.