✨1 简介
- 通过STM32控制SD卡创建text文件或Excel文件来保存传感器采集到的数据,并且当文件大小超过规定时,自动重新新建文件继续保存数据。
- 单片机型号:STM32F103VCT6
✨2 SD卡工作原理
🎉2.1 SD卡基本介绍
- SD卡分为SDIO模式与SPI模式,SDIO模式使用SD总线协议,使用4根数据线进行数据传输,SPI使用1收1发2根数据线数据传输,理论上SDIO模式会比SPI模式速度快4倍,但SDIO模式还牵扯到CRC校验位的计算,一般情况下SPI模式就足够了。本文就是基于SPI模式进行开发的。
- SD卡一般分为:标准尺寸,Mini SD,Micro SD card(TF卡)。在嵌入式领域,尤其是小设备,通常使用TF卡。下文中除特殊说明外,用SD卡统一称呼而不做区分。
🎉2.2 SPI模式介绍
- 简要说明:主机首先进行片选,选中从机,同时产生通讯时钟,每到来一个时钟周期,按照配置的SPI通信模式,主机移出一位数据发送到从机,同时从机发送一位数据到主机,完成一位数据交换,重复多次,即可完成一个字节数据,若此次传输完成,取消片选。
- 接线:SI主收从发 SO主发从收 SCK 时钟线(主设备产生) SD_CS片选(主设备控制,一主多从时刻控制从机的选择)
😜本次具体连线:SD_CS–PA12 SI–PB5 SO–PB4 SCK–PB3
✨STM32CubeMx配置
- 常规配置在此不进行展示,相比其他就多一个SPI配置和一个FatFS文件配置,其他都是常见的引脚配置。
✨FatFS文件系统
- 简要概括:FATFS是一个为小型嵌入式系统设计的通用FAT(File Allocation Table)文件系统模块。FatFs 的编写遵循ANSI C,并且完全与磁盘I/O层分开。因此,它独立(不依赖)于硬件架构。它可以被嵌入到低成本的微控制器中,如AVR, 8051, PIC, ARM, Z80, 68K 等等,而不需要做任何修改。 我们直接调用就OK了。
- 本次主要用到了以下函数,后面代码有详细使用
✔f_open:创建 / 打开一个用于访问文件的文件对象
✔f_close :关闭一个打开的文件
✔f_lseek:移动一个打开的文件对象的文件读 / 写指针。也可以被用来扩展文件大小 ( 簇预分配 ) 。
✔f_read :从一个文件读取数据
✔f_write:写入数据到一个文件
✔f_mkfs:在驱动器上创建一个文件系统
✔f_mount:在 FatFs 模块上注册 / 注销一个工作区 ( 文件系统对象 )
✔f_printf:f_printf 函数向文件中写入一个格式化字符串。
🐱🚀🐱🚀🐱🚀想深入理解:
FatFS文件系统详细介绍
✨具体代码(直接上干货)
- 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);
- 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
- 实现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****/
- 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
🌹🌹🌹最后祝大家顺利毕业。
😊😊😊结果: