SDIO读写SD卡

平台:STM32ZET6(核心板)+ST-LINK/V2+SD卡+USB串口线

工程介绍:主要文件在USER组中,bsp_sdio_sdcard.c,bsp_sdio_sdcard.h和main.c,另外FatFs是用来后面移植文件系统使用的,对于本节内容暂时不需要。bsp_sdio_sdcard.c和bsp_sdio_sdcard.h文件主要参考教材《STM32库开发实战指南——基于STM32F03》。教材中部分省略内容来自于其他网友总结,在此表示感谢。

本文想大概讲一下SD卡读写实验的具体步骤,以及最重要的是一个细节问题,它使我在这个问题上耗了两天的时间。

大概流程如下:

1.硬件设计:

因为在该平台的板子上已经存在了一个SD卡接口,因此,不需要额外的连线,只需要一条串口连接线连接到电脑上输出调试信息即可。

2.软件设计:

操作的大致流程:

1》初始化相关的GPIO及SDIO外设;

2》配置SDIO基本通信环境进入卡识别模式,通过几个命令处理得到卡类型;

3》如果是可用卡就进入数据传输模式,接下来可以进行读写和擦差操作。

具体的步骤:

因为官方固件库有实现的案例,所以我们不妨”拿来主义“。其实bsp_sdio_sdcard.c和bsp_sdio_sdcard.h就是对stm32_eval_sdio_sd.c和stm32_eval_sdio_sd.h文件的修改和借鉴。我是直接把名字改了,然后在源文件的基础上做了修改。

2.1 GPIO的初始化和DMA配置(以下函数位于bsp_sdio_sdcard.c和bsp_sdio_sdcard.h文件中,其大部分内容来自于上面固件库的例程stm32_eval_sdio_sd.c和stm32_eval_sdio_sd.h的修改和添加

//宏定义
//SDIO_FIOF地址=SDIO地址+0x80至SDIO地址+0xfc
#define SDIO_FIFO_ADDRESS ((uint32_t) 0x40018080)

//SDIO初始化时钟频率,最大400kHz
#define SDIO_INIT_CLK_DIV ((uint8_t) 0xB2)

//数据传输时钟频率,最大25MHz
#define SDIO_TRANSFER_CLK_DIV ((uint8_t) 0x02)//数据读写速率

//SDIO传输使用的模式,可选DMA模式或者普通模式
#define SD_DMA_MODE ((uint32_t) 0x00000000)
//#define SD_POLLING_MODE ((uint32_t) 0x00000002)

//SD卡检测GPIO引脚
#define SD_DETECT_GPIO_PORT GPIOC
#define SD_DETECT_PIN GPIO_Pin_13 

//GPIO初始化,开启时钟
static void GPIO_Configuration()
{
	GPIO_InitTypeDef GPIO_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD, ENABLE);//开启时钟
	
	//配置PC8/9/10/11/12引脚(D0/1/2/3和CLK)
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//设置复用模式
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOC, &GPIO_InitStructure);
	
	//配置PD2为CMD引脚	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
	GPIO_Init(GPIOD, &GPIO_InitStructure);
	
	//使能SDIO AHB时钟
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_SDIO, ENABLE);
	
	//使能DMA时钟
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE);
}
//DMA传输配置,接收数据
void SD_DMA_RxConfig(uint32_t* BufferDST, uint32_t BufferSize)
{
	DMA_InitTypeDef DMA_InitStructure;
	
	//清除DMA标志位
	DMA_ClearFlag(DMA2_FLAG_TC4 | DMA2_FLAG_TE4 | DMA2_FLAG_HT4 | DMA2_FLAG_GL4);
	
	//配置前禁止DMA
	DMA_Cmd(DMA2_Channel4, DISABLE);
	
	//DMA传输配置
	//外设地址
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)SDIO_FIFO_ADDRESS;
	//目标地址
	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)BufferDST;
	//外设为源地址
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
	//除以4,把字转换成字节单位
	DMA_InitStructure.DMA_BufferSize = BufferSize / 4;
	//外设地址不增
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
	//存储目标地址
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
	//外设数据大小为字
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
	//存储数据大小
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
	//不循环,一次
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
	//通道优先级为高
	DMA_InitStructure.DMA_Priority = DMA_Priority_High;
	//非存储器至存储器模式
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
	
	//初始化DMA
	DMA_Init(DMA2_Channel4, &DMA_InitStructure);
	
	//使能DMA通道
	DMA_Cmd(DMA2_Channel4, ENABLE);
}
//为SDIO发送数据配置DMA2的通道2的请求
void SD_DMA_TxConfig(uint32_t *BufferSRC, uint32_t BufferSize)
{
	DMA_InitTypeDef DMA_InitStructure;
	
	//清除DMA标志位
	DMA_ClearFlag(DMA2_FLAG_TC4 | DMA2_FLAG_TE4 | DMA2_FLAG_HT4 | DMA2_FLAG_GL4);
	
	//配置前禁止DMA
	DMA_Cmd(DMA2_Channel4, DISABLE);
	
	//DMA传输配置
	//外设地址
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)SDIO_FIFO_ADDRESS;
	//存储地址
	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)BufferSRC;
	//外设为目的地址
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
	//除以4,把字转换成字节单位
	DMA_InitStructure.DMA_BufferSize = BufferSize / 4;
	//外设地址不增
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
	//存储目标地址
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
	//外设数据大小为字
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
	//存储数据大小
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
	//不循环,一次
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
	//通道优先级为高
	DMA_InitStructure.DMA_Priority = DMA_Priority_High;
	//非存储器至存储器模式
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
	
	//初始化DMA
	DMA_Init(DMA2_Channel4, &DMA_InitStructure);
	
	//使能DMA通道
	DMA_Cmd(DMA2_Channel4, ENABLE);
}
//配置中断控制器
void SDIO_NVIC_Configuration()
{
	NVIC_InitTypeDef NVIC_InitStructure;
	
	//优先级组选择
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
	
	//选择USART的中断源
	NVIC_InitStructure.NVIC_IRQChannel = SDIO_IRQn;
	//配置抢占优先级
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
	//配置子优先级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
	//使能中断
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	
	//初始化配置
	NVIC_Init(&NVIC_InitStructure);	
}
2.2 SDIO初始化

/**
  * @brief  Initializes the SD Card and put it into StandBy State (Ready for data 
  *         transfer).
  * @param  None
  * @retval SD_Error: SD Card Error code.
  */
SD_Error SD_Init(void)
{
  SD_Error errorstatus = SD_OK;
  //配置SDIO中断优先级
  SDIO_NVIC_Configuration();
  //SDIO外设底层引脚初始化
  GPIO_Configuration();
  //对SDIO所有的寄存器进行复位
  SDIO_DeInit();
  //上电,并进行卡识别流程,确认卡的操作电压
  errorstatus = SD_PowerON();
  //识别不成功
  if (errorstatus != SD_OK)
  {
    /*!< CMD Response TimeOut (wait for CMDSENT flag) */
    return(errorstatus);
  }
  //识别成功,对SD卡进行初始化
  errorstatus = SD_InitializeCards();
  //初始化失败
  if (errorstatus != SD_OK)
  {
    /*!< CMD Response TimeOut (wait for CMDSENT flag) */
    return(errorstatus);
  }

  //进入数据传输模式,提高读写速度
  /*!< Configure the SDIO peripheral */
  /*!< SDIOCLK = HCLK, SDIO_CK = HCLK/(2 + SDIO_TRANSFER_CLK_DIV) */
  /*!< on STM32F2xx devices, SDIOCLK is fixed to 48MHz */  
  SDIO_InitStructure.SDIO_ClockDiv = SDIO_TRANSFER_CLK_DIV; 
  SDIO_InitStructure.SDIO_ClockEdge = SDIO_ClockEdge_Rising;
  SDIO_InitStructure.SDIO_ClockBypass = SDIO_ClockBypass_Disable;
  SDIO_InitStructure.SDIO_ClockPowerSave = SDIO_ClockPowerSave_Disable;
  SDIO_InitStructure.SDIO_BusWide = SDIO_BusWide_1b;
  SDIO_InitStructure.SDIO_HardwareFlowControl = SDIO_HardwareFlowControl_Disable;
  SDIO_Init(&SDIO_InitStructure);
  
  if (errorstatus == SD_OK)
  {
    /*----------------- Read CSD/CID MSD registers ------------------*/
    errorstatus = SD_GetCardInfo(&SDCardInfo);
  }
  if (errorstatus == SD_OK)
  {
    /*----------------- Select Card --------------------------------*/
    errorstatus = SD_SelectDeselect((uint32_t) (SDCardInfo.RCA << 16));
  }
  if (errorstatus == SD_OK)
  {
    //为了提高读写,开启四位模式
    errorstatus = SD_EnableWideBusOperation(SDIO_BusWide_4b);
  }  
  return(errorstatus);
}
与之相对的还有一个SD_DeInit()函数,复位寄存器。

void SD_LowLevel_DeInit(void)
{
  GPIO_InitTypeDef  GPIO_InitStructure;
  
  /*!< Disable SDIO Clock */
  SDIO_ClockCmd(DISABLE);
  
  /*!< Set Power State to OFF */
  SDIO_SetPowerState(SDIO_PowerState_OFF);

  /*!< DeInitializes the SDIO peripheral */
  SDIO_DeInit();
  
  /*!< Disable the SDIO AHB Clock */
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_SDIO, DISABLE);

  /*!< Configure PC.08, PC.09, PC.10, PC.11, PC.12 pin: D0, D1, D2, D3, CLK pin */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  GPIO_Init(GPIOC, &GPIO_InitStructure);

  /*!< Configure PD.02 CMD line */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
  GPIO_Init(GPIOD, &GPIO_InitStructure);
}
//对SDIO所有的寄存器进行复位
void SD_DeInit(void)
{ 
  SD_LowLevel_DeInit();
}
SD_PowerOn()函数用于查询卡的工作电压和时钟控制配置,没有修改。可以看一下。
/**
  * @brief  Enquires cards about their operating voltage and configures 
  *   clock controls.
  * @param  None
  * @retval SD_Error: SD Card Error code.
  */
SD_Error SD_PowerON(void)
{
  SD_Error errorstatus = SD_OK;
  uint32_t response = 0, count = 0, validvoltage = 0;
  uint32_t SDType = SD_STD_CAPACITY;

  /*!< Power ON Sequence -----------------------------------------------------*/
  /*!< Configure the SDIO peripheral */
  /*!< SDIOCLK = HCLK, SDIO_CK = HCLK/(2 + SDIO_INIT_CLK_DIV) */
  /*!< on STM32F2xx devices, SDIOCLK is fixed to 48MHz */
  /*!< SDIO_CK for initialization should not exceed 400 KHz */  
  SDIO_InitStructure.SDIO_ClockDiv = SDIO_INIT_CLK_DIV;
  SDIO_InitStructure.SDIO_ClockEdge = SDIO_ClockEdge_Rising;
  SDIO_InitStructure.SDIO_ClockBypass = SDIO_ClockBypass_Disable;
  SDIO_InitStructure.SDIO_ClockPowerSave = SDIO_ClockPowerSave_Disable;
  SDIO_InitStructure.SDIO_BusWide = SDIO_BusWide_1b;
  SDIO_InitStructure.SDIO_HardwareFlowControl = SDIO_HardwareFlowControl_Disable;
  SDIO_Init(&SDIO_InitStructure);

  /*!< Set Power State to ON */
  SDIO_SetPowerState(SDIO_PowerState_ON);

  /*!< Enable SDIO Clock */
  SDIO_ClockCmd(ENABLE);

  /*!< CMD0: 复位所有的SD卡,使其进入空闲状态 ---------------------------------------------------*/
  /*!< No CMD response required */
  SDIO_CmdInitStructure.SDIO_Argument = 0x0;
  SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_GO_IDLE_STATE;
  SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_No;
  SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;//关闭等待中断
  SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;//CPSM在发送数据之前等待数据传输结束
  SDIO_SendCommand(&SDIO_CmdInitStructure);

  //检测是否正确接收到Cmd0
  errorstatus = CmdError();

  if (errorstatus != SD_OK)
  {
    /*!< CMD Response TimeOut (wait for CMDSENT flag) */
    return(errorstatus);
  }

  /*!< CMD8: SEND_IF_COND ----------------------------------------------------*/
  /*!< Send CMD8 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) */
  /*!< CMD Response: R7 */
  SDIO_CmdInitStructure.SDIO_Argument = SD_CHECK_PATTERN;
  SDIO_CmdInitStructure.SDIO_CmdIndex = SDIO_SEND_IF_COND;
  SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
  SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
  SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
  SDIO_SendCommand(&SDIO_CmdInitStructure);

  //检查是否接收到该命令
  errorstatus = CmdResp7Error();

  if (errorstatus == SD_OK)
  {
    CardType = SDIO_STD_CAPACITY_SD_CARD_V2_0; /*!< SD Card 2.0 */
    SDType = SD_HIGH_CAPACITY;
  }
  else
  {
    //有相应就是SD CARD 2.0,此时无响应,代表是1.x或MMC
    /*!< CMD55 */
    SDIO_CmdInitStructure.SDIO_Argument = 0x00;
    SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_APP_CMD;
    SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
    SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
    SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
    SDIO_SendCommand(&SDIO_CmdInitStructure);
    errorstatus = CmdResp1Error(SD_CMD_APP_CMD);
  }
  //检查是SD卡,还是MMC卡,或者是不支持的卡
  /*!< CMD55 */
  SDIO_CmdInitStructure.SDIO_Argument = 0x00;
  SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_APP_CMD;
  SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
  SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
  SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
  SDIO_SendCommand(&SDIO_CmdInitStructure);
  errorstatus = CmdResp1Error(SD_CMD_APP_CMD);

  /*!< If errorstatus is Command TimeOut, it is a MMC card */
  /*!< If errorstatus is SD_OK it is a SD card: SD card 2.0 (voltage range mismatch)
     or SD card 1.x */
  if (errorstatus == SD_OK)
  {
    /*!< SD CARD */
    /*!< Send ACMD41 SD_APP_OP_COND with Argument 0x80100000 */
    while ((!validvoltage) && (count < SD_MAX_VOLT_TRIAL))
    {

      /*!< SEND CMD55 APP_CMD with RCA as 0 */
      SDIO_CmdInitStructure.SDIO_Argument = 0x00;
      SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_APP_CMD;
      SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
      SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
      SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
      SDIO_SendCommand(&SDIO_CmdInitStructure);

      errorstatus = CmdResp1Error(SD_CMD_APP_CMD);

      if (errorstatus != SD_OK)
      {
        return(errorstatus);
      }
      SDIO_CmdInitStructure.SDIO_Argument = SD_VOLTAGE_WINDOW_SD | SDType;
      SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SD_APP_OP_COND;
      SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
      SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
      SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
      SDIO_SendCommand(&SDIO_CmdInitStructure);

      errorstatus = CmdResp3Error();
      if (errorstatus != SD_OK)
      {
        return(errorstatus);
      }

      response = SDIO_GetResponse(SDIO_RESP1);
      validvoltage = (((response >> 31) == 1) ? 1 : 0);
      count++;
    }
    if (count >= SD_MAX_VOLT_TRIAL)
    {
      errorstatus = SD_INVALID_VOLTRANGE;
      return(errorstatus);
    }

    if (response &= SD_HIGH_CAPACITY)
    {
      CardType = SDIO_HIGH_CAPACITY_SD_CARD;
    }

  }/*!< else MMC Card */

  return(errorstatus);
}
初始化所有的卡或者单个卡进入就绪状态,没有修改。

/**
  * @brief  Intialises all cards or single card as the case may be Card(s) come 
  *         into standby state.
  * @param  None
  * @retval SD_Error: SD Card Error code.
  */
SD_Error SD_InitializeCards(void)
{
  SD_Error errorstatus = SD_OK;
  uint16_t rca = 0x01;

  if (SDIO_GetPowerState() == SDIO_PowerState_OFF)
  {
    errorstatus = SD_REQUEST_NOT_APPLICABLE;
    return(errorstatus);
  }

  if (SDIO_SECURE_DIGITAL_IO_CARD != CardType)
  {
    /*!< Send CMD2 ALL_SEND_CID */
    SDIO_CmdInitStructure.SDIO_Argument = 0x0;
    SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_ALL_SEND_CID;
    SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Long;
    SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
    SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
    SDIO_SendCommand(&SDIO_CmdInitStructure);

    errorstatus = CmdResp2Error();

    if (SD_OK != errorstatus)
    {
      return(errorstatus);
    }

    CID_Tab[0] = SDIO_GetResponse(SDIO_RESP1);
    CID_Tab[1] = SDIO_GetResponse(SDIO_RESP2);
    CID_Tab[2] = SDIO_GetResponse(SDIO_RESP3);
    CID_Tab[3] = SDIO_GetResponse(SDIO_RESP4);
  }
  if ((SDIO_STD_CAPACITY_SD_CARD_V1_1 == CardType) ||  (SDIO_STD_CAPACITY_SD_CARD_V2_0 == CardType) ||  (SDIO_SECURE_DIGITAL_IO_COMBO_CARD == CardType)
      ||  (SDIO_HIGH_CAPACITY_SD_CARD == CardType))
  {
    /*!< Send CMD3 SET_REL_ADDR with argument 0 */
    /*!< SD Card publishes its RCA. */
    SDIO_CmdInitStructure.SDIO_Argument = 0x00;
    SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SET_REL_ADDR;
    SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
    SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
    SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
    SDIO_SendCommand(&SDIO_CmdInitStructure);

    errorstatus = CmdResp6Error(SD_CMD_SET_REL_ADDR, &rca);

    if (SD_OK != errorstatus)
    {
      return(errorstatus);
    }
  }

  if (SDIO_SECURE_DIGITAL_IO_CARD != CardType)
  {
    RCA = rca;

    /*!< Send CMD9 SEND_CSD with argument as card's RCA */
    SDIO_CmdInitStructure.SDIO_Argument = (uint32_t)(rca << 16);
    SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SEND_CSD;
    SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Long;
    SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
    SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
    SDIO_SendCommand(&SDIO_CmdInitStructure);

    errorstatus = CmdResp2Error();

    if (SD_OK != errorstatus)
    {
      return(errorstatus);
    }

    CSD_Tab[0] = SDIO_GetResponse(SDIO_RESP1);
    CSD_Tab[1] = SDIO_GetResponse(SDIO_RESP2);
    CSD_Tab[2] = SDIO_GetResponse(SDIO_RESP3);
    CSD_Tab[3] = SDIO_GetResponse(SDIO_RESP4);
  }

  errorstatus = SD_OK; /*!< All cards get intialized */

  return(errorstatus);
}
2.3 数据操作

SD卡数据擦除操作,基本没有修改。

/**
  * @brief  Allows to erase memory area specified for the given card.
  * @param  startaddr: the start address.
  * @param  endaddr: the end address.
  * @retval SD_Error: SD Card Error code.
  */
SD_Error SD_Erase(uint32_t startaddr, uint32_t endaddr)
{
  SD_Error errorstatus = SD_OK;
  uint32_t delay = 0;
  __IO uint32_t maxdelay = 0;
  uint8_t cardstate = 0;

  /*!< Check if the card coomnd class supports erase command */
  if (((CSD_Tab[1] >> 20) & SD_CCCC_ERASE) == 0)
  {
    errorstatus = SD_REQUEST_NOT_APPLICABLE;
    return(errorstatus);
  }

  //延时,根据时钟分频设置来计算
  maxdelay = 120000 / ((SDIO->CLKCR & 0xFF) + 2);

  if (SDIO_GetResponse(SDIO_RESP1) & SD_CARD_LOCKED)
  {
    errorstatus = SD_LOCK_UNLOCK_FAILED;
    return(errorstatus);
  }
  if (CardType == SDIO_HIGH_CAPACITY_SD_CARD)
  {
    startaddr /= 512;
    endaddr /= 512;
  }
  /*!< According to sd-card spec 1.0 ERASE_GROUP_START (CMD32) and erase_group_end(CMD33) */
  if ((SDIO_STD_CAPACITY_SD_CARD_V1_1 == CardType) || (SDIO_STD_CAPACITY_SD_CARD_V2_0 == CardType) || (SDIO_HIGH_CAPACITY_SD_CARD == CardType))
  {
    /*!< Send CMD32 SD_ERASE_GRP_START with argument as addr  */
    SDIO_CmdInitStructure.SDIO_Argument = startaddr;
    SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SD_ERASE_GRP_START;
    SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
    SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
    SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
    SDIO_SendCommand(&SDIO_CmdInitStructure);

    errorstatus = CmdResp1Error(SD_CMD_SD_ERASE_GRP_START);
    if (errorstatus != SD_OK)
    {
      return(errorstatus);
    }

    /*!< Send CMD33 SD_ERASE_GRP_END with argument as addr  */
    SDIO_CmdInitStructure.SDIO_Argument = endaddr;
    SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SD_ERASE_GRP_END;
    SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
    SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
    SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
    SDIO_SendCommand(&SDIO_CmdInitStructure);

    errorstatus = CmdResp1Error(SD_CMD_SD_ERASE_GRP_END);
    if (errorstatus != SD_OK)
    {
      return(errorstatus);
    }
  }

  /*!< Send CMD38 ERASE */
  SDIO_CmdInitStructure.SDIO_Argument = 0;
  SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_ERASE;
  SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
  SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
  SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
  SDIO_SendCommand(&SDIO_CmdInitStructure);

  errorstatus = CmdResp1Error(SD_CMD_ERASE);

  if (errorstatus != SD_OK)
  {
    return(errorstatus);
  }

  for (delay = 0; delay < maxdelay; delay++)
  {}

  /*!< Wait till the card is in programming state */
  errorstatus = IsCardProgramming(&cardstate);

  while ((errorstatus == SD_OK) && ((SD_CARD_PROGRAMMING == cardstate) || (SD_CARD_RECEIVING == cardstate)))
  {
    errorstatus = IsCardProgramming(&cardstate);
  }

  return(errorstatus);
}
数据写入操作,需要修改,关键,否则可能出错。

/**
  * @brief  Allows to write one block starting from a specified address in a card.
  *         The Data transfer can be managed by DMA mode or Polling mode.
  * @note   This operation should be followed by two functions to check if the 
  *         DMA Controller and SD Card status.
  *          - SD_ReadWaitOperation(): this function insure that the DMA
  *            controller has finished all data transfer.
  *          - SD_GetStatus(): to check that the SD Card has finished the 
  *            data transfer and it is ready for data.      
  * @param  writebuff: pointer to the buffer that contain the data to be transferred.
  * @param  WriteAddr: Address from where data are to be read.   
  * @param  BlockSize: the SD card Data block size. The Block size should be 512.
  * @retval SD_Error: SD Card Error code.
  */
SD_Error SD_WriteBlock(uint8_t *writebuff, uint32_t WriteAddr, uint16_t BlockSize)
{
  SD_Error errorstatus = SD_OK;

  TransferError = SD_OK;
  TransferEnd = 0;
  StopCondition = 0;
  
	//复位SDIO配置
  SDIO->DCTRL = 0x0;

  if (CardType == SDIO_HIGH_CAPACITY_SD_CARD)
  {
    BlockSize = 512;
    WriteAddr /= 512;
  }
	
	//----------------------在库的基础上添加的,没有此段将容易卡死在DMA检测中--------------------------
  /*!< Send CMD16 设置块大小 */
  SDIO_CmdInitStructure.SDIO_Argument = BlockSize;
  SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SET_BLOCKLEN;
  SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
  SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
  SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
  SDIO_SendCommand(&SDIO_CmdInitStructure);
	
	errorstatus = CmdResp1Error(SD_CMD_SET_BLOCKLEN);

  if (errorstatus != SD_OK)
  {
    return(errorstatus);
  }
	//----------------------------------------------------------------------------------------------
	
  
  /*!< Send CMD24 WRITE_SINGLE_BLOCK */
  SDIO_CmdInitStructure.SDIO_Argument = WriteAddr;
  SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_WRITE_SINGLE_BLOCK;
  SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
  SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
  SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
  SDIO_SendCommand(&SDIO_CmdInitStructure);

  errorstatus = CmdResp1Error(SD_CMD_WRITE_SINGLE_BLOCK);

  if (errorstatus != SD_OK)
  {
    return(errorstatus);
  }

	//配置SDIO写数据寄存器
  SDIO_DataInitStructure.SDIO_DataTimeOut = SD_DATATIMEOUT;
  SDIO_DataInitStructure.SDIO_DataLength = BlockSize;
  SDIO_DataInitStructure.SDIO_DataBlockSize = (uint32_t) 9 << 4;
  SDIO_DataInitStructure.SDIO_TransferDir = SDIO_TransferDir_ToCard;
  SDIO_DataInitStructure.SDIO_TransferMode = SDIO_TransferMode_Block;
  SDIO_DataInitStructure.SDIO_DPSM = SDIO_DPSM_Enable;//开启数据通道状态机
  SDIO_DataConfig(&SDIO_DataInitStructure);

  SDIO_ITConfig(SDIO_IT_DATAEND, ENABLE);//开启数据传输结束中断
	
  SD_DMA_TxConfig((uint32_t *)writebuff, BlockSize);//配置DMA,跟Rx类似
  SDIO_DMACmd(ENABLE);//使能SDIO的DMA请求

  return(errorstatus);
}
/**
  * @brief  This function waits until the SDIO DMA data transfer is finished. 写操作等待函数
  *         This function should be called after SDIO_WriteBlock() and
  *         SDIO_WriteMultiBlocks() function to insure that all data sent by the 
  *         card are already transferred by the DMA controller.        
  * @param  None.
  * @retval SD_Error: SD Card Error code.
  */
SD_Error SD_WaitWriteOperation(void)
{
  SD_Error errorstatus = SD_OK;
  while ((SD_DMAEndOfTransferStatus() == RESET) && (TransferEnd == 0) && (TransferError == SD_OK))
  {}
  if (TransferError != SD_OK)
  {
    return(TransferError);
  }
  /*!< Clear all the static flags */
  SDIO_ClearFlag(SDIO_STATIC_FLAGS);
  return(errorstatus);
}
数据读取操作,需要修改,关键,否则可能出错。
/**
  * @brief  Allows to read one block from a specified address in a card. The Data 
  *         transfer can be managed by DMA mode or Polling mode. 
  * @note   This operation should be followed by two functions to check if the 
  *         DMA Controller and SD Card status.
  *          - SD_ReadWaitOperation(): this function insure that the DMA
  *            controller has finished all data transfer.
  *          - SD_GetStatus(): to check that the SD Card has finished the 
  *            data transfer and it is ready for data.            
  * @param  readbuff: pointer to the buffer that will contain the received data
  * @param  ReadAddr: Address from where data are to be read.  
  * @param  BlockSize: the SD card Data block size. The Block size should be 512.
  * @retval SD_Error: SD Card Error code.
  */
SD_Error SD_ReadBlock(uint8_t *readbuff, uint32_t ReadAddr, uint16_t BlockSize)
{
  SD_Error errorstatus = SD_OK;

  TransferError = SD_OK;
  TransferEnd = 0;//清除传输结束标志位,在中断服务中置一
  StopCondition = 0;
  
  SDIO->DCTRL = 0x0;
  
  if (CardType == SDIO_HIGH_CAPACITY_SD_CARD)
  {
    BlockSize = 512;
    ReadAddr /= 512;
  }
  //----------------------在库的基础上添加的,没有此段将容易卡死在DMA检测中--------------------------
  /*!< Send CMD16 设置块大小 */
  SDIO_CmdInitStructure.SDIO_Argument = BlockSize;
  SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SET_BLOCKLEN;
  SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
  SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
  SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
  SDIO_SendCommand(&SDIO_CmdInitStructure);
	
  errorstatus = CmdResp1Error(SD_CMD_SET_BLOCKLEN);

  if (errorstatus != SD_OK)
  {
    return(errorstatus);
  }
  //----------------------------------------------------------------------------------------------
	
  SDIO_DataInitStructure.SDIO_DataTimeOut = SD_DATATIMEOUT;
  SDIO_DataInitStructure.SDIO_DataLength = BlockSize;
  SDIO_DataInitStructure.SDIO_DataBlockSize = (uint32_t) 9 << 4;
  SDIO_DataInitStructure.SDIO_TransferDir = SDIO_TransferDir_ToSDIO;
  SDIO_DataInitStructure.SDIO_TransferMode = SDIO_TransferMode_Block;
  SDIO_DataInitStructure.SDIO_DPSM = SDIO_DPSM_Enable;
  SDIO_DataConfig(&SDIO_DataInitStructure);

  /*!< Send CMD17 READ_SINGLE_BLOCK */
  SDIO_CmdInitStructure.SDIO_Argument = (uint32_t)ReadAddr;
  SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_READ_SINGLE_BLOCK;
  SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
  SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
  SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
  SDIO_SendCommand(&SDIO_CmdInitStructure);

  errorstatus = CmdResp1Error(SD_CMD_READ_SINGLE_BLOCK);

  if (errorstatus != SD_OK)
  {
    return(errorstatus);
  }
  SDIO_ITConfig(SDIO_IT_DATAEND, ENABLE);
  SDIO_DMACmd(ENABLE);
  SD_DMA_RxConfig((uint32_t *)readbuff, BlockSize);
  return(errorstatus);
}
/**
  * @brief  This function waits until the SDIO DMA data transfer is finished. 
  *         This function should be called after SDIO_ReadMultiBlocks() function
  *         to insure that all data sent by the card are already transferred by 
  *         the DMA controller.        
  * @param  None.
  * @retval SD_Error: SD Card Error code.
  */
SD_Error SD_WaitReadOperation(void)
{
  SD_Error errorstatus = SD_OK;

  while ((SD_DMAEndOfTransferStatus() == RESET) && (TransferEnd == 0) && (TransferError == SD_OK))
  {}
  if (TransferError != SD_OK)
  {
    return(TransferError);
  }
  return(errorstatus);
}
2.4 中断服务函数,此处需要提醒的是,这个中断处理函数的位置推荐也放在 bsp_sdio_sdcard.c文件中,因为这样的话可以防止忘记,Keil会自动地根据它的名称识别出它是一个中断函数,无论该函数位于哪个文件中,这个请放心。

//SDIO中断服务函数
void SDIO_IRQHandler()
{
	//中断相关的处理
	SD_ProcessIRQSrc();
}
/**
  * @brief  Allows to process all the interrupts that are high. 由中断服务程序调用,负责中断相关的处理
  * @param  None
  * @retval SD_Error: SD Card Error code.
  */
SD_Error SD_ProcessIRQSrc(void)
{
  if (StopCondition == 1)//发送读取/多块读写命令时置一
  {
    SDIO->ARG = 0x0;//命令参数寄存器
    SDIO->CMD = 0x44C;//命令寄存器
    TransferError = CmdResp1Error(SD_CMD_STOP_TRANSMISSION);//开启命令状态机短响应
  }
  else
  {
    TransferError = SD_OK;
  }
  SDIO_ClearITPendingBit(SDIO_IT_DATAEND);//清中断
  SDIO_ITConfig(SDIO_IT_DATAEND, DISABLE);//关闭SDIO中断使能
  TransferEnd = 1;
  return(TransferError);
}
2.5 测试函数

#include "sdio_test.h"
#include "stdio.h"
#include "bsp_sdio_sdcard.h"
#include "stm32f10x.h"

#define BLOCK_SIZE 512
#define NUMBER_OF_BLOCKS 32
#define MULTI_BUFFER_SIZE (BLOCK_SIZE * NUMBER_OF_BLOCKS)//缓冲区大小

uint8_t Buffer_Block_Tx[BLOCK_SIZE];
uint8_t Buffer_Block_Rx[BLOCK_SIZE];
uint8_t Buffer_MultiBlock_Tx[MULTI_BUFFER_SIZE];
uint8_t Buffer_MultiBlock_Rx[MULTI_BUFFER_SIZE];
SD_Error Status;

//测试状态
typedef enum
{
	FAILED=0,
	PASSED=!FAILED
} TestStatus;

volatile TestStatus EraseStatus = FAILED, TransferStatus1 = FAILED, TransferStatus2 = FAILED;
//声明外部变量
extern SD_CardInfo SDCardInfo;

//是否擦除成功
TestStatus eBuffercmp(uint8_t* pBuffer,uint32_t BufferLength)
{
	while(BufferLength--)
	{
		if((*pBuffer!=0xFF) && (*pBuffer!=0x00))//擦除后是0xff或0x00
		{
			return FAILED;
		}
		pBuffer++;
	}
	return PASSED;
}
/*
*函数名:Buffercmp
*描述:比较两个缓冲区中的数据是否相等
*输入:-pBuffer1,-pBuffer2:要比较的缓冲区的指针*-BufferLength缓冲区长度*输出:-PASSED相等*-FAILED不等*/
TestStatus Buffercmp(uint8_t* pBuffer1,uint8_t* pBuffer2,uint32_t BufferLength)
{
	while(BufferLength--)
	{
		if(*pBuffer1 != *pBuffer2)
		{
			return FAILED;
		}
		pBuffer1++;
		pBuffer2++;
	}
	return PASSED;
}

/*
*函数名:Fill_Buffer
*描述:在缓冲区中填写数据
*输入:-pBuffer要填充的缓冲区
*		-BufferLength要填充的大小
*		-Offset填在缓冲区的第一个值
*输出:无
*/
void Fill_Buffer(uint8_t* pBuffer,uint32_t BufferLength,uint32_t Offset)
{
	uint16_t index=0;
	/*Putin global buffer some values*/
	for(index=0;index<BufferLength;index++)
	{
		pBuffer[index] = index + Offset;
	}
}

/*
*函数名:SD_EraseTest
*描述:擦除数据测试
*输入:无
*输出:无
*/
void SD_EraseTest(void)
{
	/*-------------------BlockErase------------------------------------------*/
	if(Status==SD_OK)
	{
		Status=SD_Erase(0x00,(BLOCK_SIZE*NUMBER_OF_BLOCKS));//第一个参数为擦除起始地址,第二个参数为擦除结束地址
	}
	if(Status==SD_OK)
	{
		//读取刚刚擦除的区域
		Status=SD_ReadMultiBlocks(Buffer_MultiBlock_Rx,0x00,BLOCK_SIZE,NUMBER_OF_BLOCKS);
		/*Check if the Transfer is finished*/
		Status=SD_WaitReadOperation();//循环查询dma传输是否结束
		/*Wait until end of DMA transfer*/
		while(SD_GetStatus()!=SD_TRANSFER_OK);
	}
	/*Check the correctness of erased blocks*/
	if(Status==SD_OK)
	{
		//把擦除区域读出来对比
		EraseStatus=eBuffercmp(Buffer_MultiBlock_Rx,MULTI_BUFFER_SIZE);
	}
	if(EraseStatus==PASSED)
	{
		printf("擦除测试成功!\r\n");
	}
	else
	{
		printf("擦除测试失败!\r\n");
	}
}

//单块读写测试
void SD_SingleBlockTest()
{
	//向数组填充要写入的数据
	Fill_Buffer(Buffer_Block_Tx, BLOCK_SIZE, 0x320F);
	
	if(Status == SD_OK)
	{
		//将BLOCK_SIZE个字节写入SD卡的0地址
		Status = SD_WriteBlock(Buffer_Block_Tx, 0x00, BLOCK_SIZE);
		//检查传输
		Status = SD_WaitWriteOperation();
		while(SD_GetStatus() != SD_TRANSFER_OK);
	}
	
	if(Status == SD_OK)
	{
		//从SD卡的0地址读取512个字节的数据
		Status = SD_ReadBlock(Buffer_Block_Rx, 0x00, BLOCK_SIZE);
		//检查传输
		Status = SD_WaitWriteOperation();
		while(SD_GetStatus() != SD_TRANSFER_OK);
	}
	
	//校验数据是否一致
	if(Status == SD_OK)
	{
		TransferStatus1 = Buffercmp(Buffer_Block_Tx, Buffer_Block_Rx, BLOCK_SIZE);
	}
	
	if(TransferStatus1 == PASSED)
	{
		printf("单块读写测试成功!\r\n");
	}
	else
	{
		printf("单块读写测试失败!\r\n");
	}
}


//SD卡测试函数
void SD_Test()
{
	//SD卡使用SDIO中断及DMA中断接收数据,中断服务程序位于"bsp_sdio_sdcard.c"中
	Status = SD_Init();
	
	if(Status != SD_OK)
	{
		printf("SD卡初始化失败!请确保SD卡插在开发板上,或者换一张SD卡测试!\r\n");
	}
	else
	{
		printf("SD卡初始化成功!\r\n");
		
		printf("SD_cid:%d \r\n", (SDCardInfo.SD_cid.ManufacturerID));
		printf("CardBlockSize:%d \r\n", SDCardInfo.CardBlockSize);
		printf("CardCapacity:%d \r\n", SDCardInfo.CardCapacity);
	}
	
	if(Status == SD_OK)
	{
		//擦除测试
		SD_EraseTest();
		
		//单块读写测试
		SD_SingleBlockTest();
	}
}
2 .6 重要提醒

因为检测SD卡是否正确插入的宏SD_PRESENT和SD_NOT_PRESENT是ST官方的原SD驱动程序以一个输入引脚电平判断SD卡是否插入的,但是我们硬件中没有使用到该引脚,所以我们应该把ST驱动中原来用来进行引脚检测部分的代码删除掉,但代码中,我们还是保留了宏SD_PRESENT和SD_NOT_PRESENT的定义。

用于代码检测的具体位置:

修改方案:

其实就是不做检查,利用初始化是否成功来代替检查过程,就可以了。


删除步骤:

/**
 * @brief  Detect if SD card is correctly plugged in the memory slot.
 * @param  None
 * @retval Return if SD is detected or not
 */
uint8_t SD_Detect(void)
{
  __IO uint8_t status = SD_PRESENT;

  /*!< Check GPIO to detect SD */
  if (GPIO_ReadInputDataBit(SD_DETECT_GPIO_PORT, SD_DETECT_PIN) != Bit_RESET)
  {
    status = SD_NOT_PRESENT;
  }
  return status;
}

//来自于测试文件中的外部全局变量
extern SD_Error Status;

/**
  * @brief  Returns the current card's state.
  * @param  None
  * @retval SDCardState: SD Card Error or SD Card Current State.
  */
SDCardState SD_GetState(void)
{
  uint32_t resp1 = 0;
  //原SD卡驱动程序以一个输入引脚的电平判断SD卡是否正确插入,由于我们硬件中没有使用该引脚,所以我们的程序需要在这里屏蔽掉SD驱动中原来引脚电平检测部分的代码
  //if(SD_Detect()== SD_PRESENT)
	if(Status == SD_OK)
  {
    if (SD_SendStatus(&resp1) != SD_OK)
    {
      return SD_CARD_ERROR;
    }
    else
    {
      return (SDCardState)((resp1 >> 9) & 0x0F);
    }
  }
  else
  {
    return SD_CARD_ERROR;
  }
}
你可能会缺少的代码:
/**
  * @brief  Gets the cuurent sd card data transfer status.
  * @param  None
  * @retval SDTransferState: Data Transfer state.
  *   This value can be: 
  *        - SD_TRANSFER_OK: No data transfer is acting
  *        - SD_TRANSFER_BUSY: Data transfer is acting
  */
SDTransferState SD_GetStatus(void)
{
  SDCardState cardstate =  SD_CARD_TRANSFER;

  cardstate = SD_GetState();
  
  if (cardstate == SD_CARD_TRANSFER)
  {
    return(SD_TRANSFER_OK);
  }
  else if(cardstate == SD_CARD_ERROR)
  {
    return (SD_TRANSFER_ERROR);
  }
  else
  {
    return(SD_TRANSFER_BUSY);
  }
}
就是这些,下一节将介绍向SD卡移植FatFs。

猜你喜欢

转载自blog.csdn.net/zyxhangiian123456789/article/details/79096687