论坛原始地址(持续更新):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 初学者重要提示
- 对于SD控制SD卡或者MMC,掌握本章的知识点就够用了,更深入的认识可以看STM32F4的参考手册。
- 注意,操作SD卡是采用的函数HAL_SD_XXXX,而操作MMC是采用的函数HAL_MMC_XXXX,也就是说他们采用的函数前缀是不同的。
- SD卡官网: www.sdcard.org 。
- SDIO驱动MMC支持1线,4线和8线模式。
- 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表示忙,正在使用中。
注意事项:
- 函数HAL_SD_MspInit用于初始化SD的底层时钟、引脚等功能。需要用户自己在此函数里面实现具体的功能。由于这个函数是弱定义的,允许用户在工程其它源文件里面重新实现此函数。当然,不限制一定要在此函数里面实现,也可以像早期的标准库那样,用户自己初始化即可,更灵活些。
- 如果形参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的参考手册。