diff --git a/JBao_JL7016_MOD_FW/AC701N.cbp b/JBao_JL7016_MOD_FW/AC701N.cbp index ce04d70..0cbc6c7 100644 --- a/JBao_JL7016_MOD_FW/AC701N.cbp +++ b/JBao_JL7016_MOD_FW/AC701N.cbp @@ -174,8 +174,7 @@ - - + @@ -758,25 +757,23 @@ - + - - + + + - - + + - - - - - + + + diff --git a/JBao_JL7016_MOD_FW/Makefile b/JBao_JL7016_MOD_FW/Makefile index 6819daf..80160c8 100644 --- a/JBao_JL7016_MOD_FW/Makefile +++ b/JBao_JL7016_MOD_FW/Makefile @@ -248,8 +248,7 @@ INCLUDES := \ -Iapps/usr_le_code \ -Iapps/usr_le_code/thirdpart \ -Iapps/usr_le_code/thirdpart/mjson \ - -Iapps/usr_jb_proto/Protocol \ - -Iapps/usr_jb_proto/Utils \ + -Iapps/usr_mcu_bridge \ -I$(SYS_INC_DIR) \ @@ -614,11 +613,10 @@ c_SRC_FILES := \ cpu/br28/tws_audio.c \ cpu/br28/uart_dev.c \ cpu/br28/umidigi_chargestore.c \ - apps/usr_jb_proto/Protocol/jb_product.c \ - apps/usr_jb_proto/Protocol/jb_protocol.c \ - apps/usr_jb_proto/Utils/jb_common.c \ - apps/usr_jb_proto/Utils/jb_ringbuffer.c \ - apps/usr_jb_proto/usr_jb_main.c \ + apps/usr_mcu_bridge/mcu_bridge.c \ + apps/usr_mcu_bridge/mcu_frame.c \ + apps/usr_mcu_bridge/mcu_uart.c \ + apps/usr_mcu_bridge/mcu_util.c \ apps/usr_le_code/ble_proto.c \ apps/usr_le_code/bleproto.c \ apps/usr_le_code/bleproto_packer.c \ diff --git a/JBao_JL7016_MOD_FW/apps/earphone/app_main.c b/JBao_JL7016_MOD_FW/apps/earphone/app_main.c index c09a1e1..5bbca73 100644 --- a/JBao_JL7016_MOD_FW/apps/earphone/app_main.c +++ b/JBao_JL7016_MOD_FW/apps/earphone/app_main.c @@ -16,6 +16,7 @@ #include "bt_profile_cfg.h" #include "dev_manager/dev_manager.h" #include "update_loader_download.h" +#include "mcu_bridge.h" #ifndef CONFIG_MEDIA_NEW_ENABLE #ifndef CONFIG_MEDIA_DEVELOP_ENABLE @@ -254,7 +255,6 @@ const struct rtc_dev_platform_data rtc_init_data= }; #endif // 0 extern void bt_osc_offset_ext_updata(s32 offset); -extern void usr_jb_init(); void app_main() { int update = 0; @@ -292,7 +292,7 @@ void app_main() } app_var_init(); - usr_jb_init(); + mcu_bridge_init(); #if TCFG_MC_BIAS_AUTO_ADJUST mc_trim_init(update); @@ -325,13 +325,11 @@ void app_main() } /* - * 上电强制进入配对模式: - * 1) usr_goto_pair=1:后续 usr_ble_adv_init() 走绑定广播(encode4bind),不走重连广播 - * 2) usr_clear_pair_info_noreset():清 VM 中三元组,保证 paired_flag=0 - * 注意:须在 BLE 协议栈调用 usr_ble_adv_init() 之前设置;此处不清复位,避免上电死循环复位 + * BLE+MCU:上电不强制配网。 + * - 未配网:等 MCU 0x10 后再绑定广播 + * - 已配网:身份就绪后由 usr_ble_adv_init() 发回连广播 */ - usr_var.usr_goto_pair = 1; - usr_clear_pair_info_noreset(); + usr_var.usr_goto_pair = 0; ui_manage_init(); ui_update_status(STATUS_POWERON); diff --git a/JBao_JL7016_MOD_FW/apps/earphone/board/br28/board_jl701n_demo_cfg.h b/JBao_JL7016_MOD_FW/apps/earphone/board/br28/board_jl701n_demo_cfg.h index 3b89093..61391bc 100644 --- a/JBao_JL7016_MOD_FW/apps/earphone/board/br28/board_jl701n_demo_cfg.h +++ b/JBao_JL7016_MOD_FW/apps/earphone/board/br28/board_jl701n_demo_cfg.h @@ -23,7 +23,7 @@ //*********************************************************************************// #define TCFG_UART0_ENABLE ENABLE_THIS_MOUDLE //串口打印模块使能 #define TCFG_UART0_RX_PORT NO_CONFIG_PORT //串口接收脚配置(用于打印可以选择NO_CONFIG_PORT) -#define TCFG_UART0_TX_PORT IO_PORTC_05 //串口发送脚配置 +#define TCFG_UART0_TX_PORT IO_PORTC_08 //串口发送脚配置 #define TCFG_UART0_BAUDRATE 1000000 //串口波特率配置 //*********************************************************************************// @@ -733,6 +733,7 @@ DAC硬件上的连接方式,可选的配置: //*********************************************************************************// #define TCFG_LOWPOWER_POWER_SEL PWR_LDO15//PWR_LDO15 //电源模式设置,可选DCDC和LDO #define TCFG_LOWPOWER_BTOSC_DISABLE 0 //低功耗模式下BTOSC是否保持 +/* MCU 串口方案:配合 mcu_bridge LP target,忙时保持唤醒、空闲可进低功耗 */ #define TCFG_LOWPOWER_LOWPOWER_SEL SLEEP_EN//DEEP_SLEEP_EN//SLEEP_EN//DEEP_SLEEP_EN //低功耗使能,蓝牙&&系统空闲可进入低功耗 #define TCFG_LOWPOWER_VDDIOM_LEVEL VDDIOM_VOL_28V //vddiom等级 #define TCFG_LOWPOWER_OSC_TYPE OSC_TYPE_LRC //低功耗晶振类型,btosc/lrc diff --git a/JBao_JL7016_MOD_FW/apps/earphone/include/app_main.h b/JBao_JL7016_MOD_FW/apps/earphone/include/app_main.h index c7e3c30..f7998ef 100644 --- a/JBao_JL7016_MOD_FW/apps/earphone/include/app_main.h +++ b/JBao_JL7016_MOD_FW/apps/earphone/include/app_main.h @@ -6,9 +6,21 @@ typedef struct _USR_VAR { u8 usr_plan_cnt ; u8 usr_intterupt_reinit_falg ; u8 usr_goto_updata ; + /** + * 是否处于“主动配网”流程(RAM 运行态,掉电丢失) + * 0:正常态。未配网则不广播;已配网则发回连广播 + * 1:MCU 下发 0x10 / 调用 usr_goto_pair_mode() 后置位,强制发绑定广播 + * 配网成功(AUTHSETUP 写对)或配网超时后应清 0 + */ u8 usr_goto_pair ; u8 usr_can_pair ; u8 usr_ota_succ_flag ; + /** + * 广播数据是否需要刷新到协议栈(一次性触发位) + * 1:usr_ble_adv_updata() 会把 muti_adv_data 写入栈并 enable 广播,然后清 0 + * 0:本周期不更新广播数据 + * 注意:置 1 不等于“一直在广播”,只是请求“应用一次新的广播包” + */ u8 usr_adv_flag ; u8 usr_low_power_flag ; u8 usr_allow_to_sleep_cnt; diff --git a/JBao_JL7016_MOD_FW/apps/earphone/log_config/app_config.c b/JBao_JL7016_MOD_FW/apps/earphone/log_config/app_config.c index a307a06..6686694 100644 --- a/JBao_JL7016_MOD_FW/apps/earphone/log_config/app_config.c +++ b/JBao_JL7016_MOD_FW/apps/earphone/log_config/app_config.c @@ -97,7 +97,7 @@ const char log_tag_const_e_APP_POWER AT(.LOG_TAG_CONST) = TRUE; const char log_tag_const_v_APP AT(.LOG_TAG_CONST) = FALSE; const char log_tag_const_i_APP AT(.LOG_TAG_CONST) = TRUE; -const char log_tag_const_d_APP AT(.LOG_TAG_CONST) = FALSE; +const char log_tag_const_d_APP AT(.LOG_TAG_CONST) = TRUE; /* MCU_BR 等 APP 标签的 log_debug 开关 */ const char log_tag_const_w_APP AT(.LOG_TAG_CONST) = TRUE; const char log_tag_const_e_APP AT(.LOG_TAG_CONST) = TRUE; diff --git a/JBao_JL7016_MOD_FW/apps/usr_jb_proto/Protocol/jb_product.c b/JBao_JL7016_MOD_FW/apps/usr_jb_proto/Protocol/jb_product.c deleted file mode 100644 index 1afb4a6..0000000 --- a/JBao_JL7016_MOD_FW/apps/usr_jb_proto/Protocol/jb_product.c +++ /dev/null @@ -1,249 +0,0 @@ -/* ================================================================ - * jb_product.c — 用户层实现(请填补空白处) - * 设备: 侠客猫激光 | 生成时间: 2026-07-31 17:08:51 - * - * ══ 开发流程 ══ - * 1. 实现硬件抽象: jbGetTimerCount(定时器由 SOC SDK 提供) - * 发送由 SOC SDK 的 ble_send_data 承担,协议层已直接调用,无需 uartWrite - * 2. userInit() 设置 DP 默认值与外设初始化 - * 3. userHandle() 用户数据采集 - * 4. jbEventProcess() 处理 APP 下发的控制命令 - * 5. 主动上报 / OTA 的网关应答在收到 REPORTCMD_V2 后调用 jbReportAckCheck 清除等待 - * 6. main.c / 调用方需周期性调用 jbReportAckCheck(cmd, cnt, int0) - * ================================================================ - */ - -#include "jb_product.h" - -/* ════════════════════════════════════════════════════════════════ - * 全局变量 - * ════════════════════════════════════════════════════════════════ */ - -jb_data_point_t gDevData; -static volatile uint32_t timerMs; - -/* ════════════════════════════════════════════════════════════════ - * userInit — DP 默认值 - * ════════════════════════════════════════════════════════════════ */ -void userInit(void) -{ - memset(&gDevData, 0, sizeof(jb_data_point_t)); - - gDevData.power = 0; /* DP0 开关 0=关 1=开 */ - gDevData.play_mode = 0; /* PLAY_MODE_0 (关) */ - gDevData.hand_mode = 0; /* HAND_MODE_0 (关) */ - gDevData.alm_low_battery = 0; /* DP3 低电量报警 0=关 1=开 */ - gDevData.beep_trig = 0; /* DP4 声音开关 0=关 1=开 */ - gDevData.battery = 0; /* DP5 电量上报 */ - gDevData.shake_wake = 0; /* DP6 震动触发开机 0=关 1=开 */ - memset(gDevData.no_disturb_enable, 0, 13); /* DP7 勿扰模式开关 */ - gDevData.reserva_dp1 = 0; /* DP8 预留DP1 */ - gDevData.reserva_dp2 = 0; /* DP9 预留DP2 */ - gDevData.reserva_dp3 = 0; /* DP10 预留DP3 */ -} - -/* ════════════════════════════════════════════════════════════════ - * userHandle — 用户数据采集 - * 协议层自动检测变化并上报(注意:此处是上报,不是下发控制) - * ════════════════════════════════════════════════════════════════ */ -void userHandle(void) -{ - /* ── 可上报 DP (RO / RW): 用户数据采集 ── */ - // gDevData.power = read_sensor(); /* DP0 开关 */ - // gDevData.play_mode = read_sensor(); /* DP1 自动模式 */ - // gDevData.alm_low_battery = read_sensor(); /* DP3 低电量报警 */ - // gDevData.beep_trig = read_sensor(); /* DP4 声音开关 */ - // gDevData.battery = read_sensor(); /* DP5 电量上报 */ - // gDevData.shake_wake = read_sensor(); /* DP6 震动触发开机 */ - // memcpy(gDevData.no_disturb_enable, sensor_read(), 13); /* DP7 勿扰模式开关 */ - // gDevData.reserva_dp1 = read_sensor(); /* DP8 预留DP1 */ - // gDevData.reserva_dp2 = read_sensor(); /* DP9 预留DP2 */ - // gDevData.reserva_dp3 = read_sensor(); /* DP10 预留DP3 */ -} - -/* ════════════════════════════════════════════════════════════════ - * jbEventProcess — 0x03 APP 控制事件处理(下行 / DP 赋值方向) - * - * 协议层自动解包 0x03 帧后调用本函数。 - * info->dp_ids[] = 被控制的 DP ID 列表 - * ctrl 已填入下发的值;复制到 gDevData 并执行动作 - * ════════════════════════════════════════════════════════════════ */ -int8_t jbEventProcess(jb_event_info_t *info, jb_data_point_t *ctrl) -{ - if (!info || !ctrl) - { - return -1; - } - - for (uint8_t i = 0; i < info->count; i++) - { - switch (info->dp_ids[i]) - { - - /* ── DP0 开关 (bool) ── */ - case DP_ID_POWER: - if (ctrl->power) - { - gDevData.power = 1; - // TODO: 开 — 执行动作 - } - else - { - gDevData.power = 0; - // TODO: 关 — 执行动作 - } - break; - - /* ── DP1 自动模式 (enum) ── */ - case DP_ID_PLAY_MODE: - gDevData.play_mode = ctrl->play_mode; - switch (ctrl->play_mode) - { - case PLAY_MODE_0: /* 关 */ - // TODO: 处理 关 - break; - case PLAY_MODE_1: /* mode1 */ - // TODO: 处理 mode1 - break; - case PLAY_MODE_2: /* mode2 */ - // TODO: 处理 mode2 - break; - case PLAY_MODE_3: /* mode3 */ - // TODO: 处理 mode3 - break; - case PLAY_MODE_4: /* mode4 */ - // TODO: 处理 mode4 - break; - default: - break; - } - break; - - /* ── DP2 手动模式 (enum) ── */ - case DP_ID_HAND_MODE: - gDevData.hand_mode = ctrl->hand_mode; - switch (ctrl->hand_mode) - { - case HAND_MODE_0: /* 关 */ - // TODO: 处理 关 - break; - case HAND_MODE_1: /* 顺时针 */ - // TODO: 处理 顺时针 - break; - case HAND_MODE_2: /* 逆时针 */ - // TODO: 处理 逆时针 - break; - default: - break; - } - break; - - /* ── DP4 声音开关 (bool) ── */ - case DP_ID_BEEP_TRIG: - if (ctrl->beep_trig) - { - gDevData.beep_trig = 1; - // TODO: 开 — 执行动作 - } - else - { - gDevData.beep_trig = 0; - // TODO: 关 — 执行动作 - } - break; - - /* ── DP6 震动触发开机 (bool) ── */ - case DP_ID_SHAKE_WAKE: - if (ctrl->shake_wake) - { - gDevData.shake_wake = 1; - // TODO: 开 — 执行动作 - } - else - { - gDevData.shake_wake = 0; - // TODO: 关 — 执行动作 - } - break; - - /* ── DP7 勿扰模式开关 (raw) ── */ - case DP_ID_NO_DISTURB_ENABLE: - memcpy(gDevData.no_disturb_enable, ctrl->no_disturb_enable, 13); - // TODO: 根据 gDevData.no_disturb_enable 执行动作 (长度 13 字节) - break; - - /* ── DP8 预留DP1 (uint32) ── */ - case DP_ID_RESERVA_DP1: - gDevData.reserva_dp1 = ctrl->reserva_dp1; - // TODO: 根据 ctrl->reserva_dp1 执行动作 - break; - - /* ── DP9 预留DP2 (uint32) ── */ - case DP_ID_RESERVA_DP2: - gDevData.reserva_dp2 = ctrl->reserva_dp2; - // TODO: 根据 ctrl->reserva_dp2 执行动作 - break; - - /* ── DP10 预留DP3 (uint32) ── */ - case DP_ID_RESERVA_DP3: - gDevData.reserva_dp3 = ctrl->reserva_dp3; - // TODO: 根据 ctrl->reserva_dp3 执行动作 - break; - - default: - break; - } - } - return 0; -} - -/* 0x30 OTA 升级请求 */ -void jbOnOtaNotify(uint32_t fwVersion, uint32_t fwLength, uint32_t fwCrc32) -{ - (void)fwVersion; (void)fwLength; (void)fwCrc32; - // TODO: 检查 Flash 空间与版本,决定是否接收升级 - // 允许: 直接返回(协议层默认应答 JB_OK + MaxDataLen=128) - // 拒绝: 在 jbHandleOtaNotify 中改为 jbSendFrame(CMD_OTA_NOTIFY_ACK, sn, 错误码, 3) -} - -/* 0x32 OTA 数据(含分片序号)*/ -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) -} - -/* 0x36 / 0x38 停止 OTA */ -void jbOnOtaStop(void) -{ - // TODO: 终止 OTA,释放资源 -} - -/* 任意 MCU→BLE 请求的 ACK 回调(cmd 标识是哪条请求的应答,result 为结果码) - - SOC 仅保留 OTA 主动请求应答: OTA完成 0x35 / 停止 0x39 - - 可在本回调中设置"XX 完成/失败"标志,供主循环判断"确认成功才继续" */ -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: ... } -} - -/* ════════════════════════════════════════════════════════════════ - * 硬件抽象(按实际平台实现) - * ════════════════════════════════════════════════════════════════ */ - -/* ── 定时器 ── */ -void jbTimerIrq(void) -{ - timerMs++; -} /* 在 1ms 中断中调用本函数 */ - -uint32_t jbGetTimerCount(void) -{ - return timerMs; -} - -/* SOC: 发送由 SOC SDK 的 ble_send_data 承担,无需 uartInit / uartWrite; - * 定时器由 SOC SDK 提供,仅保留 jbTimerIrq / jbGetTimerCount 接入即可。 */ diff --git a/JBao_JL7016_MOD_FW/apps/usr_jb_proto/Protocol/jb_product.h b/JBao_JL7016_MOD_FW/apps/usr_jb_proto/Protocol/jb_product.h deleted file mode 100644 index af034db..0000000 --- a/JBao_JL7016_MOD_FW/apps/usr_jb_proto/Protocol/jb_product.h +++ /dev/null @@ -1,55 +0,0 @@ -/* ================================================================ - * jb_product.h — 用户层头文件(自动生成) - * 设备: 侠客猫激光 | 生成时间: 2026-07-31 17:08:51 - * - * 声明用户需实现的函数。数据结构定义见 jb_protocol.h - * ================================================================ - */ - -#ifndef _JB_PRODUCT_H -#define _JB_PRODUCT_H - -#include "jb_protocol.h" - -/* ════════════════════════════════════════════════════════════════ - * 硬件配置(SOC SDK 已提供定时器,无需 UART 波特率配置) - * ════════════════════════════════════════════════════════════════ */ -#define TIMER_PERIOD_MS 1 - -/* ════════════════════════════════════════════════════════════════ - * 版本管理 - * ════════════════════════════════════════════════════════════════ */ -/* MCU software version: Vmajor.minor.patch */ -#define JB_MCU_SW_VERSION_MAJOR 1 -#define JB_MCU_SW_VERSION_MINOR 0 -#define JB_MCU_SW_VERSION_PATCH 0 - -/* MCU hardware version: Vmajor.minor.patch */ -#define JB_MCU_HW_VERSION_MAJOR 1 -#define JB_MCU_HW_VERSION_MINOR 0 -#define JB_MCU_HW_VERSION_PATCH 0 - -/* ════════════════════════════════════════════════════════════════ - * 用户 API - * ════════════════════════════════════════════════════════════════ */ - -/* 全局设备数据 — 协议层直接读写该变量 */ -extern jb_data_point_t gDevData; - -/* ==================== 用户必须实现 ============================== */ -void userInit(void); /* DP 默认值与外设初始化 */ -void userHandle(void); /* 用户数据采集 */ -void jbTimerIrq(void); /* Call from 1ms timer ISR */ - -/* =========== 协议回调(声明见 jb_protocol.h)================== */ -/* int8_t jbEventProcess(...); // 0x03 控制事件 */ -/* void jbOnOtaNotify(...); // 0x30 OTA 请求 */ -/* void jbOnOtaData(...); // 0x32 OTA 数据 */ -/* void jbOnOtaStop(); // 0x36/0x38 停止 OTA */ -/* void jbOnAck(...); // 任意请求 ACK(结果) */ - -/* ============= 主动请求(声明见 jb_protocol.h)================ */ -/* int32_t jbNotifyOtaStop(); // 0x38 通知 BLE 停止 OTA */ -/* int32_t jbSendOtaResult(...); // 0x34 OTA 结果 */ - -#endif /* _JB_PRODUCT_H */ diff --git a/JBao_JL7016_MOD_FW/apps/usr_jb_proto/Protocol/jb_protocol.c b/JBao_JL7016_MOD_FW/apps/usr_jb_proto/Protocol/jb_protocol.c deleted file mode 100644 index 4fc8ec2..0000000 --- a/JBao_JL7016_MOD_FW/apps/usr_jb_proto/Protocol/jb_protocol.c +++ /dev/null @@ -1,749 +0,0 @@ -/* ================================================================ - * jb_protocol.c — 见宝 IOT 协议实现(自动生成) - * 设备: 侠客猫激光 | 生成时间: 2026-07-31 17:08:51 - * ================================================================ - */ - -#include "jb_protocol.h" -#include "jb_product.h" -#include "jb_ringbuffer.h" -#include "jb_common.h" - -/* ════════════════════════════════════════════════════════════════ - * 全局实例 - * ════════════════════════════════════════════════════════════════ */ -jb_protocol_t jbProtocol; -static jb_ringbuffer_t rb; /* UART 接收缓冲区 */ - -/* ════════════════════════════════════════════════════════════════ - * jbInit — 协议层初始化 - * ════════════════════════════════════════════════════════════════ */ -void jbInit(void) -{ - memset(&jbProtocol, 0, sizeof(jb_protocol_t)); - jbRbInit(&rb); -} - -/* ════════════════════════════════════════════════════════════════ - * jbPutData — UART 中断将数据压入环形缓冲区 - * ════════════════════════════════════════════════════════════════ */ -int32_t jbPutData(uint8_t *buf, uint32_t len) -{ - if (!buf) -{ - return -1; - } - for (uint32_t i = 0; i < len; i++) - { - jbRbPush(&rb, buf[i]); - } - return (int32_t)len; -} - -/* ════════════════════════════════════════════════════════════════ - * jbSendFrame — 组装协议帧并经 ble_send_data 发送(SOC 无串口) - * 返回帧总长度,< 0 表示出错 - * ════════════════════════════════════════════════════════════════ */ -static int32_t jbSendFrame(uint8_t cmd, uint8_t sn, - const uint8_t *payload, uint16_t pLen) -{ - uint8_t buf[MAX_PACKAGE_LEN]; - uint16_t lenField = pLen + 3; /* CMD + SN + payload + CKSUM */ - uint16_t total = lenField + 4; - - if (total > MAX_PACKAGE_LEN) - { - return -1; - } - - /* 55 AA | LEN_H LEN_L | CMD | SN | payload | CKSUM */ - buf[0] = PKT_HEAD_0; - buf[1] = PKT_HEAD_1; - jbWriteU16BE(&buf[2], lenField); - buf[4] = cmd; - buf[5] = sn; - if (payload && pLen > 0) - { - memcpy(&buf[6], payload, pLen); - } - buf[total - 1] = jbChecksum(buf, total); - - ble_send_data(buf, total); - return (int32_t)total; -} - -/* 发送单字节结果应答 */ -static void jbSendAck(uint8_t cmd, uint8_t sn, uint8_t result) -{ - jbSendFrame(cmd, sn, &result, 1); -} - -/* ════════════════════════════════════════════════════════════════ - * jbGetOnePacket — 从环形缓冲区提取完整帧 - * 返回: 0 = 成功, 1 = 需要更多数据, -2 = 校验和错误 - * ════════════════════════════════════════════════════════════════ */ -static int8_t jbGetOnePacket(uint8_t *out, uint16_t *outLen) -{ - static uint8_t state = 0; - static uint16_t expect = 0; - static uint16_t idx = 0; - static uint8_t prev = 0; - - while (jbRbAvailable(&rb)) - { - uint8_t b = jbRbPop(&rb); - - if (state == 0) - { - /* 搜索同步头 55 AA */ - if (prev == PKT_HEAD_0 && b == PKT_HEAD_1) - { - out[0] = PKT_HEAD_0; - out[1] = PKT_HEAD_1; - idx = 2; - state = 1; - } - } - else if (state == 1) - { - /* 接收 LEN(2B) + CMD + SN */ - out[idx++] = b; - if (idx == 6) - { - expect = 4 + jbReadU16BE(&out[2]); - if (expect > MAX_PACKAGE_LEN || expect < 7) - { - state = 0; - } - else - { - state = 2; - } - } - } - else - { - /* 接收剩余数据 */ - out[idx++] = b; - if (idx >= expect) - { - state = 0; - if (out[expect - 1] == jbChecksum(out, expect)) - { - *outLen = expect; - return 0; - } - return -2; - } - } - prev = b; - } - return 1; -} - -/* ════════════════════════════════════════════════════════════════ - * jbPackDps — dataPoint → outBuf[bitmap | values] - * mask 指定要打包的 DP(bit 序号 = DP ID) - * 返回 outBuf 总字节数(bitmap + values) - * 仅打包 RO / RW 方向的 DP - * ════════════════════════════════════════════════════════════════ */ -static uint16_t jbPackDps(const jb_data_point_t *dp, - const uint8_t *mask, uint8_t *outBuf) -{ - uint8_t *bitmap = outBuf; - uint8_t *out = outBuf + DP_BITMAP_SIZE; - uint16_t valIdx = 0; - memset(bitmap, 0, DP_BITMAP_SIZE); - - /* DP0 开关 (bool, 1B, RW) */ - if (mask[DP_ID_POWER / 8] & (1 << (DP_ID_POWER % 8))) - { - bitmap[DP_ID_POWER / 8] |= (1 << (DP_ID_POWER % 8)); - out[valIdx++] = dp->power ? 1 : 0; - } - /* DP1 自动模式 (enum, 1B, RW) */ - if (mask[DP_ID_PLAY_MODE / 8] & (1 << (DP_ID_PLAY_MODE % 8))) - { - bitmap[DP_ID_PLAY_MODE / 8] |= (1 << (DP_ID_PLAY_MODE % 8)); - out[valIdx++] = (uint8_t)dp->play_mode; - } - /* DP3 低电量报警 (bool, 1B, RO) */ - if (mask[DP_ID_ALM_LOW_BATTERY / 8] & (1 << (DP_ID_ALM_LOW_BATTERY % 8))) - { - bitmap[DP_ID_ALM_LOW_BATTERY / 8] |= (1 << (DP_ID_ALM_LOW_BATTERY % 8)); - out[valIdx++] = dp->alm_low_battery ? 1 : 0; - } - /* DP4 声音开关 (bool, 1B, RW) */ - if (mask[DP_ID_BEEP_TRIG / 8] & (1 << (DP_ID_BEEP_TRIG % 8))) - { - bitmap[DP_ID_BEEP_TRIG / 8] |= (1 << (DP_ID_BEEP_TRIG % 8)); - out[valIdx++] = dp->beep_trig ? 1 : 0; - } - /* DP5 电量上报 (uint8, 1B, RO) */ - if (mask[DP_ID_BATTERY / 8] & (1 << (DP_ID_BATTERY % 8))) - { - bitmap[DP_ID_BATTERY / 8] |= (1 << (DP_ID_BATTERY % 8)); - out[valIdx++] = (uint8_t)dp->battery; - } - /* DP6 震动触发开机 (bool, 1B, RW) */ - if (mask[DP_ID_SHAKE_WAKE / 8] & (1 << (DP_ID_SHAKE_WAKE % 8))) - { - bitmap[DP_ID_SHAKE_WAKE / 8] |= (1 << (DP_ID_SHAKE_WAKE % 8)); - out[valIdx++] = dp->shake_wake ? 1 : 0; - } - /* DP7 勿扰模式开关 (raw, 13B, RW) */ - if (mask[DP_ID_NO_DISTURB_ENABLE / 8] & (1 << (DP_ID_NO_DISTURB_ENABLE % 8))) - { - bitmap[DP_ID_NO_DISTURB_ENABLE / 8] |= (1 << (DP_ID_NO_DISTURB_ENABLE % 8)); - memcpy(&out[valIdx], dp->no_disturb_enable, 13); - valIdx += 13; - } - /* DP8 预留DP1 (uint32, 4B, RW) */ - if (mask[DP_ID_RESERVA_DP1 / 8] & (1 << (DP_ID_RESERVA_DP1 % 8))) - { - bitmap[DP_ID_RESERVA_DP1 / 8] |= (1 << (DP_ID_RESERVA_DP1 % 8)); - jbWriteU32BE(&out[valIdx], (uint32_t)dp->reserva_dp1); - valIdx += 4; - } - /* DP9 预留DP2 (uint32, 4B, RW) */ - if (mask[DP_ID_RESERVA_DP2 / 8] & (1 << (DP_ID_RESERVA_DP2 % 8))) - { - bitmap[DP_ID_RESERVA_DP2 / 8] |= (1 << (DP_ID_RESERVA_DP2 % 8)); - jbWriteU32BE(&out[valIdx], (uint32_t)dp->reserva_dp2); - valIdx += 4; - } - /* DP10 预留DP3 (uint32, 4B, RW) */ - if (mask[DP_ID_RESERVA_DP3 / 8] & (1 << (DP_ID_RESERVA_DP3 % 8))) - { - bitmap[DP_ID_RESERVA_DP3 / 8] |= (1 << (DP_ID_RESERVA_DP3 % 8)); - jbWriteU32BE(&out[valIdx], (uint32_t)dp->reserva_dp3); - valIdx += 4; - } - - return DP_BITMAP_SIZE + valIdx; -} - -/* ════════════════════════════════════════════════════════════════ - * jbUnpackDps — bitmap+values → dataPoint + event_info - * 仅解包 WO / RW 方向的 DP - * ════════════════════════════════════════════════════════════════ */ -static void jbUnpackDps(const uint8_t *bitmap, const uint8_t *values, - uint16_t valLen, jb_data_point_t *dp, - jb_event_info_t *info) -{ - uint16_t valIdx = 0; - if (info) - { - info->count = 0; - } - - /* DP0 开关 (bool, 1B, RW) */ - if (bitmap[DP_ID_POWER / 8] & (1 << (DP_ID_POWER % 8))) - { - if (valIdx < valLen) - { - dp->power = (values[valIdx++] != 0); - } - if (info && info->count < DP_ID_MAX) - { - info->dp_ids[info->count++] = DP_ID_POWER; - } - } - /* DP1 自动模式 (enum, 1B, RW) */ - if (bitmap[DP_ID_PLAY_MODE / 8] & (1 << (DP_ID_PLAY_MODE % 8))) - { - if (valIdx < valLen) - { - dp->play_mode = values[valIdx++]; - } - if (info && info->count < DP_ID_MAX) - { - info->dp_ids[info->count++] = DP_ID_PLAY_MODE; - } - } - /* DP2 手动模式 (enum, 1B, WO) */ - if (bitmap[DP_ID_HAND_MODE / 8] & (1 << (DP_ID_HAND_MODE % 8))) - { - if (valIdx < valLen) - { - dp->hand_mode = values[valIdx++]; - } - if (info && info->count < DP_ID_MAX) - { - info->dp_ids[info->count++] = DP_ID_HAND_MODE; - } - } - /* DP4 声音开关 (bool, 1B, RW) */ - if (bitmap[DP_ID_BEEP_TRIG / 8] & (1 << (DP_ID_BEEP_TRIG % 8))) - { - if (valIdx < valLen) - { - dp->beep_trig = (values[valIdx++] != 0); - } - if (info && info->count < DP_ID_MAX) - { - info->dp_ids[info->count++] = DP_ID_BEEP_TRIG; - } - } - /* DP6 震动触发开机 (bool, 1B, RW) */ - if (bitmap[DP_ID_SHAKE_WAKE / 8] & (1 << (DP_ID_SHAKE_WAKE % 8))) - { - if (valIdx < valLen) - { - dp->shake_wake = (values[valIdx++] != 0); - } - if (info && info->count < DP_ID_MAX) - { - info->dp_ids[info->count++] = DP_ID_SHAKE_WAKE; - } - } - /* DP7 勿扰模式开关 (raw, 13B, RW) */ - if (bitmap[DP_ID_NO_DISTURB_ENABLE / 8] & (1 << (DP_ID_NO_DISTURB_ENABLE % 8))) - { - if (valIdx + 13 <= valLen) - { - memcpy(dp->no_disturb_enable, &values[valIdx], 13); - valIdx += 13; - } - if (info && info->count < DP_ID_MAX) - { - info->dp_ids[info->count++] = DP_ID_NO_DISTURB_ENABLE; - } - } - /* DP8 预留DP1 (uint32, 4B, RW) */ - if (bitmap[DP_ID_RESERVA_DP1 / 8] & (1 << (DP_ID_RESERVA_DP1 % 8))) - { - if (valIdx + 4 <= valLen) - { - dp->reserva_dp1 = (uint32_t)jbReadU32BE(&values[valIdx]); - valIdx += 4; - } - if (info && info->count < DP_ID_MAX) - { - info->dp_ids[info->count++] = DP_ID_RESERVA_DP1; - } - } - /* DP9 预留DP2 (uint32, 4B, RW) */ - if (bitmap[DP_ID_RESERVA_DP2 / 8] & (1 << (DP_ID_RESERVA_DP2 % 8))) - { - if (valIdx + 4 <= valLen) - { - dp->reserva_dp2 = (uint32_t)jbReadU32BE(&values[valIdx]); - valIdx += 4; - } - if (info && info->count < DP_ID_MAX) - { - info->dp_ids[info->count++] = DP_ID_RESERVA_DP2; - } - } - /* DP10 预留DP3 (uint32, 4B, RW) */ - if (bitmap[DP_ID_RESERVA_DP3 / 8] & (1 << (DP_ID_RESERVA_DP3 % 8))) - { - if (valIdx + 4 <= valLen) - { - dp->reserva_dp3 = (uint32_t)jbReadU32BE(&values[valIdx]); - valIdx += 4; - } - if (info && info->count < DP_ID_MAX) - { - info->dp_ids[info->count++] = DP_ID_RESERVA_DP3; - } - } -} - -/* ════════════════════════════════════════════════════════════════ - * jbBuildDiffMask — 比较上次与当前值,为变化的可上报 DP 置 mask 位 - * 返回: 1 = 有 DP 发生变化, 0 = 全部未变化 - * ════════════════════════════════════════════════════════════════ */ -static uint8_t jbBuildDiffMask(const jb_data_point_t *last, - const jb_data_point_t *cur, - uint8_t *mask) -{ - uint8_t hasChange = 0; - memset(mask, 0, DP_BITMAP_SIZE); - - /* DP0 开关 */ - if (last->power != cur->power) - { - mask[DP_ID_POWER / 8] |= (1 << (DP_ID_POWER % 8)); - hasChange = 1; - } - /* DP1 自动模式 */ - if (last->play_mode != cur->play_mode) - { - mask[DP_ID_PLAY_MODE / 8] |= (1 << (DP_ID_PLAY_MODE % 8)); - hasChange = 1; - } - /* DP3 低电量报警 */ - if (last->alm_low_battery != cur->alm_low_battery) - { - mask[DP_ID_ALM_LOW_BATTERY / 8] |= (1 << (DP_ID_ALM_LOW_BATTERY % 8)); - hasChange = 1; - } - /* DP4 声音开关 */ - if (last->beep_trig != cur->beep_trig) - { - mask[DP_ID_BEEP_TRIG / 8] |= (1 << (DP_ID_BEEP_TRIG % 8)); - hasChange = 1; - } - /* DP5 电量上报 */ - if (last->battery != cur->battery) - { - mask[DP_ID_BATTERY / 8] |= (1 << (DP_ID_BATTERY % 8)); - hasChange = 1; - } - /* DP6 震动触发开机 */ - if (last->shake_wake != cur->shake_wake) - { - mask[DP_ID_SHAKE_WAKE / 8] |= (1 << (DP_ID_SHAKE_WAKE % 8)); - hasChange = 1; - } - /* DP7 勿扰模式开关 */ - if (memcmp(last->no_disturb_enable, cur->no_disturb_enable, 13)) - { - mask[DP_ID_NO_DISTURB_ENABLE / 8] |= (1 << (DP_ID_NO_DISTURB_ENABLE % 8)); - hasChange = 1; - } - /* DP8 预留DP1 */ - if (last->reserva_dp1 != cur->reserva_dp1) - { - mask[DP_ID_RESERVA_DP1 / 8] |= (1 << (DP_ID_RESERVA_DP1 % 8)); - hasChange = 1; - } - /* DP9 预留DP2 */ - if (last->reserva_dp2 != cur->reserva_dp2) - { - mask[DP_ID_RESERVA_DP2 / 8] |= (1 << (DP_ID_RESERVA_DP2 % 8)); - hasChange = 1; - } - /* DP10 预留DP3 */ - if (last->reserva_dp3 != cur->reserva_dp3) - { - mask[DP_ID_RESERVA_DP3 / 8] |= (1 << (DP_ID_RESERVA_DP3 % 8)); - hasChange = 1; - } - - return hasChange; -} - -/* 置全量掩码(所有可上报 DP)*/ -static void jbSetFullMask(uint8_t *mask) -{ - memset(mask, 0xFF, DP_BITMAP_SIZE); - if (DP_COUNT % 8) - { - mask[DP_BITMAP_SIZE - 1] &= (1 << (DP_COUNT % 8)) - 1; - } -} - -/* ════════════════════════════════════════════════════════════════ - * jbSendReqWithAck — 发送请求帧并登记 ACK 重传 - * - * 与 jbSendFrame 不同,本函数把完整帧缓存到 waitAck.buf, - * 并启动 ACK 等待。主动上报 / OTA 等网关应答均走 REPORTCMD_V2, - * 由 jbReportAckCheck() 清除等待;若 SEND_MAX_TIME 内未收到, - * jbAckRetry() 自动重传,最多 SEND_MAX_NUM 次。 - * 返回 ble_send_data 结果(<0 出错)。 - * ════════════════════════════════════════════════════════════════ */ -static int32_t jbSendReqWithAck(uint8_t cmd, uint8_t sn, - const uint8_t *payload, uint16_t pLen) -{ - uint16_t lenField = pLen + 3; /* CMD + SN + payload + CKSUM */ - uint16_t total = lenField + 4; /* + 55 AA + LEN(2) */ - - if (total > MAX_PACKAGE_LEN) - { - return -1; - } - - uint8_t buf[MAX_PACKAGE_LEN]; - buf[0] = PKT_HEAD_0; - buf[1] = PKT_HEAD_1; - jbWriteU16BE(&buf[2], lenField); - buf[4] = cmd; - buf[5] = sn; - if (payload && pLen > 0) - { - memcpy(&buf[6], payload, pLen); - } - buf[total - 1] = jbChecksum(buf, total); - - /* 缓存整帧,供超时重传 */ - memcpy(jbProtocol.waitAck.buf, buf, total); - jbProtocol.waitAck.len = total; - jbProtocol.waitAck.active = 1; - jbProtocol.waitAck.sn = sn; - jbProtocol.waitAck.retry = 0; - jbProtocol.waitAck.sendTime = jbGetTimerCount(); - - ble_send_data(buf, total); - return (int32_t)total; -} - -/* ════════════════════════════════════════════════════════════════ - * jbAckRetry — ACK 超时重传 - * ════════════════════════════════════════════════════════════════ */ -static void jbAckRetry(void) -{ - if (!jbProtocol.waitAck.active) - { - return; - } - if (jbProtocol.waitAck.retry >= SEND_MAX_NUM) - { - memset(&jbProtocol.waitAck, 0, sizeof(jb_wait_ack_t)); - return; - } - if (jbGetTimerCount() - jbProtocol.waitAck.sendTime > SEND_MAX_TIME) - { - ble_send_data(jbProtocol.waitAck.buf, jbProtocol.waitAck.len); - jbProtocol.waitAck.retry++; - jbProtocol.waitAck.sendTime = jbGetTimerCount(); - } -} - -/* ════════════════════════════════════════════════════════════════ - * jbReportAckCheck — 网关应答确认(主动上报 / OTA 共用) - * - * 网关通过 REPORTCMD_V2 回传(非 55AA)。成功条件: - * cmd == 0 - * gateway_int_cnt == 1 - * gateway_int0 == 0 - * 满足则清除 waitAck,停止重传,并回调 jbOnAck(wait_cmd, 0)。 - * - * 返回: 1=成功并清除等待; 0=未命中/条件不符。 - * ════════════════════════════════════════════════════════════════ */ -int8_t jbReportAckCheck(uint16_t cmd, uint8_t gateway_int_cnt, int32_t gateway_int0) -{ - if (!jbProtocol.waitAck.active) - { - return 0; - } - if (cmd == 0 && gateway_int_cnt == 1 && gateway_int0 == 0) - { - uint8_t wait_cmd = jbProtocol.waitAck.buf[4]; - memset(&jbProtocol.waitAck, 0, sizeof(jb_wait_ack_t)); - jbOnAck(wait_cmd, 0); /* result=0 表示应答成功 */ - return 1; - } - return 0; -} - -/* ════════════════════════════════════════════════════════════════ - * jbSendReport — 发送主动上报 (0x07) - * - * mask 指定本次上报包含哪些 DP: - * - 差分掩码(来自 jbBuildDiffMask) → 仅上报变化的 DP - * - 全量掩码(来自 jbSetFullMask) → 定时全量同步 - * ════════════════════════════════════════════════════════════════ */ -static void jbSendReport(jb_data_point_t *data, const uint8_t *mask) -{ - uint8_t payload[MAX_PACKAGE_LEN]; - uint16_t plen = jbPackDps(data, mask, payload); - uint8_t sn = jbProtocol.sn++; - jbSendReqWithAck(CMD_REPORT, sn, payload, plen); -} - -/* ════════════════════════════════════════════════════════════════ - * jbReportPolicy — 变化上报(差分) + 定时上报(全量) - * - * 策略: - * 1. DP 发生变化 → 仅上报变化的 DP(差分掩码) - * 2. 每隔 REPORT_PERIOD ms → 上报全部可上报 DP(全量同步) - * ════════════════════════════════════════════════════════════════ */ -static void jbReportPolicy(jb_data_point_t *data) -{ - uint32_t now = jbGetTimerCount(); - uint8_t diffMask[DP_BITMAP_SIZE]; - - /* ── 1. 变化上报(差分) ── */ - uint8_t hasChange = jbBuildDiffMask(&jbProtocol.lastData, data, diffMask); - if (hasChange && !jbProtocol.waitAck.active && - (now - jbProtocol.lastChangeTime > REPORT_DEBOUNCE)) - { - jbSendReport(data, diffMask); - memcpy(&jbProtocol.lastData, data, sizeof(jb_data_point_t)); - jbProtocol.lastChangeTime = now; - jbProtocol.lastReportTime = now; - return; - } - - /* ── 2. 定时全量上报 ── */ - if (!jbProtocol.waitAck.active && - (now - jbProtocol.lastReportTime > REPORT_PERIOD)) - { - uint8_t fullMask[DP_BITMAP_SIZE]; - jbSetFullMask(fullMask); - jbSendReport(data, fullMask); - memcpy(&jbProtocol.lastData, data, sizeof(jb_data_point_t)); - jbProtocol.lastReportTime = now; - } -} - -/* ════════════════════════════════════════════════════════════════ - * 命令处理函数(BLE → MCU 下行帧) - * ════════════════════════════════════════════════════════════════ */ - -/* ── 0x03 APP 下发控制 ── */ -static void jbHandleControl(uint8_t *frame, uint16_t len) -{ - uint8_t sn = frame[5]; - uint8_t *bitmap = &frame[6]; - uint8_t *values = &frame[6 + DP_BITMAP_SIZE]; - uint16_t valLen = len - 7 - DP_BITMAP_SIZE; - - jb_event_info_t info; - jb_data_point_t ctrlData; - memset(&ctrlData, 0, sizeof(ctrlData)); - jbUnpackDps(bitmap, values, valLen, &ctrlData, &info); - - /* 分发给用户层;用户在回调中更新 gDevData */ - jbEventProcess(&info, &ctrlData); - jbSendAck(CMD_CONTROL_ACK, sn, JB_OK); -} - -/* ── 0x05 APP 读取请求 ── */ -static void jbHandleRead(uint8_t *frame, uint16_t len, - jb_data_point_t *data) -{ - uint8_t sn = frame[5]; - uint8_t *reqBitmap = &frame[6]; - - uint8_t mask[DP_BITMAP_SIZE]; - memcpy(mask, reqBitmap, DP_BITMAP_SIZE); - - /* bitmap 全 1(=0xFF...) 表示读取全部 DP */ - uint8_t allOnes = 1; - for (int i = 0; i < DP_BITMAP_SIZE; i++) - { - if (mask[i] != 0xFF) - { - allOnes = 0; - break; - } - } - if (allOnes) - { - jbSetFullMask(mask); - } - - uint8_t payload[MAX_PACKAGE_LEN]; - uint16_t plen = jbPackDps(data, mask, payload); - jbSendFrame(CMD_READ_ACK, sn, payload, plen); -} - -/* ── 0x30 OTA 升级请求 ── */ -static void jbHandleOtaNotify(uint8_t *frame, uint16_t len) -{ - uint8_t sn = frame[5]; - uint32_t fwVersion = ((uint32_t)frame[6] << 16) - | ((uint32_t)frame[7] << 8) - | frame[8]; - uint32_t fwLength = jbReadU32BE(&frame[9]); - uint32_t fwCrc32 = jbReadU32BE(&frame[13]); - - jbOnOtaNotify(fwVersion, fwLength, fwCrc32); - - /* 默认应答: 结果码 + 最大单包长度(2B) */ - /* maxDataLen 由 MCU 自身能力决定,填入 0x31 应答,BLE 据此分片 */ - uint8_t ack[3]; - ack[0] = JB_OK; - ack[1] = (uint8_t)(JB_OTA_MAX_PAYLOAD >> 8); - ack[2] = (uint8_t)(JB_OTA_MAX_PAYLOAD & 0xFF); - jbSendFrame(CMD_OTA_NOTIFY_ACK, sn, ack, 3); -} - -/* ── 0x32 OTA 数据(含分片序号)── */ -static void jbHandleOtaData(uint8_t *frame, uint16_t len) -{ - uint8_t sn = frame[5]; - uint16_t shardIdx = jbReadU16BE(&frame[6]); - uint16_t totalShards = jbReadU16BE(&frame[8]); - const uint8_t *pData = &frame[10]; - uint16_t dataLen = (len > 11) ? (len - 11) : 0; - jbOnOtaData(pData, dataLen, shardIdx, totalShards); - jbSendAck(CMD_OTA_DATA_ACK, sn, JB_OK); -} - -/* ── 0x36 BLE 通知停止 OTA ── */ -static void jbHandleOtaStop(uint8_t *frame, uint16_t len) -{ - uint8_t sn = frame[5]; - jbOnOtaStop(); - jbSendAck(CMD_OTA_STOP_BLE_ACK, sn, JB_OK); -} - -/* 0x38 通知 BLE 停止 OTA(MCU 主动,等待 ACK 重传)*/ -int32_t jbNotifyOtaStop(void) -{ - uint8_t sn = jbProtocol.sn++; - return jbSendReqWithAck(CMD_OTA_STOP_MCU, sn, NULL, 0); -} - -/* 0x34 OTA 升级完成(result + MCU 版本 3B,等待 ACK 重传)*/ -int32_t jbSendOtaResult(uint8_t result, const uint8_t *mcuVersion) -{ - uint8_t sn = jbProtocol.sn++; - uint8_t payload[4]; - payload[0] = result; - if (mcuVersion) - { - memcpy(&payload[1], mcuVersion, 3); - } - else - { - memset(&payload[1], 0, 3); - } - return jbSendReqWithAck(CMD_OTA_COMPLETE, sn, payload, 4); -} - -/* ════════════════════════════════════════════════════════════════ - * jbProtocolHandle — 协议主循环(在 while(1) 中调用) - * ════════════════════════════════════════════════════════════════ */ -int32_t jbProtocolHandle(jb_data_point_t *data) -{ - if (!data) - { - return -1; - } - - /* ACK 超时重传 */ - jbAckRetry(); - - /* 提取一帧 */ - uint16_t pktLen = 0; - int8_t ret = jbGetOnePacket(jbProtocol.rxBuf, &pktLen); - uint8_t cmd; - if (ret == 0) - { - cmd = jbProtocol.rxBuf[4]; - - /* 按命令码统一分发 BLE → MCU 的所有帧(含 MCU 主动请求的应答)*/ - switch (cmd) - { - /* ── APP-BLE 数据 ── */ - case CMD_CONTROL: - jbHandleControl(jbProtocol.rxBuf, pktLen); - break; - case CMD_READ: - jbHandleRead(jbProtocol.rxBuf, pktLen, data); - break; - /* ── BLE-MCU OTA ── */ - case CMD_OTA_NOTIFY: - jbHandleOtaNotify(jbProtocol.rxBuf, pktLen); - break; - case CMD_OTA_DATA: - jbHandleOtaData(jbProtocol.rxBuf, pktLen); - break; - case CMD_OTA_STOP_BLE: - jbHandleOtaStop(jbProtocol.rxBuf, pktLen); - break; - /* 主动上报 / OTA 完成&停止 的网关应答均走 REPORTCMD_V2 → jbReportAckCheck */ - default: - break; - } - } - - jbReportPolicy(data); - return 0; -} diff --git a/JBao_JL7016_MOD_FW/apps/usr_jb_proto/Protocol/jb_protocol.h b/JBao_JL7016_MOD_FW/apps/usr_jb_proto/Protocol/jb_protocol.h deleted file mode 100644 index e009008..0000000 --- a/JBao_JL7016_MOD_FW/apps/usr_jb_proto/Protocol/jb_protocol.h +++ /dev/null @@ -1,266 +0,0 @@ -/* ================================================================ - * jb_protocol.h — 见宝 IOT 协议层(自动生成) - * 设备: 侠客猫激光 | DP 数: 11 | Bitmap: 2B - * 协议版本: V1.0 | 生成时间: 2026-07-31 17:08:51 - * - * 帧格式: [55 AA] [LEN_H LEN_L] [CMD] [SN] [payload...] [CKSUM] - * LEN = CMD(1) + SN(1) + payload + CKSUM(1) = payload + 3 - * CKSUM = sum(LEN_H .. payload_last) & 0xFF - * Bitmap = 每个 DP 占 1 bit,bit 序号 = DP ID(0 起始) - * Values = 按 bitmap 置位顺序排列,每个 DP 占完整 byte_length - * ================================================================ - */ - -#ifndef _JB_PROTOCOL_H -#define _JB_PROTOCOL_H - -#include -#include -#include - -/* ════════════════════════════════════════════════════════════════ - * 协议常量 - * ════════════════════════════════════════════════════════════════ */ -#define PKT_HEAD_0 0x55 -#define PKT_HEAD_1 0xAA -#define MAX_PACKAGE_LEN 128 -#define JB_OTA_MAX_PAYLOAD 96 /* OTA 单包 Payload 上限(Byte); 须 <= MAX_PACKAGE_LEN - 11(0x32 帧开销) */ - -#define DP_COUNT 11 -#define DP_BITMAP_SIZE 2 - -#define SEND_MAX_TIME 2500 /* ACK 重传超时(ms);BLE Indication往返常需数百ms~1s,过短会误重传导致双应答 */ -#define SEND_MAX_NUM 3 /* 最大重传次数 */ -#define REPORT_DEBOUNCE 2000 /* 变化上报防抖时间(ms) */ -#define REPORT_PERIOD 600000 /* 定时全量上报周期(ms) */ - -/* Device identity: change these values for the target product. - * JB_CATEGORY_CODE: 必须 4 字符 ASCII,供 BLE 广播 prodcode 使用 - * JB_PRODUCT_CODE : 必须 6 字符 ASCII,供 BLE 广播 pcode 使用 - */ -#define JB_PROTOCOL_VERSION 0x0100 /* V1.0 */ -#define JB_CATEGORY_CODE "CBC0" -#define JB_PRODUCT_CODE "PICBC0" -#define JB_DEVICE_SN 0x00 -#define JB_DEVICE_CAPABILITY 0ULL - -/* ════════════════════════════════════════════════════════════════ - * 命令码(见《MCU方案接入协议通讯文档》) - * ════════════════════════════════════════════════════════════════ */ -typedef enum -{ - /* ── APP-BLE 数据(6) ── */ - CMD_CONTROL = 0x03, /* APP/BLE → MCU: 下发控制 */ - CMD_CONTROL_ACK = 0x04, /* MCU → APP/BLE: 控制应答 */ - CMD_READ = 0x05, /* APP/BLE → MCU: 读取请求 */ - CMD_READ_ACK = 0x06, /* MCU → APP/BLE: 读取应答 */ - CMD_REPORT = 0x07, /* MCU → APP/BLE: 主动上报 */ - CMD_REPORT_ACK = 0x08, /* APP/BLE → MCU: 上报应答 */ - /* ── OTA(10) ── */ - CMD_OTA_NOTIFY = 0x30, /* BLE → MCU: OTA 升级请求 */ - CMD_OTA_NOTIFY_ACK = 0x31, /* MCU → BLE: OTA 应答(含MaxLen) */ - CMD_OTA_DATA = 0x32, /* BLE → MCU: OTA 数据 */ - CMD_OTA_DATA_ACK = 0x33, /* MCU → BLE: OTA 数据应答 */ - CMD_OTA_COMPLETE = 0x34, /* MCU → BLE: OTA 完成 */ - CMD_OTA_COMPLETE_ACK = 0x35, /* BLE → MCU: OTA 完成应答 */ - CMD_OTA_STOP_BLE = 0x36, /* BLE → MCU: 停止 OTA */ - CMD_OTA_STOP_BLE_ACK = 0x37, /* MCU → BLE: 停止应答 */ - CMD_OTA_STOP_MCU = 0x38, /* MCU → BLE: 通知停止 OTA */ - CMD_OTA_STOP_MCU_ACK = 0x39, /* BLE → MCU: 停止应答 */ -} jb_cmd_t; - -/* ════════════════════════════════════════════════════════════════ - * 结果码 - * ════════════════════════════════════════════════════════════════ */ -typedef enum -{ - JB_OK = 0x00, - JB_PARAM_ERR = 0x01, - JB_BUSY = 0x02, - JB_NOT_SUPPORT = 0x03, - JB_CRC_ERR = 0x04, - JB_FAIL = 0x05, - /* ── OTA 专用错误码 ── */ - JB_OTA_NO_NEED = 0x20, /* 无需升级(版本一致) */ - JB_OTA_VER_LOW = 0x21, /* OTA 版本低于当前版本 */ - JB_OTA_SPACE_ERR = 0x22, /* Flash 空间不足 */ - JB_OTA_STATE_ERR = 0x23, /* OTA 状态错误 */ - JB_OTA_FILE_ERR = 0x24, /* OTA 文件检验失败 */ - JB_OTA_FAIL = 0x25, /* 升级失败 */ -} jb_result_t; - -/* ════════════════════════════════════════════════════════════════ - * BLE 工作状态位 (0x14) - * ════════════════════════════════════════════════════════════════ */ -#define WS_CONFIGURED (1 << 0) /* 已配网 */ -#define WS_INTERNET (1 << 1) /* 已连外网 */ -#define WS_APP_ONLINE (1 << 2) /* App 在线 */ -#define WS_BLE_CONNECTED (1 << 3) /* 手机 BLE 已连接 */ -#define WS_OTA_UPGRADING (1 << 4) /* OTA 升级中 */ -#define WS_PAIRING_MODE (1 << 5) /* 配对模式激活 */ - -/* ════════════════════════════════════════════════════════════════ - * 设备能力位图 (0x02 Capability Bitmap, 8B 大端) - * ════════════════════════════════════════════════════════════════ */ -#define CAP_BLE_OTA (1ULL << 0) /* BLE OTA */ -#define CAP_MCU_OTA (1ULL << 1) /* MCU OTA */ -#define CAP_APP_DIRECT (1ULL << 2) /* APP 直连 BLE */ -#define CAP_IPC_GATEWAY (1ULL << 3) /* IPC 网关连接 */ -#define CAP_PRODUCTION (1ULL << 4) /* 产测模式 */ -#define CAP_PARAM_CFG (1ULL << 5) /* 参数配置 */ -#define CAP_LOG (1ULL << 6) /* 日志功能 */ - -/* ════════════════════════════════════════════════════════════════ - * 身份信息 (0x02) / MCU 信息 (0x41) - * ════════════════════════════════════════════════════════════════ */ -/* ════════════════════════════════════════════════════════════════ - * DP ID 枚举(从 Excel 自动生成) - * ════════════════════════════════════════════════════════════════ */ -typedef enum -{ - DP_ID_POWER = 0, /* 开关 (bool, RW) */ - DP_ID_PLAY_MODE = 1, /* 自动模式 (enum, RW) */ - DP_ID_HAND_MODE = 2, /* 手动模式 (enum, WO) */ - DP_ID_ALM_LOW_BATTERY = 3, /* 低电量报警 (bool, RO) */ - DP_ID_BEEP_TRIG = 4, /* 声音开关 (bool, RW) */ - DP_ID_BATTERY = 5, /* 电量上报 (uint8, RO) */ - DP_ID_SHAKE_WAKE = 6, /* 震动触发开机 (bool, RW) */ - DP_ID_NO_DISTURB_ENABLE = 7, /* 勿扰模式开关 (raw, RW) */ - DP_ID_RESERVA_DP1 = 8, /* 预留DP1 (uint32, RW) */ - DP_ID_RESERVA_DP2 = 9, /* 预留DP2 (uint32, RW) */ - DP_ID_RESERVA_DP3 = 10, /* 预留DP3 (uint32, RW) */ - DP_ID_MAX -} dp_id_t; - -/* DP 数据长度(字节) */ -#define DP_LEN_POWER 1 -#define DP_LEN_PLAY_MODE 1 -#define DP_LEN_HAND_MODE 1 -#define DP_LEN_ALM_LOW_BATTERY 1 -#define DP_LEN_BEEP_TRIG 1 -#define DP_LEN_BATTERY 1 -#define DP_LEN_SHAKE_WAKE 1 -#define DP_LEN_NO_DISTURB_ENABLE 13 -#define DP_LEN_RESERVA_DP1 4 -#define DP_LEN_RESERVA_DP2 4 -#define DP_LEN_RESERVA_DP3 4 - -/* ════════════════════════════════════════════════════════════════ - * DP 枚举值定义 - * ════════════════════════════════════════════════════════════════ */ -/* 自动模式 */ -typedef enum -{ - PLAY_MODE_0 = 0, /* 关 */ - PLAY_MODE_1 = 1, /* mode1 */ - PLAY_MODE_2 = 2, /* mode2 */ - PLAY_MODE_3 = 3, /* mode3 */ - PLAY_MODE_4 = 4, /* mode4 */ -} PLAY_MODE_t; - -/* 手动模式 */ -typedef enum -{ - HAND_MODE_0 = 0, /* 关 */ - HAND_MODE_1 = 1, /* 顺时针 */ - HAND_MODE_2 = 2, /* 逆时针 */ -} HAND_MODE_t; - -/* ════════════════════════════════════════════════════════════════ - * 数据结构 - * ════════════════════════════════════════════════════════════════ */ - -/* 数据点 — 所有 DP 的当前值 */ -typedef struct -{ - uint8_t power; /* DP0 开关 */ - uint8_t play_mode; /* DP1 自动模式 */ - uint8_t hand_mode; /* DP2 手动模式 */ - uint8_t alm_low_battery;/* DP3 低电量报警 */ - uint8_t beep_trig; /* DP4 声音开关 */ - uint8_t battery; /* DP5 电量上报 */ - uint8_t shake_wake; /* DP6 震动触发开机 */ - uint8_t no_disturb_enable[13];/* DP7 勿扰模式开关 */ - uint32_t reserva_dp1; /* DP8 预留DP1 */ - uint32_t reserva_dp2; /* DP9 预留DP2 */ - uint32_t reserva_dp3; /* DP10 预留DP3 */ -} jb_data_point_t; - -/* 控制事件信息 */ -typedef struct -{ - uint8_t count; - uint8_t dp_ids[DP_ID_MAX]; -} jb_event_info_t; - -/* ACK 等待 / 重传管理器 */ -typedef struct -{ - uint8_t active; /* 是否有等待中的请求 */ - uint8_t sn; /* 帧序列号 */ - uint8_t retry; - uint8_t buf[MAX_PACKAGE_LEN]; - uint16_t len; - uint32_t sendTime; -} jb_wait_ack_t; - -/* 协议全局状态 */ -typedef struct -{ - uint8_t sn; /* 设备 SN 计数器 */ - uint8_t rxBuf[MAX_PACKAGE_LEN]; - jb_wait_ack_t waitAck; /* ACK 重传管理器 */ - jb_data_point_t lastData; /* 上次上报快照 */ - uint32_t lastReportTime; /* 上次上报时刻 */ - uint32_t lastChangeTime; /* 上次变化上报时刻 */ -} jb_protocol_t; - -/* ════════════════════════════════════════════════════════════════ - * 全局实例 - * ════════════════════════════════════════════════════════════════ */ -extern jb_protocol_t jbProtocol; - -/* ════════════════════════════════════════════════════════════════ - * 协议层 API(自动生成) - * ════════════════════════════════════════════════════════════════ */ -void jbInit(void); -int32_t jbPutData(uint8_t *buf, uint32_t len); - -int32_t jbNotifyOtaStop(void); -int32_t jbSendOtaResult(uint8_t result, const uint8_t *mcuVersion); - -int32_t jbProtocolHandle(jb_data_point_t *data); - -/* - * 网关应答确认(主动上报 / OTA 共用,REPORTCMD_V2,非 55AA) - * cmd: ble_proto 包 cmd - * gateway_int_cnt: gateway int 个数 - * gateway_int0: gateway_int[0] - * 成功条件: cmd==0 && gateway_int_cnt==1 && gateway_int0==0 - * 返回: 1=成功并清除等待; 0=未命中/条件不符 - */ -int8_t jbReportAckCheck(uint16_t cmd, uint8_t gateway_int_cnt, int32_t gateway_int0); - -/* ════════════════════════════════════════════════════════════════ - * 硬件抽象(用户实现) - * ════════════════════════════════════════════════════════════════ */ -uint32_t jbGetTimerCount(void); /* ms 计数器(定时器由 SOC SDK 提供) */ - -/* 发送接口:经 SOC SDK 提供的 BLE 发送封装(由用户/SDK 实现,非本生成代码范围)*/ -extern void ble_send_data(uint8_t *data, uint16_t len); - -/* ════════════════════════════════════════════════════════════════ - * 用户回调(用户实现) - * ════════════════════════════════════════════════════════════════ */ -/* 0x03 APP 下发控制事件 */ -int8_t jbEventProcess(jb_event_info_t *info, jb_data_point_t *data); - -/* BLE → MCU 事件回调 */ -void jbOnOtaNotify(uint32_t fwVersion, uint32_t fwLength, uint32_t fwCrc32); /* 0x30 */ -void jbOnOtaData(const uint8_t *data, uint16_t len,uint16_t shardIdx, uint16_t totalShards); /* 0x32 */ -void jbOnOtaStop(void); /* 0x36 / 0x38 停止 */ - -/* MCU → BLE 应答回调(收到 BLE 应答时触发)*/ -void jbOnAck(uint8_t cmd, uint8_t result); /* 任意请求 ACK(结果) */ - -#endif /* _JB_PROTOCOL_H */ diff --git a/JBao_JL7016_MOD_FW/apps/usr_jb_proto/Utils/jb_common.c b/JBao_JL7016_MOD_FW/apps/usr_jb_proto/Utils/jb_common.c deleted file mode 100644 index a631a1b..0000000 --- a/JBao_JL7016_MOD_FW/apps/usr_jb_proto/Utils/jb_common.c +++ /dev/null @@ -1,69 +0,0 @@ -/* ================================================================ - * jb_common.c — 工具函数实现 - * ================================================================ - */ -#include "jb_common.h" - -/* ════════════════════════════════════════════════════════════════ - * 校验和 - * ════════════════════════════════════════════════════════════════ */ -uint8_t jbChecksum(const uint8_t *frame, uint16_t totalLen) -{ - if (!frame || totalLen < 4) - { - return 0; - } - uint16_t sum = 0; - /* 从 LEN_H(buf[2]) 累加到最后一个 payload 字节(buf[totalLen-2]) */ - for (uint16_t i = 2; i < totalLen - 1; i++) - { - sum += frame[i]; - } - return (uint8_t)(sum & 0xFF); -} - -/* ════════════════════════════════════════════════════════════════ - * 大端序读写 - * ════════════════════════════════════════════════════════════════ */ -uint16_t jbReadU16BE(const uint8_t *buf) -{ - if (!buf) - { - return 0; - } - return ((uint16_t)buf[0] << 8) | buf[1]; -} - -uint32_t jbReadU32BE(const uint8_t *buf) -{ - if (!buf) - { - return 0; - } - return ((uint32_t)buf[0] << 24) - | ((uint32_t)buf[1] << 16) - | ((uint32_t)buf[2] << 8) - | buf[3]; -} - -void jbWriteU16BE(uint8_t *buf, uint16_t val) -{ - if (!buf) - { - return; - } - buf[0] = (uint8_t)(val >> 8); - buf[1] = (uint8_t)(val & 0xFF); -} - -void jbWriteU32BE(uint8_t *buf, uint32_t val) -{ - if (!buf) - { - return; - } - buf[0] = (uint8_t)(val >> 24); - buf[1] = (uint8_t)(val >> 16); - buf[2] = (uint8_t)(val >> 8); - buf[3] = (uint8_t)(val & 0xFF); -} diff --git a/JBao_JL7016_MOD_FW/apps/usr_jb_proto/Utils/jb_common.h b/JBao_JL7016_MOD_FW/apps/usr_jb_proto/Utils/jb_common.h deleted file mode 100644 index c9face2..0000000 --- a/JBao_JL7016_MOD_FW/apps/usr_jb_proto/Utils/jb_common.h +++ /dev/null @@ -1,37 +0,0 @@ -/* ================================================================ - * jb_common.h — 工具函数(校验和、大端序转换) - * - * 说明: 见宝协议采用大端序(Big-Endian)。 - * 多字节值(uint16/uint32)按高位在前传输。 - * ================================================================ - */ -#ifndef _JB_COMMON_H -#define _JB_COMMON_H - -#include -#include - -/* ════════════════════════════════════════════════════════════════ - * 校验和 - * sum(LEN_H, LEN_L, CMD, SN, ...payload_last) & 0xFF - * buf[0]=55, buf[1]=AA,累加范围从 buf[2] 到 buf[totalLen-2] - * ════════════════════════════════════════════════════════════════ */ -uint8_t jbChecksum(const uint8_t *frame, uint16_t totalLen); - -/* ════════════════════════════════════════════════════════════════ - * 大端序转换 - * ════════════════════════════════════════════════════════════════ */ - -/* 从字节数组读取 uint16(大端序)*/ -uint16_t jbReadU16BE(const uint8_t *buf); - -/* 从字节数组读取 uint32(大端序)*/ -uint32_t jbReadU32BE(const uint8_t *buf); - -/* 将 uint16 写入字节数组(大端序)*/ -void jbWriteU16BE(uint8_t *buf, uint16_t val); - -/* 将 uint32 写入字节数组(大端序)*/ -void jbWriteU32BE(uint8_t *buf, uint32_t val); - -#endif /* _JB_COMMON_H */ diff --git a/JBao_JL7016_MOD_FW/apps/usr_jb_proto/Utils/jb_ringbuffer.c b/JBao_JL7016_MOD_FW/apps/usr_jb_proto/Utils/jb_ringbuffer.c deleted file mode 100644 index b9c2e28..0000000 --- a/JBao_JL7016_MOD_FW/apps/usr_jb_proto/Utils/jb_ringbuffer.c +++ /dev/null @@ -1,75 +0,0 @@ -/* ================================================================ - * jb_ringbuffer.c — 环形缓冲区实现 - * ================================================================ - */ -#include "jb_ringbuffer.h" - -/* ── 初始化 ── */ -void jbRbInit(jb_ringbuffer_t *rb) -{ - if (!rb) - { - return; - } - rb->write = rb->read = rb->count = 0; -} - -/* ── 压入一个字节 ── */ -void jbRbPush(jb_ringbuffer_t *rb, uint8_t byte) -{ - if (!rb) - { - return; - } - if (rb->count >= JB_RB_CAPACITY) - { - return; /* 已满 — 丢弃 */ - } - rb->buf[rb->write] = byte; - rb->write = (rb->write + 1) % JB_RB_CAPACITY; - rb->count++; -} - -/* ── 弹出一个字节 ── */ -uint8_t jbRbPop(jb_ringbuffer_t *rb) -{ - if (!rb || rb->count == 0) - { - return 0; - } - uint8_t byte = rb->buf[rb->read]; - rb->read = (rb->read + 1) % JB_RB_CAPACITY; - rb->count--; - return byte; -} - -/* ── 查看指定偏移字节(不消费)── */ -uint8_t jbRbPeek(const jb_ringbuffer_t *rb, uint16_t offset) -{ - if (!rb || offset >= rb->count) - { - return 0; - } - uint16_t idx = (rb->read + offset) % JB_RB_CAPACITY; - return rb->buf[idx]; -} - -/* ── 可读字节数 ── */ -uint16_t jbRbAvailable(const jb_ringbuffer_t *rb) -{ - if (!rb) - { - return 0; - } - return rb->count; -} - -/* ── 清空缓冲区 ── */ -void jbRbClear(jb_ringbuffer_t *rb) -{ - if (!rb) - { - return; - } - rb->write = rb->read = rb->count = 0; -} diff --git a/JBao_JL7016_MOD_FW/apps/usr_jb_proto/Utils/jb_ringbuffer.h b/JBao_JL7016_MOD_FW/apps/usr_jb_proto/Utils/jb_ringbuffer.h deleted file mode 100644 index 190a82b..0000000 --- a/JBao_JL7016_MOD_FW/apps/usr_jb_proto/Utils/jb_ringbuffer.h +++ /dev/null @@ -1,30 +0,0 @@ -/* ================================================================ - * jb_ringbuffer.h — 环形缓冲区(UART 接收) - * ================================================================ - */ -#ifndef _JB_RINGBUFFER_H -#define _JB_RINGBUFFER_H - -#include -#include - -/* 缓冲区配置 */ -#define JB_RB_CAPACITY 256 /* 缓冲区容量(2 的幂可优化取模运算)*/ - -/* 环形缓冲区实例 */ -typedef struct -{ - uint8_t buf[JB_RB_CAPACITY]; - uint16_t write; /* 写索引 */ - uint16_t read; /* 读索引 */ - uint16_t count; /* 已用字节数 */ -} jb_ringbuffer_t; - -void jbRbInit(jb_ringbuffer_t *rb); -void jbRbPush(jb_ringbuffer_t *rb, uint8_t byte); -uint8_t jbRbPop(jb_ringbuffer_t *rb); -uint8_t jbRbPeek(const jb_ringbuffer_t *rb, uint16_t offset); -uint16_t jbRbAvailable(const jb_ringbuffer_t *rb); -void jbRbClear(jb_ringbuffer_t *rb); - -#endif /* _JB_RINGBUFFER_H */ diff --git a/JBao_JL7016_MOD_FW/apps/usr_jb_proto/usr_jb_main.c b/JBao_JL7016_MOD_FW/apps/usr_jb_proto/usr_jb_main.c deleted file mode 100644 index 80b0657..0000000 --- a/JBao_JL7016_MOD_FW/apps/usr_jb_proto/usr_jb_main.c +++ /dev/null @@ -1,49 +0,0 @@ -#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" - -/* 向 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] -}; -const uint8_t g_usr_le_pcode[USR_LE_PCODE_LEN] = { - JB_PRODUCT_CODE[0], JB_PRODUCT_CODE[1], JB_PRODUCT_CODE[2], - JB_PRODUCT_CODE[3], JB_PRODUCT_CODE[4], JB_PRODUCT_CODE[5] -}; - -void usr_timer_1_ms(); -static int jb_timer_id=0; - -void usr_jb_init() -{ - jbInit(); - userInit(); - if(jb_timer_id==0)jb_timer_id = sys_s_hi_timer_add(NULL, usr_timer_1_ms, TIMER_PERIOD_MS); -} - -void usr_timer_1_ms() -{ - jbTimerIrq(); -} - -void usr_jb_le_recieve_data(u8* data,u16 len) -{ - jbPutData(data,len); -} - -void usr_timer_loop()///500ms -{ - userHandle(); - jbProtocolHandle(&gDevData); -} diff --git a/JBao_JL7016_MOD_FW/apps/usr_le_code/bleproto_packer.h b/JBao_JL7016_MOD_FW/apps/usr_le_code/bleproto_packer.h index 9b5661f..01bb9b0 100644 --- a/JBao_JL7016_MOD_FW/apps/usr_le_code/bleproto_packer.h +++ b/JBao_JL7016_MOD_FW/apps/usr_le_code/bleproto_packer.h @@ -403,6 +403,11 @@ typedef struct bleproto_authsetup_req { uint8_t gwmac[6]; ///< 主控mac地址, 设备需要保存 char devid[36 + 1]; ///< 主控分配的子设备id, 设备需要保存 char authcode[32 + 1]; ///< 主控分配的子设备认证码, 设备需要保存 + /** + * paired_flag:设备是否已完成配网(持久化在 VM) + * 0 = 未配网:默认不广播,除非 usr_goto_pair=1(绑定广播) + * 1 = 已配网:断连后发回连广播;AUTHSETUP 写三元组成功后置 1 + */ uint8_t paired_flag; // clang-format on } bleproto_authsetup_req_t; diff --git a/JBao_JL7016_MOD_FW/apps/usr_le_code/usr_le_adv.c b/JBao_JL7016_MOD_FW/apps/usr_le_code/usr_le_adv.c index befd16e..fc54e89 100644 --- a/JBao_JL7016_MOD_FW/apps/usr_le_code/usr_le_adv.c +++ b/JBao_JL7016_MOD_FW/apps/usr_le_code/usr_le_adv.c @@ -15,8 +15,9 @@ #include "bleproto.h" #include "bleproto_api.h" #include "usr_le_product.h" +#include "mcu_bridge.h" #define LOG_TAG_CONST APP -#define LOG_TAG "[APP]" +#define LOG_TAG "[LE_ADV]" #define LOG_ERROR_ENABLE #define LOG_DEBUG_ENABLE #define LOG_INFO_ENABLE @@ -24,6 +25,27 @@ #define LOG_CLI_ENABLE #include "debug.h" +/****************************************************************************** + * 广播相关三个标志(务必区分): + * + * 1) usr_auth_data.paired_flag —— 配网结果(存 VM,掉电保持) + * - 0:未配网 + * - 1:已配网(三元组已保存) + * + * 2) usr_var.usr_goto_pair —— 是否主动进入配网流程(RAM,掉电丢失) + * - 0:非配网流程 + * - 1:MCU 0x10 / usr_goto_pair_mode() 触发,发【绑定广播】 + * + * 3) usr_var.usr_adv_flag —— 广播内容是否需要刷新到协议栈(一次性) + * - 1:usr_ble_adv_updata() 会重新 set adv data 并 enable + * - 0:本周期不刷新 + * + * 决策表(身份就绪后): + * usr_goto_pair==1 → 绑定广播(重新配网) + * paired_flag==0 且未配网 → 不广播 + * paired_flag==1 → 回连广播 + ******************************************************************************/ + extern u8 muti_adv_data[31];//={0}; extern u8 muti_adv_data_len; @@ -46,6 +68,10 @@ extern u8 usr_get_work_status(); extern void usr_printf_sta(); extern int muti_make_set_adv_data_updata(); +/** + * @brief 从 VM 读取配对信息到 usr_auth_data + * @note 失败则清零,并将 paired_flag 置 0 写回 + */ void usr_get_pair_info() { int ret = 0xffff; @@ -53,46 +79,69 @@ void usr_get_pair_info() if(ret<=0) { memset(&usr_auth_data,0x00,sizeof(usr_auth_data)); - usr_auth_data.paired_flag=0; + usr_auth_data.paired_flag=0; /* 未配网 */ syscfg_write(CFG_USER_PAIR_INFO, (u8 *)&usr_auth_data, sizeof(usr_auth_data)); } } + +/** + * @brief 标记已配网并保存三元组到 VM + * @note AUTHSETUP 成功后调用,paired_flag = 1 + */ void usr_write_pair_info() { - usr_auth_data.paired_flag=1; + usr_auth_data.paired_flag=1; /* 已配网 */ syscfg_write(CFG_USER_PAIR_INFO, (u8 *)&usr_auth_data, sizeof(usr_auth_data)); } +/** + * @brief 清除配对信息并延时复位 + */ void usr_clear_pair_info() { printf("usr_clear_pair_info\n"); memset(&usr_auth_data,0x00,sizeof(usr_auth_data)); - usr_auth_data.paired_flag=0; + usr_auth_data.paired_flag=0; /* 回到未配网 */ syscfg_write(CFG_USER_PAIR_INFO, (u8 *)&usr_auth_data, sizeof(usr_auth_data)); + mcu_bridge_on_pair_cleared(); sys_timeout_add(NULL,cpu_reset,600); } + +/** + * @brief 清除配对信息但不复位(用于重新配网) + */ void usr_clear_pair_info_noreset() { printf("usr_clear_pair_info2\n"); memset(&usr_auth_data,0x00,sizeof(usr_auth_data)); - usr_auth_data.paired_flag=0; + usr_auth_data.paired_flag=0; /* 回到未配网,后续由 usr_goto_pair 决定是否绑广播 */ syscfg_write(CFG_USER_PAIR_INFO, (u8 *)&usr_auth_data, sizeof(usr_auth_data)); + mcu_bridge_on_pair_cleared(); } extern void usr_set_adv_interval(u16 val); extern u16 usr_get_adv_interval(); extern void ble_multi_trans_disconnect(void); +extern void ble_trans_module_enable(u8 flag); void usr_ble_adv_init(); + +/** + * @brief 进入配网模式:清配对 + 置 usr_goto_pair + 发绑定广播 + * @note MCU 0x10 配网请求最终会走到这里 + */ void usr_goto_pair_mode() { ble_multi_trans_disconnect(); usr_clear_pair_info_noreset(); - usr_var.usr_goto_pair=1; + usr_var.usr_goto_pair=1; /* 进入主动配网流程 → 绑定广播 */ os_time_dly(5); usr_ble_adv_init(); } - +/** + * @brief 按当前标志刷新广播策略(不一定开广播) + * @note 见文件头「三个标志」说明;未拿身份 / 未配网时保持不广播 + */ void usr_ble_adv_init() { int8_t txpower = 0; @@ -101,36 +150,48 @@ void usr_ble_adv_init() uint8_t gmac[6] = {0}; uint8_t pcode[BLE_PROTO_ADV_LENGTH_PCODE]; - /* 产品身份由应用层 g_usr_le_* 提供(见 usr_jb_main.c) */ + /* 产品身份由 MCU 桥写入 g_usr_le_* */ memcpy(prodcode, g_usr_le_prodcode, BLEPROTO_ADV_TLV_LENGTH_PRODCODE); memcpy(pcode, g_usr_le_pcode, BLE_PROTO_ADV_LENGTH_PCODE); usr_set_adv_interval(80); if(usr_var.usr_ota_succ_flag)return; - //muti_adv_data - if(usr_var.usr_goto_pair) - { - /* app_main 上电已置 usr_goto_pair=1 并清配对信息,此处发绑定广播供网关配网 */ - usr_clear_pair_info_noreset(); - printf("power on force to par flag= %d\n",usr_auth_data.paired_flag); - { - muti_adv_data_len=bleproto_advdata_encode4bind(muti_adv_data,txpower,manucode,pcode,prodcode); + + /* 未拿到 MCU 身份:完全不广播 */ + if (!mcu_bridge_is_identity_ready()) { + printf("usr_ble_adv_init: wait MCU identity, no adv\n"); + usr_var.usr_adv_flag = 0; /* 不请求刷新广播 */ + ble_trans_module_enable(0); + if (le_timer_handle == 0) { + le_timer_handle = sys_timer_add(NULL, usr_ble_adv_updata, 500); } - usr_var.usr_adv_flag=1; + return; + } + + if(usr_var.usr_goto_pair) + { + /* 主动配网中:发绑定广播,请求刷新到协议栈 */ + usr_clear_pair_info_noreset(); + printf("goto pair bind adv, paired_flag= %d\n",usr_auth_data.paired_flag); + muti_adv_data_len=bleproto_advdata_encode4bind(muti_adv_data,txpower,manucode,pcode,prodcode); + usr_var.usr_adv_flag=1; /* 请求:写入绑定广播并开启 */ } else { usr_get_pair_info(); if(usr_auth_data.paired_flag==0) { - muti_adv_data_len=bleproto_advdata_encode4bind(muti_adv_data,txpower,manucode,pcode,prodcode); + /* 未配网且未进入配网流程:完全不广播 */ + printf("usr_ble_adv_init: not paired, no adv\n"); usr_var.usr_adv_flag=0; + ble_trans_module_enable(0); } else { + /* 已配网:回连广播 */ memcpy(gmac,usr_auth_data.gwmac,6); muti_adv_data_len=bleproto_advdata_encode4reconn(muti_adv_data,txpower,gmac,manucode,pcode,prodcode); - usr_var.usr_adv_flag=1; + usr_var.usr_adv_flag=1; /* 请求:写入回连广播并开启 */ } } @@ -138,6 +199,18 @@ void usr_ble_adv_init() if(le_timer_handle==0)le_timer_handle = sys_timer_add(NULL, usr_ble_adv_updata, 500); } +/** + * @brief 获取是否已配网 + * @return 1=已配网(paired_flag),0=未配网 + */ +/** + * @brief 获取是否已配网 + * @return 1=已配网(paired_flag),0=未配网 + */ +/** + * @brief 获取是否已配网 + * @return paired_flag:1=已配网,0=未配网 + */ u8 usr_get_pair_flag() { return usr_auth_data.paired_flag; @@ -154,9 +227,12 @@ void usr_reconn_adv() muti_adv_data_len=bleproto_advdata_encode4reconn(muti_adv_data,txpower,gmac,manucode,pcode,prodcode); usr_adv_flag=1; }*/ -extern void ble_trans_module_enable(u8 flag); -extern void usr_timer_loop(); extern u8 usr_get_ota_status(); + +/** + * @brief 周期处理广播刷新(由 500ms 定时器调用) + * @note 仅当 usr_adv_flag==1 时:停广播 → 写入 muti_adv_data → 再开广播,并清 usr_adv_flag + */ void usr_ble_adv_updata() { //printf("usr_ble_adv_updata\n"); @@ -164,7 +240,7 @@ void usr_ble_adv_updata() if(usr_var.usr_adv_flag) { ble_trans_module_enable(0); - usr_var.usr_adv_flag=0; + usr_var.usr_adv_flag=0; /* 一次性触发,用完即清 */ muti_make_set_adv_data(muti_adv_data,muti_adv_data_len); printf("###muti_adv_data = "); printf_buf(muti_adv_data,muti_adv_data_len); @@ -208,9 +284,6 @@ void usr_ble_adv_updata() if(usr_var.usr_ota_succ_flag==0)usr_ota_exit(); } } - - /* 协议层周期处理(上报/应答) */ - usr_timer_loop(); } void usr_soft_off() diff --git a/JBao_JL7016_MOD_FW/apps/usr_le_code/usr_le_api.h b/JBao_JL7016_MOD_FW/apps/usr_le_code/usr_le_api.h index 69ef648..71e3b9c 100644 --- a/JBao_JL7016_MOD_FW/apps/usr_le_code/usr_le_api.h +++ b/JBao_JL7016_MOD_FW/apps/usr_le_code/usr_le_api.h @@ -1,11 +1,12 @@ /****************************************************************************** * @file usr_le_api.h - * @brief usr_le_code 对外 API(广播 / 收发 / 配网) + * @brief usr_le_code 对外 API(广播 / 收发 / 配网 / MCU 帧转发) * @author cyWu <1917507415@qq.com> - * @date 2026.08.03 - * @version V1.0.0 + * @date 2026.08.06 + * @version V1.1.0 * @history * - V1.0.0, 2026.08.03, cyWu, 首次发布 + * - V1.1.0, 2026.08.06, cyWu, 增加 MCU 帧 BLE 发送接口,解耦模组桥 ******************************************************************************/ #ifndef __USR_LE_API_H__ @@ -18,8 +19,12 @@ extern "C" { #endif /** - * @brief 初始化 BLE 广播数据(绑定广播或重连广播) - * @note 根据配对状态与 usr_goto_pair 选择 bind/reconn 广播,并启动 500ms 周期更新 + * @brief 初始化/刷新 BLE 广播策略(不一定开启广播) + * @note 决策依赖三个标志: + * - paired_flag:是否已配网(VM) + * - usr_goto_pair:是否主动配网中(发绑定广播) + * - usr_adv_flag:是否请求把新广播包刷到协议栈 + * 详见 usr_le_adv.c 文件头注释 */ void usr_ble_adv_init(void); @@ -27,18 +32,25 @@ void usr_ble_adv_init(void); * @brief BLE 协议栈接收数据入口 * @param data 接收缓冲区 * @param len 本次接收长度 - * @note 组包后解析 bleproto,并分发 DEVICEINFO/AUTH/RUNCMD/OTA 等服务 */ void usr_le_data_recieve(uint8_t *data, uint16_t len); +/** + * @brief 将 MCU 协议 55AA 帧封装为 bleproto 后经 GATT 发送 + * @param data 完整 55AA 帧 + * @param len 帧长度 + * @note 0x07/0x34/0x38 走 REPORTCMD_V2 主动上报;其余走 RUNCMD_V2 应答 + */ +void usr_le_send_mcu_frame(uint8_t *data, uint16_t len); + /** * @brief 进入配对/配网模式 - * @note 断开当前连接、清除配对信息(不复位),重新发绑定广播 + * @note 清配对信息,置 usr_goto_pair=1,发绑定广播(非回连) */ void usr_goto_pair_mode(void); /** - * @brief 清除配对信息但不复位 + * @brief 清除配对信息但不复位(paired_flag→0) */ void usr_clear_pair_info_noreset(void); @@ -48,14 +60,13 @@ void usr_clear_pair_info_noreset(void); void usr_clear_pair_info(void); /** - * @brief 获取当前是否已配对 - * @return 1=已配对,0=未配对 + * @brief 获取是否已配网 + * @return paired_flag:1=已配网,0=未配网 */ uint8_t usr_get_pair_flag(void); /** * @brief 启动应用侧 OTA 流程 - * @note 置位 usr_goto_updata,并创建 1s 周期定时器驱动断连与 OTA 广播切换 */ void usr_app_ota_init(void); diff --git a/JBao_JL7016_MOD_FW/apps/usr_le_code/usr_le_product.h b/JBao_JL7016_MOD_FW/apps/usr_le_code/usr_le_product.h index f4bb613..58cae62 100644 --- a/JBao_JL7016_MOD_FW/apps/usr_le_code/usr_le_product.h +++ b/JBao_JL7016_MOD_FW/apps/usr_le_code/usr_le_product.h @@ -1,11 +1,12 @@ /****************************************************************************** * @file usr_le_product.h - * @brief usr_le_code 产品身份配置接口(由应用层提供符号,模块内不写死) + * @brief usr_le_code 产品身份配置接口(由 MCU 桥运行时填充) * @author cyWu <1917507415@qq.com> * @date 2026.08.03 - * @version V1.0.0 + * @version V1.1.0 * @history * - V1.0.0, 2026.08.03, cyWu, 首次发布 + * - V1.1.0, 2026.08.06, cyWu, 改为非 const,由 mcu_bridge 从 MCU 0x02 写入 ******************************************************************************/ #ifndef __USR_LE_PRODUCT_H__ @@ -29,15 +30,15 @@ extern "C" { /** * @brief 产品类别码,对应 BLE 广播 prodcode(固定 4 字节 ASCII) - * @note 应用层用 JB_CATEGORY_CODE 填充,见 usr_jb_main.c + * @note 由 mcu_bridge 从 MCU 身份应答取前 4 字节写入 */ -extern const uint8_t g_usr_le_prodcode[USR_LE_PRODCODE_LEN]; +extern uint8_t g_usr_le_prodcode[USR_LE_PRODCODE_LEN]; /** * @brief 平台产品码,对应 BLE 广播 pcode(固定 6 字节 ASCII) - * @note 应用层用 JB_PRODUCT_CODE 填充,见 usr_jb_main.c + * @note 由 mcu_bridge 从 MCU 身份应答取前 6 字节写入 */ -extern const uint8_t g_usr_le_pcode[USR_LE_PCODE_LEN]; +extern uint8_t g_usr_le_pcode[USR_LE_PCODE_LEN]; #ifdef __cplusplus } diff --git a/JBao_JL7016_MOD_FW/apps/usr_le_code/usr_le_recieve.c b/JBao_JL7016_MOD_FW/apps/usr_le_code/usr_le_recieve.c index 5a28e16..81245f9 100644 --- a/JBao_JL7016_MOD_FW/apps/usr_le_code/usr_le_recieve.c +++ b/JBao_JL7016_MOD_FW/apps/usr_le_code/usr_le_recieve.c @@ -16,6 +16,7 @@ #include "bleproto.h" #include "bleproto_api.h" #include "ble_proto.h" +#include "mcu_frame.h" #define LOG_TAG_CONST APP #define LOG_TAG "[APP]" #define LOG_ERROR_ENABLE @@ -43,8 +44,12 @@ extern void app_audio_volume_set(u8 value); extern int dual_ota_app_data_deal(u32 msg, u8 *buf, u32 len); extern int uart_tr_send_data(u8 *data, u32 len); extern void usr_intterupt_close(); -extern void usr_jb_le_recieve_data(u8 *data, u16 len); -extern int8_t jbReportAckCheck(u16 cmd, u8 gateway_int_cnt, int32_t gateway_int0); +extern void mcu_bridge_on_paired(void); +extern void mcu_bridge_on_pair_cleared(void); +extern void mcu_bridge_on_app_frame(const uint8_t *data, uint16_t len); +extern void mcu_bridge_on_gateway_report_ack(uint16_t cmd, + uint8_t gateway_int_cnt, + int32_t gateway_int0); void usr_ota_deal_task(); void usr_data_ana(bleproto_appdata_desc_t *desc); @@ -61,6 +66,14 @@ void usr_le_data_recieve(u8 *data, u16 len) /// BLE数据接收 int rc_result = 0; + if ((u32)usr_len + len > sizeof(rxbuf)) + { + /* 累积超界:多半是丢包/半包导致 decode 一直返回 0,防止缓冲区溢出 */ + printf("### rxbuf overflow, usr_len=%u len=%u, reset\n", usr_len, len); + usr_len = 0; + return; + } + memcpy(&rxbuf[usr_len], data, len); usr_len += len; rc_result = bleproto_appdata_decode(rxbuf, usr_len, &desc); @@ -149,6 +162,7 @@ void usr_data_ana(bleproto_appdata_desc_t *desc) if (usr_auth_data.paired_flag == 0) { usr_write_pair_info(); + mcu_bridge_on_paired(); printf("usr_auth_data %s %s ", usr_auth_data.authcode, usr_auth_data.devid); printf_buf(usr_auth_data.gwmac, 6); } @@ -305,16 +319,16 @@ void usr_data_ana(bleproto_appdata_desc_t *desc) break; case E_BLEPROTO_SERVICE_ID_REPORTCMD_V2: { - /* 网关对主动上报 / OTA 的应答(非 55AA): - * cmd==0 && gateway_int_cnt==1 && gateway_int[0]==0 → 应答成功 */ + /* 网关对 MCU 主动上报的应答:成功后由桥接层回 MCU 0x08 */ ble_proto_packet_t *rpt_ack = &desc->datast.reportcmd_v2_rsp; printf("E_BLEPROTO_SERVICE_ID_REPORTCMD_V2 cmd=%u cnt=%u int0=%ld\n", (unsigned)rpt_ack->cmd, (unsigned)rpt_ack->gateway_int_cnt, (long)((rpt_ack->gateway_int_cnt > 0) ? rpt_ack->gateway_int[0] : 0)); - jbReportAckCheck(rpt_ack->cmd, - rpt_ack->gateway_int_cnt, - (rpt_ack->gateway_int_cnt > 0) ? rpt_ack->gateway_int[0] : -1); + mcu_bridge_on_gateway_report_ack( + rpt_ack->cmd, + rpt_ack->gateway_int_cnt, + (rpt_ack->gateway_int_cnt > 0) ? rpt_ack->gateway_int[0] : -1); break; } case E_BLEPROTO_SERVICE_ID_OTADATA: @@ -431,7 +445,7 @@ void usr_ota_deal_task() static u8 s_last_runcmd_msgid = 0; void usr_run_cmd_code_v2(ble_proto_packet_t *runcmd_par, u8 usr_msg_id) { - u8 jb_frame[128]; /* 与 jb_protocol.h MAX_PACKAGE_LEN 一致 */ + u8 mcu_frame[MCU_FRAME_MAX_LEN]; u16 copy_len; if (!runcmd_par || !runcmd_par->bytes_data || runcmd_par->device_bytes_len == 0) @@ -444,45 +458,40 @@ void usr_run_cmd_code_v2(ble_proto_packet_t *runcmd_par, u8 usr_msg_id) printf("runcmd_par id = %d, bytes_len = %u\n", runcmd_par->cmd, runcmd_par->device_bytes_len); - /* 先拷到本地再入 JB 环缓,避免零拷贝指针被后续逻辑覆盖 */ copy_len = runcmd_par->device_bytes_len; - if (copy_len > sizeof(jb_frame)) + if (copy_len > sizeof(mcu_frame)) { printf("usr_run_cmd_code_v2 bytes too long %u\n", copy_len); return; } - memcpy(jb_frame, runcmd_par->bytes_data, copy_len); + memcpy(mcu_frame, runcmd_par->bytes_data, copy_len); s_last_runcmd_msgid = usr_msg_id; - usr_jb_le_recieve_data(jb_frame, copy_len); + mcu_bridge_on_app_frame(mcu_frame, copy_len); } /** - * @brief 将 JB 协议帧封装为 bleproto V2 后通过 BLE 发送 - * @param data JB 协议原始帧(55 AA | LEN | CMD | ...) + * @brief 将 MCU 协议帧封装为 bleproto V2 后通过 BLE 发送 + * @param data 55AA 完整帧 * @param len 帧长度 - * @note 根据 JB CMD 区分主动上报 / 应答: - * - 主动(0x07/0x34/0x38): REPORTCMD_V2 + REQ + cmd=100 - * - 应答(其余): RUNCMD_V2 + RSP + cmd=110 + * @note 主动上报(0x07/0x34/0x38) → REPORTCMD_V2;其余 → RUNCMD_V2 应答 */ -void ble_send_data(u8 *data, u16 len) +void usr_le_send_mcu_frame(u8 *data, u16 len) { bleproto_appdata_desc_t encode_desc = {0}; ble_proto_packet_t *pkt = NULL; - u8 jb_cmd; + u8 mcu_cmd; u8 is_active_send; int elen; if (!data || len < 5) { - printf("ble_send_data invalid param, len=%d\n", len); + printf("usr_le_send_mcu_frame invalid param, len=%d\n", len); return; } - /* JB帧: 55 AA | LEN_H LEN_L | CMD | SN | ...,CMD 在偏移 4 */ - jb_cmd = data[4]; - /* 主动上报 0x07;MCU 主动 OTA 完成/停止 0x34/0x38;其余按应答 */ - is_active_send = (jb_cmd == 0x07) || (jb_cmd == 0x34) || (jb_cmd == 0x38); + mcu_cmd = data[4]; + is_active_send = (mcu_cmd == 0x07) || (mcu_cmd == 0x34) || (mcu_cmd == 0x38); encode_desc.header.version = 0; encode_desc.header.datafmt = BLEPROTO_APPDATA_DATAFMT_JSON; @@ -490,7 +499,6 @@ void ble_send_data(u8 *data, u16 len) if (is_active_send) { - /* 主动发送:REPORTCMD_V2 / REQ / cmd=100 */ encode_desc.header.msgtype = BLEPROTO_APPDATA_MSGTYPE_REQ; encode_desc.header.msgid = 0; encode_desc.header.serviceid = E_BLEPROTO_SERVICE_ID_REPORTCMD_V2; @@ -502,7 +510,6 @@ void ble_send_data(u8 *data, u16 len) } else { - /* 应答命令:RUNCMD_V2 / RSP / cmd=110,回填最新 msgid */ encode_desc.header.msgtype = BLEPROTO_APPDATA_MSGTYPE_RSP; encode_desc.header.msgid = s_last_runcmd_msgid; encode_desc.header.serviceid = E_BLEPROTO_SERVICE_ID_RUNCMD_V2; @@ -521,8 +528,8 @@ void ble_send_data(u8 *data, u16 len) pkt->bytes_data = data; elen = bleproto_appdata_encode(txbuf, sizeof(txbuf), sizeof(txbuf), &encode_desc); - printf("ble_send_data jb_cmd=0x%02X active=%d encode_len=%d msgid=%d\n", - jb_cmd, is_active_send, elen, encode_desc.header.msgid); + printf("usr_le_send_mcu_frame cmd=0x%02X active=%d encode_len=%d msgid=%d\n", + mcu_cmd, is_active_send, elen, encode_desc.header.msgid); if (elen > 0) { usr_ble_send_data(txbuf, elen); diff --git a/JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_bridge.c b/JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_bridge.c new file mode 100644 index 0000000..709581d --- /dev/null +++ b/JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_bridge.c @@ -0,0 +1,1090 @@ +/****************************************************************************** + * @file mcu_bridge.c + * @brief BLE+MCU 协议桥 P0 实现 + * @author cyWu <1917507415@qq.com> + * @date 2026.08.06 + * @version V1.2.0 + * @history + * - V1.0.0, 2026.08.06, cyWu, 首次发布 + * - V1.1.0, 2026.08.06, cyWu, 改为事件队列 + 独立任务驱动, + * 彻底消除跨任务共享状态的竞态(UART 接收任务 / BLE 协议栈任务 / + * 原 sys_timer 回调三方并发访问 s_br 的问题) + * - V1.2.0, 2026.08.10, cyWu, 注册 LP target:桥接忙时禁止进低功耗, + * 空闲可睡,配合板级 SLEEP_EN 恢复串口方案下的低功耗 + ******************************************************************************/ + +#include "system/includes.h" +#include "asm/power/power_api.h" +#include "app_main.h" +#include "mcu_bridge.h" +#include "mcu_uart.h" +#include "mcu_frame.h" +#include "usr_le_api.h" +#include "usr_le_product.h" +#include "mcu_util.h" + +#define LOG_TAG_CONST APP +#define LOG_TAG "[MCU_BR]" +#define LOG_ERROR_ENABLE +#define LOG_DEBUG_ENABLE +#define LOG_INFO_ENABLE +#define LOG_DUMP_ENABLE +#define LOG_CLI_ENABLE +#include "debug.h" + +/*============================================================================*/ +/* 私有宏 */ +/*============================================================================*/ + +#define MCU_IDENTITY_RETRY_MS (1000u) /* 0x01 轮询间隔 */ +#define MCU_HEARTBEAT_IDLE_MS (55000u) /* 无 MCU 数据超时发心跳 */ +#define MCU_HEARTBEAT_WAIT_MS (2500u) /* 心跳应答等待 */ +#define MCU_HEARTBEAT_MAX_RETRY (3u) +#define MCU_APP_ACK_WAIT_MS (2500u) /* APP 透传等 MCU ACK */ +#define MCU_APP_ACK_MAX_RETRY (3u) +#define MCU_REPORT_GW_WAIT_MS (3000u) /* MCU 上报等网关 ACK */ +#define MCU_REPORT_GW_MAX_RETRY (3u) +#define MCU_IDENTITY_PAYLOAD_MIN (2 + 8 + 8 + 16 + 8) /* Ver+Cat+Prod+SN+Cap */ + +#define MCU_BRIDGE_TICK_MS (200u) /* 期望的兜底轮询周期(毫秒) */ +/* 杰理 os_sem_pend 超时单位是 OS tick,OS_TICKS_PER_SEC=100 → 1 tick = 10ms */ +#define MCU_BRIDGE_TICK_OS ((MCU_BRIDGE_TICK_MS + 9u) / 10u) +#define MCU_EVT_QUEUE_DEPTH (8u) /* 事件队列深度 */ +#define MCU_BRIDGE_TASK_PRIO (31u) +#define MCU_BRIDGE_TASK_STACK (1024u) + +/*============================================================================*/ +/* 外部符号 */ +/*============================================================================*/ + +extern void ble_trans_module_enable(u8 flag); +extern u16 usr_get_conn_service(void); + +/* 运行时可改写的产品身份(供广播使用) */ +uint8_t g_usr_le_prodcode[USR_LE_PRODCODE_LEN]; +uint8_t g_usr_le_pcode[USR_LE_PCODE_LEN]; + +/*============================================================================*/ +/* 私有类型 */ +/*============================================================================*/ + +typedef struct { + uint8_t active; + uint8_t sn; + uint8_t expect_ack; /* 期望的 ACK 命令 */ + uint8_t retry; + uint32_t send_time; + uint8_t buf[MCU_FRAME_MAX_LEN]; + uint16_t len; +} McuPending_t; + +/** + * @brief 桥接层内部事件类型 + * @note 所有跨任务(UART 接收任务 / BLE 协议栈任务)产生的输入, + * 都封装为事件投递到队列,只在 mcu_bridge_task 单一上下文中 + * 消费处理,从而避免对 s_br 的并发访问。 + */ +typedef enum { + MCU_EVT_UART_FRAME = 0, /* MCU 经 UART 送来的完整合法帧 */ + MCU_EVT_APP_FRAME, /* APP/网关经 BLE 下发的 55AA 帧 */ + MCU_EVT_GATEWAY_ACK, /* 网关对 MCU 上报(0x07)的应答 */ + MCU_EVT_BLE_PAIRED, /* AUTHSETUP 成功(配网完成) */ + MCU_EVT_BLE_PAIR_CLEARED /* 配对信息被清除 */ +} McuEvtType_t; + +typedef struct { + McuEvtType_t type; + uint16_t len; + uint8_t buf[MCU_FRAME_MAX_LEN]; + uint16_t gw_cmd; + uint8_t gw_int_cnt; + int32_t gw_int0; +} McuEvt_t; + +typedef struct { + uint8_t initialized; + uint8_t identity_ready; + uint8_t identity_adv_pending; /* 身份刚就绪,待刷新广播 */ + uint8_t sn; + uint8_t work_status; + uint8_t last_pushed_status; + uint8_t hb_fail_cnt; + uint8_t hb_waiting; + uint8_t mcu_comm_alarm; + uint8_t last_ble_conn; + uint16_t pair_timeout_s; + uint32_t pair_start_ms; + uint32_t last_mcu_rx_ms; + uint32_t last_identity_tx_ms; + uint32_t last_hb_tx_ms; + McuPending_t app_pending; /* APP→MCU 透传等待 */ + McuPending_t ble_pending; /* BLE 主动请求等待(心跳/状态等) */ + McuPending_t report_pending; /* MCU 0x07 上报,等网关 ACK 后再回 0x08 */ +} McuBridge_t; + +/*============================================================================*/ +/* 私有变量 */ +/*============================================================================*/ + +/* 业务状态:仅由 mcu_bridge_task 单一任务上下文读写,天然无竞态 */ +static McuBridge_t s_br; +static uint8_t s_parse_out[MCU_FRAME_MAX_LEN]; + +/* + * 低功耗空闲查询:桥接任务处理事件/周期逻辑时置 1,禁止进 sleep; + * pend 等待期间置 0,允许系统进入低功耗。由 idle 任务跨上下文读取,故用 volatile。 + */ +static volatile u8 s_bridge_busy = 0; + +/** + * @brief 事件队列:环形缓冲 + 同步原语的集合 + * @note 生产者可来自任意任务(UART 接收任务 / BLE 协议栈任务), + * 消费者固定为 mcu_bridge_task。lock 只保护本结构体自身的 + * 入队/出队操作,不涉及业务逻辑,因此临界区极短。 + */ +typedef struct { + McuEvt_t queue[MCU_EVT_QUEUE_DEPTH]; + uint8_t head; /* 下一个可取出的位置 */ + uint8_t tail; /* 下一个可写入的位置 */ + uint8_t count; /* 当前事件个数 */ + OS_MUTEX lock; /* 保护 queue/head/tail/count 四个字段 */ + OS_SEM sem; /* 有新事件入队时唤醒 mcu_bridge_task */ +} McuEvtQueue_t; + +static McuEvtQueue_t s_evt_q; + +/*============================================================================*/ +/* 私有函数声明 */ +/*============================================================================*/ + +static uint8_t mcu_bridge_next_sn(void); +static int32_t mcu_bridge_send_frame(uint8_t cmd, uint8_t sn, + const uint8_t *payload, uint16_t plen); +static void mcu_bridge_send_ack(uint8_t cmd, uint8_t sn, uint8_t result); +static void mcu_bridge_handle_frame(const uint8_t *frame, uint16_t len); +static void mcu_bridge_handle_identity_ack(const uint8_t *frame, uint16_t len); +static void mcu_bridge_handle_pair_cfg(const uint8_t *frame, uint16_t len); +static void mcu_bridge_handle_report(const uint8_t *frame, uint16_t len); +static void mcu_bridge_handle_app_frame(const uint8_t *data, uint16_t len); +static void mcu_bridge_handle_gateway_ack(uint16_t cmd, uint8_t gateway_int_cnt, + int32_t gateway_int0); +static void mcu_bridge_handle_paired(void); +static void mcu_bridge_handle_pair_cleared(void); +static void mcu_bridge_try_identity(void); +static void mcu_bridge_try_heartbeat(void); +static void mcu_bridge_try_work_status(void); +static void mcu_bridge_update_conn_bits(void); +static void mcu_bridge_check_pair_timeout(void); +static void mcu_bridge_check_app_pending(void); +static void mcu_bridge_check_ble_pending(void); +static void mcu_bridge_check_report_pending(void); +static void mcu_bridge_set_ws_bit(uint8_t bit, uint8_t on); +static void mcu_bridge_periodic(void); +static uint8_t mcu_bridge_post_event(const McuEvt_t *evt); +static uint8_t mcu_bridge_pop_event(McuEvt_t *evt); +static void mcu_bridge_dispatch_event(const McuEvt_t *evt); +static void mcu_bridge_task(void *arg); + +/*============================================================================*/ +/* 私有函数 */ +/*============================================================================*/ + +/** + * @brief 投递一个事件到队列并唤醒 mcu_bridge_task + * @param evt 事件内容(按值拷贝入队) + * @return 1=成功,0=队列已满被丢弃 + * @note 可从任意任务上下文调用(UART 接收任务 / BLE 协议栈任务等) + */ +static uint8_t mcu_bridge_post_event(const McuEvt_t *evt) +{ + uint8_t ok = 0; + + os_mutex_pend(&s_evt_q.lock, 0); + if (s_evt_q.count < MCU_EVT_QUEUE_DEPTH) { + s_evt_q.queue[s_evt_q.tail] = *evt; + s_evt_q.tail = (uint8_t)((s_evt_q.tail + 1) % MCU_EVT_QUEUE_DEPTH); + s_evt_q.count++; + ok = 1; + } + os_mutex_post(&s_evt_q.lock); + + if (ok) { + os_sem_post(&s_evt_q.sem); + } else { + log_error("event queue full, drop type=%d", evt->type); + } + return ok; +} + +/** + * @brief 从队列取出一个事件 + * @param evt 输出事件 + * @return 1=取到,0=队列为空 + * @note 仅由 mcu_bridge_task 调用 + */ +static uint8_t mcu_bridge_pop_event(McuEvt_t *evt) +{ + uint8_t ok = 0; + + os_mutex_pend(&s_evt_q.lock, 0); + if (s_evt_q.count > 0) { + *evt = s_evt_q.queue[s_evt_q.head]; + s_evt_q.head = (uint8_t)((s_evt_q.head + 1) % MCU_EVT_QUEUE_DEPTH); + s_evt_q.count--; + ok = 1; + } + os_mutex_post(&s_evt_q.lock); + return ok; +} + +/** + * @brief 事件分发:将队列事件转交给对应业务处理函数 + * @param evt 事件 + * @note 仅在 mcu_bridge_task 上下文执行,s_br 在此单一上下文内读写 + */ +static void mcu_bridge_dispatch_event(const McuEvt_t *evt) +{ + switch (evt->type) { + case MCU_EVT_UART_FRAME: + mcu_bridge_handle_frame(evt->buf, evt->len); + break; + case MCU_EVT_APP_FRAME: + mcu_bridge_handle_app_frame(evt->buf, evt->len); + break; + case MCU_EVT_GATEWAY_ACK: + mcu_bridge_handle_gateway_ack(evt->gw_cmd, evt->gw_int_cnt, evt->gw_int0); + break; + case MCU_EVT_BLE_PAIRED: + mcu_bridge_handle_paired(); + break; + case MCU_EVT_BLE_PAIR_CLEARED: + mcu_bridge_handle_pair_cleared(); + break; + default: + log_error("unknown evt type=%d", evt->type); + break; + } +} + +/** + * @brief 桥接层主任务:事件驱动 + 兜底轮询 + * @param arg 未使用 + * @note 唯一读写 s_br 的任务上下文;醒来时机 = 有新事件 或 每 + * MCU_BRIDGE_TICK_MS 兜底一次,用于身份轮询/心跳/超时重传等。 + * 处理期间通过 LP target 挡住系统休眠,避免 UART TX/DMA 异常。 + */ +static void mcu_bridge_task(void *arg) +{ + McuEvt_t evt; + + (void)arg; + log_info("bridge task start"); + + while (1) { + /* 有事件立刻醒;空闲时约每 MCU_BRIDGE_TICK_MS 醒一次做周期检查 */ + os_sem_pend(&s_evt_q.sem, (int)MCU_BRIDGE_TICK_OS); + + s_bridge_busy = 1; + while (mcu_bridge_pop_event(&evt)) { + mcu_bridge_dispatch_event(&evt); + } + mcu_bridge_periodic(); + s_bridge_busy = 0; + } +} + +/** + * @brief 低功耗空闲查询:桥接忙则不允许进 sleep + * @return 1=可进低功耗,0=保持唤醒 + */ +static u8 mcu_bridge_lp_idle_query(void) +{ + return s_bridge_busy ? 0 : 1; +} + +REGISTER_LP_TARGET(mcu_bridge_lp_target) = { + .name = "mcu_bridge", + .is_idle = mcu_bridge_lp_idle_query, +}; + +/** + * @brief 取下一个序列号 + * @return SN + */ +static uint8_t mcu_bridge_next_sn(void) +{ + return s_br.sn++; +} + +/** + * @brief 组帧并通过 UART 发给 MCU + * @param cmd 命令 + * @param sn 序列号 + * @param payload 数据 + * @param plen 长度 + * @return 帧长;<0 失败 + */ +static int32_t mcu_bridge_send_frame(uint8_t cmd, uint8_t sn, + const uint8_t *payload, uint16_t plen) +{ + uint8_t buf[MCU_FRAME_MAX_LEN]; + int32_t total; + + total = mcu_frame_pack(buf, sizeof(buf), cmd, sn, payload, plen); + if (total <= 0) { + return -1; + } + if (mcu_uart_send(buf, (uint16_t)total) != MCU_UART_OK) { + return -1; + } + log_debug("MCU TX cmd=0x%02X sn=%u len=%d", cmd, sn, (int)total); + return total; +} + +/** + * @brief 发送仅含 Result 的 ACK 帧给 MCU + * @param cmd ACK 命令字 + * @param sn 序列号 + * @param result 结果码 + */ +static void mcu_bridge_send_ack(uint8_t cmd, uint8_t sn, uint8_t result) +{ + mcu_bridge_send_frame(cmd, sn, &result, 1); +} + +/** + * @brief 设置/清除工作状态位 + * @param bit 位掩码 + * @param on 1=置位,0=清位 + */ +static void mcu_bridge_set_ws_bit(uint8_t bit, uint8_t on) +{ + if (on) { + s_br.work_status |= bit; + } else { + s_br.work_status &= (uint8_t)~bit; + } +} + +/** + * @brief 处理 0x02 身份应答 + * @param frame 完整帧 + * @param len 长度 + * @note 载荷布局(Result 之后):Ver(2)+Category(8)+Product(8)+SN(16)+Cap(8) + */ +static void mcu_bridge_handle_identity_ack(const uint8_t *frame, uint16_t len) +{ + if (len < 7) { + return; + } + if (frame[6] != MCU_RESULT_OK) { + log_error("identity ack result=0x%02X", frame[6]); + return; + } + if (len < (7 + MCU_IDENTITY_PAYLOAD_MIN + 1)) { + log_error("identity ack too short %u", len); + return; + } + + /* Category Code 在 Result(frame[6]) 后偏移 Ver(2),取前 4; + * Product Code 再后 Category(8),取前 6 */ + memcpy(g_usr_le_prodcode, &frame[7 + 2], USR_LE_PRODCODE_LEN); + memcpy(g_usr_le_pcode, &frame[7 + 2 + 8], USR_LE_PCODE_LEN); + + s_br.identity_ready = 1; + s_br.identity_adv_pending = 1; + log_info("identity ok prodcode:"); + log_info_hexdump(g_usr_le_prodcode, USR_LE_PRODCODE_LEN); + log_info("identity ok pcode:"); + log_info_hexdump(g_usr_le_pcode, USR_LE_PCODE_LEN); + + /* 置位后由 mcu_bridge_periodic 刷新广播 */ + s_br.last_pushed_status = 0xFF; +} + +/** + * @brief 处理 0x10 配网请求 + * @param frame 完整帧 + * @param len 长度 + */ +static void mcu_bridge_handle_pair_cfg(const uint8_t *frame, uint16_t len) +{ + uint8_t sn; + uint8_t mode; + uint16_t timeout_s; + + if (len < 10) { + return; + } + sn = frame[5]; + mode = frame[6]; + timeout_s = mcu_util_read_u16_be(&frame[7]); + + if (mode != 1 && mode != 2) { + mcu_bridge_send_ack(MCU_CMD_PAIR_CFG_ACK, sn, MCU_RESULT_PARAM_ERR); + return; + } + if (!s_br.identity_ready) { + mcu_bridge_send_ack(MCU_CMD_PAIR_CFG_ACK, sn, MCU_RESULT_BUSY); + return; + } + + s_br.pair_timeout_s = timeout_s; + s_br.pair_start_ms = mcu_util_get_ms(); + mcu_bridge_set_ws_bit(MCU_WS_PAIRING_MODE, 1); + mcu_bridge_set_ws_bit(MCU_WS_CONFIGURED, 0); + + mcu_bridge_send_ack(MCU_CMD_PAIR_CFG_ACK, sn, MCU_RESULT_OK); + + /* 重新配网:清配对并开绑定广播(非回连) */ + log_info("MCU pair cfg mode=%u timeout=%u", mode, timeout_s); + usr_goto_pair_mode(); +} + +/** + * @brief 处理 MCU 主动上报 0x07 + * @param frame 完整帧 + * @param len 长度 + * @note 先转发网关;收到 REPORTCMD_V2 成功应答后再回 MCU 0x08 + */ +static void mcu_bridge_handle_report(const uint8_t *frame, uint16_t len) +{ + uint8_t sn = frame[5]; + + if (s_br.report_pending.active) { + log_error("report busy, drop sn=%u", sn); + mcu_bridge_send_ack(MCU_CMD_REPORT_ACK, sn, MCU_RESULT_BUSY); + return; + } + if (len > sizeof(s_br.report_pending.buf)) { + mcu_bridge_send_ack(MCU_CMD_REPORT_ACK, sn, MCU_RESULT_PARAM_ERR); + return; + } + + memcpy(s_br.report_pending.buf, frame, len); + s_br.report_pending.len = len; + s_br.report_pending.sn = sn; + s_br.report_pending.expect_ack = MCU_CMD_REPORT_ACK; /* 最终要回给 MCU 的命令 */ + s_br.report_pending.retry = 0; + s_br.report_pending.send_time = mcu_util_get_ms(); + s_br.report_pending.active = 1; + + /* 先上报网关,暂不回 MCU 0x08 */ + usr_le_send_mcu_frame(s_br.report_pending.buf, s_br.report_pending.len); + log_debug("MCU report sn=%u -> gateway, wait ACK", sn); +} + +/** + * @brief 分发一帧完整 MCU 协议 + * @param frame 完整帧 + * @param len 长度 + */ +static void mcu_bridge_handle_frame(const uint8_t *frame, uint16_t len) +{ + uint8_t cmd; + uint8_t sn; + + if (!frame || len < 6) { + return; + } + + s_br.last_mcu_rx_ms = mcu_util_get_ms(); + s_br.hb_fail_cnt = 0; + s_br.hb_waiting = 0; + s_br.mcu_comm_alarm = 0; + + cmd = frame[4]; + sn = frame[5]; + log_debug("MCU RX cmd=0x%02X sn=%u len=%u", cmd, sn, len); + + /* APP 透传等待的 ACK */ + if (s_br.app_pending.active && + (cmd == s_br.app_pending.expect_ack) && + (sn == s_br.app_pending.sn)) { + s_br.app_pending.active = 0; + usr_le_send_mcu_frame((u8 *)frame, len); + return; + } + + /* BLE 主动请求的 ACK(心跳/状态) */ + if (s_br.ble_pending.active && + (cmd == s_br.ble_pending.expect_ack) && + (sn == s_br.ble_pending.sn)) { + s_br.ble_pending.active = 0; + if (cmd == MCU_CMD_HEARTBEAT_ACK) { + s_br.hb_waiting = 0; + s_br.hb_fail_cnt = 0; + } + return; + } + + switch (cmd) { + case MCU_CMD_GET_IDENTITY_ACK: + mcu_bridge_handle_identity_ack(frame, len); + break; + case MCU_CMD_PAIR_CFG: + mcu_bridge_handle_pair_cfg(frame, len); + break; + case MCU_CMD_REPORT: + mcu_bridge_handle_report(frame, len); + break; + case MCU_CMD_CONTROL_ACK: + case MCU_CMD_READ_ACK: + /* 无匹配 pending 时仍尝试转发给 APP(容错) */ + usr_le_send_mcu_frame((u8 *)frame, len); + break; + case MCU_CMD_HEARTBEAT_ACK: + case MCU_CMD_WORK_STATUS_ACK: + break; + default: + /* P0 未实现:回不支持(仅对 MCU→BLE 请求类) */ + if (cmd == MCU_CMD_RESET || cmd == MCU_CMD_REBOOT || + cmd == MCU_CMD_GET_TIME || cmd == MCU_CMD_FACTORY || + cmd == MCU_CMD_PHOTO || cmd == MCU_CMD_RECORD || + cmd == MCU_CMD_AUDIO) { + mcu_bridge_send_ack((uint8_t)(cmd + 1), sn, MCU_RESULT_NOT_SUPPORT); + } + break; + } +} + +/** + * @brief 处理 APP/网关经 BLE 下发的 55AA 帧 + * @param data 帧数据 + * @param len 长度 + */ +static void mcu_bridge_handle_app_frame(const uint8_t *data, uint16_t len) +{ + uint8_t cmd; + uint8_t sn; + uint8_t expect_ack; + + cmd = data[4]; + sn = data[5]; + + if (cmd == MCU_CMD_CONTROL) { + expect_ack = MCU_CMD_CONTROL_ACK; + } else if (cmd == MCU_CMD_READ) { + expect_ack = MCU_CMD_READ_ACK; + } else { + /* 其它命令 P0:原样转发,不等待(后续 OTA 等再扩展) */ + mcu_uart_send(data, len); + return; + } + + if (s_br.app_pending.active) { + log_error("app frame busy, drop cmd=0x%02X", cmd); + return; + } + + memcpy(s_br.app_pending.buf, data, len); + s_br.app_pending.len = len; + s_br.app_pending.sn = sn; + s_br.app_pending.expect_ack = expect_ack; + s_br.app_pending.retry = 0; + s_br.app_pending.send_time = mcu_util_get_ms(); + s_br.app_pending.active = 1; + + mcu_uart_send(data, len); + log_debug("app->mcu forward cmd=0x%02X sn=%u wait 0x%02X", + cmd, sn, expect_ack); +} + +/** + * @brief 处理网关对 REPORTCMD_V2 主动上报的应答 + * @param cmd bleproto 包 cmd + * @param gateway_int_cnt gateway_int 个数 + * @param gateway_int0 gateway_int[0] + * @note 成功条件:cmd==0 && cnt==1 && int0==0;成功后才向 MCU 回 0x08 + */ +static void mcu_bridge_handle_gateway_ack(uint16_t cmd, + uint8_t gateway_int_cnt, + int32_t gateway_int0) +{ + uint8_t sn; + + if (!s_br.report_pending.active) { + return; + } + + /* 与旧 SOC jbReportAckCheck 成功条件一致 */ + if (cmd == 0 && gateway_int_cnt == 1 && gateway_int0 == 0) { + sn = s_br.report_pending.sn; + mcu_bridge_send_ack(MCU_CMD_REPORT_ACK, sn, MCU_RESULT_OK); + s_br.report_pending.active = 0; + log_info("gateway report ack ok, MCU 0x08 sn=%u", sn); + return; + } + + log_error("gateway report ack fail cmd=%u cnt=%u int0=%ld", + (unsigned)cmd, (unsigned)gateway_int_cnt, (long)gateway_int0); + /* 失败暂不立刻回 MCU,留给超时重试 */ +} + +/** + * @brief 处理配网成功通知 + */ +static void mcu_bridge_handle_paired(void) +{ + usr_var.usr_goto_pair = 0; + mcu_bridge_set_ws_bit(MCU_WS_CONFIGURED, 1); + mcu_bridge_set_ws_bit(MCU_WS_PAIRING_MODE, 0); + /* Bit1/2/3 预留:INTERNET / APP_ONLINE / BLE_CONNECTED 暂固定为 0 */ + log_info("mcu_bridge_handle_paired"); +} + +/** + * @brief 处理配对信息被清除通知 + */ +static void mcu_bridge_handle_pair_cleared(void) +{ + mcu_bridge_set_ws_bit(MCU_WS_CONFIGURED, 0); +} + +/** + * @brief 身份未就绪时每 1s 发 0x01 + */ +static void mcu_bridge_try_identity(void) +{ + uint32_t now; + uint8_t sn; + int32_t total; + + if (s_br.identity_ready) { + return; + } + + now = mcu_util_get_ms(); + if ((now - s_br.last_identity_tx_ms) < MCU_IDENTITY_RETRY_MS) { + return; + } + s_br.last_identity_tx_ms = now; + + sn = mcu_bridge_next_sn(); + total = mcu_bridge_send_frame(MCU_CMD_GET_IDENTITY, sn, NULL, 0); + if (total > 0) { + log_debug("identity req sn=%u", sn); + } +} + +/** + * @brief 心跳检测与发送 + */ +static void mcu_bridge_try_heartbeat(void) +{ + uint32_t now = mcu_util_get_ms(); + uint8_t sn; + uint8_t buf[MCU_FRAME_MAX_LEN]; + int32_t total; + + if (!s_br.identity_ready) { + return; + } + + if (s_br.hb_waiting) { + if ((now - s_br.last_hb_tx_ms) <= MCU_HEARTBEAT_WAIT_MS) { + return; + } + s_br.hb_waiting = 0; + s_br.ble_pending.active = 0; + s_br.hb_fail_cnt++; + log_error("heartbeat timeout fail=%u", s_br.hb_fail_cnt); + if (s_br.hb_fail_cnt >= MCU_HEARTBEAT_MAX_RETRY) { + s_br.mcu_comm_alarm = 1; + log_error("MCU comm alarm"); + /* 告警后重新计时,避免狂打心跳 */ + s_br.last_mcu_rx_ms = now; + return; + } + /* 未达 3 次:立即再发一帧心跳 */ + } else if ((now - s_br.last_mcu_rx_ms) < MCU_HEARTBEAT_IDLE_MS) { + return; + } + + sn = mcu_bridge_next_sn(); + total = mcu_frame_pack(buf, sizeof(buf), MCU_CMD_HEARTBEAT, sn, NULL, 0); + if (total <= 0) { + return; + } + if (mcu_uart_send(buf, (uint16_t)total) != MCU_UART_OK) { + return; + } + + s_br.ble_pending.active = 1; + s_br.ble_pending.sn = sn; + s_br.ble_pending.expect_ack = MCU_CMD_HEARTBEAT_ACK; + s_br.ble_pending.send_time = now; + s_br.ble_pending.retry = 0; + s_br.last_hb_tx_ms = now; + s_br.hb_waiting = 1; + log_debug("heartbeat tx sn=%u", sn); +} + +/** + * @brief 工作状态变化时推送 0x14 + */ +static void mcu_bridge_try_work_status(void) +{ + uint8_t payload[2]; + uint8_t sn; + uint8_t buf[MCU_FRAME_MAX_LEN]; + int32_t total; + uint32_t now; + + if (!s_br.identity_ready) { + return; + } + if (s_br.work_status == s_br.last_pushed_status) { + return; + } + if (s_br.ble_pending.active) { + return; /* 避免与心跳等抢占 */ + } + + payload[0] = s_br.work_status; + payload[1] = 0x00; /* RSSI 占位,后续可接真实信号强度 */ + + sn = mcu_bridge_next_sn(); + total = mcu_frame_pack(buf, sizeof(buf), MCU_CMD_WORK_STATUS, sn, payload, 2); + if (total <= 0) { + return; + } + if (mcu_uart_send(buf, (uint16_t)total) != MCU_UART_OK) { + return; + } + + now = mcu_util_get_ms(); + s_br.ble_pending.active = 1; + s_br.ble_pending.sn = sn; + s_br.ble_pending.expect_ack = MCU_CMD_WORK_STATUS_ACK; + s_br.ble_pending.send_time = now; + s_br.last_pushed_status = s_br.work_status; + log_info("work_status push 0x%02X", s_br.work_status); +} + +/** + * @brief 轮询 BLE 连接,维护 Bit5,并在已配网断连时开回连广播 + * @note - Bit1/2/3(INTERNET / APP_ONLINE / BLE_CONNECTED)预留,固定为 0,此处不更新 + * - 已配网(Bit0)时:断连置 Bit5=1,连上后清 Bit5 + * - 未配网时 Bit5 只由 0x10 / 配网超时 / 配网成功路径维护 + */ +static void mcu_bridge_update_conn_bits(void) +{ + uint8_t conn = (usr_get_conn_service() != 0) ? 1 : 0; + uint8_t paired = usr_get_pair_flag(); + + if (conn != s_br.last_ble_conn) { + s_br.last_ble_conn = conn; + + if (!conn && paired) { + /* 已配网断连:发回连广播(非绑定广播) */ + usr_var.usr_goto_pair = 0; + if (s_br.identity_ready) { + usr_ble_adv_init(); + } + } + } + + /* 已配网:未连接显示「配网中」,已连接则退出「配网中」 */ + if (paired) { + mcu_bridge_set_ws_bit(MCU_WS_PAIRING_MODE, conn ? 0 : 1); + } +} + +/** + * @brief 配网超时退出 + */ +static void mcu_bridge_check_pair_timeout(void) +{ + uint32_t now; + uint32_t timeout_ms; + + if (!(s_br.work_status & MCU_WS_PAIRING_MODE)) { + return; + } + if (s_br.pair_timeout_s == 0) { + return; + } + if (usr_get_pair_flag()) { + return; + } + + now = mcu_util_get_ms(); + timeout_ms = (uint32_t)s_br.pair_timeout_s * 1000u; + if ((now - s_br.pair_start_ms) < timeout_ms) { + return; + } + + log_info("pair timeout, stop adv"); + usr_var.usr_goto_pair = 0; + mcu_bridge_set_ws_bit(MCU_WS_PAIRING_MODE, 0); + ble_trans_module_enable(0); + usr_var.usr_adv_flag = 0; +} + +/** + * @brief APP 透传等待超时重发 + */ +static void mcu_bridge_check_app_pending(void) +{ + uint32_t now; + + if (!s_br.app_pending.active) { + return; + } + + now = mcu_util_get_ms(); + if ((now - s_br.app_pending.send_time) <= MCU_APP_ACK_WAIT_MS) { + return; + } + + if (s_br.app_pending.retry >= MCU_APP_ACK_MAX_RETRY) { + log_error("app pending give up cmd expect=0x%02X", + s_br.app_pending.expect_ack); + s_br.app_pending.active = 0; + return; + } + + mcu_uart_send(s_br.app_pending.buf, s_br.app_pending.len); + s_br.app_pending.retry++; + s_br.app_pending.send_time = now; + log_debug("app pending retry %u", s_br.app_pending.retry); +} + +/** + * @brief MCU 上报等网关 ACK:超时则重发网关,超限则回 MCU 失败 + */ +static void mcu_bridge_check_report_pending(void) +{ + uint32_t now; + + if (!s_br.report_pending.active) { + return; + } + + now = mcu_util_get_ms(); + if ((now - s_br.report_pending.send_time) <= MCU_REPORT_GW_WAIT_MS) { + return; + } + + if (s_br.report_pending.retry >= MCU_REPORT_GW_MAX_RETRY) { + log_error("gateway report ack timeout, sn=%u", s_br.report_pending.sn); + mcu_bridge_send_ack(MCU_CMD_REPORT_ACK, s_br.report_pending.sn, MCU_RESULT_FAIL); + s_br.report_pending.active = 0; + return; + } + + usr_le_send_mcu_frame(s_br.report_pending.buf, s_br.report_pending.len); + s_br.report_pending.retry++; + s_br.report_pending.send_time = now; + log_debug("gateway report retry %u sn=%u", + s_br.report_pending.retry, s_br.report_pending.sn); +} + +/** + * @brief BLE→MCU 主动帧等待超时清理 + * @note 工作状态 ACK 超时后会重置 last_pushed_status, + * 使 mcu_bridge_try_work_status 在下个周期自动重推,避免状态永久失步 + */ +static void mcu_bridge_check_ble_pending(void) +{ + uint32_t now; + + if (!s_br.ble_pending.active) { + return; + } + /* 心跳有独立超时逻辑 */ + if (s_br.ble_pending.expect_ack == MCU_CMD_HEARTBEAT_ACK) { + return; + } + + now = mcu_util_get_ms(); + if ((now - s_br.ble_pending.send_time) > MCU_APP_ACK_WAIT_MS) { + if (s_br.ble_pending.expect_ack == MCU_CMD_WORK_STATUS_ACK) { + log_error("work_status ack timeout, will retry"); + s_br.last_pushed_status = 0xFF; + } + s_br.ble_pending.active = 0; + } +} + +/** + * @brief 周期处理:身份轮询 / 心跳 / 重传 / 状态同步 + * @note 仅在 mcu_bridge_task 上下文调用 + */ +static void mcu_bridge_periodic(void) +{ + mcu_bridge_try_identity(); + + if (s_br.identity_adv_pending) { + s_br.identity_adv_pending = 0; + usr_ble_adv_init(); + } + + mcu_bridge_update_conn_bits(); + mcu_bridge_check_pair_timeout(); + mcu_bridge_check_app_pending(); + mcu_bridge_check_ble_pending(); + mcu_bridge_check_report_pending(); + mcu_bridge_try_heartbeat(); + mcu_bridge_try_work_status(); +} + +/*============================================================================*/ +/* 公有函数 */ +/*============================================================================*/ + +/** + * @brief 初始化 MCU 协议桥(含 UART + 事件队列 + 独立任务) + * @return McuBridge_Ret_t + */ +McuBridge_Ret_t mcu_bridge_init(void) +{ + if (s_br.initialized) { + return MCU_BRIDGE_OK; + } + + memset(&s_br, 0, sizeof(s_br)); + memset(g_usr_le_prodcode, 0, sizeof(g_usr_le_prodcode)); + memset(g_usr_le_pcode, 0, sizeof(g_usr_le_pcode)); + mcu_frame_parser_reset(); + + if (mcu_uart_init() != MCU_UART_OK) { + return MCU_BRIDGE_ERR_UNKNOWN; + } + + memset(&s_evt_q, 0, sizeof(s_evt_q)); + os_mutex_create(&s_evt_q.lock); + os_sem_create(&s_evt_q.sem, 0); + + s_br.last_mcu_rx_ms = mcu_util_get_ms(); + s_br.last_identity_tx_ms = 0; /* 立即允许发第一帧 0x01 */ + s_br.last_pushed_status = 0xFF; /* 强制首轮差异 */ + s_br.initialized = 1; + + /* 上电:不强制配网;广播由 usr_ble_adv_init 在身份就绪后按配对状态决定 */ + usr_var.usr_goto_pair = 0; + usr_var.usr_adv_flag = 0; + + os_task_create(mcu_bridge_task, NULL, MCU_BRIDGE_TASK_PRIO, + MCU_BRIDGE_TASK_STACK, 0, "mcu_bridge"); + + log_info("mcu_bridge_init ok"); + return MCU_BRIDGE_OK; +} + +/** + * @brief UART 收到原始数据(生产者,可在 UART 接收任务上下文调用) + * @param data 数据 + * @param len 长度 + * @note 仅做逐字节组帧,帧就绪后投递事件,不直接触碰业务状态 s_br + */ +void mcu_bridge_on_uart_rx(const uint8_t *data, uint16_t len) +{ + uint16_t i; + uint16_t out_len; + int8_t ret; + McuEvt_t evt; + + if (!s_br.initialized || !data || len == 0) { + return; + } + + for (i = 0; i < len; i++) { + ret = mcu_frame_feed(data[i], s_parse_out, &out_len); + if (ret == 1) { + evt.type = MCU_EVT_UART_FRAME; + evt.len = out_len; + memcpy(evt.buf, s_parse_out, out_len); + mcu_bridge_post_event(&evt); + } + } +} + +/** + * @brief APP/网关经 BLE 下发的 55AA 帧(生产者,可在 BLE 协议栈任务调用) + * @param data 55AA 帧 + * @param len 长度 + */ +void mcu_bridge_on_app_frame(const uint8_t *data, uint16_t len) +{ + McuEvt_t evt; + + if (!s_br.initialized || !data || len < 6 || len > sizeof(evt.buf)) { + log_error("app frame invalid len=%u", len); + return; + } + if (data[0] != MCU_PKT_HEAD_0 || data[1] != MCU_PKT_HEAD_1) { + log_error("app frame head err"); + return; + } + + evt.type = MCU_EVT_APP_FRAME; + evt.len = len; + memcpy(evt.buf, data, len); + mcu_bridge_post_event(&evt); +} + +/** + * @brief 是否已从 MCU 获取到设备身份 + * @return 1=已就绪,0=未就绪 + * @note 单字节只读快照,可在任意任务直接调用(无需加锁) + */ +uint8_t mcu_bridge_is_identity_ready(void) +{ + return s_br.identity_ready; +} + +/** + * @brief 配网成功通知(生产者,可在 BLE 协议栈任务调用) + */ +void mcu_bridge_on_paired(void) +{ + McuEvt_t evt; + + memset(&evt, 0, sizeof(evt)); + evt.type = MCU_EVT_BLE_PAIRED; + mcu_bridge_post_event(&evt); +} + +/** + * @brief 配对信息被清除通知(生产者,可在 BLE 协议栈任务调用) + */ +void mcu_bridge_on_pair_cleared(void) +{ + McuEvt_t evt; + + memset(&evt, 0, sizeof(evt)); + evt.type = MCU_EVT_BLE_PAIR_CLEARED; + mcu_bridge_post_event(&evt); +} + +/** + * @brief 获取当前工作状态字节 + * @return 工作状态 bitmap + * @note 单字节只读快照,可在任意任务直接调用(无需加锁) + */ +uint8_t mcu_bridge_get_work_status(void) +{ + return s_br.work_status; +} + +/** + * @brief 网关对 REPORTCMD_V2 主动上报的应答(生产者,可在 BLE 协议栈任务调用) + * @param cmd bleproto 包 cmd + * @param gateway_int_cnt gateway_int 个数 + * @param gateway_int0 gateway_int[0] + */ +void mcu_bridge_on_gateway_report_ack(uint16_t cmd, + uint8_t gateway_int_cnt, + int32_t gateway_int0) +{ + McuEvt_t evt; + + memset(&evt, 0, sizeof(evt)); + evt.type = MCU_EVT_GATEWAY_ACK; + evt.gw_cmd = cmd; + evt.gw_int_cnt = gateway_int_cnt; + evt.gw_int0 = gateway_int0; + mcu_bridge_post_event(&evt); +} diff --git a/JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_bridge.h b/JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_bridge.h new file mode 100644 index 0000000..ff20ee2 --- /dev/null +++ b/JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_bridge.h @@ -0,0 +1,100 @@ +/****************************************************************************** + * @file mcu_bridge.h + * @brief BLE+MCU 协议桥对外 API(模组侧唯一业务入口) + * @author cyWu <1917507415@qq.com> + * @date 2026.08.06 + * @version V1.1.0 + * @history + * - V1.0.0, 2026.08.06, cyWu, 首次发布 + * - V1.1.0, 2026.08.06, cyWu, 内部改为事件队列 + 独立任务驱动; + * 所有 on_xxx 接口均可在任意任务上下文安全调用(生产者-消费者模型) + ******************************************************************************/ + +#ifndef __MCU_BRIDGE_H__ +#define __MCU_BRIDGE_H__ + +#include +#include "mcu_cmd.h" + +/*============================================================================*/ +/* 错误码 */ +/*============================================================================*/ + +typedef enum { + MCU_BRIDGE_OK = 0, + MCU_BRIDGE_ERR_PARAM, + MCU_BRIDGE_ERR_BUSY, + MCU_BRIDGE_ERR_TIMEOUT, + MCU_BRIDGE_ERR_NOT_INIT, + MCU_BRIDGE_ERR_UNKNOWN +} McuBridge_Ret_t; + +/*============================================================================*/ +/* 外部接口 */ +/*============================================================================*/ + +/** + * @brief 初始化 MCU 协议桥(UART + 事件队列 + 独立任务) + * @return McuBridge_Ret_t + * @note 在 app_main 中调用一次即可;内部会创建 "mcu_bridge" 任务, + * 所有身份/心跳/透传/状态机逻辑均在该任务单一上下文中处理 + */ +McuBridge_Ret_t mcu_bridge_init(void); + +/** + * @brief UART 收到原始数据(由 mcu_uart 接收任务调用) + * @param data 数据 + * @param len 长度 + * @note 仅做逐字节组帧;帧就绪后投递事件到 mcu_bridge 任务处理, + * 可安全在 UART 接收任务上下文调用 + */ +void mcu_bridge_on_uart_rx(const uint8_t *data, uint16_t len); + +/** + * @brief APP/网关经 BLE 下发的 55AA 帧 + * @param data 帧数据 + * @param len 长度 + * @note 对 0x03/0x05:转发 MCU,等 0x04/0x06 后再回传 BLE; + * 可安全在 BLE 协议栈任务上下文调用(内部投递事件异步处理) + */ +void mcu_bridge_on_app_frame(const uint8_t *data, uint16_t len); + +/** + * @brief 是否已从 MCU 获取到设备身份 + * @return 1=已就绪,0=未就绪 + * @note 单字节只读快照,可在任意任务直接调用 + */ +uint8_t mcu_bridge_is_identity_ready(void); + +/** + * @brief 配网成功通知(AUTHSETUP 写对后由 BLE 层调用) + * @note 可安全在 BLE 协议栈任务上下文调用(内部投递事件异步处理) + */ +void mcu_bridge_on_paired(void); + +/** + * @brief 配对信息被清除通知 + * @note 可安全在 BLE 协议栈任务上下文调用(内部投递事件异步处理) + */ +void mcu_bridge_on_pair_cleared(void); + +/** + * @brief 获取当前工作状态字节 + * @return 工作状态 bitmap + * @note 单字节只读快照,可在任意任务直接调用 + */ +uint8_t mcu_bridge_get_work_status(void); + +/** + * @brief 网关对 REPORTCMD_V2 主动上报的应答 + * @param cmd bleproto 包 cmd + * @param gateway_int_cnt gateway_int 个数 + * @param gateway_int0 gateway_int[0] + * @note 成功条件:cmd==0 && cnt==1 && int0==0; + * 成功后才向 MCU 回 0x08;可安全在 BLE 协议栈任务上下文调用 + */ +void mcu_bridge_on_gateway_report_ack(uint16_t cmd, + uint8_t gateway_int_cnt, + int32_t gateway_int0); + +#endif /* __MCU_BRIDGE_H__ */ diff --git a/JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_cmd.h b/JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_cmd.h new file mode 100644 index 0000000..b3b6806 --- /dev/null +++ b/JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_cmd.h @@ -0,0 +1,110 @@ +/****************************************************************************** + * @file mcu_cmd.h + * @brief MCU 接入协议命令字 / 结果码 / 工作状态位定义 + * @author cyWu <1917507415@qq.com> + * @date 2026.08.06 + * @version V1.0.0 + * @history + * - V1.0.0, 2026.08.06, cyWu, 首次发布 + ******************************************************************************/ + +#ifndef __MCU_CMD_H__ +#define __MCU_CMD_H__ + +#include + +/*============================================================================*/ +/* 工作状态 Bit(命令 0x14) */ +/*============================================================================*/ + +#define MCU_WS_CONFIGURED (1u << 0) /**< 已完成配网 */ +#define MCU_WS_INTERNET (1u << 1) /**< 已连互联网(云),预留:暂无法获取,固定为 0 */ +#define MCU_WS_APP_ONLINE (1u << 2) /**< APP 在线,预留:暂无法获取,固定为 0 */ +#define MCU_WS_BLE_CONNECTED (1u << 3) /**< 手机已连接 BLE,预留:暂无法获取,固定为 0 */ +#define MCU_WS_OTA_UPGRADING (1u << 4) /**< OTA 升级中 */ +#define MCU_WS_PAIRING_MODE (1u << 5) /**< 配网模式中 */ + +/*============================================================================*/ +/* 命令字 */ +/*============================================================================*/ + +typedef enum { + /* 身份 */ + MCU_CMD_GET_IDENTITY = 0x01, + MCU_CMD_GET_IDENTITY_ACK = 0x02, + + /* 数据透传 */ + MCU_CMD_CONTROL = 0x03, + MCU_CMD_CONTROL_ACK = 0x04, + MCU_CMD_READ = 0x05, + MCU_CMD_READ_ACK = 0x06, + MCU_CMD_REPORT = 0x07, + MCU_CMD_REPORT_ACK = 0x08, + + /* 配网 / 心跳 / 状态 */ + MCU_CMD_PAIR_CFG = 0x10, + MCU_CMD_PAIR_CFG_ACK = 0x11, + MCU_CMD_HEARTBEAT = 0x12, + MCU_CMD_HEARTBEAT_ACK = 0x13, + MCU_CMD_WORK_STATUS = 0x14, + MCU_CMD_WORK_STATUS_ACK = 0x15, + + /* 模组管理(后续扩展) */ + MCU_CMD_RESET = 0x16, + MCU_CMD_RESET_ACK = 0x17, + MCU_CMD_REBOOT = 0x18, + MCU_CMD_REBOOT_ACK = 0x19, + MCU_CMD_MCU_REBOOT = 0x1A, + MCU_CMD_MCU_REBOOT_ACK = 0x1B, + MCU_CMD_GET_TIME = 0x1C, + MCU_CMD_GET_TIME_ACK = 0x1D, + + /* 扩展功能(后续扩展) */ + MCU_CMD_FACTORY = 0x20, + MCU_CMD_FACTORY_ACK = 0x21, + MCU_CMD_PHOTO = 0x22, + MCU_CMD_PHOTO_ACK = 0x23, + MCU_CMD_RECORD = 0x24, + MCU_CMD_RECORD_ACK = 0x25, + MCU_CMD_AUDIO = 0x26, + MCU_CMD_AUDIO_ACK = 0x27, + + /* OTA(后续扩展) */ + MCU_CMD_OTA_NOTIFY = 0x30, + MCU_CMD_OTA_NOTIFY_ACK = 0x31, + MCU_CMD_OTA_DATA = 0x32, + MCU_CMD_OTA_DATA_ACK = 0x33, + MCU_CMD_OTA_COMPLETE = 0x34, + MCU_CMD_OTA_COMPLETE_ACK = 0x35, + MCU_CMD_OTA_STOP_BLE = 0x36, + MCU_CMD_OTA_STOP_BLE_ACK = 0x37, + MCU_CMD_OTA_STOP_MCU = 0x38, + MCU_CMD_OTA_STOP_MCU_ACK = 0x39, + + /* MCU 信息 */ + MCU_CMD_GET_MCU_INFO = 0x40, + MCU_CMD_GET_MCU_INFO_ACK = 0x41 +} McuCmd_t; + +/*============================================================================*/ +/* 结果码 */ +/*============================================================================*/ + +typedef enum { + MCU_RESULT_OK = 0x00, + MCU_RESULT_PARAM_ERR = 0x01, + MCU_RESULT_BUSY = 0x02, + MCU_RESULT_NOT_SUPPORT = 0x03, + MCU_RESULT_CRC_ERR = 0x04, + MCU_RESULT_FAIL = 0x05, + + /* OTA 专用 */ + MCU_RESULT_OTA_NO_NEED = 0x20, + MCU_RESULT_OTA_VER_LOW = 0x21, + MCU_RESULT_OTA_SPACE_ERR = 0x22, + MCU_RESULT_OTA_STATE_ERR = 0x23, + MCU_RESULT_OTA_FILE_ERR = 0x24, + MCU_RESULT_OTA_FAIL = 0x25 +} McuResult_t; + +#endif /* __MCU_CMD_H__ */ diff --git a/JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_frame.c b/JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_frame.c new file mode 100644 index 0000000..cc1f171 --- /dev/null +++ b/JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_frame.c @@ -0,0 +1,140 @@ +/****************************************************************************** + * @file mcu_frame.c + * @brief MCU 接入协议 55AA 帧组包/解包 + * @author cyWu <1917507415@qq.com> + * @date 2026.08.06 + * @version V1.0.0 + * @history + * - V1.0.0, 2026.08.06, cyWu, 首次发布 + ******************************************************************************/ + +#include "mcu_frame.h" +#include "mcu_util.h" +#include + +/*============================================================================*/ +/* 私有变量 */ +/*============================================================================*/ + +static uint8_t s_state = 0; /* 0=寻头 1=收长度高 2=收长度低 3=收载荷 */ +static uint8_t s_prev = 0; +static uint16_t s_expect = 0; +static uint16_t s_idx = 0; +static uint8_t s_buf[MCU_FRAME_MAX_LEN]; + +/*============================================================================*/ +/* 公有函数 */ +/*============================================================================*/ + +/** + * @brief 复位帧解析状态机 + */ +void mcu_frame_parser_reset(void) +{ + s_state = 0; + s_prev = 0; + s_expect = 0; + s_idx = 0; +} + +/** + * @brief 组帧并写入 out(含包头与校验) + * @param out 输出缓冲 + * @param out_max 缓冲最大长度 + * @param cmd 命令字 + * @param sn 序列号 + * @param payload 数据区,可为 NULL + * @param plen 数据长度 + * @return 帧总长度;<0 失败 + */ +int32_t mcu_frame_pack(uint8_t *out, uint16_t out_max, + uint8_t cmd, uint8_t sn, + const uint8_t *payload, uint16_t plen) +{ + uint16_t len_field; + uint16_t total; + + if (!out) { + return -1; + } + + /* LEN = CMD + SN + payload + CKSUM */ + len_field = (uint16_t)(plen + 3); + total = (uint16_t)(len_field + 4); + + if (total > out_max || total > MCU_FRAME_MAX_LEN) { + return -1; + } + + out[0] = MCU_PKT_HEAD_0; + out[1] = MCU_PKT_HEAD_1; + mcu_util_write_u16_be(&out[2], len_field); + out[4] = cmd; + out[5] = sn; + if (payload && plen > 0) { + memcpy(&out[6], payload, plen); + } + out[total - 1] = mcu_util_checksum(out, total); + return (int32_t)total; +} + +/** + * @brief 向解析器喂入原始字节,尝试提取完整帧 + * @param byte 输入字节 + * @param out 输出完整帧缓冲 + * @param out_len 输出帧长度 + * @return 1=得到完整帧,0=继续,-1=校验错误丢弃 + */ +int8_t mcu_frame_feed(uint8_t byte, uint8_t *out, uint16_t *out_len) +{ + if (!out || !out_len) { + return 0; + } + + if (s_state == 0) { + if (s_prev == MCU_PKT_HEAD_0 && byte == MCU_PKT_HEAD_1) { + s_buf[0] = MCU_PKT_HEAD_0; + s_buf[1] = MCU_PKT_HEAD_1; + s_idx = 2; + s_state = 1; + } + s_prev = byte; + return 0; + } + + if (s_state == 1) { + s_buf[s_idx++] = byte; + s_state = 2; + return 0; + } + + if (s_state == 2) { + s_buf[s_idx++] = byte; + s_expect = mcu_util_read_u16_be(&s_buf[2]); + if (s_expect < 3 || (s_expect + 4) > MCU_FRAME_MAX_LEN) { + mcu_frame_parser_reset(); + s_prev = byte; + return -1; + } + s_state = 3; + return 0; + } + + s_buf[s_idx++] = byte; + if (s_idx >= (uint16_t)(s_expect + 4)) { + uint16_t total = (uint16_t)(s_expect + 4); + uint8_t ck = mcu_util_checksum(s_buf, total); + int8_t ret; + + if (ck == s_buf[total - 1]) { + memcpy(out, s_buf, total); + *out_len = total; + ret = 1; + } else { + ret = -1; + } + mcu_frame_parser_reset(); + return ret; + } + return 0; +} diff --git a/JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_frame.h b/JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_frame.h new file mode 100644 index 0000000..09139d0 --- /dev/null +++ b/JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_frame.h @@ -0,0 +1,57 @@ +/****************************************************************************** + * @file mcu_frame.h + * @brief MCU 接入协议 55AA 帧组包/解包 + * @author cyWu <1917507415@qq.com> + * @date 2026.08.06 + * @version V1.0.0 + * @history + * - V1.0.0, 2026.08.06, cyWu, 首次发布 + ******************************************************************************/ + +#ifndef __MCU_FRAME_H__ +#define __MCU_FRAME_H__ + +#include +#include "mcu_cmd.h" + +/*============================================================================*/ +/* 宏定义 */ +/*============================================================================*/ + +#define MCU_PKT_HEAD_0 (0x55) +#define MCU_PKT_HEAD_1 (0xAA) +#define MCU_FRAME_MAX_LEN (128) + +/*============================================================================*/ +/* 外部接口 */ +/*============================================================================*/ + +/** + * @brief 组帧并写入 out(含包头与校验) + * @param out 输出缓冲 + * @param out_max 缓冲最大长度 + * @param cmd 命令字 + * @param sn 序列号 + * @param payload 数据区,可为 NULL + * @param plen 数据长度 + * @return 帧总长度;<0 失败 + */ +int32_t mcu_frame_pack(uint8_t *out, uint16_t out_max, + uint8_t cmd, uint8_t sn, + const uint8_t *payload, uint16_t plen); + +/** + * @brief 向解析器喂入原始字节,尝试提取完整帧 + * @param byte 输入字节 + * @param out 输出完整帧缓冲 + * @param out_len 输出帧长度 + * @return 1=得到完整帧,0=继续,-1=校验错误丢弃 + */ +int8_t mcu_frame_feed(uint8_t byte, uint8_t *out, uint16_t *out_len); + +/** + * @brief 复位帧解析状态机 + */ +void mcu_frame_parser_reset(void); + +#endif /* __MCU_FRAME_H__ */ diff --git a/JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_uart.c b/JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_uart.c new file mode 100644 index 0000000..bddf1bc --- /dev/null +++ b/JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_uart.c @@ -0,0 +1,141 @@ +/****************************************************************************** + * @file mcu_uart.c + * @brief BLE 模组与外部 MCU 的 UART 驱动(PA7 TX / PA8 RX, 9600) + * @author cyWu <1917507415@qq.com> + * @date 2026.08.06 + * @version V1.3.0 + * @history + * - V1.0.0, 2026.08.06, cyWu, 首次发布 + * - V1.1.0, 2026.08.07, cyWu, TX 使用静态对齐缓冲 + write() + * - V1.2.0, 2026.08.07, cyWu, 对齐供应商 demo:关闭低功耗 + * - V1.3.0, 2026.08.07, cyWu, 发送与厂家一致,直接 write(data),去掉 TX 拷贝 + * + * @note 串口收发期间由 mcu_bridge 的 LP target 挡住休眠; + * 板级可开 SLEEP_EN,勿在无 LP 保护时直接开 DEEP_SLEEP。 + ******************************************************************************/ + +#include "system/includes.h" +#include "asm/uart_dev.h" +#include "asm/gpio.h" +#include "mcu_uart.h" +#include "mcu_bridge.h" +#include "mcu_frame.h" + +#define LOG_TAG_CONST APP +#define LOG_TAG "[MCU_UART]" +#define LOG_ERROR_ENABLE +#define LOG_DEBUG_ENABLE +#define LOG_INFO_ENABLE +#define LOG_DUMP_ENABLE +#define LOG_CLI_ENABLE +#include "debug.h" + +/*============================================================================*/ +/* 私有变量 */ +/*============================================================================*/ + +static uint8_t s_initialized = 0; +static const uart_bus_t *s_uart_bus = NULL; +static u8 s_rx_cbuf[MCU_UART_RX_CBUF_SIZE] __attribute__((aligned(4))); +static u8 s_rx_tmp[MCU_UART_RX_CBUF_SIZE]; +static OS_MUTEX s_tx_lock; + +/*============================================================================*/ +/* 私有函数 */ +/*============================================================================*/ + +/** + * @brief UART 接收任务:阻塞读串口,喂入协议桥 + * @param arg 未使用 + */ +static void mcu_uart_rx_task(void *arg) +{ + u32 rxcnt; + + (void)arg; + log_info("rx task start"); + + while (1) { + if (!s_uart_bus) { + os_time_dly(10); + continue; + } + /* timeout=0:无数据时 pend,有 RX/OT 中断后唤醒 */ + rxcnt = s_uart_bus->read(s_rx_tmp, sizeof(s_rx_tmp), 0); + if (rxcnt > 0) { + mcu_bridge_on_uart_rx(s_rx_tmp, (uint16_t)rxcnt); + } + } +} + +/*============================================================================*/ +/* 公有函数 */ +/*============================================================================*/ + +/** + * @brief 打开 MCU 串口并创建接收任务 + * @return McuUart_Ret_t + */ +McuUart_Ret_t mcu_uart_init(void) +{ + struct uart_platform_data_t u_arg = {0}; + + if (s_initialized) { + return MCU_UART_OK; + } + + u_arg.tx_pin = IO_PORTA_07; + u_arg.rx_pin = IO_PORTA_08; + u_arg.rx_cbuf = s_rx_cbuf; + u_arg.rx_cbuf_size = MCU_UART_RX_CBUF_SIZE; + u_arg.frame_length = MCU_UART_RX_CBUF_SIZE; + u_arg.rx_timeout = 10; /* ms,帧间隙产生 OT */ + u_arg.isr_cbfun = NULL; + u_arg.baud = MCU_UART_BAUD; + u_arg.is_9bit = 0; + + s_uart_bus = uart_dev_open(&u_arg); + if (s_uart_bus == NULL) { + log_error("init open fail"); + return MCU_UART_ERR_OPEN; + } + + os_mutex_create(&s_tx_lock); + os_task_create(mcu_uart_rx_task, NULL, 31, 1024, 0, "mcu_uart"); + + s_initialized = 1; + log_info("init ok, PA7/PA8 @ %d", MCU_UART_BAUD); + return MCU_UART_OK; +} + +/** + * @brief 向 MCU 发送数据 + * @param data 发送缓冲 + * @param len 长度 + * @return McuUart_Ret_t + * @note 与 usr_hw_uart.c 一致:uart_bus->write(data, len) + */ +McuUart_Ret_t mcu_uart_send(const uint8_t *data, uint16_t len) +{ + if (!s_initialized || !s_uart_bus) { + return MCU_UART_ERR_NOT_INIT; + } + if (!data || len == 0 || len > MCU_FRAME_MAX_LEN) { + return MCU_UART_ERR_PARAM; + } + + os_mutex_pend(&s_tx_lock, 0); + s_uart_bus->write((void *)data, len); + os_mutex_post(&s_tx_lock); + + return MCU_UART_OK; +} + +/** + * @brief 获取串口是否已初始化 + * @return 1=已初始化,0=未初始化 + */ +uint8_t mcu_uart_is_ready(void) +{ + return s_initialized; +} diff --git a/JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_uart.h b/JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_uart.h new file mode 100644 index 0000000..226fef8 --- /dev/null +++ b/JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_uart.h @@ -0,0 +1,62 @@ +/****************************************************************************** + * @file mcu_uart.h + * @brief BLE 模组与外部 MCU 的 UART 驱动接口(PA7/PA8, 9600) + * @author cyWu <1917507415@qq.com> + * @date 2026.08.06 + * @version V1.0.0 + * @history + * - V1.0.0, 2026.08.06, cyWu, 首次发布 + ******************************************************************************/ + +#ifndef __MCU_UART_H__ +#define __MCU_UART_H__ + +#include + +/*============================================================================*/ +/* 宏定义 */ +/*============================================================================*/ + +#define MCU_UART_BAUD (9600) /**< 协议默认波特率 */ +#define MCU_UART_RX_CBUF_SIZE (512) /**< DMA 循环缓冲,须为 2 的幂 */ +/* 引脚:TX=PA7(IO_PORTA_07),RX=PA8(IO_PORTA_08),在 mcu_uart.c 中配置 */ + +/*============================================================================*/ +/* 错误码 */ +/*============================================================================*/ + +typedef enum { + MCU_UART_OK = 0, + MCU_UART_ERR_PARAM, + MCU_UART_ERR_BUSY, + MCU_UART_ERR_TIMEOUT, + MCU_UART_ERR_NOT_INIT, + MCU_UART_ERR_OPEN, + MCU_UART_ERR_UNKNOWN +} McuUart_Ret_t; + +/*============================================================================*/ +/* 外部接口 */ +/*============================================================================*/ + +/** + * @brief 打开 MCU 串口并创建接收任务 + * @return McuUart_Ret_t + */ +McuUart_Ret_t mcu_uart_init(void); + +/** + * @brief 向 MCU 发送数据 + * @param data 发送缓冲 + * @param len 长度 + * @return McuUart_Ret_t + */ +McuUart_Ret_t mcu_uart_send(const uint8_t *data, uint16_t len); + +/** + * @brief 获取串口是否已初始化 + * @return 1=已初始化,0=未初始化 + */ +uint8_t mcu_uart_is_ready(void); + +#endif /* __MCU_UART_H__ */ diff --git a/JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_util.c b/JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_util.c new file mode 100644 index 0000000..63d27cf --- /dev/null +++ b/JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_util.c @@ -0,0 +1,108 @@ +/****************************************************************************** + * @file mcu_util.c + * @brief MCU 桥通用工具实现 + * @author cyWu <1917507415@qq.com> + * @date 2026.08.06 + * @version V1.2.0 + * @history + * - V1.0.0, 2026.08.06, cyWu, 首次发布 + * - V1.1.0, 2026.08.07, cyWu, get_ms 改用 timer_get_ms + * - V1.2.0, 2026.08.10, cyWu, 删除空壳 timer_init + ******************************************************************************/ + +#include "system/includes.h" +#include "asm/timer.h" +#include "mcu_util.h" + +/*============================================================================*/ +/* 公有函数 */ +/*============================================================================*/ + +/** + * @brief 获取开机以来的毫秒计数 + * @return 毫秒计数 + * @note 直接使用系统 tick 换算的毫秒,不受低功耗拉长 usr_timer 影响 + */ +uint32_t mcu_util_get_ms(void) +{ + return timer_get_ms(); +} + +/** + * @brief 协议累加和校验 + * @param frame 完整帧(含 55 AA) + * @param total_len 帧总长度 + * @return 校验字节 + */ +uint8_t mcu_util_checksum(const uint8_t *frame, uint16_t total_len) +{ + uint16_t sum = 0; + uint16_t i; + + if (!frame || total_len < 4) { + return 0; + } + for (i = 2; i < (uint16_t)(total_len - 1); i++) { + sum = (uint16_t)(sum + frame[i]); + } + return (uint8_t)(sum & 0xFF); +} + +/** + * @brief 大端读取 uint16 + * @param buf 字节缓冲 + * @return 数值 + */ +uint16_t mcu_util_read_u16_be(const uint8_t *buf) +{ + if (!buf) { + return 0; + } + return (uint16_t)(((uint16_t)buf[0] << 8) | buf[1]); +} + +/** + * @brief 大端读取 uint32 + * @param buf 字节缓冲 + * @return 数值 + */ +uint32_t mcu_util_read_u32_be(const uint8_t *buf) +{ + if (!buf) { + return 0; + } + return ((uint32_t)buf[0] << 24) + | ((uint32_t)buf[1] << 16) + | ((uint32_t)buf[2] << 8) + | (uint32_t)buf[3]; +} + +/** + * @brief 大端写入 uint16 + * @param buf 字节缓冲 + * @param val 数值 + */ +void mcu_util_write_u16_be(uint8_t *buf, uint16_t val) +{ + if (!buf) { + return; + } + buf[0] = (uint8_t)(val >> 8); + buf[1] = (uint8_t)(val & 0xFF); +} + +/** + * @brief 大端写入 uint32 + * @param buf 字节缓冲 + * @param val 数值 + */ +void mcu_util_write_u32_be(uint8_t *buf, uint32_t val) +{ + if (!buf) { + return; + } + buf[0] = (uint8_t)(val >> 24); + buf[1] = (uint8_t)(val >> 16); + buf[2] = (uint8_t)(val >> 8); + buf[3] = (uint8_t)(val & 0xFF); +} diff --git a/JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_util.h b/JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_util.h new file mode 100644 index 0000000..78f964c --- /dev/null +++ b/JBao_JL7016_MOD_FW/apps/usr_mcu_bridge/mcu_util.h @@ -0,0 +1,65 @@ +/****************************************************************************** + * @file mcu_util.h + * @brief MCU 桥通用工具:校验和、大端序、毫秒节拍 + * @author cyWu <1917507415@qq.com> + * @date 2026.08.06 + * @version V1.1.0 + * @history + * - V1.0.0, 2026.08.06, cyWu, 首次发布 + * - V1.1.0, 2026.08.10, cyWu, 删除空壳 timer_init + ******************************************************************************/ + +#ifndef __MCU_UTIL_H__ +#define __MCU_UTIL_H__ + +#include + +/*============================================================================*/ +/* 外部接口 */ +/*============================================================================*/ + +/** + * @brief 获取开机以来的毫秒计数 + * @return 毫秒计数 + * @note 基于系统 timer_get_ms,不受低功耗拉长 usr_timer 影响 + */ +uint32_t mcu_util_get_ms(void); + +/** + * @brief 协议累加和校验 + * @param frame 完整帧(含 55 AA) + * @param total_len 帧总长度 + * @return 校验字节 + * @note 累加范围:LEN 字段起至校验和前一字节 + */ +uint8_t mcu_util_checksum(const uint8_t *frame, uint16_t total_len); + +/** + * @brief 大端读取 uint16 + * @param buf 字节缓冲 + * @return 数值 + */ +uint16_t mcu_util_read_u16_be(const uint8_t *buf); + +/** + * @brief 大端读取 uint32 + * @param buf 字节缓冲 + * @return 数值 + */ +uint32_t mcu_util_read_u32_be(const uint8_t *buf); + +/** + * @brief 大端写入 uint16 + * @param buf 字节缓冲 + * @param val 数值 + */ +void mcu_util_write_u16_be(uint8_t *buf, uint16_t val); + +/** + * @brief 大端写入 uint32 + * @param buf 字节缓冲 + * @param val 数值 + */ +void mcu_util_write_u32_be(uint8_t *buf, uint32_t val); + +#endif /* __MCU_UTIL_H__ */ diff --git a/JBao_JL7016_MOD_FW/cpu/br28/tools/download/earphone/download_app_ota.bat b/JBao_JL7016_MOD_FW/cpu/br28/tools/download/earphone/download_app_ota.bat index 1e15e05..a585ec3 100644 --- a/JBao_JL7016_MOD_FW/cpu/br28/tools/download/earphone/download_app_ota.bat +++ b/JBao_JL7016_MOD_FW/cpu/br28/tools/download/earphone/download_app_ota.bat @@ -10,10 +10,10 @@ copy ..\..\ota.bin . copy ..\..\anc_coeff.bin . copy ..\..\anc_gains.bin . -..\..\isd_download.exe ..\..\isd_config.ini -tonorflash -dev br28 -boot 0x120000 -div8 -wait 300 -uboot ..\..\uboot.boot -app ..\..\app.bin -res ..\..\cfg_tool.bin tone.cfg p11_code.bin -uboot_compress -key RS-5B88.key +..\..\isd_download.exe ..\..\isd_config.ini -tonorflash -dev br28 -boot 0x120000 -div8 -wait 300 -uboot ..\..\uboot.boot -app ..\..\app.bin -res ..\..\cfg_tool.bin tone.cfg p11_code.bin -uboot_compress -:: -format all +:: -format all -key RS-5B88.key ::-reboot 2500 @rem ɾʱļ-format all