STM32H7B0VBT6使用Free RTOS配置SD卡+Fatfs文件管理系统

news2024/11/26 14:42:35

作者:Jack_G
时间:2023.05.26
版本:V1.0
上次修改时间:
环境:
\quad \quad \quad \quad STM32Cube MX V6.8.1
\quad \quad \quad \quad STM32CubeH7 Firmware Package V1.11.0 / 04-Nov-2022
\quad \quad \quad \quad Fatfs: R0.12C
\quad \quad \quad \quad Keil: V5.29

文章目录

  • 前言
  • 一、基本配置
      • 1.1 配置时钟源为外部晶振
      • 1.2 配置烧录程序的引脚
      • 1.3 配置时钟树,配置主频280M
  • 二、配置SDMMC
  • 三、配置FreeRTOS
  • 四、配置Fatfs
  • 五、修改堆栈
  • 六、增加测试代码


前言

裸机使用Fatfs时,没有任何问题,加入Free RTOS后就一直不能f_mount,返回值一直为1,百思不得其解,几经周转,最后重新配置了一次就正常使用了,具体配置过程如下:


一、基本配置

1.1 配置时钟源为外部晶振

在这里插入图片描述



1.2 配置烧录程序的引脚

在这里插入图片描述




1.3 配置时钟树,配置主频280M

在这里插入图片描述




`注意:其中:使用SD卡时,SDMMC时钟常配为48 MHz,经过分频后不超过25MHz`(但是经过实际我测试,我配置的280 MHz,SDMMC分频系数为5也能使用,即:280/(2*5) = 28 MHz,想提速考虑这部分再斟酌)

在这里插入图片描述



配置个串口用于信息打印测试

在这里插入图片描述




二、配置SDMMC

选择四线模式,分频系数我写的5(速度较低)。
在这里插入图片描述





查了一些博客,有的同学反映需要将数据线和命令先上拉,不然有可能出现f_mount失败的情况,修改引脚上下拉再DPIO中修改
在这里插入图片描述






使能SDMMC的中断,在配置完Free RTOS后优先级会自动变为5
在这里插入图片描述




三、配置FreeRTOS

将heap改大一点
在这里插入图片描述
将初始任务的堆栈改大一点
在这里插入图片描述





四、配置Fatfs

修改支持长文件名和MAX_SS
不建议在此单片机开启文件名的中文支持,因为RAM不够,
配置

五、修改堆栈

增大堆栈空间
在这里插入图片描述

最后生成代码即可

六、增加测试代码

  1. 在main.c中添加fputc重定义一下printf
    添加头文件
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
/* USER CODE END Includes */

重定向fputc

/* USER CODE BEGIN 4 */

//重定向fputc函数printf 
int fputc(int ch,FILE *f){
	HAL_UART_Transmit(&huart1,(uint8_t *)&ch,1,HAL_MAX_DELAY);
	return ch;
} 
//重定向fgetc函数 scanf
int fgetc(FILE *f){
	uint8_t ch;
	HAL_UART_Receive(&huart1,(uint8_t *)&ch,1,HAL_MAX_DELAY);
	return ch;
}
/* USER CODE END 4 */
  1. 在freertos.c中添加测试代码
    (Fatfs的初始化啥的都在任务中进行初始化)

这部分是添加的测试函数代码

#include "fatfs.h"
#include "sdmmc.h"
#include <stdio.h>

#define FF_MAX_SS		4096

//	函数:FatFs_FileTest
//	功能:进行文件写入和读取测试
//
uint8_t  FatFs_FileTest(void)	//文件创建和写入测试
{
	uint8_t i = 0;
	uint16_t BufferSize = 0;	
	FIL	MyFile;			// 文件对象
	UINT 	MyFile_Num;		//	数据长度
	BYTE 	MyFile_WriteBuffer[] = "STM32H7B0 SD卡 文件系统测试";	//要写入的数据
	BYTE 	MyFile_ReadBuffer[1024];	//要读出的数据
	uint8_t MyFile_Res;    /* Return value for SD */
	
	printf("-------------FatFs 文件创建和写入测试---------------\r\n");
	
	MyFile_Res = f_open(&MyFile,"0:FatFs Test.txt",FA_CREATE_ALWAYS | FA_WRITE);	//打开文件,若不存在则创建该文件
	if(MyFile_Res == FR_OK)
	{
		printf("文件打开/创建成功,准备写入数据...\r\n");
		
		MyFile_Res = f_write(&MyFile,MyFile_WriteBuffer,sizeof(MyFile_WriteBuffer),&MyFile_Num);	//向文件写入数据
		if (MyFile_Res == FR_OK)	
		{
			printf("写入成功,写入内容为:\r\n");
			printf("%s\r\n",MyFile_WriteBuffer);
		}
		else
		{
			printf("文件写入失败,请检查SD卡或重新格式化!\r\n");
			f_close(&MyFile);	  //关闭文件	
			return ERROR;			
		}
		f_close(&MyFile);	  //关闭文件			
	}
	else
	{
		printf("无法打开/创建文件,请检查SD卡或重新格式化!\r\n");
		f_close(&MyFile);	  //关闭文件	
		return ERROR;		
	}
	
	printf("-------------FatFs 文件读取测试---------------\r\n");	
	
	BufferSize = sizeof(MyFile_WriteBuffer)/sizeof(BYTE);									// 计算写入的数据长度
	MyFile_Res = f_open(&MyFile,"0:FatFs Test.txt",FA_OPEN_EXISTING | FA_READ);	//打开文件,若不存在则创建该文件
	MyFile_Res = f_read(&MyFile,MyFile_ReadBuffer,BufferSize,&MyFile_Num);			// 读取文件
	if(MyFile_Res == FR_OK)
	{
		printf("文件读取成功,正在校验数据...\r\n");
		
		for(i=0;i<BufferSize;i++)
		{
			if(MyFile_WriteBuffer[i] != MyFile_ReadBuffer[i])		// 校验数据
			{
				printf("校验失败,请检查SD卡或重新格式化!\r\n");
				f_close(&MyFile);	  //关闭文件	
				return ERROR;
			}
		}
		printf("校验成功,读出的数据为:\r\n");
		printf("%s\r\n",MyFile_ReadBuffer);
	}	
	else
	{
		printf("无法读取文件,请检查SD卡或重新格式化!\r\n");
		f_close(&MyFile);	  //关闭文件	
		return ERROR;		
	}	
	
	f_close(&MyFile);	  //关闭文件	
	return SUCCESS;
}
//	函数:FatFs_GetVolume
//	功能:计算设备的容量,包括总容量和剩余容量

void FatFs_GetVolume(void)	// 计算设备容量
{
	FATFS *fs;		//定义结构体指针
	uint32_t SD_CardCapacity = 0;		//SD卡的总容量
	uint32_t SD_FreeCapacity = 0;		//SD卡空闲容量
	DWORD fre_clust, fre_sect, tot_sect; 	//空闲簇,空闲扇区数,总扇区数

	f_getfree("0:",&fre_clust,&fs);			//获取SD卡剩余的簇

	tot_sect = (fs->n_fatent-2) * fs->csize;	//总扇区数量 = 总的簇 * 每个簇包含的扇区数
	fre_sect = fre_clust * fs->csize;			//计算剩余的可用扇区数	   

	SD_CardCapacity = tot_sect / 2048 ;	// SD卡总容量 = 总扇区数 * 512( 每扇区的字节数 ) / 1048576(换算成MB)
	SD_FreeCapacity = fre_sect / 2048 ;	//计算剩余的容量,单位为M
	printf("-------------------获取设备容量信息-----------------\r\n");		
	printf("SD容量:%dMB\r\n",SD_CardCapacity);	
	printf("SD剩余:%dMB\r\n",SD_FreeCapacity);
}

void FatFs_Check(void)	//判断FatFs是否挂载成功,若没有创建FatFs则格式化SD卡
{
	BYTE work[FF_MAX_SS]; 
	
	FATFS_LinkDriver(&SD_Driver, SDPath);		// 初始化驱动
	retSD = f_mount(&SDFatFS,"0:",1);	//	挂载SD卡
	
	if (retSD == FR_OK)	//判断是否挂载成功
	{
		printf("\r\nSD文件系统挂载成功\r\n");
		FatFs_GetVolume();
	}
	else		
	{
		if(retSD == 13)
		{
			printf("SD卡还未创建文件系统,即将格式化\r\n");
			
			retSD = f_mkfs("0:",FM_FAT32,0,work,sizeof work);		//格式化SD卡,FAT32,簇默认大小16K
			
			if (retSD == FR_OK)		//判断是否格式化成功
				printf("SD卡格式化成功!\r\n");
			else
				printf("格式化失败,请检查或更换SD卡!\r\n");
		}
		else
			printf("挂载失败:%d\r\n",retSD);
	}
}
void printf_sdcard_info(void)
{
	HAL_SD_CardInfoTypeDef  SDCardInfo;  
	uint64_t CardCap;      	//SD卡容量
	HAL_SD_CardCIDTypeDef SDCard_CID; 

	HAL_SD_GetCardCID(&hsd1,&SDCard_CID);	//获取CID
	HAL_SD_GetCardInfo(&hsd1,&SDCardInfo);                    //获取SD卡信息
	CardCap=(uint64_t)(SDCardInfo.LogBlockNbr)*(uint64_t)(SDCardInfo.LogBlockSize);	//计算SD卡容量
	switch(SDCardInfo.CardType)
	{
		case CARD_SDSC:
		{
			if(SDCardInfo.CardVersion == CARD_V1_X)
				printf("Card Type:SDSC V1\r\n");
			else if(SDCardInfo.CardVersion == CARD_V2_X)
				printf("Card Type:SDSC V2\r\n");
		}
		break;
		case CARD_SDHC_SDXC:printf("Card Type:SDHC\r\n");break;
		default:break;
	}	
		
    printf("Card ManufacturerID: %d \r\n",SDCard_CID.ManufacturerID);				//制造商ID	
 	printf("CardVersion:         %d \r\n",(uint32_t)(SDCardInfo.CardVersion));		//卡版本号
	printf("Class:               %d \r\n",(uint32_t)(SDCardInfo.Class));		    //
 	printf("Card RCA(RelCardAdd):%d \r\n",SDCardInfo.RelCardAdd);					//卡相对地址
	printf("Card BlockNbr:       %d \r\n",SDCardInfo.BlockNbr);						//块数量
 	printf("Card BlockSize:      %d \r\n",SDCardInfo.BlockSize);					//块大小
	printf("LogBlockNbr:         %d \r\n",(uint32_t)(SDCardInfo.LogBlockNbr));		//逻辑块数量
	printf("LogBlockSize:        %d \r\n",(uint32_t)(SDCardInfo.LogBlockSize));		//逻辑块大小
	printf("Card Capacity:       %d MB\r\n",(uint32_t)(CardCap>>20));				//卡容量
}

最后在任务中进行调用即可
示例:

/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void *argument)
{
  /* USER CODE BEGIN StartDefaultTask */
  /* Infinite loop */
	
	FatFs_Check();
	printf_sdcard_info();
	FatFs_FileTest();
	
  for(;;)
  {
	HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);
    osDelay(1000);
  }
  /* USER CODE END StartDefaultTask */
}

附上工程代码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/573616.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于混合蛙跳的路径规划算法

路径规划算法&#xff1a;基于混合蛙跳优化的路径规划算法- 附代码 文章目录 路径规划算法&#xff1a;基于混合蛙跳优化的路径规划算法- 附代码1.算法原理1.1 环境设定1.2 约束条件1.3 适应度函数 2.算法结果3.MATLAB代码4.参考文献 摘要&#xff1a;本文主要介绍利用智能优化…

改变开发的未来 | 探索无服务器与人工智能的协同效应

近年来&#xff0c;无服务器计算和人工智能深刻改变着应用程序的开发方式。 无服务器计算实现无需管理底层基础架构就能构建和运行应用程序&#xff0c;而人工智能则让应用程序依据数据和算例做出智能决策。借助云计算&#xff0c;开发者打开了一个应用程序开发、构建的全新世…

Linux网络编程——有限状态机

在逻辑单元内部的一种高效的编程方法&#xff1a;有限状态机。 有的应用层协议头部包含数据包类型字段&#xff0c;每种类型可以映射为逻辑单元的一种执行状态&#xff0c;服务器可以根据它来编写相应的处理逻辑&#xff0c;下面代码展示的是状态独立的有限状态机 STATE_MACH…

Python中模块的使用3

在运行Python程序时&#xff0c;总会用到Python的标准库模块。一些标准库模块被内嵌到Python解释器中&#xff0c;通过调用这些模块提供的函数&#xff0c;可以实现特殊的功能。sys模块就是Python的一个标准库模块&#xff0c;该模块被被内嵌到Python解释器中。 1 sys模块的导…

阿里云备案服务码申请方法流程

阿里云备案服务码是什么&#xff1f;ICP备案服务码怎么获取&#xff1f;阿里云备案服务码分为免费和付费两种&#xff0c;申请备案服务码是有限制条件的&#xff0c;需要你的阿里云账号下有可用于申请备案服务码的云产品&#xff0c;如云服务器、建站产品、虚拟主机等&#xff…

计算机组成原理-指令系统-指令格式及寻址方式

目录 一、指令的定义 1.1 扩展操作码指令格式 二、指令寻址方式 2.1 顺序寻址 2.2 跳跃寻址 三、 数据寻址 3.1 直接寻址 3.2 间接寻址 3.3 寄存器寻址 ​ 3.4 寄存器间接寻址 3.5 隐含寻址 3.6 立即寻址 3.7 偏移地址 3.7.1 基址寻址 3.7.2 变址寻址 3.7.3 相对寻址…

ICLR2023 | Mole-BERT: 对分子GNN预训练的反思

原文标题&#xff1a;MOLE-BERT: RETHINKING PRE-TRAINING GRAPH NEURAL NETWORKS FOR MOLECULES 原文链接&#xff1a;Mole-BERT: Rethinking Pre-training Graph Neural Networks for Molecules | OpenReview https://github.com/junxia97/Mole-BERT 一、Introduction At…

TreeMap(1):TreeMap介绍

1 TreeMap的特点 概念&#xff1a; TreeMap是一个双列集合&#xff0c;是Map的子类。底层由红黑树结构构成。 特点&#xff1a; 元素中键不能重复元素会按照大小顺序排序 2 TreeMap的数据结构 2.1二叉查找树 2.1.1二叉查找树的定义 特点&#xff1a; 若左子树不空&#…

分享五个前端WebGis地图框架(优缺点以及该如何选择)附地址

如何选择一个合适的前端GIS框架时&#xff0c;需要考虑以下几点&#xff1a; 1&#xff0c;功能和特性 前端GIS框架的主要目的是在 Web 环境下使用&#xff0c;以可视化地理数据。需要考虑所需功能和特性&#xff0c;例如应用需不需要数据编辑、地图标注、3D 可视化、如何实现…

五、Spring从入门到改行

一、Spring概述 Spring是一个轻量级的控制反转(IOC)和面向切面(AOP)的容器&#xff08;框架&#xff09;。 二、Spring基础程序 1、pom.xml中导入依赖 <dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifact…

【特征选择】基于二进制粒子群算法的特征选择方法(KNN分类器)【Matlab代码#28】

文章目录 【可更换其他算法&#xff0c;获取资源请见文章第5节&#xff1a;资源获取】1. 基于群智能算法的特征选择2. 二进制粒子群算法3. 部分代码展示4. 仿真结果展示5. 资源获取 【可更换其他算法&#xff0c;获取资源请见文章第5节&#xff1a;资源获取】 1. 基于群智能算法…

ollvm反混淆实战

[原创]记一次基于unidbg模拟执行的去除ollvm混淆-Android安全-看雪-安全社区|安全招聘|kanxue.com 参考上面的博客进行操作。这里记录操作细节 首先配置unidbg框架 git clone --recursive https://github.com/zhaoboy9692/unidbgweb.git 然后在unidbg-android中添加自定义类…

Vue3.x的设计理念-Vue3导读

目录 VUE-NEXT【vue3】 VUE-NEXT最核心的变更 Why not SFC&#xff1f;【单文件组件】 Composition API 生命周期钩子变化 什么是响应式&#xff08;Reactivity&#xff09; Reactive值 Proactive vs Reactive 声明式程序 声明式程序&#xff1a;创造语言 声明式程序…

MATLAB串口通信使STM32出现复位情况分析

前言 这几天&#xff0c;在使用matlab进行串口读取的操作&#xff0c;一次读几百个数据后&#xff0c;对数据进行操作分析&#xff0c;打印图片。但是遇到一个奇怪的问题&#xff0c;每次串口操作完毕后&#xff0c;STM32就会出现复位状况。 matlab串口操作 下面直接附上正常…

【特征选择】基于二进制蝗虫优化算法的特征选择方法(KNN分类器)【Matlab代码#29】

文章目录 【可更换其他算法&#xff0c;获取资源请见文章第5节&#xff1a;资源获取】1. 基于群智能算法的特征选择2. 二进制蝗虫优化算法3. 部分代码展示4. 仿真结果展示5. 资源获取 【可更换其他算法&#xff0c;获取资源请见文章第5节&#xff1a;资源获取】 1. 基于群智能算…

【社区图书馆】读《一本书读懂AIGC:ChatGPT、AI绘画、智能文明与生产力变革》所感

文章目录 《一本书读懂AIGC&#xff1a;ChatGPT、AI绘画、智能文明与生产力变革》目录作者简介我的体会&#xff1a; AI带来的挑战和机遇是不可避免的 《一本书读懂AIGC&#xff1a;ChatGPT、AI绘画、智能文明与生产力变革》 作者&#xff1a;a15a 著 贾雪丽 0xAres 张炯 主编 …

百度统计是什么?百度统计数据可以自动同步吗?

百度统计是什么&#xff1f; 百度统计是一款稳定、专业、安全的数据分析产品&#xff0c;提供数据看板、行为分析、用户管理、转化归因、营销管理、AB测试等多个板块的高阶分析能力&#xff0c;帮助提升客户各职能角色工作效能&#xff0c;以数据分析助力企业达成用户全生命周…

算法|5.快速排序相关

算法|5.快速排序相关 1.荷兰国旗问题 题意&#xff1a;给定一个数组arr&#xff0c;以arr[R]为划分值&#xff0c;请把arr[R]的数放在数组的左边&#xff0c;等于arr[R]的数放在数组的中间&#xff0c;大于arr[R]的数放在数组的右边&#xff0c;返回等于arr[R]的区间。要求额…

MSP432笔记8:定时器A_PWM驱动舵机

开发板型号&#xff1a;MSP432P401r 今日得以继续我的MSP432电赛速通之路&#xff0c;文首提供本次学习实践项目文件。 注&#xff1a;我笔记实践都是从原始空项目工程文件开始配置的。 有道是 —_—_—_—_— “山无重数周遭碧&#xff0c;花不知名分外娇” “曲…

Linux驱动入门(三)——源码下载阅读、分析和嵌入式文件系统介绍

文章目录 从内核出发获取内核源码使用Git安装内核源码使用补丁 阅读Linux内核源码Source Insight简介阅读源码 内核开发的特点无libc库抑或无标准头文件GNU C没有内存保护机制不要轻易在内核中使用浮点数容积小而固定的栈同步和并发可移植性的重要性 Linux源码分析Linux源码结构…