接入温湿度风扇产品固件(仲裁/条件/定时/显示),并补齐规格书与外设驱动
新增 usr_app 产品逻辑:手动>定时>条件仲裁、条件控温湿、定时任务、数码管显示、 按键/LED、风扇档位、NVM 掉电恢复、配对与开关机,上电按规格恢复上次状态。 新增 usr_periph:AHT20、CT1642、板级引脚与硬件封装;见宝协议 cond_config 改为 7 字节编解码,并接入产品 DP。 补充《温湿度风扇_固件需求规格书》,工程加入对应源文件。 .gitignore 忽略 WorkBuddy/Cursor 本地会话
This commit is contained in:
@@ -1,18 +1,30 @@
|
||||
/* ================================================================
|
||||
* jb_product.c — 用户层实现(请填补空白处)
|
||||
* 设备: 温湿度风扇 | 生成时间: 2026-08-29 10:31:50
|
||||
* jb_product.c — 用户层实现(薄适配:只写 DP,不写业务算法)
|
||||
* 设备: 温湿度风扇 | 生成时间: 2026-08-29(cond_config 已按 Excel 重生 7 字节)
|
||||
*
|
||||
* ══ 开发流程 ══
|
||||
* 1. 实现硬件抽象: jbGetTimerCount(定时器由 SOC SDK 提供)
|
||||
* 发送由 SOC SDK 的 ble_send_data 承担,协议层已直接调用,无需 uartWrite
|
||||
* 2. userInit() 设置 DP 默认值与外设初始化
|
||||
* 3. userHandle() 用户数据采集
|
||||
* 4. jbEventProcess() 处理 APP 下发的控制命令
|
||||
* 5. 主动上报 / OTA 的网关应答在收到 REPORTCMD_V2 后调用 jbReportAckCheck 清除等待
|
||||
* 发送由 SOC SDK 的 ble_send_data 承担,协议层已直接调用
|
||||
* 2. userInit() 设置 DP 默认值
|
||||
* 3. userHandle() 填温湿度 DP(500ms)
|
||||
* 4. jbEventProcess() APP 下行:只写 gDevData 并转调 app_*,不在这里写业务
|
||||
* ================================================================
|
||||
*/
|
||||
|
||||
#include "jb_product.h"
|
||||
#include "app_th.h"
|
||||
#include "app_fan.h"
|
||||
#include "app_mode.h"
|
||||
#include "app_cond.h"
|
||||
#include "app_timer_task.h"
|
||||
#include "app_arbiter.h"
|
||||
#include "app_nvm.h"
|
||||
#include "app_function.h"
|
||||
|
||||
#define LOG_TAG_CONST APP
|
||||
#define LOG_TAG "[JB_PRD]"
|
||||
#define LOG_INFO_ENABLE
|
||||
#include "debug.h"
|
||||
|
||||
/* ════════════════════════════════════════════════════════════════
|
||||
* 全局变量
|
||||
@@ -28,19 +40,19 @@ void userInit(void)
|
||||
{
|
||||
memset(&gDevData, 0, sizeof(jb_data_point_t));
|
||||
|
||||
gDevData.power = 0; /* DP0 开关 0=关 1=开 */
|
||||
gDevData.power = 0; /* DP0 预留:运行中由 app_function 上报 1,写忽略 */
|
||||
gDevData.mode = 0; /* MODE_0 (关闭模式) */
|
||||
gDevData.fan_gear = 0; /* FAN_GEAR_0 (关闭风扇) */
|
||||
gDevData.current_temp = 0.0f; /* DP3 当前温度 (float) */
|
||||
gDevData.current_humi = 0; /* DP4 当前湿度 */
|
||||
gDevData.cond_en = 0; /* DP5 条件使能 0=关 1=开 */
|
||||
memset(gDevData.cond_config1, 0, 6); /* DP6 条件配置1 */
|
||||
memset(gDevData.cond_config2, 0, 6); /* DP7 条件配置2 */
|
||||
memset(gDevData.cond_config3, 0, 6); /* DP8 条件配置3 */
|
||||
memset(gDevData.cond_config4, 0, 6); /* DP9 条件配置4 */
|
||||
memset(gDevData.cond_config5, 0, 6); /* DP10 条件配置5 */
|
||||
memset(gDevData.cond_config6, 0, 6); /* DP11 条件配置6 */
|
||||
memset(gDevData.cond_config7, 0, 6); /* DP12 条件配置7 */
|
||||
memset(gDevData.cond_config1, 0, 7); /* DP6 条件配置1(7 字节) */
|
||||
memset(gDevData.cond_config2, 0, 7); /* DP7 条件配置2 */
|
||||
memset(gDevData.cond_config3, 0, 7); /* DP8 条件配置3 */
|
||||
memset(gDevData.cond_config4, 0, 7); /* DP9 条件配置4 */
|
||||
memset(gDevData.cond_config5, 0, 7); /* DP10 条件配置5 */
|
||||
memset(gDevData.cond_config6, 0, 7); /* DP11 条件配置6 */
|
||||
memset(gDevData.cond_config7, 0, 7); /* DP12 条件配置7 */
|
||||
gDevData.time_en = 0; /* DP13 定时使能 0=关 1=开 */
|
||||
memset(gDevData.time_config1, 0, 8); /* DP14 定时配置1 */
|
||||
memset(gDevData.time_config2, 0, 8); /* DP15 定时配置2 */
|
||||
@@ -52,7 +64,7 @@ void userInit(void)
|
||||
gDevData.fault_en = 0; /* DP21 设备故障使能 */
|
||||
gDevData.fault_code = 0; /* DP22 设备故障 */
|
||||
gDevData.alarm_en = 0; /* DP23 设备告警使能 */
|
||||
gDevData.alarm_code = 0; /* DP24 设备告警 */
|
||||
gDevData.alarm_code = 0; /* DP24 设备告警(保持 0) */
|
||||
gDevData.factory_reset = 0; /* DP25 恢复出厂设置 0=关 1=开 */
|
||||
gDevData.reserva_dp1 = 0; /* DP26 预留DP1 */
|
||||
gDevData.reserva_dp2 = 0; /* DP27 预留DP2 */
|
||||
@@ -60,48 +72,23 @@ void userInit(void)
|
||||
}
|
||||
|
||||
/* ════════════════════════════════════════════════════════════════
|
||||
* userHandle — 用户数据采集
|
||||
* 协议层自动检测变化并上报(注意:此处是上报,不是下发控制)
|
||||
* userHandle — 用户数据采集(500ms 由 usr_timer_loop 调用)
|
||||
* 只填温湿度上报 DP;档位/模式/fault 由 app_function_poll(20ms)同步
|
||||
* ════════════════════════════════════════════════════════════════ */
|
||||
void userHandle(void)
|
||||
{
|
||||
/* ── 可上报 DP (RO / RW): 用户数据采集 ── */
|
||||
// gDevData.power = read_sensor(); /* DP0 开关 */
|
||||
// gDevData.mode = read_sensor(); /* DP1 模式 */
|
||||
// gDevData.fan_gear = read_sensor(); /* DP2 风速挡位 */
|
||||
// gDevData.current_temp = 23.5f; /* DP3 当前温度 (float, scale=0.1), 直接填物理量 */
|
||||
// gDevData.current_humi = read_sensor(); /* DP4 当前湿度 */
|
||||
// gDevData.cond_en = read_sensor(); /* DP5 条件使能 */
|
||||
// memcpy(gDevData.cond_config1, sensor_read(), 6); /* DP6 条件配置1 */
|
||||
// memcpy(gDevData.cond_config2, sensor_read(), 6); /* DP7 条件配置2 */
|
||||
// memcpy(gDevData.cond_config3, sensor_read(), 6); /* DP8 条件配置3 */
|
||||
// memcpy(gDevData.cond_config4, sensor_read(), 6); /* DP9 条件配置4 */
|
||||
// memcpy(gDevData.cond_config5, sensor_read(), 6); /* DP10 条件配置5 */
|
||||
// memcpy(gDevData.cond_config6, sensor_read(), 6); /* DP11 条件配置6 */
|
||||
// memcpy(gDevData.cond_config7, sensor_read(), 6); /* DP12 条件配置7 */
|
||||
// gDevData.time_en = read_sensor(); /* DP13 定时使能 */
|
||||
// memcpy(gDevData.time_config1, sensor_read(), 8); /* DP14 定时配置1 */
|
||||
// memcpy(gDevData.time_config2, sensor_read(), 8); /* DP15 定时配置2 */
|
||||
// memcpy(gDevData.time_config3, sensor_read(), 8); /* DP16 定时配置3 */
|
||||
// memcpy(gDevData.time_config4, sensor_read(), 8); /* DP17 定时配置4 */
|
||||
// memcpy(gDevData.time_config5, sensor_read(), 8); /* DP18 定时配置5 */
|
||||
// memcpy(gDevData.time_config6, sensor_read(), 8); /* DP19 定时配置6 */
|
||||
// memcpy(gDevData.time_config7, sensor_read(), 8); /* DP20 定时配置7 */
|
||||
// gDevData.fault_en = read_sensor(); /* DP21 设备故障使能 */
|
||||
// gDevData.fault_code = read_sensor(); /* DP22 设备故障 */
|
||||
// gDevData.alarm_en = read_sensor(); /* DP23 设备告警使能 */
|
||||
// gDevData.alarm_code = read_sensor(); /* DP24 设备告警 */
|
||||
// gDevData.reserva_dp1 = read_sensor(); /* DP26 预留DP1 */
|
||||
// gDevData.reserva_dp2 = read_sensor(); /* DP27 预留DP2 */
|
||||
// gDevData.reserva_dp3 = read_sensor(); /* DP28 预留DP3 */
|
||||
if (app_th_get_valid()) {
|
||||
/* DP3 链路传 uint16(scale=0.1),直接填物理量 float */
|
||||
gDevData.current_temp = (float)app_th_get_temp_x10() / 10.0f;
|
||||
gDevData.current_humi = app_th_get_humi();
|
||||
}
|
||||
}
|
||||
|
||||
/* ════════════════════════════════════════════════════════════════
|
||||
* jbEventProcess — 0x03 APP 控制事件处理(下行 / DP 赋值方向)
|
||||
*
|
||||
* 协议层自动解包 0x03 帧后调用本函数。
|
||||
* info->dp_ids[] = 被控制的 DP ID 列表
|
||||
* ctrl 已填入下发的值;复制到 gDevData 并执行动作
|
||||
* ⚠ 只做两件事:DP 写入 gDevData + 转调 app_* 模块。业务算法一律
|
||||
* 不进本文件(规格书第 0 章第 2 条)。
|
||||
* ════════════════════════════════════════════════════════════════ */
|
||||
int8_t jbEventProcess(jb_event_info_t *info, jb_data_point_t *ctrl)
|
||||
{
|
||||
@@ -114,216 +101,122 @@ int8_t jbEventProcess(jb_event_info_t *info, jb_data_point_t *ctrl)
|
||||
{
|
||||
switch (info->dp_ids[i])
|
||||
{
|
||||
|
||||
/* ── DP0 开关 (bool) ── */
|
||||
/* ── DP0 开关:预留,写忽略(规格书第 15 章)── */
|
||||
case DP_ID_POWER:
|
||||
if (ctrl->power)
|
||||
{
|
||||
gDevData.power = 1;
|
||||
// TODO: 开 — 执行动作
|
||||
}
|
||||
else
|
||||
{
|
||||
gDevData.power = 0;
|
||||
// TODO: 关 — 执行动作
|
||||
}
|
||||
break;
|
||||
|
||||
/* ── DP1 模式 (enum) ── */
|
||||
/* ── DP1 模式:写则手动抢权(仲裁器释放自动化占用)── */
|
||||
case DP_ID_MODE:
|
||||
gDevData.mode = ctrl->mode;
|
||||
switch (ctrl->mode)
|
||||
{
|
||||
case MODE_0: /* 关闭模式 */
|
||||
// TODO: 处理 关闭模式
|
||||
break;
|
||||
case MODE_1: /* 通风模式 */
|
||||
// TODO: 处理 通风模式
|
||||
break;
|
||||
case MODE_2: /* 强力模式 */
|
||||
// TODO: 处理 强力模式
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
app_arbiter_manual_intervene(); /* 手动动作:条件/定时让位 */
|
||||
app_mode_set_manual(ctrl->mode);
|
||||
break;
|
||||
|
||||
/* ── DP2 风速挡位 (enum) ── */
|
||||
/* ── DP2 风速挡位:写则手动抢权,退模式 ── */
|
||||
case DP_ID_FAN_GEAR:
|
||||
gDevData.fan_gear = ctrl->fan_gear;
|
||||
switch (ctrl->fan_gear)
|
||||
{
|
||||
case FAN_GEAR_0: /* 关闭风扇 */
|
||||
// TODO: 处理 关闭风扇
|
||||
break;
|
||||
case FAN_GEAR_1: /* 1档风速 */
|
||||
// TODO: 处理 1档风速
|
||||
break;
|
||||
case FAN_GEAR_2: /* 2档风速 */
|
||||
// TODO: 处理 2档风速
|
||||
break;
|
||||
case FAN_GEAR_3: /* 3档风速 */
|
||||
// TODO: 处理 3档风速
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
app_arbiter_manual_intervene(); /* 手动动作:条件/定时让位 */
|
||||
app_mode_set_manual(0);
|
||||
app_fan_set_gear(ctrl->fan_gear);
|
||||
break;
|
||||
|
||||
/* ── DP5 条件使能 (bool) ── */
|
||||
/* ── DP5 条件使能 ── */
|
||||
case DP_ID_COND_EN:
|
||||
if (ctrl->cond_en)
|
||||
{
|
||||
gDevData.cond_en = 1;
|
||||
// TODO: 开 — 执行动作
|
||||
}
|
||||
else
|
||||
{
|
||||
gDevData.cond_en = 0;
|
||||
// TODO: 关 — 执行动作
|
||||
}
|
||||
gDevData.cond_en = ctrl->cond_en ? 1 : 0;
|
||||
app_cond_set_en(gDevData.cond_en);
|
||||
break;
|
||||
|
||||
/* ── DP6 条件配置1 (raw) ── */
|
||||
/* ── DP6~12 条件配置(7 字节 memcpy + 条件状态机复位该组计时)── */
|
||||
case DP_ID_COND_CONFIG1:
|
||||
memcpy(gDevData.cond_config1, ctrl->cond_config1, 6);
|
||||
// TODO: 根据 gDevData.cond_config1 执行动作 (长度 6 字节)
|
||||
memcpy(gDevData.cond_config1, ctrl->cond_config1, 7);
|
||||
app_cond_set_config(1);
|
||||
break;
|
||||
|
||||
/* ── DP7 条件配置2 (raw) ── */
|
||||
case DP_ID_COND_CONFIG2:
|
||||
memcpy(gDevData.cond_config2, ctrl->cond_config2, 6);
|
||||
// TODO: 根据 gDevData.cond_config2 执行动作 (长度 6 字节)
|
||||
memcpy(gDevData.cond_config2, ctrl->cond_config2, 7);
|
||||
app_cond_set_config(2);
|
||||
break;
|
||||
|
||||
/* ── DP8 条件配置3 (raw) ── */
|
||||
case DP_ID_COND_CONFIG3:
|
||||
memcpy(gDevData.cond_config3, ctrl->cond_config3, 6);
|
||||
// TODO: 根据 gDevData.cond_config3 执行动作 (长度 6 字节)
|
||||
memcpy(gDevData.cond_config3, ctrl->cond_config3, 7);
|
||||
app_cond_set_config(3);
|
||||
break;
|
||||
|
||||
/* ── DP9 条件配置4 (raw) ── */
|
||||
case DP_ID_COND_CONFIG4:
|
||||
memcpy(gDevData.cond_config4, ctrl->cond_config4, 6);
|
||||
// TODO: 根据 gDevData.cond_config4 执行动作 (长度 6 字节)
|
||||
memcpy(gDevData.cond_config4, ctrl->cond_config4, 7);
|
||||
app_cond_set_config(4);
|
||||
break;
|
||||
|
||||
/* ── DP10 条件配置5 (raw) ── */
|
||||
case DP_ID_COND_CONFIG5:
|
||||
memcpy(gDevData.cond_config5, ctrl->cond_config5, 6);
|
||||
// TODO: 根据 gDevData.cond_config5 执行动作 (长度 6 字节)
|
||||
memcpy(gDevData.cond_config5, ctrl->cond_config5, 7);
|
||||
app_cond_set_config(5);
|
||||
break;
|
||||
|
||||
/* ── DP11 条件配置6 (raw) ── */
|
||||
case DP_ID_COND_CONFIG6:
|
||||
memcpy(gDevData.cond_config6, ctrl->cond_config6, 6);
|
||||
// TODO: 根据 gDevData.cond_config6 执行动作 (长度 6 字节)
|
||||
memcpy(gDevData.cond_config6, ctrl->cond_config6, 7);
|
||||
app_cond_set_config(6);
|
||||
break;
|
||||
|
||||
/* ── DP12 条件配置7 (raw) ── */
|
||||
case DP_ID_COND_CONFIG7:
|
||||
memcpy(gDevData.cond_config7, ctrl->cond_config7, 6);
|
||||
// TODO: 根据 gDevData.cond_config7 执行动作 (长度 6 字节)
|
||||
memcpy(gDevData.cond_config7, ctrl->cond_config7, 7);
|
||||
app_cond_set_config(7);
|
||||
break;
|
||||
|
||||
/* ── DP13 定时使能 (bool) ── */
|
||||
/* ── DP13 定时使能 ── */
|
||||
case DP_ID_TIME_EN:
|
||||
if (ctrl->time_en)
|
||||
{
|
||||
gDevData.time_en = 1;
|
||||
// TODO: 开 — 执行动作
|
||||
}
|
||||
else
|
||||
{
|
||||
gDevData.time_en = 0;
|
||||
// TODO: 关 — 执行动作
|
||||
}
|
||||
gDevData.time_en = ctrl->time_en ? 1 : 0;
|
||||
app_timer_set_en(gDevData.time_en);
|
||||
break;
|
||||
|
||||
/* ── DP14 定时配置1 (raw) ── */
|
||||
/* ── DP14~20 定时配置(8 字节)── */
|
||||
case DP_ID_TIME_CONFIG1:
|
||||
memcpy(gDevData.time_config1, ctrl->time_config1, 8);
|
||||
// TODO: 根据 gDevData.time_config1 执行动作 (长度 8 字节)
|
||||
app_timer_set_config(1);
|
||||
break;
|
||||
|
||||
/* ── DP15 定时配置2 (raw) ── */
|
||||
case DP_ID_TIME_CONFIG2:
|
||||
memcpy(gDevData.time_config2, ctrl->time_config2, 8);
|
||||
// TODO: 根据 gDevData.time_config2 执行动作 (长度 8 字节)
|
||||
app_timer_set_config(2);
|
||||
break;
|
||||
|
||||
/* ── DP16 定时配置3 (raw) ── */
|
||||
case DP_ID_TIME_CONFIG3:
|
||||
memcpy(gDevData.time_config3, ctrl->time_config3, 8);
|
||||
// TODO: 根据 gDevData.time_config3 执行动作 (长度 8 字节)
|
||||
app_timer_set_config(3);
|
||||
break;
|
||||
|
||||
/* ── DP17 定时配置4 (raw) ── */
|
||||
case DP_ID_TIME_CONFIG4:
|
||||
memcpy(gDevData.time_config4, ctrl->time_config4, 8);
|
||||
// TODO: 根据 gDevData.time_config4 执行动作 (长度 8 字节)
|
||||
app_timer_set_config(4);
|
||||
break;
|
||||
|
||||
/* ── DP18 定时配置5 (raw) ── */
|
||||
case DP_ID_TIME_CONFIG5:
|
||||
memcpy(gDevData.time_config5, ctrl->time_config5, 8);
|
||||
// TODO: 根据 gDevData.time_config5 执行动作 (长度 8 字节)
|
||||
app_timer_set_config(5);
|
||||
break;
|
||||
|
||||
/* ── DP19 定时配置6 (raw) ── */
|
||||
case DP_ID_TIME_CONFIG6:
|
||||
memcpy(gDevData.time_config6, ctrl->time_config6, 8);
|
||||
// TODO: 根据 gDevData.time_config6 执行动作 (长度 8 字节)
|
||||
app_timer_set_config(6);
|
||||
break;
|
||||
|
||||
/* ── DP20 定时配置7 (raw) ── */
|
||||
case DP_ID_TIME_CONFIG7:
|
||||
memcpy(gDevData.time_config7, ctrl->time_config7, 8);
|
||||
// TODO: 根据 gDevData.time_config7 执行动作 (长度 8 字节)
|
||||
app_timer_set_config(7);
|
||||
break;
|
||||
|
||||
/* ── DP21 设备故障使能 (uint8) ── */
|
||||
/* ── DP21/23 使能:原样存,P0 不做位级过滤 ── */
|
||||
case DP_ID_FAULT_EN:
|
||||
gDevData.fault_en = ctrl->fault_en;
|
||||
// TODO: 根据 ctrl->fault_en 执行动作
|
||||
break;
|
||||
|
||||
/* ── DP23 设备告警使能 (uint8) ── */
|
||||
case DP_ID_ALARM_EN:
|
||||
gDevData.alarm_en = ctrl->alarm_en;
|
||||
// TODO: 根据 ctrl->alarm_en 执行动作
|
||||
break;
|
||||
|
||||
/* ── DP25 恢复出厂设置 (bool) ── */
|
||||
/* ── DP25 恢复出厂设置:清业务 VM(留配对)+ RAM 复位,不断开不软复位(14 章)── */
|
||||
case DP_ID_FACTORY_RESET:
|
||||
if (ctrl->factory_reset)
|
||||
{
|
||||
gDevData.factory_reset = 1;
|
||||
// TODO: 开 — 执行动作
|
||||
}
|
||||
else
|
||||
{
|
||||
gDevData.factory_reset = 0;
|
||||
// TODO: 关 — 执行动作
|
||||
gDevData.factory_reset = ctrl->factory_reset ? 1 : 0;
|
||||
if (gDevData.factory_reset) {
|
||||
app_function_factory_reset();
|
||||
}
|
||||
break;
|
||||
|
||||
/* ── DP26 预留DP1 (uint32) ── */
|
||||
/* ── DP26~28 预留:存 RAM,可不实现动作 ── */
|
||||
case DP_ID_RESERVA_DP1:
|
||||
gDevData.reserva_dp1 = ctrl->reserva_dp1;
|
||||
// TODO: 根据 ctrl->reserva_dp1 执行动作
|
||||
break;
|
||||
|
||||
/* ── DP27 预留DP2 (uint32) ── */
|
||||
case DP_ID_RESERVA_DP2:
|
||||
gDevData.reserva_dp2 = ctrl->reserva_dp2;
|
||||
// TODO: 根据 ctrl->reserva_dp2 执行动作
|
||||
break;
|
||||
|
||||
/* ── DP28 预留DP3 (uint32) ── */
|
||||
case DP_ID_RESERVA_DP3:
|
||||
gDevData.reserva_dp3 = ctrl->reserva_dp3;
|
||||
// TODO: 根据 ctrl->reserva_dp3 执行动作
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -337,13 +230,12 @@ int8_t jbEventProcess(jb_event_info_t *info, jb_data_point_t *ctrl)
|
||||
* 返回 0=接受升级(协议层应答 JB_OK + MaxDataLen)
|
||||
* 非 0=拒绝,返回值即错误码(JB_OTA_*)
|
||||
*
|
||||
* ⚠ SOC 侧 OTA 尚未实现:当前仅应答,不执行任何升级动作。 */
|
||||
* ⚠ SOC 侧 OTA 尚未实现(规格书第 15 章:P2 可继续 stub,禁止假装升级
|
||||
* 成功后擦用户区):当前仅应答,不执行任何升级动作。 */
|
||||
int8_t jbOnOtaNotify(uint32_t fwVersion, uint32_t fwLength, uint32_t fwCrc32)
|
||||
{
|
||||
(void)fwVersion; (void)fwLength; (void)fwCrc32;
|
||||
// TODO: 检查 Flash 空间与版本,决定是否接收升级
|
||||
// 允许: 返回 JB_OK(协议层应答 JB_OK + MaxDataLen)
|
||||
// 拒绝: 返回对应 JB_OTA_* 错误码(如 JB_OTA_VER_LOW / JB_OTA_SPACE_ERR)
|
||||
/* TODO(P2): 检查 Flash 空间与版本(拒绝降级/同版本),决定是否接收升级 */
|
||||
return JB_OK;
|
||||
}
|
||||
|
||||
@@ -352,35 +244,32 @@ void jbOnOtaData(const uint8_t *data, uint16_t len,
|
||||
uint16_t shardIdx, uint16_t totalShards)
|
||||
{
|
||||
(void)data; (void)len; (void)shardIdx; (void)totalShards;
|
||||
// TODO: 写入 Flash,累计 CRC32
|
||||
// 全部数据接收完成后调用 jbSendOtaResult(JB_OK, mcuVersion)
|
||||
/* TODO(P2): 写入 Flash,累计 CRC32;全部接收后调用 jbSendOtaResult */
|
||||
}
|
||||
|
||||
/* 0x36 / 0x38 停止 OTA */
|
||||
void jbOnOtaStop(void)
|
||||
{
|
||||
// TODO: 终止 OTA,释放资源
|
||||
/* TODO(P2): 终止 OTA,释放资源 */
|
||||
}
|
||||
|
||||
/* 任意 MCU→BLE 请求的 ACK 回调(cmd 标识是哪条请求的应答,result 为结果码)
|
||||
- SOC 仅保留 OTA 主动请求应答: OTA完成 0x35 / 停止 0x39
|
||||
- 可在本回调中设置"XX 完成/失败"标志,供主循环判断"确认成功才继续" */
|
||||
/* 任意 MCU→BLE 请求的 ACK 回调 */
|
||||
void jbOnAck(uint8_t cmd, uint8_t result)
|
||||
{
|
||||
(void)cmd;
|
||||
(void)result;
|
||||
// TODO: switch(cmd){ case CMD_OTA_COMPLETE_ACK: gOtaDone = (result==JB_OK); break; case CMD_OTA_STOP_MCU_ACK: ... }
|
||||
/* TODO(P2): switch(cmd){ case CMD_OTA_COMPLETE_ACK: ... } */
|
||||
}
|
||||
|
||||
/* ════════════════════════════════════════════════════════════════
|
||||
* 硬件抽象(按实际平台实现)
|
||||
* ════════════════════════════════════════════════════════════════ */
|
||||
|
||||
/* ── 定时器 ── */
|
||||
/* ── 定时器:由 usr_jb_main 的 20ms 节拍按 1ms 步进调用 jbTimerIrq() ── */
|
||||
void jbTimerIrq(void)
|
||||
{
|
||||
timerMs++;
|
||||
} /* 在 1ms 中断中调用本函数 */
|
||||
}
|
||||
|
||||
uint32_t jbGetTimerCount(void)
|
||||
{
|
||||
|
||||
@@ -194,54 +194,54 @@ static uint16_t jbPackDps(const jb_data_point_t *dp,
|
||||
bitmap[DP_ID_COND_EN / 8] |= (1 << (DP_ID_COND_EN % 8));
|
||||
out[valIdx++] = dp->cond_en ? 1 : 0;
|
||||
}
|
||||
/* DP6 条件配置1 (raw, 6B, RW) */
|
||||
/* DP6 条件配置1 (raw, 7B, RW) */
|
||||
if (mask[DP_ID_COND_CONFIG1 / 8] & (1 << (DP_ID_COND_CONFIG1 % 8)))
|
||||
{
|
||||
bitmap[DP_ID_COND_CONFIG1 / 8] |= (1 << (DP_ID_COND_CONFIG1 % 8));
|
||||
memcpy(&out[valIdx], dp->cond_config1, 6);
|
||||
valIdx += 6;
|
||||
memcpy(&out[valIdx], dp->cond_config1, 7);
|
||||
valIdx += 7;
|
||||
}
|
||||
/* DP7 条件配置2 (raw, 6B, RW) */
|
||||
/* DP7 条件配置2 (raw, 7B, RW) */
|
||||
if (mask[DP_ID_COND_CONFIG2 / 8] & (1 << (DP_ID_COND_CONFIG2 % 8)))
|
||||
{
|
||||
bitmap[DP_ID_COND_CONFIG2 / 8] |= (1 << (DP_ID_COND_CONFIG2 % 8));
|
||||
memcpy(&out[valIdx], dp->cond_config2, 6);
|
||||
valIdx += 6;
|
||||
memcpy(&out[valIdx], dp->cond_config2, 7);
|
||||
valIdx += 7;
|
||||
}
|
||||
/* DP8 条件配置3 (raw, 6B, RW) */
|
||||
/* DP8 条件配置3 (raw, 7B, RW) */
|
||||
if (mask[DP_ID_COND_CONFIG3 / 8] & (1 << (DP_ID_COND_CONFIG3 % 8)))
|
||||
{
|
||||
bitmap[DP_ID_COND_CONFIG3 / 8] |= (1 << (DP_ID_COND_CONFIG3 % 8));
|
||||
memcpy(&out[valIdx], dp->cond_config3, 6);
|
||||
valIdx += 6;
|
||||
memcpy(&out[valIdx], dp->cond_config3, 7);
|
||||
valIdx += 7;
|
||||
}
|
||||
/* DP9 条件配置4 (raw, 6B, RW) */
|
||||
/* DP9 条件配置4 (raw, 7B, RW) */
|
||||
if (mask[DP_ID_COND_CONFIG4 / 8] & (1 << (DP_ID_COND_CONFIG4 % 8)))
|
||||
{
|
||||
bitmap[DP_ID_COND_CONFIG4 / 8] |= (1 << (DP_ID_COND_CONFIG4 % 8));
|
||||
memcpy(&out[valIdx], dp->cond_config4, 6);
|
||||
valIdx += 6;
|
||||
memcpy(&out[valIdx], dp->cond_config4, 7);
|
||||
valIdx += 7;
|
||||
}
|
||||
/* DP10 条件配置5 (raw, 6B, RW) */
|
||||
/* DP10 条件配置5 (raw, 7B, RW) */
|
||||
if (mask[DP_ID_COND_CONFIG5 / 8] & (1 << (DP_ID_COND_CONFIG5 % 8)))
|
||||
{
|
||||
bitmap[DP_ID_COND_CONFIG5 / 8] |= (1 << (DP_ID_COND_CONFIG5 % 8));
|
||||
memcpy(&out[valIdx], dp->cond_config5, 6);
|
||||
valIdx += 6;
|
||||
memcpy(&out[valIdx], dp->cond_config5, 7);
|
||||
valIdx += 7;
|
||||
}
|
||||
/* DP11 条件配置6 (raw, 6B, RW) */
|
||||
/* DP11 条件配置6 (raw, 7B, RW) */
|
||||
if (mask[DP_ID_COND_CONFIG6 / 8] & (1 << (DP_ID_COND_CONFIG6 % 8)))
|
||||
{
|
||||
bitmap[DP_ID_COND_CONFIG6 / 8] |= (1 << (DP_ID_COND_CONFIG6 % 8));
|
||||
memcpy(&out[valIdx], dp->cond_config6, 6);
|
||||
valIdx += 6;
|
||||
memcpy(&out[valIdx], dp->cond_config6, 7);
|
||||
valIdx += 7;
|
||||
}
|
||||
/* DP12 条件配置7 (raw, 6B, RW) */
|
||||
/* DP12 条件配置7 (raw, 7B, RW) */
|
||||
if (mask[DP_ID_COND_CONFIG7 / 8] & (1 << (DP_ID_COND_CONFIG7 % 8)))
|
||||
{
|
||||
bitmap[DP_ID_COND_CONFIG7 / 8] |= (1 << (DP_ID_COND_CONFIG7 % 8));
|
||||
memcpy(&out[valIdx], dp->cond_config7, 6);
|
||||
valIdx += 6;
|
||||
memcpy(&out[valIdx], dp->cond_config7, 7);
|
||||
valIdx += 7;
|
||||
}
|
||||
/* DP13 定时使能 (bool, 1B, RW) */
|
||||
if (mask[DP_ID_TIME_EN / 8] & (1 << (DP_ID_TIME_EN % 8)))
|
||||
@@ -409,91 +409,91 @@ static void jbUnpackDps(const uint8_t *bitmap, const uint8_t *values,
|
||||
info->dp_ids[info->count++] = DP_ID_COND_EN;
|
||||
}
|
||||
}
|
||||
/* DP6 条件配置1 (raw, 6B, RW) */
|
||||
/* DP6 条件配置1 (raw, 7B, RW) */
|
||||
if (bitmap[DP_ID_COND_CONFIG1 / 8] & (1 << (DP_ID_COND_CONFIG1 % 8)))
|
||||
{
|
||||
if (valIdx + 6 <= valLen)
|
||||
if (valIdx + 7 <= valLen)
|
||||
{
|
||||
memcpy(dp->cond_config1, &values[valIdx], 6);
|
||||
valIdx += 6;
|
||||
memcpy(dp->cond_config1, &values[valIdx], 7);
|
||||
valIdx += 7;
|
||||
}
|
||||
if (info && info->count < DP_ID_MAX)
|
||||
{
|
||||
info->dp_ids[info->count++] = DP_ID_COND_CONFIG1;
|
||||
}
|
||||
}
|
||||
/* DP7 条件配置2 (raw, 6B, RW) */
|
||||
/* DP7 条件配置2 (raw, 7B, RW) */
|
||||
if (bitmap[DP_ID_COND_CONFIG2 / 8] & (1 << (DP_ID_COND_CONFIG2 % 8)))
|
||||
{
|
||||
if (valIdx + 6 <= valLen)
|
||||
if (valIdx + 7 <= valLen)
|
||||
{
|
||||
memcpy(dp->cond_config2, &values[valIdx], 6);
|
||||
valIdx += 6;
|
||||
memcpy(dp->cond_config2, &values[valIdx], 7);
|
||||
valIdx += 7;
|
||||
}
|
||||
if (info && info->count < DP_ID_MAX)
|
||||
{
|
||||
info->dp_ids[info->count++] = DP_ID_COND_CONFIG2;
|
||||
}
|
||||
}
|
||||
/* DP8 条件配置3 (raw, 6B, RW) */
|
||||
/* DP8 条件配置3 (raw, 7B, RW) */
|
||||
if (bitmap[DP_ID_COND_CONFIG3 / 8] & (1 << (DP_ID_COND_CONFIG3 % 8)))
|
||||
{
|
||||
if (valIdx + 6 <= valLen)
|
||||
if (valIdx + 7 <= valLen)
|
||||
{
|
||||
memcpy(dp->cond_config3, &values[valIdx], 6);
|
||||
valIdx += 6;
|
||||
memcpy(dp->cond_config3, &values[valIdx], 7);
|
||||
valIdx += 7;
|
||||
}
|
||||
if (info && info->count < DP_ID_MAX)
|
||||
{
|
||||
info->dp_ids[info->count++] = DP_ID_COND_CONFIG3;
|
||||
}
|
||||
}
|
||||
/* DP9 条件配置4 (raw, 6B, RW) */
|
||||
/* DP9 条件配置4 (raw, 7B, RW) */
|
||||
if (bitmap[DP_ID_COND_CONFIG4 / 8] & (1 << (DP_ID_COND_CONFIG4 % 8)))
|
||||
{
|
||||
if (valIdx + 6 <= valLen)
|
||||
if (valIdx + 7 <= valLen)
|
||||
{
|
||||
memcpy(dp->cond_config4, &values[valIdx], 6);
|
||||
valIdx += 6;
|
||||
memcpy(dp->cond_config4, &values[valIdx], 7);
|
||||
valIdx += 7;
|
||||
}
|
||||
if (info && info->count < DP_ID_MAX)
|
||||
{
|
||||
info->dp_ids[info->count++] = DP_ID_COND_CONFIG4;
|
||||
}
|
||||
}
|
||||
/* DP10 条件配置5 (raw, 6B, RW) */
|
||||
/* DP10 条件配置5 (raw, 7B, RW) */
|
||||
if (bitmap[DP_ID_COND_CONFIG5 / 8] & (1 << (DP_ID_COND_CONFIG5 % 8)))
|
||||
{
|
||||
if (valIdx + 6 <= valLen)
|
||||
if (valIdx + 7 <= valLen)
|
||||
{
|
||||
memcpy(dp->cond_config5, &values[valIdx], 6);
|
||||
valIdx += 6;
|
||||
memcpy(dp->cond_config5, &values[valIdx], 7);
|
||||
valIdx += 7;
|
||||
}
|
||||
if (info && info->count < DP_ID_MAX)
|
||||
{
|
||||
info->dp_ids[info->count++] = DP_ID_COND_CONFIG5;
|
||||
}
|
||||
}
|
||||
/* DP11 条件配置6 (raw, 6B, RW) */
|
||||
/* DP11 条件配置6 (raw, 7B, RW) */
|
||||
if (bitmap[DP_ID_COND_CONFIG6 / 8] & (1 << (DP_ID_COND_CONFIG6 % 8)))
|
||||
{
|
||||
if (valIdx + 6 <= valLen)
|
||||
if (valIdx + 7 <= valLen)
|
||||
{
|
||||
memcpy(dp->cond_config6, &values[valIdx], 6);
|
||||
valIdx += 6;
|
||||
memcpy(dp->cond_config6, &values[valIdx], 7);
|
||||
valIdx += 7;
|
||||
}
|
||||
if (info && info->count < DP_ID_MAX)
|
||||
{
|
||||
info->dp_ids[info->count++] = DP_ID_COND_CONFIG6;
|
||||
}
|
||||
}
|
||||
/* DP12 条件配置7 (raw, 6B, RW) */
|
||||
/* DP12 条件配置7 (raw, 7B, RW) */
|
||||
if (bitmap[DP_ID_COND_CONFIG7 / 8] & (1 << (DP_ID_COND_CONFIG7 % 8)))
|
||||
{
|
||||
if (valIdx + 6 <= valLen)
|
||||
if (valIdx + 7 <= valLen)
|
||||
{
|
||||
memcpy(dp->cond_config7, &values[valIdx], 6);
|
||||
valIdx += 6;
|
||||
memcpy(dp->cond_config7, &values[valIdx], 7);
|
||||
valIdx += 7;
|
||||
}
|
||||
if (info && info->count < DP_ID_MAX)
|
||||
{
|
||||
@@ -728,43 +728,43 @@ static uint8_t jbBuildDiffMask(const jb_data_point_t *last,
|
||||
hasChange = 1;
|
||||
}
|
||||
/* DP6 条件配置1 */
|
||||
if (memcmp(last->cond_config1, cur->cond_config1, 6))
|
||||
if (memcmp(last->cond_config1, cur->cond_config1, 7))
|
||||
{
|
||||
mask[DP_ID_COND_CONFIG1 / 8] |= (1 << (DP_ID_COND_CONFIG1 % 8));
|
||||
hasChange = 1;
|
||||
}
|
||||
/* DP7 条件配置2 */
|
||||
if (memcmp(last->cond_config2, cur->cond_config2, 6))
|
||||
if (memcmp(last->cond_config2, cur->cond_config2, 7))
|
||||
{
|
||||
mask[DP_ID_COND_CONFIG2 / 8] |= (1 << (DP_ID_COND_CONFIG2 % 8));
|
||||
hasChange = 1;
|
||||
}
|
||||
/* DP8 条件配置3 */
|
||||
if (memcmp(last->cond_config3, cur->cond_config3, 6))
|
||||
if (memcmp(last->cond_config3, cur->cond_config3, 7))
|
||||
{
|
||||
mask[DP_ID_COND_CONFIG3 / 8] |= (1 << (DP_ID_COND_CONFIG3 % 8));
|
||||
hasChange = 1;
|
||||
}
|
||||
/* DP9 条件配置4 */
|
||||
if (memcmp(last->cond_config4, cur->cond_config4, 6))
|
||||
if (memcmp(last->cond_config4, cur->cond_config4, 7))
|
||||
{
|
||||
mask[DP_ID_COND_CONFIG4 / 8] |= (1 << (DP_ID_COND_CONFIG4 % 8));
|
||||
hasChange = 1;
|
||||
}
|
||||
/* DP10 条件配置5 */
|
||||
if (memcmp(last->cond_config5, cur->cond_config5, 6))
|
||||
if (memcmp(last->cond_config5, cur->cond_config5, 7))
|
||||
{
|
||||
mask[DP_ID_COND_CONFIG5 / 8] |= (1 << (DP_ID_COND_CONFIG5 % 8));
|
||||
hasChange = 1;
|
||||
}
|
||||
/* DP11 条件配置6 */
|
||||
if (memcmp(last->cond_config6, cur->cond_config6, 6))
|
||||
if (memcmp(last->cond_config6, cur->cond_config6, 7))
|
||||
{
|
||||
mask[DP_ID_COND_CONFIG6 / 8] |= (1 << (DP_ID_COND_CONFIG6 % 8));
|
||||
hasChange = 1;
|
||||
}
|
||||
/* DP12 条件配置7 */
|
||||
if (memcmp(last->cond_config7, cur->cond_config7, 6))
|
||||
if (memcmp(last->cond_config7, cur->cond_config7, 7))
|
||||
{
|
||||
mask[DP_ID_COND_CONFIG7 / 8] |= (1 << (DP_ID_COND_CONFIG7 % 8));
|
||||
hasChange = 1;
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
* ════════════════════════════════════════════════════════════════ */
|
||||
#define PKT_HEAD_0 0x55
|
||||
#define PKT_HEAD_1 0xAA
|
||||
#define MAX_PACKAGE_LEN 128
|
||||
#define MAX_PACKAGE_LEN 256
|
||||
#define JB_OTA_MAX_PAYLOAD 96 /* OTA 单包 Payload 上限(Byte); 须 <= MAX_PACKAGE_LEN - 11(0x32 帧开销) */
|
||||
|
||||
#define DP_COUNT 29
|
||||
@@ -157,13 +157,13 @@ typedef enum
|
||||
#define DP_LEN_CURRENT_TEMP 2
|
||||
#define DP_LEN_CURRENT_HUMI 1
|
||||
#define DP_LEN_COND_EN 1
|
||||
#define DP_LEN_COND_CONFIG1 6
|
||||
#define DP_LEN_COND_CONFIG2 6
|
||||
#define DP_LEN_COND_CONFIG3 6
|
||||
#define DP_LEN_COND_CONFIG4 6
|
||||
#define DP_LEN_COND_CONFIG5 6
|
||||
#define DP_LEN_COND_CONFIG6 6
|
||||
#define DP_LEN_COND_CONFIG7 6
|
||||
#define DP_LEN_COND_CONFIG1 7
|
||||
#define DP_LEN_COND_CONFIG2 7
|
||||
#define DP_LEN_COND_CONFIG3 7
|
||||
#define DP_LEN_COND_CONFIG4 7
|
||||
#define DP_LEN_COND_CONFIG5 7
|
||||
#define DP_LEN_COND_CONFIG6 7
|
||||
#define DP_LEN_COND_CONFIG7 7
|
||||
#define DP_LEN_TIME_EN 1
|
||||
#define DP_LEN_TIME_CONFIG1 8
|
||||
#define DP_LEN_TIME_CONFIG2 8
|
||||
@@ -214,13 +214,13 @@ typedef struct
|
||||
float current_temp; /* DP3 当前温度 (float, scale=0.1) */
|
||||
uint8_t current_humi; /* DP4 当前湿度 */
|
||||
uint8_t cond_en; /* DP5 条件使能 */
|
||||
uint8_t cond_config1[6];/* DP6 条件配置1 */
|
||||
uint8_t cond_config2[6];/* DP7 条件配置2 */
|
||||
uint8_t cond_config3[6];/* DP8 条件配置3 */
|
||||
uint8_t cond_config4[6];/* DP9 条件配置4 */
|
||||
uint8_t cond_config5[6];/* DP10 条件配置5 */
|
||||
uint8_t cond_config6[6];/* DP11 条件配置6 */
|
||||
uint8_t cond_config7[6];/* DP12 条件配置7 */
|
||||
uint8_t cond_config1[7];/* DP6 条件配置1 (7B: en/tempH/tempL/humi/dur/action/cond) */
|
||||
uint8_t cond_config2[7];/* DP7 条件配置2 */
|
||||
uint8_t cond_config3[7];/* DP8 条件配置3 */
|
||||
uint8_t cond_config4[7];/* DP9 条件配置4 */
|
||||
uint8_t cond_config5[7];/* DP10 条件配置5 */
|
||||
uint8_t cond_config6[7];/* DP11 条件配置6 */
|
||||
uint8_t cond_config7[7];/* DP12 条件配置7 */
|
||||
uint8_t time_en; /* DP13 定时使能 */
|
||||
uint8_t time_config1[8];/* DP14 定时配置1 */
|
||||
uint8_t time_config2[8];/* DP15 定时配置2 */
|
||||
|
||||
+247
-15
@@ -1,18 +1,59 @@
|
||||
/******************************************************************************
|
||||
* @file usr_jb_main.c
|
||||
* @brief CBR3 业务组合根:只做"先后调用谁、谁通知谁",不含具体业务算法
|
||||
* @author cyWu <1917507415@qq.com>
|
||||
* @date 2026.08.29
|
||||
* @version V1.0.0
|
||||
* @history
|
||||
* - V1.0.0, 2026.08.29, cyWu, 首次发布(移植门夹:删电池/USB,加风扇/显示/传感)
|
||||
*
|
||||
* @note 协议生成文件 jb_product.c 不要填业务。APP 下行写 gDevData。
|
||||
* usr_timer_loop 由 BLE 回调,勿再注册第二份。
|
||||
* 具体业务逻辑去对应模块找:
|
||||
* app_boot —— 开机 3s/5s/12s 按键判定
|
||||
* app_power —— 软关机请求(无电池/充电)
|
||||
* app_pair —— 配对会话计时
|
||||
* app_fan —— 风扇档位
|
||||
* app_th —— AHT20 采样
|
||||
* app_disp —— 数码管显示
|
||||
* app_function —— 按键动作 + DP 同步
|
||||
******************************************************************************/
|
||||
|
||||
#include "system/includes.h"
|
||||
#include "app_config.h"
|
||||
#include "app_main.h"
|
||||
#include "app_task.h"
|
||||
#include "user_cfg.h"
|
||||
#include "vm.h"
|
||||
#include "sys_time.h"
|
||||
|
||||
#include "jb_protocol.h"
|
||||
#include "jb_product.h"
|
||||
#include "jb_ringbuffer.h"
|
||||
#include "jb_common.h"
|
||||
#include "usr_le_product.h"
|
||||
#include "usr_le_api.h"
|
||||
#include "bsp_hw.h"
|
||||
#include "app_power.h"
|
||||
#include "app_pair.h"
|
||||
#include "app_boot.h"
|
||||
#include "app_function.h"
|
||||
#include "app_fan.h"
|
||||
#include "app_th.h"
|
||||
#include "app_disp.h"
|
||||
#include "app_mode.h"
|
||||
#include "app_cond.h"
|
||||
#include "app_timer_task.h"
|
||||
#include "app_arbiter.h"
|
||||
#include "app_nvm.h"
|
||||
#include "app_led.h"
|
||||
#include "key_manager.h"
|
||||
#include "asm/power/power_api.h"
|
||||
|
||||
/* 向 usr_le_code 库提供产品身份(换产品只改 jb_protocol.h 宏即可) */
|
||||
#define LOG_TAG_CONST APP
|
||||
#define LOG_TAG "[USR_JB]"
|
||||
#define LOG_INFO_ENABLE
|
||||
#include "debug.h"
|
||||
|
||||
/* 向 usr_le_code 提供产品身份(换产品只改 jb_protocol.h 宏即可) */
|
||||
const uint8_t g_usr_le_prodcode[USR_LE_PRODCODE_LEN] = {
|
||||
JB_CATEGORY_CODE[0], JB_CATEGORY_CODE[1],
|
||||
JB_CATEGORY_CODE[2], JB_CATEGORY_CODE[3]
|
||||
@@ -22,28 +63,219 @@ const uint8_t g_usr_le_pcode[USR_LE_PCODE_LEN] = {
|
||||
JB_PRODUCT_CODE[3], JB_PRODUCT_CODE[4], JB_PRODUCT_CODE[5]
|
||||
};
|
||||
|
||||
void usr_timer_1_ms();
|
||||
static int jb_timer_id=0;
|
||||
#define USR_TIMER_IDLE_MS 20 /**< 全局统一节拍(规格书第 3 章,禁止再开 1ms ISR) */
|
||||
|
||||
void usr_jb_init()
|
||||
/**
|
||||
* @brief 业务节拍档位
|
||||
* @note 全系统只用一种节拍周期(USR_TIMER_IDLE_MS),区别只在"能不能休眠"
|
||||
*/
|
||||
typedef enum {
|
||||
USR_TICK_IDLE = 0, /**< 可休眠 */
|
||||
USR_TICK_BLOCK_SLEEP /**< 禁止休眠(按键按住/风扇转/关机流程/配对/OTA) */
|
||||
} UsrTickMode_t;
|
||||
|
||||
/** 调度器状态 */
|
||||
typedef struct {
|
||||
int timer_id;
|
||||
UsrTickMode_t mode;
|
||||
} UsrSchedulerCtx_t;
|
||||
|
||||
static UsrSchedulerCtx_t s_sched;
|
||||
|
||||
static void usr_timer_tick(void *priv);
|
||||
static void usr_timer_rearm(UsrTickMode_t mode);
|
||||
static void usr_log_boot_version(void);
|
||||
|
||||
/**
|
||||
* @brief 软关机请求钩子:先落盘当前 mode/gear/配置,再停风扇、灯全灭
|
||||
* @note ⚠ app_nvm_save() 必须在 app_function_stop_all() 之前调用:
|
||||
* stop_all 会把 mode/gear 清 0,先存才能保住用户最后的运行状态
|
||||
* (规格书 13 章:软关机前写 VM,开机恢复上次 mode/gear)
|
||||
* @return 无
|
||||
*/
|
||||
static void usr_poweroff_request_hook(void)
|
||||
{
|
||||
app_nvm_save(); /* 软关机前立即写 VM(不等待 500ms 防抖) */
|
||||
app_function_stop_all();
|
||||
app_led_clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 软关机最终钩子:50ms 到点,真正进入深睡
|
||||
* @note 不走 sys_enter_soft_poweroff():那是耳机 SDK 的优雅关机流程,实测慢。
|
||||
* 本产品无 DAC/音频,直接进软关机(规格书 5.1:风扇 0、数码管锁存灭、
|
||||
* LED 灭、BLE 停)。
|
||||
* @return 无
|
||||
*/
|
||||
static void usr_poweroff_finalize_hook(void)
|
||||
{
|
||||
bsp_hw_enter_sleep_io();
|
||||
power_set_soft_poweroff();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 按当前业务状态判断能不能休眠
|
||||
* @note 按键按住、风扇转、关机流程、配对中、OTA 灯效中 → 不能睡
|
||||
* @return 本次应使用的档位
|
||||
*/
|
||||
static UsrTickMode_t usr_timer_pick_mode(void)
|
||||
{
|
||||
if (bsp_key_is_pressed() || app_power_poweroff_pending()
|
||||
|| (app_fan_get_gear() != 0) || app_mode_get_active()
|
||||
|| app_pair_is_active() || app_led_is_ota()) {
|
||||
return USR_TICK_BLOCK_SLEEP;
|
||||
}
|
||||
return USR_TICK_IDLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 切换业务定时器。priority=1 时系统无法 sleep;节拍周期恒为 USR_TIMER_IDLE_MS
|
||||
* @param mode 目标档位
|
||||
* @return 无
|
||||
*/
|
||||
static void usr_timer_rearm(UsrTickMode_t mode)
|
||||
{
|
||||
if (s_sched.timer_id) {
|
||||
usr_timer_del((u16)s_sched.timer_id);
|
||||
s_sched.timer_id = 0;
|
||||
}
|
||||
s_sched.mode = mode;
|
||||
s_sched.timer_id = (int)usr_timer_add(NULL, usr_timer_tick, USR_TIMER_IDLE_MS,
|
||||
(mode == USR_TICK_BLOCK_SLEEP) ? 1 : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 业务定时器:固定 USR_TIMER_IDLE_MS 一拍;忙时禁睡,空闲可睡
|
||||
* @param priv 未使用
|
||||
* @note ⚠ 运行在硬件定时器时基上下文:禁止阻塞等待、禁止耗时操作。
|
||||
* 关机请走 app_power 的钩子,不要在这里直接 power_set_soft_poweroff。
|
||||
* @return 无
|
||||
*/
|
||||
static void usr_timer_tick(void *priv)
|
||||
{
|
||||
uint16_t dt = USR_TIMER_IDLE_MS;
|
||||
uint16_t i;
|
||||
UsrTickMode_t want;
|
||||
|
||||
(void)priv;
|
||||
for (i = 0; i < dt; i++) {
|
||||
jbTimerIrq();
|
||||
}
|
||||
|
||||
if (!app_power_poweroff_pending()) {
|
||||
user_key_scan(dt);
|
||||
user_key_handle(); /* 长按 3s 会在这里同步触发 app_power_request_poweroff() */
|
||||
}
|
||||
|
||||
app_th_poll(dt); /* 内部 2s 才真正采;IO 分离后采集与显示互不干扰 */
|
||||
app_mode_run(dt); /* 通风/强力状态机(20ms 计时精度;条件/定时在 500ms 任务上下文) */
|
||||
app_fan_run(dt); /* P0 无动作 */
|
||||
app_disp_run(dt); /* 刷新显示(告警闪烁) */
|
||||
app_function_poll(); /* 同步 DP */
|
||||
app_nvm_tick(dt); /* 档位/模式/配置变化检测 + 500ms 防抖计时(不写 flash!) */
|
||||
|
||||
/* OTA 灯效期间最高优先级;关机流程中灯灭 */
|
||||
if (app_led_is_ota()) {
|
||||
app_led_run(dt, app_function_is_power_on());
|
||||
} else if (app_power_poweroff_pending()) {
|
||||
app_led_clear();
|
||||
} else {
|
||||
app_led_run(dt, app_function_is_power_on());
|
||||
}
|
||||
|
||||
want = usr_timer_pick_mode(); /* 判断能不能休眠 */
|
||||
if (want != s_sched.mode) {
|
||||
usr_timer_rearm(want);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 上电打印固件版本与编译时间,方便串口对应当前是哪一包
|
||||
* @return 无
|
||||
*/
|
||||
static void usr_log_boot_version(void)
|
||||
{
|
||||
log_info("sw V%d.%d.%d hw V%d.%d.%d build %s %s\n",
|
||||
JB_MCU_SW_VERSION_MAJOR, JB_MCU_SW_VERSION_MINOR, JB_MCU_SW_VERSION_PATCH,
|
||||
JB_MCU_HW_VERSION_MAJOR, JB_MCU_HW_VERSION_MINOR, JB_MCU_HW_VERSION_PATCH,
|
||||
__DATE__, __TIME__);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 业务初始化(在 BLE 栈起来之前由 app_main 调用)
|
||||
* @note 内部会等按键 3s/5s/12s;不足 3s 直接深睡,不会启动业务定时器。
|
||||
* app_led_init() 排在 app_boot_wait_key() 前面:3s/5s 提示灯要用
|
||||
* LED 事件表(规格书 5.2)。boot 深睡分支不返回,后面初始化不会浪费。
|
||||
* @return 无
|
||||
*/
|
||||
void usr_jb_init(void)
|
||||
{
|
||||
usr_log_boot_version(); /* UART 已在 app_main 起来,按键判定前就能看到版本 */
|
||||
|
||||
bsp_hw_init();
|
||||
app_led_init(); /* 提前到按键判定之前:3s 常亮/5s 配对闪都要用灯效事件表 */
|
||||
|
||||
app_boot_wait_key(); /* 深睡分支不会返回;返回即代表确定要开机 */
|
||||
|
||||
jbInit();
|
||||
userInit();
|
||||
if(jb_timer_id==0)jb_timer_id = sys_s_hi_timer_add(NULL, usr_timer_1_ms, TIMER_PERIOD_MS);
|
||||
if (usr_var.usr_goto_updata) {
|
||||
/* 开机 12s 已进 OTA:立刻挂 200ms 闪 */
|
||||
app_led_request_ota();
|
||||
}
|
||||
|
||||
app_fan_init();
|
||||
app_disp_init();
|
||||
/* CT1642 无内部扫描 RAM,4ms 快节拍轮询 4 位。priority=1 会挡住系统 sleep,
|
||||
* USB 常电产品故意如此:管子必须持续扫描,否则肉眼闪。勿改成 priority=0。 */
|
||||
usr_timer_add(NULL, app_disp_scan, BOARD_DISP_SCAN_MS, 1);
|
||||
app_th_init();
|
||||
app_mode_init();
|
||||
app_arbiter_init(); /* 模式仲裁(手动>定时>条件)先于条件/定时引擎 */
|
||||
app_cond_init();
|
||||
app_timer_init();
|
||||
app_nvm_init();
|
||||
app_nvm_load();
|
||||
|
||||
app_power_init();
|
||||
app_power_set_hooks(usr_poweroff_request_hook, usr_poweroff_finalize_hook);
|
||||
app_function_init();
|
||||
|
||||
user_key_init();
|
||||
user_key_lock_if_held();
|
||||
app_function_power_on_silent();
|
||||
|
||||
usr_timer_rearm(USR_TICK_IDLE);
|
||||
log_info("timer idle %ums, sleep ok\n", (unsigned)USR_TIMER_IDLE_MS);
|
||||
}
|
||||
|
||||
void usr_timer_1_ms()
|
||||
/**
|
||||
* @brief BLE 收到见宝载荷,送入协议环形缓冲
|
||||
* @param data 载荷指针
|
||||
* @param len 载荷长度,单位字节
|
||||
* @return 无
|
||||
*/
|
||||
void usr_jb_le_recieve_data(u8 *data, u16 len)
|
||||
{
|
||||
jbTimerIrq();
|
||||
jbPutData(data, len);
|
||||
}
|
||||
|
||||
void usr_jb_le_recieve_data(u8* data,u16 len)
|
||||
{
|
||||
jbPutData(data,len);
|
||||
}
|
||||
|
||||
void usr_timer_loop()///500ms
|
||||
/**
|
||||
* @brief 约 500ms:协议收包与 DP 上报 + 自动化引擎(由 usr_le_code 回调)
|
||||
* @note userHandle 只负责填温湿度 DP(规格书第 3 章)。
|
||||
* ⚠ 本函数跑在 sys_timer 回调(usr_ble_adv_updata 500ms)= **任务上下文**
|
||||
* (BLE 线程,栈充足),与 20ms 的 usr_timer_tick(usr_timer_add 异步
|
||||
* 接口 = 硬件定时器中断上下文,栈极小)不同。
|
||||
* **所有重量级/自动化引擎必须放这里**:条件/定时引擎(500ms 粒度对
|
||||
* 2s 采样与分钟级时长足够,且单次自毁要写 VM)、VM 落盘、关机收口。
|
||||
* 禁止放进 usr_timer_tick——曾因在 20ms 中断里调 syscfg_write 栈溢出软复位。
|
||||
* @return 无
|
||||
*/
|
||||
void usr_timer_loop(void)
|
||||
{
|
||||
userHandle();
|
||||
jbProtocolHandle(&gDevData);
|
||||
app_cond_run(500); /* 条件引擎(任务上下文;条件/定时仲裁见 app_arbiter) */
|
||||
app_timer_run(500); /* 定时引擎(任务上下文;RTC 门闩 + 窗判断) */
|
||||
app_function_loop(); /* 任务上下文:VM 防抖落盘 + 延后关机收口 */
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user