当前位置: 首页 > news >正文

STM32H743-ARM例程13-SDIO

目录

  • 实验平台
  • SDIO
    • SDIO简介
    • SD卡物理结构
    • SDIO总线
      • 命令
      • 响应
    • SD卡操作模式
      • SD卡识别模式
      • 数据传输模式
    • SDMMC
      • SDMMC框图
      • SDMMC时钟
  • 原理图
  • STM32CubeMX生成工程
  • 实验代码
  • 实验现象

实验平台

硬件:银杏科技GT7000双核心开发板-ARM-STM32H743XIH6,银杏科技iToolXE仿真器
软件:最新版本STM32CubeH7固件库,STM32CubeMX v6.10.0,开发板环境MDK v5.35,串口工具putty

SDIO

SDIO简介

  SD 卡(Secure Digital Memory Card) 在我们生活中已经非常普遍了,控制器对 SD 卡进行读写通信操作一般有两种通信接口可选,一种是 SPI 接口,另外一种就是 SDIO 接口。SDIO 全称是安全数字输入/输出接口,多媒体卡(MMC)、SD 卡、SD I/O 卡都有 SDIO 接口。 MMC 卡可以说是 SD 卡的前身,现阶段已经用得很少。STM32H743x系列控制器有两个SDIO主机接口,它可以与MMC卡、SD卡、SD I/O卡以及CE-ATA设备进行数据传输。SD I/O卡是在SD内存卡接口的基础上发展起来的外设接口,SDIO接口兼容以前的SD内存卡,并且可以连接SDIO接口的设备,目前根据SDIO协议的SPEC,SDIO接口支持的设备总类有蓝牙,网卡,电视卡等。CE-ATA是一种使用MMC接口界面,ATA指令集的接口,CE-ATA相较于ATA节省了很多管脚,有助于简化消费电子(CE)和ATA硬盘的结合,例如轻薄笔记本硬盘设计的硬盘高速通讯接口。
  各种媒体卡都有自己的系统规范,想了解的读者可以参考他们的官网:多媒体卡协会网站www.mmca.org、SD卡协会网站www.sdcard.org、CE-ATA工作组网站www.ce-ata.org。
  到目前为止,SDIO接口的设备整体概括图如下图所示:
在这里插入图片描述
  本章实验将针对SD卡进行读写测试,STM32H743x系列控制器只支持SD卡规范版本2.0, 即只支持标准容量SD和高容量SDHC标准卡,不支持超大容量SDXC标准卡,所以可以支持的最高卡容量是32GB。

SD卡物理结构

在这里插入图片描述

  SD卡也有着自己的寄存器,但是并不能直接进行读写,只能通过对于的命令访问,SDIO协议定义64个命令来实现一些特殊功能,SD卡接收到命令后,根据接受到的命令对内部寄存器进行修改,
下图就是我们对SD卡发送命令的数据通道:
在这里插入图片描述

SDIO总线

  SD卡一般支持两种接口,一种SDIO,另一种是SPI,本章实验我们用的SDIO接口操作方式,SPI我们后续章节我详细介绍。另外,STM32H743x系列控制器的SDIO是不支持SPI通信模式的,如果需要用到SPI通信只能使用SPI外设。
  SD总线上的通信以命令/数据的比特流为基础,起始位表示数据的传输的开始,终止位表示数据传输的结束。

  • 命令(Command):命令是开启一个操作的标志。命令可以从主机发送到单个卡(寻址命令),也可以发送到所有已连接的卡(广播命令),命令通过CMD线以串行方式传输
  • 响应(Response):响应是卡收到命令后,需要发送到主机的命令的应答,响应通过CMD线以串行方式传输
  • 数据:数据的传输是双向的,通过Data线传输。
    在这里插入图片描述

命令

  SD命令由主机发出,以广播命令和寻址命令为例,广播命令是针对与SD主机总线连接的所有从设备发送的,寻址命令是指定某个地址设备进行命令传输。命令字段的组成如下:
在这里插入图片描述

  • 起始位是1bit的 0
  • 传输标志是1bit的 1,该位为1时表示命令,方向为主机传输到SD卡,该位为0时表示响应,方向为SD卡传输到主机。
  • 命令号长度是6bit,代表命令的编号它固定占用6bit,例如对于CMD17(代号:CMD0~CMD63),这6bit就应该是 17 的二进制表示 010001,每个命令都有特定的用途, 部分命令不适用于SD卡操作,只是专门用于MMC卡或者SD I/O卡
  • 参数/地址长度是32bit,不同命令有不同的参数要求
  • CRC7是校验码,长度是7bit,需要根据命令包的 [47:8] 位来计算 (起始位、方向位、命令号、参数都参与 CRC7 的计算)
  • 结束为是1bit的 1

命令类型:
SD 命令有 4 种类型:

  • 无响应广播命令(bc),发送到所有卡,不返回任务响应;
  • 带响应广播命令(bcr),发送到所有卡,同时接收来自所有卡响应;
  • 寻址命令(ac),发送到选定卡,DAT 线无数据传输;
  • 寻址数据传输命令(adtc),发送到选定卡,DAT线有数据传输。

详细命令描述本章不再赘述,有兴趣读者可以参考文章【SDIO】SD2.0协议分析总结(三)-- SD卡相关命令介绍。

响应

  响应 (response) 的方向都是从SD卡向主机发出,发生在 sdcmd 信号上,且只可能紧随一个命令后。部分命令要求SD卡作出响应,这些响应多用于反馈SD卡的状态。SDIO响应有7种类型 (R1-7)。其中 SD卡没有 R4、R5类型响应。各个类型响应具体情况如下表所示。

在这里插入图片描述

SD卡操作模式

SD卡识别模式

  在系统复位后,主机处于卡识别模式,认其工作电压范围,识别SD卡类型, 并且获取SD卡的相对地址(卡相对地址较短,便于寻址)。
  在主机和SD卡开始通信时,主机可能不知道SD卡支持的电压,SD卡也可能不知道它是否支持当前提供的电压。主机发出一个复位命令(CMD0),同时假定它电压可能被SD卡支持。为了验证电压,SD卡标准V2.0版本文档中定义了以下新命令,SEND_IF_COND(CMD8)命令就是用于验证卡接口操作条件的(主要是电压支持)。卡会根据命令的参数来检测操作条件匹配性,如果卡支持主机电压就产生响应,否则不响应。
  SD_SEND_OP_COND (ACMD41)被设计为为主机提供一种机制来识别和拒绝与主机所提供的VDD范围不匹配的卡。不能在指定范围内进行数据传输的SD卡,应放弃总线操作,进入Inactive 状态。OCR寄存器定义了相关的电压等级。卡在响应ACMD41之后进入准备状态,不响应ACMD41的卡为不可用卡,进入无效状态。
注意:ACMD41是特定应用程序的命令,因此APP_CMD (CMD55)应该始终在ACMD41之前被发送。在idle_state中用于CMD55的RCA将是SD卡的默认值(RCA = 0 x0000)。

在这里插入图片描述
  ALL_SEND_CID(CMD2)用来控制所有卡返回它们的卡识别号(CID),处于准备状态的卡在发送CID之后就进入识别状态。之后主机就发送SEND_RELATIVE_ADDR(CMD3)命令, 让卡自己推荐一个相对地址(RCA)并响应命令。这个RCA是16bit地址,而CID是128bit地址,使用RCA简化通信。卡在接收到CMD3并发出响应后就进入数据传输模式, 并处于待机状态,主机在获取所有卡RCA之后也进入数据传输模式。

数据传输模式

  在数据模式下我们可以对SD卡的存储块进行读写访问操作。SD卡数据包有两种格式,一种是常规数据(8bit宽),它先发低字节再发高字节,而每个字节则是先发高位再发低位,4线传输示意如图 8位宽数据包传输。
  当使用4线模式传输8-bit结构的数据时,数据仍按MSB先发送的原则,DAT[3:0]的高位发送高数据位,低位发送低数据位。硬件支持的情况下,使用4线传输可以提升传输速率。
在这里插入图片描述

  另一种数据包发送格式是宽位数据包格式,宽数据(SD内存寄存器):宽数据以MSB位优先发送。一次按512字节传输,主机发出ACMD13命令后SD卡将SSR寄存器内容通过DAT线发送给主机。
在这里插入图片描述
  只有SD卡系统处于数据传输模式下才可以进行数据读写操作。数据传输模式下可以将主机SD时钟频率设置为FPP,默认最高为25MHz,频率切换可以通过CMD4命令来实现。数据传输模式下,SD卡状态转换过程见下图
在这里插入图片描述
  CMD7用于选择一张SD卡并将其置于传输状态,在同一时刻只能有一张卡处于传输状态。如果先前选择的卡处于传输状态,它与主机的连接将被释放,它将返回到待机状态。当CMD7以保留的相对卡片地址“0x0000”发出时,所有卡片都被退回到待机状态。
  据传输模式下的数据通信都是主机和目标卡之间通过寻址命令点对点进行的。卡处于传输状态下可以通过命令对卡进行数据读写、擦除。CMD12可以中断正在进行的数据通信,让卡返回到传输状态。CMD0和CMD15会中止任何数据编程操作, 返回卡识别模式,这可能导致卡数据被损坏。

SDMMC

SDMMC框图

SDMMC 由三部分组成:

  • AHB 从接口访问 SDMMC 适配器寄存器,并且生成中断信号和 IDMA 控制信号。
  • SDMMC 适配器块提供特定于 MMC/SD/SD I/O 卡的所有功能,如时钟生成单元、命令
    和数据传输。
  • 内部 DMA(IDMA)模块及其 AHB 主接口。
    在这里插入图片描述
      图中,右侧的信号为SDMMC对外引脚(信号),如下图所示:
    在这里插入图片描述

  SDMMC_CK、SDMMC_D[3:0]、SDMMC_CMD这几根线即可正常驱动SD卡,复位后默认情况下SDMMC_D0用于数据传输。初始化后主机可以改变数据总线的宽度(通过ACMD6命令设置)。
SDMMC_CMD有两种操作模式:
①用于初始化时的开路模式(仅用于MMC版本V3.31或之前版本)
②用于命令传输的推挽模式(SD/SD I/O卡和MMC V4.2在初始化时也使用推挽驱动)
  STM32H7支持DS(Default Speed)、HS(High Speed),SDR以及DDR50等多种模式,见表格 SDIO总线速度模式。
在这里插入图片描述
  SDR表示单倍速率(时钟单边沿采样),DDR表示双倍速率(时钟双边沿采样),由表可知,STM32H7支持最高的SDR104总线速度模式,理论上传输速度最高可达104MB/s(实际上由于IO口速度无法达到208Mhz,所以,是达不到104MB/s的传输速度的)。这里的DS、HS、SDR12、SDR25、DDR50、SDR50、SDR104等速度模式,实际上是超高速I卡所支持的一些速度等级。
  我们的SD卡接口是3.3V供电的,所以,仅支持DS和HS模式(最高25MB/s的传输速度)。

SDMMC时钟

  在SDMMC功能框图中我们可以发现,SDMMC总共有3个时钟,分别是:
卡时钟(SDMMC_CK): SDMMC(Secure Digital MultiMediaCard)控制器输出的时钟信号,用于同步主机与SD卡/设备之间的通信。每个时钟周期在命令和数据线上传输1位命令或数据‌。标准模式下:频率范围≤25MHz,卡识别:‌频率范围≤400kHz。
SDMMC适配器内核时钟(sdmmc_ker_ck): SDMMC控制器的内核时钟输入,用于驱动SDMMC适配器内部逻辑。它通常与AHB总线时钟(sdmmc_hclk)协同工作,为SDMMC控制器提供时钟基准‌,默认选择来自pll1_q_ck,其频率一般为240Mhz,并用于产生SDMMC_CK时钟。
AHB3总线接口时钟(sdmmc_hclk): SDMMC控制器的AHB总线接口时钟,用于驱动SDMMC控制器的AHB总线接口。其频率通常为HCLK(AHB总线时钟)的一半。

原理图

在这里插入图片描述

STM32CubeMX生成工程

我们参考前面章节STM32H743-结合CubeMX新建HAL库MDK工程,打开CubeMX软件,重复步骤不再展示,我们来看配置SDMMC部分如下图所示:

在这里插入图片描述
在这里插入图片描述

实验代码

1. 主函数

在这里插入代码片`int main(void)
{HAL_Init();SystemClock_Config();MX_GPIO_Init();MX_SDMMC1_SD_Init();MX_USART6_UART_Init();uart6.printf("Micro SD Card Test... \r\n"); uint8_t read_buf[512];      // 读数据缓存uint8_t write_buf[512];     // 写数据缓存HAL_SD_ConfigWideBusOperation(&hsd1, SDMMC_BUS_WIDE_4B);/* SD卡状态 */int sdcard_status = 0;HAL_SD_CardCIDTypeDef sdcard_cid;/* 获取SD卡状态 */sdcard_status = HAL_SD_GetCardState(&hsd1);// 处于数据传输模式的传输状态if(sdcard_status == HAL_SD_CARD_TRANSFER){uart6.printf("SD card init ok!\r\n\r\n");// 打印SD卡基本信息uart6.printf("SD card information! \r\n");// 容量信息uart6.printf("CardCapacity(Byte): %llu \r\n",((unsigned long long)hsd1.SdCard.BlockSize * hsd1.SdCard.BlockNbr));// 块大小 uart6.printf("CardBlockSize(Byte): %d \r\n", hsd1.SdCard.BlockSize);// 有多少个块uart6.printf("CardBlockNumber: %d \r\n", hsd1.SdCard.BlockNbr); uart6.printf("RCA: %d \r\n", hsd1.SdCard.RelCardAdd);   uart6.printf("CardType: %d \r\n", hsd1.SdCard.CardType);// 读取并打印SD卡的CID信息HAL_SD_GetCardCID(&hsd1, &sdcard_cid);// 制造商uart6.printf("ManufacturerID: %d \r\n",sdcard_cid.ManufacturerID);}else{uart6.printf("SD card init fail! \r\n" );return 0;}/* 读取未操作之前的数据 */uart6.printf("------------------- Read SD card block data Test ------------------\r\n");/*读一个扇区的数据:0: 从第0个扇区开始。1:读一个扇区的数据。0xffff:等待时间。note:也就是只读了第0个扇区。*/sdcard_status = HAL_SD_ReadBlocks(&hsd1, (uint8_t *)read_buf, 0, 1, 0xffff);if(sdcard_status == HAL_OK){ uart6.printf("Read block data ok! \r\n");for(int i = 0; i < 512; i++){uart6.printf("0x%02x ", read_buf[i]);if((i+1)%16 == 0){uart6.printf("\r\n");}}}else{uart6.printf("Read block data fail! status = %d \r\n", sdcard_status);}/* 向SD卡块写入数据 */uart6.printf("------------------- Write SD card block data Test ------------------\r\n");/* 填充缓冲区数据 */for(int i = 0; i < 512; i++){write_buf[i] = i % 256;}// 开始写入数据/*写一个扇区的数据:0: 从第0个扇区开始。1:写一个扇区的数据。0xffff:等待时间。note:也就是只写了第0个扇区。*/sdcard_status = HAL_SD_WriteBlocks(&hsd1, (uint8_t *)write_buf, 0, 1, 0xffff);if(sdcard_status == HAL_OK){ /* 传输完成不代表写入完成,因此要等待SD卡状态变为可传输状态。擦除操作也是一样。 */uart6.printf("Writing block data. state = %d \r\n", HAL_SD_GetCardState(&hsd1));while (HAL_SD_GetCardState(&hsd1) == HAL_SD_CARD_PROGRAMMING);uart6.printf("Write block data ok,state = %d \r\n", HAL_SD_GetCardState(&hsd1));}else{uart6.printf("Write block data fail! status = %d \r\n", sdcard_status);}/* 读取写入之后的数据 */uart6.printf("------------------- Read SD card block data after Write ------------------\r\n");sdcard_status = HAL_SD_ReadBlocks(&hsd1, (uint8_t *)read_buf, 0, 1, 0xffff);if(sdcard_status == HAL_OK){ uart6.printf("Read block data ok! \r\n");for(int i = 0; i < 512; i++){uart6.printf("0x%02x ", read_buf[i]);if((i+1)%16 == 0){uart6.printf("\r\n");}}}else{uart6.printf("Read block data fail! status = %d \r\n", sdcard_status);}/* 擦除SD卡块 */uart6.printf("------------------- Block Erase -------------------------------\r\n");/*擦除512个扇区的数据:0: 从第0个扇区开始。1:一直擦除到512扇区。note:擦除第0到第512个扇区数据,也包括0512,也就是一共512个。*/sdcard_status = HAL_SD_Erase(&hsd1, 0, 512);// 等待擦除完毕if (sdcard_status == HAL_OK){   uart6.printf("Erasing block. state = %d \r\n", HAL_SD_GetCardState(&hsd1));while (HAL_SD_GetCardState(&hsd1) == HAL_SD_CARD_PROGRAMMING);uart6.printf("Erase block ok state = %d \r\n", HAL_SD_GetCardState(&hsd1));}else{uart6.printf("Erase block fail! status = %d \r\n", sdcard_status);}/* 读取擦除之后的数据 */uart6.printf("------------------- Read SD card block data after Erase ------------------\r\n");sdcard_status = HAL_SD_ReadBlocks(&hsd1, (uint8_t *)read_buf, 0, 1, 0xffff);if(sdcard_status == HAL_OK){ uart6.printf("Read block data ok \r\n" );for(int i = 0; i < 512; i++){uart6.printf("0x%02x ", read_buf[i]);if((i+1)%16 == 0){uart6.printf("\r\n");}}}else{uart6.printf("Read block data fail! status = %d \r\n", sdcard_status);}uart6.printf("------------------- Over ------------------\r\n");while (1){}}

2. SDMMC初始化函数

void MX_SDMMC1_SD_Init(void)
{hsd1.Instance = SDMMC1;hsd1.Init.ClockEdge = SDMMC_CLOCK_EDGE_RISING;                                                          //上升沿hsd1.Init.ClockPowerSave = SDMMC_CLOCK_POWER_SAVE_DISABLE;                              //空闲时不关闭时钟电源hsd1.Init.BusWide = SDMMC_BUS_WIDE_4B;                                                                              //4位数据线hsd1.Init.HardwareFlowControl = SDMMC_HARDWARE_FLOW_CONTROL_DISABLE;        //关闭硬件流控制hsd1.Init.ClockDiv = 10;                                                                                                                    //分频if (HAL_SD_Init(&hsd1) != HAL_OK){Error_Handler();}}

3. SD卡写数据块函数

HAL_SD_WriteBlocks(SD_HandleTypeDef *hsd, uint8_t *pData, uint32_t BlockAdd, uint32_t NumberOfBlocks, uint32_t Timeout)//*hsd:指向SD卡的指针
//*pData:指向要写入的数据的指针
//BlockAdd:数据块地址
//NumberOfBlocks:写入的块数
//Timeout:写入超时设置

4. SD读数据块函数

HAL_SD_ReadBlocks(SD_HandleTypeDef *hsd, uint8_t *pData, uint32_t BlockAdd, uint32_t NumberOfBlocks, uint32_t Timeout)//*hsd:指向SD卡的指针
//*pData:指向数据读取后存放区的指针
//BlockAdd:数据块地址
//NumberOfBlocks:读取的块数
//Timeout:读取超时设置

5. SDMMC外设管理结构体

typedef struct 
{SD_TypeDef *Instance;                       /*!< SDMMC 寄存器基地址*/SD_InitTypeDef Init;                         /*!< SD 初始化结构体*/HAL_LockTypeDef Lock;                     /*!< SD 锁资源*/uint32_t *pTxBuffPtr;                        /*!< 存放发送数据地址的指针*/uint32_t TxXferSize;                         /*!< 发送数据的大小 */uint32_t *pRxBuffPtr;                        /*!< 存放接受数据地址的指针*/uint32_t RxXferSize;                         /*!< 接受数据的大小*/__IO uint32_t Context;                        /*!< SDMMC 的工作模式 */__IO HAL_SD_StateTypeDef State;             /*!< SD 卡的状态值*/__IO uint32_t ErrorCode;                      /*!< SD 错误操作返回值*/HAL_SD_CardInfoTypeDef SdCard;             /*!< SD 卡的信息*/uint32_t CSD[4];                             /*!< SD 卡的 CSD 寄存器值*/uint32_t CID[4];                             /*!< SD 卡的 CID 寄存器值*/
} SD_HandleTypeDef;

6. SDMMC 数据初始化结构体

typedef struct 
{uint32_t DataTimeOut;       // 数据传输超时uint32_t DataLength;        // 数据长度uint32_t DataBlockSize;     // 数据块大小uint32_t TransferDir;        // 数据传输方向uint32_t TransferMode;      // 数据传输模式uint32_t DPSM;            // 数据路径状态机
} SDMMC_DataInitTypeDef;

7. SD卡信息结构体

typedef struct
{uint32_t CardType;                                /*卡种类*/uint32_t CardVersion;                              /*卡版本*/uint32_t Class;                                    /*卡类*/uint32_t RelCardAdd;                              /*相对卡地址*/uint32_t BlockNbr;                                /*以块为单位指定卡容量*/uint32_t BlockSize;                                /*一个块大小(以字节为单位)*/uint32_t LogBlockNbr;                             /*以块为单位指定卡逻辑容量*/uint32_t LogBlockSize;                             /*逻辑块大小(以字节为单位)*/uint32_t CardSpeed;                               /*卡速度*/
}HAL_SD_CardInfoTypeDef;

实验现象

我们插入SD卡,运行程序,终端显示出SD卡的相关信息,并输出写入SD卡的数据。
在这里插入图片描述

http://www.dtcms.com/a/423065.html

相关文章:

  • 求职招聘小程序:轻量化场景下的核心功能解析
  • 玳瑁的嵌入式日记---0929(ARM--ADC)
  • 国外seo做的好的网站wordpress 登陆
  • 东欣建设集团网站设计素材网站上的素材可以商用吗
  • OD C卷 - 有效子字符串
  • 无人机定点派送技术要点与运行方式
  • 实战项目:活动报名小程序-应用内数据库CRUD
  • 从零构建能自我优化的AI Agent:Reflection和Reflexion机制对比详解与实现
  • 能见度监测站的优点是什么
  • 网站建设需要代码哪些网站可以做国外生意
  • 建网站不想用怎样撤销有关于做茗茶的网站
  • PDDA-CY5用于制备荧光标记聚合物、纳米载体或功能化材料
  • 信息安全基础知识:10入侵检测技术
  • 电机控制:前馈与反馈
  • 创网站永久免费建站如何让百度不收录网站
  • Frames:Runway推出的AI图像生成模型,提供前所未有的风格控制和视觉一致性
  • Sentinel 深度解析:限流与熔断降级的微服务稳定性保障实践
  • wordpress 网站图标软件开发一个月多少工资
  • 计算机视觉(opencv)——基于 dlib 人脸对齐
  • 古代游戏中的社交密码
  • (免费分享)基于python的飞机大战游戏
  • 虚幻引擎UE5专用服务器游戏开发-21 连招技能动画蒙太奇播放
  • 洛阳市建设厅网站网站优化布局
  • 20250929给PRO-RK3566开发板在Buildroot系统下裁剪内核【已关闭摄像头ov4689为例子】
  • Python 豆瓣TOP250 爬虫类讲解
  • 建一个网站迈年广电基础设施建设官方网站
  • Microsoft Access SQL 查询中的通配符
  • 服务好的高端网站建设企业网站开发一般用的什么架构
  • Trae添加mysql mcp AI编程 链接数据库
  • LeetCode 199. 二叉树的右视图