STM32与FATFS在SDIO接口SD卡上的协同运行指南
参考文章
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 continue
while (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 card
int 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.txt
retSD = 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 data
retSD = 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 */
需要自行添加函数文件声明。
运行效果
作者:andylauren