历史上的今天
返回首页

历史上的今天

今天是:2026年03月23日(星期一)

正在发生

2023年03月23日 | STM32CubeMX系列 | SD卡

2023-03-23 来源:zhihu

1. SD卡介绍

1.1 SD卡简介

很多单片机系统都需要大容量存储设备,以存储数据(常用的有U盘、FLASH芯片、SD卡等),比较而言SD卡是单片机大容量外部存储的首选,只需要少数几个IO口即可外扩一个容量从几十M到几十G的,且有多种体积尺寸可选(标准SD卡、TF卡等)的外部存储器

SD卡(Secure Digital Memory Card)即:安全数码卡,它是在MMC的基础上发展而来,是一种基于半导体快闪记忆器的新一代记忆设备,它被广泛地于便携式装置上使用,例如数码相机、个人数码助理(PDA)和多媒体播放器等。SD卡由日本松下、东芝及美国SanDisk公司于1999年8月共同开发研制。 SD卡按容量分类,可以分为3类:SD卡、SDHC卡、SDXC卡,如下表所示:

SD卡和SDHC卡协议基本兼容,但是SDXC卡的区别比较大,这里仅介绍SD/SDHC卡(简称SD卡),SD卡由9个引脚与外部通讯,支持SPI和SDIO两种操作模式,不同模式下SD卡引脚功能描叙如下图表示:

1.2 SD卡的物理结构及内部框图

SD卡的物理结构一般包括以下5个部分:

- 存储单元:是存储数据部件;
- 存储单元接口:存储单元通过存储单元接口与卡控制单元进行数据传输;
- 电源检测单元:保证SD卡工作在合适的电压下,如出现掉电或上状态时,它会使控制单元和存储单元接口复位;
- 卡及接口控制单元:控制SD卡的运行状态,它包括有8个寄存器;
- 接口驱动器:控制SD卡引脚的输入输出

SDIO由SDIO适配器和APB2接口两部分组成:

- SDIO适配器:提供特定于MMC/SD/SD I/O卡的所有功能,如时钟生成单元、命令和数据传输
- APB2接口:访问SDIO适配器寄存器,并且生成中断和DMA请求信号

下图是SDIO功能框图及SDIO适配器框图:

1.3 SD卡命令

SD卡命令由主机发出,命令格式固定为48位,通过CMD线连续传输,数据线不参与。SD命令结构如下图示:由6个字节组成,字节1的最高2位固定为01、低6位为命令号(比如CMD16);字节2 ~ 5为命令参数(有的命令没有参数);字节6的高7位为CRC、最低位恒定为1

SD命令组成的详细说明如下:
- 起始位和终止位:命令的主体包含在起始位与终止位之间,它们都只包含一个数据位,起始位为 0,终止位为 1。
- 传输标志:用于区分传输方向,该位为 1 时表示命令,方向为主机传输到 SD 卡,该位为 0时表示响应,方向为 SD卡传输到主机。 - 命令主体内容包括命令、地址信息/参数和 CRC 校验三个部分
- 命令号:它固定占用 6bit,所以总共有 64个命令(代号:CMD0~CMD63),每个命令都有特定的用途,部分命令不适用于 SD 卡操作,只是专门用于 MMC卡或者SD I/O卡。
- 地址/参数:每个命令有 32bit地址信息/参数用于命令附加内容,例如,广播命令没有地址信息,这 32bit用于指定参数,而寻址命令这 32bit用于指定目标 SD卡的地址。
- CRC7 校验:长度为 7bit的校验位用于验证命令传输内容正确性,如果发生外部干扰导致传输数据个别位状态改变将导致校准失败,也意味着命令传输失败,SD卡不执行命令。

1.4 SD卡响应

SD卡命令的响应由SD卡向主机发出,部分命令要求SD卡作出响应,这些响应多用于反馈SD卡的状态。基本特性如下:

- SDIO总共有7个响应类型(代号:R1~R7),其中SD卡没有R4、R5类型响应。特定的命令对应有特定的响应类型,比如当主机发送CMD3命令时,可以得到响应R6。
- 与命令一样,SD卡的响应也是通过CMD线连续传输的。
- 根据响应内容大小可以分为短响应和长响应。短响应是48bit长度,只有R2类型是长响应,其长度为136bit。

SD的读写操作是以块为操作对象。先发送命令开始传输,然后传输数据块,传输完数据块紧接着传输CRC检验值。最好发送停止命令停止数据传输

1.5 SD卡的操作模式及切换

SD卡有多个版本,STM32控制器目前最高支持《Physical Layer Simplified Specification V2.0》定义的SD卡,STM32控制器对SD卡进行数据读写之前需要识别卡的种类:V1.0标准卡、V2.0标准卡、V2.0高容量卡或者不被识别卡。

SD卡系统定义了两种操作模式:卡识别模式和数据传输模式

在系统复位后,主机处于卡识别模式,寻找总线上可用的SDIO设备;同时,SD卡也处于卡识别模式,直到被主机识别到,即当SD卡接收到SEND_RCA(CMD3)命令后,SD卡就会进入数据传输模式,而主机在总线上所有卡被识别后也进入数据传输模式。

2. 硬件设计

D1指示灯用来提示系统运行状态,K_UP写入数据,K_DOWN读取数据,TFTLCD用来显示SD卡的容量、类型等信息,串口1用来打印调试信息

  • D1指示灯

  • USART1

  • K_UP/K_DOWN/K_LEFT/K_RIGHT

  • TFTLCD模块

  • TF卡

从以上电路图可以看出,SD卡支持SPI和SDIO模式,两种模式可以通过端子进行切换,P4端子与SD卡连接,SD端子与STM32F1的SDIO接口连接,IO端子与STM32F1的SPI2接口连接,本例程使用SDIO模式(将P4端子与SD端子短接)

3. 软件设计

3.1 STM32CubeMX设置

  • RCC设置外接HSE,时钟设置为72M

  • PC0设置为GPIO推挽输出模式、上拉、高速、默认输出电平为高电平

  • PA0设置为GPIO输入模式、下拉模式;PE2/PE3/PE4设置为GPIO输入模式、上拉模式

  • USART1选择为异步通讯方式,波特率设置为115200Bits/s,传输数据长度为8Bit,无奇偶校验,1位停止位

  • 激活FSMC,详细请参考TFTLCD显示章节的设置

  • 激活SDIO,选择4线SD模式,分频因子设为4,使能流控,其余默认设置

  • 最好激活CRC功能,以避免后续读写SD卡报CRC校验错误

  • 输入工程名,选择工程路径(不要有中文),选择MDK-ARM V5;勾选Generated periphera initialization as a pair of ‘.c/.h’ files per IP ;点击GENERATE CODE,生成工程代码

3.2 MDK-ARM编程

  • 在sdio.c文件下可以看到sdio初始化函数,同时在该文件下添加显示SD卡信息函数

void MX_SDIO_SD_Init(void){

  hsd.Instance = SDIO;

  hsd.Init.ClockEdge = SDIO_CLOCK_EDGE_RISING;  //上升沿

  hsd.Init.ClockBypass = SDIO_CLOCK_BYPASS_DISABLE; //比使用bypass,直接用HCLK分配得到SDIO_CK

  hsd.Init.ClockPowerSave = SDIO_CLOCK_POWER_SAVE_DISABLE;  //空闲时不关闭时钟电源

  hsd.Init.BusWide = SDIO_BUS_WIDE_1B;  //1位数据线

  hsd.Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_ENABLE; //开启硬件流控

  hsd.Init.ClockDiv = 4;    //4分频

  if (HAL_SD_Init(&hsd) != HAL_OK){

    Error_Handler();

  }

  if (HAL_SD_ConfigWideBusOperation(&hsd, SDIO_BUS_WIDE_4B) != HAL_OK){

    Error_Handler();    //初始化完成后使能宽总线(4位)模式

  }

}

//显示SD卡信息函数

void Show_SDMMC_Info(void){

    HAL_SD_CardCIDTypeDef SDCard_CID;

    HAL_SD_CardInfoTypeDef SDCard_INFO;

    uint64_t CardCap;                       //SD卡容量

    HAL_SD_GetCardCID(&hsd,&SDCard_CID);    //获取CID

    HAL_SD_GetCardInfo(&hsd,&SDCard_INFO);  //获取SD卡信息

    FRONT_COLOR=BROWN;  

    switch(SDCard_INFO.CardType)            //SD卡类型

    {

        case CARD_SDSC:

            if(SDCard_INFO.CardVersion == CARD_V1_X){

                LCD_ShowString(120,190,50,16,16,(uint8_t *)"SDSC V1");

                printf("Card Type: SDSC V1rn");

            }

            else if(SDCard_INFO.CardVersion == CARD_V2_X){

                LCD_ShowString(120,190,50,16,16,(uint8_t *)"SDSC V2");

                printf("Card Type: SDSC V2rn");

            }

            break;

        case CARD_SDHC_SDXC:

            LCD_ShowString(120,190,50,16,16,(uint8_t *)"SDHC");

            printf("Card Type: SDHCrn");

            break;

    }


    CardCap = (uint64_t)(SDCard_INFO.LogBlockNbr)*(uint64_t)(SDCard_INFO.LogBlockSize)/1024/1024;   //计算SD卡容量

    printf("Card ManufacturerID:%drn",SDCard_CID.ManufacturerID); //制造商ID

    printf("Card RCA:%drn",SDCard_INFO.RelCardAdd);       //卡相对地址

    printf("LogBlockNbr:%drn",SDCard_INFO.LogBlockNbr);   //逻辑块数量

    printf("LogBlockSize:%drn",SDCard_INFO.LogBlockSize); //逻辑块大小

    printf("Card Capacity:%d MBrn",(uint32_t)CardCap);    //显示容量

    printf("Card BlockSize:%drn",SDCard_INFO.BlockSize);  //块大小

    LCD_ShowNum(120,210,CardCap,4,16);

    LCD_ShowString(160,210,50,16,16,(uint8_t *)"MB");

}

添加按键驱动文件key.c和key.h,参考按键输入例程

添加TFTLCD驱动文件tftlcd.c 和tftlcd.h,参考TFTLCD显示例程

在main.c文件中编写SD卡读写测试代码

uint8_t Buffer_Tx[512],Buffer_Rx[512] = {0};

uint32_t i;

int main(void){

    uint8_t key;

    HAL_Init();

    SystemClock_Config();

    MX_GPIO_Init();

    MX_FSMC_Init();

    MX_SDIO_SD_Init();

    MX_USART1_UART_Init();

    MX_CRC_Init();

  /* USER CODE BEGIN 2 */

    TFTLCD_Init();  

    FRONT_COLOR=BROWN;

    LCD_DrawRectangle(25,25,215,135);

    FRONT_COLOR=RED;                    

    LCD_ShowString(30,30,200,16,16,(uint8_t *)"ANDYXI STM32");  

    LCD_ShowString(30,50,200,16,16,(uint8_t *)"STM32CubeMX");   

    LCD_ShowString(30,70,200,16,16,(uint8_t *)"SDIO TEST");

    FRONT_COLOR=BLACK;

    LCD_ShowString(30,90,200,16,16, (uint8_t *)"K_U:ReadSD K_D:WriteSD");

    LCD_ShowString(30,110,200,16,16,(uint8_t *)"K_R:EaseSD K_L:None");

    FRONT_COLOR=BLUE;

    LCD_ShowString(30,170,200,16,16,(uint8_t *)"SD Card Information");

    LCD_ShowString(30,190,80,16,16,(uint8_t *)"Card Type:     ");

    LCD_ShowString(30,210,80,16,16,(uint8_t *)"Card Capa:");    

    Show_SDMMC_Info();

    memset(Buffer_Tx,0x15,sizeof(Buffer_Tx));   

  /* USER CODE END 2 */

  while (1){

    key = KEY_Scan(0);

    switch(key)

    {

        case KEY_RIGHT_PRES:

            if(HAL_SD_Erase(&hsd,0,1) == HAL_OK){

                while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER);

                printf("rnErase Block Success!rn");

                LCD_ShowString(30,250,200,16,16,(uint8_t *)"Erase Block Success!");

            }

            else{

                printf("rnErase Block Failed!rn");  

                LCD_ShowString(30,250,200,16,16,(uint8_t *)"Erase Block Failed!");              

            }

            break;

        case KEY_UP_PRES:

            if(HAL_SD_ReadBlocks(&hsd,Buffer_Rx,0,1,0xffffffff) == HAL_OK){

                while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER);

                printf("rnRead Block Success!rn");

                for(i=0;i                    printf("%02x ",Buffer_Rx[i]);

                printf("rn");

                LCD_ShowString(30,250,200,16,16,(uint8_t *)"Read Block Success!");

            }else{

                printf("rnRead Block Failed!rn");

                LCD_ShowString(30,250,200,16,16,(uint8_t *)"Read Block Success!");              

            }

            break;    

        case KEY_DOWN_PRES:

            if(HAL_SD_WriteBlocks(&hsd,Buffer_Tx,0,1,0xffffffff) == HAL_OK){

                while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER);

                printf("rnWrite Block Success!rn");

推荐阅读

史海拾趣

Benchmarq Microelectronics Inc公司的发展小趣事

在国内市场取得一定成绩后,Benchmarq Microelectronics Inc开始积极拓展国际市场。公司积极参加国际电子展会和交流活动,与国际同行建立合作关系,将产品打入国际市场。同时,公司还加强品牌建设,提升品牌知名度和美誉度。通过广告宣传、媒体报道等多种方式,公司成功塑造了专业、可靠的品牌形象,赢得了客户的信任和认可。

DILABS公司的发展小趣事

随着全球环保意识的提高,DILABS开始注重绿色生产。他们引入了先进的环保技术和设备,确保在生产过程中最大限度地减少废弃物和污染物的排放。同时,DILABS还积极推动电子产品的环保设计,帮助客户实现绿色采购和可持续发展。

Ametek公司的发展小趣事

随着全球环保意识的提高,DILABS开始注重绿色生产。他们引入了先进的环保技术和设备,确保在生产过程中最大限度地减少废弃物和污染物的排放。同时,DILABS还积极推动电子产品的环保设计,帮助客户实现绿色采购和可持续发展。

Aerotronics Marketing Inc公司的发展小趣事

Aerotronics Marketing Inc公司在市场定位上独具慧眼,准确抓住了电子行业中无人机市场的增长趋势。公司制定了一系列精准的营销策略,包括线上线下推广、行业展会展示、合作伙伴计划等,有效提升了品牌知名度和市场占有率。同时,公司还注重客户关系管理,通过提供优质的售后服务和技术支持,赢得了客户的信任和忠诚。

Gauthier Connectique公司的发展小趣事
比较替代产品的灵敏度、功耗、稳定性等性能参数,选择性能更优的产品。
弘凯光电(BRIGHTEK)公司的发展小趣事

随着技术的不断进步和市场的逐步打开,弘凯光电开始将业务拓展至全球范围。公司的客户群迅速扩大,遍布60多个国家和地区,同时在欧洲、北美、南美、东南亚和中东地区均设立了经销网点。此外,弘凯光电还积极寻求国际认证,以证明其产品的品质和可靠性。通过取得ISO9001质量保证体系、ISO14001国际环境管理体系认证等一系列认证,公司进一步提升了品牌形象和市场竞争力。

问答坊 | AI 解惑

应届生求职大礼包

应届生求职大礼包…

查看全部问答>

多串口卡IO地址/中断由BIOS指定

芯惠通的JetCard1404的每个串口的IO地址和中断在哪能找到? 打电话问客服,说是在BIOS里面有,具体的哪儿也不知道 我在设备管理器里面找,由多串口卡扩展出来的4个串口都没有“资源“这一项! 请问到底怎么找啊?因为要用C语言在TC里面编程,找 ...…

查看全部问答>

直流电机的问题,,,求助

用AT89C2051来使四个继电器来分别控制四个直流电机的同时正反转,而且还要用到二极管来演示电机的工作情况,具体应该怎办??…

查看全部问答>

2410init.s中的疑问和分析

我发现在2410init.s中的“拷贝nand中内容到SDRAM”的这个代码是有问题的。理由如下:      我们知道nand中存在坏块的,但是第一个block肯定是好块,这个可以确认。 如果我我烧录的bin文件是低于steppingstone 4KB大小,那么 ...…

查看全部问答>

USB Host端 怎样读取多重配置描述符

各位大虾, 我现在在做usb host端程序,处理的设备有两种配置,1。U盘,2,hid+audio, 现在默认配置是u盘功能,现在我想要在设备插入时,枚举过程中,选择配置2,hid+audio, 该怎样的命令流程,希望大虾们能指点一下,谢谢! PS:现在固定的枚举 ...…

查看全部问答>

wince上 如何实现端口和进程关联

RT 谁做个这方面的 能否提供点思路 或者源码 PC上关于这方面的代码都挺多的,在Wince上没有找到相关的资料,迷茫 据我分析 PC上的方法在Wince上面都无法实现的 PC上的方法: 1. 通过IP Helper Function中的几个未公开的函数实现 All ...…

查看全部问答>

请问电压驻波比

请问 input return loss 这个dB是越大越好,还是越小越好呢 output return loss 这个dB是越大越好,还是越小越好呢…

查看全部问答>

谁能帮我找下这款接收器的资料说明,有图。

谁能帮我找下这款接收器的资料说明,有图。型号是NX3BU4 封装是PBGA88 如图:…

查看全部问答>