46. SDIO协议
一、SDIO简介
SDIO,全称为安全数字输入/输出接口。STM32F4 的 SDIO 控制器支持多媒体卡(MMC 卡)、SD 存储卡、SDI/O 卡和 CE-ATA 设备等。SDIO 的主要功能如下:
- 与多媒体卡系统规格书版本 4.2 全兼容。支持三种不同的数据总线模式:1 位(默认)、4 位和 8 位。
- 与较早的多媒体卡系统规格版本全兼容(向前兼容)。
- 与 SD 存储卡规格版本 2.0 全兼容。SD 卡规范版本 2.0,包括 SD 和高容量 SDHC 标准卡,故不支持超大容量 SDXC/SDUC 标准卡,所以 STM32F4xx 的 SDIO 可以支持的最高卡容量是 32GB。
- 与 SDI/O 卡规格版本 2.0 全兼容:支持良种不同的数据总线模式:1 位(默认)和 4 位。
- 完全支持 CE-ATA 功能(与 CE-ATA 数字协议版本 1.1 全兼容)。8 位总线模式下数据传输速率可达 48MHz。
- 数据和命令输出使能信号,用于控制外部双向驱动器。
- SDIO 不具备兼容 SPI 的通信模式。
二、SDIO框图
STM32F4 的 SDIO 控制器包含 2 个部分:SDIO 适配器模块和 APB2 总线接口,其功能框图如下所示:
复位后默认情况下 SDIO_D0 用于数据传输。初始化后主机可以改变数据总线的宽度(通过 ACMD6 命令设置)。
如果一个多媒体卡接到了总线上,则 SDIO_D0、SDIO_D[3:0] 或 SDIO_D[7:0] 可以用于数据传输。MMC 版本 V3.31 和之前版本的协议只支持 1 位数据线,所以只能用 SDIO_D0(为了通用性考虑,在程序里面我们只要检测到是 MMC 卡就设置为 1 位总线数据)。
如果一个 SD 或 SD I/O 卡接到了总线上,可以通过主机配置数据传输使用 SDIO_D0 或 SDIO_D[3:0]。所有的数据线都工作在推挽模式。
【1】、SDIO 的时钟
从中我们可以看到 SDIO 总共有 3 个时钟,分别是:
① 卡时钟(SDIO_CK):由 SDIO 适配器中的时钟产生器在外部引脚输出的通信时钟信号。不同总线协议,最高时钟频率不同。每个时钟脉冲传输的是命令或数据每个时钟周期在命令和数据线上传输 1 位命令或数据。对于多媒体卡 V3.31 协议,时钟频率可以在 0MHz 至 20MHz 间变化;对于多媒体卡 V4.0/4.2 协议,时钟频率可以在 0MHz 至 48MHz 间变化;对于 SD 或 SDI/O 卡,时钟频率可以在 0MHz 至 25MHz 间变化。
其中,SDIO CLK 为 PLL48CK,一般是 48Mhz,而 CLKDIV 则是分配系数,可以通过 SDIO 的 SDIO_CLKCR 寄存器进行设置(确保 SDIO_CK 不超过卡的最大操作频率)。
SD 卡初始化时,SDIO_CK 不可超过 400KHz;初始化完成后,可设为最大(不可超过 SD 卡最大操作频率 25MHz))并可更改数据总线宽度(默认只用 SDIO_D0 进行初始化)。
② SDIO 适配器时钟(SDIOCLK):该时钟用于驱动 SDIO 适配器,来自主 PLL 的独立输出(OLL48CK),和 PLLCLK 独立 ,其频率一般为 48Mhz,并用于产生 SDIO_CK 时钟。
③ APB2 总线接口时钟(PCLK2):该时钟用于驱动 SDIO 的 APB2 总线接口,其频率为 HCLK/2,一般为 84Mhz。
【2】、SDIO 适配器
SDIO 适配器提供 SD 卡特有的功能:产生时钟、发送命令、接收应答、双向传输数据。
- 控制单元:电源管理和时钟管理功能。
- 命令通道:控制命令发送并接收响应。
- 数据通道:负责主机和卡之间传输数据。
- 数据 FIFO:具有发送和接收数据缓冲器。
- 置位发送使能,写命令寄存器:命令开始发送。
- 不需要响应,进入空闲状态。
- 如果还需响应,进入等待状态,启动命令定时器。
- 超时未接收到响应,置位超时标志,进入空闲状态。
- 接收响应完成,根据CRC设置状态寄存器。
命令发送完成后设置状态标志:
- CMDREND:响应CRC正常。
- CCRCFAIL:响应CRC失败。
- CMDSENT:发送了不需要响应的命令。
- CTIMEOUT:响应超时。
- CMDACT:正在进行命令传输。
【3】、数据 FIFO
数据 FIFO 包括 32 个字的数据缓冲,和发送与接收电路。TXACT 和 RXACT 标志分别表明当前处于发送还是接收状态,由数据通道子单元对这两个标志互斥的置位。
发送 FIFO:SDIO 发送数据时,由数据通道单元置位 TXACT,表示数据传输正在进行当 TXACT 位为 1,通过 APB2 接口将数据写入到发送 FIFO。发送 FIFO 相关状态:TXFIFOF,TXFIFOE,TXFIFOHE,TXDAVL,TXUNDERRUN。
接收 FIFO:SDIO 接收数据时,由数据通道单元置位 RXACT。当 RXACT 位为 1,通过接收 FIFO 存放从数据通道接收到的数据。接收 FIFO 的相关状态: RXFIFOF,RXFIFOE,RXFIFOHF,RXDAVL,RXOVERR。
三、SDIO的命令与响应
SDIO 的命令分为应用相关命令(ACMD)和通用命令(CMD)两部分,应用相关命令(ACMD)的发送,必须先发送通用命令(CMD55),然后才能发送应用相关命令(ACMD)。SDIO 的所有命令和响应都只通过 SDIO_CMD 引脚传输的,任何命令的长度都是固定为 48 位,SDIO 的命令格式如下所示:
所有的命令都是由 STM32F4 发出,其中开始位、传输位、CRC7 和结束位由 SDIO 硬件控制,我们需要设置的就只有命令索引和参数部分。其中命令索引(如 CMD0,CMD1 之类的)在 SDIO_CMD 寄存器里面设置,命令参数则由寄存器 SDIO_ARG 设置。
一般情况下,选中的 SD 卡在接收到命令之后,都会回复一个应答(注意 CMD0 是无应答的),这个应答我们称之为响应,响应也是在 CMD 线上串行传输的。STM32F4 的 SDIO 控制器支持 2 种响应类型,即:短响应(48 位)和长响应(136 位),这两种响应类型都带 CRC 错误检测(注意不带 CRC 的响应应该忽略 CRC 错误标志,如 CMD1 的响应)。
短响应的格式如下所示:
长响应的格式如下所示:
同样,硬件为我们滤除了开始位、传输位、CRC7 以及结束位等信息,对于短响应,命令索引存放在 SDIO_RESPCMD 寄存器,参数则存放在 SDIO_RESP1 寄存器里面。对于长响应,则仅留 CID/CSD 位域,存放在 SDIO_RESP1 ~ SDIO_RESP4 等 4 个寄存器。
四、SDIO块数据传输
对于 SDI/SDIO 存储器,数据是以数据块的形式传输的。
SDIO(多)数据块读操作如下所示:
从上图,我们可以看出,从机在收到主机相关命令后,开始发送数据块给主机,所有数据块都带有 CRC 校验值(CRC 由 SDIO 硬件自动处理),单个数据块读的时候,在收到 1 个数据块以后即可以停止了,不需要发送停止命令(CMD12)。但是多块数据读的时候,SD 卡将一直发送数据给主机,直到接到主机发送的 STOP 命令(CMD12)。
SDIO(多)数据块写操作如下所示:
数据块写操作同数据块读操作基本类似,只是数据块写的时候,多了一个繁忙判断,新的数据块必须在 SD 卡非繁忙的时候发送。这里的繁忙信号由 SD 卡拉低 SDIO_D0,以表示繁忙,SDIO 硬件自动控制,不需要我们软件处理。
五、SDIO寄存器
5.1、SDIO电源控制寄存器
该寄存器复位值为 0,所以 SDIO 的电源是关闭的,我们要启用 SDIO,第一步就是要设置该寄存器最低 2 个位均为 1,让 SDIO 上电,开启卡时钟。
5.2、SDIO时钟控制寄存器
SDIO 时钟控制寄存器(SDIO_CLKCR),该寄存器主要用于设置 SDIO_CK 的分配系数,开关等,并可以设置 SDIO 的数据位宽。WIDBUS 用于设置 SDIO 总线位宽,正常使用的时候,设置为 1,即 4 位宽度。BYPASS 用于设置分频器是否旁路,我们一般要使用分频器,所以这里设置为 0,禁止旁路。CLKEN 则用于设置是否使能 SDIO_CK,我们设置为 1。最后,CLKDIV 则用于控制 SDIO_CK 的分频,设置为 1,即可得到 24Mhz 的 SDIO_CK 频率。
5.3、SDIO参数寄存器
SDIO 参数寄存器(SDIO_ARG),该寄存器包含一个 32 位命令参数,该参数作为命令消息的一部分发送到卡,如果命令包含参数,则在将命令写入到命令寄存器之前,必须将参数加载到此寄存器中。
5.4、SDIO命令寄存器
其中低 6 位为命令索引,也就是我们要发送的命令索引号(比如发送 CMD1,其值为 1,索引就设置为 1)。位[7:6],用于设置等待响应位,用于指示 CPSM 是否需要等待,以及等待类型等。这里的 CPSM,即命令通道状态机。
5.5、SDIO命令响应寄存器
SDIO 命令响应寄存器(SDIO_RESPCMD),该寄存器为 32 位,但只有低 6 位有效,比较简单,用于存储最后收到的命令响应中的命令索引。如果传输的命令响应不包含命令索引(长响应或 OCR 响应),则该寄存器的内容不可预知。
5.6、SDIO 响应1..4寄存器
SDIO 响应寄存器组(SDIO_RESP1 ~ SDIO_RESP4),该寄存器组总共由 4 个 32 位寄存器组成,用于存放接收到的卡响应部分信息。如果收到短响应,则数据存放在 SDIO_RESP1 寄存器里面,其它三个寄存器没有用到。而如果收到长响应,则依次存放在 SDIO_RESP1 ~ SDIO_RESP4 里面。
5.7、SDIO数据定时器寄存器
SDIO 数据定时器寄存器(SDIO_DTIMER)用于存储以卡总线时钟(SDIO_CK)为周期的数据超时时间,一个计数器将从 SDIO_DTIMER 寄存器加载数值,并在数据通道状态机(DPSM)进入 Wait_R 或繁忙状态时进行递减计数,当 DPSM 处在这些状态时,如果计数器减为 0,则设置超时标志。这里的 DPSM,即数据通道状态机,类似 CPSM,详细请参考《STM32F4xx 参考手册_V4(中文版).pdf》第 809 页。注意:在写入数据控制寄存器,进行数据传输之前,必须先写入该寄存器(SDIO_DTIMER)和数据长度寄存器(SDIO_DLEN)!
5.8、SDIO数据长度寄存器
SDIO 数据长度寄存器(SDIO_DLEN)低 25 位有效,用于设置需要传输的数据字节长度。对于块数据传输,该寄存器的数值,必须是数据块长度(通过 SDIO_DCTRL 设置)的倍数。
5.9、SDIO数据控制寄存器
该寄存器,用于控制数据通道状态机(DPSM),包括数据传输使能、传输方向、传输模式、DMA 使能、数据块长度等信息,都是通过该寄存器设置。我们需要根据自己的实际情况,来配置该寄存器,才可正常实现数据收发。
5.10、SDIO数据计数器寄存器
5.11、SDIO状态寄存器
状态寄存器可以用来查询 SDIO 控制器的当前状态,以便处理各种事务。比如 SDIO_STA的位 2 表示命令响应超时,说明 SDIO 的命令响应出了问题。我们通过设置 SDIO_ICR 的位 2则可以清除这个超时标志,而设置 SDIO_MASK 的位 2,则可以开启命令响应超时中断,设置为 0 关闭。
5.12、SDIO中断清零寄存器
5.13、SDIO屏蔽寄存器
5.14、SDIO FIFO计数器寄存器
5.15、SDIO数据FIFO寄存器
数据 FIFO 寄存器包括接收和发送 FIFO,他们由一组连续的 32 个地址上的 32 个寄存器组成,CPU 可以使用 FIFO 读写多个操作数。例如我们要从 SD 卡读数据,就必须读 SDIO_FIFO寄存器,要写数据到 SD 卡,则要写 SDIO_FIFO 寄存器。SDIO 将这 32 个地址分为 16 个一组,发送接收各占一半。而我们每次读写的时候,最多就是读取发送 FIFO 或写入接收 FIFO 的一半大小的数据,也就是 8 个字(32 个字节),这里特别提醒,我们操作 SDIO_FIFO(不论读出还是写入)必须是以 4 字节对齐的内存进行操作,否则将导致出错!
六、SDIO引脚
SDIO_CK | SDIO_CMD | SDIO_D0 | SDIO_D1 | SDIO_D2 | SDIO_D3 |
---|---|---|---|---|---|
PC12 | PD2 | PC8 | PC9 | PC10 | PC11 |
七、SDIO配置步骤
7.1、使能SDIO时钟
使能 SDIO 时钟:
#define __HAL_RCC_SDIO_CLK_ENABLE() do { \
__IO uint32_t tmpreg = 0x00U; \
SET_BIT(RCC->APB2ENR, RCC_APB2ENR_SDIOEN);\
/* Delay after an RCC peripheral clock enabling */ \
tmpreg = READ_BIT(RCC->APB2ENR, RCC_APB2ENR_SDIOEN);\
UNUSED(tmpreg); \
} while(0U)
使能对应的 GPIO 时钟:
#define __HAL_RCC_GPIOC_CLK_ENABLE() do { \
__IO uint32_t tmpreg = 0x00U; \
SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOCEN);\
/* Delay after an RCC peripheral clock enabling */ \
tmpreg = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOCEN);\
UNUSED(tmpreg); \
} while(0U)
#define __HAL_RCC_GPIOD_CLK_ENABLE() do { \
__IO uint32_t tmpreg = 0x00U; \
SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIODEN);\
/* Delay after an RCC peripheral clock enabling */ \
tmpreg = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIODEN);\
UNUSED(tmpreg); \
} while(0U)
7.2、初始化SD卡
HAL_StatusTypeDef HAL_SD_Init(SD_HandleTypeDef *hsd);
形参 hsd 是 SDIO 的句柄,SD_HandleTypeDef 结构体类型,其定义如下:
typedef struct
{
SD_TypeDef *Instance; // SDIO相关寄存器基地址
SD_InitTypeDef Init; // sDIO初始化变量
HAL_LockTypeDef Lock; // 互斥锁,用于解决外设访问冲突
uint8_t *pTxBuffPtr; // SD发送数据指针
uint32_t TxXferSize; // SD发送缓存按字节数的大小
uint8_t *pRxBuffPtr; // SD接收数据指针
uint32_t RxXferSize; // SD接收缓存按字节数的大
__IO uint32_t Context; // HAL库对SD卡的操作阶段
__IO HAL_SD_StateTypeDef State; // SD卡操作状态
__IO uint32_t ErrorCode; // SD卡错误代码
DMA_HandleTypeDef *hdmatx; // SD DMA数据发送指针
DMA_HandleTypeDef *hdmarx; // SD DMA 据接收指针
HAL_SD_CardInfoTypeDef SdCard; // SD卡信息的结构体
uint32_t CSD[4]; // 保存SD卡CSD寄存器信息
uint32_t CID[4]; // 保存SD卡CID寄存器信息
}SD_HandleTypeDef;
Instance:指向 SDIO 寄存器基地址。实际上这个基地址 HAL 库已经定义好了。
#define SD_TypeDef SDIO_TypeDef
#define SDIO ((SDIO_TypeDef *) SDIO_BASE)
Init:SDIO 初始化结构体,用于配置 SDIO 工作参数。
typedef struct
{
uint32_t ClockEdge; // 数据或指令变化的时钟沿
uint32_t ClockBypass; // 是否设置旁路分频器
uint32_t ClockPowerSave; // 空闲状态是否输出时钟
uint32_t BusWide; // 设置SDIO总线位宽
uint32_t HardwareFlowControl; // 用于使能硬件流控制
uint32_t ClockDiv; // 设置SDIO时钟分频
}SDIO_InitTypeDef;
#define SD_InitTypeDef SDIO_InitTypeDef
成员 ClockEdge 设置 数据或指令变化的时钟沿,可选值如下:
#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 SDIO_HARDWARE_FLOW_CONTROL_DISABLE 0x00000000U
#define SDIO_HARDWARE_FLOW_CONTROL_ENABLE SDIO_CLKCR_HWFC_EN
成员 ClockDiv 设置 SDIO时钟分频,可选值如下:
#define SDIO_INIT_CLK_DIV ((uint8_t)0x76) /* 48MHz / (SDMMC_INIT_CLK_DIV + 2) < 400KHz */
/* SDIO Data Transfer Frequency (25MHz max) */
#define SDIO_TRANSFER_CLK_DIV ((uint8_t)0x0) /* 48MHz / (SDMMC_TRANSFER_CLK_DIV + 2) < 25MHz */
该函数的返回值是 HAL_StatusTypeDef 枚举类型的值,有 4 个,分别是 HAL_OK 表示 成功,HAL_ERROR 表示 错误,HAL_BUSY 表示 忙碌,HAL_TIMEOUT 表示 超时。
typedef enum
{
HAL_OK = 0x00U, // 成功
HAL_ERROR = 0x01U, // 错误
HAL_BUSY = 0x02U, // 忙碌
HAL_TIMEOUT = 0x03U // 超时
} HAL_StatusTypeDef;
7.3、SDIO底层初始化
HAL 库中,提供 HAL_GPIO_Init() 函数用于配置 GPIO 功能模式,初始化 GPIO。该函数的声明如下:
void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init);
该函数的第一个形参 GPIOx 用来 指定端口号,可选值如下:
#define GPIOC ((GPIO_TypeDef *) GPIOC_BASE)
#define GPIOD ((GPIO_TypeDef *) GPIOD_BASE)
第二个参数是 GPIO_InitTypeDef 类型的结构体变量,用来 设置 GPIO 的工作模式,其定义如下:
typedef struct
{
uint32_t Pin; // 引脚号
uint32_t Mode; // 模式设置
uint32_t Pull; // 上下拉设置
uint32_t Speed; // 速度设置
uint32_t Alternate; // 复用功能设置
}GPIO_InitTypeDef;
成员 Pin 表示 引脚号,范围:GPIO_PIN_0 到 GPIO_PIN_15。
#define GPIO_PIN_11 ((uint16_t)0x0800) /* Pin 11 selected */
#define GPIO_PIN_12 ((uint16_t)0x1000) /* Pin 12 selected */
#define GPIO_PIN_13 ((uint16_t)0x2000) /* Pin 13 selected */
成员 Mode 是 GPIO 的 模式选择,有以下选择项:
#define GPIO_MODE_AF_OD 0x00000002U // 开漏式复用
成员 Pull 用于 配置上下拉电阻,有以下选择项:
#define GPIO_NOPULL 0x00000000U // 无上下拉
#define GPIO_PULLUP 0x00000001U // 上拉
#define GPIO_PULLDOWN 0x00000002U // 下拉
成员 Speed 用于 配置 GPIO 的速度,有以下选择项:
#define GPIO_SPEED_FREQ_LOW 0x00000000U // 低速
#define GPIO_SPEED_FREQ_MEDIUM 0x00000001U // 中速
#define GPIO_SPEED_FREQ_HIGH 0x00000002U // 高速
#define GPIO_SPEED_FREQ_VERY_HIGH 0x00000003U // 极速
成员 Alternate 用于 配置具体的复用功能,不同的 GPIO 口可以复用的功能不同,具体可参考数据手册。
#define GPIO_AF12_SDIO ((uint8_t)0x0C) /* SDIO Alternate Function mapping */
7.4、配置总线宽度
SD 卡上电后默认使用 1 位数据总线进行数据传输,卡如果允许,可以在初始化完成后重新设置 SD 卡的数据位宽以加快数据传输过程。
HAL_StatusTypeDef HAL_SD_ConfigWideBusOperation(SD_HandleTypeDef *hsd, uint32_t WideMode);
形参 hsd 是 SD_HandleTypeDef 结构体类型指针变量。
形参 WideMode 是 总线宽度,可选值如下:
#define SDIO_BUS_WIDE_1B 0x00000000U
#define SDIO_BUS_WIDE_4B SDIO_CLKCR_WIDBUS_0
#define SDIO_BUS_WIDE_8B SDIO_CLKCR_WIDBUS_1
该函数的返回值是 HAL_StatusTypeDef 枚举类型的值,有 4 个,分别是 HAL_OK 表示 成功,HAL_ERROR 表示 错误,HAL_BUSY 表示 忙碌,HAL_TIMEOUT 表示 超时。
7.5、获取SD卡的相关状态信息
SD 卡初始化后,根据设备句柄读 SD 卡的相关状态信息:
HAL_StatusTypeDef HAL_SD_GetCardInfo(SD_HandleTypeDef *hsd, HAL_SD_CardInfoTypeDef *pCardInfo);
形参 hsd 是 SD_HandleTypeDef 结构体类型指针变量。
形参 pCardInfo 是 HAL_SD_CardInfoTypeDef 结构体类型指针变量。用来提取初始化后的卡信息,包括卡类型、容量等参数。
typedef struct
{
uint32_t CardType; // 存储卡类型标记:标准卡、高速卡
uint32_t CardVersion; // 存储卡版本
uint32_t Class; // 卡类型
uint32_t RelCardAdd; // 卡相对地址
uint32_t BlockNbr; // 卡存储块数
uint32_t BlockSize; // SD卡每个存储块大小
uint32_t LogBlockNbr; // 以块表示的卡逻辑容量
uint32_t LogBlockSize; // 以字节为单位的逻辑块大小
}HAL_SD_CardInfoTypeDef;
该函数的返回值是 HAL_StatusTypeDef 枚举类型的值,有 4 个,分别是 HAL_OK 表示 成功,HAL_ERROR 表示 错误,HAL_BUSY 表示 忙碌,HAL_TIMEOUT 表示 超时。
7.6、读取块数据
SD 卡初始化后从 SD 卡的指定扇区读数据:
HAL_StatusTypeDef HAL_SD_ReadBlocks(SD_HandleTypeDef *hsd, uint8_t *pData, uint32_t BlockAdd, uint32_t NumberOfBlocks, uint32_t Timeout);
形参 hsd 是 SD_HandleTypeDef 结构体类型指针变量。
形参 pData 是 是一个指向 8 位类型的数据指针缓冲,它用于接收我们需要的数据。
形参 BlockAdd 指向我们需要访问的数据扇区,对于任意的存储都是类似的,像 SD 卡这样的大存储块也同样是通过位置标识来访问不同的数据。
形参 NumberOfBlocks 对应的是我们本次要从指定扇区读取的 字节数。
形参 Timeout 表示 读的超时时间。
该函数的返回值是 HAL_StatusTypeDef 枚举类型的值,有 4 个,分别是 HAL_OK 表示 成功,HAL_ERROR 表示 错误,HAL_BUSY 表示 忙碌,HAL_TIMEOUT 表示 超时。
7.7、写入块数据
SD 卡初始化后,在 SD 卡的指定扇区写入数据:
HAL_StatusTypeDef HAL_SD_WriteBlocks(SD_HandleTypeDef *hsd, uint8_t *pData, uint32_t BlockAdd, uint32_t NumberOfBlocks, uint32_t Timeout);
形参 hsd 是 SD_HandleTypeDef 结构体类型指针变量。
形参 pData 是 是一个指向 8 位类型的数据指针缓冲,它用于接收我们需要的数据。
形参 BlockAdd 指向我们需要访问的数据扇区,对于任意的存储都是类似的,像 SD 卡这样的大存储块也同样是通过位置标识来访问不同的数据。
形参 NumberOfBlocks 对应的是我们本次要从指定扇区写入的 字节数。
形参 Timeout 表示 读的超时时间。
该函数的返回值是 HAL_StatusTypeDef 枚举类型的值,有 4 个,分别是 HAL_OK 表示 成功,HAL_ERROR 表示 错误,HAL_BUSY 表示 忙碌,HAL_TIMEOUT 表示 超时。