重构 usr_mcu_bridge:拆成 protocol/business 分层,精简上报与 UART 超时逻辑
1、协议层迁入 protocol/,业务拆为 identity/pairing/link_status,mcu_bridge 收敛为薄门面 2、0x07 上报不再缓存重发,由 MCU 侧超时重发;模组只记 SN,网关 ACK 后回 0x08 3、APP 透传重试用尽后主动回 FAIL;UART ACK 等待统一为 MCU_UART_ACK_WAIT_MS=500ms
This commit is contained in:
@@ -1 +1,2 @@
|
||||
PY32F040开发框架/*
|
||||
.vscode/
|
||||
@@ -175,6 +175,8 @@
|
||||
<Add directory="apps/usr_le_code/thirdpart/mjson" />
|
||||
<Add directory="apps/usr_le_code/thirdpart" />
|
||||
<Add directory="apps/usr_mcu_bridge" />
|
||||
<Add directory="apps/usr_mcu_bridge/protocol" />
|
||||
<Add directory="apps/usr_mcu_bridge/business" />
|
||||
</Compiler>
|
||||
<Linker>
|
||||
<Add option="-flto" />
|
||||
@@ -761,19 +763,39 @@
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="apps/usr_mcu_bridge/mcu_bridge.h" />
|
||||
<Unit filename="apps/usr_mcu_bridge/mcu_cmd.h" />
|
||||
<Unit filename="apps/usr_mcu_bridge/mcu_frame.c">
|
||||
<Unit filename="apps/usr_mcu_bridge/protocol/mcu_cmd.h" />
|
||||
<Unit filename="apps/usr_mcu_bridge/protocol/mcu_frame.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="apps/usr_mcu_bridge/mcu_frame.h" />
|
||||
<Unit filename="apps/usr_mcu_bridge/mcu_uart.c">
|
||||
<Unit filename="apps/usr_mcu_bridge/protocol/mcu_frame.h" />
|
||||
<Unit filename="apps/usr_mcu_bridge/protocol/mcu_uart.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="apps/usr_mcu_bridge/mcu_uart.h" />
|
||||
<Unit filename="apps/usr_mcu_bridge/mcu_util.c">
|
||||
<Unit filename="apps/usr_mcu_bridge/protocol/mcu_uart.h" />
|
||||
<Unit filename="apps/usr_mcu_bridge/protocol/mcu_util.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="apps/usr_mcu_bridge/mcu_util.h" />
|
||||
<Unit filename="apps/usr_mcu_bridge/protocol/mcu_util.h" />
|
||||
<Unit filename="apps/usr_mcu_bridge/protocol/mcu_protocol.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="apps/usr_mcu_bridge/protocol/mcu_protocol.h" />
|
||||
<Unit filename="apps/usr_mcu_bridge/business/mcu_identity.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="apps/usr_mcu_bridge/business/mcu_identity.h" />
|
||||
<Unit filename="apps/usr_mcu_bridge/business/mcu_pairing.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="apps/usr_mcu_bridge/business/mcu_pairing.h" />
|
||||
<Unit filename="apps/usr_mcu_bridge/business/mcu_link_status.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="apps/usr_mcu_bridge/business/mcu_link_status.h" />
|
||||
<Unit filename="apps/usr_mcu_bridge/business/mcu_business.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="apps/usr_mcu_bridge/business/mcu_business.h" />
|
||||
<Unit filename="apps/usr_le_code/ble_proto.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
|
||||
@@ -249,6 +249,8 @@ INCLUDES := \
|
||||
-Iapps/usr_le_code/thirdpart \
|
||||
-Iapps/usr_le_code/thirdpart/mjson \
|
||||
-Iapps/usr_mcu_bridge \
|
||||
-Iapps/usr_mcu_bridge/protocol \
|
||||
-Iapps/usr_mcu_bridge/business \
|
||||
-I$(SYS_INC_DIR) \
|
||||
|
||||
|
||||
@@ -614,9 +616,14 @@ c_SRC_FILES := \
|
||||
cpu/br28/uart_dev.c \
|
||||
cpu/br28/umidigi_chargestore.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_mcu_bridge/protocol/mcu_frame.c \
|
||||
apps/usr_mcu_bridge/protocol/mcu_uart.c \
|
||||
apps/usr_mcu_bridge/protocol/mcu_util.c \
|
||||
apps/usr_mcu_bridge/protocol/mcu_protocol.c \
|
||||
apps/usr_mcu_bridge/business/mcu_identity.c \
|
||||
apps/usr_mcu_bridge/business/mcu_pairing.c \
|
||||
apps/usr_mcu_bridge/business/mcu_link_status.c \
|
||||
apps/usr_mcu_bridge/business/mcu_business.c \
|
||||
apps/usr_le_code/ble_proto.c \
|
||||
apps/usr_le_code/bleproto.c \
|
||||
apps/usr_le_code/bleproto_packer.c \
|
||||
|
||||
@@ -3,10 +3,12 @@
|
||||
* @brief usr_le_code 产品身份配置接口(由 MCU 桥运行时填充)
|
||||
* @author cyWu <1917507415@qq.com>
|
||||
* @date 2026.08.03
|
||||
* @version V1.1.0
|
||||
* @version V1.2.0
|
||||
* @history
|
||||
* - V1.0.0, 2026.08.03, cyWu, 首次发布
|
||||
* - V1.1.0, 2026.08.06, cyWu, 改为非 const,由 mcu_bridge 从 MCU 0x02 写入
|
||||
* - V1.2.0, 2026.08.12, cyWu, 改由 mcu_link 从 MCU 0x02 写入
|
||||
* - V1.3.0, 2026.08.12, cyWu, 改由 business/mcu_identity 从 MCU 0x02 写入
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef __USR_LE_PRODUCT_H__
|
||||
@@ -30,13 +32,13 @@ extern "C" {
|
||||
|
||||
/**
|
||||
* @brief 产品类别码,对应 BLE 广播 prodcode(固定 4 字节 ASCII)
|
||||
* @note 由 mcu_bridge 从 MCU 身份应答取前 4 字节写入
|
||||
* @note 由 business/mcu_identity 从 MCU 身份应答取前 4 字节写入
|
||||
*/
|
||||
extern uint8_t g_usr_le_prodcode[USR_LE_PRODCODE_LEN];
|
||||
|
||||
/**
|
||||
* @brief 平台产品码,对应 BLE 广播 pcode(固定 6 字节 ASCII)
|
||||
* @note 由 mcu_bridge 从 MCU 身份应答取前 6 字节写入
|
||||
* @note 由 business/mcu_identity 从 MCU 身份应答取前 6 字节写入
|
||||
*/
|
||||
extern uint8_t g_usr_le_pcode[USR_LE_PCODE_LEN];
|
||||
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
/******************************************************************************
|
||||
* @file mcu_business.c
|
||||
* @brief 业务门面实现
|
||||
* @author cyWu <1917507415@qq.com>
|
||||
* @date 2026.08.12
|
||||
* @version V1.0.0
|
||||
* @history
|
||||
* - V1.0.0, 2026.08.12, cyWu, 取代原 mcu_link,编排三个业务子模块
|
||||
******************************************************************************/
|
||||
|
||||
#include "system/includes.h"
|
||||
#include "app_main.h"
|
||||
#include "mcu_business.h"
|
||||
#include "mcu_identity.h"
|
||||
#include "mcu_pairing.h"
|
||||
#include "mcu_link_status.h"
|
||||
#include "mcu_cmd.h"
|
||||
|
||||
#define LOG_TAG_CONST APP
|
||||
#define LOG_TAG "[MCU_BIZ]"
|
||||
#define LOG_ERROR_ENABLE
|
||||
#define LOG_INFO_ENABLE
|
||||
#include "debug.h"
|
||||
|
||||
/**
|
||||
* @brief 初始化业务层
|
||||
* @note 上电不强制配网;广播由 mcu_identity_on_ack 在身份就绪后按
|
||||
* 配对状态触发(usr_ble_adv_init 内部决策)
|
||||
*/
|
||||
void mcu_business_init(void)
|
||||
{
|
||||
mcu_identity_init();
|
||||
mcu_pairing_init();
|
||||
mcu_link_status_init();
|
||||
|
||||
usr_var.usr_goto_pair = 0;
|
||||
usr_var.usr_adv_flag = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 周期处理
|
||||
*/
|
||||
void mcu_business_periodic(void)
|
||||
{
|
||||
mcu_identity_periodic();
|
||||
mcu_link_status_periodic();
|
||||
mcu_pairing_periodic();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 是否身份就绪
|
||||
* @return 1/0
|
||||
*/
|
||||
uint8_t mcu_business_is_identity_ready(void)
|
||||
{
|
||||
return mcu_identity_is_ready();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取工作状态
|
||||
* @return work_status
|
||||
*/
|
||||
uint8_t mcu_business_get_work_status(void)
|
||||
{
|
||||
return mcu_link_status_get();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 处理身份应答
|
||||
* @param frame 完整帧
|
||||
* @param len 长度
|
||||
*/
|
||||
void mcu_business_on_identity_ack(const uint8_t *frame, uint16_t len)
|
||||
{
|
||||
mcu_identity_on_ack(frame, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 处理配网请求
|
||||
* @param mode 模式
|
||||
* @param timeout_s 超时秒
|
||||
*/
|
||||
void mcu_business_on_pair_cfg(uint8_t mode, uint16_t timeout_s)
|
||||
{
|
||||
mcu_pairing_start(mode, timeout_s);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 配网成功
|
||||
*/
|
||||
void mcu_business_on_paired(void)
|
||||
{
|
||||
usr_var.usr_goto_pair = 0;
|
||||
mcu_pairing_stop();
|
||||
mcu_link_status_set_phase(MCU_WS_CONFIGURED);
|
||||
log_info("paired -> CONFIGURED");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 配对清除
|
||||
* @note 若正处于配网流程(phase==PAIRING),保持不变;
|
||||
* 否则(例如单纯清空配对表)回到未配网空闲
|
||||
*/
|
||||
void mcu_business_on_pair_cleared(void)
|
||||
{
|
||||
if (mcu_link_status_get_phase() != MCU_WS_PAIRING) {
|
||||
mcu_link_status_set_phase(0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/******************************************************************************
|
||||
* @file mcu_business.h
|
||||
* @brief 业务门面:协调身份 / 配网 / 连接状态三个业务子模块
|
||||
* @author cyWu <1917507415@qq.com>
|
||||
* @date 2026.08.12
|
||||
* @version V1.0.0
|
||||
* @history
|
||||
* - V1.0.0, 2026.08.12, cyWu, 取代原 mcu_link,供协议层(mcu_protocol)
|
||||
* 单点调用;协议层不直接依赖 identity/pairing/link_status 三个子模块
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef __MCU_BUSINESS_H__
|
||||
#define __MCU_BUSINESS_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/*============================================================================*/
|
||||
/* 说明 */
|
||||
/* 本模块只做业务编排,不含具体状态;具体逻辑见: */
|
||||
/* mcu_identity.c —— 身份获取 */
|
||||
/* mcu_pairing.c —— 配网请求 / 超时 */
|
||||
/* mcu_link_status.c —— 连接状态位图(Bit0/1/2) */
|
||||
/* 须在协议任务(mcu_protocol_task)单一上下文中调用。 */
|
||||
/*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief 初始化业务层(身份 + 配网 + 连接状态)
|
||||
*/
|
||||
void mcu_business_init(void);
|
||||
|
||||
/**
|
||||
* @brief 周期处理:依次驱动身份 / 连接状态 / 配网三个子业务
|
||||
*/
|
||||
void mcu_business_periodic(void);
|
||||
|
||||
/**
|
||||
* @brief 是否已从 MCU 获取到设备身份
|
||||
* @return 1=已就绪,0=未就绪
|
||||
*/
|
||||
uint8_t mcu_business_is_identity_ready(void);
|
||||
|
||||
/**
|
||||
* @brief 获取当前工作状态字节(命令 0x14 载荷)
|
||||
* @return 工作状态 bitmap
|
||||
*/
|
||||
uint8_t mcu_business_get_work_status(void);
|
||||
|
||||
/**
|
||||
* @brief 处理 0x02 身份应答
|
||||
* @param frame 完整帧
|
||||
* @param len 长度
|
||||
*/
|
||||
void mcu_business_on_identity_ack(const uint8_t *frame, uint16_t len);
|
||||
|
||||
/**
|
||||
* @brief 处理已通过协议校验的 0x10 配网请求
|
||||
* @param mode 1 或 2
|
||||
* @param timeout_s 超时秒数,0 表示不超时
|
||||
*/
|
||||
void mcu_business_on_pair_cfg(uint8_t mode, uint16_t timeout_s);
|
||||
|
||||
/**
|
||||
* @brief 配网成功(AUTHSETUP 写对)
|
||||
*/
|
||||
void mcu_business_on_paired(void);
|
||||
|
||||
/**
|
||||
* @brief 配对信息被清除
|
||||
*/
|
||||
void mcu_business_on_pair_cleared(void);
|
||||
|
||||
#endif /* __MCU_BUSINESS_H__ */
|
||||
@@ -0,0 +1,139 @@
|
||||
/******************************************************************************
|
||||
* @file mcu_identity.c
|
||||
* @brief 身份业务实现
|
||||
* @author cyWu <1917507415@qq.com>
|
||||
* @date 2026.08.12
|
||||
* @version V1.0.0
|
||||
* @history
|
||||
* - V1.0.0, 2026.08.12, cyWu, 从 mcu_link 拆出
|
||||
******************************************************************************/
|
||||
|
||||
#include "system/includes.h"
|
||||
#include "mcu_identity.h"
|
||||
#include "mcu_protocol.h"
|
||||
#include "mcu_cmd.h"
|
||||
#include "mcu_util.h"
|
||||
#include "usr_le_api.h"
|
||||
#include "usr_le_product.h"
|
||||
|
||||
#define LOG_TAG_CONST APP
|
||||
#define LOG_TAG "[MCU_ID]"
|
||||
#define LOG_ERROR_ENABLE
|
||||
#define LOG_DEBUG_ENABLE
|
||||
#define LOG_INFO_ENABLE
|
||||
#include "debug.h"
|
||||
|
||||
/*============================================================================*/
|
||||
/* 私有宏 */
|
||||
/*============================================================================*/
|
||||
|
||||
#define MCU_IDENTITY_RETRY_MS (1000u)
|
||||
#define MCU_IDENTITY_PAYLOAD_MIN (2 + 8 + 8 + 16 + 8) /* Ver+Cat+Prod+SN+Cap */
|
||||
|
||||
/*============================================================================*/
|
||||
/* 公有变量:运行时可改写的产品身份(供 BLE 广播使用) */
|
||||
/*============================================================================*/
|
||||
|
||||
uint8_t g_usr_le_prodcode[USR_LE_PRODCODE_LEN];
|
||||
uint8_t g_usr_le_pcode[USR_LE_PCODE_LEN];
|
||||
|
||||
/*============================================================================*/
|
||||
/* 私有类型/变量 */
|
||||
/*============================================================================*/
|
||||
|
||||
typedef struct {
|
||||
uint8_t ready;
|
||||
uint32_t last_tx_ms;
|
||||
} McuIdentity_t;
|
||||
|
||||
static McuIdentity_t s_identity;
|
||||
|
||||
/*============================================================================*/
|
||||
/* 公有函数 */
|
||||
/*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief 初始化身份业务状态
|
||||
*/
|
||||
void mcu_identity_init(void)
|
||||
{
|
||||
memset(&s_identity, 0, sizeof(s_identity));
|
||||
memset(g_usr_le_prodcode, 0, sizeof(g_usr_le_prodcode));
|
||||
memset(g_usr_le_pcode, 0, sizeof(g_usr_le_pcode));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 身份未就绪时每 1s 发 0x01
|
||||
*/
|
||||
void mcu_identity_periodic(void)
|
||||
{
|
||||
uint32_t now;
|
||||
|
||||
if (s_identity.ready) {
|
||||
return;
|
||||
}
|
||||
|
||||
now = mcu_util_get_ms();
|
||||
if ((now - s_identity.last_tx_ms) < MCU_IDENTITY_RETRY_MS) {
|
||||
return;
|
||||
}
|
||||
s_identity.last_tx_ms = now;
|
||||
|
||||
if (mcu_protocol_send_cmd(MCU_CMD_GET_IDENTITY, NULL, 0) > 0) {
|
||||
log_debug("identity req");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 是否身份就绪
|
||||
* @return 1/0
|
||||
*/
|
||||
uint8_t mcu_identity_is_ready(void)
|
||||
{
|
||||
return s_identity.ready;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 处理 0x02 身份应答
|
||||
* @param frame 完整帧
|
||||
* @param len 长度
|
||||
* @note 载荷布局(Result 之后):Ver(2)+Category(8)+Product(8)+SN(16)+Cap(8)
|
||||
*/
|
||||
void mcu_identity_on_ack(const uint8_t *frame, uint16_t len)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!frame || 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_identity.ready = 1;
|
||||
|
||||
printf("identity ok prodcode:");
|
||||
for (i = 0; i < USR_LE_PRODCODE_LEN; i++) {
|
||||
printf("%c", g_usr_le_prodcode[i]);
|
||||
}
|
||||
printf("\n");
|
||||
printf("identity ok pcode:");
|
||||
for (i = 0; i < USR_LE_PCODE_LEN; i++) {
|
||||
printf("%c", g_usr_le_pcode[i]);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
/* 身份就绪:强制协议层重推 0x14,并刷新广播(决策见 usr_ble_adv_init) */
|
||||
mcu_protocol_invalidate_work_status_push();
|
||||
usr_ble_adv_init();
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/******************************************************************************
|
||||
* @file mcu_identity.h
|
||||
* @brief 身份业务:获取并持有 MCU 产品身份(prodcode/pcode)
|
||||
* @author cyWu <1917507415@qq.com>
|
||||
* @date 2026.08.12
|
||||
* @version V1.0.0
|
||||
* @history
|
||||
* - V1.0.0, 2026.08.12, cyWu, 从 mcu_link 拆出,独立为身份业务模块
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef __MCU_IDENTITY_H__
|
||||
#define __MCU_IDENTITY_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* @brief 初始化身份业务状态
|
||||
*/
|
||||
void mcu_identity_init(void);
|
||||
|
||||
/**
|
||||
* @brief 周期处理:未就绪时每 1s 重发 0x01 身份请求
|
||||
* @note 仅在协议任务上下文调用
|
||||
*/
|
||||
void mcu_identity_periodic(void);
|
||||
|
||||
/**
|
||||
* @brief 是否已从 MCU 获取到设备身份
|
||||
* @return 1=已就绪,0=未就绪
|
||||
*/
|
||||
uint8_t mcu_identity_is_ready(void);
|
||||
|
||||
/**
|
||||
* @brief 处理 0x02 身份应答
|
||||
* @param frame 完整帧
|
||||
* @param len 长度
|
||||
* @note 成功后写入 g_usr_le_prodcode/g_usr_le_pcode,并刷新 BLE 广播
|
||||
*/
|
||||
void mcu_identity_on_ack(const uint8_t *frame, uint16_t len);
|
||||
|
||||
#endif /* __MCU_IDENTITY_H__ */
|
||||
@@ -0,0 +1,129 @@
|
||||
/******************************************************************************
|
||||
* @file mcu_link_status.c
|
||||
* @brief 连接状态业务实现
|
||||
* @author cyWu <1917507415@qq.com>
|
||||
* @date 2026.08.12
|
||||
* @version V1.0.0
|
||||
* @history
|
||||
* - V1.0.0, 2026.08.12, cyWu, 从 mcu_link 拆出
|
||||
******************************************************************************/
|
||||
|
||||
#include "system/includes.h"
|
||||
#include "app_main.h"
|
||||
#include "mcu_link_status.h"
|
||||
#include "mcu_identity.h"
|
||||
#include "mcu_cmd.h"
|
||||
#include "usr_le_api.h"
|
||||
|
||||
#define LOG_TAG_CONST APP
|
||||
#define LOG_TAG "[MCU_LNK]"
|
||||
#define LOG_ERROR_ENABLE
|
||||
#define LOG_DEBUG_ENABLE
|
||||
#define LOG_INFO_ENABLE
|
||||
#include "debug.h"
|
||||
|
||||
/*============================================================================*/
|
||||
/* 外部符号 */
|
||||
/*============================================================================*/
|
||||
|
||||
extern u16 usr_get_conn_service(void);
|
||||
|
||||
/*============================================================================*/
|
||||
/* 私有变量 */
|
||||
/*============================================================================*/
|
||||
|
||||
typedef struct {
|
||||
uint8_t work_status;
|
||||
uint8_t last_ble_conn;
|
||||
} McuLinkStatus_t;
|
||||
|
||||
static McuLinkStatus_t s_status;
|
||||
|
||||
/*============================================================================*/
|
||||
/* 公有函数 */
|
||||
/*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief 初始化连接状态
|
||||
*/
|
||||
void mcu_link_status_init(void)
|
||||
{
|
||||
memset(&s_status, 0, sizeof(s_status));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取完整工作状态字节
|
||||
* @return work_status
|
||||
*/
|
||||
uint8_t mcu_link_status_get(void)
|
||||
{
|
||||
return s_status.work_status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取当前 Bit0/1/2 互斥过程态
|
||||
* @return phase
|
||||
*/
|
||||
uint8_t mcu_link_status_get_phase(void)
|
||||
{
|
||||
return (uint8_t)(s_status.work_status & MCU_WS_LINK_MASK);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置 Bit0/1/2 互斥过程态
|
||||
* @param phase 目标过程态
|
||||
*/
|
||||
void mcu_link_status_set_phase(uint8_t phase)
|
||||
{
|
||||
s_status.work_status &= (uint8_t)~MCU_WS_LINK_MASK;
|
||||
if (phase & MCU_WS_LINK_MASK) {
|
||||
s_status.work_status |= (uint8_t)(phase & MCU_WS_LINK_MASK);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置/清除 Bit6 OTA
|
||||
* @param on 1/0
|
||||
*/
|
||||
void mcu_link_status_set_ota(uint8_t on)
|
||||
{
|
||||
if (on) {
|
||||
s_status.work_status |= MCU_WS_OTA_UPGRADING;
|
||||
} else {
|
||||
s_status.work_status &= (uint8_t)~MCU_WS_OTA_UPGRADING;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 周期处理:轮询 BLE 连接,维护 Bit0/1/2,已配网断连时开回连广播
|
||||
* @note - 配网中(phase==PAIRING)由配网业务维护,此处不覆盖
|
||||
* - 已配网:已连接→CONFIGURED,未连接→RECONNECTING
|
||||
* - Bit3~5 预留固定 0,不在此处更新
|
||||
*/
|
||||
void mcu_link_status_periodic(void)
|
||||
{
|
||||
uint8_t conn = (usr_get_conn_service() != 0) ? 1 : 0;
|
||||
uint8_t paired = usr_get_pair_flag();
|
||||
|
||||
if (conn != s_status.last_ble_conn) {
|
||||
s_status.last_ble_conn = conn;
|
||||
|
||||
if (!conn && paired) {
|
||||
/* 已配网断连:发回连广播(非绑定广播) */
|
||||
usr_var.usr_goto_pair = 0;
|
||||
if (mcu_identity_is_ready()) {
|
||||
usr_ble_adv_init();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mcu_link_status_get_phase() == MCU_WS_PAIRING) {
|
||||
return; /* 配网流程中不切换到回连/已配网 */
|
||||
}
|
||||
|
||||
if (paired) {
|
||||
mcu_link_status_set_phase(conn ? MCU_WS_CONFIGURED : MCU_WS_RECONNECTING);
|
||||
} else {
|
||||
mcu_link_status_set_phase(0); /* 未配网空闲 */
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/******************************************************************************
|
||||
* @file mcu_link_status.h
|
||||
* @brief 连接状态业务:工作状态位图(Bit0/1/2 互斥过程态 + OTA 位)
|
||||
* @author cyWu <1917507415@qq.com>
|
||||
* @date 2026.08.12
|
||||
* @version V1.0.0
|
||||
* @history
|
||||
* - V1.0.0, 2026.08.12, cyWu, 从 mcu_link 拆出,独立为连接状态业务模块
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef __MCU_LINK_STATUS_H__
|
||||
#define __MCU_LINK_STATUS_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* @brief 初始化连接状态
|
||||
*/
|
||||
void mcu_link_status_init(void);
|
||||
|
||||
/**
|
||||
* @brief 周期处理:轮询 BLE 连接,维护 Bit0/1/2,已配网断连时触发回连广播
|
||||
* @note 配网中(phase==PAIRING)时不会被本函数覆盖
|
||||
*/
|
||||
void mcu_link_status_periodic(void);
|
||||
|
||||
/**
|
||||
* @brief 获取完整工作状态字节(命令 0x14 载荷)
|
||||
* @return work_status
|
||||
*/
|
||||
uint8_t mcu_link_status_get(void);
|
||||
|
||||
/**
|
||||
* @brief 获取当前 Bit0/1/2 互斥过程态
|
||||
* @return MCU_WS_PAIRING / MCU_WS_CONFIGURED / MCU_WS_RECONNECTING / 0
|
||||
*/
|
||||
uint8_t mcu_link_status_get_phase(void);
|
||||
|
||||
/**
|
||||
* @brief 设置 Bit0/1/2 互斥过程态
|
||||
* @param phase MCU_WS_PAIRING / MCU_WS_CONFIGURED / MCU_WS_RECONNECTING / 0
|
||||
* @note 只改 Bit0/1/2,不影响 Bit6 OTA 等独立位
|
||||
*/
|
||||
void mcu_link_status_set_phase(uint8_t phase);
|
||||
|
||||
/**
|
||||
* @brief 设置/清除 Bit6 OTA 升级中
|
||||
* @param on 1=置位,0=清除
|
||||
* @note 预留给后续 OTA 模块调用
|
||||
*/
|
||||
void mcu_link_status_set_ota(uint8_t on);
|
||||
|
||||
#endif /* __MCU_LINK_STATUS_H__ */
|
||||
@@ -0,0 +1,108 @@
|
||||
/******************************************************************************
|
||||
* @file mcu_pairing.c
|
||||
* @brief 配网业务实现
|
||||
* @author cyWu <1917507415@qq.com>
|
||||
* @date 2026.08.12
|
||||
* @version V1.0.0
|
||||
* @history
|
||||
* - V1.0.0, 2026.08.12, cyWu, 从 mcu_link 拆出
|
||||
******************************************************************************/
|
||||
|
||||
#include "system/includes.h"
|
||||
#include "app_main.h"
|
||||
#include "mcu_pairing.h"
|
||||
#include "mcu_link_status.h"
|
||||
#include "mcu_cmd.h"
|
||||
#include "mcu_util.h"
|
||||
#include "usr_le_api.h"
|
||||
|
||||
#define LOG_TAG_CONST APP
|
||||
#define LOG_TAG "[MCU_PAIR]"
|
||||
#define LOG_ERROR_ENABLE
|
||||
#define LOG_DEBUG_ENABLE
|
||||
#define LOG_INFO_ENABLE
|
||||
#include "debug.h"
|
||||
|
||||
/*============================================================================*/
|
||||
/* 外部符号 */
|
||||
/*============================================================================*/
|
||||
|
||||
extern void ble_trans_module_enable(u8 flag);
|
||||
|
||||
/*============================================================================*/
|
||||
/* 私有变量 */
|
||||
/*============================================================================*/
|
||||
|
||||
typedef struct {
|
||||
uint16_t timeout_s;
|
||||
uint32_t start_ms;
|
||||
} McuPairing_t;
|
||||
|
||||
static McuPairing_t s_pairing;
|
||||
|
||||
/*============================================================================*/
|
||||
/* 公有函数 */
|
||||
/*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief 初始化配网业务状态
|
||||
*/
|
||||
void mcu_pairing_init(void)
|
||||
{
|
||||
memset(&s_pairing, 0, sizeof(s_pairing));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 开始配网
|
||||
* @param mode 模式(预留)
|
||||
* @param timeout_s 超时秒数
|
||||
*/
|
||||
void mcu_pairing_start(uint8_t mode, uint16_t timeout_s)
|
||||
{
|
||||
s_pairing.timeout_s = timeout_s;
|
||||
s_pairing.start_ms = mcu_util_get_ms();
|
||||
mcu_link_status_set_phase(MCU_WS_PAIRING);
|
||||
|
||||
log_info("pairing start mode=%u timeout=%u", mode, timeout_s);
|
||||
usr_goto_pair_mode(); /* 清配对 + 置 usr_goto_pair + 发绑定广播 */
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 停止配网计时
|
||||
*/
|
||||
void mcu_pairing_stop(void)
|
||||
{
|
||||
s_pairing.timeout_s = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 配网超时检测
|
||||
*/
|
||||
void mcu_pairing_periodic(void)
|
||||
{
|
||||
uint32_t now;
|
||||
uint32_t timeout_ms;
|
||||
|
||||
if (mcu_link_status_get_phase() != MCU_WS_PAIRING) {
|
||||
return;
|
||||
}
|
||||
if (s_pairing.timeout_s == 0) {
|
||||
return;
|
||||
}
|
||||
if (usr_get_pair_flag()) {
|
||||
return; /* 已配网成功,交由 mcu_business_on_paired 处理切态 */
|
||||
}
|
||||
|
||||
now = mcu_util_get_ms();
|
||||
timeout_ms = (uint32_t)s_pairing.timeout_s * 1000u;
|
||||
if ((now - s_pairing.start_ms) < timeout_ms) {
|
||||
return;
|
||||
}
|
||||
|
||||
log_info("pairing timeout, stop adv");
|
||||
usr_var.usr_goto_pair = 0;
|
||||
s_pairing.timeout_s = 0;
|
||||
mcu_link_status_set_phase(0);
|
||||
ble_trans_module_enable(0);
|
||||
usr_var.usr_adv_flag = 0;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/******************************************************************************
|
||||
* @file mcu_pairing.h
|
||||
* @brief 配网业务:0x10 配网请求处理、配网超时退出
|
||||
* @author cyWu <1917507415@qq.com>
|
||||
* @date 2026.08.12
|
||||
* @version V1.0.0
|
||||
* @history
|
||||
* - V1.0.0, 2026.08.12, cyWu, 从 mcu_link 拆出,独立为配网业务模块
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef __MCU_PAIRING_H__
|
||||
#define __MCU_PAIRING_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* @brief 初始化配网业务状态
|
||||
*/
|
||||
void mcu_pairing_init(void);
|
||||
|
||||
/**
|
||||
* @brief 周期处理:配网超时检测
|
||||
* @note 仅在 mcu_link_status_get_phase()==MCU_WS_PAIRING 时生效
|
||||
*/
|
||||
void mcu_pairing_periodic(void);
|
||||
|
||||
/**
|
||||
* @brief 开始配网(协议层已完成参数校验与 ACK)
|
||||
* @param mode 1 或 2(预留,当前两种模式行为一致)
|
||||
* @param timeout_s 超时秒数,0 表示不超时
|
||||
* @note 置 Bit0=配网中,并触发绑定广播(usr_goto_pair_mode)
|
||||
*/
|
||||
void mcu_pairing_start(uint8_t mode, uint16_t timeout_s);
|
||||
|
||||
/**
|
||||
* @brief 停止配网计时(配网成功后调用)
|
||||
*/
|
||||
void mcu_pairing_stop(void);
|
||||
|
||||
#endif /* __MCU_PAIRING_H__ */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,13 +1,16 @@
|
||||
/******************************************************************************
|
||||
* @file mcu_bridge.h
|
||||
* @brief BLE+MCU 协议桥对外 API(模组侧唯一业务入口)
|
||||
* @brief BLE+MCU 接入模块对外总入口(组合门面)
|
||||
* @author cyWu <1917507415@qq.com>
|
||||
* @date 2026.08.06
|
||||
* @version V1.1.0
|
||||
* @date 2026.08.12
|
||||
* @version V2.0.0
|
||||
* @history
|
||||
* - V1.0.0, 2026.08.06, cyWu, 首次发布
|
||||
* - V1.1.0, 2026.08.06, cyWu, 内部改为事件队列 + 独立任务驱动;
|
||||
* 所有 on_xxx 接口均可在任意任务上下文安全调用(生产者-消费者模型)
|
||||
* - V1.1.0, 2026.08.06, cyWu, 内部改为事件队列 + 独立任务驱动
|
||||
* - V1.2.0, 2026.08.12, cyWu, 与 mcu_link 解耦
|
||||
* - V2.0.0, 2026.08.12, cyWu, 整体重构为 protocol/ + business/ 分层:
|
||||
* 本文件不再包含任何实现逻辑,只做“组合根”,对外 API 保持不变,
|
||||
* 外部调用方(app_main.c / usr_le_adv.c / usr_le_recieve.c 等)无需改动
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef __MCU_BRIDGE_H__
|
||||
@@ -16,6 +19,28 @@
|
||||
#include <stdint.h>
|
||||
#include "mcu_cmd.h"
|
||||
|
||||
/*============================================================================*/
|
||||
/* 说明:整体分层 */
|
||||
/* */
|
||||
/* apps/usr_mcu_bridge/ */
|
||||
/* ├── mcu_bridge.c/h —— 本文件:组合根,稳定对外 API */
|
||||
/* ├── protocol/ —— 协议层:只做协议机制,不含业务判断 */
|
||||
/* │ ├── mcu_cmd.h 命令字 / 结果码 / work_status 位定义 */
|
||||
/* │ ├── mcu_frame.c/h 55AA 组帧/解帧 */
|
||||
/* │ ├── mcu_uart.c/h UART 收发驱动 */
|
||||
/* │ ├── mcu_util.c/h 校验和/大端序/毫秒工具 */
|
||||
/* │ └── mcu_protocol.c/h 事件队列/任务/分发/ACK/透传重传/心跳 */
|
||||
/* └── business/ —— 业务层:按业务拆分,互不直接依赖对方实现 */
|
||||
/* ├── mcu_identity.c/h 身份业务(0x01/0x02) */
|
||||
/* ├── mcu_pairing.c/h 配网业务(0x10、超时) */
|
||||
/* ├── mcu_link_status.c/h 连接状态业务(Bit0/1/2/6 位图) */
|
||||
/* └── mcu_business.c/h 业务门面:协调以上三者,供协议层调用 */
|
||||
/* */
|
||||
/* 依赖方向(单向,禁止反向 include): */
|
||||
/* mcu_bridge → protocol → business(子模块) → business(门面) */
|
||||
/* business 子模块只经 mcu_protocol_send_cmd 等窄接口回调协议层发送 */
|
||||
/*============================================================================*/
|
||||
|
||||
/*============================================================================*/
|
||||
/* 错误码 */
|
||||
/*============================================================================*/
|
||||
@@ -30,14 +55,12 @@ typedef enum {
|
||||
} McuBridge_Ret_t;
|
||||
|
||||
/*============================================================================*/
|
||||
/* 外部接口 */
|
||||
/* 外部接口(稳定 API,供 app_main / usr_le_code 等外部模块调用) */
|
||||
/*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief 初始化 MCU 协议桥(UART + 事件队列 + 独立任务)
|
||||
* @brief 初始化 MCU 接入模块(组合 protocol + business)
|
||||
* @return McuBridge_Ret_t
|
||||
* @note 在 app_main 中调用一次即可;内部会创建 "mcu_bridge" 任务,
|
||||
* 所有身份/心跳/透传/状态机逻辑均在该任务单一上下文中处理
|
||||
*/
|
||||
McuBridge_Ret_t mcu_bridge_init(void);
|
||||
|
||||
@@ -45,8 +68,6 @@ 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);
|
||||
|
||||
@@ -54,34 +75,28 @@ 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 协议栈任务上下文调用(内部投递事件异步处理)
|
||||
* @brief 配网成功通知(BLE 协议栈任一上下文调用均可,内部会入队串行处理)
|
||||
*/
|
||||
void mcu_bridge_on_paired(void);
|
||||
|
||||
/**
|
||||
* @brief 配对信息被清除通知
|
||||
* @note 可安全在 BLE 协议栈任务上下文调用(内部投递事件异步处理)
|
||||
*/
|
||||
void mcu_bridge_on_pair_cleared(void);
|
||||
|
||||
/**
|
||||
* @brief 获取当前工作状态字节
|
||||
* @brief 获取当前工作状态字节(命令 0x14 载荷)
|
||||
* @return 工作状态 bitmap
|
||||
* @note 单字节只读快照,可在任意任务直接调用
|
||||
*/
|
||||
uint8_t mcu_bridge_get_work_status(void);
|
||||
|
||||
@@ -90,8 +105,6 @@ uint8_t mcu_bridge_get_work_status(void);
|
||||
* @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,
|
||||
|
||||
+19
-8
@@ -2,10 +2,12 @@
|
||||
* @file mcu_cmd.h
|
||||
* @brief MCU 接入协议命令字 / 结果码 / 工作状态位定义
|
||||
* @author cyWu <1917507415@qq.com>
|
||||
* @date 2026.08.06
|
||||
* @version V1.0.0
|
||||
* @date 2026.08.12
|
||||
* @version V1.1.0
|
||||
* @history
|
||||
* - V1.0.0, 2026.08.06, cyWu, 首次发布
|
||||
* - V1.1.0, 2026.08.12, cyWu, 重定义工作状态位:Bit0/1/2 互斥过程态;
|
||||
* 随协议层迁入 protocol/ 目录
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef __MCU_CMD_H__
|
||||
@@ -15,14 +17,23 @@
|
||||
|
||||
/*============================================================================*/
|
||||
/* 工作状态 Bit(命令 0x14) */
|
||||
/* */
|
||||
/* Bit0/1/2 互斥(至多一个为 1;可全 0=未配网空闲): */
|
||||
/* Bit0 配网中 / Bit1 已配网正常 / Bit2 断线重连中 */
|
||||
/* Bit3~5、Bit7 预留,固定 0;Bit6 为 OTA,可与过程态独立叠加 */
|
||||
/*============================================================================*/
|
||||
|
||||
#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) /**< 配网模式中 */
|
||||
#define MCU_WS_PAIRING (1u << 0) /**< 配网中(绑定广播) */
|
||||
#define MCU_WS_CONFIGURED (1u << 1) /**< 已配网正常态(已连接/稳定工作) */
|
||||
#define MCU_WS_RECONNECTING (1u << 2) /**< 断线重连中(回连广播) */
|
||||
#define MCU_WS_CLOUD_ONLINE (1u << 3) /**< 已连接到云,预留:暂固定 0 */
|
||||
#define MCU_WS_APP_ONLINE (1u << 4) /**< APP 在线,预留:暂固定 0 */
|
||||
#define MCU_WS_APP_DIRECT (1u << 5) /**< APP 直连,预留:暂固定 0 */
|
||||
#define MCU_WS_OTA_UPGRADING (1u << 6) /**< 模组 OTA 升级中 */
|
||||
/* Bit7 预留 */
|
||||
|
||||
/** Bit0/1/2 过程态掩码(互斥组) */
|
||||
#define MCU_WS_LINK_MASK (MCU_WS_PAIRING | MCU_WS_CONFIGURED | MCU_WS_RECONNECTING)
|
||||
|
||||
/*============================================================================*/
|
||||
/* 命令字 */
|
||||
@@ -0,0 +1,881 @@
|
||||
/******************************************************************************
|
||||
* @file mcu_protocol.c
|
||||
* @brief MCU 接入协议引擎实现
|
||||
* @author cyWu <1917507415@qq.com>
|
||||
* @date 2026.08.12
|
||||
* @version V1.1.0
|
||||
* @history
|
||||
* - V1.0.0, 2026.08.12, cyWu, 从 mcu_bridge.c 拆出(历史版本见原文件):
|
||||
* 事件队列 + 独立任务驱动、LP target、心跳/透传重传均原样保留
|
||||
* - V1.1.0, 2026.08.12, cyWu, 0x07 上报改为不缓存、不超时重发:
|
||||
* MCU 自身已有超时重发机制,模组侧只记 SN 转发+等 ACK 回 0x08,
|
||||
* 去掉 report_pending 整包缓存与 mcu_protocol_check_report_pending()
|
||||
******************************************************************************/
|
||||
|
||||
#include "system/includes.h"
|
||||
#include "asm/power/power_api.h"
|
||||
#include "mcu_protocol.h"
|
||||
#include "mcu_uart.h"
|
||||
#include "mcu_frame.h"
|
||||
#include "mcu_util.h"
|
||||
#include "usr_le_api.h"
|
||||
#include "mcu_business.h"
|
||||
|
||||
#define LOG_TAG_CONST APP
|
||||
#define LOG_TAG "[MCU_PROTO]"
|
||||
#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_HEARTBEAT_IDLE_MS (55000u)
|
||||
#define MCU_HEARTBEAT_MAX_RETRY (3u)
|
||||
#define MCU_UART_ACK_WAIT_MS (500u) /* 等 MCU UART ACK 超时(心跳/透传/0x14 等) */
|
||||
#define MCU_APP_ACK_MAX_RETRY (3u)
|
||||
|
||||
#define MCU_PROTOCOL_TICK_MS (200u)
|
||||
/* 杰理 os_sem_pend 超时单位是 OS tick,OS_TICKS_PER_SEC=100 → 1 tick = 10ms */
|
||||
#define MCU_PROTOCOL_TICK_OS ((MCU_PROTOCOL_TICK_MS + 9u) / 10u)
|
||||
#define MCU_EVT_QUEUE_DEPTH (8u)
|
||||
#define MCU_PROTOCOL_TASK_PRIO (31u)
|
||||
#define MCU_PROTOCOL_TASK_STACK (2048u)
|
||||
|
||||
/*============================================================================*/
|
||||
/* 私有类型 */
|
||||
/*============================================================================*/
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t active;
|
||||
uint8_t sn;
|
||||
uint8_t expect_ack;
|
||||
uint8_t retry;
|
||||
uint32_t send_time;
|
||||
uint8_t buf[MCU_FRAME_MAX_LEN];
|
||||
uint16_t len;
|
||||
} McuPending_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
MCU_EVT_UART_FRAME = 0,
|
||||
MCU_EVT_APP_FRAME,
|
||||
MCU_EVT_GATEWAY_ACK,
|
||||
MCU_EVT_BLE_PAIRED,
|
||||
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 sn;
|
||||
uint8_t last_pushed_status;
|
||||
uint8_t hb_fail_cnt;
|
||||
uint8_t hb_waiting;
|
||||
uint8_t mcu_comm_alarm;
|
||||
uint32_t last_mcu_rx_ms;
|
||||
uint32_t last_hb_tx_ms;
|
||||
McuPending_t app_pending; /* APP→MCU 透传等待 */
|
||||
McuPending_t ble_pending; /* BLE 主动请求等待(心跳/状态等) */
|
||||
uint8_t report_waiting; /* 1=已转网关,等 ACK 后再回 MCU 0x08 */
|
||||
uint8_t report_sn; /* 等待回 0x08 的 SN */
|
||||
} McuProtocol_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
McuEvt_t queue[MCU_EVT_QUEUE_DEPTH];
|
||||
uint8_t head;
|
||||
uint8_t tail;
|
||||
uint8_t count;
|
||||
OS_MUTEX lock;
|
||||
OS_SEM sem;
|
||||
} McuEvtQueue_t;
|
||||
|
||||
/*============================================================================*/
|
||||
/* 私有变量 */
|
||||
/*============================================================================*/
|
||||
|
||||
static McuProtocol_t s_proto;
|
||||
static uint8_t s_parse_out[MCU_FRAME_MAX_LEN];
|
||||
static volatile u8 s_protocol_busy = 0;
|
||||
static McuEvtQueue_t s_evt_q;
|
||||
|
||||
/*============================================================================*/
|
||||
/* 私有函数声明 */
|
||||
/*============================================================================*/
|
||||
|
||||
static uint8_t mcu_protocol_next_sn(void);
|
||||
static int32_t mcu_protocol_send_frame(uint8_t cmd, uint8_t sn,
|
||||
const uint8_t *payload, uint16_t plen);
|
||||
static void mcu_protocol_send_ack(uint8_t cmd, uint8_t sn, uint8_t result);
|
||||
static void mcu_protocol_handle_frame(const uint8_t *frame, uint16_t len);
|
||||
static void mcu_protocol_handle_pair_cfg(const uint8_t *frame, uint16_t len);
|
||||
static void mcu_protocol_handle_report(const uint8_t *frame, uint16_t len);
|
||||
static void mcu_protocol_handle_app_frame(const uint8_t *data, uint16_t len);
|
||||
static void mcu_protocol_handle_gateway_ack(uint16_t cmd, uint8_t gateway_int_cnt,
|
||||
int32_t gateway_int0);
|
||||
static void mcu_protocol_try_heartbeat(void);
|
||||
static void mcu_protocol_try_work_status(void);
|
||||
static void mcu_protocol_check_app_pending(void);
|
||||
static void mcu_protocol_check_ble_pending(void);
|
||||
static void mcu_protocol_periodic(void);
|
||||
static uint8_t mcu_protocol_post_event(const McuEvt_t *evt);
|
||||
static uint8_t mcu_protocol_pop_event(McuEvt_t *evt);
|
||||
static void mcu_protocol_dispatch_event(const McuEvt_t *evt);
|
||||
static void mcu_protocol_task(void *arg);
|
||||
|
||||
/*============================================================================*/
|
||||
/* 事件队列 */
|
||||
/*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief 投递事件并唤醒协议任务
|
||||
* @param evt 事件
|
||||
* @return 1=成功,0=队列满
|
||||
*/
|
||||
static uint8_t mcu_protocol_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=空
|
||||
*/
|
||||
static uint8_t mcu_protocol_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_protocol_task 上下文执行,s_proto 在此单一上下文内读写
|
||||
*/
|
||||
static void mcu_protocol_dispatch_event(const McuEvt_t *evt)
|
||||
{
|
||||
switch (evt->type)
|
||||
{
|
||||
case MCU_EVT_UART_FRAME:
|
||||
mcu_protocol_handle_frame(evt->buf, evt->len);
|
||||
break;
|
||||
case MCU_EVT_APP_FRAME:
|
||||
mcu_protocol_handle_app_frame(evt->buf, evt->len);
|
||||
break;
|
||||
case MCU_EVT_GATEWAY_ACK:
|
||||
mcu_protocol_handle_gateway_ack(evt->gw_cmd, evt->gw_int_cnt, evt->gw_int0);
|
||||
break;
|
||||
case MCU_EVT_BLE_PAIRED:
|
||||
mcu_business_on_paired();
|
||||
break;
|
||||
case MCU_EVT_BLE_PAIR_CLEARED:
|
||||
mcu_business_on_pair_cleared();
|
||||
break;
|
||||
default:
|
||||
log_error("unknown evt type=%d", evt->type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 协议引擎主任务:事件驱动 + 兜底轮询
|
||||
* @param arg 未使用
|
||||
*/
|
||||
static void mcu_protocol_task(void *arg)
|
||||
{
|
||||
McuEvt_t evt;
|
||||
|
||||
(void)arg;
|
||||
log_info("protocol task start");
|
||||
|
||||
while (1)
|
||||
{
|
||||
os_sem_pend(&s_evt_q.sem, (int)MCU_PROTOCOL_TICK_OS);
|
||||
|
||||
s_protocol_busy = 1;
|
||||
while (mcu_protocol_pop_event(&evt))
|
||||
{
|
||||
mcu_protocol_dispatch_event(&evt);
|
||||
}
|
||||
mcu_protocol_periodic();
|
||||
s_protocol_busy = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 低功耗空闲查询:协议任务忙则不允许进 sleep
|
||||
* @return 1=可睡,0=保持唤醒
|
||||
*/
|
||||
static u8 mcu_protocol_lp_idle_query(void)
|
||||
{
|
||||
return s_protocol_busy ? 0 : 1;
|
||||
}
|
||||
|
||||
REGISTER_LP_TARGET(mcu_protocol_lp_target) = {
|
||||
.name = "mcu_protocol",
|
||||
.is_idle = mcu_protocol_lp_idle_query,
|
||||
};
|
||||
|
||||
/*============================================================================*/
|
||||
/* 发送 */
|
||||
/*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief 取下一个 SN
|
||||
* @return SN
|
||||
*/
|
||||
static uint8_t mcu_protocol_next_sn(void)
|
||||
{
|
||||
return s_proto.sn++;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 组帧发送
|
||||
* @param cmd 命令
|
||||
* @param sn 序列号
|
||||
* @param payload 载荷
|
||||
* @param plen 长度
|
||||
* @return 帧长;<0 失败
|
||||
*/
|
||||
static int32_t mcu_protocol_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 自动 SN 发送(供 business 层调用)
|
||||
* @param cmd 命令
|
||||
* @param payload 载荷
|
||||
* @param plen 长度
|
||||
* @return 帧长;<0 失败
|
||||
*/
|
||||
int32_t mcu_protocol_send_cmd(uint8_t cmd, const uint8_t *payload, uint16_t plen)
|
||||
{
|
||||
return mcu_protocol_send_frame(cmd, mcu_protocol_next_sn(), payload, plen);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 发送仅含 Result 的 ACK 帧
|
||||
* @param cmd ACK 命令
|
||||
* @param sn 序列号
|
||||
* @param result 结果码
|
||||
*/
|
||||
static void mcu_protocol_send_ack(uint8_t cmd, uint8_t sn, uint8_t result)
|
||||
{
|
||||
mcu_protocol_send_frame(cmd, sn, &result, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 强制重推工作状态
|
||||
*/
|
||||
void mcu_protocol_invalidate_work_status_push(void)
|
||||
{
|
||||
s_proto.last_pushed_status = 0xFF;
|
||||
}
|
||||
|
||||
/*============================================================================*/
|
||||
/* 命令处理 */
|
||||
/*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief 处理 0x10:协议校验 + ACK,业务交给 business 层
|
||||
* @param frame 完整帧
|
||||
* @param len 长度
|
||||
*/
|
||||
static void mcu_protocol_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_protocol_send_ack(MCU_CMD_PAIR_CFG_ACK, sn, MCU_RESULT_PARAM_ERR);
|
||||
return;
|
||||
}
|
||||
if (!mcu_business_is_identity_ready())
|
||||
{
|
||||
mcu_protocol_send_ack(MCU_CMD_PAIR_CFG_ACK, sn, MCU_RESULT_BUSY);
|
||||
return;
|
||||
}
|
||||
|
||||
mcu_protocol_send_ack(MCU_CMD_PAIR_CFG_ACK, sn, MCU_RESULT_OK);
|
||||
mcu_business_on_pair_cfg(mode, timeout_s);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 处理 MCU 上报 0x07:转网关,等网关 ACK 后再回 0x08
|
||||
* @param frame 完整帧
|
||||
* @param len 长度
|
||||
* @note 不缓存重发、不超时回失败;MCU 自己会超时重发 0x07,
|
||||
* 这里收到新的 0x07 会直接覆盖上一次等待中的 SN
|
||||
*/
|
||||
static void mcu_protocol_handle_report(const uint8_t *frame, uint16_t len)
|
||||
{
|
||||
uint8_t sn = frame[5];
|
||||
|
||||
s_proto.report_sn = sn;
|
||||
s_proto.report_waiting = 1;
|
||||
usr_le_send_mcu_frame((u8 *)frame, len);
|
||||
log_debug("MCU report sn=%u -> gateway, wait ACK", sn);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 分发 MCU 完整帧
|
||||
* @param frame 完整帧
|
||||
* @param len 长度
|
||||
*/
|
||||
static void mcu_protocol_handle_frame(const uint8_t *frame, uint16_t len)
|
||||
{
|
||||
uint8_t cmd;
|
||||
uint8_t sn;
|
||||
|
||||
if (!frame || len < 6)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
s_proto.last_mcu_rx_ms = mcu_util_get_ms();
|
||||
s_proto.hb_fail_cnt = 0;
|
||||
s_proto.hb_waiting = 0;
|
||||
s_proto.mcu_comm_alarm = 0;
|
||||
|
||||
cmd = frame[4];
|
||||
sn = frame[5];
|
||||
log_debug("MCU RX cmd=0x%02X sn=%u len=%u", cmd, sn, len);
|
||||
|
||||
if (s_proto.app_pending.active &&
|
||||
(cmd == s_proto.app_pending.expect_ack) &&
|
||||
(sn == s_proto.app_pending.sn))
|
||||
{
|
||||
s_proto.app_pending.active = 0;
|
||||
usr_le_send_mcu_frame((u8 *)frame, len);
|
||||
return;
|
||||
}
|
||||
|
||||
if (s_proto.ble_pending.active &&
|
||||
(cmd == s_proto.ble_pending.expect_ack) &&
|
||||
(sn == s_proto.ble_pending.sn))
|
||||
{
|
||||
s_proto.ble_pending.active = 0;
|
||||
if (cmd == MCU_CMD_HEARTBEAT_ACK)
|
||||
{
|
||||
s_proto.hb_waiting = 0;
|
||||
s_proto.hb_fail_cnt = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
switch (cmd)
|
||||
{
|
||||
case MCU_CMD_GET_IDENTITY_ACK:
|
||||
mcu_business_on_identity_ack(frame, len);
|
||||
break;
|
||||
case MCU_CMD_PAIR_CFG:
|
||||
mcu_protocol_handle_pair_cfg(frame, len);
|
||||
break;
|
||||
case MCU_CMD_REPORT:
|
||||
mcu_protocol_handle_report(frame, len);
|
||||
break;
|
||||
case MCU_CMD_CONTROL_ACK:
|
||||
case MCU_CMD_READ_ACK:
|
||||
usr_le_send_mcu_frame((u8 *)frame, len);
|
||||
break;
|
||||
case MCU_CMD_HEARTBEAT_ACK:
|
||||
case MCU_CMD_WORK_STATUS_ACK:
|
||||
break;
|
||||
default:
|
||||
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_protocol_send_ack((uint8_t)(cmd + 1), sn, MCU_RESULT_NOT_SUPPORT);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 处理 APP 下发 55AA
|
||||
* @param data 帧
|
||||
* @param len 长度
|
||||
*/
|
||||
static void mcu_protocol_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
|
||||
{
|
||||
mcu_uart_send(data, len);
|
||||
return;
|
||||
}
|
||||
|
||||
if (s_proto.app_pending.active)
|
||||
{
|
||||
log_error("app frame busy, drop cmd=0x%02X", cmd);
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(s_proto.app_pending.buf, data, len);
|
||||
s_proto.app_pending.len = len;
|
||||
s_proto.app_pending.sn = sn;
|
||||
s_proto.app_pending.expect_ack = expect_ack;
|
||||
s_proto.app_pending.retry = 0;
|
||||
s_proto.app_pending.send_time = mcu_util_get_ms();
|
||||
s_proto.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 处理网关对 0x07 的应答
|
||||
* @param cmd bleproto cmd
|
||||
* @param gateway_int_cnt int 个数
|
||||
* @param gateway_int0 int0
|
||||
*/
|
||||
static void mcu_protocol_handle_gateway_ack(uint16_t cmd,
|
||||
uint8_t gateway_int_cnt,
|
||||
int32_t gateway_int0)
|
||||
{
|
||||
uint8_t sn;
|
||||
|
||||
if (!s_proto.report_waiting)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (cmd == 0 && gateway_int_cnt == 1 && gateway_int0 == 0)
|
||||
{
|
||||
sn = s_proto.report_sn;
|
||||
mcu_protocol_send_ack(MCU_CMD_REPORT_ACK, sn, MCU_RESULT_OK);
|
||||
s_proto.report_waiting = 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);
|
||||
}
|
||||
|
||||
/*============================================================================*/
|
||||
/* 周期:心跳 / 状态推送 / pending 重试 */
|
||||
/*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief 心跳
|
||||
*/
|
||||
static void mcu_protocol_try_heartbeat(void)
|
||||
{
|
||||
uint32_t now = mcu_util_get_ms();
|
||||
uint8_t sn;
|
||||
uint8_t buf[MCU_FRAME_MAX_LEN];
|
||||
int32_t total;
|
||||
|
||||
if (!mcu_business_is_identity_ready())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (s_proto.hb_waiting)
|
||||
{
|
||||
if ((now - s_proto.last_hb_tx_ms) <= MCU_UART_ACK_WAIT_MS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
s_proto.hb_waiting = 0;
|
||||
s_proto.ble_pending.active = 0;
|
||||
s_proto.hb_fail_cnt++;
|
||||
log_error("heartbeat timeout fail=%u", s_proto.hb_fail_cnt);
|
||||
if (s_proto.hb_fail_cnt >= MCU_HEARTBEAT_MAX_RETRY)
|
||||
{
|
||||
s_proto.mcu_comm_alarm = 1;
|
||||
log_error("MCU comm alarm");
|
||||
s_proto.last_mcu_rx_ms = now;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if ((now - s_proto.last_mcu_rx_ms) < MCU_HEARTBEAT_IDLE_MS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
sn = mcu_protocol_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_proto.ble_pending.active = 1;
|
||||
s_proto.ble_pending.sn = sn;
|
||||
s_proto.ble_pending.expect_ack = MCU_CMD_HEARTBEAT_ACK;
|
||||
s_proto.ble_pending.send_time = now;
|
||||
s_proto.ble_pending.retry = 0;
|
||||
s_proto.last_hb_tx_ms = now;
|
||||
s_proto.hb_waiting = 1;
|
||||
log_debug("heartbeat tx sn=%u", sn);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 工作状态变化时推送 0x14(状态值来自 business 层)
|
||||
*/
|
||||
static void mcu_protocol_try_work_status(void)
|
||||
{
|
||||
uint8_t payload[2];
|
||||
uint8_t sn;
|
||||
uint8_t buf[MCU_FRAME_MAX_LEN];
|
||||
int32_t total;
|
||||
uint32_t now;
|
||||
uint8_t ws;
|
||||
|
||||
if (!mcu_business_is_identity_ready())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ws = mcu_business_get_work_status();
|
||||
if (ws == s_proto.last_pushed_status)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (s_proto.ble_pending.active)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
payload[0] = ws;
|
||||
payload[1] = 0x00;
|
||||
|
||||
sn = mcu_protocol_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_proto.ble_pending.active = 1;
|
||||
s_proto.ble_pending.sn = sn;
|
||||
s_proto.ble_pending.expect_ack = MCU_CMD_WORK_STATUS_ACK;
|
||||
s_proto.ble_pending.send_time = now;
|
||||
s_proto.last_pushed_status = ws;
|
||||
log_info("work_status push 0x%02X", ws);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief APP 透传超时重发;重试用尽后主动回 FAIL,不让 APP 干等
|
||||
*/
|
||||
static void mcu_protocol_check_app_pending(void)
|
||||
{
|
||||
uint32_t now;
|
||||
uint8_t buf[MCU_FRAME_MAX_LEN];
|
||||
uint8_t result;
|
||||
int32_t total;
|
||||
|
||||
if (!s_proto.app_pending.active)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
now = mcu_util_get_ms();
|
||||
if ((now - s_proto.app_pending.send_time) <= MCU_UART_ACK_WAIT_MS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (s_proto.app_pending.retry >= MCU_APP_ACK_MAX_RETRY)
|
||||
{
|
||||
log_error("app pending give up cmd expect=0x%02X, notify APP FAIL",
|
||||
s_proto.app_pending.expect_ack);
|
||||
s_proto.app_pending.active = 0;
|
||||
|
||||
result = MCU_RESULT_FAIL;
|
||||
total = mcu_frame_pack(buf, sizeof(buf), s_proto.app_pending.expect_ack,
|
||||
s_proto.app_pending.sn, &result, 1);
|
||||
if (total > 0)
|
||||
{
|
||||
usr_le_send_mcu_frame(buf, (uint16_t)total);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
mcu_uart_send(s_proto.app_pending.buf, s_proto.app_pending.len);
|
||||
s_proto.app_pending.retry++;
|
||||
s_proto.app_pending.send_time = now;
|
||||
log_debug("app pending retry %u", s_proto.app_pending.retry);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief BLE 主动帧 pending 超时清理
|
||||
*/
|
||||
static void mcu_protocol_check_ble_pending(void)
|
||||
{
|
||||
uint32_t now;
|
||||
|
||||
if (!s_proto.ble_pending.active)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (s_proto.ble_pending.expect_ack == MCU_CMD_HEARTBEAT_ACK)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
now = mcu_util_get_ms();
|
||||
if ((now - s_proto.ble_pending.send_time) > MCU_UART_ACK_WAIT_MS)
|
||||
{
|
||||
if (s_proto.ble_pending.expect_ack == MCU_CMD_WORK_STATUS_ACK)
|
||||
{
|
||||
log_error("work_status ack timeout, will retry");
|
||||
s_proto.last_pushed_status = 0xFF;
|
||||
}
|
||||
s_proto.ble_pending.active = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 周期处理:串口防睡 / 业务周期 / pending 重试 / 心跳 / 状态推送
|
||||
*/
|
||||
static void mcu_protocol_periodic(void)
|
||||
{
|
||||
mcu_uart_rx_keep_awake_tick(); /* 串口防睡 */
|
||||
mcu_business_periodic(); /* 业务周期 */
|
||||
mcu_protocol_check_app_pending(); /* APP 透传超时重发;重试用尽后主动回 FAIL,不让 APP 干等 */
|
||||
mcu_protocol_check_ble_pending(); /* BLE 主动帧 pending 超时清理 */
|
||||
mcu_protocol_try_heartbeat(); /* 心跳 */
|
||||
mcu_protocol_try_work_status(); /* 工作状态变化时推送 0x14(状态值来自 business 层) */
|
||||
}
|
||||
|
||||
/*============================================================================*/
|
||||
/* 公有入口 */
|
||||
/*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief 初始化协议引擎
|
||||
* @return 1=成功,0=失败
|
||||
*/
|
||||
uint8_t mcu_protocol_init(void)
|
||||
{
|
||||
if (s_proto.initialized)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
memset(&s_proto, 0, sizeof(s_proto));
|
||||
mcu_frame_parser_reset();
|
||||
mcu_business_init();
|
||||
|
||||
if (mcu_uart_init() != MCU_UART_OK)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
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_proto.last_mcu_rx_ms = mcu_util_get_ms();
|
||||
s_proto.last_pushed_status = 0xFF;
|
||||
s_proto.initialized = 1;
|
||||
|
||||
os_task_create(mcu_protocol_task, NULL, MCU_PROTOCOL_TASK_PRIO,
|
||||
MCU_PROTOCOL_TASK_STACK, 0, "mcu_bridge");
|
||||
|
||||
log_info("mcu_protocol_init ok");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief UART RX 生产者
|
||||
* @param data 数据
|
||||
* @param len 长度
|
||||
*/
|
||||
void mcu_protocol_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_proto.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);
|
||||
printf("UART valid data:");
|
||||
printf_buf(evt.buf, evt.len);
|
||||
mcu_protocol_post_event(&evt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief APP 帧生产者
|
||||
* @param data 帧
|
||||
* @param len 长度
|
||||
*/
|
||||
void mcu_protocol_on_app_frame(const uint8_t *data, uint16_t len)
|
||||
{
|
||||
McuEvt_t evt;
|
||||
|
||||
if (!s_proto.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_protocol_post_event(&evt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 配网成功入队
|
||||
*/
|
||||
void mcu_protocol_notify_paired(void)
|
||||
{
|
||||
McuEvt_t evt;
|
||||
|
||||
memset(&evt, 0, sizeof(evt));
|
||||
evt.type = MCU_EVT_BLE_PAIRED;
|
||||
mcu_protocol_post_event(&evt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 配对清除入队
|
||||
*/
|
||||
void mcu_protocol_notify_pair_cleared(void)
|
||||
{
|
||||
McuEvt_t evt;
|
||||
|
||||
memset(&evt, 0, sizeof(evt));
|
||||
evt.type = MCU_EVT_BLE_PAIR_CLEARED;
|
||||
mcu_protocol_post_event(&evt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 网关上报应答入队
|
||||
* @param cmd cmd
|
||||
* @param gateway_int_cnt cnt
|
||||
* @param gateway_int0 int0
|
||||
*/
|
||||
void mcu_protocol_notify_gateway_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_protocol_post_event(&evt);
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/******************************************************************************
|
||||
* @file mcu_protocol.h
|
||||
* @brief MCU 接入协议引擎:事件队列 / 任务 / 命令分发 / ACK / 透传重传 / 心跳
|
||||
* @author cyWu <1917507415@qq.com>
|
||||
* @date 2026.08.12
|
||||
* @version V1.0.0
|
||||
* @history
|
||||
* - V1.0.0, 2026.08.12, cyWu, 从 mcu_bridge.c 拆出,仅保留协议机制,
|
||||
* 不含任何业务判断(身份/配网/连接状态见 ../business)
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef __MCU_PROTOCOL_H__
|
||||
#define __MCU_PROTOCOL_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include "mcu_cmd.h"
|
||||
|
||||
/*============================================================================*/
|
||||
/* 说明 */
|
||||
/* 本模块只做协议机制:组帧收发、事件队列驱动的单一任务、命令分发、 */
|
||||
/* ACK 与超时重传、心跳保活。所有业务判断(该不该配网/该发什么广播/ */
|
||||
/* work_status 具体取值)一律转交 business 层,本模块不感知业务细节。 */
|
||||
/*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief 初始化协议引擎(UART + 事件队列 + 独立任务 + business 层)
|
||||
* @return 1=成功,0=失败
|
||||
*/
|
||||
uint8_t mcu_protocol_init(void);
|
||||
|
||||
/**
|
||||
* @brief UART 收到原始数据(由 mcu_uart 接收任务调用)
|
||||
* @param data 数据
|
||||
* @param len 长度
|
||||
*/
|
||||
void mcu_protocol_on_uart_rx(const uint8_t *data, uint16_t len);
|
||||
|
||||
/**
|
||||
* @brief APP/网关经 BLE 下发的 55AA 帧
|
||||
* @param data 帧数据
|
||||
* @param len 长度
|
||||
*/
|
||||
void mcu_protocol_on_app_frame(const uint8_t *data, uint16_t len);
|
||||
|
||||
/**
|
||||
* @brief 配网成功通知(入队,交由协议任务在自身上下文调用 business 层)
|
||||
*/
|
||||
void mcu_protocol_notify_paired(void);
|
||||
|
||||
/**
|
||||
* @brief 配对信息被清除通知(入队)
|
||||
*/
|
||||
void mcu_protocol_notify_pair_cleared(void);
|
||||
|
||||
/**
|
||||
* @brief 网关对 REPORTCMD_V2 主动上报的应答(入队)
|
||||
* @param cmd bleproto 包 cmd
|
||||
* @param gateway_int_cnt gateway_int 个数
|
||||
* @param gateway_int0 gateway_int[0]
|
||||
*/
|
||||
void mcu_protocol_notify_gateway_ack(uint16_t cmd,
|
||||
uint8_t gateway_int_cnt,
|
||||
int32_t gateway_int0);
|
||||
|
||||
/*============================================================================*/
|
||||
/* 供 business 层调用的协议发送接口 */
|
||||
/*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief 自动分配 SN 并发送一帧到 MCU
|
||||
* @param cmd 命令字
|
||||
* @param payload 载荷,可为 NULL
|
||||
* @param plen 载荷长度
|
||||
* @return 帧长;<0 失败
|
||||
*/
|
||||
int32_t mcu_protocol_send_cmd(uint8_t cmd, const uint8_t *payload, uint16_t plen);
|
||||
|
||||
/**
|
||||
* @brief 强制下一轮重新推送 0x14(业务状态变化后调用)
|
||||
*/
|
||||
void mcu_protocol_invalidate_work_status_push(void);
|
||||
|
||||
#endif /* __MCU_PROTOCOL_H__ */
|
||||
+1
-1
@@ -44,7 +44,7 @@ static OS_MUTEX s_tx_lock;
|
||||
|
||||
/*
|
||||
* 防睡计数:唤醒口或 UART 收到数据时置 MCU_UART_RX_KEEP_AWAKE_TICKS;
|
||||
* 由 mcu_bridge 主循环周期递减;非 0 时 LP target 禁止进 sleep。
|
||||
* 由 mcu_protocol 主循环周期递减;非 0 时 LP target 禁止进 sleep。
|
||||
*/
|
||||
static volatile u8 s_usr_uart_recieve = 0;
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@
|
||||
#define MCU_UART_RX_PIN IO_PORTA_08 /**< MCU 通信 UART RX(board 唤醒口须一致) */
|
||||
|
||||
/*
|
||||
* sleep 唤醒或串口收数后保持唤醒的 tick 数(mcu_bridge 约每 200ms 递减一次)。
|
||||
* sleep 唤醒或串口收数后保持唤醒的 tick 数(mcu_protocol 约每 200ms 递减一次)。
|
||||
* 取大一些:第一包可能在唤醒过程中丢失,留给对端协议重试窗口(约 3s)。
|
||||
*/
|
||||
#define MCU_UART_RX_KEEP_AWAKE_TICKS (15u)
|
||||
Reference in New Issue
Block a user