HAL库 CubeMX STM32通过SDIO模式实现对SD卡的读写

开发环境:
STM32CubeMX 5.6.0
Keil MDK 5.33
STM32F103RET6开发板
16G SD-Micro存储卡

SD卡

SD卡大家一定都不陌生,不过我想很多年龄不是很大的人(比如我)可能都对SD卡不是很了解,在我们的意识中,可能大家以为SD卡长这样HAL库 CubeMX STM32通过SDIO模式实现对SD卡的读写_第1张图片
不过这只是SD卡的一种,这种卡属于SD-Micro,是一种小型的卡,就如卡面上也写着Micro一样,当然SD卡除了这种卡之外,还有两种分别是SD和mini-SD,他们分别长这样:HAL库 CubeMX STM32通过SDIO模式实现对SD卡的读写_第2张图片
HAL库 CubeMX STM32通过SDIO模式实现对SD卡的读写_第3张图片
SD卡的四个角有一个是没有的,以便我们认识正反来使用它,SD卡的一侧还有一个可以扳动的读写保护开关,这三种卡里面SD卡最大,SD-Micro最小。HAL库 CubeMX STM32通过SDIO模式实现对SD卡的读写_第4张图片
根据SD卡的容量,可划分为SDSC(SD Standard Capacity)、SDHC(SD High Capacity)、SDXC(SD Extended Capacity)三种标准。现今,市场的主流SD产品是SDHC和SDXC这两种较大容量的存储卡,而SDSC卡因容量过小,已逐渐被市场淘汰。SD卡(三种卡的统称)的存储空间是由一个一个扇区组成的,SD卡的扇区大小是固定的,为512byte(这一点很重要) ,若干个扇区又可以组成一个分配单元(也被成为簇),分配单元常见的大小为4K、8K、16K、32K、64K。
需要注意的是,SD-Micro只有8个引脚,而SD卡是有九个引脚的,这两种都可以直线4线通讯。想了解更多关于SD卡的信息,给大家推荐一篇科普文:【科普】解密SD卡的种类、规格、速度、容量和寿命,附SD卡选购指南.

工程的创建

选中开发板之后,首先对系统内核进行配置:HAL库 CubeMX STM32通过SDIO模式实现对SD卡的读写_第5张图片
HAL库 CubeMX STM32通过SDIO模式实现对SD卡的读写_第6张图片
这里为什么选择TIM2,是因为我后面要用FreeRtos,FreeRtos要抢占SysTick,所以我要提前让出来。
然后配置SDIO和串口:HAL库 CubeMX STM32通过SDIO模式实现对SD卡的读写_第7张图片
我这里配置串口1是与串口调试助手交互,串口2我是为了与另一个板子通讯,不需要的可以忽略。
HAL库 CubeMX STM32通过SDIO模式实现对SD卡的读写_第8张图片
HAL库 CubeMX STM32通过SDIO模式实现对SD卡的读写_第9张图片
SDIO使用4线通讯,这样速度更快,关于clock divide的配置,这里要跟大家说一下,我们用的卡一般的频率为0~25MHz,
卡频率(SDIO_CK) = 适配器频率(SDIOCLK)/(2+分频值)。
这里的分频值便是clock divide,数值多少自己根据自己单片机SDIO模块所在总线的频率来确定。
接下来配置时钟树:
HAL库 CubeMX STM32通过SDIO模式实现对SD卡的读写_第10张图片
如图所示,我配置成了最大时钟频率:72MHz。此时SDIO总线上的频率为72MHz。
最后配置存储路径生成工程就可以了
HAL库 CubeMX STM32通过SDIO模式实现对SD卡的读写_第11张图片
HAL库 CubeMX STM32通过SDIO模式实现对SD卡的读写_第12张图片
到这里,我们的工程就创建完了。

代码编写

接下来接进行SD卡读取的代码编写,在代码编写之前,需要做些准备工作:
1、确定你的开发板上有SDIO模块
2、你的SD卡为一块小于或等于32G的存储卡,因为目前大多数单片机只支持SC和HC类型的卡,这与单片机所遵循的SD卡协议有关系,总之,你的单片机很有可能不支持XC类型的卡,如果确定支持XC的卡也可以用。
3、对你的SD卡用读卡器进行格式化,格式化为FAT32模式的文件系统,分配单元自己定,分配单元越小,空间利用率越高(第三步格式化为FAT32模式,也为用FatFs系统开发做准备)。
以上便是我们的准备工作,打开我们生成的工程之后,首先带领大家认识几个我们会用到的函数:
在stm32f1xx_hal_sd.c源文件中的SD卡初始化函数:
HAL库 CubeMX STM32通过SDIO模式实现对SD卡的读写_第13张图片
在stm32f1xx_hal_sd.c源文件中的SD卡擦除函数:
HAL库 CubeMX STM32通过SDIO模式实现对SD卡的读写_第14张图片
在stm32f1xx_hal_sd.c源文件中的SD卡对扇区写函数:
HAL库 CubeMX STM32通过SDIO模式实现对SD卡的读写_第15张图片
在stm32f1xx_hal_sd.c源文件中的SD卡对扇区读函数:
HAL库 CubeMX STM32通过SDIO模式实现对SD卡的读写_第16张图片
函数内具体怎么实现的我就不清楚了,本人能力有限,这也是我为什么应CubaMX的原因,对小白友好,如果读者也是初学者,大可不必取深究函数内部结构,只需要会用即可。
接下来展示代码:
别忘了声明头文件:

#include "stdio.h"

首先在/* Private define /下的/ USER CODE BEGIN PD /和/ USER CODE END PD */之间定义一些宏(不在这里定义也可以,但是这里是官方给大家定义的写宏的区域,我希望大家初学养成一个规范的好习惯)

#define BLOCK_SIZE            512            // SD卡块大小     
#define NUMBER_OF_BLOCKS      1              // 测试块数量(小于15)
#define WRITE_READ_ADDRESS    0x00000000     // 测试读写地址

然后在/* Private variables /下的/ USER CODE BEGIN PD /和/ USER CODE END PD */创建写数组和读数组

/* 注意我们这里设置的数字一个元素占4字节,元素个数为128个,
所以这一个数组申请的内存空间为512字节,正好为我们SD卡的一个扇区 */
__align(4) uint32_t Buffer_Block_Tx[BLOCK_SIZE*NUMBER_OF_BLOCKS/4];    //将要写入的数据放在这里,__align(4)是四字节对齐关键词
__align(4) uint32_t Buffer_Block_Rx[BLOCK_SIZE*NUMBER_OF_BLOCKS/4];    //将读出的数据放在此数组

接下来进行SD卡的读写操作
在/* USER CODE BEGIN 2 /和/ USER CODE END 2 */之间添加代码

HAL_SD_InitCard(&hsd);   //初始化SD卡,注意句柄
if(HAL_SD_Erase(&hsd,WRITE_READ_ADDRESS,WRITE_READ_ADDRESS + 0x00000200) == HAL_OK)
{
	printf("Erase Successful\r\n");
	HAL_Delay(20);    //如果不延时可能到值写操作失败
	if(HAL_SD_WriteBlocks(&hsd, (uint8_t *)Buffer_Block_Tx, WRITE_READ_ADDRESS, NUMBER_OF_BLOCKS, 0xFFFF) == HAL_OK )
	{
		printf("Write Successful\r\n");
		HAL_Delay(20);
		if(HAL_SD_ReadBlocks(&hsd, (uint8_t *)Buffer_Block_Rx, WRITE_READ_ADDRESS, NUMBER_OF_BLOCKS, 0xFFFF) == HAL_OK)
		{
			printf("Raed Successful\r\n");
			uint8_t j = 0;
			for(uint16_t i=0;i<BLOCK_SIZE*NUMBER_OF_BLOCKS/4;i++)   //这个循环是为了让读数组里的数组个打印到电脑上
			{
				if(j==8)
				{
					printf("\r\n");
					j=0;
				}
				printf("%d  ",Buffer_Block_Rx[i]);
				j++;
			}
		}
		else
		{
			printf("Read Error\r\n");
		}
	}
	else
	{
		printf("Write Error\r\n");
	}
}
else
{
	printf("Erase Error\r\n");
}

在/* USER CODE BEGIN 4 /和/ USER CODE END 4 */之间添加代码:

//重定义fputc函数 
int fputc(int ch,FILE *f)
{
	HAL_UART_Transmit(&huart1,(uint8_t *)&ch,1,HAL_MAX_DELAY);   //你用的单片机串口几是通过USB线与电脑通讯的要注意,这里的串口根据自己实际情况设定
	return ch;
}

这样读写SD卡就完成了。不过要注意的是,只读写一个扇区时,有时可能会出错,如果读写一个扇区不行,就把数组扩大为原来的两倍,同时读写两个扇区再试试,数组扩大了一些其他参数也别忘了改。

注意

有些人可能会想,我能不能同时写两个扇区,读的时候把首地址设为写地址的基础上偏移一个扇区的地址(即写地址+0x00000200),直接跳过第一个扇区而读取第二个扇区呢?答案是不可以的,为什么呢?因为你在同时写两个扇区的时候,两个扇区会被视为一个块,要访问这个块就必须从首地址开始读取,第二个扇区是没有地址的所以不能直接读取第二个扇区。

你可能感兴趣的:(HAL库STM32,单片机,嵌入式,c语言,stm32)