我爱编程

运动控制器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卡进入待机模式,可以进行读写,初始化的流程如下:

  1. 设置时钟,检查卡型号(标准还是大容量),频率的公式如下:SDIO_CK = SDIOCLK / (SDIO_INIT_CLK_DIV + 2),标准卡不要超过400KHZ。
  2. 获取CID和CSD数据,这些数据我们保存在SDCardInfo的结构体中。
  3. 配置数据传输频率,一般设置为24MHZ,通过配置SDIO_TRANSFER_CLK_DIV
  4. 选中发回响应的SD卡,见第二步的操作。
  5. 配置SD卡的总线宽度为4位。

SD卡读操作

  1. SD_ReadBlock()和SD_ReadMultiBlocks() 函数可以用于读512字节长度的数据。
  2. SD_ReadBlock可以读取512字节的数据,可以通过DMA或者轮询模式读取,默认为DMA模式
  3. SD_ReadMultiBlocks可以读取512的倍数。
  4. 每一次读操作都需要调用两个函数SD_ReadWaitOperation获取DMA状态和SD_GetStatus获取SD卡状态,确保数据传输完成后再进行下一步。
  5. DMA数据完成后中断,调用SD_ProcessIRQ()函数进入中断函数,记得使能中断SDIO_IRQn 。

SD卡写操作

  1. 同样有两个函数可以执行写操作:WriteBlock和WriteMultiBlocks,长度也是512s
  2. 同上支持512,支持DMA或者轮询。
  3. 以及支持512的倍数
  4. 和读操作一样,我们也需要在每次通信时查询SD模块和SD卡的状态。
  5. 以及设置好中断。

SD卡状态获取

  1. 任何时候我们都可以获取SD卡的状态通过GetStatus,我们只需要确保SD硬件连接即可调用此函数。
  2. 也可以直接查寄存器用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 总线
上一篇下一篇

猜你喜欢

热点阅读