【STM32F429】第3章 ThreadX FileX移植SDIO接口SD的基础知识

 论坛原始地址(持续更新):http://www.armbbs.cn/forum.php?mod=viewthread&tid=100749

第3章   ThreadX FileX移植SDIO接口SD的基础知识

本章节为大家讲解SDMMC(Secure digital input/output MultiMediaCard interface)总线的基础知识和对应的HAL库API。为下个章节SD卡的移植做准备。

3.1 初学者重要提示

3.2 SDMMC总线基础知识

3.3 SDMMC总线的HAL库用法

3.4 SDMMC总线源文件stm32f4xx_hal_sd.c

3.5 总结

3.1   初学者重要提示

  1.   对于SD控制SD卡或者MMC,掌握本章的知识点就够用了,更深入的认识可以看STM32F4的参考手册。
  2.   注意,操作SD卡是采用的函数HAL_SD_XXXX,而操作MMC是采用的函数HAL_MMC_XXXX,也就是说他们采用的函数前缀是不同的。
  3.   SD卡官网: www.sdcard.org
  4.   SDIO驱动MMC支持1线,4线和8线模式。
  5.   SDIO驱动SD卡支持1线和4线模式。

3.2   SDIO总线基础知识

3.2.1      SDIO总线的硬件框图

认识一个外设,最好的方式就是看它的框图,方便我们快速的了解SDIO的基本功能,然后再看手册了解细节。

 

通过这个框图,我们可以得到如下信息:

  •   APB2 interface(APB2 bus)

用于访问SDIO,并生成中断和DMA请求信号。

  •   PCLK2

用于给APB2 interface提供时钟。

  •   SDIOCLK

SDIO外设时钟,允许的最大值50MHz。

触发信号。

  •   SDIO_CMD

SD/SDIO/MMC卡双向/响应信号。

  •   SDIO_D[7:0]

SD/SDIO/MMC卡双向数据线。

  •   SDIO_CK

与SD/SDIO/MMC卡相连的时钟。对于MMC V3.31,时钟频率可以在 0 MHz 到 20 MHz 之间变化,对于 MMC V4.0/4.2,可以在 0到 48 MHz 之间变化,对于 SD/SD I/O 卡,可以在 0 到 25 MHz 之间变化(注:使用SD Class10测试,50MHz也可以)。

默认情况下, SDIO_D0 用于数据传输,SDIO时钟运行在400KHz。初始化后可以设置到SDIO支持的最高时钟,并且可以更改数据总线宽度。

3.2.2      SDIO时钟

SDIO最高时钟支持50MHz,并且SDIO时钟是和USB,RNG共用。由于USB要固定使用48MHz时钟,所以我们这里固定使用48MHz。

如果没有选择旁路分频器,SDIO外设固定做了2分频,所以实际驱动SD卡的时钟在0-25MHz。如果选择了旁路分频器,SDIO可以以最高的50MHz时钟驱动芯片。

 

STM32CubeMX对此处的注释:

 

3.2.3      SDIO硬件流控制

硬件流控制功能用于避免FIFO下溢(发送模式)和上溢(接收模式)错误。该功能可停止 SDIO_CK 并冻结 SDIO 状态机。数据传输将停止,而 FIFO 无法发送或接收数据。只有由 SDIOCLK 提供时钟的状态机才会冻结, APB2 接口仍保持活动状态。

3.2.4      SDIO使用DMA方式的4字节对齐问题

正常情况使用SDIO的DMA方式要注意数据发送和数据接收缓冲区的4字节对齐问题,也就是要保证数据发送首地址和数据接收首地址对4求余等于0。这里提供一个非常简单的处理办法,用户无需做发送和接收缓冲区的4字节对齐:   

  /* DMA接收配置 */
  dmaRxHandle.Init.Channel             = SD_DMAx_Rx_CHANNEL;
  dmaRxHandle.Init.Direction           = DMA_PERIPH_TO_MEMORY;
  dmaRxHandle.Init.PeriphInc           = DMA_PINC_DISABLE;
  dmaRxHandle.Init.MemInc              = DMA_MINC_ENABLE;
  dmaRxHandle.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
  dmaRxHandle.Init.MemDataAlignment    = DMA_MDATAALIGN_BYTE;
  dmaRxHandle.Init.Mode                = DMA_PFCTRL;
  dmaRxHandle.Init.Priority            = DMA_PRIORITY_VERY_HIGH;
  dmaRxHandle.Init.FIFOMode            = DMA_FIFOMODE_ENABLE;
  dmaRxHandle.Init.FIFOThreshold       = DMA_FIFO_THRESHOLD_FULL;
  dmaRxHandle.Init.MemBurst            = DMA_MBURST_SINGLE;
  dmaRxHandle.Init.PeriphBurst         = DMA_PBURST_SINGLE;
  
  /* */
  dmaTxHandle.Init.Channel             = SD_DMAx_Tx_CHANNEL;
  dmaTxHandle.Init.Direction           = DMA_MEMORY_TO_PERIPH;
  dmaTxHandle.Init.PeriphInc           = DMA_PINC_DISABLE;
  dmaTxHandle.Init.MemInc              = DMA_MINC_ENABLE;
  dmaTxHandle.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
  dmaTxHandle.Init.MemDataAlignment    = DMA_MDATAALIGN_BYTE;
  dmaTxHandle.Init.Mode                = DMA_PFCTRL;
  dmaTxHandle.Init.Priority            = DMA_PRIORITY_VERY_HIGH;
  dmaTxHandle.Init.FIFOMode            = DMA_FIFOMODE_ENABLE;
  dmaTxHandle.Init.FIFOThreshold       = DMA_FIFO_THRESHOLD_FULL;
  dmaTxHandle.Init.MemBurst            = DMA_MBURST_SINGLE;
  dmaTxHandle.Init.PeriphBurst         = DMA_PBURST_SINGLE;

使能发送和接收DMA配置的FIFO,并设置MemDataAligment对齐方式为BYTE即可解决。这样设置的原理是DMA传输的源地址和目的地址数据宽度不同时,需要开启FIFO,这样就很好的解决了DMA的4字节对齐问题。

3.3   SDIO总线的HAL库用法

3.3.1      SDIO总线结构体SD_TypeDef

SDMMC总线相关的寄存器是通过HAL库中的结构体SD_TypeDef定义,在stm32f4xx.h中可以找到这个类型定义:

#define SD_TypeDef          SDIO_TypeDef
typedef struct
{
  __IO uint32_t POWER;                 /*!< SDIO power control register,    Address offset: 0x00 */
  __IO uint32_t CLKCR;                 /*!< SDI clock control register,     Address offset: 0x04 */
  __IO uint32_t ARG;                   /*!< SDIO argument register,         Address offset: 0x08 */
  __IO uint32_t CMD;                   /*!< SDIO command register,          Address offset: 0x0C */
  __IO const uint32_t  RESPCMD;        /*!< SDIO command response register, Address offset: 0x10 */
  __IO const uint32_t  RESP1;          /*!< SDIO response 1 register,       Address offset: 0x14 */
  __IO const uint32_t  RESP2;          /*!< SDIO response 2 register,       Address offset: 0x18 */
  __IO const uint32_t  RESP3;          /*!< SDIO response 3 register,       Address offset: 0x1C */
  __IO const uint32_t  RESP4;          /*!< SDIO response 4 register,       Address offset: 0x20 */
  __IO uint32_t DTIMER;                /*!< SDIO data timer register,       Address offset: 0x24 */
  __IO uint32_t DLEN;                  /*!< SDIO data length register,      Address offset: 0x28 */
  __IO uint32_t DCTRL;                 /*!< SDIO data control register,     Address offset: 0x2C */
  __IO const uint32_t  DCOUNT;         /*!< SDIO data counter register,     Address offset: 0x30 */
  __IO const uint32_t  STA;            /*!< SDIO status register,           Address offset: 0x34 */
  __IO uint32_t ICR;                   /*!< SDIO interrupt clear register,  Address offset: 0x38 */
  __IO uint32_t MASK;                  /*!< SDIO mask register,             Address offset: 0x3C */
  uint32_t      RESERVED0[2];          /*!< Reserved, 0x40-0x44                                  */
  __IO const uint32_t  FIFOCNT;        /*!< SDIO FIFO counter register,     Address offset: 0x48 */
  uint32_t      RESERVED1[13];         /*!< Reserved, 0x4C-0x7C                                  */
  __IO uint32_t FIFO;                  /*!< SDIO data FIFO register,        Address offset: 0x80 */
} SDIO_TypeDef;

这个结构体的成员名称和排列次序和CPU的寄存器是一 一对应的。

__IO表示volatile, 这是标准C语言中的一个修饰字,表示这个变量是非易失性的,编译器不要将其优化掉。core_m4.h 文件定义了这个宏:

#define     __O     volatile             /*!< Defines 'write only' permissions */
#define     __IO    volatile             /*!< Defines 'read / write' permissions */

下面我们看下SDMMC的定义,在stm32f4xx.h文件。

#define PERIPH_BASE           0x40000000UL
#define APB2PERIPH_BASE       (PERIPH_BASE + 0x00010000UL)
#define SDIO_BASE             (APB2PERIPH_BASE + 0x2C00UL)

#define SDIO                ((SDIO_TypeDef *) SDIO_BASE) <----- 展开这个宏,(SDIO_TypeDef *)0x40012C00

我们访问SDIO的CMD寄存器可以采用这种形式:SDIO->CMD = 0。

3.3.2      SDIO总线初始化结构体SD_InitTypeDef

下面是SDIO总线的初始化结构体:

#define SD_InitTypeDef      SDIO_InitTypeDef
typedef struct
{
  uint32_t ClockEdge;           
  uint32_t ClockBypass;         
  uint32_t ClockPowerSave;      
  uint32_t BusWide;              
  uint32_t HardwareFlowControl; 
  uint32_t ClockDiv;            
}SDIO_InitTypeDef;

下面将结构体成员逐一做个说明:

  •   ClockEdge

用于设置SDIO的数据或者命令变化的时钟沿。

#define SDIO_CLOCK_EDGE_RISING               0x00000000U
#define SDIO_CLOCK_EDGE_FALLING              SDIO_CLKCR_NEGEDGE
  •   ClockBypass

用于设置是否旁路分频器。

#define SDIO_CLOCK_BYPASS_DISABLE             0x00000000U
#define SDIO_CLOCK_BYPASS_ENABLE              SDIO_CLKCR_BYPASS   
  •   ClockPowerSave

用于设置空闲状态,是否输出时钟。

#define SDIO_CLOCK_POWER_SAVE_DISABLE         0x00000000U
#define SDIO_CLOCK_POWER_SAVE_ENABLE          SDIO_CLKCR_PWRSAV
  •   BusWide

用于设置SDIO总线位宽。

#define SDIO_BUS_WIDE_1B                      0x00000000U
#define SDIO_BUS_WIDE_4B                      SDIO_CLKCR_WIDBUS_0
#define SDIO_BUS_WIDE_8B                      SDIO_CLKCR_WIDBUS_1
  •   HardwareFlowControl

用于设置时候使能硬件流控制。

#define SDMMC_HARDWARE_FLOW_CONTROL_DISABLE    ((uint32_t)0x00000000U)
#define SDMMC_HARDWARE_FLOW_CONTROL_ENABLE     SDMMC_CLKCR_HWFC_EN
#define SDIO_HARDWARE_FLOW_CONTROL_DISABLE    0x00000000U
#define SDIO_HARDWARE_FLOW_CONTROL_ENABLE     SDIO_CLKCR_HWFC_EN
  •   ClockDiv

用于设置SDIO时钟分频,参数范围0到255。

3.3.3      SDIO接SD卡信息结构体HAL_SD_CardInfoTypeDef

下面是SDIO总线的卡信息结构体:

typedef struct
{
  uint32_t CardType;                     /*!< Specifies the card Type                         */
  uint32_t CardVersion;                  /*!< Specifies the card version                      */
  uint32_t Class;                        /*!< Specifies the class of the card class           */
  uint32_t RelCardAdd;                   /*!< Specifies the Relative Card Address             */
  uint32_t BlockNbr;                     /*!< Specifies the Card Capacity in blocks           */
  uint32_t BlockSize;                    /*!< Specifies one block size in bytes               */
  uint32_t LogBlockNbr;                  /*!< Specifies the Card logical Capacity in blocks   */
  uint32_t LogBlockSize;                 /*!< Specifies logical block size in bytes           */
}HAL_SD_CardInfoTypeDef;

下面将结构体成员逐一做个说明:

  •   CardType

卡类型。

/*!< SD Standard Capacity <2Go                        */
#define CARD_SDSC                  ((uint32_t)0x00000000U) 
/*!< SD High Capacity <32Go, SD Extended Capacity <2To  */
#define CARD_SDHC_SDXC             ((uint32_t)0x00000001U)  
#define CARD_SECURED               ((uint32_t)0x00000003U)
  •   CardVersion

卡版本。

#define CARD_V1_X                  ((uint32_t)0x00000000U)
#define CARD_V2_X                  ((uint32_t)0x00000001U)
  •   Class

卡类型。

  •   RelCardAdd

卡相对地址。

  •   BlockNbr

整个卡的块数。

  •   BlockSize

每个块的字节数。

  •   LogBlockNbr

整个卡的逻辑块数。

  •   LogBlockSize

逻辑块大小

3.3.4      SDIO总线句柄结构体SD_HandleTypeDef

下面是SDIO句柄结构体:

typedef struct __SD_HandleTypeDef
{
  SD_TypeDef                   *Instance;        /*!< SD registers base address           */
  SD_InitTypeDef               Init;             /*!< SD required parameters              */
  HAL_LockTypeDef              Lock;             /*!< SD locking object                   */
  uint32_t                     *pTxBuffPtr;      /*!< Pointer to SD Tx transfer Buffer    */
  uint32_t                     TxXferSize;       /*!< SD Tx Transfer size                 */
  uint32_t                     *pRxBuffPtr;      /*!< Pointer to SD Rx transfer Buffer    */
  uint32_t                     RxXferSize;       /*!< SD Rx Transfer size                 */
  __IO uint32_t                Context;          /*!< SD transfer context                 */
  __IO HAL_SD_StateTypeDef     State;            /*!< SD card State                       */
  __IO uint32_t                ErrorCode;        /*!< SD Card Error codes                 */  
  DMA_HandleTypeDef            *hdmarx;          /*!< SD Rx DMA handle parameters         */
  DMA_HandleTypeDef            *hdmatx;          /*!< SD Tx DMA handle parameters         */
  HAL_SD_CardInfoTypeDef       SdCard;           /*!< SD Card information                 */
  uint32_t                     CSD[4];           /*!< SD card specific data table         */
  uint32_t                     CID[4];           /*!< SD card identification number table */
  
#if (USE_HAL_SD_REGISTER_CALLBACKS == 1)
  void (* TxCpltCallback)                 (struct __SD_HandleTypeDef *hsd);
  void (* RxCpltCallback)                 (struct __SD_HandleTypeDef *hsd);
  void (* ErrorCallback)                  (struct __SD_HandleTypeDef *hsd);
  void (* AbortCpltCallback)              (struct __SD_HandleTypeDef *hsd);

  void (* MspInitCallback)                (struct __SD_HandleTypeDef *hsd);
  void (* MspDeInitCallback)              (struct __SD_HandleTypeDef *hsd);
#endif  
}SD_HandleTypeDef;

注意事项:

条件编译USE_HAL_SD_REGISTER_CALLBACKS用来设置使用自定义回调还是使用默认回调,此定义一般放在stm32f4xx_hal_conf.h文件里面设置:

  #define   USE_HAL_SD_REGISTER_CALLBACKS   1

通过函数HAL_SD_RegisterCallback注册回调,取消注册使用函数HAL_SD_UnRegisterCallback。

这里重点介绍下面几个参数,其它参数主要是HAL库内部使用和自定义回调函数。

  •   SD_TypeDef   *Instance

这个参数是寄存器的例化,方便操作寄存器。

  •   SD_InitTypeDef  Init

这个参数在本章节3.2小节已经进行了详细说明。

3.4   SDIO总线源文件stm32f4xx_hal_sd.c

此文件涉及到的函数较多,这里把几个常用的函数做个说明:

  •   HAL_SD_Init
  •   HAL_SD_DeInit
  •   HAL_SD_ReadBlocks
  •   HAL_SD_WriteBlocks
  •   HAL_SD_ReadBlocks_DMA
  •   HAL_SD_WriteBlocks_DMA
  •   HAL_SD_Erase

3.4.1      函数HAL_SD_Init

函数原型:

HAL_StatusTypeDef HAL_SD_Init(SD_HandleTypeDef *hsd)
{
  /* 检测SD卡句柄 */
  if(hsd == NULL)
  {
    return HAL_ERROR;
  }

  /* 检查参数 */
  assert_param(IS_SDIO_ALL_INSTANCE(hsd->Instance));
  assert_param(IS_SDIO_CLOCK_EDGE(hsd->Init.ClockEdge));
  assert_param(IS_SDIO_CLOCK_BYPASS(hsd->Init.ClockBypass));
  assert_param(IS_SDIO_CLOCK_POWER_SAVE(hsd->Init.ClockPowerSave));
  assert_param(IS_SDIO_BUS_WIDE(hsd->Init.BusWide));
  assert_param(IS_SDIO_HARDWARE_FLOW_CONTROL(hsd->Init.HardwareFlowControl));
  assert_param(IS_SDIO_CLKDIV(hsd->Init.ClockDiv));

  if(hsd->State == HAL_SD_STATE_RESET)
  {
    hsd->Lock = HAL_UNLOCKED;
#if (USE_HAL_SD_REGISTER_CALLBACKS == 1)
    /* 复位默认的回调 */
    hsd->TxCpltCallback    = HAL_SD_TxCpltCallback;
    hsd->RxCpltCallback    = HAL_SD_RxCpltCallback;
    hsd->ErrorCallback     = HAL_SD_ErrorCallback;
    hsd->AbortCpltCallback = HAL_SD_AbortCallback;

    if(hsd->MspInitCallback == NULL)
    {
      hsd->MspInitCallback = HAL_SD_MspInit;
    }

    /* 初始化底层硬件 */
    hsd->MspInitCallback(hsd);
#else
    /* 初始化底层硬件: GPIO, CLOCK, CORTEX...etc */
    HAL_SD_MspInit(hsd);
#endif
  }

  hsd->State = HAL_SD_STATE_BUSY;

  /* 初始化卡参数 */
  HAL_SD_InitCard(hsd);

  /* 无错误代码 */
  hsd->ErrorCode = HAL_DMA_ERROR_NONE;
  
  /* 无操作 */
  hsd->Context = SD_CONTEXT_NONE;
                                                                                     
  /* 初始化SD卡状态 */
  hsd->State = HAL_SD_STATE_READY;

  return HAL_OK;
}

函数描述:

此函数用于初始化SD卡。

函数参数:

  •   第1个参数是SD_HandleTypeDef类型结构体指针变量,用于配置要初始化的参数。
  •   返回值,返回HAL_TIMEOUT表示超时,HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示忙,正在使用中。

注意事项:

  1. 函数HAL_SD_MspInit用于初始化SD的底层时钟、引脚等功能。需要用户自己在此函数里面实现具体的功能。由于这个函数是弱定义的,允许用户在工程其它源文件里面重新实现此函数。当然,不限制一定要在此函数里面实现,也可以像早期的标准库那样,用户自己初始化即可,更灵活些。
  2. 如果形参hsd的结构体成员State没有做初始状态,这个地方就是个坑。特别是用户搞了一个局部变量SD_HandleTypeDef SdHandle。

对于局部变量来说,这个参数就是一个随机值,如果是全局变量还好,一般MDK和IAR都会将全部变量初始化为0,而恰好这个 HAL_SD_STATE_RESET  = 0x00U。

解决办法有三

方法1:用户自己初始化SD和涉及到的GPIO等。

方法2:定义SD_HandleTypeDef SdHandle为全局变量。

方法3:下面的方法

if(HAL_SD_DeInit(&SdHandle) != HAL_OK)
{
    Error_Handler();
}  
if(HAL_SD_Init(&SdHandle) != HAL_OK)
{
    Error_Handler();
}

使用举例:

SD_HandleTypeDef uSdHandle;

uSdHandle.Instance = SDMMC1;

/* if CLKDIV = 0 then SDMMC Clock frequency = SDMMC Kernel Clock
     else SDMMC Clock frequency = SDMMC Kernel Clock / [2 * CLKDIV].
     50MHz / (2+x) = 25MHz
*/
uSdHandle.Init.ClockDiv            = 0; 
uSdHandle.Init.ClockPowerSave      = SDMMC_CLOCK_POWER_SAVE_DISABLE;
uSdHandle.Init.ClockEdge           = SDMMC_CLOCK_EDGE_RISING;
uSdHandle.Init.HardwareFlowControl = SDMMC_HARDWARE_FLOW_CONTROL_DISABLE;
uSdHandle.Init.BusWide             = SDMMC_BUS_WIDE_4B;
uSdHandle.Init.ClockBypass         = SDIO_CLOCK_BYPASS_DISABLE;

if(HAL_SD_Init(&uSdHandle) != HAL_OK)
{
   sd_state = MSD_ERROR;
}

3.4.2      函数HAL_SD_DeInit

函数原型:

HAL_StatusTypeDef HAL_SD_DeInit(SD_HandleTypeDef *hsd)
{
  /* 检测句柄 */
  if(hsd == NULL)
  {
    return HAL_ERROR;
  }
  
  /* 检测参数 */
  assert_param(IS_SDIO_ALL_INSTANCE(hsd->Instance));

  hsd->State = HAL_SD_STATE_BUSY;
  
  /* 关电 */ 
  SD_PowerOFF(hsd);
  
#if (USE_HAL_SD_REGISTER_CALLBACKS == 1)
  if(hsd->MspDeInitCallback == NULL)
  {
    hsd->MspDeInitCallback = HAL_SD_MspDeInit;
  }

  /* 复位底层硬件 */
  hsd->MspDeInitCallback(hsd);
#else
  /* 复位底层硬件 */
  HAL_SD_MspDeInit(hsd);
#endif
  
  hsd->ErrorCode = HAL_SD_ERROR_NONE;
  hsd->State = HAL_SD_STATE_RESET;
  
  return HAL_OK;
}

函数描述:

用于复位SD总线初始化。

函数参数:

  •   第1个参数是SD_HandleTypeDef类型结构体指针变量。
  •   返回值,返回HAL_TIMEOUT表示超时,HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示忙,正在使用中。

3.4.3      函数HAL_SD_ReadBlocks

函数原型:

HAL_StatusTypeDef HAL_SD_ReadBlocks(SD_HandleTypeDef *hsd, uint8_t *pData, uint32_t BlockAdd, uint32_t NumberOfBlocks, uint32_t Timeout)
{
  SDIO_DataInitTypeDef config;
  uint32_t errorstate = HAL_SD_ERROR_NONE;
  uint32_t tickstart = HAL_GetTick();
  uint32_t count = 0U, *tempbuff = (uint32_t *)pData;
  
  if(NULL == pData)
  {
    hsd->ErrorCode |= HAL_SD_ERROR_PARAM;
    return HAL_ERROR;
  }
 
  if(hsd->State == HAL_SD_STATE_READY)
  {
    hsd->ErrorCode = HAL_DMA_ERROR_NONE;
    
    if((BlockAdd + NumberOfBlocks) > (hsd->SdCard.LogBlockNbr))
    {
      hsd->ErrorCode |= HAL_SD_ERROR_ADDR_OUT_OF_RANGE;
      return HAL_ERROR;
    }
    
    hsd->State = HAL_SD_STATE_BUSY;
    
    /* 初始化数据控制寄存器 */
    hsd->Instance->DCTRL = 0U;
    
    if(hsd->SdCard.CardType != CARD_SDHC_SDXC)
    {
      BlockAdd *= 512U;
    }
      
    /* 设置卡的块大小 */
    errorstate = SDMMC_CmdBlockLength(hsd->Instance, BLOCKSIZE);
    if(errorstate != HAL_SD_ERROR_NONE)
    {
      /* 清除所有的静态标志 */
      __HAL_SD_CLEAR_FLAG(hsd, SDIO_STATIC_FLAGS);      
      hsd->ErrorCode |= errorstate;
      hsd->State = HAL_SD_STATE_READY;
      return HAL_ERROR;
    }
    
    /* 配置SD DPSM ( Data Path State Machine) */
    config.DataTimeOut   = SDMMC_DATATIMEOUT;
    config.DataLength    = NumberOfBlocks * BLOCKSIZE;
    config.DataBlockSize = SDIO_DATABLOCK_SIZE_512B;
    config.TransferDir   = SDIO_TRANSFER_DIR_TO_SDIO;
    config.TransferMode  = SDIO_TRANSFER_MODE_BLOCK;
    config.DPSM          = SDIO_DPSM_ENABLE;
    SDIO_ConfigData(hsd->Instance, &config);
    
    /* 查询方式块读取 */
    if(NumberOfBlocks > 1U)
    {
      hsd->Context = SD_CONTEXT_READ_MULTIPLE_BLOCK;
      
      /* 读取多块 */ 
      errorstate = SDMMC_CmdReadMultiBlock(hsd->Instance, BlockAdd);
    }
    else
    {
      hsd->Context = SD_CONTEXT_READ_SINGLE_BLOCK;
      
      /* 读取一个块 */
      errorstate = SDMMC_CmdReadSingleBlock(hsd->Instance, BlockAdd);
    }
    if(errorstate != HAL_SD_ERROR_NONE)
    {
      /* 清除所有的静态标志 */
      __HAL_SD_CLEAR_FLAG(hsd, SDIO_STATIC_FLAGS);
      hsd->ErrorCode |= errorstate;
      hsd->State = HAL_SD_STATE_READY;
      return HAL_ERROR;
    }
      
    /* 查询SDIO标志 */
#ifdef SDIO_STA_STBITERR
    while(!__HAL_SD_GET_FLAG(hsd, SDIO_FLAG_RXOVERR | SDIO_FLAG_DCRCFAIL | SDIO_FLAG_DTIMEOUT | SDIO_FLAG_DATAEND | SDIO_STA_STBITERR))
#else /* SDIO_STA_STBITERR 未定义 */
    while(!__HAL_SD_GET_FLAG(hsd, SDIO_FLAG_RXOVERR | SDIO_FLAG_DCRCFAIL | SDIO_FLAG_DTIMEOUT | SDIO_FLAG_DATAEND))
#endif /* SDIO_STA_STBITERR */
    {
      if(__HAL_SD_GET_FLAG(hsd, SDIO_FLAG_RXFIFOHF))
      {
        /* 从SDIO Rx FIFO读取数据 */
        for(count = 0U; count < 8U; count++)
        {
          *(tempbuff + count) = SDIO_ReadFIFO(hsd->Instance);
        }
        tempbuff += 8U;
      }
      
      if((Timeout == 0U)||((HAL_GetTick()-tickstart) >=  Timeout))
      {
        /* 清除所有的静态标志 */
        __HAL_SD_CLEAR_FLAG(hsd, SDIO_STATIC_FLAGS);
        hsd->ErrorCode |= HAL_SD_ERROR_TIMEOUT;
        hsd->State= HAL_SD_STATE_READY;
        return HAL_TIMEOUT;
      }
    }
    
    /* 多块读命令时,发送停止传输命令 */
    if(__HAL_SD_GET_FLAG(hsd, SDIO_FLAG_DATAEND) && (NumberOfBlocks > 1U))
    {    
      if(hsd->SdCard.CardType != CARD_SECURED)
      {
        /* 发送停止传输命令 */
        errorstate = SDMMC_CmdStopTransfer(hsd->Instance);
        if(errorstate != HAL_SD_ERROR_NONE)
        {
          /* 清除所有静态标志 */
          __HAL_SD_CLEAR_FLAG(hsd, SDIO_STATIC_FLAGS);
          hsd->ErrorCode |= errorstate;
          hsd->State = HAL_SD_STATE_READY;
          return HAL_ERROR;
        }
      }
    }
    
    /* 获取错误状态 */
    if(__HAL_SD_GET_FLAG(hsd, SDIO_FLAG_DTIMEOUT))
    {
      /* 清除静态标志 */
      __HAL_SD_CLEAR_FLAG(hsd, SDIO_STATIC_FLAGS);
      hsd->ErrorCode |= HAL_SD_ERROR_DATA_TIMEOUT;
      hsd->State = HAL_SD_STATE_READY;
      return HAL_ERROR;
    }
    else if(__HAL_SD_GET_FLAG(hsd, SDIO_FLAG_DCRCFAIL))
    {
      /* 清除静态标志 */
      __HAL_SD_CLEAR_FLAG(hsd, SDIO_STATIC_FLAGS);
      hsd->ErrorCode |= HAL_SD_ERROR_DATA_CRC_FAIL;
      hsd->State = HAL_SD_STATE_READY;
      return HAL_ERROR;
    }
    else if(__HAL_SD_GET_FLAG(hsd, SDIO_FLAG_RXOVERR))
    {
      /* 清除静态标志 */
      __HAL_SD_CLEAR_FLAG(hsd, SDIO_STATIC_FLAGS);
      hsd->ErrorCode |= HAL_SD_ERROR_RX_OVERRUN;
      hsd->State = HAL_SD_STATE_READY;
      return HAL_ERROR;
    }
    
    /* 如果还有数据,清空FIFO */
    while ((__HAL_SD_GET_FLAG(hsd, SDIO_FLAG_RXDAVL)))
    {
      *tempbuff = SDIO_ReadFIFO(hsd->Instance);
      tempbuff++;
      
      if((Timeout == 0U)||((HAL_GetTick()-tickstart) >=  Timeout))
      {
        /* 清除所有静态标志 */
        __HAL_SD_CLEAR_FLAG(hsd, SDIO_STATIC_FLAGS);        
        hsd->ErrorCode |= HAL_SD_ERROR_TIMEOUT;
        hsd->State= HAL_SD_STATE_READY;
        return HAL_ERROR;
      }
    }
    
    /* 清除所有静态标志 */
    __HAL_SD_CLEAR_FLAG(hsd, SDIO_STATIC_FLAGS);
    
    hsd->State = HAL_SD_STATE_READY;
    
    return HAL_OK;
  }
  else
  {
    hsd->ErrorCode |= HAL_SD_ERROR_BUSY;
    return HAL_ERROR;
  }
}

函数描述:

此函数主要用于SD卡数据读取。

函数参数:

  •   第1个参数是SD_HandleTypeDef类型结构体指针变量。
  •   第2个参数是接收数据的缓冲地址。
  •   第3个参数是要读取的扇区地址,即从第几个扇区开始读取(512字节为一个扇区)。
  •   第4个参数是读取的扇区数。
  •   第5个参数是传输过程的溢出时间,单位ms。
  •   返回值,返回HAL_TIMEOUT表示超时,HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示忙,正在使用中。

使用举例:

/**
  * @brief  Reads block(s) from a specified address in an SD card, in polling mode.
  * @param  pData: Pointer to the buffer that will contain the data to transmit
  * @param  ReadAddr: Address from where data is to be read
  * @param  NumOfBlocks: Number of SD blocks to read
  * @param  Timeout: Timeout for read operation
  * @retval SD status
  */
uint8_t BSP_SD_ReadBlocks(uint32_t *pData, uint32_t ReadAddr, uint32_t NumOfBlocks, uint32_t Timeout)
{

  if( HAL_SD_ReadBlocks(&uSdHandle, (uint8_t *)pData, ReadAddr, NumOfBlocks, Timeout) == HAL_OK)
  {
    return MSD_OK;
  }
  else
  {
    return MSD_ERROR;
  }

}

3.4.4      函数HAL_SD_WriteBlocks

函数原型:

HAL_StatusTypeDef HAL_SD_WriteBlocks(SD_HandleTypeDef *hsd, uint8_t *pData, uint32_t BlockAdd, uint32_t NumberOfBlocks, uint32_t Timeout)
{
  SDIO_DataInitTypeDef config;
  uint32_t errorstate = HAL_SD_ERROR_NONE;
  uint32_t tickstart = HAL_GetTick();
  uint32_t count = 0U;
  uint32_t *tempbuff = (uint32_t *)pData;
  
  if(NULL == pData)
  {
    hsd->ErrorCode |= HAL_SD_ERROR_PARAM;
    return HAL_ERROR;
  }

  if(hsd->State == HAL_SD_STATE_READY)
  {
    hsd->ErrorCode = HAL_DMA_ERROR_NONE;
    
    if((BlockAdd + NumberOfBlocks) > (hsd->SdCard.LogBlockNbr))
    {
      hsd->ErrorCode |= HAL_SD_ERROR_ADDR_OUT_OF_RANGE;
      return HAL_ERROR;
    }
    
    hsd->State = HAL_SD_STATE_BUSY;
    
    /* 初始化数据控制寄存器 */
    hsd->Instance->DCTRL = 0U;
     
    if(hsd->SdCard.CardType != CARD_SDHC_SDXC)
    {
      BlockAdd *= 512U;
    }
    
    /* 设置卡的块大小 */ 
    errorstate = SDMMC_CmdBlockLength(hsd->Instance, BLOCKSIZE);
    if(errorstate != HAL_SD_ERROR_NONE)
    {
      /* 清除所有静态标志 */
      __HAL_SD_CLEAR_FLAG(hsd, SDIO_STATIC_FLAGS);  
      hsd->ErrorCode |= errorstate;
      hsd->State = HAL_SD_STATE_READY;
      return HAL_ERROR;
    }
    
    /* 查询方式的块写入 */
    if(NumberOfBlocks > 1U)
    {
      hsd->Context = SD_CONTEXT_WRITE_MULTIPLE_BLOCK;
      
      /* 多块写入命令 */ 
      errorstate = SDMMC_CmdWriteMultiBlock(hsd->Instance, BlockAdd);
    }
    else
    {
      hsd->Context = SD_CONTEXT_WRITE_SINGLE_BLOCK;
      
      /* 单块写入命令 */
      errorstate = SDMMC_CmdWriteSingleBlock(hsd->Instance, BlockAdd);
    }
    if(errorstate != HAL_SD_ERROR_NONE)
    {
      /* 清除所有静态标志 */
      __HAL_SD_CLEAR_FLAG(hsd, SDIO_STATIC_FLAGS);  
      hsd->ErrorCode |= errorstate;
      hsd->State = HAL_SD_STATE_READY;
      return HAL_ERROR;
    }
    
    /* 配置SD DPSM (Data Path State Machine) */ 
    config.DataTimeOut   = SDMMC_DATATIMEOUT;
    config.DataLength    = NumberOfBlocks * BLOCKSIZE;
    config.DataBlockSize = SDIO_DATABLOCK_SIZE_512B;
    config.TransferDir   = SDIO_TRANSFER_DIR_TO_CARD;
    config.TransferMode  = SDIO_TRANSFER_MODE_BLOCK;
    config.DPSM          = SDIO_DPSM_ENABLE;
    SDIO_ConfigData(hsd->Instance, &config);
    
    /* 查询方式块写入 */
#ifdef SDIO_STA_STBITERR
    while(!__HAL_SD_GET_FLAG(hsd, SDIO_FLAG_TXUNDERR | SDIO_FLAG_DCRCFAIL | SDIO_FLAG_DTIMEOUT | SDIO_FLAG_DATAEND | SDIO_FLAG_STBITERR))
#else /* SDIO_STA_STBITERR 未定义 */
    while(!__HAL_SD_GET_FLAG(hsd, SDIO_FLAG_TXUNDERR | SDIO_FLAG_DCRCFAIL | SDIO_FLAG_DTIMEOUT | SDIO_FLAG_DATAEND))
#endif /* SDIO_STA_STBITERR */
    {
      if(__HAL_SD_GET_FLAG(hsd, SDIO_FLAG_TXFIFOHE))
      {
        /* 写入数据到SDIO Tx FIFO */
        for(count = 0U; count < 8U; count++)
        {
          SDIO_WriteFIFO(hsd->Instance, (tempbuff + count));
        }
        tempbuff += 8U;
      }
      
      if((Timeout == 0U)||((HAL_GetTick()-tickstart) >=  Timeout))
      {
        /* 清除所有静态标志 */
        __HAL_SD_CLEAR_FLAG(hsd, SDIO_STATIC_FLAGS);  
        hsd->ErrorCode |= errorstate;
        hsd->State = HAL_SD_STATE_READY;
        return HAL_TIMEOUT;
      }
    }
    
    /* 多块写入时,发送停止发送命令 */
    if(__HAL_SD_GET_FLAG(hsd, SDIO_FLAG_DATAEND) && (NumberOfBlocks > 1U))
    { 
      if(hsd->SdCard.CardType != CARD_SECURED)
      {
        /* 发送停止传输命令 */
        errorstate = SDMMC_CmdStopTransfer(hsd->Instance);
        if(errorstate != HAL_SD_ERROR_NONE)
        {
          /* 清除所有静态标志 */
          __HAL_SD_CLEAR_FLAG(hsd, SDIO_STATIC_FLAGS);  
          hsd->ErrorCode |= errorstate;
          hsd->State = HAL_SD_STATE_READY;
          return HAL_ERROR;
        }
      }
    }
    
    /* 获取错误状态 */
    if(__HAL_SD_GET_FLAG(hsd, SDIO_FLAG_DTIMEOUT))
    {
      /* 清除所有静态标志 */
      __HAL_SD_CLEAR_FLAG(hsd, SDIO_STATIC_FLAGS);
      hsd->ErrorCode |= HAL_SD_ERROR_DATA_TIMEOUT;
      hsd->State = HAL_SD_STATE_READY;
      return HAL_ERROR;
    }
    else if(__HAL_SD_GET_FLAG(hsd, SDIO_FLAG_DCRCFAIL))
    {
      /* 清除所有静态标志 */
      __HAL_SD_CLEAR_FLAG(hsd, SDIO_STATIC_FLAGS);
      hsd->ErrorCode |= HAL_SD_ERROR_DATA_CRC_FAIL;      
      hsd->State = HAL_SD_STATE_READY;
      return HAL_ERROR;
    }
    else if(__HAL_SD_GET_FLAG(hsd, SDIO_FLAG_TXUNDERR))
    {
      /* 清除所有静态标志 */
      __HAL_SD_CLEAR_FLAG(hsd, SDIO_STATIC_FLAGS);
      hsd->ErrorCode |= HAL_SD_ERROR_TX_UNDERRUN;
      hsd->State = HAL_SD_STATE_READY;
      return HAL_ERROR;
    }
    
      /* 清除所有静态标志 */
    __HAL_SD_CLEAR_FLAG(hsd, SDIO_STATIC_FLAGS);
    
    hsd->State = HAL_SD_STATE_READY;
    
    return HAL_OK;
  }
  else
  {
    hsd->ErrorCode |= HAL_SD_ERROR_BUSY;
    return HAL_ERROR;
  }
}

函数描述:

此函数主要用于向SD卡写入数据。

函数参数:

  •   第1个参数是SD_HandleTypeDef类型结构体指针变量。
  •   第2个参数是要写入到SD卡的数据缓冲地址。
  •   第3个参数是要写入的扇区地址,即从第几个扇区开始写入(512字节为一个扇区)。
  •   第4个参数是读取的扇区数。
  •   第5个参数是传输过程的溢出时间,单位ms。
  •   返回值,返回HAL_TIMEOUT表示超时,HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示忙,正在使用中。

使用举例:

/**
  * @brief  Writes block(s) to a specified address in an SD card, in polling mode.
  * @param  pData: Pointer to the buffer that will contain the data to transmit
  * @param  WriteAddr: Address from where data is to be written
  * @param  NumOfBlocks: Number of SD blocks to write
  * @param  Timeout: Timeout for write operation
  * @retval SD status
  */
uint8_t BSP_SD_WriteBlocks(uint32_t *pData, uint32_t WriteAddr, uint32_t NumOfBlocks, uint32_t Timeout)
{

  if( HAL_SD_WriteBlocks(&uSdHandle, (uint8_t *)pData, WriteAddr, NumOfBlocks, Timeout) == HAL_OK)
  {
    return MSD_OK;
  }
  else
  {
    return MSD_ERROR;
  }
}

3.4.5      函数HAL_SD_ReadBlocks_DMA

函数原型:

HAL_StatusTypeDef HAL_SD_ReadBlocks_DMA(SD_HandleTypeDef *hsd, uint8_t *pData, uint32_t BlockAdd, uint32_t NumberOfBlocks)
{
  SDIO_DataInitTypeDef config;
  uint32_t errorstate = HAL_SD_ERROR_NONE;
  
  if(NULL == pData)
  {
    hsd->ErrorCode |= HAL_SD_ERROR_PARAM;
    return HAL_ERROR;
  }
  
  if(hsd->State == HAL_SD_STATE_READY)
  {
    hsd->ErrorCode = HAL_DMA_ERROR_NONE;
    
    if((BlockAdd + NumberOfBlocks) > (hsd->SdCard.LogBlockNbr))
    {
      hsd->ErrorCode |= HAL_SD_ERROR_ADDR_OUT_OF_RANGE;
      return HAL_ERROR;
    }
    
    hsd->State = HAL_SD_STATE_BUSY;
    
    /* 初始化数据控制寄存器 */
    hsd->Instance->DCTRL = 0U;
    
#ifdef SDIO_STA_STBITERR
    __HAL_SD_ENABLE_IT(hsd, (SDIO_IT_DCRCFAIL | SDIO_IT_DTIMEOUT | SDIO_IT_RXOVERR | SDIO_IT_DATAEND | SDIO_IT_STBITERR));
#else /* SDIO_STA_STBITERR 未定义 */
    __HAL_SD_ENABLE_IT(hsd, (SDIO_IT_DCRCFAIL | SDIO_IT_DTIMEOUT | SDIO_IT_RXOVERR | SDIO_IT_DATAEND));
#endif /* SDIO_STA_STBITERR */
    
    /* 设置DMA传输完成回调 */
    hsd->hdmarx->XferCpltCallback = SD_DMAReceiveCplt;
    
    /* 设置DMA错误回调 */
    hsd->hdmarx->XferErrorCallback = SD_DMAError;
    
    /* 设置DMA终止回调 */
    hsd->hdmarx->XferAbortCallback = NULL;
    
    /* 使能DMA通道 */
    HAL_DMA_Start_IT(hsd->hdmarx, (uint32_t)&hsd->Instance->FIFO, (uint32_t)pData, (uint32_t)(BLOCKSIZE * NumberOfBlocks)/4);
    
    /* 使能SD DMA传输 */
    __HAL_SD_DMA_ENABLE(hsd);
    
    if(hsd->SdCard.CardType != CARD_SDHC_SDXC)
    {
      BlockAdd *= 512U;
    }
    
    /* 配置SD DPSM (Data Path State Machine) */ 
    config.DataTimeOut   = SDMMC_DATATIMEOUT;
    config.DataLength    = BLOCKSIZE * NumberOfBlocks;
    config.DataBlockSize = SDIO_DATABLOCK_SIZE_512B;
    config.TransferDir   = SDIO_TRANSFER_DIR_TO_SDIO;
    config.TransferMode  = SDIO_TRANSFER_MODE_BLOCK;
    config.DPSM          = SDIO_DPSM_ENABLE;
    SDIO_ConfigData(hsd->Instance, &config);

    /* 设置卡块大小 */ 
    errorstate = SDMMC_CmdBlockLength(hsd->Instance, BLOCKSIZE);
    if(errorstate != HAL_SD_ERROR_NONE)
    {
      /* 清除所有静态标志 */
      __HAL_SD_CLEAR_FLAG(hsd, SDIO_STATIC_FLAGS); 
      hsd->ErrorCode |= errorstate;
      hsd->State = HAL_SD_STATE_READY;
      return HAL_ERROR;
    }
        
    /* DMA模式块读取 */
    if(NumberOfBlocks > 1U)
    {
      hsd->Context = (SD_CONTEXT_READ_MULTIPLE_BLOCK | SD_CONTEXT_DMA);
      
      /* 多块读取 */ 
      errorstate = SDMMC_CmdReadMultiBlock(hsd->Instance, BlockAdd);
    }
    else
    {
      hsd->Context = (SD_CONTEXT_READ_SINGLE_BLOCK | SD_CONTEXT_DMA);
      
      /* 单块读取命令 */ 
      errorstate = SDMMC_CmdReadSingleBlock(hsd->Instance, BlockAdd);
    }
    if(errorstate != HAL_SD_ERROR_NONE)
    {
      /* 清除所有静态标志 */
      __HAL_SD_CLEAR_FLAG(hsd, SDIO_STATIC_FLAGS); 
      hsd->ErrorCode |= errorstate;
      hsd->State = HAL_SD_STATE_READY;
      return HAL_ERROR;
    }

    return HAL_OK;
  }
  else
  {
    return HAL_BUSY;
  }
}

函数描述:

此函数主要用于SD卡数据读取,DMA方式。

函数参数:

  •   第1个参数是SD_HandleTypeDef类型结构体指针变量。
  •   第2个参数是接收数据的缓冲地址。
  •   第3个参数是要读取的扇区地址,即从第几个扇区开始读取(512字节为一个扇区)。
  •   第4个参数是读取的扇区数。
  •   返回值,返回HAL_TIMEOUT表示超时,HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示忙,正在使用中。

使用举例:

/**
* @brief  Reads block(s) from a specified address in an SD card, in DMA mode.
* @param  pData: Pointer to the buffer that will contain the data to transmit
* @param  ReadAddr: Address from where data is to be read
* @param  NumOfBlocks: Number of SD blocks to read
* @retval SD status
*/
uint8_t BSP_SD_ReadBlocks_DMA(uint32_t *pData, uint32_t ReadAddr, uint32_t NumOfBlocks)
{

  if( HAL_SD_ReadBlocks_DMA(&uSdHandle, (uint8_t *)pData, ReadAddr, NumOfBlocks) == HAL_OK)
  {
    return MSD_OK;
  }
  else
  {
    return MSD_ERROR;
  }
}

3.4.6      函数HAL_SD_WriteBlocks_DMA

函数原型:

HAL_StatusTypeDef HAL_SD_WriteBlocks_DMA(SD_HandleTypeDef *hsd, uint8_t *pData, uint32_t BlockAdd, uint32_t NumberOfBlocks)
{
  SDIO_DataInitTypeDef config;
  uint32_t errorstate = HAL_SD_ERROR_NONE;
  
  if(NULL == pData)
  {
    hsd->ErrorCode |= HAL_SD_ERROR_PARAM;
    return HAL_ERROR;
  }
  
  if(hsd->State == HAL_SD_STATE_READY)
  {
    hsd->ErrorCode = HAL_DMA_ERROR_NONE;
    
    if((BlockAdd + NumberOfBlocks) > (hsd->SdCard.LogBlockNbr))
    {
      hsd->ErrorCode |= HAL_SD_ERROR_ADDR_OUT_OF_RANGE;
      return HAL_ERROR;
    }
    
    hsd->State = HAL_SD_STATE_BUSY;
    
    /* 初始化数据控制寄存器 */
    hsd->Instance->DCTRL = 0U;
    
    /* 使能SD错误中断 */  
#ifdef SDIO_STA_STBITERR
    __HAL_SD_ENABLE_IT(hsd, (SDIO_IT_DCRCFAIL | SDIO_IT_DTIMEOUT | SDIO_IT_TXUNDERR | SDIO_IT_STBITERR));    
#else /* SDIO_STA_STBITERR 未定义 */
    __HAL_SD_ENABLE_IT(hsd, (SDIO_IT_DCRCFAIL | SDIO_IT_DTIMEOUT | SDIO_IT_TXUNDERR));    
#endif /* SDIO_STA_STBITERR */
    
    /* 设置DMA传输完成回调 */
    hsd->hdmatx->XferCpltCallback = SD_DMATransmitCplt;
    
    /* 设置DMA错误回调 */
    hsd->hdmatx->XferErrorCallback = SD_DMAError;
    
    /* 设置DMA终止回调 */
    hsd->hdmatx->XferAbortCallback = NULL;
    
    if(hsd->SdCard.CardType != CARD_SDHC_SDXC)
    {
      BlockAdd *= 512U;
    }
    
    /* 设置卡的块大小 */ 
    errorstate = SDMMC_CmdBlockLength(hsd->Instance, BLOCKSIZE);
    if(errorstate != HAL_SD_ERROR_NONE)
    {
      /* 清除所有静态标志 */
      __HAL_SD_CLEAR_FLAG(hsd, SDIO_STATIC_FLAGS); 
      hsd->ErrorCode |= errorstate;
      hsd->State = HAL_SD_STATE_READY;
      return HAL_ERROR;
    }
    
    /* DMA方式块写入 */
    if(NumberOfBlocks > 1U)
    {
      hsd->Context = (SD_CONTEXT_WRITE_MULTIPLE_BLOCK | SD_CONTEXT_DMA);
      
      /* 写多块命令 */ 
      errorstate = SDMMC_CmdWriteMultiBlock(hsd->Instance, BlockAdd);
    }
    else
    {
      hsd->Context = (SD_CONTEXT_WRITE_SINGLE_BLOCK | SD_CONTEXT_DMA);
      
      /* 写单块命令 */
      errorstate = SDMMC_CmdWriteSingleBlock(hsd->Instance, BlockAdd);
    }
    if(errorstate != HAL_SD_ERROR_NONE)
    {
      /* 清除所有静态标志 */
      __HAL_SD_CLEAR_FLAG(hsd, SDIO_STATIC_FLAGS); 
      hsd->ErrorCode |= errorstate;
      hsd->State = HAL_SD_STATE_READY;
      return HAL_ERROR;
    }
    
    /* 使能SDI DMA传输 */
    __HAL_SD_DMA_ENABLE(hsd);
    
    /* 使能DMA通道 */
    HAL_DMA_Start_IT(hsd->hdmatx, (uint32_t)pData, (uint32_t)&hsd->Instance->FIFO, (uint32_t)(BLOCKSIZE * NumberOfBlocks)/4);
    
    /* 配置 SD DPSM (Data Path State Machine) */ 
    config.DataTimeOut   = SDMMC_DATATIMEOUT;
    config.DataLength    = BLOCKSIZE * NumberOfBlocks;
    config.DataBlockSize = SDIO_DATABLOCK_SIZE_512B;
    config.TransferDir   = SDIO_TRANSFER_DIR_TO_CARD;
    config.TransferMode  = SDIO_TRANSFER_MODE_BLOCK;
    config.DPSM          = SDIO_DPSM_ENABLE;
    SDIO_ConfigData(hsd->Instance, &config);
    
    return HAL_OK;
  }
  else
  {
    return HAL_BUSY;
  }
}

函数描述:

此函数主要用于向SD卡写入数据,DMA方式。

函数参数:

  •   第1个参数是SD_HandleTypeDef类型结构体指针变量。
  •   第2个参数是要写入到SD卡的数据缓冲地址。
  •   第3个参数是要写入的扇区地址,即从第几个扇区开始写入(512字节为一个扇区)。
  •   第4个参数是读取的扇区数。
  •   返回值,返回HAL_TIMEOUT表示超时,HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示忙,正在使用中。

使用举例:

/**
* @brief  Writes block(s) to a specified address in an SD card, in DMA mode.
* @param  pData: Pointer to the buffer that will contain the data to transmit
* @param  WriteAddr: Address from where data is to be written
* @param  NumOfBlocks: Number of SD blocks to write
* @retval SD status
*/
uint8_t BSP_SD_WriteBlocks_DMA(uint32_t *pData, uint32_t WriteAddr, uint32_t NumOfBlocks)
{

  if( HAL_SD_WriteBlocks_DMA(&uSdHandle, (uint8_t *)pData, WriteAddr, NumOfBlocks) == HAL_OK)
  {
    return MSD_OK;
  }
  else
  {
    return MSD_ERROR;
  }
}

3.4.7      函数HAL_SD_Erase

函数原型:

HAL_StatusTypeDef HAL_SD_Erase(SD_HandleTypeDef *hsd, uint32_t BlockStartAdd, uint32_t BlockEndAdd)
{
  uint32_t errorstate = HAL_SD_ERROR_NONE;
  
  if(hsd->State == HAL_SD_STATE_READY)
  {
    hsd->ErrorCode = HAL_DMA_ERROR_NONE;
    
    if(BlockEndAdd < BlockStartAdd)
    {
      hsd->ErrorCode |= HAL_SD_ERROR_PARAM;
      return HAL_ERROR;
    }
    
    if(BlockEndAdd > (hsd->SdCard.LogBlockNbr))
    {
      hsd->ErrorCode |= HAL_SD_ERROR_ADDR_OUT_OF_RANGE;
      return HAL_ERROR;
    }
    
    hsd->State = HAL_SD_STATE_BUSY;
    
    /* 检测是否支持擦除命令 */
    if(((hsd->SdCard.Class) & SDIO_CCCC_ERASE) == 0U)
    {
      /* 清除所有静态标志 */
      __HAL_SD_CLEAR_FLAG(hsd, SDIO_STATIC_FLAGS);
      hsd->ErrorCode |= HAL_SD_ERROR_REQUEST_NOT_APPLICABLE;
      hsd->State = HAL_SD_STATE_READY;
      return HAL_ERROR;
    }
    
    if((SDIO_GetResponse(hsd->Instance, SDIO_RESP1) & SDMMC_CARD_LOCKED) == SDMMC_CARD_LOCKED)
    {
      /* 清除所有静态标志 */
      __HAL_SD_CLEAR_FLAG(hsd, SDIO_STATIC_FLAGS);  
      hsd->ErrorCode |= HAL_SD_ERROR_LOCK_UNLOCK_FAILED;
      hsd->State = HAL_SD_STATE_READY;
      return HAL_ERROR;
    }
    
    /* 获取开启和接收块地址 */
    if(hsd->SdCard.CardType != CARD_SDHC_SDXC)
    {
      BlockStartAdd *= 512U;
      BlockEndAdd   *= 512U;
    }
    
    /* 根据sd-card spec 1.0 ERASE_GROUP_START (CMD32) 和  erase_group_end(CMD33) */
    if(hsd->SdCard.CardType != CARD_SECURED)
    {
      /* 发送  CMD32 SD_ERASE_GRP_START 带地址命令 */
      errorstate = SDMMC_CmdSDEraseStartAdd(hsd->Instance, BlockStartAdd);
      if(errorstate != HAL_SD_ERROR_NONE)
      {
        /* 清除所有静态标志 */
        __HAL_SD_CLEAR_FLAG(hsd, SDIO_STATIC_FLAGS); 
        hsd->ErrorCode |= errorstate;
        hsd->State = HAL_SD_STATE_READY;
        return HAL_ERROR;
      }
      
      /* 发送 CMD33 SD_ERASE_GRP_END 命令带地址 */
      errorstate = SDMMC_CmdSDEraseEndAdd(hsd->Instance, BlockEndAdd);
      if(errorstate != HAL_SD_ERROR_NONE)
      {
        /* 清除所有静态标志 */
        __HAL_SD_CLEAR_FLAG(hsd, SDIO_STATIC_FLAGS); 
        hsd->ErrorCode |= errorstate;
        hsd->State = HAL_SD_STATE_READY;
        return HAL_ERROR;
      }
    }
    
    /* 发送CMD38 擦除命令 */
    errorstate = SDMMC_CmdErase(hsd->Instance);
    if(errorstate != HAL_SD_ERROR_NONE)
    {
      /* 清除所有静态标志 */
      __HAL_SD_CLEAR_FLAG(hsd, SDIO_STATIC_FLAGS); 
      hsd->ErrorCode |= errorstate;
      hsd->State = HAL_SD_STATE_READY;
      return HAL_ERROR;
    }
    
    hsd->State = HAL_SD_STATE_READY;
    
    return HAL_OK;
  }
  else
  {
    return HAL_BUSY;
  }
}

函数描述:

此函数主要用于SD卡擦除。

函数参数:

  •   第1个参数是SD_HandleTypeDef类型结构体指针变量。
  •   第2个参数是擦除的起始扇区地址,地址单位是第几个扇区(512字节为一个扇区)。
  •   第3个参数是擦除的结束扇区地址,地址单位是第几个扇区(512字节为一个扇区)。
  •   返回值,返回HAL_TIMEOUT表示超时,HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示忙,正在使用中。

使用举例:

/**
* @brief  Erases the specified memory area of the given SD card.
* @param  StartAddr: Start byte address
* @param  EndAddr: End byte address
* @retval SD status
*/
uint8_t BSP_SD_Erase(uint32_t StartAddr, uint32_t EndAddr)
{

  if( HAL_SD_Erase(&uSdHandle, StartAddr, EndAddr) == HAL_OK)
  {
    return MSD_OK;
  }
  else
  {
    return MSD_ERROR;
  }
}

3.5   总结

本章节就为大家讲解这么多,更多SDIO知识可以看STM32F4的参考手册。

 

微信公众号:armfly_com 安富莱论坛:www.armbbs.cn 安富莱淘宝:https://armfly.taobao.com
原文地址:https://www.cnblogs.com/armfly/p/14525052.html