[其他ST产品] 基于STM32的SD卡+STM32CubeMx+FatFS文件系统创建Excel表保存数据

[复制链接]
2548|18
 楼主| 怎么总是重复啊 发表于 2023-9-22 10:23 | 显示全部楼层 |阅读模式
简介
通过STM32控制SD卡创建text文件或Excel文件来保存传感器采集到的数据,并且当文件大小超过规定时,自动重新新建文件继续保存数据。
单片机型号:STM32F103VCT6
✨2 SD卡工作原理
47341650cfaa6c922e.png
 楼主| 怎么总是重复啊 发表于 2023-9-22 10:23 | 显示全部楼层
2.2 SPI模式介绍
简要说明:主机首先进行片选,选中从机,同时产生通讯时钟,每到来一个时钟周期,按照配置的SPI通信模式,主机移出一位数据发送到从机,同时从机发送一位数据到主机,完成一位数据交换,重复多次,即可完成一个字节数据,若此次传输完成,取消片选。
 楼主| 怎么总是重复啊 发表于 2023-9-22 10:24 | 显示全部楼层
接线:SI主收从发 SO主发从收 SCK 时钟线(主设备产生) SD_CS片选(主设备控制,一主多从时刻控制从机的选择)
24652650cfaccb7933.png
 楼主| 怎么总是重复啊 发表于 2023-9-22 10:24 | 显示全部楼层
 楼主| 怎么总是重复啊 发表于 2023-9-22 10:25 | 显示全部楼层
STM32CubeMx配置
常规配置在此不进行展示,相比其他就多一个SPI配置和一个FatFS文件配置,其他都是常见的引脚配置。
 楼主| 怎么总是重复啊 发表于 2023-9-22 10:25 | 显示全部楼层
 楼主| 怎么总是重复啊 发表于 2023-9-22 10:26 | 显示全部楼层
 楼主| 怎么总是重复啊 发表于 2023-9-22 10:26 | 显示全部楼层
FatFS文件系统
简要概括:FATFS是一个为小型嵌入式系统设计的通用FAT(File Allocation Table)文件系统模块。FatFs 的编写遵循ANSI C,并且完全与磁盘I/O层分开。因此,它独立(不依赖)于硬件架构。它可以被嵌入到低成本的微控制器中,如AVR, 8051, PIC, ARM, Z80, 68K 等等,而不需要做任何修改。 我们直接调用就OK了。
 楼主| 怎么总是重复啊 发表于 2023-9-22 10:26 | 显示全部楼层
本次主要用到了以下函数,后面代码有详细使用
✔f_open:创建 / 打开一个用于访问文件的文件对象
✔f_close :关闭一个打开的文件
✔f_lseek:移动一个打开的文件对象的文件读 / 写指针。也可以被用来扩展文件大小 ( 簇预分配 ) 。
✔f_read :从一个文件读取数据
✔f_write:写入数据到一个文件
✔f_mkfs:在驱动器上创建一个文件系统
✔f_mount:在 FatFs 模块上注册 / 注销一个工作区 ( 文件系统对象 )
✔f_printf:f_printf 函数向文件中写入一个格式化字符串。
 楼主| 怎么总是重复啊 发表于 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;                      //返回收到的数据        
}



 楼主| 怎么总是重复啊 发表于 2023-9-22 10:27 | 显示全部楼层
  1. u8 SPI1_ReadWriteByte(u8 TxData);
  2. void SPI1_SetSpeed(u8 SPI_BaudRatePrescaler);
 楼主| 怎么总是重复啊 发表于 2023-9-22 10:27 | 显示全部楼层
SD卡的配置程序:m_sd.c 和 m_sd.h
  1. #include "main.h"
  2. #include "spi.h"
  3. #include "usart.h"                               
  4. u8  SD_Type=0;//SD卡的类型
  5. //移植时候的接口
  6. //data:要写入的数据
  7. //返回值:读到的数据
  8. u8 SD_SPI_ReadWriteByte(u8 data)
  9. {
  10.     return SPI1_ReadWriteByte(data);
  11. }

  12. //SD卡初始化的时候,需要低速
  13. void SD_SPI_SpeedLow(void)
  14. {
  15.      SPI1_SetSpeed(SPI_BAUDRATEPRESCALER_256);//设置到低速模式   
  16. }

  17. //SD卡正常工作的时候,可以高速了
  18. void SD_SPI_SpeedHigh(void)
  19. {
  20.      SPI1_SetSpeed(SPI_BAUDRATEPRESCALER_2);//设置到高速模式   
  21. }

  22. //SPI硬件层初始化
  23. void SD_SPI_Init(void)
  24. {
  25.     //设置硬件上与SD卡相关联的控制引脚输出
  26.     GPIO_InitTypeDef GPIO_Initure;
  27.     __HAL_RCC_GPIOA_CLK_ENABLE();               //开启GPIOA时钟
  28.     //PA12
  29.     GPIO_Initure.Pin=GPIO_PIN_12
  30.     GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP;      //推挽输出
  31.     GPIO_Initure.Pull=GPIO_PULLUP;              //上拉
  32.     GPIO_Initure.Speed=GPIO_SPEED_HIGH;             //高速
  33.     HAL_GPIO_Init(GPIOA,&GPIO_Initure);
  34.    
  35.     HAL_GPIO_WritePin(GPIOA,GPIO_PIN_3,GPIO_PIN_SET);
  36.     MX_SPI1_Init();
  37.     SD_CS_1;
  38. }
  39. ///
  40. //取消选择,释放SPI总线
  41. void SD_DisSelect(void)
  42. {
  43.     SD_CS_1;
  44.     SD_SPI_ReadWriteByte(0xff);//提供额外的8个时钟
  45. }

  46. //选择sd卡,并且等待卡准备OK
  47. //返回值:0,成功;1,失败;
  48. u8 SD_Select(void)
  49. {
  50.     SD_CS_0;
  51.     if(SD_WaitReady()==0)return 0;//等待成功
  52.     SD_DisSelect();
  53.     return 1;//等待失败
  54. }

  55. //等待卡准备好
  56. //返回值:0,准备好了;其他,错误代码
  57. u8 SD_WaitReady(void)
  58. {
  59.     u32 t=0;
  60.     do
  61.     {
  62.         if(SD_SPI_ReadWriteByte(0XFF)==0XFF)return 0;//OK
  63.         t++;              
  64.     }while(t<0XFFFFFF);//等待
  65.     return 1;
  66. }

  67. //等待SD卡回应
  68. //Response:要得到的回应值
  69. //返回值:0,成功得到了该回应值
  70. //    其他,得到回应值失败
  71. u8 SD_GetResponse(u8 Response)
  72. {
  73.     u16 Count=0xFFFF;//等待次数                                 
  74.     while ((SD_SPI_ReadWriteByte(0XFF)!=Response)&&Count)Count--;//等待得到准确的回应        
  75.     if (Count==0)return MSD_RESPONSE_FAILURE;//得到回应失败   
  76.     else return MSD_RESPONSE_NO_ERROR;//正确回应
  77. }

  78. //从sd卡读取一个数据包的内容
  79. //buf:数据缓存区
  80. //len:要读取的数据长度.
  81. //返回值:0,成功;其他,失败;   
  82. u8 SD_RecvData(u8*buf,u16 len)
  83. {                    
  84.     if(SD_GetResponse(0xFE))return 1;//等待SD卡发回数据起始令牌0xFE
  85.     while(len--)//开始接收数据
  86.     {
  87.         *buf=SPI1_ReadWriteByte(0xFF);
  88.         buf++;
  89.     }
  90.     //下面是2个伪CRC(dummy CRC)
  91.     SD_SPI_ReadWriteByte(0xFF);
  92.     SD_SPI_ReadWriteByte(0xFF);                                                              
  93.     return 0;//读取成功
  94. }

  95. //向sd卡写入一个数据包的内容 512字节
  96. //buf:数据缓存区
  97. //cmd:指令
  98. //返回值:0,成功;其他,失败;   
  99. u8 SD_SendBlock(u8*buf,u8 cmd)
  100. {   
  101.     u16 t;               
  102.     if(SD_WaitReady())return 1;//等待准备失效
  103.     SD_SPI_ReadWriteByte(cmd);
  104.     if(cmd!=0XFD)//不是结束指令
  105.     {
  106.         for(t=0;t<512;t++)SPI1_ReadWriteByte(buf[t]);//提高速度,减少函数传参时间
  107.         SD_SPI_ReadWriteByte(0xFF);//忽略crc
  108.         SD_SPI_ReadWriteByte(0xFF);
  109.         t=SD_SPI_ReadWriteByte(0xFF);//接收响应
  110.         if((t&0x1F)!=0x05)return 2;//响应错误                                                              
  111.     }                                                                                       
  112.     return 0;//写入成功
  113. }

  114. //向SD卡发送一个命令
  115. //输入: u8 cmd   命令
  116. //      u32 arg  命令参数
  117. //      u8 crc   crc校验值      
  118. //返回值:SD卡返回的响应                                                              
  119. u8 SD_SendCmd(u8 cmd, u32 arg, u8 crc)
  120. {
  121.     u8 r1;   
  122.     u8 Retry=0;
  123.     SD_DisSelect();//取消上次片选
  124.     if(SD_Select())return 0XFF;//片选失效
  125.     //发送
  126.     SD_SPI_ReadWriteByte(cmd | 0x40);//分别写入命令
  127.     SD_SPI_ReadWriteByte(arg >> 24);
  128.     SD_SPI_ReadWriteByte(arg >> 16);
  129.     SD_SPI_ReadWriteByte(arg >> 8);
  130.     SD_SPI_ReadWriteByte(arg);      
  131.     SD_SPI_ReadWriteByte(crc);
  132.     if(cmd==CMD12)SD_SPI_ReadWriteByte(0xff);//Skip a stuff byte when stop reading
  133.     //等待响应,或超时退出
  134.     Retry=0X1F;
  135.     do
  136.     {
  137.         r1=SD_SPI_ReadWriteByte(0xFF);
  138.     }while((r1&0X80) && Retry--);     
  139.     //返回状态值
  140.     return r1;
  141. }                                                                                          
  142. //获取SD卡的CID信息,包括制造商信息
  143. //输入: u8 *cid_data(存放CID的内存,至少16Byte)      
  144. //返回值:0:NO_ERR
  145. //         1:错误                                                           
  146. u8 SD_GetCID(u8 *cid_data)
  147. {
  148.     u8 r1;      
  149.     //发CMD10命令,读CID
  150.     r1=SD_SendCmd(CMD10,0,0x01);
  151.     if(r1==0x00)
  152.     {
  153.         r1=SD_RecvData(cid_data,16);//接收16个字节的数据     
  154.     }
  155.     SD_DisSelect();//取消片选
  156.     if(r1)return 1;
  157.     else return 0;
  158. }                                                                                 
  159. //获取SD卡的CSD信息,包括容量和速度信息
  160. //输入:u8 *cid_data(存放CID的内存,至少16Byte)        
  161. //返回值:0:NO_ERR
  162. //         1:错误                                                           
  163. u8 SD_GetCSD(u8 *csd_data)
  164. {
  165.     u8 r1;     
  166.     r1=SD_SendCmd(CMD9,0,0x01);//发CMD9命令,读CSD
  167.     if(r1==0)
  168.     {
  169.         r1=SD_RecvData(csd_data, 16);//接收16个字节的数据
  170.     }
  171.     SD_DisSelect();//取消片选
  172.     if(r1)return 1;
  173.     else return 0;
  174. }  
  175. //获取SD卡的总扇区数(扇区数)   
  176. //返回值:0: 取容量出错
  177. //       其他:SD卡的容量(扇区数/512字节)
  178. //每扇区的字节数必为512,因为如果不是512,则初始化不能通过.                                                         
  179. u32 SD_GetSectorCount(void)
  180. {
  181.     u8 csd[16];
  182.     u32 Capacity;  
  183.     u8 n;
  184.     u16 csize;                          
  185.     //取CSD信息,如果期间出错,返回0
  186.     if(SD_GetCSD(csd)!=0) return 0;        
  187.     //如果为SDHC卡,按照下面方式计算
  188.     if((csd[0]&0xC0)==0x40)     //V2.00的卡
  189.     {   
  190.         csize = csd[9] + ((u16)csd[8] << 8) + 1;
  191.         Capacity = (u32)csize << 10;//得到扇区数               
  192.     }else//V1.XX的卡
  193.     {   
  194.         n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
  195.         csize = (csd[8] >> 6) + ((u16)csd[7] << 2) + ((u16)(csd[6] & 3) << 10) + 1;
  196.         Capacity= (u32)csize << (n - 9);//得到扇区数   
  197.     }
  198.     return Capacity;
  199. }
  200. //初始化SD卡
  201. u8 SD_Init(void)
  202. {
  203.     u8 r1;      // 存放SD卡的返回值
  204.     u16 retry;  // 用来进行超时计数
  205.     u8 buf[4];  
  206.     u16 i;

  207.     SD_SPI_Init();        //初始化IO
  208.      SD_SPI_SpeedLow();    //设置到低速模式
  209.      for(i=0;i<10;i++)SD_SPI_ReadWriteByte(0XFF);//发送最少74个脉冲
  210.     retry=20;
  211.     do
  212.     {
  213.         r1=SD_SendCmd(CMD0,0,0x95);//进入IDLE状态
  214.     }while((r1!=0X01) && retry--);
  215.      SD_Type=0;//默认无卡
  216.     if(r1==0X01)
  217.     {
  218.         if(SD_SendCmd(CMD8,0x1AA,0x87)==1)//SD V2.0
  219.         {
  220.             for(i=0;i<4;i++)buf[i]=SD_SPI_ReadWriteByte(0XFF);    //Get trailing return value of R7 resp
  221.             if(buf[2]==0X01&&buf[3]==0XAA)//卡是否支持2.7~3.6V
  222.             {
  223.                 retry=0XFFFE;
  224.                 do
  225.                 {
  226.                     SD_SendCmd(CMD55,0,0X01);    //发送CMD55
  227.                     r1=SD_SendCmd(CMD41,0x40000000,0X01);//发送CMD41
  228.                 }while(r1&&retry--);
  229.                 if(retry&&SD_SendCmd(CMD58,0,0X01)==0)//鉴别SD2.0卡版本开始
  230.                 {
  231.                     for(i=0;i<4;i++)buf[i]=SD_SPI_ReadWriteByte(0XFF);//得到OCR值
  232.                     if(buf[0]&0x40)SD_Type=SD_TYPE_V2HC;    //检查CCS
  233.                     else SD_Type=SD_TYPE_V2;   
  234.                 }
  235.             }
  236.         }else//SD V1.x/ MMC    V3
  237.         {
  238.             SD_SendCmd(CMD55,0,0X01);        //发送CMD55
  239.             r1=SD_SendCmd(CMD41,0,0X01);    //发送CMD41
  240.             if(r1<=1)
  241.             {        
  242.                 SD_Type=SD_TYPE_V1;
  243.                 retry=0XFFFE;
  244.                 do //等待退出IDLE模式
  245.                 {
  246.                     SD_SendCmd(CMD55,0,0X01);    //发送CMD55
  247.                     r1=SD_SendCmd(CMD41,0,0X01);//发送CMD41
  248.                 }while(r1&&retry--);
  249.             }else//MMC卡不支持CMD55+CMD41识别
  250.             {
  251.                 SD_Type=SD_TYPE_MMC;//MMC V3
  252.                 retry=0XFFFE;
  253.                 do //等待退出IDLE模式
  254.                 {                                                
  255.                     r1=SD_SendCmd(CMD1,0,0X01);//发送CMD1
  256.                 }while(r1&&retry--);  
  257.             }
  258.             if(retry==0||SD_SendCmd(CMD16,512,0X01)!=0)SD_Type=SD_TYPE_ERR;//错误的卡
  259.         }
  260.     }
  261.     SD_DisSelect();//取消片选
  262.     SD_SPI_SpeedHigh();//高速
  263.     if(SD_Type)return 0;
  264.     else if(r1)return r1;        
  265.     return 0xaa;//其他错误
  266. }

  267. //读SD卡
  268. //buf:数据缓存区
  269. //sector:扇区
  270. //cnt:扇区数
  271. //返回值:0,ok;其他,失败.
  272. u8 SD_ReadDisk(u8*buf,u32 sector,u8 cnt)
  273. {
  274.     u8 r1;
  275.     if(SD_Type!=SD_TYPE_V2HC)sector <<= 9;//转换为字节地址
  276.     if(cnt==1)
  277.     {
  278.         r1=SD_SendCmd(CMD17,sector,0X01);//读命令
  279.         if(r1==0)//指令发送成功
  280.         {
  281.             r1=SD_RecvData(buf,512);//接收512个字节      
  282.         }
  283.     }else
  284.     {
  285.         r1=SD_SendCmd(CMD18,sector,0X01);//连续读命令
  286.         do
  287.         {
  288.             r1=SD_RecvData(buf,512);//接收512个字节     
  289.             buf+=512;  
  290.         }while(--cnt && r1==0);     
  291.         SD_SendCmd(CMD12,0,0X01);    //发送停止命令
  292.     }   
  293.     SD_DisSelect();//取消片选
  294.     return r1;//
  295. }

  296. //写SD卡
  297. //buf:数据缓存区
  298. //sector:起始扇区
  299. //cnt:扇区数
  300. //返回值:0,ok;其他,失败.
  301. u8 SD_WriteDisk(u8*buf,u32 sector,u8 cnt)
  302. {
  303.     u8 r1;
  304.     if(SD_Type!=SD_TYPE_V2HC)sector *= 512;//转换为字节地址
  305.     if(cnt==1)
  306.     {
  307.         r1=SD_SendCmd(CMD24,sector,0X01);//读命令
  308.         if(r1==0)//指令发送成功
  309.         {
  310.             r1=SD_SendBlock(buf,0xFE);//写512个字节      
  311.         }
  312.     }else
  313.     {
  314.         if(SD_Type!=SD_TYPE_MMC)
  315.         {
  316.             SD_SendCmd(CMD55,0,0X01);   
  317.             SD_SendCmd(CMD23,cnt,0X01);//发送指令   
  318.         }
  319.          r1=SD_SendCmd(CMD25,sector,0X01);//连续读命令
  320.         if(r1==0)
  321.         {
  322.             do
  323.             {
  324.                 r1=SD_SendBlock(buf,0xFC);//接收512个字节     
  325.                 buf+=512;  
  326.             }while(--cnt && r1==0);
  327.             r1=SD_SendBlock(0,0xFD);//接收512个字节
  328.         }
  329.     }   
  330.     SD_DisSelect();//取消片选
  331.     return r1;
  332. }   




 楼主| 怎么总是重复啊 发表于 2023-9-22 10:28 | 显示全部楼层
m_sd.h

  1. #ifndef __M_SD_H__
  2. #define __M_SD_H__

  3. #include "type.h"  
  4. #include "stm32f1xx_hal.h"   


  5. #define SD_CS_1  HAL_GPIO_WritePin(GPIOA,GPIO_PIN_12,GPIO_PIN_SET)
  6. #define SD_CS_0  HAL_GPIO_WritePin(GPIOA,GPIO_PIN_12,GPIO_PIN_RESET)

  7. // SD卡类型定义  
  8. #define SD_TYPE_ERR     0X00
  9. #define SD_TYPE_MMC     0X01
  10. #define SD_TYPE_V1      0X02
  11. #define SD_TYPE_V2      0X04
  12. #define SD_TYPE_V2HC    0X06

  13. // SD卡指令表         
  14. #define CMD0    0       //卡复位
  15. #define CMD1    1
  16. #define CMD8    8       //命令8 ,SEND_IF_COND
  17. #define CMD9    9       //命令9 ,读CSD数据
  18. #define CMD10   10      //命令10,读CID数据
  19. #define CMD12   12      //命令12,停止数据传输
  20. #define CMD16   16      //命令16,设置SectorSize 应返回0x00
  21. #define CMD17   17      //命令17,读sector
  22. #define CMD18   18      //命令18,读Multi sector
  23. #define CMD23   23      //命令23,设置多sector写入前预先擦除N个block
  24. #define CMD24   24      //命令24,写sector
  25. #define CMD25   25      //命令25,写Multi sector
  26. #define CMD41   41      //命令41,应返回0x00
  27. #define CMD55   55      //命令55,应返回0x01
  28. #define CMD58   58      //命令58,读OCR信息
  29. #define CMD59   59      //命令59,使能/禁止CRC,应返回0x00
  30. //数据写入回应字意义
  31. #define MSD_DATA_OK                0x05
  32. #define MSD_DATA_CRC_ERROR         0x0B
  33. #define MSD_DATA_WRITE_ERROR       0x0D
  34. #define MSD_DATA_OTHER_ERROR       0xFF
  35. //SD卡回应标记字
  36. #define MSD_RESPONSE_NO_ERROR      0x00
  37. #define MSD_IN_IDLE_STATE          0x01
  38. #define MSD_ERASE_RESET            0x02
  39. #define MSD_ILLEGAL_COMMAND        0x04
  40. #define MSD_COM_CRC_ERROR          0x08
  41. #define MSD_ERASE_SEQUENCE_ERROR   0x10
  42. #define MSD_ADDRESS_ERROR          0x20
  43. #define MSD_PARAMETER_ERROR        0x40
  44. #define MSD_RESPONSE_FAILURE       0xFF
  45.                                                             
  46. extern u8  SD_Type;            //SD卡的类型
  47. //函数申明区
  48. u8 SD_SPI_ReadWriteByte(u8 data);
  49. void SD_SPI_SpeedLow(void);
  50. void SD_SPI_SpeedHigh(void);
  51. u8 SD_WaitReady(void);                            //等待SD卡准备
  52. u8 SD_GetResponse(u8 Response);                    //获得相应
  53. u8 SD_Init(void);                                //初始化
  54. u8 SD_ReadDisk(u8*buf,u32 sector,u8 cnt);        //读块
  55. u8 SD_WriteDisk(u8*buf,u32 sector,u8 cnt);        //写块
  56. u32 SD_GetSectorCount(void);                       //读扇区数
  57. u8 SD_GetCID(u8 *cid_data);                     //读SD卡CID
  58. u8 SD_GetCSD(u8 *csd_data);                     //读SD卡CSD

  59. #endif
 楼主| 怎么总是重复啊 发表于 2023-9-22 10:29 | 显示全部楼层
实现FATFS读写SD卡的主要代码:
user_diskio.c

  1. #ifdef USE_OBSOLETE_USER_CODE_SECTION_0
  2. /*
  3. * Warning: the user section 0 is no more in use (starting from CubeMx version 4.16.0)
  4. * To be suppressed in the future.
  5. * Kept to ensure backward compatibility with previous CubeMx versions when
  6. * migrating projects.
  7. * User code previously added there should be copied in the new user sections before
  8. * the section contents can be deleted.
  9. */
  10. /* USER CODE BEGIN 0 */
  11. /* USER CODE END 0 */
  12. #endif

  13. /* USER CODE BEGIN DECL */
  14. /* Includes ------------------------------------------------------------------*/
  15. #include <string.h>
  16. #include "ff_gen_drv.h"

  17. /* Private typedef -----------------------------------------------------------*/
  18. /* Private define ------------------------------------------------------------*/
  19. #include "m_sd.h"
  20. #define SD_CARD  0
  21. /* Private variables ---------------------------------------------------------*/
  22. /* Disk status */
  23. static volatile DSTATUS Stat = STA_NOINIT;

  24. /* USER CODE END DECL */
  25. /* Private function prototypes -----------------------------------------------*/
  26. DSTATUS USER_initialize (BYTE pdrv);
  27. DSTATUS USER_status (BYTE pdrv);
  28. DRESULT USER_read (BYTE pdrv, BYTE *buff, DWORD sector, UINT count);
  29. #if _USE_WRITE == 1
  30.   DRESULT USER_write (BYTE pdrv, const BYTE *buff, DWORD sector, UINT count);
  31. #endif /* _USE_WRITE == 1 */
  32. #if _USE_IOCTL == 1
  33.   DRESULT USER_ioctl (BYTE pdrv, BYTE cmd, void *buff);
  34. #endif /* _USE_IOCTL == 1 */

  35. Diskio_drvTypeDef  USER_Driver =
  36. {
  37.   USER_initialize,
  38.   USER_status,
  39.   USER_read,
  40. #if  _USE_WRITE
  41.   USER_write,
  42. #endif  /* _USE_WRITE == 1 */
  43. #if  _USE_IOCTL == 1
  44.   USER_ioctl,
  45. #endif /* _USE_IOCTL == 1 */
  46. };

  47. /* Private functions ---------------------------------------------------------*/

  48. /**
  49.   * [url=home.php?mod=space&uid=247401]@brief[/url]  Initializes a Drive
  50.   * @param  pdrv: Physical drive number (0..)
  51.   * @retval DSTATUS: Operation status
  52.   */
  53. DSTATUS USER_initialize (
  54.         BYTE pdrv           /* Physical drive nmuber to identify the drive */
  55. )
  56. {
  57.   /* USER CODE BEGIN INIT */
  58.     Stat = RES_OK;
  59.     return Stat;
  60.   /* USER CODE END INIT */
  61. }

  62. /**
  63.   * @brief  Gets Disk Status
  64.   * @param  pdrv: Physical drive number (0..)
  65.   * @retval DSTATUS: Operation status
  66.   */
  67. DSTATUS USER_status (
  68.         BYTE pdrv       /* Physical drive number to identify the drive */
  69. )
  70. {
  71.   /* USER CODE BEGIN STATUS */
  72.    u8 res=0;        
  73.     switch(pdrv)
  74.     {
  75.         case SD_CARD:        //SD卡
  76.             res=SD_Init();    //SD卡初始化
  77.               break;
  78.         
  79.         default:
  80.             res=1;
  81.     }         
  82.     if(res)return  STA_NOINIT;
  83.     else return 0; //初始化成功
  84.   /* USER CODE END STATUS */
  85. }

  86. /**
  87.   * @brief  Reads Sector(s)
  88.   * @param  pdrv: Physical drive number (0..)
  89.   * @param  *buff: Data buffer to store read data
  90.   * @param  sector: Sector address (LBA)
  91.   * @param  count: Number of sectors to read (1..128)
  92.   * @retval DRESULT: Operation result
  93.   */
  94. DRESULT USER_read (
  95.         BYTE pdrv,      /* Physical drive nmuber to identify the drive */
  96.         BYTE *buff,     /* Data buffer to store read data */
  97.         DWORD sector,   /* Sector address in LBA */
  98.         UINT count      /* Number of sectors to read */
  99. )
  100. {
  101.   /* USER CODE BEGIN READ */
  102.    u8 res=0;
  103.     if (!count)return RES_PARERR;//count不能等于0,否则返回参数错误              
  104.     switch(pdrv)
  105.     {
  106.         case SD_CARD://SD卡
  107.             res=SD_ReadDisk(buff,sector,count);     
  108.             while(res)//读出错
  109.             {
  110.                 SD_Init();    //重新初始化SD卡
  111.                 res=SD_ReadDisk(buff,sector,count);   
  112.             }
  113.             break;
  114.         default:
  115.             res=1;
  116.     }
  117.    //处理返回值,将SPI_SD_driver.c的返回值转成ff.c的返回值
  118.     if(res==0x00)return RES_OK;     
  119.     else return RES_ERROR;  
  120.   /* USER CODE END READ */
  121. }

  122. /**
  123.   * @brief  Writes Sector(s)
  124.   * @param  pdrv: Physical drive number (0..)
  125.   * @param  *buff: Data to be written
  126.   * @param  sector: Sector address (LBA)
  127.   * @param  count: Number of sectors to write (1..128)
  128.   * @retval DRESULT: Operation result
  129.   */
  130. #if _USE_WRITE == 1
  131. DRESULT USER_write (
  132.         BYTE pdrv,          /* Physical drive nmuber to identify the drive */
  133.         const BYTE *buff,   /* Data to be written */
  134.         DWORD sector,       /* Sector address in LBA */
  135.         UINT count          /* Number of sectors to write */
  136. )
  137. {
  138.   /* USER CODE BEGIN WRITE */
  139.   /* USER CODE HERE */
  140.    u8 res=0;  
  141.     if (!count)return RES_PARERR;//count不能等于0,否则返回参数错误              
  142.     switch(pdrv)
  143.     {
  144.         case SD_CARD://SD卡
  145.             res=SD_WriteDisk((u8*)buff,sector,count);
  146.             while(res)//写出错
  147.             {
  148.                 SD_Init();    //重新初始化SD卡
  149.                 res=SD_WriteDisk((u8*)buff,sector,count);   
  150.             }
  151.             break;
  152.         default:
  153.             res=1;
  154.     }
  155.     //处理返回值,将SPI_SD_driver.c的返回值转成ff.c的返回值
  156.     if(res == 0x00)return RES_OK;     
  157.     else return RES_ERROR;
  158.   /* USER CODE END WRITE */
  159. }
  160. #endif /* _USE_WRITE == 1 */

  161. /**
  162.   * @brief  I/O control operation
  163.   * @param  pdrv: Physical drive number (0..)
  164.   * @param  cmd: Control code
  165.   * @param  *buff: Buffer to send/receive control data
  166.   * @retval DRESULT: Operation result
  167.   */
  168. #if _USE_IOCTL == 1
  169. DRESULT USER_ioctl (
  170.         BYTE pdrv,      /* Physical drive nmuber (0..) */
  171.         BYTE cmd,       /* Control code */
  172.         void *buff      /* Buffer to send/receive control data */
  173. )
  174. {
  175.   /* USER CODE BEGIN IOCTL */
  176.     DRESULT res;                                          
  177.     if(pdrv==SD_CARD)//SD卡
  178.     {
  179.         switch(cmd)
  180.         {
  181.             case CTRL_SYNC:
  182.                 res = RES_OK;
  183.                 break;     
  184.             case GET_SECTOR_SIZE:
  185.                 *(DWORD*)buff = 512;
  186.                 res = RES_OK;
  187.                 break;     
  188.             case GET_BLOCK_SIZE:
  189.                 *(WORD*)buff = 8;
  190.                 res = RES_OK;
  191.                 break;     
  192.             case GET_SECTOR_COUNT:
  193.                 *(DWORD*)buff = SD_GetSectorCount();
  194.                 res = RES_OK;
  195.                 break;
  196.             default:
  197.                 res = RES_PARERR;
  198.                 break;
  199.         }
  200.     }

  201. else res=RES_ERROR;//其他的不支持
  202.     return res;
  203.   /* USER CODE END IOCTL */
  204. }
  205. #endif /* _USE_IOCTL == 1 */

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

 楼主| 怎么总是重复啊 发表于 2023-9-22 10:29 | 显示全部楼层
main.c 调用函数具体实现Excel文件的实现
  1. u8 excle_head=0;   //控制Excel表头
  2. u16 num_text[]={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,,16,17};   //模拟传感器收到的数据
  3. u32 SD_number=0; //写入数据进行计数
  4. //sd卡
  5. char SD_FileName[]="data1.csv";   
  6. int SD_FileName_number=1;   //默认为1,超过大小后自动++
 楼主| 怎么总是重复啊 发表于 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;
}
 楼主| 怎么总是重复啊 发表于 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);                        //取消挂载
       
}
 楼主| 怎么总是重复啊 发表于 2023-9-22 10:30 | 显示全部楼层
while(1)里实现:
  1.                 if(SD_number>=36)   //判断文件大小,重新创建        1M 约= 18000   根据自己每次写入的数据大小进行修改
  2.                                 {
  3.                                  SD_FileName_creat();
  4.                                  SD_number=0;
  5.                                 }
  6.                                 if(excle_head==0)
  7.                 {
  8.                                 excle_head=1;
  9.                                 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",
  10.                                 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表头
  11.                                 }else
  12.                                 {
  13.                                  WritetoSD_data();          //具体数据
  14.                                 }
 楼主| 怎么总是重复啊 发表于 2023-9-22 10:31 | 显示全部楼层
结果:
93740650cfc5a1e931.png

82394650cfc6c3303a.png
公羊子丹 发表于 2024-8-11 07:25 | 显示全部楼层

一般PCB是V-CUT,
您需要登录后才可以回帖 登录 | 注册

本版积分规则

29

主题

262

帖子

1

粉丝
快速回复 在线客服 返回列表 返回顶部