参考文章
STM32CubeMX | SD Card FATFS - 知乎
[STM32F4]基于F407的硬件移植Free RTOS+FATFS(SDIO)_freertos+fatfs-CSDN博客
例程地址:STM32FatFS: 基于stm32的fatfs例程,配合博客文章
基于梁山派天空星开发板,STM32F407VET6
开发板引脚
STM32配置
系统模式配置
输出串口配置
系统时钟配置
这里有个48M的时钟,需要把这里配置成48M,SDIO使用的是系统时钟HCLK,也就是96M,这个需要记住,后面配置SDIO的时候会用到。
SDIO配置
选择4位总线,分频选择4分频,原因点击选项会看到。
SDIO_CK = SDIOCLK / [CLKDIV + 2]. The output clock frequency can vary between 187 KHz and 24 MHz. It is advised to keep the default ClockDiv value (0) to have a maximum SDIO_CK frequency of 24 MHz.
要求SDIO_CK在187 KHz 和 24 MHz之间,我们的系统时钟是96M,分频4,SDIO_CK计算后16M,符合要求。如果有的卡不能识别可以尝试降低频率。
需要开启DMA,因为在FATFS下的SDIO设置中,由于使用了Free RTOS,所以只能选择DMA的方式进行数据处理,且DMA的中断等级要低于SDIO的中断等级,但是在中断系统中0-4的优先级被RTOS占用。
打开SDIO中断
中断优先级调整,将DMA中断调整为7.
配置检测引脚
配置FATFS
使能SD卡,配置简体中文编码,因为资源足够多,可以选择长文件名支持,最大扇区尺寸选择4k。
配置检测引脚
这个引脚会在mount的时候进行判断,如果没有插卡会直接返回错误。
FREERTOS配置
选择V2版本,STM32F103可能需要选择V1版本,任务堆栈使用2048,避免栈溢出导致程序运行错误
长文件名支持,需要增大堆
配置独立c和h文件
代码
CUBEIDE自动生成的代码基本把驱动都实现了,只需要修改一部分内容,我是参考了
[STM32F4]基于F407的硬件移植Free RTOS+FATFS(SDIO)_freertos+fatfs-CSDN博客
文章修改的生成代码
在main.c中添加printf实现的支持函数
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */int __io_putchar(int ch)
{/* Implementation of __io_putchar *//* e.g. write a character to the UART1 and Loop until the end of transmission */HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFFFFFF);return ch;
}
int __io_getchar(void)
{/* Implementation of __io_getchar */char rxChar;// This loops in case of HAL timeout, but if an ok or error occurs, we continuewhile (HAL_UART_Receive(&huart1, (uint8_t *)&rxChar, 1, 0xFFFFFFFF) == HAL_TIMEOUT);return rxChar;
}
/* USER CODE END 0 */
还需要修改sdio.c源码,当我们挂载磁盘时,一次进入:
f_mount
----->find_volume
----->disk_initialize
----->disk.drv[pdrv]->disk_initialize(disk.lun[pdrv])
----->BSP_SD_Init;
SD卡在刚上电寻卡获取卡参数等一系列的初始化过程中,是采用单总线模式(SDIO_BUS_WIDE_1B)且时钟频率不超过400K的模式下进行的;但是问题在于初始化完成之后,要调用 HAL_SD_ConfigWideBusOperation 设置为4总线,在这个过程中要获取 SD卡 scr下的内容,此时采用4总线的方式获取不到数据,所以会导致最终的初始化失败!!!
解决方法:所以我们要将初始化配置里的总线模式从 SDIO_BUS_WIDE_4B 改为 SDIO_BUS_WIDE_1B,然后后面在使用 HAL_SD_ConfigWideBusOperation 使能设置为4总线时就没有问题了;
代码修改如下
void MX_SDIO_SD_Init(void)
{/* USER CODE BEGIN SDIO_Init 0 *//* USER CODE END SDIO_Init 0 *//* USER CODE BEGIN SDIO_Init 1 *//* USER CODE END SDIO_Init 1 */hsd.Instance = SDIO;hsd.Init.ClockEdge = SDIO_CLOCK_EDGE_RISING;hsd.Init.ClockBypass = SDIO_CLOCK_BYPASS_DISABLE;hsd.Init.ClockPowerSave = SDIO_CLOCK_POWER_SAVE_DISABLE;hsd.Init.BusWide = SDIO_BUS_WIDE_4B;hsd.Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE;hsd.Init.ClockDiv = 4;/* USER CODE BEGIN SDIO_Init 2 */hsd.Init.BusWide = SDIO_BUS_WIDE_1B;/* USER CODE END SDIO_Init 2 */}
增加了 hsd.Init.BusWide = SDIO_BUS_WIDE_1B;,也就是在初始化之后将SDIO_BUS_WIDE_4B改为了SDIO_BUS_WIDE_1B
下面是编写freertos的逻辑部分
在freertos.c文件中
添加头文件
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "fatfs.h"
#include "sdio.h"
#include <stdio.h>
/* USER CODE END Includes */
在默认任务中实现挂载、写入的实现。
/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void *argument)
{/* USER CODE BEGIN StartDefaultTask *//*测试SD card FATFS */// printf("》SD card FATFS BSP_SD_Init %ld.\r\n", BSP_SD_Init());// printf("》SD card FATFS HAL_SD_GetCardState %ld.\r\n", HAL_SD_GetCardState(&hsd));printf("Compilation Date: %s %s\n", __DATE__, __TIME__);// mount SD cardint retSD = f_mount(&SDFatFS, (TCHAR const *)SDPath, 1);if (retSD == FR_OK) {printf("》Filesystem mount ok, now you can read/write files.\r\n");// 创建或者打开文件 SD_Card_test.txtretSD = f_open(&SDFile, "SD_Card_test.txt", FA_OPEN_ALWAYS | FA_WRITE);if (retSD == FR_OK) {printf("》open/create SD_Card_test.txt OK, write data to it.\r\n");// Move to end of the file to append dataretSD = f_lseek(&SDFile, f_size(&SDFile));if (retSD == FR_OK) {f_printf(&SDFile, "SD card FATFS test.\r\n");printf("》write data to file OK, write bytes: SD card FATFS test.\r\n");} else {printf("!! File Write error: (%d)\n", retSD);}/* close file */f_close(&SDFile);} else {printf("!! open/Create file SD_Card_test.txt Fail (%d).\r\n", retSD);}} else {printf("!! SDcard mount filesystem error。(%d)\r\n", retSD);}// 不带fatfs调试函数SDCard_ShowInfo();// SDCard_ShowStatus();/* Infinite loop */for(;;){osDelay(1);}/* USER CODE END StartDefaultTask */
}
两个SDIO的测试函数
/* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application */
/*显示SD卡的信息*/
void SDCard_ShowInfo(void)
{// SD卡信息结构体变量HAL_SD_CardInfoTypeDef cardInfo;HAL_StatusTypeDef res = HAL_SD_GetCardInfo(&hsd, &cardInfo);if (res != HAL_OK) {printf("HAL_SD_GetCardInfo() error\r\n");return;}printf("\r\n*** HAL_SD_GetCardInfo() info ***\r\n");printf("Card Type= %ld\r\n", cardInfo.CardType);printf("Card Version= %ld\r\n", cardInfo.CardVersion);printf("Card Class= %ld\r\n", cardInfo.Class);printf("Relative Card Address= %ld\r\n", cardInfo.RelCardAdd);printf("Block Count= %ld\r\n", cardInfo.BlockNbr);printf("Block Size(Bytes)= %ld\r\n", cardInfo.BlockSize);printf("LogiBlockCount= %ld\r\n", cardInfo.LogBlockNbr);printf("LogiBlockSize(Bytes)= %ld\r\n", cardInfo.LogBlockSize);printf("SD Card Capacity(MB)= %ld\r\n", cardInfo.BlockNbr >> 1 >> 10);
}
// 获取SD卡当前状态
void SDCard_ShowStatus(void)
{// SD卡状态结构体变量HAL_SD_CardStatusTypeDef cardStatus;HAL_StatusTypeDef res = HAL_SD_GetCardStatus(&hsd, &cardStatus);if (res != HAL_OK) {printf("HAL_SD_GetCardStatus() error\r\n");return;}printf("\r\n*** HAL_SD_GetCardStatus() info ***\r\n");printf("DataBusWidth= %d\r\n", cardStatus.DataBusWidth);printf("CardType= %d\r\n", cardStatus.CardType);printf("SpeedClass= %d\r\n", cardStatus.SpeedClass);printf("AllocationUnitSize= %d\r\n", cardStatus.AllocationUnitSize);printf("EraseSize= %d\r\n", cardStatus.EraseSize);printf("EraseTimeout= %d\r\n", cardStatus.EraseTimeout);
}
/* USER CODE END Application */
需要自行添加函数文件声明。
运行效果