【HAL库】STM32CubeMX开发----STM32F407----SD卡存储 SDIO通信DMA读写

STM32CubeMX 下载和安装 详细教程
【HAL库】STM32CubeMX开发----STM32F407----目录
STM32F407:SD卡存储----SDIO基础通信和DMA读写----程序源码


前言

关于SDIO和SD卡的基础信息,请参考:【HAL库】STM32CubeMX开发----STM32F407----SD卡存储 SDIO基础通信
本次主要介绍SDIO通过DMA读写SD卡数据,不占用CPU的运行时间。

一、使用STM32CubeMX----新建工程

步骤1:打开 STM32CubeMX 软件

具体 STM32CubeMX 界面如下:

在这里插入图片描述

步骤2:选择 从MCU开始我的项目

在这里插入图片描述

步骤3:选择MCU芯片

我们本次实验选择 STM32F407VET6 芯片做 MCU
在这里插入图片描述

进入工程编辑,具体界面如下:

在这里插入图片描述

步骤4:设置烧写方式为 SWD模式

这一步必须选择一种烧写方式,否则烧写一次程序后,芯片中就没法再次烧写程序了。需要硬件重启才可以重新烧写程序。

STM32烧写程序连接方式详解

在这里插入图片描述

步骤5:设置时钟源

设置外部晶振为芯片系统时钟源。
在这里插入图片描述

步骤6:设置SDIO

  • Mode:选择SD的四线模式,即 SD 4 bits Wide bus.
  • 参数部分:F4系列不用修改配置,默认即可。F103系列,需把时钟分频系数修改为 6,即SDIOCLK Clock divide factor这一项,由默认0改为6, 不然会通信失败。
    在这里插入图片描述

步骤7:DMA配置

Add添加SDIO_RX,DMA功能,参数默认。
在这里插入图片描述
按照以上操作,添加SDIO_TX,DMA功能,参数默认。
在这里插入图片描述
勾选 SDIO global interrupt
在这里插入图片描述
修改SDIO中断优先级,具体如下图:
在这里插入图片描述

步骤8:时钟配置

在这里插入图片描述
重点: 箭头所指的Q值,它用于控制USB 、SDIO和随机数生成器的时钟,必须为 48M

步骤9:项目编辑

在这里插入图片描述

步骤10:代码生成编辑

在这里插入图片描述

步骤11:点击 GENERATE CODE 生成代码

在这里插入图片描述
新建的工程,已经生成,接下来就是对工程代码的编辑。

二、使用keil5----编辑程序

想要使用keil5打开工程,需要电脑先安装 keil5编译环境,具体操作流程。
可以点击下方文章链接:Keil5编译环境搭建流程----STM32和GD32

步骤1:使用keil5打开工程

点击工程文件中的MDK-ARM 文件
在这里插入图片描述

打开keil5工程
在这里插入图片描述
打开代码,编译运行一下,没有错误和告警为正常。
在这里插入图片描述

步骤2:用户代码 编辑位置

用户代码 要写在 USER xxxx BEGIN xxxUSER xxxx END xxx 之间。
这样下次使用 STM32CubeMX 重新生成代码后,自己写的代码不会被删除。
在这里插入图片描述

步骤3:修改代码

CubeMX生成的SDIO初始化代码,有一个bug,需要手动修改,操作如下:

  • 编译后,右击 main.c 文件中函数 MX_SDIO_SD_Init(),
  • 在弹出菜单中:Go To Definition Of …; 将跳转到SD卡初始化函数;
    在这里插入图片描述
    跳转到 sdio.c文件的 MX_SDIO_SD_Init()函数内部后,
    把下图位置中的 4B,改为 1B ;
    重点:每次重新生成后,都要手动修改一次。如果不修改,初始化过程会导致程序卡死。
    在这里插入图片描述

步骤4:使用到的HAL库函数

typedef struct
{
  uint32_t CardType;     		//SD卡类型
  uint32_t CardVersion;         //SD卡版本
  uint32_t Class;               //SD卡等级
  uint32_t RelCardAdd;          //SD卡地址
  uint32_t BlockNbr;            //SD卡容量(块)         
  uint32_t BlockSize;           //SD卡块大小(字节)          
}HAL_SD_CardInfoTypeDef;        //SD卡信息结构体

typedef uint32_t HAL_SD_CardStateTypeDef;		//SD卡状态信息
#define HAL_SD_CARD_READY          0x00000001U  //SD卡状态准备就绪
#define HAL_SD_CARD_IDENTIFICATION 0x00000002U  //SD卡识别状态
#define HAL_SD_CARD_STANDBY        0x00000003U  //SD卡待机状态
#define HAL_SD_CARD_TRANSFER       0x00000004U  //SD卡传输状态
#define HAL_SD_CARD_SENDING        0x00000005U  //SD卡正在发送状态
#define HAL_SD_CARD_RECEIVING      0x00000006U  //SD卡正在接收状态
#define HAL_SD_CARD_PROGRAMMING    0x00000007U  //SD卡编程状态
#define HAL_SD_CARD_DISCONNECTED   0x00000008U  //SD卡断开连接状态
#define HAL_SD_CARD_ERROR          0x000000FFU  //SD卡响应状态

// 1、获取 SD 卡的状态---参数:SD卡的句柄
HAL_SD_CardStateTypeDef HAL_SD_GetCardState(SD_HandleTypeDef *hsd);

// 2、获取 SD 卡的信息---参数:SD卡的句柄、SD卡信息
HAL_StatusTypeDef HAL_SD_GetCardInfo(SD_HandleTypeDef *hsd, HAL_SD_CardInfoTypeDef *pCardInfo);            
 
//3、读数据---参数:SD卡的句柄、数据、块地址、块数量、超时ms
HAL_StatusTypeDef HAL_SD_ReadBlocks(SD_HandleTypeDef *hsd, uint8_t *pData, uint32_t BlockAdd, uint32_t NumberOfBlocks, uint32_t Timeout);   
                                 
//4、写数据---参数:SD卡的句柄、数据、块地址、块数量、超时ms
HAL_StatusTypeDef HAL_SD_WriteBlocks(SD_HandleTypeDef *hsd, uint8_t *pData, uint32_t BlockAdd, uint32_t NumberOfBlocks, uint32_t Timeout);
 
//5、擦除数据---参数:SD卡的句柄、块起始地址、块结束地址
HAL_StatusTypeDef HAL_SD_Erase(SD_HandleTypeDef *hsd, uint32_t BlockStartAdd, uint32_t BlockEndAdd);  

//6、通过DMA读取数据---参数:SD句柄、数据、块起始地址、需要读取的块数量;
HAL_StatusTypeDef HAL_SD_ReadBlocks_DMA(SD_HandleTypeDef *hsd, uint8_t *pData, uint32_t BlockAdd, uint32_t NumberOfBlocks);  
 
//7、通过DMA写入数据---参数:SD句柄、数据、块起始地址、需要写入的块数量;
HAL_StatusTypeDef HAL_SD_WriteBlocks_DMA(SD_HandleTypeDef *hsd, uint8_t *pData, uint32_t BlockAdd, uint32_t NumberOfBlocks);

//8、DMA Tx发送传输完成中断回调函数---参数:SD句柄
void HAL_SD_TxCpltCallback(SD_HandleTypeDef *hsd);

//9、DMA Rx接收传输完成中断回调函数---参数:SD句柄
void HAL_SD_RxCpltCallback(SD_HandleTypeDef *hsd);

三、使用keil5----调试和运行

主要使用串口1通过printf函数打印运行数据,这部分操作主要参考:【HAL库】STM32CubeMX开发----STM32F407----Uart串口通信实验

实验:SD卡-DMA读写数据

在初始化的时候,调用SD卡-写数据,写入数据到SD卡,然后在运行时周期读取SD卡数据。

SD卡-DMA收发中断回调函数

volatile uint8_t myFlagSDReadReady = 0;    // 读取传输完成标志; 重要:CubeMX生成的Keil工程,编译优化等级默认是3,如果变量没有标记为易变的(volatile),编译器可能会认为其值在循环中不会改变,从而导致优化后的代码无法正确检测到变量值的变化,特别是在空循环体中。
volatile uint8_t myFlagSDWriteReady = 0;   // 写入传输完成标志; 重要:CubeMX生成的Keil工程,编译优化等级默认是3,如果变量没有标记为易变的(volatile),编译器可能会认为其值在循环中不会改变,从而导致优化后的代码无法正确检测到变量值的变化,特别是在空循环体中。

/*DMA Tx传输完成中断回调函数*/
void HAL_SD_TxCpltCallback(SD_HandleTypeDef *hsd)
{
    myFlagSDWriteReady = 1;   // 写入传输完成标志; 重要:CubeMX生成的Keil工程,编译优化等级默认是3,如果变量没有标记为易变的(volatile),编译器可能会认为其值在循环中不会改变,从而导致优化后的代码无法正确检测到变量值的变化,特别是在空循环体中。
}
 
/*DMA Rx传输完成中断回调函数*/
void HAL_SD_RxCpltCallback(SD_HandleTypeDef *hsd)
{
    myFlagSDReadReady = 1;    // 读取传输完成标志; 重要:CubeMX生成的Keil工程,编译优化等级默认是3,如果变量没有标记为易变的(volatile),编译器可能会认为其值在循环中不会改变,从而导致优化后的代码无法正确检测到变量值的变化,特别是在空循环体中。
}

SD卡-DMA读数据

void User_SD_ReadData_DMA(void)
{
  /*读测试:读出测试位置原数据,保存在ReadData[] */
	uint8_t ReadData[1024] = {0};
	printf("\r读取测试块的原数据\r\n");
	myFlagSDReadReady = 0;
	HAL_SD_ReadBlocks_DMA(&hsd, ReadData, 8, 2);// 读SD卡数据块; 参数:SD结构体、数据、块起始地址、读的块数量;
	while (myFlagSDReadReady == 0); 			// 等待卡的读操作结束
	for (uint32_t i = 0; i < 1024; i++)         // 打印 原数据
	{
		printf("%X ",  ReadData[i]);
	}
	printf("\r\n");
}

SD卡-DMA写数据

void User_SD_WriteData_DMA(void)
{
	 /* 写测试:在测试的块上写入数据 */
		uint8_t WriteData[1024] = {0};
		for (uint32_t i = 0; i < 1024; i++)
		{
			WriteData[i]=1024-i;
		}
		printf("\rSD卡 写入测试\r\n");
		myFlagSDWriteReady = 0; 
		HAL_SD_WriteBlocks_DMA(&hsd, WriteData, 8, 2); // 向SD卡写入数据块; 参数:SD结构体、数据地址、块起始地址、写入的块数量、超时时间;
		while (myFlagSDWriteReady == 0); // 等待卡的读写操作结束
		printf("对指定块写入结束! \r写入的数据是:\n");
		for (uint32_t i = 0; i < 1024; i++)                // 打印 写入的数据
		{
			printf("%X ",  WriteData[i]);
		}
		printf("\r\n");
}

运行结果

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


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

根号五

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值