运动控制器26:STM32如何读写一张SD卡
2018-01-29 本文已影响0人
吴松乾
如何描述一张物理SD卡
描述SD卡的结构体
我们用4个结构体来完整描述一张物理的SD卡
struct SD_CardInfo // SD_GetCardInfo用到,获取SD卡的信息
uint32_t CardBlockSize
uint32_t CardCapacity
uint8_t CardType
uint16_t RCA
struct SD_CardStatus
__IO uint8_t AU_SIZE
__IO uint8_t DAT_BUS_WIDTH
__IO uint8_t ERASE_OFFSET
__IO uint16_t ERASE_SIZE
__IO uint8_t ERASE_TIMEOUT
__IO uint8_t PERFORMANCE_MOVE
__IO uint16_t SD_CARD_TYPE
__IO uint8_t SECURED_MODE
__IO uint32_t SIZE_OF_PROTECTED_AREA
__IO uint8_t SPEED_CLASS
struct SD_CID
__IO uint8_t CID_CRC
__IO uint16_t ManufactDate
__IO uint8_t ManufacturerID
__IO uint16_t OEM_AppliID
__IO uint32_t ProdName1
__IO uint8_t ProdName2
__IO uint8_t ProdRev
__IO uint32_t ProdSN
__IO uint8_t Reserved1
__IO uint8_t Reserved2
struct SD_CSD
//这个描述比较多,只截取了一部分
__IO uint16_t CardComdClasses
__IO uint8_t ContentProtectAppli
__IO uint8_t CopyFlag
__IO uint8_t CSD_CRC
所有的SD卡操作命令
初始化卡的时候,我们需要所有的SD卡发送CID,我们用到了下面的结构体生成一条命令:
00673 /*!< Send CMD2 ALL_SEND_CID */
00674 SDIO_CmdInitStructure.SDIO_Argument = 0x0;
00675 SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_ALL_SEND_CID;
00676 SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Long;
00677 SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
00678 SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
00679 SDIO_SendCommand(&SDIO_CmdInitStructure);
而SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_ALL_SEND_CID;则指示这条命令的功能,这样的命令对于SD卡还有很多,下面的三条命令是最通用的命令,三条命令的功能显而易见,比如设置总线宽度的命令,在SDEnWideBus().就用到
如下:
#define SD_CMD_ALL_SEND_CID ((uint8_t)2)
#define SD_CMD_APP_CMD ((uint8_t)55)
#define SD_CMD_APP_SD_SET_BUSWIDTH ((uint8_t)6)
下面的7条命令,主要针对的是SD卡的特殊操作,比如清写的端口,以及3条擦除命令,最后一条命令很常用,用于SD_GoIdleState(), and SD_PowerON().两个函数中。
#define SD_CMD_CLR_WRITE_PROT ((uint8_t)29)
#define SD_CMD_ERASE ((uint8_t)38)
#define SD_CMD_ERASE_GRP_END ((uint8_t)36)
#define SD_CMD_ERASE_GRP_START ((uint8_t)35)
#define SD_CMD_FAST_IO ((uint8_t)39)
#define SD_CMD_GEN_CMD ((uint8_t)56)
#define SD_CMD_GO_IDLE_STATE ((uint8_t)0)
下面是SD卡的APP命令,主要针对特殊安全命令:
#define SD_CMD_SD_APP_OP_COND ((uint8_t)41)
#define SD_CMD_SD_APP_SECURE_ERASE ((uint8_t)38)
#define SD_CMD_SD_APP_SECURE_READ_MULTIPLE_BLOCK ((uint8_t)18)
#define SD_CMD_SD_APP_SECURE_WRITE_MKB ((uint8_t)48)
#define SD_CMD_SD_APP_SECURE_WRITE_MULTIPLE_BLOCK ((uint8_t)25)
#define SD_CMD_SD_APP_SEND_NUM_WRITE_BLOCKS ((uint8_t)22)
#define SD_CMD_SD_APP_SEND_SCR ((uint8_t)51)
#define SD_CMD_SD_APP_SET_CER_RES2 ((uint8_t)47)
#define SD_CMD_SD_APP_SET_CER_RN1 ((uint8_t)45)
#define SD_CMD_SD_APP_SET_CLR_CARD_DETECT ((uint8_t)42)
#define SD_CMD_SD_APP_STAUS ((uint8_t)13)
#define SD_CMD_SD_ERASE_GRP_END ((uint8_t)33)
#define SD_CMD_SD_ERASE_GRP_START ((uint8_t)32)
#define SD_CMD_SDIO_RW_DIRECT ((uint8_t)52)
#define SD_CMD_SDIO_RW_EXTENDED ((uint8_t)53)
#define SD_CMD_SDIO_SEN_OP_COND ((uint8_t)5)
#define SD_CMD_SEL_DESEL_CARD ((uint8_t)7)
#define SD_CMD_SEND_CID ((uint8_t)10)
#define SD_CMD_SEND_CSD ((uint8_t)9)
#define SD_CMD_SEND_OP_COND ((uint8_t)1)
#define SD_CMD_SEND_STATUS ((uint8_t)13)
#define SD_CMD_SEND_WRITE_PROT ((uint8_t)30)
#define SD_CMD_SET_BLOCK_COUNT ((uint8_t)23)
#define SD_CMD_SET_BLOCKLEN ((uint8_t)16)
#define SD_CMD_SET_DSR ((uint8_t)4)
#define SD_CMD_SET_REL_ADDR ((uint8_t)3)
#define SD_CMD_SET_WRITE_PROT ((uint8_t)28)
#define SD_CMD_STOP_TRANSMISSION ((uint8_t)12)
#define SD_CMD_WRITE_DAT_UNTIL_STOP ((uint8_t)20)
#define SD_CMD_WRITE_MULT_BLOCK ((uint8_t)25)
#define SD_CMD_WRITE_SINGLE_BLOCK ((uint8_t)24)
#define SD_DMA_MODE ((uint32_t)0x00000000)
#define SD_NOT_PRESENT ((uint8_t)0x00)
#define SD_PRESENT ((uint8_t)0x01)
SD卡初始化和配置
初始化用到SD_Init() 函数,函数调用以后,SD卡进入待机模式,可以进行读写,初始化的流程如下:
- 设置时钟,检查卡型号(标准还是大容量),频率的公式如下:SDIO_CK = SDIOCLK / (SDIO_INIT_CLK_DIV + 2),标准卡不要超过400KHZ。
- 获取CID和CSD数据,这些数据我们保存在SDCardInfo的结构体中。
- 配置数据传输频率,一般设置为24MHZ,通过配置SDIO_TRANSFER_CLK_DIV
- 选中发回响应的SD卡,见第二步的操作。
- 配置SD卡的总线宽度为4位。
SD卡读操作
- SD_ReadBlock()和SD_ReadMultiBlocks() 函数可以用于读512字节长度的数据。
- SD_ReadBlock可以读取512字节的数据,可以通过DMA或者轮询模式读取,默认为DMA模式
- SD_ReadMultiBlocks可以读取512的倍数。
- 每一次读操作都需要调用两个函数SD_ReadWaitOperation获取DMA状态和SD_GetStatus获取SD卡状态,确保数据传输完成后再进行下一步。
- DMA数据完成后中断,调用SD_ProcessIRQ()函数进入中断函数,记得使能中断SDIO_IRQn 。
SD卡写操作
- 同样有两个函数可以执行写操作:WriteBlock和WriteMultiBlocks,长度也是512s
- 同上支持512,支持DMA或者轮询。
- 以及支持512的倍数
- 和读操作一样,我们也需要在每次通信时查询SD模块和SD卡的状态。
- 以及设置好中断。
SD卡状态获取
- 任何时候我们都可以获取SD卡的状态通过GetStatus,我们只需要确保SD硬件连接即可调用此函数。
- 也可以直接查寄存器用SendSDStatus函数。
SD卡编程示范
Status = SD_Init(); // Initialization Step as described in section A
// SDIO Interrupt ENABLE
NVIC_InitStructure.NVIC_IRQChannel = SDIO_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
// Write operation as described in Section C
Status = SD_WriteBlock(buffer, address, 512);
Status = SD_WaitWriteOperation();
while(SD_GetStatus() != SD_TRANSFER_OK);
Status = SD_WriteMultiBlocks(buffer, address, 512, NUMBEROFBLOCKS);
Status = SD_WaitWriteOperation();
while(SD_GetStatus() != SD_TRANSFER_OK);
Read operation as described in Section B
Status = SD_ReadBlock(buffer, address, 512);
Status = SD_WaitReadOperation();
while(SD_GetStatus() != SD_TRANSFER_OK);
Status = SD_ReadMultiBlocks(buffer, address, 512, NUMBEROFBLOCKS);
Status = SD_WaitReadOperation();
while(SD_GetStatus() != SD_TRANSFER_OK);
SD卡引脚分配
引脚 | 功能 |
---|---|
D2 | 总线 |
D3 | 总线 |
CMD | 命令 |
VCC | 3.3V |
CLK | 时钟 |
GND | 0 |
D0 | 总线 |
D1 | 总线 |