You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何基于Mbed在Nucleo开发板内部存储器记录时间日期并保存100条记录

嘿,我来帮你搞定这个需求——基于Mbed框架在Nucleo板的内部存储器里存储100条时间日期记录。我之前做过类似的项目,下面一步步给你拆解思路,再附上可落地的C/C++代码指导:

整体实现思路

要完成这个功能,核心要解决三个问题:时间获取数据存储结构设计内部Flash操作,具体拆解如下:

  • 时间来源:用Nucleo板自带的硬件RTC(实时时钟),Mbed框架有现成的API可以直接调用,比软件模拟的RTC精度高得多。
  • 存储介质:优先选STM32芯片的内部Flash作为非易失存储(多数Nucleo板的STM32没有内置EEPROM,用Flash模拟非易失存储是最常用的方案)。Flash的扇区大小足够存100条记录(每条按8字节算,100条才800字节,远小于最小的Flash扇区)。
  • 数据管理:设计固定格式的时间记录结构体,用一个计数器记录当前存储的条目数,达到100条时可以选择循环覆盖(避免Flash空间不足),每次写入前要确保Flash扇区已擦除(Flash特性:只能从1写0,必须先擦除才能写入新数据)。
具体C/C++代码实现指导

下面的代码基于Mbed OS 6.x编写,适配大多数Nucleo板(比如STM32F4、F1系列),你可以根据自己的芯片微调参数。

1. 基础依赖与定义

首先引入必要的头文件,定义时间记录结构体和Flash存储的关键参数:

#include "mbed.h"
#include "FlashIAP.h"

// 定义时间记录结构体,内存对齐后每条占8字节
typedef struct {
    uint16_t year;   // 年(比如2024)
    uint8_t month;   // 月(1-12)
    uint8_t day;     // 日(1-31)
    uint8_t hour;    // 时(0-23)
    uint8_t minute;  // 分(0-59)
    uint8_t second;  // 秒(0-59)
} TimeRecord;

// Flash存储配置:根据你的芯片调整起始地址(查芯片手册找空闲扇区)
FlashIAP flash;
const uint32_t FLASH_STORAGE_START = 0x080E0000;  // STM32F4最后一个扇区起始地址
const uint32_t MAX_RECORDS = 100;
const uint32_t RECORD_SIZE = sizeof(TimeRecord);
const uint32_t TOTAL_STORAGE_SIZE = MAX_RECORDS * RECORD_SIZE;
uint32_t current_record_count = 0;  // 当前已存储的记录数

2. 初始化存储系统

上电后先初始化Flash,读取已存储的记录数,第一次使用时擦除扇区并初始化计数器:

void init_storage() {
    flash.init();

    // 读取存储在Flash末尾的计数器
    flash.read(&current_record_count, FLASH_STORAGE_START + TOTAL_STORAGE_SIZE, sizeof(current_record_count));

    // 如果是首次使用,Flash默认值为0xFFFFFFFF,初始化计数器并擦除扇区
    if (current_record_count == 0xFFFFFFFF) {
        current_record_count = 0;
        uint32_t sector_size = flash.get_sector_size(FLASH_STORAGE_START);
        flash.erase(FLASH_STORAGE_START, sector_size);
        // 写入初始计数器值
        flash.program(&current_record_count, FLASH_STORAGE_START + TOTAL_STORAGE_SIZE, sizeof(current_record_count));
    }
}

3. 获取当前RTC时间

通过Mbed的时间API获取格式化的时间记录:

TimeRecord get_current_time() {
    TimeRecord record;
    time_t current_epoch = time(NULL);
    struct tm* time_info = localtime(&current_epoch);

    record.year = time_info->tm_year + 1900;  // tm_year是从1900年开始的偏移量
    record.month = time_info->tm_mon + 1;     // tm_mon是0-11,要+1
    record.day = time_info->tm_mday;
    record.hour = time_info->tm_hour;
    record.minute = time_info->tm_min;
    record.second = time_info->tm_sec;

    return record;
}

4. 写入时间记录到Flash

处理记录写入逻辑,达到最大条数时循环覆盖:

bool write_time_record(TimeRecord record) {
    // 达到最大记录数时,擦除扇区并重置计数器(循环覆盖)
    if (current_record_count >= MAX_RECORDS) {
        current_record_count = 0;
        uint32_t sector_size = flash.get_sector_size(FLASH_STORAGE_START);
        flash.erase(FLASH_STORAGE_START, sector_size);
    }

    // 计算当前记录的写入地址
    uint32_t write_addr = FLASH_STORAGE_START + current_record_count * RECORD_SIZE;
    // 写入记录到Flash
    flash.program(&record, write_addr, RECORD_SIZE);

    // 更新计数器并写入Flash
    current_record_count++;
    flash.program(&current_record_count, FLASH_STORAGE_START + TOTAL_STORAGE_SIZE, sizeof(current_record_count));

    return true;
}

5. 读取所有存储的记录

从Flash中读取已存储的时间记录:

void read_all_records(TimeRecord records[], uint32_t* out_count) {
    *out_count = current_record_count > MAX_RECORDS ? MAX_RECORDS : current_record_count;
    for (uint32_t i = 0; i < *out_count; i++) {
        uint32_t read_addr = FLASH_STORAGE_START + i * RECORD_SIZE;
        flash.read(&records[i], read_addr, RECORD_SIZE);
    }
}

6. 主函数示例

整合所有功能,实现定时写入和读取演示:

int main() {
    // 初始化RTC:设置初始时间为2024-01-01 00:00:00(Unix时间戳)
    set_time(1704067200);
    // 初始化存储系统
    init_storage();

    // 读取已存储的记录并打印
    TimeRecord stored_records[MAX_RECORDS];
    uint32_t record_count;
    read_all_records(stored_records, &record_count);
    printf("已存储 %d 条记录:\n", record_count);
    for (uint32_t i = 0; i < record_count; i++) {
        printf("%04d-%02d-%02d %02d:%02d:%02d\n",
               stored_records[i].year, stored_records[i].month, stored_records[i].day,
               stored_records[i].hour, stored_records[i].minute, stored_records[i].second);
    }

    // 每隔10秒写入一条新记录
    while (1) {
        ThisThread::sleep_for(10s);
        TimeRecord new_record = get_current_time();
        write_time_record(new_record);
        printf("已写入新记录: %04d-%02d-%02d %02d:%02d:%02d\n",
               new_record.year, new_record.month, new_record.day,
               new_record.hour, new_record.minute, new_record.second);
    }
}
关键注意事项
  • Flash地址调整:不同STM32芯片的Flash扇区地址和大小不同,比如STM32F1的扇区大小是1KB/2KB,你需要查芯片参考手册找到合适的空闲扇区,或者用flash.get_sector_start()动态获取。
  • Flash寿命:内部Flash的擦写次数约为10万次,循环覆盖可以延长使用寿命,如果需要更专业的磨损均衡,可以设计多个扇区轮流使用。
  • RTC保持:Nucleo板的RTC断电后,如果没有外接后备电池,上电需要重新设置时间,你可以通过串口或按键输入来设置初始时间。
  • 数据完整性:如果写入过程中断电,可能会损坏记录,你可以给每条记录加校验位(比如CRC),读取时验证完整性。

内容的提问来源于stack exchange,提问作者joe adamu

火山引擎 最新活动