STM32F103对SD卡数的读写

文章目录

  • 一、SD卡协议
    • 1.SD卡介绍
    • 2.SD卡物理结构
    • 2.SD卡寄存器
    • 3.SD总线协议
    • 4.SD卡的操作模式及切换
    • 5.SD卡初始化(SPI模式)
    • 6.SD卡读取与写入(SPI模式)
  • 二、题目要求
  • 三、STM32CubeMX建立工程
  • 四、修改文件内容
  • 五、连接验证
  • 六、总结
  • 七、参考资料

一、SD卡协议

1.SD卡介绍

SD存储卡是一种基于半导体快闪记忆器的新一代记忆设备,由于它体积小、数据传输速度快、可热插拔等优良的特性,被广泛地于便携式装置上使用,例如数码相机、平板电脑和多媒体播放器等。
SD卡具有高记忆容量、快速数据传输率、极大的移动灵活性以及很好的安全性,它被广泛地应用于便携式装置上,例如数码相机、平板电脑和多媒体播放器等。
SD卡的结构能保证数字文件传送的安全性,也很容易重新格式化,所以有着广泛的应用领域。音乐、电影等多媒体文件都可以方便地保存到SD卡中。目前市场上SD卡的品牌很多诸如:SANDISK、Kingmax、Panasonic和Kingston。
SD卡作为一种新型的存储设备,具有以下特点:
●高存储容量,最常用的容量:8GB、16GB、32GB、128GB、256GB等。
●内置加密技术,适应基于SDMI协议的著作版权保护功能。
●高速数据传送;最大读写速率为100MB/s。
●体积轻小,便于携带,具有很强的抗冲击能力。

2.SD卡物理结构

一张SD卡包括有存储单元、存储单元接口、电源检测、卡及接口控制器和接口驱动器5个部分,见图 362。存储单元是存储数据部件,存储单元通过存储单元接口与卡控制单元进行数据传输;电源检测单元保证SD卡工作在合适的电压下,如出现掉电或上状态时,它会使控制单元和存储单元接口复位;卡及接口控制单元控制SD卡的运行状态,它包括有8个寄存器;接口驱动器控制SD卡引脚的输入输出。
STM32F103对SD卡数的读写_第1张图片

2.SD卡寄存器

SD卡总共有8个寄存器,用于设定或表示SD卡信息,参考表格。这些寄存器只能通过对应的命令访问,对SD卡进行控制操作并不是像操作控制器GPIO相关寄存器那样一次读写一个寄存器的,它是通过命令来控制,SDIO定义了64个命令,每个命令都有特殊意义,可以实现某一特定功能,SD卡接收到命令后,根据命令要求对SD卡内部寄存器进行修改,程序控制中只需要发送组合命令就可以实现SD卡的控制以及读写操作。

名称 bit宽度 描述
CID 128 卡识别号(Card identification number):用来识别的卡的个体号码(唯一的)
RCA 16 相对地址(Relative card address):卡的本地系统地址,初始化时,动态地由卡建议,主机核准
DSR 16 驱动级寄存器(Driver Stage Register):配置卡的输出驱动
CSD 128 卡的特定数据(Card Specific Data):卡的操作条件信息
SCR 64 SD配置寄存器(CD Configuration Register):SD卡特殊特性信息
OCR 32 操作条件寄存器(Operation conditiongs register)
SSR 512 SD状态(SD Status):SD卡专有特征的信息
CSR 32 卡状态(Card Status):卡状态信息

3.SD总线协议

SD总线通信是基于命令和数据传输的。通讯由一个起始位(“0”),由一个停止位(“1”)终止。SD通信一般是主机发送一个命令(Command),从设备在接收到命令后作出响应(Response),如有需要会有数据(Data)传输参与。
STM32F103对SD卡数的读写_第2张图片

SD数据是以块(Black)形式传输的,SDHC卡数据块长度一般为512字节,数据可以从主机到卡,也可以是从卡到主机。数据块需要CRC位来保证数据传输成功。CRC位由SD卡系统硬件生成。STM32控制器可以控制使用单线或4线传输,本开发板设计使用4线传输。图 365为主机向SD卡写入数据块操作示意。
STM32F103对SD卡数的读写_第3张图片

4.SD卡的操作模式及切换

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

SD卡系统(包括主机和SD卡)定义了两种操作模式:卡识别模式和数据传输模式。在系统复位后,主机处于卡识别模式,寻找总线上可用的SDIO设备;同时,SD卡也处于卡识别模式,直到被主机识别到,即当SD卡接收到SEND_RCA(CMD3)命令后,SD卡就会进入数据传输模式,而主机在总线上所有卡被识别后也进入数据传输模式。在每个操作模式下,SD卡都有几种状态,参考表 格,通过命令控制实现卡状态的切换。
STM32F103对SD卡数的读写_第4张图片

5.SD卡初始化(SPI模式)

SPI操作模式下:在SD卡收到复位命令时,CS为有效电平(低电平),则SPI模式被启用,在发送CMD之前要先发送74个时钟,64个为内部供电上升时间,10个用于SD卡同步;之后才能开始CMD操作,在初始化时CLK时钟不能超过400KHz。

1、初始化与SD卡连接的硬件条件(MCU的SPI配置,IO口配置);
2、上电延时(>74个CLK);
3、复位卡(CMD0),进入IDLE状态;
4、发送CMD8,检查是否支持2.0协议;
5、根据不同协议检查SD卡(命令包括:CMD55、CMD41、CMD58和CMD1等);
6、取消片选,发多8个CLK,结束初始化
这样我们就完成了对SD卡的初始化,注意末尾发送的8个CLK是提供SD卡额外的时钟,完成某些操作。通过SD卡初始化,我们可以知道SD卡的类型(V1、V2、V2HC或者MMC),在完成了初始化之后,就可以开始读写数据了。

6.SD卡读取与写入(SPI模式)

1、发送CMD17;
2、接收卡响应R1;
3、接收数据起始令牌0XFE;
4、接收数据;
5、接收2个字节的CRC,如果不使用CRC,这两个字节在读取后可以丢掉。
6、禁止片选之后,发多8个CLK;

以上就是一个典型的读取SD卡数据过程,SD卡的写于读数据差不多,写数据通过CMD24来实现,具体过程如下:

1、发送CMD24;
2、接收卡响应R1;
3、发送写数据起始令牌0XFE;
4、发送数据;
5、发送2字节的伪CRC;
6、禁止片选之后,发多8个CLK;

以上就是一个典型的写SD卡过程。

二、题目要求

掌握SD卡协议原理,用STM32F103完成对SD卡的数据读取(fat文件模式)。

三、STM32CubeMX建立工程

1、打开STM32CubeMX新建一个工程。配置管脚,将对应的管脚设置成如图所示的样子。
STM32F103对SD卡数的读写_第5张图片
2、在FATFS中勾选User-defined,其它设置保持默认不变。
STM32F103对SD卡数的读写_第6张图片
3、在SYS中勾选Serial Wire
STM32F103对SD卡数的读写_第7张图片
4、GPIO中默认不配置
STM32F103对SD卡数的读写_第8张图片
5、SPI1使用默认的设置
STM32F103对SD卡数的读写_第9张图片
6、USART1默认配置
STM32F103对SD卡数的读写_第10张图片
7、配置RCC为高速时钟源
STM32F103对SD卡数的读写_第11张图片
STM32F103对SD卡数的读写_第12张图片
8、生成工程,设置保存路径,文件名,使用的编译器和堆栈大小。
STM32F103对SD卡数的读写_第13张图片

四、修改文件内容

1、首先移植两个文件到我们的工程内,在工程目录下复制如图两个文件。我会将完整工程放在文章最后。
STM32F103对SD卡数的读写_第14张图片
2、在工程内添加两个文件
STM32F103对SD卡数的读写_第15张图片
3、修改接口里面的内容到SPI映射上。
STM32F103对SD卡数的读写_第16张图片
我这里直接给出修改的内容:

/* USER CODE BEGIN INIT */
  uint8_t res;
	res = SD_init();//SD_Initialize() 
		 	if(res)//STM32 SPI的bug,在sd卡操作失败的时候如果不执行下面的语句,可能导致SPI读写异常
			{
				SPI_setspeed(SPI_BAUDRATEPRESCALER_256);
				spi_readwrite(0xff);//提供额外的8个时钟
				SPI_setspeed(SPI_BAUDRATEPRESCALER_2);
			}
	if(res)return  STA_NOINIT;
	else return RES_OK; //初始化成功
  /* USER CODE END INIT */

main.c

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * 

© Copyright (c) 2019 STMicroelectronics. * All rights reserved.

* * This software component is licensed by ST under Ultimate Liberty license * SLA0044, the "License"; You may not use this file except in compliance with * the License. You may obtain a copy of the License at: * www.st.com/SLA0044 * ****************************************************************************** */
/* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "main.h" #include "fatfs.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include "SDdriver.h" /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ SPI_HandleTypeDef hspi1; UART_HandleTypeDef huart1; /* USER CODE BEGIN PV */ /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_SPI1_Init(void); static void MX_USART1_UART_Init(void); /* USER CODE BEGIN PFP */ /* USER CODE END PFP */ /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ int fputc(int ch, FILE *f) { HAL_UART_Transmit(&huart1, (unsigned char *)&ch, 1, 0xFFFF); return ch; } uint16_t uart_value[3]; uint8_t aRxBuffer1; //uart rx buff void WritetoSD(BYTE write_buff[],uint8_t bufSize); char SD_FileName[] = "hello.txt"; uint8_t WriteBuffer[] = "yoga 6319070304** \r\n"; //uint8_t test_sd =0; //用于测试格式化 uint8_t write_cnt =0; //写SD卡次数 void WritetoSD(BYTE write_buff[],uint8_t bufSize) { FATFS fs; FIL file; uint8_t res=0; UINT Bw; res = SD_init(); //SD卡初始化 if(res == 1) { printf("SD卡初始化失败! \r\n"); } else { printf("SD卡初始化成功! \r\n"); } res=f_mount(&fs,"0:",1); //挂载 // if(test_sd == 0) //用于测试格式化 if(res == FR_NO_FILESYSTEM) //没有文件系统,格式化 { // test_sd =1; //用于测试格式化 printf("没有文件系统! \r\n"); res = f_mkfs("", 0, 0); //格式化sd卡 if(res == FR_OK) { printf("格式化成功! \r\n"); res = f_mount(NULL,"0:",1); //格式化后先取消挂载 res = f_mount(&fs,"0:",1); //重新挂载 if(res == FR_OK) { printf("SD卡已经成功挂载,可以进进行文件写入测试!\r\n"); } } else { printf("格式化失败! \r\n"); } } else if(res == FR_OK) { printf("挂载成功! \r\n"); } else { printf("挂载失败! \r\n"); } res = f_open(&file,SD_FileName,FA_OPEN_ALWAYS |FA_WRITE); if((res & FR_DENIED) == FR_DENIED) { printf("卡存储已满,写入失败!\r\n"); } f_lseek(&file, f_size(&file));//确保写词写入不会覆盖之前的数据 if(res == FR_OK) { printf("打开成功/创建文件成功! \r\n"); res = f_write(&file,write_buff,bufSize,&Bw); //写数据到SD卡 if(res == FR_OK) { printf("文件写入成功! \r\n"); } else { printf("文件写入失败! \r\n"); } } else { printf("打开文件失败!\r\n"); } f_close(&file); //关闭文件 f_mount(NULL,"0:",1); //取消挂载 } void Get_SDCard_Capacity(void) { FRESULT result; FATFS FS; FATFS *fs; DWORD fre_clust,AvailableSize,UsedSize; uint16_t TotalSpace; uint8_t res; res = SD_init(); //SD卡初始化 if(res == 1) { printf("SD卡初始化失败! \r\n"); } else { printf("SD卡初始化成功! \r\n"); } /* 挂载 */ res=f_mount(&FS,"0:",1); //挂载 if (res != FR_OK) { printf("FileSystem Mounted Failed (%d)\r\n", result); } res = f_getfree("0:", &fre_clust, &fs); /* 根目录 */ if ( res == FR_OK ) { TotalSpace=(uint16_t)(((fs->n_fatent - 2) * fs->csize ) / 2 /1024); AvailableSize=(uint16_t)((fre_clust * fs->csize) / 2 /1024); UsedSize=TotalSpace-AvailableSize; /* Print free space in unit of MB (assuming 512 bytes/sector) */ printf("\r\n%d MB total drive space.\r\n""%d MB available.\r\n""%d MB used.\r\n",TotalSpace, AvailableSize,UsedSize); } else { printf("Get SDCard Capacity Failed (%d)\r\n", result); } } /* USER CODE END 0 */ /** * @brief The application entry point. * @retval int */ 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(" OK \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 */ } /** * @brief System Clock Configuration * @retval None */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; /** Initializes the CPU, AHB and APB busses clocks */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI_DIV2; RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL16; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } /** Initializes the CPU, AHB and APB busses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV2; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) { Error_Handler(); } } /** * @brief SPI1 Initialization Function * @param None * @retval None */ static void MX_SPI1_Init(void) { /* USER CODE BEGIN SPI1_Init 0 */ /* USER CODE END SPI1_Init 0 */ /* USER CODE BEGIN SPI1_Init 1 */ /* USER CODE END SPI1_Init 1 */ /* SPI1 parameter configuration*/ hspi1.Instance = SPI1; hspi1.Init.Mode = SPI_MODE_MASTER; hspi1.Init.Direction = SPI_DIRECTION_2LINES; hspi1.Init.DataSize = SPI_DATASIZE_8BIT; hspi1.Init.CLKPolarity = SPI_POLARITY_LOW; hspi1.Init.CLKPhase = SPI_PHASE_1EDGE; hspi1.Init.NSS = SPI_NSS_SOFT; hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2; hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB; hspi1.Init.TIMode = SPI_TIMODE_DISABLE; hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; hspi1.Init.CRCPolynomial = 10; if (HAL_SPI_Init(&hspi1) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN SPI1_Init 2 */ /* USER CODE END SPI1_Init 2 */ } /** * @brief USART1 Initialization Function * @param None * @retval None */ static void MX_USART1_UART_Init(void) { /* USER CODE BEGIN USART1_Init 0 */ /* USER CODE END USART1_Init 0 */ /* USER CODE BEGIN USART1_Init 1 */ /* USER CODE END USART1_Init 1 */ huart1.Instance = USART1; huart1.Init.BaudRate = 115200; huart1.Init.WordLength = UART_WORDLENGTH_8B; huart1.Init.StopBits = UART_STOPBITS_1; huart1.Init.Parity = UART_PARITY_NONE; huart1.Init.Mode = UART_MODE_TX_RX; huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart1.Init.OverSampling = UART_OVERSAMPLING_16; if (HAL_UART_Init(&huart1) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN USART1_Init 2 */ /* USER CODE END USART1_Init 2 */ } /** * @brief GPIO Initialization Function * @param None * @retval None */ static void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; /* GPIO Ports Clock Enable */ __HAL_RCC_GPIOA_CLK_ENABLE(); /*Configure GPIO pin Output Level */ HAL_GPIO_WritePin(SD_CS_GPIO_Port, SD_CS_Pin, GPIO_PIN_RESET); /*Configure GPIO pin : SD_CS_Pin */ GPIO_InitStruct.Pin = SD_CS_Pin; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(SD_CS_GPIO_Port, &GPIO_InitStruct); } /* USER CODE BEGIN 4 */ /* USER CODE END 4 */ /** * @brief This function is executed in case of error occurrence. * @retval None */ void Error_Handler(void) { /* USER CODE BEGIN Error_Handler_Debug */ /* User can add his own implementation to report the HAL error return state */ /* USER CODE END Error_Handler_Debug */ } #ifdef USE_FULL_ASSERT /** * @brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * @param file: pointer to the source file name * @param line: assert_param error line source number * @retval None */ void assert_failed(uint8_t *file, uint32_t line) { /* USER CODE BEGIN 6 */ /* User can add his own implementation to report the file name and line number, tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ /* USER CODE END 6 */ } #endif /* USE_FULL_ASSERT */ /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

主函数内用一个OK来证明是否reset,这里可以你自己来设置这个标记字符串。
STM32F103对SD卡数的读写_第17张图片
在SD卡写入这里将如图两行代码注释掉,否则输出会有乱码。
STM32F103对SD卡数的读写_第18张图片
在主函数内改写你想写入SD卡内的内容,还可以改写文件保存类型。
STM32F103对SD卡数的读写_第19张图片

五、连接验证

准备好一块SD卡:
STM32F103对SD卡数的读写_第20张图片
连线方式如表格所示:这里供电一定要使用5V,否则可能无法初始化,因为驱动能力不行。

SD卡模块 STM32
CS PA4
SCK PA5
MOSI PA6
MISO PA7
VCC 5V
GND GND

连线结果实物图:STM32F103对SD卡数的读写_第21张图片
串口测试:
STM32F103对SD卡数的读写_第22张图片
如图显示初始化成功,挂载成功。

STM32F103对SD卡数的读写_第23张图片
最后用读卡器打开SD卡看到如图所示内容,与我们工程中编写的内容一致,说明成功完成实验。

六、总结

本次实验主要使用了SD卡,深入了解了SD卡的协议相关以及如何对SD卡进行初始化和使用,在使用过程中总是出现挂载失败或者没有显示的问题,这不一定是我们的程序的问题,这里就是因为供电幅值,或者接触不良,或者USB连接有问题导致的。

七、参考资料

https://blog.csdn.net/flyleaf91/article/details/52325539/?ops_request_misc=&request_id=&biz_id=102&utm_term=SD%E5%8D%A1%E7%89%A9%E7%90%86%E7%BB%93%E6%9E%84&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduweb~default-3-52325539.pc_search_result_control_group&spm=1018.2226.3001.4187

https://blog.csdn.net/qq_46467126/article/details/122033766

你可能感兴趣的:(stm32,单片机,物联网)