STM32F103基于HAL工程挂载FatFS驱动SD卡

news2024/11/24 20:06:58

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

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

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

相关文章

Scala入门【变量和数据类型】

目录 Scala基本认知 Hello Scala 方法的定义 伴生对象 Java&#xff1a; Scala&#xff1a; 2、变量和数据类型 2.1、注释 2.2、变量和常量 Java中的变量和常量 Scala基本语法 2.3、标识符 2.4、字符串 基本语法 2.5、标准输入输出 基本语法 2.6、文件的读写 …

6-8 二分查找

今天是端午节&#xff0c;祝大家端午节快乐~ 竟然这样&#xff0c;还不点点赞。 言归正传┏ (゜ω゜)☞ 目录 引入 二分查找算法思想 时间复杂度O&#xff08;logN&#xff09; 二分查找算法描述 二分查找算法的框架如下&#xff1a; 例题1&#xff1a; 例题2&#x…

秒懂SpringBoot之如何集成SpringDoc(全网目前最新最系统最全面的springdoc教程)

[版权申明] 非商业目的注明出处可自由转载 出自&#xff1a;shusheng007 文章目录 概述概念解释SpringDoc使用简单集成配置文档信息配置文档分组使用注解TagOperationSchemaParameterParametersApiResponses 和ApiResponse 认证授权无需认证需要认证 总结源码 概述 近来颇为懈…

13. WebGPU 正交投影

在上一篇文章中&#xff0c;讨论了矩阵的工作原理。讨论了如何通过 1 个矩阵和一些神奇的矩阵数学来完成平移、旋转、缩放&#xff0c;甚至从像素到裁剪空间的投影。实现 3D 操作 只需要再向前迈一小步。 在之前的 2D 示例中&#xff0c;将 2D 点 (x, y) 乘以 3x3 矩阵。要实现…

【机器学习】——续上:卷积神经网络(CNN)与参数训练

目录 引入 一、CNN基本结构 1、卷积层 2、下采样层 3、全连接层 二、CNN参数训练 总结 引入 卷积神经网络&#xff08;CNN&#xff09;是一种有监督深度模型框架&#xff0c;尤其适合处理二维数据问题&#xff0c;如行人检测、人脸识别、信号处理等领域&#xff0c;是带…

19c rac添加节点

在正常的节点 [rootdb1 ~]# xhost access control disabled, clients can connect from any host [rootdb1 ~]# su - grid ASM1:/home/griddb1>export DISPLAY:1.0 ASM1:/home/griddb1>$ORACLE_HOME/gridSetup.sh [rootdb2 ~]# /tmp/GridSetupActions2021-09-16_…

基于Nginx1.22+PHP8+MySQL8安装Discuz! X3.5

基于Nginx1.22PHP8MySQL8安装Discuz! X3.5 1. 安装PHP82. 安装MySQL83. 配置Nginx1.224. 安装Discuz! X3.5 1. 安装PHP8 更新系统&#xff1a; yum update安装EPEL存储库&#xff1a; yum install epel-release安装Remi存储库&#xff08;提供了最新的 PHP 版本&#xff09;&…

【十三】druid 原理解析

druid 原理解析 先前写了一篇博客关于druid集成相关的&#xff0c;这里来分析一下druid原理&#xff0c;结合这两篇文章希望读者能够把druid理解透彻。 一、druid介绍 Druid连接池是阿里巴巴开源的数据库连接池项目。Druid连接池为监控而生&#xff0c;内置强大的监控功能&…

【前端JS交互篇】ECMA核心语法 ——常量、变量、数据类型、各种弹框

一、javascript简介 1.1 Javascript简史 在WEB日益发展的同时&#xff0c;网页的大小和复杂性不断增加&#xff0c;受制于网速的限制&#xff0c;为完成简单的表单验证而频繁地与服务器交换数据只会加重用户的负担&#xff0c;当时走在技术革新最前沿的Netscape&#xff08;网…

阿里云服务器的弹性计算能力如何?是否支持按需扩展和缩减?

阿里云服务器的弹性计算能力如何&#xff1f;是否支持按需扩展和缩减&#xff1f;   【本文由阿里云代理商[聚搜云www.4526.cn]撰写】   阿里云服务器&#xff0c;作为业界领先的云计算服务提供商&#xff0c;其弹性计算能力是如何体现的&#xff1f;是否真的支持按需扩展和…

定制你的Blocks UI布局:Gradio的Block Layouts模块介绍

❤️觉得内容不错的话&#xff0c;欢迎点赞收藏加关注&#x1f60a;&#x1f60a;&#x1f60a;&#xff0c;后续会继续输入更多优质内容❤️ &#x1f449;有问题欢迎大家加关注私戳或者评论&#xff08;包括但不限于NLP算法相关&#xff0c;linux学习相关&#xff0c;读研读博…

【python】如何在 Python 中创建相关矩阵

目录 一、说明 二、相关理论 2.1 何为相关 2.2 相关的前提 2.3 Correlation Matrix是个啥&#xff1f; 2.4 皮尔逊相关系数 三、Python演示如何创建相关矩阵 四、数据可视化观察 五、后记 一、说明 本教程介绍如何在 Python 中创建和解释相关矩阵。然而&#xff0c;创…

English Learning - L3 作业打卡 Lesson7 Day47 2023.6.20 周二

English Learning - L3 作业打卡 Lesson7 Day47 2023.6.20 周二 引言&#x1f349;句1: Growing up in a hot Las Vegas desert, all I wanted was to be free.成分划分弱读连读语调 &#x1f349;句2: I would daydream about traveling the world, living in a place where i…

有三个线程,分别只能打印A,B和C要求按顺序打印ABC,打印10次(多种方法,小白也懂)

目录 第一种方法:使用LockSupport的park和unpark功能(推荐) 第二种方式:synchronizedwaitnotify 第三种:暴力循环方法(不推荐) 第一种方法:使用LockSupport的park和unpark功能(推荐) 简单来说我们有一个名为LockSupport的方法 park就是阻塞当前进程 unpark就是取消阻塞让其…

DRIFTINGBLUES: 4实战演练

文章目录 DRIFTINGBLUES: 4实战演练一、前期准备1、相关信息 二、信息收集1、端口扫描2、访问网站3、查看源码4、解密5、访问网页6、解密7、访问网页8、微信扫一扫9、爆破FTP10、登录FTP11、下载文件并查看12、写入SSH密钥并上传13、SSH连接 三、后渗透1、查看第一个flag2、查找…

Golang | Web开发之Gin静态资源映射及HTML模板渲染

欢迎关注「全栈工程师修炼指南」公众号 点击 &#x1f447; 下方卡片 即可关注我哟! 设为「星标⭐」每天带你 基础入门 到 进阶实践 再到 放弃学习&#xff01; 专注 企业运维实践、网络安全、系统运维、应用开发、物联网实战、全栈文章 等知识分享 “ 花开堪折直须折&#xf…

[Eigen中文文档] 稀疏矩阵操作

文档总目录 本文目录 稀疏矩阵格式SparseMatrix 类 第一个示例SparseMatrix 类矩阵和向量属性迭代非零系数 填充稀疏矩阵支持的运算符和函数基本操作矩阵乘积块操作三角形视图和自共轭视图 英文原文(Sparse matrix manipulations) 处理和解决稀疏问题涉及各种模块&#xff0c…

【马蹄集】第十六周作业

第十六周作业 目录 MT2149 最长子段和MT2150 旅费MT2156 矩阵取数MT2157 迷宫MT2155 四柱河内塔 MT2149 最长子段和 难度&#xff1a;钻石    时间限制&#xff1a;1秒    占用内存&#xff1a;128M 题目描述 给出一个长度为 n n n 的序列 A A A&#xff0c;选出其中连续…

Android studio的安装的详细过程

Android开发环境 Eclipse Eclipse最初是由IBM公司开发的替代商业软件Visual Age for Java的下一代IDE开发环境&#xff0c;2001年11月贡献给开源社区&#xff0c;现在它由非营利软件供应商联盟Eclipse基金会&#xff08;Eclipse Foundation&#xff09;管理。 Eclipse是一种面…

动手实现条件随机场(上)

引言 本文基于PyTorch实现条件随机场&#xff0c;实现CRF层参考论文Neural Architectures for Named Entity Recognition中关于CRF层的描述。包含大量的图解和例子说明&#xff0c;看完一定能理解&#xff01; 论文地址&#xff1a; https://arxiv.org/pdf/1603.01360.pdf 也可…