一、SDMMC简介
MMC:MMC就是MultiMediaCard的缩写,即多媒体卡
SD:SD卡为Secure Digital Memory Card, 即安全数码卡
SDIO:SD Input Output 带有输入输出接口,SDIO是在SD标准上定义了一种外设接口
SD种类
SD卡:<=2GB
SDHC卡(SD High Capacity,大容量SD卡):4GB~32GB
SDXC卡(SD eXtended Capacity):64GB~2TB。
SD管脚图
我们现在常用的是Micro SD卡,尺寸非常小的,其管脚图如下。


SD卡的接口可以支持SD卡模式和SPI模式两种操作模式。
SD模式:采用6线制,使用CLK、CMD、DAT0~DAT3。其中CLK为时钟线,CMD为命令控制线,DAT0~DAT3为数据线,允许4线的高速数据传输;
SPI模式:通用的SPI通道接口,使用CS、CLK、DI、DO进行数据通信。
SD模式的数据传输速度比SPI模块要快。我们这一章主要讲通过SD模式控制。
SD卡相关寄存器
SD卡内部有7个寄存器,其中OCR,CID,CSD和SCR寄存器保存卡的配置信息;RCA寄存器保存着通信过程中卡当前暂时分配的地址;卡状态(Card Status)和SD状态(SD Status)寄存器保存着卡的状态,这两个寄存器的内容与通信模式(SD模式或SPI模式)相关

命令传输
命令是用于启动操作的令牌。命令在CMD线上以串行的方式传输。所以命令都为固定长度48位。命令路径以半双工模式运行,因此可以发送和接收和响应。
最大支持64个命令:CMD0~CMD63(其中CMD57~63是保留的),另外还有ACMD应用命令。
(本章不打算详细介绍SD各条命令的含义,而且我们编写程序的时候也不会用到,只需大概了解一下即可)


响应是一个令牌,作为对先前接收命令的应答,从卡发送到主机,响应在CMD线上以串行方式传输。
SDMMC支持两种响应类型,48位短响应和136位长响应。两种类型均使用CRC错误检验。

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

SD卡识别流畅图和数据传输流程图:
(本章不打算详细介绍SD的状态图,编写程序的时候也不会用到,只需大概了解一下即可)


二.示例程序
复制串口printf的工程,修改文件夹名。击STM32F746I.ioc打开STM32cubeMX的工程文件重新配置,SDMMC选择四线SD模式。

此时SDMMC对应的管脚也被选中。

配置SDMMC的时钟为48MHz(最高为48MHz)。

SDMMC配置参数只有一个分频因子,此处为默认0,不修改。

开启SDMMC接收和发送DMA。

特别注意,开启DMA后必须开启SDMMC中断,否则不能判断DMA传输是否完成,程序一直等待。且SDMMC中断的抢占优先级必须比SDMMC DMA中断高。

生成报告以及代码,编译程序。在sdmmc.c文件中可以看到SDMMC初始化函数。在stm32f7xx_hal_sd.h头文件中可以看SD卡的操作函数。
在main.c文件前面添加变量,Status保存程序返回状态,Buffer_Tx,Buffer_Rx存储读写数据。
| 1 | /* USER CODE BEGIN PV */ |
| 2 | /* Private variables ---------------------------------------------------------*/ |
| 3 | HAL_SD_ErrorTypedef Status; |
| 4 | uint32_t Buffer_Tx[512/4], Buffer_Rx[512/4]; |
程序中用的memset函数填充缓存数据,所以要添加字符头文件。
| 1 | /* USER CODE BEGIN Includes */ |
| 3 | /* USER CODE END Includes */ |
在main函数中添加下面应用程序。程序中首先输出SD卡信息,然后进行读写擦除块等操作。
| 01 | /* USER CODE BEGIN 2 */ |
| 02 | printf(" Warning: this program may erase all the TF card data. rn"); |
| 04 | printf("rn Initialize SD card successfully!rnrn"); |
| 05 | printf(" SD card information! rn"); |
| 06 | printf(" CardCapacity : %llu rn",SDCardInfo1.CardCapacity ); |
| 07 | printf(" CardBlockSize : %d rn",SDCardInfo1.CardBlockSize); |
| 08 | printf(" RCA : %d rn",SDCardInfo1.RCA); |
| 09 | printf(" CardType : %d rn",SDCardInfo1.CardType); |
| 11 | /*------------------- Block Write --------------------------*/ |
| 12 | memset(Buffer_Tx,0x15,sizeof(Buffer_Tx)); |
| 13 | if(HAL_SD_WriteBlocks_DMA(&hsd1, Buffer_Tx, 0, 512, 1) == SD_OK) |
| 15 | Status = HAL_SD_CheckWriteOperation(&hsd1, (uint32_t)100000000); |
| 18 | printf("rn Write block successfully!rn"); |
| 21 | printf("%02x:0x%08x ",i,Buffer_Tx[i]); |
| 26 | printf("rn Write block fail!rn"); |
| 29 | /*------------------- Block Read --------------------------*/ |
| 30 | if(HAL_SD_ReadBlocks_DMA(&hsd1, Buffer_Rx, 0, 512, 1) == SD_OK) |
| 32 | Status = HAL_SD_CheckReadOperation(&hsd1, 0xFFFF); |
| 35 | printf("rn Read block successfully!rn"); |
| 38 | printf("%02x:0x%08x ",i,Buffer_Rx[i]); |
| 43 | printf("rn Read block fail!rn"); |
| 46 | /*------------------- Block Erase -------------------------------*/ |
| 47 | Status = HAL_SD_Erase(&hsd1, 0, 512); |
| 49 | printf("rn Erase block successfully!rn"); |
| 51 | printf("rn Erase block fail!rn"); |
| 53 | /*------------------- Block Read --------------------------*/ |
| 54 | if(HAL_SD_ReadBlocks_DMA(&hsd1, Buffer_Rx, 0, 512, 1) == SD_OK) |
| 56 | Status = HAL_SD_CheckReadOperation(&hsd1, 0xFFFF); |
| 59 | printf("rn Read block successfully!rn"); |
| 62 | printf("%02x:0x%08x ",i,Buffer_Rx[i]); |
| 67 | printf("rn Read block fail!rn"); |
在SDMMC接口初始化函数MX_SDMMC1_SD_Init()中,调用HAL_SD_Init(&hsd1, &SDCardInfo1)函数初始SD卡(有兴趣的少年可以对比上面的状态图看看SD卡的初始化程序),将SD卡的信息保存在SDCardInfo1结构体中。
SDCardInfo1结构体类型为HAL_SD_CardInfoTypedef,在stm32f7xx_hal_sd.h中可以看到结构体的成员变量。
其中SD_csd,SD_cid分别对应SD卡的CSD,CID寄存器。CardCapacity为SD卡容量大小,CardBlockSize为SD卡块大小,CardType为SD类型。查看HAL_SD_CSDTypedef,HAL_SD_CIDTypedef两个类型可以知道CSD,CID寄存器各位的含义。
| 01 | /** @defgroup SD_Exported_Types_Group5 SD Card information structure |
| 06 | HAL_SD_CSDTypedef SD_csd; /*!< SD card specific data register */ |
| 07 | HAL_SD_CIDTypedef SD_cid; /*!< SD card identification number register */ |
| 08 | uint64_t CardCapacity; /*!< Card capacity */ |
| 09 | uint32_t CardBlockSize; /*!< Card block size */ |
| 10 | uint16_t RCA; /*!< SD relative card address */ |
| 11 | uint8_t CardType; /*!< SD card type */ |
| 12 | }HAL_SD_CardInfoTypedef; |
程序中HAL_SD_WriteBlocks_DMA()和HAL_SD_ReadBlocks_DMA()读写块,注意调用这函数后面要调用HAL_SD_CheckWriteOperation()/HAL_SD_CheckReadOperation()判断传输是否完成。同样也可以用HAL_SD_WriteBlocks()/HAL_SD_ReadBlocks()通过轮询的方式读写块。HAL_SD_Erase()为擦除块操作。
编译程序并下载到开发板。将Micro SD卡插入Micro SD Storage Board中,再插到Open746I-C开发的SDMMC接口中。打开串口调试助手,设置波特率为115200,按下复位串口助手上面会显示如下信息。(注意:此程序会损坏SD卡里面的文件系统,导致SD里面的数据丢失,注意备份数据)