运动控制器26:STM32如何读写一张SD卡

如何描述一张物理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 总线
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 225,498评论 6 524
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 96,668评论 3 406
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 172,857评论 0 370
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 61,305评论 1 303
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 70,308评论 6 401
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 53,747评论 1 316
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 42,078评论 3 431
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 41,080评论 0 280
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 47,649评论 1 327
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 39,644评论 3 347
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 41,760评论 1 355
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 37,352评论 5 351
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 43,076评论 3 341
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 33,490评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 34,651评论 1 277
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 50,353评论 3 383
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 46,828评论 2 367

推荐阅读更多精彩内容