stm32写sd卡

说实话写sd卡文件并不是个很可靠的记录数据的方式, 他有问题是只能事后发现,除非有什么提醒, 记录文件有太多不确定性,磁盘满了,坏区,创建文件,扇区,这不算最好的方式, 网络发送,蓝牙串口,有得选的话就不用写文件的方式. 虽然被我淘汰了但这套代码确实可用,只是未经充分测试.测试要话太久.这里把这套代码整理记录下来.

首先不知道为什么 f_mount一执行就会阻断ucosiii.所以只在裸机里用了. 具体见项目hx711_pull_test_std

main.c的主要代码

c 复制代码
#include "stm32f10x.h"
#include "sdio_sd.h"
#include "ff.h"

FATFS fs;         /* Work area (file system object) for logical drive */
FIL fsrc;         /* file objects */   
FRESULT res;
UINT br;

/* ---------- 配置 ---------- */
#define SAMPLE_COUNT   1000                    /* 采样次数 */
#define SAMPLE_SIZE    sizeof(int32_t)         /* 每个采样 4 字节 */
#define BUF_SIZE       (SAMPLE_COUNT * SAMPLE_SIZE)  /* 4000 字节 */
#define WRITES_PER_FILE 3                             /* 每个文件写 50 次 */

/* ---------- 全局 / 静态缓冲区 ---------- */
static char data_buf[BUF_SIZE];               /* 拼装后的 char 数组 */ /*static 可以容量大,且声明周期长*/

/* ---------- 文件管理(静态, 跨调用保持状态) ---------- */
static FIL     s_file;
static uint8_t s_file_open  = 0;
static uint8_t s_write_cnt  = 0;
static uint32_t s_file_idx  = 0;

void  Delay (uint32_t nCount)
{
      /* SysTick 简易延时,72MHz 主频下大约 1ms */
    while (nCount--)
    {
        volatile uint32_t count = 7200;
        while (count--);
    }
}
/* 采集一轮并写入 SD 卡, 外部调用这个即可 */
void CollectAndSave(void)
{
    UINT    bw;
    FRESULT res;

    /* 1. 采集数据到缓冲区 */

    /* 2. 如果当前没有打开的文件, 创建新文件 */
    if (!s_file_open)
    {
        char fname[32];
				printf("new file \n");
        snprintf(fname, sizeof(fname), "log_%04lu.bin", (unsigned long)s_file_idx);

        res = f_open(&s_file, fname, FA_WRITE | FA_CREATE_NEW);
				printf("f_open: res=%d, fname=%s\n", res, fname);  // 加这行
				if (res != FR_OK) return;

        s_file_open = 1;
        s_write_cnt = 0;
    }

    /* 3. 写入(写指针自动后移, 无需 f_lseek) */
    res = f_write(&s_file, data_buf, BUF_SIZE, &bw);
		printf("f_write: res=%d, bw=%u\n", res, (unsigned)bw);//期望res=0, bw=4000
		if (res != FR_OK || bw != BUF_SIZE) {
			printf("write wrong\n");
			f_close(&s_file);
			s_file_open = 0;
			return;  // 写失败直接返回
		}

    /* 4. 可选: 立即刷到卡, 防止掉电丢数据
     *    如果追求极致速度可以去掉, 靠 f_close 统一刷
     */
    res = f_sync(&s_file);
		printf("f_sync: res=%d\n", res);

    /* 5. 计数, 满 50 次则关闭当前文件, 下次调用自动建新文件 */
    s_write_cnt++;
    if (s_write_cnt >= WRITES_PER_FILE)
    {
        res = f_close(&s_file);
				printf("f_close: res=%d\n", res);
        s_file_open = 0;
        s_file_idx++;
			
    }
}
/* 系统关闭前调用, 确保当前文件正确关闭 */
void SaveFlush(void)
{
    if (s_file_open)
    {
        f_close(&s_file);
        s_file_open = 0;
    }
}

int main(void)
{
		
		uint8_t is_sleeping = 0;
		int16_t count = 0;
		
		/*
		每秒读100次, 10秒存一次. 存50次换新文件
		
		hx711_count 拼接1000个数据后,写文件
		写文件一次store_count加1 到50开新文件
	
	每个文件包含 50 次(每次1000个数据)采样的数据(50 × 4000 = 200KB)。*/
	SD_Init();
	res = f_mount(0,&fs);	
		printf("f_mount: res=%d\n", res);
while (1){
	hx711_read = ((HX711_Read()-8211730)*347)>> 16;
			//printf("buf = %d\n", hx711_read);
			if (hx711_count < SAMPLE_COUNT){
				 /*纯二进制拷贝(推荐, 体积小、速度快)
				二进制文件里就是连续的 4 字节 int32_t,用 Python 几行就能转成可读的 CSV 或文本
				用纯二进制的方式,单片机端省空间、省时间、代码也简洁,可读性转换的事交给上位机做就行。
         * 把 int32_t 的 4 个字节按小端序拷贝到 char 数组中
         * memcpy 由编译器保证对齐安全
         */
        memcpy(&data_buf[hx711_count * SAMPLE_SIZE], &hx711_read, SAMPLE_SIZE);
				hx711_count ++;
			}else{ //data_buf 里存了1000个数据, 写文件
				/* 在 CollectAndSave 开头、f_write 之前加 */
				int i;
				int32_t *p = (int32_t *)data_buf;
				for (i = 0; i < 5; i++)
				{
						printf("buf[%d] = %ld\n", i, (long)p[i]);
				}
				hx711_count = 0;
				
				//写入文件,
				CollectAndSave();
				
			}
			
			Delay(10);

}

这个代码很条理清晰, 写文件的操作全部交给CollectAndSave去做, 外部拼接二进制数据, 解读交给上位机 . 二进制数据一般用.bin后缀

依赖的文件

sdio-sd.c/.h

ff.c/.h

diskio.c/.h

ffconf.h

注意diskio.c的disk_write需要改动, 不改的话小文件写没问题,但多块写会卡死,修改为如下,绕过多块写的代码

c 复制代码
DRESULT disk_write (
	BYTE drv,			/* Physical drive nmuber (0..) */
	const BYTE *buff,	        /* Data to be written */
	DWORD sector,		/* Sector address (LBA) */
	BYTE count			/* Number of sectors to write (1..255) */
)
{
	SD_Error Status;
	//printf("disk_write: sector=%lu, count=%u\n", (unsigned long)sector, count);
  if( !count )
  {    
    return RES_PARERR;  /* count不能等于0,否则返回参数错误 */
  }

  switch (drv)
  {
    case 0:
    if(count==1)            /* 1个sector的写操作 */      
    {   
       Status = SD_WriteBlock( (uint8_t *)(&buff[0]) ,sector << 9 , SDCardInfo.CardBlockSize); 
    }                                                
    else                    /* 多个sector的写操作 */    
    {   
       //Status = SD_WriteMultiBlocks( (uint8_t *)(&buff[0]) ,sector << 9 ,SDCardInfo.CardBlockSize,count);	  
			BYTE i;
       for (i = 0; i < count; i++)//问题确认在 SD_WriteMultiBlocks,多块写本身就挂死。那就保持单块写
       {
           Status = SD_WriteBlock((uint8_t *)(&buff[i * 512]),
                                  (sector + i) << 9,
                                  SDCardInfo.CardBlockSize);
           if (Status != SD_OK) break;
       }
    }                                                
    if(Status == SD_OK)
    {
       return RES_OK;
    }
    else
    {
			//printf("disk_write 1wrong:%d \n",Status);
       return RES_ERROR;
    }
    case 2:
	   break;
    default :
       break;
  }
	//printf("disk_write 2wrong:%d \n",Status);
 return RES_ERROR;
}
相关推荐
你疯了抱抱我1 小时前
【STM32】配置vscode+C工具链+Cortex-Debug开发环境,IC:STM32F411CEU6
c语言·vscode·stm32
wangduqiang7471 小时前
stm32休眠和唤醒,和内部flash
stm32·单片机·嵌入式硬件
点灯小铭1 小时前
基于51单片机的LED点阵汉字显示系统设计
数据库·单片机·嵌入式硬件·毕业设计·51单片机·课程设计·期末大作业
橡木树的叶子2 小时前
STM32单片机+RGB三色灯模块+七色灯颜色数据发送到串口调试助手+源代码(使用cubuemx+vscode实现操作)
stm32·单片机·计算机外设
踏着七彩祥云的小丑2 小时前
嵌入式测试学习第 30 天:功耗测试、待机电流、工作电流测试
单片机·嵌入式硬件·学习
潜创微科技2 小时前
2026年专业创作KVM方案服务商选型指南:技术、场景与服务的全维度评估
嵌入式硬件·音视频
大阳1232 小时前
ARM.9(RGBLCD,PWM)
c语言·开发语言·汇编·单片机·嵌入式硬件·pwm·rgblcd
济6172 小时前
ROS开发专栏---ROS2 机械臂应用入门(1)---JointState 消息解析与机械臂往复运动控制实验---适配Ubuntu 22.04
嵌入式硬件·嵌入式·ros2·机器人开发·机器人方向
caimouse2 小时前
ReactOS 项目目录工程分析文档
stm32·单片机·嵌入式硬件