YuanHub 源码分析【一】FlashDB 初始化与项目应用

目录

[一、FlashDB 简介](#一、FlashDB 简介)

[1.1 FlashDB 简介](#1.1 FlashDB 简介)

[二、FlashDB 的初始化](#二、FlashDB 的初始化)

[2.1 向 FlashDB 输入 FLASH 的配置信息](#2.1 向 FlashDB 输入 FLASH 的配置信息)

[2.2 FlashDB 的初始化](#2.2 FlashDB 的初始化)

[2.3 框架的数据库初始化流程](#2.3 框架的数据库初始化流程)

[2.3.1 main()](#2.3.1 main())

[2.3.2 HalInit()](#2.3.2 HalInit())

[2.3.3 AppStoreInit()](#2.3.3 AppStoreInit())

[2.3.4 FlashdbDatabaseInit()](#2.3.4 FlashdbDatabaseInit())

[2.3.5 flashdb_init()](#2.3.5 flashdb_init())

[2.3.6 总结](#2.3.6 总结)

三、参数列表和项目中的初始化

[3.1 参数的初始化](#3.1 参数的初始化)

[2.3.1 main()](#2.3.1 main())

[3.1.2 HalInit()](#3.1.2 HalInit())

[3.1.3 AppParamInit()](#3.1.3 AppParamInit())

[3.1.4 MotorCtrlInit()](#3.1.4 MotorCtrlInit())

[3.1.5 AppStoreInit()](#3.1.5 AppStoreInit())

[3.1.6 flash_param_update()](#3.1.6 flash_param_update())

[3.1.7 app_param_update()](#3.1.7 app_param_update())

[3.1.8 总结](#3.1.8 总结)

四、使用这些数据

[4.1 原理概述](#4.1 原理概述)

[4.2 get() / set() 标准读写接口](#4.2 get() / set() 标准读写接口)

[4.3 命令配置](#4.3 命令配置)

[4.4 1ms 循环定时储存](#4.4 1ms 循环定时储存)


一、FlashDB 简介

1.1 FlashDB 简介

FlashDB 是一款超轻量级的嵌入式数据库,专注于提供嵌入式产品的数据存储方案。FlashDB 不仅支持传统的基于文件系统的数据库模式,而且结合了 Flash 的特性,具有较强的性能及可靠性。并在保证极低的资源占用前提下,尽可能延长 Flash 使用寿命。

FlashDB 提供两种数据库模式:

  • 键值数据库 :是一种非关系数据库,它将数据存储为键值 (Key-Value) 对集合,其中键作为唯一标识符。KVDB 操作简洁,可扩展性强。
  • 时序数据库 :时间序列数据库 (Time Series Database , 简称 TSDB) ,它将数据按照 时间顺序存储 。TSDB 数据具有时间戳,数据存储量大,插入及查询性能高。
cpp 复制代码
servo_hub\board_public\flash_db\inc\fdb_def.h

在文件第 24 行:

cpp 复制代码
#define FDB_SW_VERSION                 "2.1.1"

本项目使用 FlashDB 2.1.1

二、FlashDB 的初始化

2.1 向 FlashDB 输入 FLASH 的配置信息

在文件第 3 行:

cpp 复制代码
servo_hub-master\embedded_project\Y01\Board\bsp_flash.c
cpp 复制代码
struct fal_flash_dev nor_flash0 =
{
    .name       = FAL_USING_NOR_FLASH_DEV_NAME,
    .addr       = 0,
    .len        = 8 * 1024 * 1024,
    .blk_size   = 4 * 1024,
    .ops        = {init, read, write, erase},
    .write_gran = FDB_WRITE_GRAN
};

从上到下分别规定了 flash 名字、起始物理地址偏移、总容量、最小擦除单元、四个硬件操作

|-------------|----------------------------|---------------------------------------------------------|
| 字段 | 值 | 含义 |
| .name | "norflash0" | FAL 内部通过这个名字查找设备(分区表里的 "norflash0" 就是对应这里) |
| .addr | 0 | 这块 Flash 从地址偏移 0 开始,即整块都可用 |
| .len | 8MB | 物理芯片总大小 8MB(实际型号是 GD25Q64,即 GigaDevice 64Mbit = 8MB) |
| .blk_size | 4096 (4KB) | 最小擦除粒度是一个扇区 4KB,erase 操作必须按 4KB 对齐 |
| .ops | {init, read, write, erase} | {init, read, write, erase} 指向下面 4 个静态函数,是真正操作 SPI 硬件的代码 |
| .write_gran | 1bit | NorFlash 可以按 bit 写入(写 1→0),写粒度最细 |

在本文件内实现了 init()、read()、write()、erase() 的四个函数,其实现方式均使用 STM32 的 HAL 库实现,在这里不过多赘述。

因为我们将这些数据存储到外挂 FLASH 中,对于 STM32 H7 系列芯片来说,访问地址 addr. 就是 0x00。

在文件第 3 行:

cpp 复制代码
servo_hub-master\embedded_project\Y01\Board\fal_cfg.h
cpp 复制代码
/* flash device table */
#define FAL_FLASH_DEV_TABLE     \
{                               \
    &nor_flash0,                \
}

对 nor_flash0 的地址取了一个宏定义。

最后在文件第 19 行:

cpp 复制代码
servo_hub-master\board_public\flash_db\port\fal\src\fal_flash.c
cpp 复制代码
static const struct fal_flash_dev * const device_table[] = FAL_FLASH_DEV_TABLE;

所以 我们的 nor_flash0 变量最终被定义为 fal_flash_dev。

2.2 FlashDB 的初始化

在文件第 26 行:

cpp 复制代码
servo_hub-master\servo_hub-master\embedded_project\Y01\Board\fal_cfg.h

它的作用是把一块大容量的 Flash 芯片像电脑硬盘一样分区。

cpp 复制代码
#define FAL_PART_TABLE                                                                                              \
{                                                                                                                   \
    {FAL_PART_MAGIC_WORD,         "bl",    "norflash0",         0,  128*1024, 0}, /* Bootloader: 128K */            \
    {FAL_PART_MAGIC_WORD,        "app",    "norflash0",  128*1024, 2048*1024, 0}, /* Application: 2048K */          \
    {FAL_PART_MAGIC_WORD, "app_backup",    "norflash0", 2176*1024, 1024*1024, 0}, /* Application Backup: 1024K */   \
    {FAL_PART_MAGIC_WORD,   "kv_param",    "norflash0", 3200*1024, 2048*1024, 0}, /* kv_param: 2048K */             \
    /* ...  more partition norflash0 max size: 8192K */ \
}

在文件第 49 行:

cpp 复制代码
servo_hub-master\board_public\flash_db\port\fal\src\fal_partition.c

此处是 FlashDB 的定义:

cpp 复制代码
static const struct fal_partition partition_table_def[] = FAL_PART_TABLE;

其通过下面的流程:

最后初始化这个值:

cpp 复制代码
part_flash_cache[]

把它的地址交给运行时的全局指针 partition_table[],后续所有分区操作都走这个指针。

2.3 框架的数据库初始化流程

2.3.1 main()

cpp 复制代码
int main(void)
{
  ...
  // 硬件及应用初始化
  HalInit();
  ...
}

2.3.2 HalInit()

cpp 复制代码
void HalInit(void)
{
    ...
    AppStoreInit();
    ...
}

2.3.3 AppStoreInit()

如果 flash 中有绑定的 key-value 值,并且 flash 中有数据,这个函数会帮我们从 flash 中读取出来到目标结构体中。

cpp 复制代码
void AppStoreInit(void)
{
    flash_param_update(); // 在初次存储KV还未被创建时,将参数默认值更新到kFlashStorage后写入flash
    FlashdbDatabaseInit();

    set_app_Storage_status(FLASH_STORE_STATUS_IDLE);

    set_app_Storage_cmd(FLASH_STORE_CMD_READ_PARAM);
    AppStoreUpdata1ms();
    set_app_Storage_cmd(FLASH_STORE_CMD_READ_ERROR);
    AppStoreUpdata1ms();
    set_app_Storage_cmd(FLASH_STORE_CMD_READ_TQ_FC);
    AppStoreUpdata1ms();
}

2.3.4 FlashdbDatabaseInit()

根据 key 索引注册表,将 key 名称和 结构体绑定。

|--------------------------------|-------------|-----------------------------------------|------------------------|
| Key 索引 | Key 名称 | 存储内容 | 说明 |
| FLASHDB_KEY_INDEX_ALL_PARAM | AllParam | kFlashStorage | 全量系统参数(如 PID 参数、电机参数等) |
| FLASHDB_KEY_INDEX_ERROR_RECORD | ErrorRecord | kFlashHistoricalInfo.Error_records_list | 历史故障记录列表 |
| FLASHDB_KEY_INDEX_TQ_FC_TABLE | TqFcTable | kAxisDw.tq_fc_id_InstanceData.rtdw | Simulink 生成的运行时数据 |

cpp 复制代码
static void FlashdbDatabaseInit(void)
{
    if (bsp_flashdb_key_register(FLASHDB_KEY_INDEX_ALL_PARAM, "AllParam", \
            &kFlashStorage, sizeof(kFlashStorage)) != FLASHDB_NO_ERR)
    {
        sys_set_bsp_error_state(ERROR_FLASH_STORE, ERROR_SET);
        return;
    }
    if (bsp_flashdb_key_register(FLASHDB_KEY_INDEX_ERROR_RECORD, "ErrorRecord", \
            &kFlashHistoricalInfo.Error_records_list, \
            sizeof(kFlashHistoricalInfo.Error_records_list)) != FLASHDB_NO_ERR)
    {
        sys_set_bsp_error_state(ERROR_FLASH_STORE, ERROR_SET);
        return;
    }
    if (bsp_flashdb_key_register(FLASHDB_KEY_INDEX_TQ_FC_TABLE, "TqFcTable", \
            &kAxisDw.tq_fc_id_InstanceData.rtdw, \
            sizeof(kAxisDw.tq_fc_id_InstanceData.rtdw)) != FLASHDB_NO_ERR)
    {
        sys_set_bsp_error_state(ERROR_FLASH_STORE, ERROR_SET);
        return;
    }

    if (bsp_flashdb_init() != FLASHDB_NO_ERR)
    {
        sys_set_bsp_error_state(ERROR_FLASH_STORE, ERROR_SET);
        return;
    }
}

这个函数最终往 default_kv_table[] 表里注册了我们的 key 信息,并且又将我们的 kAxisDw 中的地址与之对应关联。

2.3.5 flashdb_init()

这个函数的作用是初始化 FlashDB 键值数据库 (KVDB),用于在 Flash 存储器中持久化保存参数。

cpp 复制代码
/**
  * @brief Flashdb初始化
  * @param void
  * @retval fdb_err_t
  * @note
  */
fdb_err_t flashdb_init(void)
{
    struct fdb_kv kv;
    struct fdb_blob blob;

    default_kv.kvs = default_kv_table;
    default_kv.num = FLASHDB_KEYS_NUM;

    /* set the lock and unlock function if you want */
    fdb_kvdb_control(&kvdb, FDB_KVDB_CTRL_SET_LOCK, (void *)lock);
    fdb_kvdb_control(&kvdb, FDB_KVDB_CTRL_SET_UNLOCK, (void *)unlock);

    /* Key-Value database initialization
         *
         *       &kvdb: database object
         *   "YuanHub": database name
         *  "kv_param": The flash partition name base on FAL. Please make sure it's in FAL partition table.
         *              Please change to YOUR partition name.
         * &default_kv: The default KV nodes. It will auto add to KVDB when first initialize successfully.
         *        NULL: The user data if you need, now is empty.
         */
    fdb_err_code = fdb_kvdb_init(&kvdb, "YuanHub", "kv_param", &default_kv, NULL);
    if (fdb_err_code != FDB_NO_ERR)
    {
        return fdb_err_code;
    }

    /* check if the default kv nodes are in the kvdb */
    for (uint8_t index = 0; index < default_kv.num; index++)
    {
        if (fdb_kv_get_obj(&kvdb, default_kv.kvs[index].key, &kv) == NULL)
        {
            fdb_err_code = fdb_kv_set_blob(&kvdb, default_kv.kvs[index].key, \
            fdb_blob_make(&blob, default_kv.kvs[index].value, default_kv.kvs[index].value_len));
            if (fdb_err_code != FDB_NO_ERR)
            {
                return fdb_err_code;
            }
        }
    }

    return fdb_err_code;
}

2.3.6 总结

至此,FlashDB 初始化完成,流程如下表,我跳过了一部分:

函数接口 功能详细说明
main() 程序起点。负责调用 HalInit 开启系统硬件与软件环境初始化,是整个生命周期的入口。
HalInit() 初始化总调度。按照预设优先级统一编排各功能模块(如时钟、外设、中间件等)的启动顺序,确保底层依赖正确。
AppStoreInit() 存储管理总入口。管理所有业务参数的持久化逻辑;初始化完成后会立即将 Flash 数据同步至 RAM,供业务逻辑直接访问。
FlashdbDatabaseInit() 业务数据库配置。向底层注册所有需要持久化的 Key-Value(键值对),并正式启动数据库引擎,建立业务逻辑与存储的映射关系。
flashdb_init() 库核心启动。负责注册线程安全的互斥锁,执行 FlashDB 核心初始化,并对库中缺失的 Key 执行默认值的补写操作。
fdb_kvdb_init() KVDB 挂载。具体初始化 FlashDB 的 KV 数据库模式,定位并挂载名为 "kv_param" 的 Flash 物理分区。

三、参数列表和项目中的初始化

3.1 参数的初始化

2.3.1 main()

cpp 复制代码
int main(void)
{
  ...
  // 硬件及应用初始化
  HalInit();
  ...
}

3.1.2 HalInit()

cpp 复制代码
void HalInit(void)
{
    ...
    AppParamInit();
    MotorCtrlInit();  //电机控制相关初始化
    ...
    AppStoreInit();
    ...
}

3.1.3 AppParamInit()

对 kAppStatusInfo 的变量赋初值,这将是存到 flash 当中的结构体:

cpp 复制代码
void AppParamInit(void)
{
    // 初始化控制字
    kAppControlWord.Controlword = 0;      // 控制字
    kAppControlWord.Halt_running_cmd = 0; // 运行中暂停控制命令

    // 初始化状态信息
    kAppStatusInfo.Statusword = 0;                   // 状态字
    kAppStatusInfo.Error_word = 0;                   // 错误码
    kAppStatusInfo.Alarm_word = 0;                   // 警告码
    kAppStatusInfo.DC_link_circuit_voltage = 0.0f;   // 母线电压当前值
    kAppStatusInfo.Drive_accumulated_heat = 0.0f;    // 驱动器热量累计值
    kAppStatusInfo.Drive_temperature = 0.0f;         // 驱动器当前温度
    kAppStatusInfo.Motor_temperature = 0.0f;         // 电机当前温度
    kAppStatusInfo.Mcu_temperature = 0.0f;           // MCU当前温度
    kAppStatusInfo.Digital_io_inputs_status = 0;     // 数字输入状态
    kAppStatusInfo.Brake_state = BRAKE_STATE_ENGAGED;// 抱闸状态
    
    ...
}

3.1.4 MotorCtrlInit()

对程序的运行变量赋初值,这些变量不写入 flash 中:

cpp 复制代码
void MotorCtrlInit(void)
{
    // 对控制层进行初始化
    MotorCtlSmInit(&kAxis, &kAxisDw);

    // 初始化硬件默认参数配置
    // 电机参数
    kAxis.pmsm_config.tc_s = 1.0f / CURRENT_FREQUENCY_HZ;  // 电流环周期
    kAxis.pmsm_config.tp_s = 1.0f / POSITION_FREQUENCY_HZ; // 位置速度环周期
    kAxis.pmsm_config.j = PMSM_J;
    kAxis.pmsm_config.b = 0.0001f;
    kAxis.pmsm_config.fc = 0.001f;
    kAxis.pmsm_config.ld = PMSM_Lp2p * 0.5f;
    kAxis.pmsm_config.lq = PMSM_Lp2p * 0.5f;
    kAxis.pmsm_config.peak_current = PMSM_PEAK_CURRENT;
    kAxis.pmsm_config.pn = PMSM_PN;
    kAxis.pmsm_config.r = PMSM_Rp2p * 0.5f;
    kAxis.pmsm_config.rated_current = PMSM_RATED_CURRENT;
    kAxis.pmsm_config.speed_max_rpm = PMSM_SPEED_MAX_RPM;
    kAxis.pmsm_config.enc_line_p_n = PMSM_MOTOR_ENC_LINE_P_N;
    kAxis.pmsm_config.kt = PMSM_TORQUE_CONSTANT_MNM_A * 0.001f;
}

3.1.5 AppStoreInit()

如果 flash 中有绑定的 key-value 值,并且 flash 中有数据,这个函数会帮我们从 flash 中读取出来到目标结构体中:

cpp 复制代码
void AppStoreInit(void)
{
    flash_param_update(); // 在初次存储KV还未被创建时,将参数默认值更新到kFlashStorage后写入flash
    FlashdbDatabaseInit();

    set_app_Storage_status(FLASH_STORE_STATUS_IDLE);

    set_app_Storage_cmd(FLASH_STORE_CMD_READ_PARAM);
    AppStoreUpdata1ms();
    set_app_Storage_cmd(FLASH_STORE_CMD_READ_ERROR);
    AppStoreUpdata1ms();
    set_app_Storage_cmd(FLASH_STORE_CMD_READ_TQ_FC);
    AppStoreUpdata1ms();
}

3.1.6 flash_param_update()

在初次存储 KV 还未被创建时,将参数默认值更新到 kFlashStorage 后写入 flash:

cpp 复制代码
void flash_param_init(void)
{
    kFlashStorage.input_shaping_config_wn_Hz = kAxis.input_shaping_config.wn_Hz;
    kFlashStorage.input_shaping_config_run_frq_Hz = kAxis.input_shaping_config.run_frq_Hz;
    kFlashStorage.pos_speed_ctl_config_aff = kAxis.pos_speed_ctl_config.aff;
    kFlashStorage.pos_speed_ctl_config_vff = kAxis.pos_speed_ctl_config.vff;
    kFlashStorage.pos_speed_ctl_config_j_kt = kAxis.pos_speed_ctl_config.j_kt;
    ...
}

3.1.7 app_param_update()

将 kAppControlWord 中结构体的内容同步到 axis 结构体中:

cpp 复制代码
void app_param_update(void)
{
    set_app_Controlword(kAppControlWord.Controlword);
    set_app_Statusword(kAppStatusInfo.Statusword);
    set_app_Error_word(kAppStatusInfo.Error_word);
    set_app_Modes_of_operation(kAppOpMode.Modes_of_operation);
    set_app_Load_encoder_resolution(kAppEncoderConfig.Load_encoder_resolution);
    set_app_Motor_encoder_resolution(kAppEncoderConfig.Motor_encoder_resolution);
    ...
}

比如在 set_app_Controlword() 函数中,val 被赋值给 kAppControlWord 和 axis 中的结构体内:

cpp 复制代码
uint32_t set_app_Controlword(uint16_t val)
{
    ...
    kAppControlWord.Controlword = val;
    /* USER CODE BEGIN set_app_Controlword 1 */
    axis->motor_ctl_sm_input.motor_enable = val;
    /* USER CODE END set_app_Controlword 1 */
    ...
}

3.1.8 总结

项目(函数名) 文件及行号 数据流向 操作的数据层
AppParamInit() data_param.c:41 宏硬编码 -> kAppXxx RAM 应用层(CiA 402 对象字典)
MotorCtrlInit() motor_ctl_loop.c:31 宏硬编码 -> kAxis RAM 控制层(Simulink 算法数据库)
AppStoreInit() app_store_param.c:435 Flash -> kFlashStorage -> RAM -> 通信数据库 存储层(Flash 持久化)
app_param_update() data_param.c:357 kAppXxx RAM -> 通信数据库 通信层(数据库同步)

四、使用这些数据

4.1 原理概述

所有模块统一通过标准化的 set/get 接口访问这个仓库,彼此之间完全解耦。

cpp 复制代码
通信层(CANopen/EtherCAT/MAVLink)
         ↕  set_app_XXX() / get_app_XXX()
         │
    ┌────▼──────────────────────────────┐
    │        database/data_param        │  ← 参数中枢(类OD实现)
    │   kAppControlWord                 │
    │   kAppStatusInfo                  │
    │   kAppMotionParam       ...       │
    │   kAppMotorConfig                 │
    └────┬──────────────────────────────┘
         ↕  直接访问 kAxis / kFlashStorage
         │
    应用层(app_scheduler / app_status_check 等)
    控制层(motor_ctl_loop / motor_ctl_sm)

4.2 get() / set() 标准读写接口

cpp 复制代码
uint16_t get_app_Controlword(void)
{
    /* USER CODE BEGIN get_app_Controlword */
    /* USER CODE END get_app_Controlword */
    return kAppControlWord.Controlword;
}
cpp 复制代码
uint32_t set_app_Controlword(uint16_t val)
{
    // 1. 范围检测
    if (val > 3) return APP_PARAM_OUT_OF_RANGE;

    // 2. 权限检测(控制层接管时只读)
    if (get_app_Internal_control_authority() == INTERNAL_CONTROL_CTRL)
        return APP_PARAM_READ_ONLY;

    // 3. 写入结构体
    kAppControlWord.Controlword = val;

    // 4. 立即同步到控制层
    axis->motor_ctl_sm_input.motor_enable = val;

    return APP_PARAM_SUCCESS;
}

4.3 命令配置

以 Mavlink 的写入值举例:

cpp 复制代码
void MavlinkRecvCallback(Axis *axis, AxisDw *axis_dw, uint8_t rx_data[], uint32_t len)
{
...
            case MAVLINK_MSG_ID_SystemCmd:
                mavlink_msg_systemcmd_decode(&msg, (mavlink_systemcmd_t *)&system_cmd_t);
                set_app_Sys_cmd(system_cmd_t.Sys_cmd);
                system_cmd_t.Sys_cmd = get_app_Sys_cmd();
                mavlink_msg_systemcmd_encode(sys_id, comp_id, &send_msg, (mavlink_systemcmd_t *)&system_cmd_t);
                break;
...
}

通过 set_app_Sys_cmd() 函数便修改了 kSystemStatus.Storage_status 的值:

cpp 复制代码
uint32_t set_app_Sys_cmd(uint8_t val)
{
    /* USER CODE BEGIN set_app_Sys_cmd 0 */
    /* USER CODE END set_app_Sys_cmd 0 */
    kSystemCmd.Sys_cmd = val;
    /* USER CODE BEGIN set_app_Sys_cmd 1 */
    switch (val)
    {
    case APP_SYSTEM_CMD_NONE:
        // Do nothing
        break;
    case APP_SYSTEM_CMD_HOMING:
        // 设置当前位置为系统零点
        // 负载端累计的绝对位置
        // 设置当前目标位置为0
        kAppBaseConfig.Home_position_offset_value = kAppMotionInfo.Position_actual_value_inc;
        set_app_Target_position(0);

        break;
    case APP_SYSTEM_CMD_SAVE_CONFIG:
        if (axis->motor_ctl_sm_output.state == MOTOR_CTL_SM_MOTOR_ENABLE)
        {
            set_app_Storage_status(FLASH_STORE_STATUS_WARNING);
            return APP_PARAM_WRITE_STATE_ERROR;
        }
        set_app_Storage_cmd(FLASH_STORE_CMD_WRITE_PARAM);
        break;
    case APP_SYSTEM_CMD_REBOOT:
        bsp_system_reset();
        break;
    case APP_SYSTEM_CMD_SAVE_TQ_FC_TABLE:
        if (axis->motor_ctl_sm_output.state == MOTOR_CTL_SM_MOTOR_ENABLE)
        {
            set_app_Storage_status(FLASH_STORE_STATUS_WARNING);
            return APP_PARAM_WRITE_STATE_ERROR;
        }
        set_app_Storage_cmd(FLASH_STORE_CMD_WRITE_TQ_FC);
        break;
    case APP_SYSTEM_CMD_TQ_FC_TABLE_CLEAR:
        if (axis->motor_ctl_sm_output.state == MOTOR_CTL_SM_MOTOR_ENABLE)
        {
            set_app_Storage_status(FLASH_STORE_STATUS_WARNING);
            return APP_PARAM_WRITE_STATE_ERROR;
        }
        set_app_Storage_cmd(FLASH_STORE_CMD_ERASE_TQ_FC);
        break;
    case APP_SYSTEM_CMD_ERROR_RECORD_CLEAR:
        if (axis->motor_ctl_sm_output.state == MOTOR_CTL_SM_MOTOR_ENABLE)
        {
            set_app_Storage_status(FLASH_STORE_STATUS_WARNING);
            return APP_PARAM_WRITE_STATE_ERROR;
        }
        set_app_Storage_cmd(FLASH_STORE_CMD_ERASE_ERROR);
        break;
    case APP_SYSTEM_CMD_FACTORY_RESET:
        if (axis->motor_ctl_sm_output.state == MOTOR_CTL_SM_MOTOR_ENABLE)
        {
            set_app_Storage_status(FLASH_STORE_STATUS_WARNING);
            return APP_PARAM_WRITE_STATE_ERROR;
        }
        set_app_Storage_cmd(FLASH_STORE_CMD_ERASE_PARAM);
        break;
    default:
        break;
    }

    /* USER CODE END set_app_Sys_cmd 1 */
    return APP_PARAM_SUCCESS;
}

4.4 1ms 循环定时储存

最后每隔 1ms调用这个更新函数,根据标志位读或者写入 flash 相关的值:

cpp 复制代码
/**
 * @brief 非实时任务1ms循环
 */
void UnrealTimeBase1ms(void)
{
    ...

    // 9. flash参数存储
    AppStoreUpdata1ms();
    ...
}
相关推荐
Deitymoon1 小时前
STM32——串口通信发送数据
stm32·单片机·嵌入式硬件
玩转单片机与嵌入式1 小时前
嵌入式AI场景:哪些应用场景不适合将AI模型部署到单片机(MCU)中?
人工智能·单片机·嵌入式硬件
做cv的小昊2 小时前
【TJU】研究生应用统计学课程笔记(6)——第二章 参数估计(2.4 区间估计)
人工智能·笔记·线性代数·算法·机器学习·数学建模·概率论
czwxkn2 小时前
8STM32(stdl)低功耗模式
stm32·单片机·嵌入式硬件
czwxkn2 小时前
9STM32(stdl)看门狗
stm32·单片机·嵌入式硬件
学机械的鱼鱼3 小时前
【学习笔记】QGroundControl安装与使用简明指南
笔记·学习
zhangrelay3 小时前
Ubuntu 18.04 经典 / 有趣 / 实用 APT 软件清单
linux·笔记·学习·ubuntu
coward913 小时前
Linux 内核 KGDB 以及内核驱动单串口调试笔记:telnet + agent-proxy + gdb-multiarch 实践
linux·单片机·嵌入式硬件
刻BITTER3 小时前
VirtualBox 安装Armbian x86 虚拟机
linux·嵌入式硬件