打印
[其他ST产品]

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

[复制链接]
1138|18
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
简介
通过STM32控制SD卡创建text文件或Excel文件来保存传感器采集到的数据,并且当文件大小超过规定时,自动重新新建文件继续保存数据。
单片机型号:STM32F103VCT6
✨2 SD卡工作原理

47341650cfaa6c922e.png (118.67 KB )

47341650cfaa6c922e.png

使用特权

评论回复
沙发
怎么总是重复啊|  楼主 | 2023-9-22 10:23 | 只看该作者
2.2 SPI模式介绍
简要说明:主机首先进行片选,选中从机,同时产生通讯时钟,每到来一个时钟周期,按照配置的SPI通信模式,主机移出一位数据发送到从机,同时从机发送一位数据到主机,完成一位数据交换,重复多次,即可完成一个字节数据,若此次传输完成,取消片选。

使用特权

评论回复
板凳
怎么总是重复啊|  楼主 | 2023-9-22 10:24 | 只看该作者
接线:SI主收从发 SO主发从收 SCK 时钟线(主设备产生) SD_CS片选(主设备控制,一主多从时刻控制从机的选择)

使用特权

评论回复
地板
怎么总是重复啊|  楼主 | 2023-9-22 10:24 | 只看该作者

19781650cfadd441f6.png (50.96 KB )

19781650cfadd441f6.png

使用特权

评论回复
5
怎么总是重复啊|  楼主 | 2023-9-22 10:25 | 只看该作者
STM32CubeMx配置
常规配置在此不进行展示,相比其他就多一个SPI配置和一个FatFS文件配置,其他都是常见的引脚配置。

使用特权

评论回复
6
怎么总是重复啊|  楼主 | 2023-9-22 10:25 | 只看该作者

使用特权

评论回复
7
怎么总是重复啊|  楼主 | 2023-9-22 10:26 | 只看该作者

使用特权

评论回复
8
怎么总是重复啊|  楼主 | 2023-9-22 10:26 | 只看该作者
FatFS文件系统
简要概括:FATFS是一个为小型嵌入式系统设计的通用FAT(File Allocation Table)文件系统模块。FatFs 的编写遵循ANSI C,并且完全与磁盘I/O层分开。因此,它独立(不依赖)于硬件架构。它可以被嵌入到低成本的微控制器中,如AVR, 8051, PIC, ARM, Z80, 68K 等等,而不需要做任何修改。 我们直接调用就OK了。

使用特权

评论回复
9
怎么总是重复啊|  楼主 | 2023-9-22 10:26 | 只看该作者
本次主要用到了以下函数,后面代码有详细使用
✔f_open:创建 / 打开一个用于访问文件的文件对象
✔f_close :关闭一个打开的文件
✔f_lseek:移动一个打开的文件对象的文件读 / 写指针。也可以被用来扩展文件大小 ( 簇预分配 ) 。
✔f_read :从一个文件读取数据
✔f_write:写入数据到一个文件
✔f_mkfs:在驱动器上创建一个文件系统
✔f_mount:在 FatFs 模块上注册 / 注销一个工作区 ( 文件系统对象 )
✔f_printf:f_printf 函数向文件中写入一个格式化字符串。

使用特权

评论回复
10
怎么总是重复啊|  楼主 | 2023-9-22 10:26 | 只看该作者
具体代码(直接上干货)
SPI协议:[color=rgba(0, 0, 0, 0.75)]spi.c 除了STM32CubeMx自动生成的,需要添加以下代码:
[color=rgba(0, 0, 0, 0.75)]
//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;                      //返回收到的数据        
}



使用特权

评论回复
11
怎么总是重复啊|  楼主 | 2023-9-22 10:27 | 只看该作者
u8 SPI1_ReadWriteByte(u8 TxData);
void SPI1_SetSpeed(u8 SPI_BaudRatePrescaler);

使用特权

评论回复
12
怎么总是重复啊|  楼主 | 2023-9-22 10:27 | 只看该作者
SD卡的配置程序:m_sd.c 和 m_sd.h
#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;
}   




使用特权

评论回复
13
怎么总是重复啊|  楼主 | 2023-9-22 10:28 | 只看该作者
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

使用特权

评论回复
14
怎么总是重复啊|  楼主 | 2023-9-22 10:29 | 只看该作者
实现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 ---------------------------------------------------------*/

/**
  * [url=home.php?mod=space&uid=247401]@brief[/url]  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****/

使用特权

评论回复
15
怎么总是重复啊|  楼主 | 2023-9-22 10:29 | 只看该作者
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,超过大小后自动++

使用特权

评论回复
16
怎么总是重复啊|  楼主 | 2023-9-22 10:29 | 只看该作者
//文件超过大小后,新建文件
void SD_FileName_creat(void)
{
        SD_FileName_number++;
    sprintf(SD_FileName,"data%d.csv",SD_FileName_number);
        excle_head=0;
}

使用特权

评论回复
17
怎么总是重复啊|  楼主 | 2023-9-22 10:30 | 只看该作者
//控制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);                        //取消挂载
       
}

使用特权

评论回复
18
怎么总是重复啊|  楼主 | 2023-9-22 10:30 | 只看该作者
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();          //具体数据
                                }

使用特权

评论回复
19
怎么总是重复啊|  楼主 | 2023-9-22 10:31 | 只看该作者
结果:



使用特权

评论回复
20
公羊子丹| | 2024-8-11 07:25 | 只看该作者

一般PCB是V-CUT,

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

21

主题

186

帖子

0

粉丝