STM32F103基于HAL工程挂载FatFS驱动SD卡
-
📌基于标准库驱动《STM32挂载SD卡基于Fatfs文件系统读取文件信息》
-
🎬驱动实验效果:
-
🔨通过STM32cubemx配置SPI1作为访问SD、TF卡通讯方式。
-
🔧在STM32cubemx配置中挂载中间件FatFS文件系统,用来管理和访问SD文件。
📓驱动代码完善
- 🌿添加源文件(.c):user_diskio_spi.c
-
🖋在
user_diskio.c
中 补充代码。(主要是完善SPI驱动SD卡相关代码。) -
- 在
FATFS/Target/user_diskio.c
文件中包含#include "user_diskio_spi.h"
头文件。
- 在
-
- 在
FATFS/Target/user_diskio.c
文件中添加相应的SPI通讯相关API函数。
- 在
在
USER_SPI_initialize(...)
函数中添加:USER_initialize(...)
,
USER_SPI_status(...)
函数中添加:USER_status(...)
,
USER_SPI_read(...)
函数中添加:USER_read(...)
,
USER_SPI_write(...)
函数中添加:USER_write(...)
,
USER_SPI_ioctl(...)
函数中添加:USER_ioctl(...)
.
- 🌿在main.h中添加SPI1句柄宏定义
/* USER CODE BEGIN Private defines */
#define SD_SPI_HANDLE hspi1
/* USER CODE END Private defines */
📗SPI速度配置
📋 低速和快速模式切换说明:
- 🌿调整SPI初始化函数中的时钟分频系数。
//spi.c文件
/* SPI1 init function */
void MX_SPI1_Init(void)
{
/* USER CODE BEGIN SPI1_Init 0 */
/* USER CODE END SPI1_Init 0 */
/* USER CODE BEGIN SPI1_Init 1 */
/* USER CODE END SPI1_Init 1 */
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi1.Init.NSS = SPI_NSS_SOFT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256;//281.25KBits/s低速模式
// hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;//4.5MB/s快速模式
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi1.Init.CRCPolynomial = 10;
if (HAL_SPI_Init(&hspi1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN SPI1_Init 2 */
/* USER CODE END SPI1_Init 2 */
}
- 🌿修改FAtFS驱动SPi初始化函数:将
FCLK_SLOW();
改为FCLK_FAST();
//user_diskio_spi.c文件
/*-----------------------------------------------------------------------*/
/* Initialize disk drive */
/*-----------------------------------------------------------------------*/
inline DSTATUS USER_SPI_initialize (
BYTE drv /* Physical drive number (0) */
)
{
BYTE n, cmd, ty, ocr[4];
if (drv != 0) return STA_NOINIT; /* Supports only drive 0 */
//assume SPI already init init_spi(); /* Initialize SPI */
if (Stat & STA_NODISK) return Stat; /* Is card existing in the soket? */
FCLK_SLOW();//低速模式
// FCLK_FAST();//快速模式
for (n = 10; n; n--) xchg_spi(0xFF); /* Send 80 dummy clocks */
ty = 0;
if (send_cmd(CMD0, 0) == 1) { /* Put the card SPI/Idle state */
SPI_Timer_On(1000); /* Initialization timeout = 1 sec */
if (send_cmd(CMD8, 0x1AA) == 1) { /* SDv2? */
for (n = 0; n < 4; n++) ocr[n] = xchg_spi(0xFF); /* Get 32 bit return value of R7 resp */
if (ocr[2] == 0x01 && ocr[3] == 0xAA) { /* Is the card supports vcc of 2.7-3.6V? */
while (SPI_Timer_Status() && send_cmd(ACMD41, 1UL << 30)) ; /* Wait for end of initialization with ACMD41(HCS) */
if (SPI_Timer_Status() && send_cmd(CMD58, 0) == 0) { /* Check CCS bit in the OCR */
for (n = 0; n < 4; n++) ocr[n] = xchg_spi(0xFF);
ty = (ocr[0] & 0x40) ? CT_SD2 | CT_BLOCK : CT_SD2; /* Card id SDv2 */
}
}
} else { /* Not SDv2 card */
if (send_cmd(ACMD41, 0) <= 1) { /* SDv1 or MMC? */
ty = CT_SD1; cmd = ACMD41; /* SDv1 (ACMD41(0)) */
} else {
ty = CT_MMC; cmd = CMD1; /* MMCv3 (CMD1(0)) */
}
while (SPI_Timer_Status() && send_cmd(cmd, 0)) ; /* Wait for end of initialization */
if (!SPI_Timer_Status() || send_cmd(CMD16, 512) != 0) /* Set block length: 512 */
ty = 0;
}
}
CardType = ty; /* Card type */
despiselect();
if (ty) { /* OK */
FCLK_FAST(); /* Set fast clock */
Stat &= ~STA_NOINIT; /* Clear STA_NOINIT flag */
} else { /* Failed */
Stat = STA_NOINIT;
}
return Stat;
}
📝main主程序代码
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2023 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "fatfs.h"
#include "spi.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include <string.h>
#include <stdarg.h> //for va_list var arg functions
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
void myprintf(const char* fmt, ...);
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void myprintf(const char* fmt, ...)
{
static char buffer[256];
va_list args;
va_start(args, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args);
int len = strlen(buffer);
HAL_UART_Transmit(&huart1, (uint8_t*)buffer, len, 10000);
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_SPI1_Init();
MX_USART1_UART_Init();
MX_FATFS_Init();
/* USER CODE BEGIN 2 */
myprintf("\r\n SD card demo \r\n");
//some variables for FatFs
FATFS FatFs; //Fatfs handle
FIL fil; //File handle
FRESULT fres; //Result after operations
fres = f_mount(&FatFs, "", 1); //1=mount now
if(fres != FR_OK)
{
myprintf("f_mount error (%i)\r\n", fres);
while(1);
}
//Let's get some statistics from the SD card
DWORD free_clusters, free_sectors, total_sectors;
FATFS* getFreeFs;
fres = f_getfree("", &free_clusters, &getFreeFs);
if(fres != FR_OK)
{
myprintf("f_getfree error (%i)\r\n", fres);
while(1);
}
//Formula comes from ChaN's documentation
total_sectors = (getFreeFs->n_fatent - 2) * getFreeFs->csize;
free_sectors = free_clusters * getFreeFs->csize;
myprintf("SD card stats:\r\n%10lu KiB total drive space.\r\n%10lu KiB available.\r\n", total_sectors / 2, free_sectors / 2);
myprintf("SD Total Size:%d MB SD Free Size:%d\r\n", total_sectors / 2048, free_sectors / 2048);
//Now let's try to open file "test.txt"
fres = f_open(&fil, "test.txt", FA_READ);
if(fres != FR_OK)
{
myprintf("f_open error (%i)\r\n", fres);
while(1);
}
myprintf("I was able to open 'test.txt' for reading!\r\n");
//Read 30 bytes from "test.txt" on the SD card
BYTE readBuf[30];
//We can either use f_read OR f_gets to get data out of files
//f_gets is a wrapper on f_read that does some string formatting for us
TCHAR* rres = f_gets((TCHAR*)readBuf, 30, &fil);
if(rres != 0)
{
myprintf("Read string from 'test.txt' contents: %s\r\n", readBuf);
}
else
{
myprintf("f_gets error (%i)\r\n", fres);
}
//Be a tidy kiwi - don't forget to close your file!
f_close(&fil);
//Now let's try and write a file "write.txt"
fres = f_open(&fil, "write.txt", FA_WRITE | FA_OPEN_ALWAYS | FA_CREATE_ALWAYS);
if(fres == FR_OK)
{
myprintf("I was able to open 'write.txt' for writing\r\n");
}
else
{
myprintf("f_open error (%i)\r\n", fres);
}
//Copy in a string
strncpy((char*)readBuf, "a new file is made!", 19);
UINT bytesWrote;
fres = f_write(&fil, readBuf, 19, &bytesWrote);
if(fres == FR_OK)
{
myprintf("Wrote %i bytes to 'write.txt'!\r\n", bytesWrote);
}
else
{
myprintf("f_write error (%i)\r\n", fres);
}
//Be a tidy kiwi - don't forget to close your file!
f_close(&fil);
//We're done, so de-mount the drive
f_mount(NULL, "", 0);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while(1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
HAL_Delay(1000);
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while(1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
📚工程源码
链接:https://pan.baidu.com/s/1Rn3wk2-sZQii3uD5wt8DAw
提取码:4x6f