相关文章
《【SDIO】SDIO、SD卡、FatFs文件系统相关文章索引》
1.前言
本篇文章主要是介绍SD卡的读写测试,包括:SD卡擦除测试、SD卡单一块读写测试、SD卡多个块读写测试。这个3个测试主要是调用了stm324x9i_eval_sdio_sd.c
里面的相关API,下面会详细的介绍这些API是如何实现的。SD卡在Transfer Mode阶段的状态图如下:
SD卡的读写测试的思维导图如下,下面会详细介绍这3个函数是如何实现的:
NOTE:阅读下面分析时最好参考SD卡读写测试的完整工程一起,这样有助于理解。工程下载地址下面有贴出来。
2.SD_EraseTest()
SD_EraseTest()
函数主要的流程是擦除指定地址块的存储,通过DMA的方式读取这写块的数据,判断是否等于0xFF或者0x00(默认情况下擦除的Flash都是0xFF,特殊的情况下也有0x00)。SD_EraseTest()
函数具体实现如下:
2.1 CMD32:SD_CMD_SD_ERASE_GRP_START
CMD32:SD_CMD_SD_ERASE_GRP_START
该命令主要是作用是设置擦除的起始块地址。
#define SD_CMD_SD_ERASE_GRP_START ((uint8_t)32)
SDIO_CmdInitStructure.SDIO_Argument =(uint32_t)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);
发送命令CMD32的Argument是 start data block address,SD_EraseTest()
设置擦除地址是0 ~ 51200Bytes,所以这里填写的是0x00 (0/512)。实际发送出去的波形如下:
然后SD卡处理完后,以R1的形式Response Card Status。通过CmdResp1Error
检测CMD是否正确响应,并且判断Card Status是否存在错误。Receive的波形如下:
Card Status = 0x00000900
对应的表格如下:
2.2 CMD33:SD_CMD_SD_ERASE_GRP_END
CMD33:SD_CMD_SD_ERASE_GRP_END
该命令主要是作用是设置擦除的结束块地址。
#define SD_CMD_SD_ERASE_GRP_END ((uint8_t)33)
SDIO_CmdInitStructure.SDIO_Argument = (uint32_t)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);
发送命令CMD33的Argument是 end data block address,SD_EraseTest()
设置擦除地址是0 ~ 51200Bytes,所以这里填写的是0x64 (51200/512)。实际发送出去的波形如下:
然后SD卡处理完后,以R1的形式Response Card Status。通过CmdResp1Error
检测CMD是否正确响应,并且判断Card Status是否存在错误。Receive的波形如下:
备注:R1 Response Card Status的状态值和CMD32一样,参考上面的截图分析。
2.3 CMD38:SD_CMD_ERASE
CMD38:SD_CMD_ERASE
该命令主要是作用是擦除预先选定的块。
#define SD_CMD_ERASE ((uint8_t)38)
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);
因为发送命令CMD38的Argument是stuff bits,所以这里需要填写0x00。实际发送出去的波形如下:
Host发送CMD38后,DATA0保持低电平,说明SD处于Busy状态。CMD响应会以R1的形式Response Card Status,通过CmdResp1Error
检测CMD是否正确响应,并且判断Card Status是否存在错误。Receive的波形如下:
Card Status = 0x00000800
对应的表格如下:
2.4 IsCardProgramming()
在SD_Erase()
函数中,通过IsCardProgramming()
函数循环获取SD卡Card Status,判断并等待是否已经退出Programming State。
errorstatus = IsCardProgramming(&cardstate);
delay = SD_DATATIMEOUT;
while ((delay > 0) && (errorstatus == SD_OK) && ((SD_CARD_PROGRAMMING == cardstate) || (SD_CARD_RECEIVING == cardstate)))
{
errorstatus = IsCardProgramming(&cardstate);
delay--;
}
下面有贴出IsCardProgramming()
函数的部分代码,主要步骤如下:
- 发送CMD13:SD_CMD_SEND_STATUS获取SD卡Card Status。
- 循环获取SDIO 状态寄存器 (SDIO_STA),等待CMD13的Response被正确接收。
- 通过
SDIO_GetCommandResponse()
获取SDIO 命令响应寄存器 (SDIO_RESPCMD)来获取Response Command Index,并且判断是否等于CMD13(SD_CMD_SEND_STATUS)。 - 通过
SDIO_GetResponse(SDIO_RESP1)
访问SDIO 响应 1 寄存器 (SDIO_RESP1),来获取Response Argument的参数,这个参数就是SD卡Card Status,然后返回SD卡Current State(Card Status[12:9])。
static SD_Error IsCardProgramming(uint8_t *pstatus)
{
SD_Error errorstatus = SD_OK;
__IO uint32_t respR1 = 0, status = 0;
SDIO_CmdInitStructure.SDIO_Argument = (uint32_t) RCA << 16;
SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SEND_STATUS;
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);
status = SDIO->STA;
while (!(status & (SDIO_FLAG_CCRCFAIL | SDIO_FLAG_CMDREND | SDIO_FLAG_CTIMEOUT)))
{
status = SDIO->STA;
}
if (status & SDIO_FLAG_CTIMEOUT)
{
errorstatus = SD_CMD_RSP_TIMEOUT;
SDIO_ClearFlag(SDIO_FLAG_CTIMEOUT);
return(errorstatus);
}
else if (status & SDIO_FLAG_CCRCFAIL)
{
errorstatus = SD_CMD_CRC_FAIL;
SDIO_ClearFlag(SDIO_FLAG_CCRCFAIL);
return(errorstatus);
}
status = (uint32_t)SDIO_GetCommandResponse();
if (status != SD_CMD_SEND_STATUS)
{
errorstatus = SD_ILLEGAL_CMD;
return(errorstatus);
}
SDIO_ClearFlag(SDIO_STATIC_FLAGS);
respR1 = SDIO_GetResponse(SDIO_RESP1);
*pstatus = (uint8_t) ((respR1 >> 9) & 0x0000000F);
if ((respR1 & SD_OCR_ERRORBITS) == SD_ALLZERO)
{
return(errorstatus);
}
...
}
发送CMD13:SD_CMD_SEND_STATUS的实际波形如下:
获取CMD13的Response Card Status,通过Argument可以知道SD卡CURRENT_STATE = programming state
,实际接收到的波形如下:
Card Status = 0x00000E00
对应的表格如下:
经过一段时间,DATA0从Low Level跳转到High Level,指示SD卡已经退出Busy状态。然后发送CMD13来获取Card Status,通过下面的波形我们可以知道Card Status = 0x00000900
,说明SD卡已经退出programming state(CURRENT_STATE = transfer state)
。
3. SD_ReadMultiBlocks()
在SD_EraseTest()
擦除测试中有用到SD_ReadMultiBlocks()
,该函数主要功能是通过DMA的方式读取SD卡多个数据块的数据。
3.1 SDIO_ITConfig()
SDIO_ITConfig()
函数主要是配置SDIO 屏蔽寄存器 (SDIO_MASK),中断屏蔽寄存器通过将对应的位置设置为 1 来确定哪一个状态标志位可以产生中断。。
SDIO_ITConfig(SDIO_IT_DCRCFAIL | SDIO_IT_DTIMEOUT | SDIO_IT_DATAEND | SDIO_IT_RXOVERR | SDIO_IT_STBITERR, ENABLE);
名称 | 描述 | Value | 备注 |
DCRCFAILIE | 数据 CRC 失败中断使能 (Data CRC fail interrupt enable) 0:禁止数据 CRC 失败中断 1:使能数据 CRC 失败中断 |
1b | SDIO_IT_DCRCFAIL |
DTIMEOUTIE | 数据超时中断使能 (Data timeout interrupt enable) 0:禁止数据超时中断 1:使能数据超时中断 |
1b | SDIO_IT_DTIMEOUT |
DATAENDIE | 数据结束中断使能 (Data end interrupt enable) 0:禁止数据结束中断 1:使能数据结束中断 |
1b | SDIO_IT_DATAEND |
RXOVERRIE | Rx FIFO 上溢错误中断使能 (Rx FIFO overrun error interrupt enable) 0:禁止 Rx FIFO 上溢错误中断 1:使能 Rx FIFO 上溢错误中断 |
1b | SDIO_IT_RXOVERR |
STBITERRIE | 起始位错误中断使能 (Start bit error interrupt enable) 0:禁止起始位错误中断 1:使能起始位错误中断 |
1b | SDIO_IT_STBITERR |
3.2 SD_LowLevel_DMA_RxConfig()
DMA 简介:直接存储器访问 (DMA) 用于在外设与存储器之间以及存储器与存储器之间提供高速数据传输。可以在无需任何 CPU 操作的情况下通过 DMA 快速移动数据。这样节省的 CPU 资源可供其它操作使用。主要的传输方式有3种:
- 外设到存储器的传输
- 存储器到外设的传输
- 存储器到存储器的传输
STM32F4xx 系列资源丰富,具有两个 DMA 控制器,同时外设繁多,为实现正常传输,DMA需要通道选择控制。每个 DMA控制器具有8个数据流,每个数据流对应 8个外设请求。在实现 DMA 传输之前,DMA控制器会通过 DMA数据流 x 配置寄存器 DMA_SxCR(x为 0~7,对应 8 个 DMA 数据流)的 CHSEL[2:0]位选择对应的通道作为该数据流的目标外设。
外设通道选择主要是为了选择哪一个外设作为该数据流的源地址或者目标地址。
我们这里使用的是SDIO外设,在DMA2的映射表中可以找到,我们有2个可以选择:
- 通道4 & 数据流3
- 通道4 & 数据流6
SD_LowLevel_DMA_RxConfig()
具体代码如下:
#define SD_SDIO_DMA DMA2
#define SD_SDIO_DMA_CLK RCC_AHB1Periph_DMA2
#define SD_SDIO_DMA_STREAM DMA2_Stream3
#define SD_SDIO_DMA_CHANNEL DMA_Channel_4
#define SD_SDIO_DMA_FLAG_FEIF DMA_FLAG_FEIF3
#define SD_SDIO_DMA_FLAG_DMEIF DMA_FLAG_DMEIF3
#define SD_SDIO_DMA_FLAG_TEIF DMA_FLAG_TEIF3
#define SD_SDIO_DMA_FLAG_HTIF DMA_FLAG_HTIF3
#define SD_SDIO_DMA_FLAG_TCIF DMA_FLAG_TCIF3
#define SD_SDIO_DMA_IRQn DMA2_Stream3_IRQn
#define SD_SDIO_DMA_IRQHANDLER DMA2_Stream3_IRQHandler
void SD_LowLevel_DMA_RxConfig(uint32_t *BufferDST, uint32_t BufferSize)
{
DMA_InitTypeDef SDDMA_InitStructure;
DMA_ClearFlag(SD_SDIO_DMA_STREAM, SD_SDIO_DMA_FLAG_FEIF | SD_SDIO_DMA_FLAG_DMEIF | SD_SDIO_DMA_FLAG_TEIF | SD_SDIO_DMA_FLAG_HTIF | SD_SDIO_DMA_FLAG_TCIF);
DMA_Cmd(SD_SDIO_DMA_STREAM, DISABLE);
DMA_DeInit(SD_SDIO_DMA_STREAM);
SDDMA_InitStructure.DMA_Channel = SD_SDIO_DMA_CHANNEL;
SDDMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)SDIO_FIFO_ADDRESS;
SDDMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)BufferDST;
SDDMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
SDDMA_InitStructure.DMA_BufferSize = 1;
SDDMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
SDDMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
SDDMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
SDDMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
SDDMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
SDDMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
SDDMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;
SDDMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
SDDMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_INC4;
SDDMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_INC4;
DMA_Init(SD_SDIO_DMA_STREAM, &SDDMA_InitStructure);
DMA_ITConfig(SD_SDIO_DMA_STREAM, DMA_IT_TC, ENABLE);
DMA_FlowControllerConfig(SD_SDIO_DMA_STREAM, DMA_FlowCtrl_Peripheral);
DMA_Cmd(SD_SDIO_DMA_STREAM, ENABLE);
}
具体分析如下:
- DMA_ClearFlag():清除DMA中断相关标志位。
- DMA_Init():初始化DMA相关寄存器,涉及到寄存器有:
- DMA 数据流 x 配置寄存器 (DMA_SxCR) (x = 0…7)
- DMA 数据流 x FIFO 控制寄存器 (DMA_SxFCR) (x = 0…7)
- DMA 数据流 x 数据项数寄存器 (DMA_SxNDTR) (x = 0…7)
- DMA 数据流 x 外设地址寄存器 (DMA_SxPAR) (x = 0…7)
- DMA 数据流 x 存储器 0 地址寄存器 (DMA_SxM0AR) (x = 0…7)
- DMA_ITConfig():配置DMA相关中断寄存器,涉及到寄存器有:
- DMA 数据流 x 配置寄存器 (DMA_SxCR) (x = 0…7)
- DMA 数据流 x FIFO 控制寄存器 (DMA_SxFCR) (x = 0…7)
- DMA_FlowControllerConfig():配置DMA相关流控制寄存器,涉及到寄存器有:
- DMA 数据流 x 配置寄存器 (DMA_SxCR) (x = 0…7)
- DMA_Cmd():配置DMA相关数据流通道Enable Or Disabled,涉及到寄存器有:
- DMA 数据流 x 配置寄存器 (DMA_SxCR) (x = 0…7)
名称 | 描述 | Value | 备注 |
CHSEL[2:0] | 通道选择 (Channel selection) 000:选择通道 0 001:选择通道 1 010:选择通道 2 011:选择通道 3 100:选择通道 4 101:选择通道 5 110:选择通道 6 111:选择通道 7 |
100b | DMA_Channel_4 |
MBURST[1:0] | 存储器突发传输配置 (Memory burst transfer configuration) 00:单次传输 01:INCR4(4 个节拍的增量突发传输) 10:INCR8(8 个节拍的增量突发传输) 11:INCR16(16 个节拍的增量突发传输) |
01b | DMA_MemoryBurst_INC4 |
PBURST[1:0] | 外设突发传输配置 (Peripheral burst transfer configuration) 00:单次传输 01:INCR4(4 个节拍的增量突发传输) 10:INCR8(8 个节拍的增量突发传输) 11:INCR16(16 个节拍的增量突发传输) |
01b | DMA_PeripheralBurst_INC4 |
PL[1:0] | 优先级 (Priority level) 00:低 01:中 10:高 11:非常高 |
11b | DMA_Priority_VeryHigh |
MSIZE[1:0] | 存储器数据大小 (Memory data size) 00:字节(8 位) 01:半字(16 位) 10:字(32 位) 11:保留 |
10b | DMA_MemoryDataSize_Word |
PSIZE[1:0] | 外设数据大小 (Peripheral data size) 00:字节(8 位) 01:半字(16 位) 10:字(32 位) 11:保留 |
10b | DMA_MemoryDataSize_Word |
MINC | 存储器递增模式 (Memory increment mode) 0:存储器地址指针固定 1:每次数据传输后,存储器地址指针递增(增量为 MSIZE 值) |
1b | DMA_MemoryInc_Enable |
PINC | 外设递增模式 (Peripheral increment mode) 0:外设地址指针固定 1:每次数据传输后,外设地址指针递增(增量为 PSIZE 值) |
0b | DMA_PeripheralInc_Disable |
DIR[1:0] | 数据传输方向 (Data transfer direction) 00:外设到存储器 01:存储器到外设 10:存储器到存储器 |
00b | DMA_DIR_PeripheralToMemory |
PFCTRL | 外设流控制器 (Peripheral flow controller) 0:DMA 是流控制器 1:外设是流控制器 |
0b | DMA_FlowCtrl_Peripheral |
TCIE | 传输完成中断使能 (Transfer complete interrupt enable) 0:禁止 TC 中断 1:使能 TC 中断 |
1b | DMA_IT_TC |
EN | 数据流使能/读作低电平时数据流就绪标志 (Stream enable / flag stream ready when read low) 0:禁止数据流 1:使能数据流 |
1b | DMA_SxCR_EN |
名称 | 描述 | Value | 备注 |
DMDIS | 直接模式禁止 (Direct mode disable) 0:使能直接模式 1:禁止直接模式 |
1b | DMA_FIFOMode_Enable |
FTH[1:0] | FIFO 阈值选择 (FIFO threshold selection) 00:FIFO 容量的 1/4 01:FIFO 容量的 1/2 10:FIFO 容量的 3/4 11:FIFO 完整容量 |
11b | DMA_FIFOThreshold_Full |
名称 | 描述 | Value | 备注 |
NDT[15:0] | 要传输的数据项数目 (Number of data items to transfer) 要传输的数据项数目(0 到 65535)。只有在禁止数据流时,才能向此寄存器执行写操作。使能数据流后,此寄存器为只读,用于指示要传输的剩余数据项数。每次 DMA 传输后,此寄存器将递减。 |
1 |
名称 | 描述 | Value | 备注 |
PAR[31:0] | 外设地址 (Peripheral address) 读/写数据的外设数据寄存器的基址。 |
0x40012C80 | SDIO_FIFO_ADDRESS: SDIO 数据 FIFO 寄存器 (SDIO_FIFO)的地址 |
名称 | 描述 | Value | 备注 |
M0A[31:0] | 存储器 0 地址 (Memory 0 address) 读/写数据的存储区 0 的基址。 |
* | BufferDST: 存放数据的内存地址 |
3.3 CMD16:SD_CMD_SET_BLOCKLEN
CMD16:SD_CMD_SET_BLOCKLEN
该命令主要是作用是设置块命令的长度。
#define SD_CMD_SET_BLOCKLEN ((uint8_t)16)
SDIO_CmdInitStructure.SDIO_Argument = (uint32_t) 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);
发送命令CMD16的Argument是 block size,SD2.0协议规定了SDIO_HIGH_CAPACITY_SD_CARD
的block size = 512Bytes,所以这里填写的是0x200 (512)。实际发送出去的波形如下:
然后SD卡处理完后,以R1的形式Response Card Status。通过CmdResp1Error
检测CMD是否正确响应,并且判断Card Status是否存在错误。Receive的波形如下:
Card Status = 0x00000900
对应的表格如下:
3.4 SDIO_DataConfig()
SDIO_DataConfig()
函数主要的功能是配置传输数据的长度、传输超时、传输方向和传输模式等。涉及到的SDIO寄存器如下:
- SDIO 数据定时器寄存器 (SDIO_DTIMER)
- SDIO 数据长度寄存器 (SDIO_DLEN)
- SDIO 数据控制寄存器 (SDIO_DCTRL)
SDIO_DataInitStructure.SDIO_DataTimeOut = SD_DATATIMEOUT;
SDIO_DataInitStructure.SDIO_DataLength = NumberOfBlocks * 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);
名称 | 描述 | Value | 备注 |
DATATIME | 数据超时周期 (Data timeout period) 以卡总线时钟周期表示的数据超时周期。 |
0xFFFFFFFF | SD_DATATIMEOUT |
名称 | 描述 | Value | 备注 |
DATALENGTH | 数据长度值 (Data length value) 要传输的数据字节数量。 |
0xC800(100 * 512) | NumberOfBlocks * BlockSize |
名称 | 描述 | Value | 备注 |
DBLOCKSIZE | 数据块大小 (Data block size) 0000:(十进制数 0)块长度 = 20 = 1 字节 0001:(十进制数 1)块长度 = 21 = 2 字节 0010:(十进制数 2)块长度 = 22 = 4 字节 0011:(十进制数 3)块长度 = 23 = 8 字节 0100:(十进制数 4)块长度 = 24 = 16 字节 0101:(十进制数 5)块长度 = 25 = 32 字节 0110:(十进制数 6)块长度 = 26 = 64 字节 0111:(十进制数 7)块长度 = 27 = 128 字节 1000:(十进制数 8)块长度 = 28 = 256 字节 1001:(十进制数 9)块长度 = 29 = 512 字节 1010:(十进制数 10)块长度 = 210 = 1024 字节 1011:(十进制数 11)块长度 = 211 = 2048 字节 1100:(十进制数 12)块长度 = 212 = 4096 字节 1101:(十进制数 13)块长度 = 213 = 8192 字节 1110:(十进制数 14)块长度 = 214 = 16384 字节 1111:(十进制数 15)保留 |
9 | 9 << 4 |
DTMODE | 数据传输模式选择 (Data transfer mode selection) 0:块数据传输 1:流或 SDIO 多字节数据传输 |
0 | SDIO_TransferMode_Block |
DTDIR | 数据传输方向选择 (Data transfer direction selection) 0:从控制器到卡。 1:从卡到控制器。 |
0 | SDIO_TransferDir_ToSDIO |
DTEN | 数据传输使能位 (Data transfer enabled bit) 如果 1 写入到 DTEN 位,则数据传输开始。 |
1 | SDIO_DPSM_Enable |
3.5 CMD18:SD_CMD_READ_MULT_BLOCK
CMD18:SD_CMD_READ_MULT_BLOCK
该命令主要是作用是从指定地址连续从 SD 卡读取数据块。
#define SD_CMD_READ_MULT_BLOCK ((uint8_t)18)
SDIO_CmdInitStructure.SDIO_Argument = (uint32_t)ReadAddr;
SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_READ_MULT_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_MULT_BLOCK);
发送命令CMD18的Argument是 start read data block address,ReadAddr
被设置为0,实际发送出去的波形如下:
然后SD卡处理完后,以R1的形式Response Card Status。通过CmdResp1Error
检测CMD是否正确响应,并且判断Card Status是否存在错误。Receive的波形如下:
3.6 SD_WaitReadOperation()
SD_WaitReadOperation()
这个函数主要的功能是等待SDIO DMA数据传输完成。通过DMA中断函数SD_ProcessDMAIRQ()
来设置DMAEndOfTransfer标志,下面有介绍。
SD_Error SD_WaitReadOperation(void)
{
SD_Error errorstatus = SD_OK;
uint32_t timeout;
timeout = SD_DATATIMEOUT;
while ((DMAEndOfTransfer == 0x00) && (TransferEnd == 0) && (TransferError == SD_OK) && (timeout > 0))
{
timeout--;
}
DMAEndOfTransfer = 0x00;
timeout = SD_DATATIMEOUT;
while(((SDIO->STA & SDIO_FLAG_RXACT)) && (timeout > 0))
{
timeout--;
}
if (StopCondition == 1)
{
errorstatus = SD_StopTransfer();
StopCondition = 0;
}
...
}
SD_ProcessDMAIRQ()
这个函数是DMA中断函数,当SDIO DMA传输完成后会将DMAEndOfTransfer 设置为0x01。
void SD_ProcessDMAIRQ(void)
{
if(DMA2->LISR & SD_SDIO_DMA_FLAG_TCIF)
{
DMAEndOfTransfer = 0x01;
DMA_ClearFlag(SD_SDIO_DMA_STREAM, SD_SDIO_DMA_FLAG_TCIF|SD_SDIO_DMA_FLAG_FEIF);
}
}
在等待中,我们可以通过波形看到传输的数据都是0x00,说明SD_EraseTest()
成功。
4.验证测试结果
测试代码如下:
static void SD_EraseTest(void)
{
printf("\r\n---->Erase test is starting...\r\n");
if (Status == SD_OK)
{
Status = SD_Erase(0x00, (BLOCK_SIZE * NUMBER_OF_BLOCKS));
}
if (Status == SD_OK)
{
Status = SD_ReadMultiBlocks(aBuffer_MultiBlock_Rx, 0x00, BLOCK_SIZE, NUMBER_OF_BLOCKS);
Status = SD_WaitReadOperation();
while(SD_GetStatus() != SD_TRANSFER_OK);
}
if (Status == SD_OK)
{
EraseStatus = eBuffercmp(aBuffer_MultiBlock_Rx, MULTI_BUFFER_SIZE);
}
if(EraseStatus == PASSED)
{
LED1(ON);
printf("\tThe earase is successful.\r\n");
}
else
{
LED1(OFF);
LED3(ON);
printf("\tThe earase is fail!!!\r\n");
}
printf("<----Erase test is end.\r\n");
}
验证测试结果成功:
5. 参考资料
SDIO参考的资料如下:
下载地址如下:
https://download.csdn.net/download/ZHONGCAI0901/14975835
移植成功的完整代码下载地址如下:
https://download.csdn.net/download/ZHONGCAI0901/15265756