基于STM32的SD卡+STM32CubeMx+FatFS文件系统创建Excel表保存数据

✨1 简介

  1. 通过STM32控制SD卡创建text文件或Excel文件来保存传感器采集到的数据,并且当文件大小超过规定时,自动重新新建文件继续保存数据。
  2. 单片机型号:STM32F103VCT6

✨2 SD卡工作原理

🎉2.1 SD卡基本介绍

  1. SD卡分为SDIO模式与SPI模式,SDIO模式使用SD总线协议,使用4根数据线进行数据传输,SPI使用1收1发2根数据线数据传输,理论上SDIO模式会比SPI模式速度快4倍,但SDIO模式还牵扯到CRC校验位的计算,一般情况下SPI模式就足够了。本文就是基于SPI模式进行开发的。
  2. SD卡一般分为:标准尺寸,Mini SD,Micro SD card(TF卡)。在嵌入式领域,尤其是小设备,通常使用TF卡。下文中除特殊说明外,用SD卡统一称呼而不做区分。
    在这里插入图片描述

🎉2.2 SPI模式介绍

  1. 简要说明:主机首先进行片选,选中从机,同时产生通讯时钟,每到来一个时钟周期,按照配置的SPI通信模式,主机移出一位数据发送到从机,同时从机发送一位数据到主机,完成一位数据交换,重复多次,即可完成一个字节数据,若此次传输完成,取消片选。
  2. 接线:SI主收从发 SO主发从收 SCK 时钟线(主设备产生) SD_CS片选(主设备控制,一主多从时刻控制从机的选择)
    在这里插入图片描述
    😜本次具体连线:SD_CS–PA12 SI–PB5 SO–PB4 SCK–PB3
    在这里插入图片描述

✨STM32CubeMx配置

  1. 常规配置在此不进行展示,相比其他就多一个SPI配置和一个FatFS文件配置,其他都是常见的引脚配置。
    在这里插入图片描述
    在这里插入图片描述

✨FatFS文件系统

  1. 简要概括:FATFS是一个为小型嵌入式系统设计的通用FAT(File Allocation Table)文件系统模块。FatFs 的编写遵循ANSI C,并且完全与磁盘I/O层分开。因此,它独立(不依赖)于硬件架构。它可以被嵌入到低成本的微控制器中,如AVR, 8051, PIC, ARM, Z80, 68K 等等,而不需要做任何修改。 我们直接调用就OK了。
  2. 本次主要用到了以下函数,后面代码有详细使用
    ✔f_open:创建 / 打开一个用于访问文件的文件对象
    ✔f_close :关闭一个打开的文件
    ✔f_lseek:移动一个打开的文件对象的文件读 / 写指针。也可以被用来扩展文件大小 ( 簇预分配 ) 。
    ✔f_read :从一个文件读取数据
    ✔f_write:写入数据到一个文件
    ✔f_mkfs:在驱动器上创建一个文件系统
    ✔f_mount:在 FatFs 模块上注册 / 注销一个工作区 ( 文件系统对象 )
    ✔f_printf:f_printf 函数向文件中写入一个格式化字符串。
    🐱‍🚀🐱‍🚀🐱‍🚀想深入理解:
    FatFS文件系统详细介绍

✨具体代码(直接上干货)

  1. SPI协议:
    😊spi.c 除了STM32CubeMx自动生成的,需要添加以下代码:
//SPI速度设置函数
//SPI速度=fAPB1/分频系数
//fAPB1时钟一般为42Mhz:
void SPI1_SetSpeed(u8 SPI_BaudRatePrescaler)
{
    assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));//判断有效性
    __HAL_SPI_DISABLE(&hspi1);            //关闭SPI
    hspi1.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;                      //返回收到的数据        
}
u8 SPI1_ReadWriteByte(u8 TxData);
void SPI1_SetSpeed(u8 SPI_BaudRatePrescaler);
  1. SD卡的配置程序:m_sd.c 和 m_sd.h
    😊m_sd.c
#include "main.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卡相关联的控制引脚输出
    GPIO_InitTypeDef GPIO_Initure;
    __HAL_RCC_GPIOA_CLK_ENABLE();               //开启GPIOA时钟
    //PA12
    GPIO_Initure.Pin=GPIO_PIN_12
    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_3,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;//OK
        t++;              
    }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卡发回数据起始令牌0xFE
    while(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);//忽略crc
        SD_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命令,读CID
    r1=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命令,读CSD
    if(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信息,如果期间出错,返回0
    if(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();        //初始化IO
     SD_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 resp
            if(buf[2]==0X01&&buf[3]==0XAA)//卡是否支持2.7~3.6V
            {
                retry=0XFFFE;
                do
                {
                    SD_SendCmd(CMD55,0,0X01);    //发送CMD55
                    r1=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;    //检查CCS
                    else SD_Type=SD_TYPE_V2;   
                }
            }
        }else//SD V1.x/ MMC    V3
        {
            SD_SendCmd(CMD55,0,0X01);        //发送CMD55
            r1=SD_SendCmd(CMD41,0,0X01);    //发送CMD41
            if(r1<=1)
            {        
                SD_Type=SD_TYPE_V1;
                retry=0XFFFE;
                do //等待退出IDLE模式
                {
                    SD_SendCmd(CMD55,0,0X01);    //发送CMD55
                    r1=SD_SendCmd(CMD41,0,0X01);//发送CMD41
                }while(r1&&retry--);
            }else//MMC卡不支持CMD55+CMD41识别
            {
                SD_Type=SD_TYPE_MMC;//MMC V3
                retry=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;
}    


  😊m_sd.h
#ifndef __M_SD_H__
#define __M_SD_H__

#include "type.h"  
#include "stm32f1xx_hal.h"    
 
 
#define SD_CS_1  HAL_GPIO_WritePin(GPIOA,GPIO_PIN_12,GPIO_PIN_SET)
#define SD_CS_0  HAL_GPIO_WritePin(GPIOA,GPIO_PIN_12,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
                                                             
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
  1. 实现FATFS读写SD卡的主要代码:
    😊user_diskio.c
#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"

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
#include "m_sd.h"
#define SD_CARD  0
/* 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 == 1
  DRESULT USER_write (BYTE pdrv, const BYTE *buff, DWORD sector, UINT count);
#endif /* _USE_WRITE == 1 */
#if _USE_IOCTL == 1
  DRESULT USER_ioctl (BYTE pdrv, BYTE cmd, void *buff);
#endif /* _USE_IOCTL == 1 */

Diskio_drvTypeDef  USER_Driver =
{
  USER_initialize,
  USER_status,
  USER_read,
#if  _USE_WRITE
  USER_write,
#endif  /* _USE_WRITE == 1 */
#if  _USE_IOCTL == 1
  USER_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 */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

  1. main.c 调用函数具体实现Excel文件的实现
u8 excle_head=0;   //控制Excel表头
u16 num_text[]={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,,16,17};   //模拟传感器收到的数据
u32 SD_number=0; //写入数据进行计数
//sd卡
char SD_FileName[]="data1.csv";   
int SD_FileName_number=1;   //默认为1,超过大小后自动++
//文件超过大小后,新建文件
void SD_FileName_creat(void)
 { 
	SD_FileName_number++; 
    sprintf(SD_FileName,"data%d.csv",SD_FileName_number);
	excle_head=0;
 }
//控制Excel表头的写入
void WritetoSD(char write_buff[],uint8_t bufSize)
{
	FATFS fs;
	FIL file;
	uint8_t res=0;
	UINT Bw;	
	
	res = SD_init();		//SD卡初始化
	
	if(res == 1)
	{
		printf("SD卡初始化失败! \r\n");		
	}
	else
	{
		printf("SD卡初始化成功! \r\n");		
	}
	
	res=f_mount(&fs,"0:",1);		//挂载
	
	if(res == FR_NO_FILESYSTEM)		//没有文件系统,格式化
	{
		printf("没有文件系统! \r\n");		
		res = f_mkfs("", 0, 0);		//格式化sd卡
		if(res == FR_OK)
		{
			printf("格式化成功! \r\n");		
			res = f_mount(NULL,"0:",1); 		//格式化后先取消挂载
			res = f_mount(&fs,"0:",1);			//重新挂载	
			if(res == FR_OK)
			{
				printf("SD卡已经成功挂载,可以进进行文件写入测试!\r\n");
			}	
		}
		else
		{
			printf("格式化失败! \r\n");		
		}
	}
	else if(res == FR_OK)
	{
		printf("挂载成功! \r\n");		
	}
	else
	{
		printf("挂载失败! \r\n");
	}	
	
	res = f_open(&file,SD_FileName,FA_OPEN_ALWAYS |FA_WRITE);
	if((res & FR_DENIED) == FR_DENIED)
	{
		printf("卡存储已满,写入失败!\r\n");		
	}
	
	f_lseek(&file, f_size(&file));//确保写词写入不会覆盖之前的数据
	if(res == FR_OK)
	{
		printf("打开成功/创建文件成功! \r\n");		
		res = f_write(&file,write_buff,bufSize,&Bw);		//写数据到SD卡
		if(res == FR_OK)
		{
			printf("文件写入成功! \r\n");			
		}
		else
		{
			printf("文件写入失败! \r\n");
		}		
	}
	else
	{
		printf("打开文件失败!\r\n");
	}	
	
	f_close(&file);						//关闭文件		
	f_mount(NULL,"0:",1);		        //取消挂载
	
}

//控制具体数据的写入
void WritetoSD_data(void)
{
	FATFS fs;
	FIL file;
	uint8_t res=0;
	int8_t ress;
	
	res = SD_init();		//SD卡初始化
	
	if(res == 1)
	{
		printf("SD卡初始化失败! \r\n");		
	}
	else
	{
		printf("SD卡初始化成功! \r\n");		
	}
	
	res=f_mount(&fs,"0:",1);		//挂载
	
	if(res == FR_NO_FILESYSTEM)		//没有文件系统,格式化
	{
		printf("没有文件系统! \r\n");		
		res = f_mkfs("", 0, 0);		//格式化sd卡
		if(res == FR_OK)
		{
			printf("格式化成功! \r\n");		
			res = f_mount(NULL,"0:",1); 		//格式化后先取消挂载
			res = f_mount(&fs,"0:",1);			//重新挂载	
			if(res == FR_OK)
			{
				printf("SD卡已经成功挂载,可以进进行文件写入测试!\r\n");
			}	
		}
		else
		{
			printf("格式化失败! \r\n");		
		}
	}
	else if(res == FR_OK)
	{
		printf("挂载成功! \r\n");		
	}
	else
	{
		printf("挂载失败! \r\n");
	}	
	
	res = f_open(&file,SD_FileName,FA_OPEN_ALWAYS |FA_WRITE);
	if((res & FR_DENIED) == FR_DENIED)
	{
		printf("卡存储已满,写入失败!\r\n");		
	}
	
	f_lseek(&file, f_size(&file));//确保写词写入不会覆盖之前的数据
	if(res == FR_OK)
	{
		printf("打开成功/创建文件成功! \r\n");		
		//res = f_write(&file,write_buff,bufSize,&Bw);		//写数据到SD卡
		ress = f_printf(&file,"%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n"
		,num_text[0],num_text[1],num_text[2],num_text[3],num_text[4],num_text[5],num_text[6],num_text[7],num_text[8]
		,num_text[9],num_text[10],num_text[11],num_text[12],num_text[13],num_text[14],num_text[15],num_text[16],num_text[17]);
		SD_number++;
		if(ress == (-1))
		{
			//printf("文件写入成功! \r\n");	
            printf("文件写入失败! \r\n");			
		}
		else
		{
			printf("文件写入成功! \r\n");	
			//printf("文件写入失败! \r\n");
		}		
	}
	else
	{
		printf("打开文件失败!\r\n");
	}	
	
	f_close(&file);						//关闭文件		
	f_mount(NULL,"0:",1);		        //取消挂载
	
}

😜while(1)里实现:

                if(SD_number>=36)   //判断文件大小,重新创建	1M 约= 18000   根据自己每次写入的数据大小进行修改
				{
				 SD_FileName_creat();
				 SD_number=0;
				}
				if(excle_head==0)
                {
				excle_head=1; 
				WritetoSD("空气湿度%RH,空气温度℃,噪声值dB,二氧化碳PPM,大气压Kpa,光照高Lux,光照低Lux,ETCH土壤含水率%,ETCH土壤温度℃,ETCH土壤电导率us/cm,ETCH盐度,ETCHTDS,氮mg/kg,磷mg/kg,磷mg/kg,土壤PH值PH,WS土壤含水率%,WS土壤温度℃\r\n",
				sizeof("空气湿度%RH,空气温度℃,噪声值dB,二氧化碳PPM,大气压Kpa,光照Lux,光照低Lux,ETCH土壤含水率%,ETCH土壤温度℃,ETCH土壤电导率us/cm,ETCH盐度,ETCHTDS,氮mg/kg,磷mg/kg,磷mg/kg,土壤PH值PH,WS土壤含水率%,WS土壤温度℃\r\n"));    //Excel表头
				}else
				{
				 WritetoSD_data();	  //具体数据
				}

🌹🌹🌹希望对各位有帮助,创作不易,需要大家的关注点赞💖,拜托拜托😍😍😍
🌹🌹🌹欢迎大家评论区讨论,互相学习,一起努力。QQ2544066178
🌹🌹🌹最后祝大家顺利毕业。

😊😊😊结果:
在这里插入图片描述

在这里插入图片描述

  • 15
    点赞
  • 104
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值