作者: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
文章目录
前言
裸机使用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不够,
五、修改堆栈
增大堆栈空间
最后生成代码即可
六、增加测试代码
- 在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 */
- 在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 */
}