本文使用到的硬件:STM32F103C8T6、Micro SD卡模块CH376S SPI接口、杜邦线、USB转TTL接口
本文使用到的软件:Keil MDK、串口调试助手
一、SPI读写SD卡介绍
SPI外设只具有两条数据线MISO和MOSI,分别用作数据的输入和输出,由于引脚较少,所以控制相对较容易。
1.1 SPI模式
在SPI模式下,数据都是以 字节(Byte)
为单位进行传输的。此时SD卡作为从机设备,一般的操作是MCU发送带有参数的命令,SD卡接收到命令和参数后进行操作,并且返回响应,MCU根据返回的响应进行下一步操作。
1.2 命令
SD卡的命令有
6个字节
(48位),由以下几部分组成:
第一字节
的最高位b7为起始位
,始终为0
接下来为传输位
,始终为1
b5-b0为命令代码
第2~5字节
为命令的参数,共4个字节
最后一个字节
的前7为CRC7校验位,最后一位为停止位,始终为1
常用命令:
命令 | 参数 | 响应类型 | 简写 | 描述 |
---|---|---|---|---|
CMD0 | 0 | R1 | GO_IDLE_STATE | 软件复位 |
CMD8 | (*1) | R7 | SEND_IF_COND | 发送MCU的电压范围,检测SD卡是否满足MCU的电压范围 |
ACMD41(*2) | (*3) | R1 | SD_SEND_OP_COND | 开始SD卡初始化和检测SD卡是否完成初始化 |
CMD9 | 0 | R1 | SEND_CSD | 读取CSD寄存器的值 |
CMD10 | 0 | R1 | SEND_CID | 读取CID寄存器的值 |
CMD12 | 0 | R1b | STOP_TRANSMISSION | 停止读取操作 |
CMD16 | 数据块长度[31:0] | R1 | SET_BLOCKLEN | 设置数据块长度(*4) |
CMD17 | 地址[31:0] | R1 | READ_SINGLE_BLOCK | 读取单个数据块 |
CMD18 | 地址[31:0] | R1 | READ_MULTIPLE_BLOCK | 读取多个块数据 |
CMD24 | 地址[31:0] | R1 | WRITE_BLOCK | 写单个块数据 |
CMD25 | 地址[31:0] | R1 | WRITE_MULTIPLE_BLOCK | 写多个块数据 |
CMD55 | 0 | R1 | APP_CMD | 定义下一条命令为ACMD命令 |
CMD58 | 0 | R3 | READ_OCR | 读取OCR寄存器 |
1.3 初始化操作
SPI模式下的初始化操作有:上电->进入SPI模式(CMD0)->检测当前MCU电压是否符合SD卡的要求(CMD8)->开始初始化(ACMD41)->读取卡类型(CMD58)
初始化过程中,SD卡时钟信号周期需为100KHz~400KHz之间,不能大于400KHz。
-
上电
当电压达到SD卡的最小工作电压的后,MCU必须使CS,DI为高电平,输出最少74个时钟脉冲后,才能开始发送第一个命令。
-
初始化过程
SD卡上电后处于SD Bus模式,使CS保持为0,并且发送
CMD0
命令,SD卡就会进入到SPI模式
。在SPI模式下,命令的CRC校验功能默认是禁止的(CMD8命令除外),但是发送第一个CMD0命令时,SD卡是处于SD Bus模式,该模式下CRC校验功能是启动的,因此第一个CMD0命令必须要有正确的CRC校验。正确的CMD0命令应为:0x40, 0x00, 0x00, 0x00, 0x00, 0x95。CMD8
用于检测SD卡接口电压是否满足要求,该命令的参数包括当前MCU接口的电压范围VHS([11:8]),以及用于检测通信的Check Pattern([7:0])。如果SD卡能满足当前MCU的接口电压,它就会返回VHS和Check Pattern的值。需要注意的是,CMD8的CRC校验值必须正确,假如CRC校验不对,SD卡返回的R1值中的CRC错误位就会置1。
ACMD41
命令用于开始初始化SD卡及检测其是否完成初始化。该命令的参数HCS([30])表示MCU是否支持SDHC和SDXC,若支持HCS置1,反之置0。如果ACDM41命令返回R1的值为0x01,说明SD卡正在初始化,MCU需要重复发送ACMD41,直到返回值R1为0。
初始化完成后
,通过发送CMD58
指令读取卡的类型(OCR寄存器的CCS位[30]), CCS为1表示当前卡的类型为SDXC或者SDHC,为0表示卡的类型为SDSC。
1.4 读写数据(多数据)
-
读取数据
读取多个数据块操作和读取单个数据块的相似,先发送命令CMD18
,然后开始等待数据块的起始标识符
。需要停止读取操作时,发送CMD12
命令,返回响应
为0表示SD卡处于忙碌状态,只有返回任何不为0的值后,MCU才能发送下一条命令。
-
写入数据
当SD卡接收到写入多个数据块命令CMD25
后,发送数据包起始符为(0xFC)
, 只有当DO不为0时,才能继续发送第二个数据包。如果要结束写入操作,则发送停止发送标识符(0xFD)
。
二、实验配置
2.1 源代码
链接:https://pan.baidu.com/s/1lXiepBi4sLZra6uo3yJgOw
提取码:dnjs
2.2 硬件连线
STM32 | SD卡模块 |
---|---|
PA4 | SDCS |
PA5 | SCK |
PA7 | MOSI |
PA6 | MISO |
VCC | VCC |
GND | GND |
2.3 部分代码分析
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_SPI1_Init();
MX_FATFS_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
HAL_UART_Receive_IT(&huart1,&aRxBuffer1,1); //enable uart
printf(" mian \r\n");
Get_SDCard_Capacity(); //得到使用内存并选择格式化
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
WritetoSD(WriteBuffer,sizeof(WriteBuffer));
HAL_Delay(500);
WriteBuffer[0] = WriteBuffer[0] +10;
WriteBuffer[1] = WriteBuffer[1] +10;
write_cnt ++;
while(write_cnt > 10)
{
printf(" while \r\n");
HAL_Delay(500);
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
- WritetoSD(WriteBuffer,sizeof(WriteBuffer));
写入SD卡程序,我们可以在文件的开始定义我们要写入SD卡的内容
- SD_init();
SD初始化函数,完成发送命令及鉴别SD卡
- while
write_cnt>10,write_cnt我们设置的初始值为0,表示当写入的次数达到11次我们就不再写入,输出while表示写入完成。
三、实验结果分析
- 烧录成功
- 初始化及写入SD卡成功
- 查看文档
四、总结
注意在接线时一定要保证5V电压,否则无法初始化,只可以完成烧录程序。虽然得到了最终结果,但理解原理是最重要的。
参考文章:
https://www.cnblogs.com/mr-bike/p/3546228.html
https://blog.csdn.net/qq_46467126/article/details/122033766
https://blog.csdn.net/IssacMi/article/details/102785961