一、软件硬件说明

软件:STM32CubeMX V6.6.1 /KEIL5 V5.29

硬件:正点原子mini开发板,SD卡,通过SPI方式驱动SD卡,用的是SPI1接口

以上内容来源于正点原子mini开发板手册,SD卡的详细介绍也可以去查阅这个手册。

二、STM32CubeMX配置

  1. RCC配置

  1. SYS配置

  1. USART1配置,用于输出调试信息,参数默认

  1. SPI1配置,具体参数如下

  1. FATFS配置,参数如下

  1. 时钟配置

  1. 工程配置,默认的堆栈改大一点

  1. 生成工程,点击GENERATE CODE,生成代码

三、代码介绍

串口重映射,printf,方便输出调试信息

usart.c

/* USER CODE BEGIN Header */
/********************************************************************************* @file    usart.c* @brief   This file provides code for the configuration*          of the USART instances.******************************************************************************* @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 "usart.h"/* USER CODE BEGIN 0 *//* USER CODE END 0 */UART_HandleTypeDef huart1;/* USART1 init function */void MX_USART1_UART_Init(void)
{/* USER CODE BEGIN USART1_Init 0 *//* USER CODE END USART1_Init 0 *//* USER CODE BEGIN USART1_Init 1 *//* USER CODE END USART1_Init 1 */huart1.Instance = USART1;huart1.Init.BaudRate = 115200;huart1.Init.WordLength = UART_WORDLENGTH_8B;huart1.Init.StopBits = UART_STOPBITS_1;huart1.Init.Parity = UART_PARITY_NONE;huart1.Init.Mode = UART_MODE_TX_RX;huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;huart1.Init.OverSampling = UART_OVERSAMPLING_16;if (HAL_UART_Init(&huart1) != HAL_OK){Error_Handler();}/* USER CODE BEGIN USART1_Init 2 *//* USER CODE END USART1_Init 2 */}void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{GPIO_InitTypeDef GPIO_InitStruct = {0};if(uartHandle->Instance==USART1){/* USER CODE BEGIN USART1_MspInit 0 *//* USER CODE END USART1_MspInit 0 *//* USART1 clock enable */__HAL_RCC_USART1_CLK_ENABLE();__HAL_RCC_GPIOA_CLK_ENABLE();/**USART1 GPIO ConfigurationPA9     ------> USART1_TXPA10     ------> USART1_RX*/GPIO_InitStruct.Pin = GPIO_PIN_9;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);GPIO_InitStruct.Pin = GPIO_PIN_10;GPIO_InitStruct.Mode = GPIO_MODE_INPUT;GPIO_InitStruct.Pull = GPIO_NOPULL;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);/* USER CODE BEGIN USART1_MspInit 1 *//* USER CODE END USART1_MspInit 1 */}
}void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle)
{if(uartHandle->Instance==USART1){/* USER CODE BEGIN USART1_MspDeInit 0 *//* USER CODE END USART1_MspDeInit 0 *//* Peripheral clock disable */__HAL_RCC_USART1_CLK_DISABLE();/**USART1 GPIO ConfigurationPA9     ------> USART1_TXPA10     ------> USART1_RX*/HAL_GPIO_DeInit(GPIOA, GPIO_PIN_9|GPIO_PIN_10);/* USER CODE BEGIN USART1_MspDeInit 1 *//* USER CODE END USART1_MspDeInit 1 */}
}/* USER CODE BEGIN 1 */
#include "stdio.h"
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endifPUTCHAR_PROTOTYPE//串口重定向
{HAL_UART_Transmit(&huart1 , (uint8_t *)&ch, 1, 0xFFFF);return ch;
}/* USER CODE END 1 */

spi.c

/* USER CODE BEGIN Header */
/********************************************************************************* @file    spi.c* @brief   This file provides code for the configuration*          of the SPI instances.******************************************************************************* @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 "spi.h"/* USER CODE BEGIN 0 */
#include "main.h"
/* USER CODE END 0 */SPI_HandleTypeDef hspi1;/* 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_8;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 */}void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle)
{GPIO_InitTypeDef GPIO_InitStruct = {0};if(spiHandle->Instance==SPI1){/* USER CODE BEGIN SPI1_MspInit 0 *//* USER CODE END SPI1_MspInit 0 *//* SPI1 clock enable */__HAL_RCC_SPI1_CLK_ENABLE();__HAL_RCC_GPIOA_CLK_ENABLE();/**SPI1 GPIO ConfigurationPA5     ------> SPI1_SCKPA6     ------> SPI1_MISOPA7     ------> SPI1_MOSI*/GPIO_InitStruct.Pin = GPIO_PIN_5|GPIO_PIN_7;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);GPIO_InitStruct.Pin = GPIO_PIN_6;GPIO_InitStruct.Mode = GPIO_MODE_INPUT;GPIO_InitStruct.Pull = GPIO_NOPULL;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);/* USER CODE BEGIN SPI1_MspInit 1 *//* USER CODE END SPI1_MspInit 1 */}
}void HAL_SPI_MspDeInit(SPI_HandleTypeDef* spiHandle)
{if(spiHandle->Instance==SPI1){/* USER CODE BEGIN SPI1_MspDeInit 0 *//* USER CODE END SPI1_MspDeInit 0 *//* Peripheral clock disable */__HAL_RCC_SPI1_CLK_DISABLE();/**SPI1 GPIO ConfigurationPA5     ------> SPI1_SCKPA6     ------> SPI1_MISOPA7     ------> SPI1_MOSI*/HAL_GPIO_DeInit(GPIOA, GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7);/* USER CODE BEGIN SPI1_MspDeInit 1 *//* USER CODE END SPI1_MspDeInit 1 */}
}/* USER CODE BEGIN 1 */
//SPI速度设置函数
//SPI速度=fAPB1/分频系数
//@ref SPI_BaudRate_Prescaler:SPI_BAUDRATEPRESCALER_2~SPI_BAUDRATEPRESCALER_2 256
//fAPB1时钟一般为42Mhz:
void SPI1_SetSpeed(u8 SPI_BaudRatePrescaler)
{assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));//判断有效性__HAL_SPI_DISABLE(&hspi1);            //关闭SPIhspi1.Instance->CR1&=0XFFC7;          //位3-5清零,用来设置波特率hspi1.Instance->CR1|=SPI_BaudRatePrescaler;//设置SPI速度__HAL_SPI_ENABLE(&hspi1);             //使能SPI
}//SPI1 读写一个字节
//TxData:要写入的字节
//返回值:读取到的字节
u8 SPI1_ReadWriteByte(u8 TxData)
{u8 Rxdata;HAL_SPI_TransmitReceive(&hspi1,&TxData,&Rxdata,1, 1000);       return Rxdata;                      //返回收到的数据
}
/* USER CODE END 1 */

MMC_SD.c

#include "mmc_sd.h"
#include "spi.h"
#include "usart.h"    u8  SD_Type=0;//SD卡的类型
移植修改区///
//移植时候的接口
//data:要写入的数据
//返回值:读到的数据
u8 SD_SPI_ReadWriteByte(u8 data)
{return SPI1_ReadWriteByte(data);
}//SD卡初始化的时候,需要低速
void SD_SPI_SpeedLow(void)
{SPI1_SetSpeed(SPI_BAUDRATEPRESCALER_256);//设置到低速模式
}//SD卡正常工作的时候,可以高速了
void SD_SPI_SpeedHigh(void)
{SPI1_SetSpeed(SPI_BAUDRATEPRESCALER_2);//设置到高速模式
}//SPI硬件层初始化
void SD_SPI_Init(void)
{//设置硬件上与SD卡相关联的控制引脚输出//禁止其他外设(NRF/W25Q64)对SD卡产生影响GPIO_InitTypeDef GPIO_Initure;__HAL_RCC_GPIOA_CLK_ENABLE();               //开启GPIOA时钟//PA2.3.4GPIO_Initure.Pin=GPIO_PIN_2|GPIO_PIN_3|GPIO_PIN_4; GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP;      //推挽输出GPIO_Initure.Pull=GPIO_PULLUP;              //上拉GPIO_Initure.Speed=GPIO_SPEED_HIGH;             //高速HAL_GPIO_Init(GPIOA,&GPIO_Initure);HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOA,GPIO_PIN_3,GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);MX_SPI1_Init();SD_CS_1;
}
///
//取消选择,释放SPI总线
void SD_DisSelect(void)
{SD_CS_1;SD_SPI_ReadWriteByte(0xff);//提供额外的8个时钟
}//选择sd卡,并且等待卡准备OK
//返回值:0,成功;1,失败;
u8 SD_Select(void)
{SD_CS_0;if(SD_WaitReady()==0)return 0;//等待成功SD_DisSelect();return 1;//等待失败
}//等待卡准备好
//返回值:0,准备好了;其他,错误代码
u8 SD_WaitReady(void)
{u32 t=0;do{if(SD_SPI_ReadWriteByte(0XFF)==0XFF)return 0;//OKt++;              }while(t<0XFFFFFF);//等待 return 1;
}//等待SD卡回应
//Response:要得到的回应值
//返回值:0,成功得到了该回应值
//    其他,得到回应值失败
u8 SD_GetResponse(u8 Response)
{u16 Count=0xFFFF;//等待次数                                 while ((SD_SPI_ReadWriteByte(0XFF)!=Response)&&Count)Count--;//等待得到准确的回应        if (Count==0)return MSD_RESPONSE_FAILURE;//得到回应失败   else return MSD_RESPONSE_NO_ERROR;//正确回应
}//从sd卡读取一个数据包的内容
//buf:数据缓存区
//len:要读取的数据长度.
//返回值:0,成功;其他,失败;
u8 SD_RecvData(u8*buf,u16 len)
{                    if(SD_GetResponse(0xFE))return 1;//等待SD卡发回数据起始令牌0xFEwhile(len--)//开始接收数据{*buf=SPI1_ReadWriteByte(0xFF);buf++;}//下面是2个伪CRC(dummy CRC)SD_SPI_ReadWriteByte(0xFF);SD_SPI_ReadWriteByte(0xFF);                                                              return 0;//读取成功
}//向sd卡写入一个数据包的内容 512字节
//buf:数据缓存区
//cmd:指令
//返回值:0,成功;其他,失败;
u8 SD_SendBlock(u8*buf,u8 cmd)
{    u16 t;                if(SD_WaitReady())return 1;//等待准备失效SD_SPI_ReadWriteByte(cmd);if(cmd!=0XFD)//不是结束指令{for(t=0;t<512;t++)SPI1_ReadWriteByte(buf[t]);//提高速度,减少函数传参时间SD_SPI_ReadWriteByte(0xFF);//忽略crcSD_SPI_ReadWriteByte(0xFF);t=SD_SPI_ReadWriteByte(0xFF);//接收响应if((t&0x1F)!=0x05)return 2;//响应错误                                                              }                                                                                       return 0;//写入成功
}//向SD卡发送一个命令
//输入: u8 cmd   命令
//      u32 arg  命令参数
//      u8 crc   crc校验值
//返回值:SD卡返回的响应
u8 SD_SendCmd(u8 cmd, u32 arg, u8 crc)
{u8 r1;    u8 Retry=0; SD_DisSelect();//取消上次片选if(SD_Select())return 0XFF;//片选失效 //发送SD_SPI_ReadWriteByte(cmd | 0x40);//分别写入命令SD_SPI_ReadWriteByte(arg >> 24);SD_SPI_ReadWriteByte(arg >> 16);SD_SPI_ReadWriteByte(arg >> 8);SD_SPI_ReadWriteByte(arg);      SD_SPI_ReadWriteByte(crc); if(cmd==CMD12)SD_SPI_ReadWriteByte(0xff);//Skip a stuff byte when stop reading//等待响应,或超时退出Retry=0X1F;do{r1=SD_SPI_ReadWriteByte(0xFF);}while((r1&0X80) && Retry--);     //返回状态值return r1;
}
//获取SD卡的CID信息,包括制造商信息
//输入: u8 *cid_data(存放CID的内存,至少16Byte)
//返回值:0:NO_ERR
//         1:错误
u8 SD_GetCID(u8 *cid_data)
{u8 r1;       //发CMD10命令,读CIDr1=SD_SendCmd(CMD10,0,0x01);if(r1==0x00){r1=SD_RecvData(cid_data,16);//接收16个字节的数据     }SD_DisSelect();//取消片选if(r1)return 1;else return 0;
}
//获取SD卡的CSD信息,包括容量和速度信息
//输入:u8 *cid_data(存放CID的内存,至少16Byte)
//返回值:0:NO_ERR
//         1:错误
u8 SD_GetCSD(u8 *csd_data)
{u8 r1;     r1=SD_SendCmd(CMD9,0,0x01);//发CMD9命令,读CSDif(r1==0){r1=SD_RecvData(csd_data, 16);//接收16个字节的数据 }SD_DisSelect();//取消片选if(r1)return 1;else return 0;
}
//获取SD卡的总扇区数(扇区数)
//返回值:0: 取容量出错
//       其他:SD卡的容量(扇区数/512字节)
//每扇区的字节数必为512,因为如果不是512,则初始化不能通过.
u32 SD_GetSectorCount(void)
{u8 csd[16];u32 Capacity;  u8 n;u16 csize;                          //取CSD信息,如果期间出错,返回0if(SD_GetCSD(csd)!=0) return 0;        //如果为SDHC卡,按照下面方式计算if((csd[0]&0xC0)==0x40)     //V2.00的卡{    csize = csd[9] + ((u16)csd[8] << 8) + 1;Capacity = (u32)csize << 10;//得到扇区数                }else//V1.XX的卡{    n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;csize = (csd[8] >> 6) + ((u16)csd[7] << 2) + ((u16)(csd[6] & 3) << 10) + 1;Capacity= (u32)csize << (n - 9);//得到扇区数   }return Capacity;
}
//初始化SD卡
u8 SD_Init(void)
{u8 r1;      // 存放SD卡的返回值u16 retry;  // 用来进行超时计数u8 buf[4];  u16 i;SD_SPI_Init();        //初始化IOSD_SPI_SpeedLow();    //设置到低速模式 for(i=0;i<10;i++)SD_SPI_ReadWriteByte(0XFF);//发送最少74个脉冲retry=20;do{r1=SD_SendCmd(CMD0,0,0x95);//进入IDLE状态}while((r1!=0X01) && retry--);SD_Type=0;//默认无卡if(r1==0X01){if(SD_SendCmd(CMD8,0x1AA,0x87)==1)//SD V2.0{for(i=0;i<4;i++)buf[i]=SD_SPI_ReadWriteByte(0XFF);    //Get trailing return value of R7 respif(buf[2]==0X01&&buf[3]==0XAA)//卡是否支持2.7~3.6V{retry=0XFFFE;do{SD_SendCmd(CMD55,0,0X01);    //发送CMD55r1=SD_SendCmd(CMD41,0x40000000,0X01);//发送CMD41}while(r1&&retry--);if(retry&&SD_SendCmd(CMD58,0,0X01)==0)//鉴别SD2.0卡版本开始{for(i=0;i<4;i++)buf[i]=SD_SPI_ReadWriteByte(0XFF);//得到OCR值if(buf[0]&0x40)SD_Type=SD_TYPE_V2HC;    //检查CCSelse SD_Type=SD_TYPE_V2;   }}}else//SD V1.x/ MMC    V3{SD_SendCmd(CMD55,0,0X01);        //发送CMD55r1=SD_SendCmd(CMD41,0,0X01);    //发送CMD41if(r1<=1){        SD_Type=SD_TYPE_V1;retry=0XFFFE;do //等待退出IDLE模式{SD_SendCmd(CMD55,0,0X01);    //发送CMD55r1=SD_SendCmd(CMD41,0,0X01);//发送CMD41}while(r1&&retry--);}else//MMC卡不支持CMD55+CMD41识别{SD_Type=SD_TYPE_MMC;//MMC V3retry=0XFFFE;do //等待退出IDLE模式{                                                r1=SD_SendCmd(CMD1,0,0X01);//发送CMD1}while(r1&&retry--);  }if(retry==0||SD_SendCmd(CMD16,512,0X01)!=0)SD_Type=SD_TYPE_ERR;//错误的卡}}SD_DisSelect();//取消片选SD_SPI_SpeedHigh();//高速if(SD_Type)return 0;else if(r1)return r1;        return 0xaa;//其他错误
}//读SD卡
//buf:数据缓存区
//sector:扇区
//cnt:扇区数
//返回值:0,ok;其他,失败.
u8 SD_ReadDisk(u8*buf,u32 sector,u8 cnt)
{u8 r1;if(SD_Type!=SD_TYPE_V2HC)sector <<= 9;//转换为字节地址if(cnt==1){r1=SD_SendCmd(CMD17,sector,0X01);//读命令if(r1==0)//指令发送成功{r1=SD_RecvData(buf,512);//接收512个字节       }}else{r1=SD_SendCmd(CMD18,sector,0X01);//连续读命令do{r1=SD_RecvData(buf,512);//接收512个字节     buf+=512;  }while(--cnt && r1==0);     SD_SendCmd(CMD12,0,0X01);    //发送停止命令}   SD_DisSelect();//取消片选return r1;//
}//写SD卡
//buf:数据缓存区
//sector:起始扇区
//cnt:扇区数
//返回值:0,ok;其他,失败.
u8 SD_WriteDisk(u8*buf,u32 sector,u8 cnt)
{u8 r1;if(SD_Type!=SD_TYPE_V2HC)sector *= 512;//转换为字节地址if(cnt==1){r1=SD_SendCmd(CMD24,sector,0X01);//读命令if(r1==0)//指令发送成功{r1=SD_SendBlock(buf,0xFE);//写512个字节       }}else{if(SD_Type!=SD_TYPE_MMC){SD_SendCmd(CMD55,0,0X01);    SD_SendCmd(CMD23,cnt,0X01);//发送指令    }r1=SD_SendCmd(CMD25,sector,0X01);//连续读命令if(r1==0){do{r1=SD_SendBlock(buf,0xFC);//接收512个字节     buf+=512;  }while(--cnt && r1==0);r1=SD_SendBlock(0,0xFD);//接收512个字节 }}   SD_DisSelect();//取消片选return r1;
}

MMC_SD.h

#ifndef _MMC_SD_H_
#define _MMC_SD_H_#include "stm32f1xx_hal.h"
#include "main.h"#define SD_CS_1  HAL_GPIO_WritePin(GPIOA,GPIO_PIN_3,GPIO_PIN_SET)
#define SD_CS_0  HAL_GPIO_WritePin(GPIOA,GPIO_PIN_3,GPIO_PIN_RESET)// SD卡类型定义
#define SD_TYPE_ERR     0X00
#define SD_TYPE_MMC     0X01
#define SD_TYPE_V1      0X02
#define SD_TYPE_V2      0X04
#define SD_TYPE_V2HC    0X06// SD卡指令表
#define CMD0    0       //卡复位
#define CMD1    1
#define CMD8    8       //命令8 ,SEND_IF_COND
#define CMD9    9       //命令9 ,读CSD数据
#define CMD10   10      //命令10,读CID数据
#define CMD12   12      //命令12,停止数据传输
#define CMD16   16      //命令16,设置SectorSize 应返回0x00
#define CMD17   17      //命令17,读sector
#define CMD18   18      //命令18,读Multi sector
#define CMD23   23      //命令23,设置多sector写入前预先擦除N个block
#define CMD24   24      //命令24,写sector
#define CMD25   25      //命令25,写Multi sector
#define CMD41   41      //命令41,应返回0x00
#define CMD55   55      //命令55,应返回0x01
#define CMD58   58      //命令58,读OCR信息
#define CMD59   59      //命令59,使能/禁止CRC,应返回0x00
//数据写入回应字意义
#define MSD_DATA_OK                0x05
#define MSD_DATA_CRC_ERROR         0x0B
#define MSD_DATA_WRITE_ERROR       0x0D
#define MSD_DATA_OTHER_ERROR       0xFF
//SD卡回应标记字
#define MSD_RESPONSE_NO_ERROR      0x00
#define MSD_IN_IDLE_STATE          0x01
#define MSD_ERASE_RESET            0x02
#define MSD_ILLEGAL_COMMAND        0x04
#define MSD_COM_CRC_ERROR          0x08
#define MSD_ERASE_SEQUENCE_ERROR   0x10
#define MSD_ADDRESS_ERROR          0x20
#define MSD_PARAMETER_ERROR        0x40
#define MSD_RESPONSE_FAILURE       0xFF//这部分应根据具体的连线来修改!
//MiniSTM32开发板使用的是PA3作为SD卡的CS脚.
#define    SD_CS  PAout(3)     //SD卡片选引脚                              extern u8  SD_Type;            //SD卡的类型
//函数申明区
u8 SD_SPI_ReadWriteByte(u8 data);
void SD_SPI_SpeedLow(void);
void SD_SPI_SpeedHigh(void);
u8 SD_WaitReady(void);                            //等待SD卡准备
u8 SD_GetResponse(u8 Response);                    //获得相应
u8 SD_Init(void);                                //初始化
u8 SD_ReadDisk(u8*buf,u32 sector,u8 cnt);        //读块
u8 SD_WriteDisk(u8*buf,u32 sector,u8 cnt);        //写块
u32 SD_GetSectorCount(void);                       //读扇区数
u8 SD_GetCID(u8 *cid_data);                     //读SD卡CID
u8 SD_GetCSD(u8 *csd_data);                     //读SD卡CSD#endif

user_diskio.c 实现FATFS读写SD卡的主要代码

/* USER CODE BEGIN Header */
/********************************************************************************* @file    user_diskio.c* @brief   This file includes a diskio driver skeleton to be completed by the user.******************************************************************************* @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 */#ifdef USE_OBSOLETE_USER_CODE_SECTION_0
/** Warning: the user section 0 is no more in use (starting from CubeMx version 4.16.0)* To be suppressed in the future.* Kept to ensure backward compatibility with previous CubeMx versions when* migrating projects.* User code previously added there should be copied in the new user sections before* the section contents can be deleted.*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
#endif/* USER CODE BEGIN DECL *//* Includes ------------------------------------------------------------------*/
#include <string.h>
#include "ff_gen_drv.h"#include "MMC_SD.h"#define SD_CARD  0/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*//* Private variables ---------------------------------------------------------*/
/* Disk status */
static volatile DSTATUS Stat = STA_NOINIT;/* USER CODE END DECL *//* Private function prototypes -----------------------------------------------*/
DSTATUS USER_initialize (BYTE pdrv);
DSTATUS USER_status (BYTE pdrv);
DRESULT USER_read (BYTE pdrv, BYTE *buff, DWORD sector, UINT count);
#if _USE_WRITE == 1DRESULT USER_write (BYTE pdrv, const BYTE *buff, DWORD sector, UINT count);
#endif /* _USE_WRITE == 1 */
#if _USE_IOCTL == 1DRESULT USER_ioctl (BYTE pdrv, BYTE cmd, void *buff);
#endif /* _USE_IOCTL == 1 */Diskio_drvTypeDef  USER_Driver =
{USER_initialize,USER_status,USER_read,
#if  _USE_WRITEUSER_write,
#endif  /* _USE_WRITE == 1 */
#if  _USE_IOCTL == 1USER_ioctl,
#endif /* _USE_IOCTL == 1 */
};/* Private functions ---------------------------------------------------------*//*** @brief  Initializes a Drive* @param  pdrv: Physical drive number (0..)* @retval DSTATUS: Operation status*/
DSTATUS USER_initialize (BYTE pdrv           /* Physical drive nmuber to identify the drive */
)
{/* USER CODE BEGIN INIT */Stat = RES_OK;return Stat;/* USER CODE END INIT */
}/*** @brief  Gets Disk Status* @param  pdrv: Physical drive number (0..)* @retval DSTATUS: Operation status*/
DSTATUS USER_status (BYTE pdrv       /* Physical drive number to identify the drive */
)
{/* USER CODE BEGIN STATUS */u8 res=0;        switch(pdrv){case SD_CARD:        //SD卡res=SD_Init();    //SD卡初始化 break;default:res=1; }         if(res)return  STA_NOINIT;else return 0; //初始化成功 /* USER CODE END STATUS */
}/*** @brief  Reads Sector(s)* @param  pdrv: Physical drive number (0..)* @param  *buff: Data buffer to store read data* @param  sector: Sector address (LBA)* @param  count: Number of sectors to read (1..128)* @retval DRESULT: Operation result*/
DRESULT USER_read (BYTE pdrv,      /* Physical drive nmuber to identify the drive */BYTE *buff,     /* Data buffer to store read data */DWORD sector,   /* Sector address in LBA */UINT count      /* Number of sectors to read */
)
{/* USER CODE BEGIN READ */u8 res=0; if (!count)return RES_PARERR;//count不能等于0,否则返回参数错误              switch(pdrv){case SD_CARD://SD卡res=SD_ReadDisk(buff,sector,count);     while(res)//读出错{SD_Init();    //重新初始化SD卡res=SD_ReadDisk(buff,sector,count);    }break;default:res=1; }//处理返回值,将SPI_SD_driver.c的返回值转成ff.c的返回值if(res==0x00)return RES_OK;     else return RES_ERROR;       /* USER CODE END READ */
}/*** @brief  Writes Sector(s)* @param  pdrv: Physical drive number (0..)* @param  *buff: Data to be written* @param  sector: Sector address (LBA)* @param  count: Number of sectors to write (1..128)* @retval DRESULT: Operation result*/
#if _USE_WRITE == 1
DRESULT USER_write (BYTE pdrv,          /* Physical drive nmuber to identify the drive */const BYTE *buff,   /* Data to be written */DWORD sector,       /* Sector address in LBA */UINT count          /* Number of sectors to write */
)
{/* USER CODE BEGIN WRITE *//* USER CODE HERE */u8 res=0;  if (!count)return RES_PARERR;//count不能等于0,否则返回参数错误              switch(pdrv){case SD_CARD://SD卡res=SD_WriteDisk((u8*)buff,sector,count);while(res)//写出错{SD_Init();    //重新初始化SD卡res=SD_WriteDisk((u8*)buff,sector,count);    }break;default:res=1; }//处理返回值,将SPI_SD_driver.c的返回值转成ff.c的返回值if(res == 0x00)return RES_OK;     else return RES_ERROR;    /* USER CODE END WRITE */
}
#endif /* _USE_WRITE == 1 *//*** @brief  I/O control operation* @param  pdrv: Physical drive number (0..)* @param  cmd: Control code* @param  *buff: Buffer to send/receive control data* @retval DRESULT: Operation result*/
#if _USE_IOCTL == 1
DRESULT USER_ioctl (BYTE pdrv,      /* Physical drive nmuber (0..) */BYTE cmd,       /* Control code */void *buff      /* Buffer to send/receive control data */
)
{/* USER CODE BEGIN IOCTL */DRESULT res;                                           if(pdrv==SD_CARD)//SD卡{switch(cmd){case CTRL_SYNC:res = RES_OK; break;     case GET_SECTOR_SIZE:*(DWORD*)buff = 512; res = RES_OK;break;     case GET_BLOCK_SIZE:*(WORD*)buff = 8;res = RES_OK;break;     case GET_SECTOR_COUNT:*(DWORD*)buff = SD_GetSectorCount();res = RES_OK;break;default:res = RES_PARERR;break;}}else res=RES_ERROR;//其他的不支持return res;/* USER CODE END IOCTL */
}
#endif /* _USE_IOCTL == 1 */

main.c 测试文件系统读写SD卡

/* 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 "ff.h"
/* 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 *//* USER CODE END PFP *//* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
FRESULT f_res;
UINT bw;
BYTE ReadBuffer[1024]={0};
BYTE WriteBuffer[]= "STM32CubeMX SDcard FATFS Test\r\n";void mount_disk(void)
{f_res = f_mount(&USERFatFS, USERPath, 0);
}void create_file(void)
{printf("write data is : %s\r\n",WriteBuffer);f_res = f_open(&USERFile, "test.txt", FA_OPEN_ALWAYS | FA_WRITE);f_res = f_write(&USERFile, WriteBuffer, sizeof(WriteBuffer), &bw);f_res = f_close(&USERFile);
}void read_file(void)
{f_res = f_open(&USERFile, "test.txt", FA_READ);f_res = f_read(&USERFile, ReadBuffer, sizeof(WriteBuffer), &bw);printf("read data is : %s\r\n",ReadBuffer);f_res = f_close(&USERFile);
}void umount_disk(void)
{f_res = f_mount(NULL, USERPath, 0);
}void FatfsTest(void)
{mount_disk(); //挂载SD卡create_file();//创建TXT文件 read_file();  //读取文件内容并放到ReadBuffer中umount_disk();//卸载SD卡
}
/* 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 */printf("\r\n ****** SDcard FatFs Example ******\r\n\r\n");HAL_Delay (500);FatfsTest();//文件系统测试/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* 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 */

四、程序运行截图

串口调试助手输出信息:

将SD卡插在电脑上,或者通过读卡器连接到电脑上,SD卡截图:

STM32CubeMX+SPI+FATFS读写SD卡相关推荐

  1. SPI方式读写SD卡速度有多快?

    很久没有写公众号了,一方面忙,另一方面也不知道写些什么内容,大家如果有想了解的(前提是我也懂),可以后台发送给我. 今天主要来测试一下SPI读写SD卡的速度.SD卡是一个嵌入式中非常常用的外设,可以用 ...

  2. stm32 Fatfs 读写SD卡

    源:stm32 Fatfs 读写SD卡 转载于:https://www.cnblogs.com/LittleTiger/p/4864052.html

  3. STM32开发板入门教程(十三) - SPI模式读写SD卡

    功能介绍 :使用SPI模式 读写SD卡block数据 可通过串口发送到PC机查看 SD卡是Secure Digital Card卡的简称,直译成汉语就是"安全数字卡",是由日本松下 ...

  4. SPI协议读写SD卡介绍

    前言 在前面的文章中,我们主要介绍了如何利用SDIO协议对SD卡进行读写操作的流程及注意事项.虽然SDIO协议读写SD卡的效率很高,但是,操作却比较麻烦,另外,还需要使用的芯片具有SDIO外设.这对于 ...

  5. STM32使用SPI方式读写SD 卡

    掌握SD卡协议原理,用STM32F103 完成对SD卡的数据读取(FAT文件模式). 文章目录 一.前言 1.SD卡 2.FATFS 二.工程分析 1.代码分析 2.连线 3.编译工程并烧录 4.验证 ...

  6. FatFs读写SD卡出现FR_NO_FILESYSTEM解决方法.

    原文地址:http://www.devlabs.cn/?p=226 18 一月 2014 3 起因 去年做了个GPS路径记录器, 将路径息记录于TF卡上, 上了FatFs系统. 刚开始那会虽然偶尔罢工 ...

  7. FatFs读写SD卡出现FR_NO_FILESYSTEM解决方法.(写的好)

    来源:http://www.devlabs.cn/?p=226 起因 去年做了个GPS路径记录器, 将路径息记录于TF卡上, 上了FatFs系统. 刚开始那会虽然偶尔罢工, 但好歹能工作. 后来没时间 ...

  8. STM32 SPI方式读写SD卡

    前段时间在51上模拟SPI实现了对SD卡的读取,效果还算不错,最近将其移植到STM32上,不过使用硬件SPI和使用软件SPI还是有差别的. 代码如下: void User_SPIInit(void) ...

  9. 使用STM32CUBEMX HAL库读写SD卡

    SD 卡系统(包括主机和 SD 卡)定义了两种操作模式: 卡识别模式 数据传输模式 在系统复位后,主机处于卡识别模式,寻找总线上可用的 SD卡设备:同时,SD 卡也处于卡 识别模式,直到被主机识别到. ...

最新文章

  1. C# 获取当前路径方法
  2. Vue CLI 3 多页应用项目的搭建
  3. 实战SSM_O2O商铺_16【商铺注册】前后端联调验证整体模块功能
  4. zabbix 监控 redis
  5. 基于朴素贝叶斯的垃圾邮件分类-着重理解拉普拉斯变换
  6. 1ke android逆向助手_android: 使用android逆向助手反编译APK
  7. SCSF 系列:Smart Client Software Factory 中 MVP 模式最佳实践
  8. 7 个建议让 Code Review 高效又高质
  9. CentOS 5.x 系统下使用yum 升级php到5.2.x
  10. 中累计直方图_试验研究中的利器强大的直方图和箱线图
  11. 推荐系统中的相似性度量
  12. linux如何查看vlan信息,dhcp – 通过tcpdump在数据包捕获(Linux)中未显示VLAN标记
  13. win10系统下第三方软件字体大小调整
  14. 升级IE11时,失败报错如何解决
  15. Material Design ui设计风格详解
  16. java闰年_java 判断闰年
  17. 全国大学生GIS应用技能大赛(开发试题参考)
  18. github免用户名密码管理代码
  19. 开源之夏来啦,欢迎报名 Apache APISIX 项目!
  20. Y C夏季Demo Day最有趣的8家创业公司

热门文章

  1. 【Java集合】集合是什么?为什么要用集合?
  2. iPhone视频播放器(1)
  3. shiro权限管理实例
  4. 数据维度太多,咋办?我们整理了17种表现形式
  5. vm fusion Linux系统克隆
  6. 51单片机-4G模块
  7. 如何评估 Web3 项目的价值?
  8. 求树的直径算法以及证明
  9. 几款常用压测工具介绍与使用
  10. 5-3中央处理器-数据通路的功能和基本结构