删除条件配置功能,修改成联动配置DP,并且增加31天运行记录,修改程序需求

This commit is contained in:
2026-09-03 10:21:11 +08:00
parent 5377186cfb
commit d56d8204e1
31 changed files with 1609 additions and 1430 deletions
+137 -116
View File
@@ -1,22 +1,25 @@
/******************************************************************************
* @file app_nvm.c
* @brief CBR3 VM 持久化实现P1mode/fan_gear/条件/定时/使能
* @brief CBR3 VM 持久化实现:mode/fan_gear/联动/定时/运行记录/使能
* @author cyWu <1917507415@qq.com>
* @date 2026.08.31
* @version V1.1.0,实现:3 个 VM item + 防抖 + 立即写
* @date 2026.09.02
* @version V2.0.0(规格书 V3.0.0):删除 CFG_USER_CBR3_COND(49B),改为
* CFG_USER_CBR3_LINKAGE(20B) + CFG_USER_CBR3_RUNREC(128B)
* **CBR3_VM_VER 1→2**,旧条件数据不读不迁移(当新设备)
*
* @note VM item 分配在 syscfg 用户自定义区 [1~49]syscfg_id.h 已占用
* CFG_USER_PAIR_INFO=2 / CFG_USER_TIM_FLAG=3 / VM_ALARM*=4~24 /
* CFG_USER_DEVICE_STA=25 / CFG_USER_PLAN_INFO1=26 /
* CFG_USER_BATTERY_LEVEL=27),本产品从 28 开始:
* CFG_USER_CBR3_STATE=28 状态(magic+ver+mode+gear+en10B
* CFG_USER_CBR3_COND =29 条件配置 cond_config[7][7]49B
* CFG_USER_CBR3_TIME =30 定时配置 time_config[7][8]56B
* 拆 3 个 item:改档/改模式只擦写 10B 小 item,不重写 105B 配置区,
* 减少 flash 磨损;首次上电(magic 不符)保持 gDevData 全 0 默认。
* CFG_USER_CBR3_STATE =28 状态(magic+ver+mode+gear+en10B
* CFG_USER_CBR3_LINKAGE =29 联动配置 linkage_config20B
* CFG_USER_CBR3_TIME =30 定时配置 time_config[7][8]56B
* CFG_USER_CBR3_RUNREC =31 运行记录 31×uint32 + slot0 日期(128B
* 拆 4 个 item:改档/改模式只擦写 10B 小 item,不重写配置区,
* 减少 flash 磨损;首次上电(magic/ver 不符)保持 gDevData 全 0 默认。
*
* 写入时机(规格书第 13 章):APP 改配置后(mark_dirty)、档位/模式
* 稳定变化(500ms 防抖)。
* 写入时机(规格书第 13 章):APP 改联动/定时、单次定时自毁、
* 档位/模式稳定变化(500ms 防抖);运行记录满 10 分钟 + 跨天立刻写
*
* ⚠⚠ 上下文硬约束(2026-08-31 复位事故定论,勿再踩):
* usr_timer_add() 是**异步接口**,回调在**硬件定时器中断上下文**执行
@@ -25,15 +28,18 @@
* - app_nvm_tick() 跑在 20ms 中断上下文 → **只能做变化检测与计时,
* 绝对不能调 syscfg_write**VM 擦写 flash 栈用量大且中断里做
* flash 操作 → 栈溢出软复位;实测 APP 下发数据后 500ms 必崩)。
* - 真正写入由 app_nvm_flush() 在 usr_timer_loop()BLE 线程、
* 任务上下文、栈充足)执行。
* 另:nvm_write_item 的比较缓冲 static,再省 128B 栈
* - 真正写入由 app_nvm_flush() / app_nvm_save_runrec() 在
* usr_timer_loop()BLE 线程、任务上下文、栈充足)执行。
* 另:nvm_write_item 的比较缓冲必须是 static(放栈上会撑爆中断栈)
******************************************************************************/
#include <string.h>
#include "app_nvm.h"
#include "app_mode.h"
#include "app_fan.h"
#include "app_defer.h"
#include "app_runrec.h"
#include "jb_product.h"
#include "board_pin.h"
#include "system/includes.h"
@@ -47,12 +53,13 @@
/* ── VM item id(用户区 1~49 内申请)── */
#define CFG_USER_CBR3_STATE 28
#define CFG_USER_CBR3_COND 29
#define CFG_USER_CBR3_LINKAGE 29
#define CFG_USER_CBR3_TIME 30
#define CFG_USER_CBR3_RUNREC 31
#define CBR3_VM_MAGIC 0xCB03
#define CBR3_VM_VER 1
#define CBR3_VM_DEBOUNCE_MS 500 /* 档位/模式稳定变化防抖(规格书 13 章) */
#define CBR3_VM_VER 2 /* V3.0.0 起:条件→联动+运行记录,旧数据不迁移 */
#define CBR3_VM_DEBOUNCE_MS 500 /* 档位/模式稳定变化防抖(规格书 13 章) */
/** 状态 item10 字节) */
typedef struct {
@@ -60,55 +67,41 @@ typedef struct {
uint8_t ver; /**< CBR3_VM_VER */
uint8_t mode; /**< 逻辑模式 0/1/2 */
uint8_t fan_gear; /**< 真实档位 0~3(模式未激活时即手动档) */
uint8_t cond_en; /**< DP5 条件总开关 */
uint8_t time_en; /**< DP13 定时总开关 */
uint8_t fault_en; /**< DP21 */
uint8_t alarm_en; /**< DP23 */
uint8_t fault_en; /**< DP23 */
uint8_t alarm_en; /**< DP25 */
uint8_t rsv[2]; /**< 对齐到 10B(原 cond_en 位置作废) */
} __packed Cbr3StateVm_t;
/** 条件配置 item49 字节) */
/** 联动配置 item20 字节DP6 原样落盘 */
typedef struct {
uint8_t cond_config[7][7];
} __packed Cbr3CondVm_t;
uint8_t linkage_config[BOARD_LINKAGE_CFG_LEN];
} __packed Cbr3LinkageVm_t;
/** 定时配置 item56 字节) */
typedef struct {
uint8_t time_config[7][8];
} __packed Cbr3TimeVm_t;
/** 运行记录 item124B 秒数 + 4B slot0 日期) */
typedef struct {
uint8_t run_record[APP_RUNREC_PACK_LEN]; /**< 31 × uint32 大端 */
uint32_t slot0_date; /**< slot[0] 对应本地日期 yyyymmdd */
} __packed Cbr3RunrecVm_t;
#if BOARD_NVM_ENABLE
static uint8_t s_dirty; /**< 1=防抖计时中 */
static uint16_t s_deb_ms; /**< 防抖计时 */
/* 防抖已满时用 app_defer_request(APP_DEFER_NVM_SAVE) 打标志,真正落盘
* 由 app_nvm_flush() 在任务上下文里 app_defer_take() 消费,见文件头说明 */
/** NVM 运行时:防抖落盘 + 已落盘档位/模式快照(变化检测基准) */
typedef struct {
uint8_t dirty; /**< 1=防抖计时中 */
uint16_t deb_ms; /**< 防抖累计毫秒 */
uint8_t last_gear; /**< 上次已同步到 VM 的档位 */
uint8_t last_mode; /**< 上次已同步到 VM 的模式 */
uint8_t last_time_en; /**< 上次已同步到 VM 的定时总开关 */
} AppNvmCtx_t;
/* 上次已同步到 VM 的状态快照:变化检测基准(覆盖按键/APP/模式引擎所有入口) */
static uint8_t s_last_gear;
static uint8_t s_last_mode;
static uint8_t s_last_cond_en;
static uint8_t s_last_time_en;
static AppNvmCtx_t s_nvm;
#endif
/**
* @brief 从 gDevData 收集条件配置到 VM 结构
* @note gDevData.cond_config1~7 是 7 个独立数组字段,用指针表统一搬运
* @param out 输出结构
* @return 无
*/
static void nvm_collect_cond(Cbr3CondVm_t *out)
{
const uint8_t *src[7] = {
gDevData.cond_config1, gDevData.cond_config2, gDevData.cond_config3,
gDevData.cond_config4, gDevData.cond_config5, gDevData.cond_config6,
gDevData.cond_config7
};
uint8_t i;
for (i = 0; i < 7; i++) {
memcpy(out->cond_config[i], src[i], 7);
}
}
/**
* @brief 从 gDevData 收集定时配置到 VM 结构
* @param out 输出结构
@@ -132,7 +125,7 @@ static void nvm_collect_time(Cbr3TimeVm_t *out)
* @brief 写单个 item:先读回比较,内容相同跳过,避免无谓擦写 flash
* @param item_id VM item id
* @param buf 新数据
* @param len 数据长度
* @param len 数据长度(≤128,比较缓冲大小)
* @return 无
*/
static void nvm_write_item(u16 item_id, void *buf, u16 len)
@@ -164,27 +157,27 @@ static void nvm_write_state(void)
{
Cbr3StateVm_t st;
memset(&st, 0, sizeof(st));
st.magic = CBR3_VM_MAGIC;
st.ver = CBR3_VM_VER;
st.mode = app_mode_get_active();
st.fan_gear = app_fan_get_gear();
st.cond_en = gDevData.cond_en ? 1 : 0;
st.time_en = gDevData.time_en ? 1 : 0;
st.fault_en = gDevData.fault_en ? 1 : 0;
st.alarm_en = gDevData.alarm_en ? 1 : 0;
st.fault_en = gDevData.fault_en;
st.alarm_en = gDevData.alarm_en;
nvm_write_item(CFG_USER_CBR3_STATE, &st, sizeof(st));
}
/**
* @brief 写条件配置 item
* @brief 写联动配置 item20B 原样落盘)
* @return 无
*/
static void nvm_write_cond(void)
static void nvm_write_linkage(void)
{
Cbr3CondVm_t cf;
Cbr3LinkageVm_t lf;
nvm_collect_cond(&cf);
nvm_write_item(CFG_USER_CBR3_COND, &cf, sizeof(cf));
memcpy(lf.linkage_config, gDevData.linkage_config, BOARD_LINKAGE_CFG_LEN);
nvm_write_item(CFG_USER_CBR3_LINKAGE, &lf, sizeof(lf));
}
/**
@@ -199,6 +192,19 @@ static void nvm_write_time(void)
nvm_write_item(CFG_USER_CBR3_TIME, &tf, sizeof(tf));
}
/**
* @brief 写运行记录 item31 格秒数 + slot0 日期)
* @return 无
*/
static void nvm_write_runrec(void)
{
Cbr3RunrecVm_t rf;
app_runrec_pack(rf.run_record);
rf.slot0_date = app_runrec_get_slot0_date();
nvm_write_item(CFG_USER_CBR3_RUNREC, &rf, sizeof(rf));
}
/**
* @brief 初始化:校验 VM 有效性并打日志(加载在 app_nvm_load
* @return 无
@@ -213,13 +219,13 @@ void app_nvm_init(void)
int ret = syscfg_read(CFG_USER_CBR3_STATE, &st, sizeof(st));
if ((ret == (int)sizeof(st)) && (st.magic == CBR3_VM_MAGIC) && (st.ver == CBR3_VM_VER)) {
log_info("nvm init, vm valid (mode=%d gear=%d cond_en=%d time_en=%d)\n",
st.mode, st.fan_gear, st.cond_en, st.time_en);
log_info("nvm init, vm valid v%d (mode=%d gear=%d time_en=%d)\n",
st.ver, st.mode, st.fan_gear, st.time_en);
} else {
/* ver 不符 = 旧条件固件留下的 VM(规格书 13 章:不迁移,当新设备) */
log_info("nvm init, vm invalid (ret=%d), use defaults\n", ret);
}
s_dirty = 0;
s_deb_ms = 0;
memset(&s_nvm, 0, sizeof(s_nvm));
app_defer_cancel(APP_DEFER_NVM_SAVE);
#endif
}
@@ -231,15 +237,14 @@ void app_nvm_init(void)
static void nvm_sync_snapshot(void)
{
#if BOARD_NVM_ENABLE
s_last_gear = app_fan_get_gear();
s_last_mode = app_mode_get_active();
s_last_cond_en = gDevData.cond_en ? 1 : 0;
s_last_time_en = gDevData.time_en ? 1 : 0;
s_nvm.last_gear = app_fan_get_gear();
s_nvm.last_mode = app_mode_get_active();
s_nvm.last_time_en = gDevData.time_en ? 1 : 0;
#endif
}
/**
* @brief 立即保存全部业务字段(软关机前调用)
* @brief 立即保存全部业务字段
* @return 无
*/
void app_nvm_save(void)
@@ -248,20 +253,34 @@ void app_nvm_save(void)
return;
#else
nvm_write_state();
nvm_write_cond();
nvm_write_linkage();
nvm_write_time();
s_dirty = 0;
s_deb_ms = 0;
nvm_write_runrec();
s_nvm.dirty = 0;
s_nvm.deb_ms = 0;
app_defer_cancel(APP_DEFER_NVM_SAVE);
nvm_sync_snapshot();
#endif
}
/**
* @brief 从 VM 加载并应用(开机 3s 后由 usr_jb_init 调用
* @brief 立即保存运行记录项(跨天旋转用,任务上下文
* @return 无
*/
void app_nvm_save_runrec(void)
{
#if !BOARD_NVM_ENABLE
return;
#else
nvm_write_runrec();
#endif
}
/**
* @brief 从 VM 加载并应用(开机由 usr_jb_init 调用)
* @note 顺序:先恢复档位再恢复模式(app_mode_set_manual 内部会快照当前档、
* 并让通风/强力从周期头开跑,规格书第 9 章"开机恢复不存相位")。
* VM 无效时保持 gDevData 全 0(首次上电默认 mode=0 gear=0
* VM 无效(magic/ver 不符,含旧条件固件)时保持 gDevData 全 0 默认
* @return 无
*/
void app_nvm_load(void)
@@ -269,9 +288,10 @@ void app_nvm_load(void)
#if !BOARD_NVM_ENABLE
return;
#else
Cbr3StateVm_t st;
Cbr3CondVm_t cf;
Cbr3TimeVm_t tf;
Cbr3StateVm_t st;
Cbr3LinkageVm_t lf;
Cbr3TimeVm_t tf;
Cbr3RunrecVm_t rf;
uint8_t i;
if ((syscfg_read(CFG_USER_CBR3_STATE, &st, sizeof(st)) != (int)sizeof(st))
@@ -280,17 +300,12 @@ void app_nvm_load(void)
return;
}
/* 条件/定时配置回填 gDevData只存不跑,P2 引擎再消费 */
if (syscfg_read(CFG_USER_CBR3_COND, &cf, sizeof(cf)) == (int)sizeof(cf)) {
uint8_t *dst[7] = {
gDevData.cond_config1, gDevData.cond_config2, gDevData.cond_config3,
gDevData.cond_config4, gDevData.cond_config5, gDevData.cond_config6,
gDevData.cond_config7
};
for (i = 0; i < 7; i++) {
memcpy(dst[i], cf.cond_config[i], 7);
}
/* 联动配置回填 gDevData20B 原样;引擎由 BOARD_LINKAGE_ENABLE 决定是否跑 */
if (syscfg_read(CFG_USER_CBR3_LINKAGE, &lf, sizeof(lf)) == (int)sizeof(lf)) {
memcpy(gDevData.linkage_config, lf.linkage_config, BOARD_LINKAGE_CFG_LEN);
}
/* 定时配置回填 gDevData(只存不跑时也要回填,P1 验收) */
if (syscfg_read(CFG_USER_CBR3_TIME, &tf, sizeof(tf)) == (int)sizeof(tf)) {
uint8_t *dst[7] = {
gDevData.time_config1, gDevData.time_config2, gDevData.time_config3,
@@ -302,10 +317,15 @@ void app_nvm_load(void)
}
}
gDevData.cond_en = st.cond_en ? 1 : 0;
/* 运行记录回填 RAM(13.1:掉电再上电先把 31 格灌回来) */
if (syscfg_read(CFG_USER_CBR3_RUNREC, &rf, sizeof(rf)) == (int)sizeof(rf)) {
app_runrec_unpack(rf.run_record);
app_runrec_set_slot0_date(rf.slot0_date);
}
gDevData.time_en = st.time_en ? 1 : 0;
gDevData.fault_en = st.fault_en ? 1 : 0;
gDevData.alarm_en = st.alarm_en ? 1 : 0;
gDevData.fault_en = st.fault_en;
gDevData.alarm_en = st.alarm_en;
/* 先档位后模式;mode 激活时引擎会从周期头强制档位 */
if (st.fan_gear > 3) {
@@ -314,8 +334,8 @@ void app_nvm_load(void)
app_fan_set_gear(st.fan_gear);
app_mode_set_manual(st.mode);
nvm_sync_snapshot(); /* 恢复值即为已落盘值,同步基准 */
log_info("nvm load: mode=%d gear=%d cond_en=%d time_en=%d\n",
st.mode, st.fan_gear, st.cond_en, st.time_en);
log_info("nvm load v%d: mode=%d gear=%d time_en=%d\n",
st.ver, st.mode, st.fan_gear, st.time_en);
#endif
}
@@ -336,26 +356,24 @@ void app_nvm_tick(uint16_t dt)
#else
uint8_t gear = app_fan_get_gear();
uint8_t mode = app_mode_get_active();
uint8_t cond_en = gDevData.cond_en ? 1 : 0;
uint8_t time_en = gDevData.time_en ? 1 : 0;
if ((gear != s_last_gear) || (mode != s_last_mode)
|| (cond_en != s_last_cond_en) || (time_en != s_last_time_en)) {
s_last_gear = gear;
s_last_mode = mode;
s_last_cond_en = cond_en;
s_last_time_en = time_en;
s_dirty = 1;
s_deb_ms = 0; /* 变化重新计满防抖窗口 */
if ((gear != s_nvm.last_gear) || (mode != s_nvm.last_mode)
|| (time_en != s_nvm.last_time_en)) {
s_nvm.last_gear = gear;
s_nvm.last_mode = mode;
s_nvm.last_time_en = time_en;
s_nvm.dirty = 1;
s_nvm.deb_ms = 0; /* 变化重新计满防抖窗口 */
}
if (!s_dirty) {
if (!s_nvm.dirty) {
return;
}
s_deb_ms = (uint16_t)(s_deb_ms + dt);
if (s_deb_ms >= CBR3_VM_DEBOUNCE_MS) {
s_deb_ms = 0;
s_dirty = 0;
s_nvm.deb_ms = (uint16_t)(s_nvm.deb_ms + dt);
if (s_nvm.deb_ms >= CBR3_VM_DEBOUNCE_MS) {
s_nvm.deb_ms = 0;
s_nvm.dirty = 0;
app_defer_request(APP_DEFER_NVM_SAVE); /* 只置标志:真正落盘在 app_nvm_flush */
}
#endif
@@ -386,14 +404,14 @@ void app_nvm_flush(void)
void app_nvm_mark_dirty(void)
{
#if BOARD_NVM_ENABLE
s_dirty = 1;
s_deb_ms = 0;
s_nvm.dirty = 1;
s_nvm.deb_ms = 0;
#endif
}
/**
* @brief 出厂重置:清全部业务 VM(保留 BLE 配对,规格书第 14 章)
* @note 3 个 item 写全 0 → magic 失效,下次开机走首次上电默认值;
* @note 4 个 item 写全 0 → magic 失效,下次开机走首次上电默认值;
* BLE 配对三元组在 CFG_USER_PAIR_INFOitem 2),不受影响
* @return 无
*/
@@ -402,18 +420,21 @@ void app_nvm_reset(void)
#if !BOARD_NVM_ENABLE
return;
#else
Cbr3StateVm_t st;
Cbr3CondVm_t cf;
Cbr3TimeVm_t tf;
Cbr3StateVm_t st;
Cbr3LinkageVm_t lf;
Cbr3TimeVm_t tf;
Cbr3RunrecVm_t rf;
memset(&st, 0, sizeof(st));
memset(&cf, 0, sizeof(cf));
memset(&lf, 0, sizeof(lf));
memset(&tf, 0, sizeof(tf));
memset(&rf, 0, sizeof(rf));
syscfg_write(CFG_USER_CBR3_STATE, &st, sizeof(st));
syscfg_write(CFG_USER_CBR3_COND, &cf, sizeof(cf));
syscfg_write(CFG_USER_CBR3_LINKAGE, &lf, sizeof(lf));
syscfg_write(CFG_USER_CBR3_TIME, &tf, sizeof(tf));
s_dirty = 0;
s_deb_ms = 0;
syscfg_write(CFG_USER_CBR3_RUNREC, &rf, sizeof(rf));
s_nvm.dirty = 0;
s_nvm.deb_ms = 0;
app_defer_cancel(APP_DEFER_NVM_SAVE);
log_info("nvm factory reset, business vm cleared (pairing kept)\n");
#endif