1、新增Dengle定时参数下发功能,支持最多10条配置,断电保存到Flash,COM打开时暂停发送、关闭后恢复
2、新增USB查询产品码(0x66)和查询Dengle配置(0x74)命令 3、修复bleproto组包栈溢出导致设置Dengle后复位的问题,并完善CDC的DTR状态判断
This commit is contained in:
@@ -171,22 +171,25 @@ static u32 cdc_setup(struct usb_device_t *usb_device, struct usb_ctrlrequest *ct
|
||||
dump_line_coding((struct usb_cdc_line_coding *)cdc_hdl->subtype_data);
|
||||
break;
|
||||
case USB_CDC_REQ_SET_CONTROL_LINE_STATE:
|
||||
log_debug("set control line state - %d", ctrl_req->wValue);
|
||||
/* wValue.bit0=DTR(打开COM通常置1), bit1=RTS */
|
||||
log_info("set control line state wValue=0x%04x DTR=%d RTS=%d",
|
||||
ctrl_req->wValue,
|
||||
!!(ctrl_req->wValue & BIT(0)),
|
||||
!!(ctrl_req->wValue & BIT(1)));
|
||||
if (cdc_hdl) {
|
||||
/* if (ctrl_req->wValue & BIT(0)) { //DTR */
|
||||
cdc_hdl->bmTransceiver |= BIT(0);
|
||||
/* } else { */
|
||||
/* usb_slave->cdc->bmTransceiver &= ~BIT(0); */
|
||||
/* } */
|
||||
/* if (ctrl_req->wValue & BIT(1)) { //RTS */
|
||||
cdc_hdl->bmTransceiver |= BIT(1);
|
||||
/* } else { */
|
||||
/* usb_slave->cdc->bmTransceiver &= ~BIT(1); */
|
||||
/* } */
|
||||
cdc_hdl->bmTransceiver |= BIT(4); //cfg done
|
||||
if (ctrl_req->wValue & BIT(0)) { /* DTR */
|
||||
cdc_hdl->bmTransceiver |= BIT(0);
|
||||
} else {
|
||||
cdc_hdl->bmTransceiver &= ~BIT(0);
|
||||
}
|
||||
if (ctrl_req->wValue & BIT(1)) { /* RTS */
|
||||
cdc_hdl->bmTransceiver |= BIT(1);
|
||||
} else {
|
||||
cdc_hdl->bmTransceiver &= ~BIT(1);
|
||||
}
|
||||
cdc_hdl->bmTransceiver |= BIT(4); /* cfg done */
|
||||
}
|
||||
usb_set_setup_phase(usb_device, USB_EP0_STAGE_SETUP);
|
||||
//cdc_endpoint_init(usb_device, (ctrl_req->wIndex & USB_RECIP_MASK));
|
||||
break;
|
||||
default:
|
||||
log_error("unsupported class req");
|
||||
@@ -238,6 +241,10 @@ static void cdc_reset(struct usb_device_t *usb_device, u32 itf)
|
||||
{
|
||||
log_error("%s()", __func__);
|
||||
const usb_dev usb_id = usb_device2id(usb_device);
|
||||
/* USB 复位/重枚举时清掉 COM 开闭状态 */
|
||||
if (cdc_hdl) {
|
||||
cdc_hdl->bmTransceiver &= ~(BIT(0) | BIT(1));
|
||||
}
|
||||
#if USB_ROOT2
|
||||
usb_disable_ep(usb_id, CDC_DATA_EP_IN);
|
||||
#if CDC_INTR_EP_ENABLE
|
||||
@@ -487,4 +494,17 @@ void cdc_release(const usb_dev usb_id)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 上位机是否已打开 COM(CDC DTR 有效)
|
||||
* @return 1=COM 已打开 0=未打开/已关闭
|
||||
*/
|
||||
u8 cdc_is_com_open(void)
|
||||
{
|
||||
if (cdc_hdl == NULL) {
|
||||
return 0;
|
||||
}
|
||||
/* DTR=1 表示主机打开了虚拟串口 */
|
||||
return (cdc_hdl->bmTransceiver & BIT(0)) ? 1 : 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -15,6 +15,11 @@ u32 cdc_write_data(const usb_dev usb_id, u8 *buf, u32 len);
|
||||
u32 cdc_write_inir(const usb_dev usb_id, u8 *buf, u32 len);
|
||||
void cdc_register(const usb_dev usb_id);
|
||||
void cdc_release(const usb_dev usb_id);
|
||||
/**
|
||||
* @brief 上位机是否已打开 COM(依据 CDC DTR)
|
||||
* @return 1=已打开 0=未打开
|
||||
*/
|
||||
u8 cdc_is_com_open(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -522,15 +522,7 @@ void usr_ble_send_data(u8* data,u16 len)
|
||||
{
|
||||
app_send_user_data(ATT_CHARACTERISTIC_15f1e601_a277_43fc_a484_dd39ef8a9100_01_VALUE_HANDLE, data, len, ATT_OP_AUTO_READ_CCC);
|
||||
}
|
||||
//printf("send rsp:");
|
||||
// printf_buf(data,len);
|
||||
printf("\n");
|
||||
for(u8 i=0;i<len;i++)
|
||||
{
|
||||
printf("%c",data[i]);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
/* 勿把二进制当字符打到调试串口,否则会出现 $m/-$m 等乱码并拖慢日志 */
|
||||
}
|
||||
static int app_send_user_data(u16 handle, u8 *data, u16 len, u8 handle_type)
|
||||
{
|
||||
|
||||
@@ -237,7 +237,9 @@ void app_main()
|
||||
usr_read_device_sta();
|
||||
{
|
||||
extern void usr_prodcode_init(void);
|
||||
extern void dengle_sched_init(void);
|
||||
usr_prodcode_init();
|
||||
dengle_sched_init();
|
||||
}
|
||||
usr_var.usr_goto_pair = 1;
|
||||
usr_clear_pair_info_noreset();
|
||||
|
||||
+28
-26
@@ -222,91 +222,93 @@ int bleproto_appdata_encode(uint8_t * txbuf,
|
||||
{
|
||||
int rc;
|
||||
uint16_t txbuf_size = size;
|
||||
/* 原栈上 appdata 约 2KB+,在 USB/定时器回调里极易栈溢出复位,改为静态 */
|
||||
static bleproto_appdata_t s_enc_appdata;
|
||||
bleproto_appdata_t *appdata = &s_enc_appdata;
|
||||
|
||||
bleproto_appdata_t appdata;
|
||||
/* header */
|
||||
{
|
||||
appdata.header = desc->header;
|
||||
appdata->header = desc->header;
|
||||
}
|
||||
/* data */
|
||||
if (appdata.header.msgtype == BLEPROTO_APPDATA_MSGTYPE_REQ) {
|
||||
switch (appdata.header.serviceid) {
|
||||
if (appdata->header.msgtype == BLEPROTO_APPDATA_MSGTYPE_REQ) {
|
||||
switch (appdata->header.serviceid) {
|
||||
case E_BLEPROTO_SERVICE_ID_DEVICEINFO:
|
||||
rc = bleproto_encjson_deviceinfo_req(&desc->datast.deviceinfo_req,
|
||||
appdata.data);
|
||||
appdata->data);
|
||||
break;
|
||||
case E_BLEPROTO_SERVICE_ID_AUTHSETUP:
|
||||
rc = bleproto_encjson_authsetup_req(&desc->datast.authsetup_req,
|
||||
appdata.data);
|
||||
appdata->data);
|
||||
break;
|
||||
case E_BLEPROTO_SERVICE_ID_AUTHDELETE:
|
||||
rc = bleproto_encjson_authdelete_req(&desc->datast.authdelete_req,
|
||||
appdata.data);
|
||||
appdata->data);
|
||||
break;
|
||||
case E_BLEPROTO_SERVICE_ID_RUNCMD:
|
||||
rc = bleproto_encjson_runcmd_req(&desc->datast.runcmd_req,
|
||||
appdata.data);
|
||||
appdata->data);
|
||||
break;
|
||||
case E_BLEPROTO_SERVICE_ID_REPORTCMD:
|
||||
rc = bleproto_encjson_reportcmd_req(&desc->datast.reportcmd_req,
|
||||
appdata.data);
|
||||
appdata->data);
|
||||
break;
|
||||
case E_BLEPROTO_SERVICE_ID_DOACTION:
|
||||
rc = bleproto_encjson_doaction_req(&desc->datast.doaction_req,
|
||||
appdata.data);
|
||||
appdata->data);
|
||||
break;
|
||||
case E_BLEPROTO_SERVICE_ID_OTANOTIFY:
|
||||
rc = bleproto_encjson_otanotify_req(&desc->datast.otanotify_req,
|
||||
appdata.data);
|
||||
appdata->data);
|
||||
break;
|
||||
case E_BLEPROTO_SERVICE_ID_OTADATA:
|
||||
rc = bleproto_encjson_otadata_req(&desc->datast.otadata_req,
|
||||
appdata.data);
|
||||
appdata->data);
|
||||
break;
|
||||
case E_BLEPROTO_SERVICE_ID_REPORTCMD_V2:
|
||||
rc = ble_proto_pack(&desc->datast.runcmd_v2_req, appdata.data, sizeof(appdata.data));
|
||||
rc = ble_proto_pack(&desc->datast.runcmd_v2_req, appdata->data, sizeof(appdata->data));
|
||||
break;
|
||||
default:
|
||||
/* unkown serviceid */
|
||||
return (-1);
|
||||
}
|
||||
} else if (appdata.header.msgtype == BLEPROTO_APPDATA_MSGTYPE_RSP) {
|
||||
switch (appdata.header.serviceid) {
|
||||
} else if (appdata->header.msgtype == BLEPROTO_APPDATA_MSGTYPE_RSP) {
|
||||
switch (appdata->header.serviceid) {
|
||||
case E_BLEPROTO_SERVICE_ID_DEVICEINFO:
|
||||
rc = bleproto_encjson_deviceinfo_rsp(&desc->datast.deviceinfo_rsp,
|
||||
appdata.data);
|
||||
appdata->data);
|
||||
break;
|
||||
case E_BLEPROTO_SERVICE_ID_AUTHSETUP:
|
||||
rc = bleproto_encjson_authsetup_rsp(&desc->datast.authsetup_rsp,
|
||||
appdata.data);
|
||||
appdata->data);
|
||||
break;
|
||||
case E_BLEPROTO_SERVICE_ID_AUTHDELETE:
|
||||
rc = bleproto_encjson_authdelete_rsp(&desc->datast.authdelete_rsp,
|
||||
appdata.data);
|
||||
appdata->data);
|
||||
break;
|
||||
case E_BLEPROTO_SERVICE_ID_RUNCMD:
|
||||
rc = bleproto_encjson_runcmd_rsp(&desc->datast.runcmd_rsp,
|
||||
appdata.data);
|
||||
appdata->data);
|
||||
break;
|
||||
case E_BLEPROTO_SERVICE_ID_REPORTCMD:
|
||||
rc = bleproto_encjson_reportcmd_rsp(&desc->datast.reportcmd_rsp,
|
||||
appdata.data);
|
||||
appdata->data);
|
||||
break;
|
||||
case E_BLEPROTO_SERVICE_ID_DOACTION:
|
||||
rc = bleproto_encjson_doaction_rsp(&desc->datast.doaction_rsp,
|
||||
appdata.data);
|
||||
appdata->data);
|
||||
break;
|
||||
case E_BLEPROTO_SERVICE_ID_OTANOTIFY:
|
||||
rc = bleproto_encjson_otanotify_rsp(&desc->datast.otanotify_rsp,
|
||||
appdata.data);
|
||||
appdata->data);
|
||||
break;
|
||||
case E_BLEPROTO_SERVICE_ID_OTADATA:
|
||||
rc = bleproto_encbin_otadata_rsp(&desc->datast.otadata_rsp,
|
||||
appdata.data);
|
||||
appdata->data);
|
||||
break;
|
||||
case E_BLEPROTO_SERVICE_ID_REPORTCMD_V2:
|
||||
case E_BLEPROTO_SERVICE_ID_RUNCMD_V2:
|
||||
rc = ble_proto_pack(&desc->datast.runcmd_v2_rsp, appdata.data, sizeof(appdata.data));
|
||||
rc = ble_proto_pack(&desc->datast.runcmd_v2_rsp, appdata->data, sizeof(appdata->data));
|
||||
break;
|
||||
default:
|
||||
/* unkown serviceid */
|
||||
@@ -322,10 +324,10 @@ int bleproto_appdata_encode(uint8_t * txbuf,
|
||||
}
|
||||
|
||||
/* datalen */
|
||||
appdata.header.datalen = (uint16_t)rc;
|
||||
appdata->header.datalen = (uint16_t)rc;
|
||||
|
||||
/* appdata_serialize */
|
||||
return bleproto_appdata_serialize(&appdata, packetlen, txbuf, txbuf_size);
|
||||
return bleproto_appdata_serialize(appdata, packetlen, txbuf, txbuf_size);
|
||||
}
|
||||
|
||||
int bleproto_appdata_decode(uint8_t* rxbuf,
|
||||
|
||||
@@ -0,0 +1,662 @@
|
||||
/******************************************************************************
|
||||
* @file usr_dengle_sched.c
|
||||
* @brief Dengle 参数定时下发实现(含 Flash 断电保存)
|
||||
* @author cyWu <1917507415@qq.com>
|
||||
* @date 2026.07.30
|
||||
* @version V1.2.0
|
||||
* @history
|
||||
* - V1.0.0, 2026.07.30, cyWu, 首次发布
|
||||
* - V1.1.0, 2026.07.30, cyWu, 增加 Flash 持久化
|
||||
* - V1.1.1, 2026.07.30, cyWu, 修复设置后立即发送导致栈溢出复位
|
||||
* - V1.2.0, 2026.07.30, cyWu, USB 连接时暂停定时发送
|
||||
* - V1.2.1, 2026.07.30, cyWu, 改为 COM(DTR) 开闭控制暂停/恢复
|
||||
*
|
||||
* USB 设置帧 payload(CMD=0x70)格式:
|
||||
* [0] slot 槽位 0~9
|
||||
* [1] enable 0=关闭该槽 1=使能
|
||||
* [2..5] period_ms 发送周期(大端 ms,建议 >=100)
|
||||
* [6..7] cmd_id CBCT/CBFD→ycmd.cmd_id;其它→ble_proto.cmd(大端)
|
||||
* [8] data_n int 模式= int32 个数;bytes 模式= 字节数
|
||||
* [9..] data int 模式= data_n 个大端 int32;
|
||||
* bytes 模式= data_n 字节原始数据
|
||||
*
|
||||
* 发送策略(按设备当前 prodcode):
|
||||
* - CBCT / CBFD → REPORTCMD + ycmd.arg_int32[](参考 usr_test_bleproto_runcmd)
|
||||
* - 其它 → REPORTCMD_V2 + bytes_data(参考 usr_test_protocol)
|
||||
******************************************************************************/
|
||||
|
||||
#include "system/includes.h"
|
||||
#include "app_config.h"
|
||||
#include "usr_dengle_sched.h"
|
||||
#include "usr_usb_jb.h"
|
||||
#include "bleproto_api.h"
|
||||
#include "syscfg_id.h"
|
||||
#if TCFG_USB_SLAVE_CDC_ENABLE
|
||||
#include "usb/device/cdc.h"
|
||||
#endif
|
||||
|
||||
#define LOG_TAG_CONST APP
|
||||
#define LOG_TAG "[DENGLE]"
|
||||
#define LOG_ERROR_ENABLE
|
||||
#define LOG_DEBUG_ENABLE
|
||||
#define LOG_INFO_ENABLE
|
||||
#define LOG_CLI_ENABLE
|
||||
#include "debug.h"
|
||||
|
||||
/*============================================================================*/
|
||||
/* 私有宏 */
|
||||
/*============================================================================*/
|
||||
|
||||
#define DENGLE_TX_BUF_SZ (384)
|
||||
#define DENGLE_PROD_CBCT "CBCT"
|
||||
#define DENGLE_PROD_CBFD "CBFD"
|
||||
#define DENGLE_FLASH_MAGIC (0xD6)
|
||||
#define DENGLE_FLASH_VER (0x01)
|
||||
#define DENGLE_FLASH_HALF (5) /* 前半/后半各 5 个槽 */
|
||||
|
||||
extern void usr_ble_send_data(u8 *data, u16 len);
|
||||
extern u16 usr_get_conn_service(void);
|
||||
|
||||
/*============================================================================*/
|
||||
/* 私有类型 */
|
||||
/*============================================================================*/
|
||||
|
||||
/** 发送模式:由设备 prodcode 决定 */
|
||||
typedef enum {
|
||||
DENGLE_MODE_INT32 = 0, /**< CBCT/CBFD:int32 数组 → REPORTCMD */
|
||||
DENGLE_MODE_BYTES /**< 其它:bytes → REPORTCMD_V2 */
|
||||
} DengleMode_t;
|
||||
|
||||
/** 运行时单条定时配置(含 elapsed,不落盘) */
|
||||
typedef struct {
|
||||
uint8_t enable;
|
||||
uint8_t mode;
|
||||
uint16_t cmd_id;
|
||||
uint32_t period_ms;
|
||||
uint32_t elapsed_ms;
|
||||
uint8_t data_n;
|
||||
union {
|
||||
int32_t i32[DENGLE_INT_MAX];
|
||||
uint8_t bytes[DENGLE_BYTES_MAX];
|
||||
} data;
|
||||
} DengleSlot_t;
|
||||
|
||||
/** Flash 单槽结构(不含 elapsed) */
|
||||
typedef struct {
|
||||
uint8_t enable;
|
||||
uint8_t mode;
|
||||
uint16_t cmd_id;
|
||||
uint32_t period_ms;
|
||||
uint8_t data_n;
|
||||
uint8_t data[DENGLE_BYTES_MAX];
|
||||
} DengleSlotFlash_t;
|
||||
|
||||
/** 半区 Flash 块:5 个槽 */
|
||||
typedef struct {
|
||||
uint8_t magic;
|
||||
uint8_t ver;
|
||||
uint8_t reserved[2];
|
||||
DengleSlotFlash_t slots[DENGLE_FLASH_HALF];
|
||||
} DengleFlashHalf_t;
|
||||
|
||||
/*============================================================================*/
|
||||
/* 静态变量 */
|
||||
/*============================================================================*/
|
||||
|
||||
static DengleSlot_t s_slots[DENGLE_SLOT_MAX];
|
||||
static uint8_t s_inited;
|
||||
static int s_tick_timer;
|
||||
static int s_save_timer;
|
||||
static int s_imm_send_timer;
|
||||
static uint8_t s_imm_send_slot;
|
||||
static uint8_t s_txbuf[DENGLE_TX_BUF_SZ];
|
||||
/* 大结构体放静态区,避免 USB/定时器回调栈溢出 */
|
||||
static DengleFlashHalf_t s_flash_half;
|
||||
static bleproto_appdata_desc_t s_encode_desc;
|
||||
static uint8_t s_usb_pause_logged;
|
||||
|
||||
/*============================================================================*/
|
||||
/* 工具函数 */
|
||||
/*============================================================================*/
|
||||
|
||||
static uint16_t dengle_read_u16_be(const uint8_t *p)
|
||||
{
|
||||
return ((uint16_t)p[0] << 8) | p[1];
|
||||
}
|
||||
|
||||
static uint32_t dengle_read_u32_be(const uint8_t *p)
|
||||
{
|
||||
return ((uint32_t)p[0] << 24) | ((uint32_t)p[1] << 16) |
|
||||
((uint32_t)p[2] << 8) | p[3];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 根据当前设备 prodcode 选择发送模式
|
||||
*/
|
||||
static DengleMode_t dengle_get_mode_by_prodcode(void)
|
||||
{
|
||||
uint8_t prod[USR_PRODCODE_LEN];
|
||||
|
||||
usr_prodcode_get(prod, NULL);
|
||||
if (memcmp(prod, DENGLE_PROD_CBCT, USR_PRODCODE_LEN) == 0 ||
|
||||
memcmp(prod, DENGLE_PROD_CBFD, USR_PRODCODE_LEN) == 0) {
|
||||
return DENGLE_MODE_INT32;
|
||||
}
|
||||
return DENGLE_MODE_BYTES;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 上位机已打开 COM 时暂停 Dengle 定时发送
|
||||
* @note 设备可一直插着 USB;仅 COM 打开=配置态,COM 关闭后恢复自动发送
|
||||
* @return 1=应暂停发送 0=允许发送
|
||||
*/
|
||||
static uint8_t dengle_is_usb_pause(void)
|
||||
{
|
||||
#if TCFG_USB_SLAVE_CDC_ENABLE
|
||||
return cdc_is_com_open() ? 1 : 0;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*============================================================================*/
|
||||
/* Flash 读写 */
|
||||
/*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief 运行时槽 → Flash 槽
|
||||
*/
|
||||
static void dengle_slot_to_flash(const DengleSlot_t *src, DengleSlotFlash_t *dst)
|
||||
{
|
||||
memset(dst, 0, sizeof(*dst));
|
||||
dst->enable = src->enable;
|
||||
dst->mode = src->mode;
|
||||
dst->cmd_id = src->cmd_id;
|
||||
dst->period_ms = src->period_ms;
|
||||
dst->data_n = src->data_n;
|
||||
memcpy(dst->data, src->data.bytes, DENGLE_BYTES_MAX);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Flash 槽 → 运行时槽(elapsed 清零)
|
||||
*/
|
||||
static void dengle_slot_from_flash(const DengleSlotFlash_t *src, DengleSlot_t *dst)
|
||||
{
|
||||
memset(dst, 0, sizeof(*dst));
|
||||
dst->enable = src->enable;
|
||||
dst->mode = src->mode;
|
||||
dst->cmd_id = src->cmd_id;
|
||||
dst->period_ms = src->period_ms;
|
||||
dst->data_n = src->data_n;
|
||||
dst->elapsed_ms = 0;
|
||||
memcpy(dst->data.bytes, src->data, DENGLE_BYTES_MAX);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 保存全部 Dengle 配置到 Flash(分两半写入,控制单条 VM 长度)
|
||||
* @note 须在任务/定时器上下文调用,禁止在中断里调用
|
||||
*/
|
||||
static void dengle_flash_save(void)
|
||||
{
|
||||
int ret;
|
||||
uint8_t i;
|
||||
|
||||
/* 前半:slot 0~4 */
|
||||
memset(&s_flash_half, 0, sizeof(s_flash_half));
|
||||
s_flash_half.magic = DENGLE_FLASH_MAGIC;
|
||||
s_flash_half.ver = DENGLE_FLASH_VER;
|
||||
for (i = 0; i < DENGLE_FLASH_HALF; i++) {
|
||||
dengle_slot_to_flash(&s_slots[i], &s_flash_half.slots[i]);
|
||||
}
|
||||
ret = syscfg_write(CFG_USER_DENGLE_L, (u8 *)&s_flash_half, sizeof(s_flash_half));
|
||||
if (ret <= 0) {
|
||||
printf("dengle flash L write fail ret=%d\n", ret);
|
||||
}
|
||||
|
||||
/* 后半:slot 5~9 */
|
||||
memset(&s_flash_half, 0, sizeof(s_flash_half));
|
||||
s_flash_half.magic = DENGLE_FLASH_MAGIC;
|
||||
s_flash_half.ver = DENGLE_FLASH_VER;
|
||||
for (i = 0; i < DENGLE_FLASH_HALF; i++) {
|
||||
dengle_slot_to_flash(&s_slots[DENGLE_FLASH_HALF + i], &s_flash_half.slots[i]);
|
||||
}
|
||||
ret = syscfg_write(CFG_USER_DENGLE_H, (u8 *)&s_flash_half, sizeof(s_flash_half));
|
||||
if (ret <= 0) {
|
||||
printf("dengle flash H write fail ret=%d\n", ret);
|
||||
} else {
|
||||
printf("dengle flash saved, active=%u\n", dengle_sched_get_active_count());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 延时落盘:先回 ACK,避免与组包发送叠在同一深调用栈
|
||||
*/
|
||||
static void dengle_flash_save_timeout(void *priv)
|
||||
{
|
||||
(void)priv;
|
||||
s_save_timer = 0;
|
||||
dengle_flash_save();
|
||||
}
|
||||
|
||||
static void dengle_flash_save_defer(void)
|
||||
{
|
||||
if (s_save_timer) {
|
||||
sys_timeout_del(s_save_timer);
|
||||
s_save_timer = 0;
|
||||
}
|
||||
s_save_timer = sys_timeout_add(NULL, dengle_flash_save_timeout, 50);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从上电 Flash 恢复 Dengle 配置
|
||||
*/
|
||||
static void dengle_flash_load(void)
|
||||
{
|
||||
int ret;
|
||||
uint8_t i;
|
||||
|
||||
/* 前半 */
|
||||
memset(&s_flash_half, 0, sizeof(s_flash_half));
|
||||
ret = syscfg_read(CFG_USER_DENGLE_L, (u8 *)&s_flash_half, sizeof(s_flash_half));
|
||||
if (ret > 0 && s_flash_half.magic == DENGLE_FLASH_MAGIC &&
|
||||
s_flash_half.ver == DENGLE_FLASH_VER) {
|
||||
for (i = 0; i < DENGLE_FLASH_HALF; i++) {
|
||||
dengle_slot_from_flash(&s_flash_half.slots[i], &s_slots[i]);
|
||||
}
|
||||
} else {
|
||||
printf("dengle flash L empty/invalid ret=%d\n", ret);
|
||||
}
|
||||
|
||||
/* 后半 */
|
||||
memset(&s_flash_half, 0, sizeof(s_flash_half));
|
||||
ret = syscfg_read(CFG_USER_DENGLE_H, (u8 *)&s_flash_half, sizeof(s_flash_half));
|
||||
if (ret > 0 && s_flash_half.magic == DENGLE_FLASH_MAGIC &&
|
||||
s_flash_half.ver == DENGLE_FLASH_VER) {
|
||||
for (i = 0; i < DENGLE_FLASH_HALF; i++) {
|
||||
dengle_slot_from_flash(&s_flash_half.slots[i],
|
||||
&s_slots[DENGLE_FLASH_HALF + i]);
|
||||
}
|
||||
} else {
|
||||
printf("dengle flash H empty/invalid ret=%d\n", ret);
|
||||
}
|
||||
|
||||
printf("dengle flash loaded, active=%u\n", dengle_sched_get_active_count());
|
||||
}
|
||||
|
||||
/*============================================================================*/
|
||||
/* BLE 发送(两条路径) */
|
||||
/*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief CBCT/CBFD:REPORTCMD + ycmd.arg_int32[]
|
||||
* @note 对齐 usr_test_bleproto_runcmd 组包方式
|
||||
*/
|
||||
static void dengle_send_int32(const DengleSlot_t *slot)
|
||||
{
|
||||
bleproto_reportcmd_req_t *req;
|
||||
int elen;
|
||||
uint8_t i;
|
||||
|
||||
if (!slot || usr_get_conn_service() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
memset(&s_encode_desc, 0, sizeof(s_encode_desc));
|
||||
s_encode_desc.header.version = 0;
|
||||
s_encode_desc.header.datafmt = BLEPROTO_APPDATA_DATAFMT_JSON;
|
||||
s_encode_desc.header.msgtype = BLEPROTO_APPDATA_MSGTYPE_REQ;
|
||||
s_encode_desc.header.msgid = usr_ble_rx_msgid_get();
|
||||
s_encode_desc.header.encrypt = 0;
|
||||
s_encode_desc.header.serviceid = E_BLEPROTO_SERVICE_ID_REPORTCMD;
|
||||
|
||||
req = &s_encode_desc.datast.reportcmd_req;
|
||||
req->ycmd.cmd_id = slot->cmd_id;
|
||||
req->ycmd.arg_int32_count = slot->data_n;
|
||||
req->ycmd.arg_string_count = 0;
|
||||
req->ycmd.arg_bytes_count = 0;
|
||||
for (i = 0; i < slot->data_n && i < DENGLE_INT_MAX; i++) {
|
||||
req->ycmd.arg_int32[i] = slot->data.i32[i];
|
||||
}
|
||||
|
||||
elen = bleproto_appdata_encode(s_txbuf, sizeof(s_txbuf), sizeof(s_txbuf), &s_encode_desc);
|
||||
printf("dengle INT32 cmd_id=%u n=%u elen=%d\n", slot->cmd_id, slot->data_n, elen);
|
||||
if (elen > 0) {
|
||||
usr_ble_send_data(s_txbuf, (u16)elen);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 其它产品:REPORTCMD_V2 + bytes_data
|
||||
* @note 对齐 usr_test_protocol 组包方式
|
||||
*/
|
||||
static void dengle_send_bytes(const DengleSlot_t *slot)
|
||||
{
|
||||
ble_proto_packet_t *pkt;
|
||||
int elen;
|
||||
|
||||
if (!slot || usr_get_conn_service() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
memset(&s_encode_desc, 0, sizeof(s_encode_desc));
|
||||
s_encode_desc.header.version = 0;
|
||||
s_encode_desc.header.datafmt = BLEPROTO_APPDATA_DATAFMT_JSON;
|
||||
s_encode_desc.header.msgtype = BLEPROTO_APPDATA_MSGTYPE_REQ;
|
||||
s_encode_desc.header.msgid = usr_ble_rx_msgid_get();
|
||||
s_encode_desc.header.encrypt = 0;
|
||||
s_encode_desc.header.serviceid = E_BLEPROTO_SERVICE_ID_REPORTCMD_V2;
|
||||
|
||||
pkt = &s_encode_desc.datast.reportcmd_v2_req;
|
||||
pkt->cmd = slot->cmd_id;
|
||||
pkt->reserved = 0;
|
||||
pkt->gateway_int_cnt = 0;
|
||||
pkt->device_int_len = 0;
|
||||
pkt->device_str_len = 0;
|
||||
pkt->device_bytes_len = slot->data_n;
|
||||
pkt->bytes_data = slot->data.bytes;
|
||||
|
||||
elen = bleproto_appdata_encode(s_txbuf, sizeof(s_txbuf), sizeof(s_txbuf), &s_encode_desc);
|
||||
printf("dengle BYTES cmd=%u n=%u elen=%d\n", slot->cmd_id, slot->data_n, elen);
|
||||
if (elen > 0) {
|
||||
usr_ble_send_data(s_txbuf, (u16)elen);
|
||||
}
|
||||
}
|
||||
|
||||
static void dengle_send_slot(const DengleSlot_t *slot)
|
||||
{
|
||||
if (!slot || !slot->enable || slot->data_n == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* USB 连着电脑时不发,避免配置阶段干扰 */
|
||||
if (dengle_is_usb_pause()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (slot->mode == DENGLE_MODE_INT32) {
|
||||
dengle_send_int32(slot);
|
||||
} else {
|
||||
dengle_send_bytes(slot);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 延时立即发送:与 USB 解析/ACK 错开,降低栈峰值
|
||||
*/
|
||||
static void dengle_imm_send_timeout(void *priv)
|
||||
{
|
||||
uint8_t slot_id = (uint8_t)((u32)priv);
|
||||
|
||||
s_imm_send_timer = 0;
|
||||
if (slot_id < DENGLE_SLOT_MAX) {
|
||||
dengle_send_slot(&s_slots[slot_id]);
|
||||
}
|
||||
}
|
||||
|
||||
static void dengle_imm_send_defer(uint8_t slot_id)
|
||||
{
|
||||
s_imm_send_slot = slot_id;
|
||||
if (s_imm_send_timer) {
|
||||
sys_timeout_del(s_imm_send_timer);
|
||||
s_imm_send_timer = 0;
|
||||
}
|
||||
s_imm_send_timer = sys_timeout_add((void *)((u32)slot_id),
|
||||
dengle_imm_send_timeout, 30);
|
||||
}
|
||||
|
||||
/*============================================================================*/
|
||||
/* 定时节拍 */
|
||||
/*============================================================================*/
|
||||
|
||||
static void dengle_tick(void *priv)
|
||||
{
|
||||
uint8_t i;
|
||||
DengleSlot_t *slot;
|
||||
|
||||
(void)priv;
|
||||
|
||||
/* USB 在线:冻结计时,断开后从完整周期重新开始,避免拔线瞬间连发 */
|
||||
if (dengle_is_usb_pause()) {
|
||||
if (!s_usb_pause_logged) {
|
||||
s_usb_pause_logged = 1;
|
||||
printf("dengle pause: COM open\n");
|
||||
}
|
||||
for (i = 0; i < DENGLE_SLOT_MAX; i++) {
|
||||
s_slots[i].elapsed_ms = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (s_usb_pause_logged) {
|
||||
s_usb_pause_logged = 0;
|
||||
printf("dengle resume: COM closed\n");
|
||||
}
|
||||
|
||||
for (i = 0; i < DENGLE_SLOT_MAX; i++) {
|
||||
slot = &s_slots[i];
|
||||
if (!slot->enable || slot->period_ms == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
slot->elapsed_ms += DENGLE_TICK_MS;
|
||||
if (slot->elapsed_ms >= slot->period_ms) {
|
||||
slot->elapsed_ms = 0;
|
||||
dengle_send_slot(slot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*============================================================================*/
|
||||
/* 对外接口 */
|
||||
/*============================================================================*/
|
||||
|
||||
Dengle_Ret_t dengle_sched_init(void)
|
||||
{
|
||||
memset(s_slots, 0, sizeof(s_slots));
|
||||
dengle_flash_load();
|
||||
s_inited = 1;
|
||||
|
||||
if (s_tick_timer == 0) {
|
||||
s_tick_timer = sys_timer_add(NULL, dengle_tick, DENGLE_TICK_MS);
|
||||
}
|
||||
|
||||
printf("dengle_sched_init ok\n");
|
||||
return DENGLE_OK;
|
||||
}
|
||||
|
||||
Dengle_Ret_t dengle_sched_clear_all(void)
|
||||
{
|
||||
if (!s_inited) {
|
||||
return DENGLE_ERR_NOT_INIT;
|
||||
}
|
||||
memset(s_slots, 0, sizeof(s_slots));
|
||||
dengle_flash_save_defer();
|
||||
printf("dengle clear all\n");
|
||||
return DENGLE_OK;
|
||||
}
|
||||
|
||||
uint8_t dengle_sched_get_active_count(void)
|
||||
{
|
||||
uint8_t i;
|
||||
uint8_t cnt = 0;
|
||||
|
||||
for (i = 0; i < DENGLE_SLOT_MAX; i++) {
|
||||
if (s_slots[i].enable) {
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
Dengle_Ret_t dengle_sched_usb_set(const uint8_t *payload, uint16_t payload_len)
|
||||
{
|
||||
DengleSlot_t *slot;
|
||||
DengleMode_t mode;
|
||||
uint8_t slot_id;
|
||||
uint8_t enable;
|
||||
uint8_t data_n;
|
||||
uint8_t i;
|
||||
uint16_t need;
|
||||
|
||||
if (!s_inited) {
|
||||
return DENGLE_ERR_NOT_INIT;
|
||||
}
|
||||
if (!payload || payload_len < 9) {
|
||||
return DENGLE_ERR_PARAM;
|
||||
}
|
||||
|
||||
slot_id = payload[0];
|
||||
enable = payload[1];
|
||||
if (slot_id >= DENGLE_SLOT_MAX) {
|
||||
return DENGLE_ERR_SLOT;
|
||||
}
|
||||
|
||||
slot = &s_slots[slot_id];
|
||||
memset(slot, 0, sizeof(*slot));
|
||||
|
||||
/* 仅关闭该槽 */
|
||||
if (enable == 0) {
|
||||
dengle_flash_save_defer();
|
||||
printf("dengle slot%u disabled\n", slot_id);
|
||||
return DENGLE_OK;
|
||||
}
|
||||
|
||||
mode = dengle_get_mode_by_prodcode();
|
||||
data_n = payload[8];
|
||||
|
||||
if (mode == DENGLE_MODE_INT32) {
|
||||
if (data_n == 0 || data_n > DENGLE_INT_MAX) {
|
||||
return DENGLE_ERR_PARAM;
|
||||
}
|
||||
need = 9 + (uint16_t)data_n * 4;
|
||||
if (payload_len < need) {
|
||||
return DENGLE_ERR_PARAM;
|
||||
}
|
||||
for (i = 0; i < data_n; i++) {
|
||||
slot->data.i32[i] = (int32_t)dengle_read_u32_be(&payload[9 + i * 4]);
|
||||
}
|
||||
} else {
|
||||
if (data_n == 0 || data_n > DENGLE_BYTES_MAX) {
|
||||
return DENGLE_ERR_PARAM;
|
||||
}
|
||||
need = 9 + data_n;
|
||||
if (payload_len < need) {
|
||||
return DENGLE_ERR_PARAM;
|
||||
}
|
||||
memcpy(slot->data.bytes, &payload[9], data_n);
|
||||
}
|
||||
|
||||
slot->enable = 1;
|
||||
slot->mode = (uint8_t)mode;
|
||||
slot->period_ms = dengle_read_u32_be(&payload[2]);
|
||||
slot->cmd_id = dengle_read_u16_be(&payload[6]);
|
||||
slot->data_n = data_n;
|
||||
slot->elapsed_ms = 0;
|
||||
|
||||
if (slot->period_ms < DENGLE_TICK_MS) {
|
||||
slot->period_ms = DENGLE_TICK_MS;
|
||||
}
|
||||
|
||||
printf("dengle set slot=%u mode=%u period=%ums cmd=%u n=%u\n",
|
||||
slot_id, slot->mode, slot->period_ms, slot->cmd_id, slot->data_n);
|
||||
|
||||
/* 先 ACK,再延时落盘/发送,避免与组包叠栈导致复位 */
|
||||
dengle_flash_save_defer();
|
||||
dengle_imm_send_defer(slot_id);
|
||||
return DENGLE_OK;
|
||||
}
|
||||
|
||||
/*============================================================================*/
|
||||
/* 查询导出(供 USB 0x74 / 0x75) */
|
||||
/*============================================================================*/
|
||||
|
||||
static void dengle_write_u16_be(uint8_t *p, uint16_t v)
|
||||
{
|
||||
p[0] = (uint8_t)(v >> 8);
|
||||
p[1] = (uint8_t)(v & 0xFF);
|
||||
}
|
||||
|
||||
static void dengle_write_u32_be(uint8_t *p, uint32_t v)
|
||||
{
|
||||
p[0] = (uint8_t)(v >> 24);
|
||||
p[1] = (uint8_t)(v >> 16);
|
||||
p[2] = (uint8_t)(v >> 8);
|
||||
p[3] = (uint8_t)(v & 0xFF);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 打包单个槽位(与 0x70 设置 payload 同格式,便于上位机复用解析)
|
||||
* enable=0 时仅输出 [slot][0]
|
||||
* enable=1 时输出 [slot][1][period][cmd][data_n][data...]
|
||||
* INT32 模式 data 仍按大端 int32 导出
|
||||
*/
|
||||
int dengle_sched_pack_slot(uint8_t slot_id, uint8_t *buf, uint16_t max_len)
|
||||
{
|
||||
const DengleSlot_t *slot;
|
||||
uint16_t need;
|
||||
uint8_t i;
|
||||
|
||||
if (!s_inited || !buf) {
|
||||
return -1;
|
||||
}
|
||||
if (slot_id >= DENGLE_SLOT_MAX) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
slot = &s_slots[slot_id];
|
||||
|
||||
if (!slot->enable) {
|
||||
if (max_len < 2) {
|
||||
return -1;
|
||||
}
|
||||
buf[0] = slot_id;
|
||||
buf[1] = 0;
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (slot->mode == DENGLE_MODE_INT32) {
|
||||
need = 9 + (uint16_t)slot->data_n * 4;
|
||||
} else {
|
||||
need = 9 + slot->data_n;
|
||||
}
|
||||
if (max_len < need) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
buf[0] = slot_id;
|
||||
buf[1] = 1;
|
||||
dengle_write_u32_be(&buf[2], slot->period_ms);
|
||||
dengle_write_u16_be(&buf[6], slot->cmd_id);
|
||||
buf[8] = slot->data_n;
|
||||
|
||||
if (slot->mode == DENGLE_MODE_INT32) {
|
||||
for (i = 0; i < slot->data_n; i++) {
|
||||
dengle_write_u32_be(&buf[9 + i * 4], (uint32_t)slot->data.i32[i]);
|
||||
}
|
||||
} else {
|
||||
memcpy(&buf[9], slot->data.bytes, slot->data_n);
|
||||
}
|
||||
|
||||
return (int)need;
|
||||
}
|
||||
|
||||
int dengle_sched_pack_all(uint8_t *buf, uint16_t max_len)
|
||||
{
|
||||
uint16_t offset = 0;
|
||||
uint8_t i;
|
||||
int n;
|
||||
|
||||
if (!s_inited || !buf) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < DENGLE_SLOT_MAX; i++) {
|
||||
n = dengle_sched_pack_slot(i, &buf[offset],
|
||||
(uint16_t)(max_len - offset));
|
||||
if (n <= 0) {
|
||||
return -1;
|
||||
}
|
||||
offset = (uint16_t)(offset + n);
|
||||
}
|
||||
return (int)offset;
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/******************************************************************************
|
||||
* @file usr_dengle_sched.h
|
||||
* @brief Dengle 参数定时下发:USB 配置最多 10 条,按周期经 BLE 发出
|
||||
* @author cyWu <1917507415@qq.com>
|
||||
* @date 2026.07.30
|
||||
* @version V1.2.0
|
||||
* @history
|
||||
* - V1.0.0, 2026.07.30, cyWu, 首次发布
|
||||
* - V1.1.0, 2026.07.30, cyWu, 增加 Flash 断电保存
|
||||
* - V1.2.0, 2026.07.30, cyWu, USB 连接时暂停定时发送
|
||||
* - V1.2.1, 2026.07.30, cyWu, 改为 COM(DTR) 开闭控制暂停/恢复
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef __USR_DENGLE_SCHED_H__
|
||||
#define __USR_DENGLE_SCHED_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/*============================================================================*/
|
||||
/* 宏定义 */
|
||||
/*============================================================================*/
|
||||
|
||||
#define DENGLE_SLOT_MAX (10) /**< 最多定时条目数 */
|
||||
#define DENGLE_INT_MAX (16) /**< CBCT/CBFD:最多 int32 个数 */
|
||||
#define DENGLE_BYTES_MAX (64) /**< 其它产品:最多 bytes 长度 */
|
||||
#define DENGLE_TICK_MS (100) /**< 调度节拍 */
|
||||
|
||||
/*============================================================================*/
|
||||
/* 错误码 */
|
||||
/*============================================================================*/
|
||||
|
||||
typedef enum {
|
||||
DENGLE_OK = 0,
|
||||
DENGLE_ERR_PARAM,
|
||||
DENGLE_ERR_SLOT,
|
||||
DENGLE_ERR_MODE,
|
||||
DENGLE_ERR_NOT_INIT
|
||||
} Dengle_Ret_t;
|
||||
|
||||
/*============================================================================*/
|
||||
/* 外部接口 */
|
||||
/*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief 初始化 Dengle 定时模块(启动 100ms 节拍)
|
||||
* @return Dengle_Ret_t
|
||||
*/
|
||||
Dengle_Ret_t dengle_sched_init(void);
|
||||
|
||||
/**
|
||||
* @brief USB 设置一条定时任务
|
||||
* @note payload 格式见 .c 文件头注释
|
||||
* @param payload USB 帧 payload(不含 55AA/LEN/CMD/SN/CKSUM)
|
||||
* @param payload_len 长度
|
||||
* @return Dengle_Ret_t
|
||||
*/
|
||||
Dengle_Ret_t dengle_sched_usb_set(const uint8_t *payload, uint16_t payload_len);
|
||||
|
||||
/**
|
||||
* @brief 清除全部定时任务
|
||||
* @return Dengle_Ret_t
|
||||
*/
|
||||
Dengle_Ret_t dengle_sched_clear_all(void);
|
||||
|
||||
/**
|
||||
* @brief 打包单个槽位配置(格式与 0x70 设置 payload 一致)
|
||||
* @param slot_id 槽位 0~9
|
||||
* @param buf 输出缓冲
|
||||
* @param max_len 缓冲最大长度
|
||||
* @return >0 写入字节数;<=0 失败
|
||||
*/
|
||||
int dengle_sched_pack_slot(uint8_t slot_id, uint8_t *buf, uint16_t max_len);
|
||||
|
||||
/**
|
||||
* @brief 打包全部槽位配置(连续拼接多个 0x70 风格记录)
|
||||
* @param buf 输出缓冲
|
||||
* @param max_len 缓冲最大长度
|
||||
* @return >0 写入字节数;<=0 失败
|
||||
*/
|
||||
int dengle_sched_pack_all(uint8_t *buf, uint16_t max_len);
|
||||
|
||||
/**
|
||||
* @brief 获取有效条目数(调试用)
|
||||
* @return 已使能槽位数
|
||||
*/
|
||||
uint8_t dengle_sched_get_active_count(void);
|
||||
|
||||
#endif /* __USR_DENGLE_SCHED_H__ */
|
||||
@@ -714,6 +714,45 @@ void usr_app_ota_init()
|
||||
}
|
||||
}
|
||||
|
||||
// void usr_test_bleproto_runcmd(void)
|
||||
// {
|
||||
// bleproto_appdata_desc_t encode_desc = {0};
|
||||
// encode_desc.header.version = 0;
|
||||
// encode_desc.header.datafmt = BLEPROTO_APPDATA_DATAFMT_JSON;
|
||||
// encode_desc.header.msgtype = BLEPROTO_APPDATA_MSGTYPE_REQ;
|
||||
// encode_desc.header.msgid = 1;
|
||||
// encode_desc.header.encrypt = 0;
|
||||
// encode_desc.header.serviceid = E_BLEPROTO_SERVICE_ID_REPORTCMD;
|
||||
|
||||
// bleproto_reportcmd_req_t *pkt = &encode_desc.datast.reportcmd_req;
|
||||
// /*
|
||||
// > 告警上报 (*设备主动上报*)
|
||||
// * 命令ID: 120
|
||||
// * 参数说明
|
||||
|
||||
// | 索引 | 类型 | 描述 | 取值 |
|
||||
// | ---- | ----- | ---------- | ----------------------------------------------------- |
|
||||
// | 0 | int32 | 告警事件id | 5000:硬件故障;5001:低电量报警; 5010:震动开机报警 |
|
||||
// | 1 | int32 | UTC时间戳 | 事件触发UTCS时间戳单位秒 |
|
||||
|
||||
// */
|
||||
// req->ycmd.cmd_id = 100;
|
||||
|
||||
// req->ycmd.arg_int32[0] = 5001; // 电量上报
|
||||
// req->ycmd.arg_int32[1] = usr_var.usr_cureent_utc; // UTC时间戳
|
||||
|
||||
// req->ycmd.arg_int32_count = 2;
|
||||
// req->ycmd.t = usr_var.usr_cureent_utc;
|
||||
|
||||
// int elen = bleproto_appdata_encode(txbuf, sizeof(txbuf), sizeof(txbuf), &encode_desc);
|
||||
// printf("encode_len = %d\n", elen);
|
||||
// if (elen > 0)
|
||||
// {
|
||||
// usr_ble_send_data(txbuf, elen);
|
||||
// printf_buf(txbuf, elen);
|
||||
// }
|
||||
// }
|
||||
|
||||
void usr_test_protocol(void)
|
||||
{
|
||||
bleproto_appdata_desc_t encode_desc = {0};
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "vm.h"
|
||||
#include "usr_usb_jb.h"
|
||||
#include "bleproto_api.h"
|
||||
#include "usr_dengle_sched.h"
|
||||
|
||||
#define LOG_TAG_CONST APP
|
||||
#define LOG_TAG "[USB_JB]"
|
||||
@@ -36,6 +37,7 @@
|
||||
#define PKT_HEAD_1 0xAA
|
||||
#define MAX_PACKAGE_LEN 128
|
||||
#define JB_RB_CAPACITY 256
|
||||
#define JB_RSP_BUF_SZ 800 /* Dengle 查询应答:result + 最多 10 槽 */
|
||||
#define USR_PROD_MAGIC 0x5A
|
||||
#define USR_JB_BLE_CMD_REPORT 100 /* 0x07/0x34/0x38 → 主动上报类 */
|
||||
#define USR_JB_BLE_CMD_ACK 110 /* 0x04/0x06/0x31/0x37 → 应答类 */
|
||||
@@ -86,6 +88,7 @@ static uint8_t s_pending_pcode[USR_PCODE_LEN];
|
||||
static int s_process_timer;
|
||||
static uint8_t s_ble_txbuf[USR_JB_BLE_TX_SZ];
|
||||
static uint8_t s_last_ble_msgid; /* 最近一次 BLE RX 的 msgid */
|
||||
static uint8_t s_rsp_buf[JB_RSP_BUF_SZ];
|
||||
|
||||
void usr_ble_rx_msgid_save(uint8_t msgid)
|
||||
{
|
||||
@@ -426,6 +429,117 @@ static void usr_jb_send_ack(uint8_t cmd, uint8_t sn, uint8_t result)
|
||||
buf[7] = usr_jb_checksum(buf, total);
|
||||
cdc_write_data(0, buf, total);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 回带 payload 的应答:CMD = 请求 CMD+1
|
||||
* @param cmd 请求命令
|
||||
* @param sn 流水号
|
||||
* @param payload 应答负载(可为 NULL)
|
||||
* @param payload_len 负载长度
|
||||
*/
|
||||
static void usr_jb_send_rsp(uint8_t cmd, uint8_t sn,
|
||||
const uint8_t *payload, uint16_t payload_len)
|
||||
{
|
||||
uint16_t len_field;
|
||||
uint16_t total;
|
||||
|
||||
/* LEN = CMD + SN + payload + CKSUM */
|
||||
len_field = (uint16_t)(2 + payload_len + 1);
|
||||
total = (uint16_t)(len_field + 4);
|
||||
if (total > JB_RSP_BUF_SZ) {
|
||||
printf("usr_jb rsp too long %u\n", total);
|
||||
usr_jb_send_ack(cmd, sn, 0x01);
|
||||
return;
|
||||
}
|
||||
|
||||
s_rsp_buf[0] = PKT_HEAD_0;
|
||||
s_rsp_buf[1] = PKT_HEAD_1;
|
||||
s_rsp_buf[2] = (uint8_t)(len_field >> 8);
|
||||
s_rsp_buf[3] = (uint8_t)(len_field & 0xFF);
|
||||
s_rsp_buf[4] = (uint8_t)(cmd + 1);
|
||||
s_rsp_buf[5] = sn;
|
||||
if (payload && payload_len) {
|
||||
memcpy(&s_rsp_buf[6], payload, payload_len);
|
||||
}
|
||||
s_rsp_buf[total - 1] = usr_jb_checksum(s_rsp_buf, total);
|
||||
cdc_write_data(0, s_rsp_buf, total);
|
||||
printf("usr_jb rsp cmd=0x%02x plen=%u total=%u\n", cmd + 1, payload_len, total);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Dengle 查询:空/0xFF=全部;单字节槽位=查一条
|
||||
* @note 应答 0x75:
|
||||
* 查全部 → [result][slot_rec x 10],slot_rec 与 0x70 payload 同格式
|
||||
* 查单槽 → [result][slot_rec x 1]
|
||||
*/
|
||||
static void usr_jb_handle_dengle_query(uint8_t sn,
|
||||
const uint8_t *payload,
|
||||
uint16_t payload_len)
|
||||
{
|
||||
static uint8_t s_body[JB_RSP_BUF_SZ - 8];
|
||||
int n;
|
||||
uint8_t slot_id;
|
||||
uint8_t i;
|
||||
|
||||
/* 查单槽 */
|
||||
if (payload_len >= 1 && payload[0] < DENGLE_SLOT_MAX) {
|
||||
slot_id = payload[0];
|
||||
s_body[0] = 0x00; /* result ok */
|
||||
n = dengle_sched_pack_slot(slot_id, &s_body[1], sizeof(s_body) - 1);
|
||||
if (n <= 0) {
|
||||
usr_jb_send_ack(USR_JB_CMD_DENGLE_QRY, sn, 0x01);
|
||||
return;
|
||||
}
|
||||
usr_jb_send_rsp(USR_JB_CMD_DENGLE_QRY, sn, s_body, (uint16_t)(1 + n));
|
||||
return;
|
||||
}
|
||||
|
||||
/* 查全部:payload 空 / 0xFF / 其它 */
|
||||
s_body[0] = 0x00;
|
||||
n = dengle_sched_pack_all(&s_body[1], sizeof(s_body) - 1);
|
||||
if (n > 0) {
|
||||
usr_jb_send_rsp(USR_JB_CMD_DENGLE_QRY, sn, s_body, (uint16_t)(1 + n));
|
||||
return;
|
||||
}
|
||||
|
||||
/* 缓冲不够时退化为逐槽上报 */
|
||||
printf("dengle qry pack_all fail, fallback per-slot\n");
|
||||
for (i = 0; i < DENGLE_SLOT_MAX; i++) {
|
||||
s_body[0] = 0x00;
|
||||
n = dengle_sched_pack_slot(i, &s_body[1], sizeof(s_body) - 1);
|
||||
if (n <= 0) {
|
||||
usr_jb_send_ack(USR_JB_CMD_DENGLE_QRY, sn, 0x01);
|
||||
return;
|
||||
}
|
||||
usr_jb_send_rsp(USR_JB_CMD_DENGLE_QRY, sn, s_body, (uint16_t)(1 + n));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 查询产品码:应答格式与 0x64 设置 payload 对齐(带 result)
|
||||
* RSP 0x67 payload:
|
||||
* [result:1][04][ProdtCode 4B][06][productCode 6B]
|
||||
*/
|
||||
static void usr_jb_handle_prodcode_query(uint8_t sn)
|
||||
{
|
||||
uint8_t body[1 + 1 + USR_PRODCODE_LEN + 1 + USR_PCODE_LEN];
|
||||
uint8_t prod[USR_PRODCODE_LEN];
|
||||
uint8_t pcode[USR_PCODE_LEN];
|
||||
|
||||
usr_prodcode_get(prod, pcode);
|
||||
|
||||
body[0] = 0x00; /* result ok */
|
||||
body[1] = USR_PRODCODE_LEN;
|
||||
memcpy(&body[2], prod, USR_PRODCODE_LEN);
|
||||
body[2 + USR_PRODCODE_LEN] = USR_PCODE_LEN;
|
||||
memcpy(&body[2 + USR_PRODCODE_LEN + 1], pcode, USR_PCODE_LEN);
|
||||
|
||||
printf("prodcode qry: %c%c%c%c / %c%c%c%c%c%c\n",
|
||||
prod[0], prod[1], prod[2], prod[3],
|
||||
pcode[0], pcode[1], pcode[2], pcode[3], pcode[4], pcode[5]);
|
||||
|
||||
usr_jb_send_rsp(USR_JB_CMD_GET_PRODCODE, sn, body, sizeof(body));
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
@@ -458,25 +572,59 @@ static void usr_jb_process_timeout(void *priv)
|
||||
printf("usr_jb cmd=0x%02x sn=%u plen=%u\n", cmd, sn, payload_len);
|
||||
printf_buf(pkt, pkt_len);
|
||||
|
||||
if (cmd == USR_JB_CMD_SET_PRODCODE) {
|
||||
switch (cmd) {
|
||||
case USR_JB_CMD_SET_PRODCODE:
|
||||
if (usr_jb_parse_prod_payload(&pkt[6], payload_len,
|
||||
s_pending_prod, s_pending_pcode) != 0) {
|
||||
printf("usr_jb set prodcode payload invalid\n");
|
||||
#if TCFG_USB_SLAVE_CDC_ENABLE
|
||||
usr_jb_send_ack(cmd, sn, 0x01);
|
||||
#endif
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
|
||||
/* 缓存后统一落盘,避免同一周期多次写 flash */
|
||||
s_pending_cmd = cmd;
|
||||
s_pending_sn = sn;
|
||||
s_pending_save = 1;
|
||||
continue;
|
||||
break;
|
||||
|
||||
case USR_JB_CMD_GET_PRODCODE:
|
||||
#if TCFG_USB_SLAVE_CDC_ENABLE
|
||||
usr_jb_handle_prodcode_query(sn);
|
||||
#endif
|
||||
break;
|
||||
|
||||
case USR_JB_CMD_DENGLE_SET: {
|
||||
Dengle_Ret_t dr = dengle_sched_usb_set(&pkt[6], payload_len);
|
||||
printf("dengle sched usb set ret=%d\n", dr);
|
||||
#if TCFG_USB_SLAVE_CDC_ENABLE
|
||||
usr_jb_send_ack(cmd, sn, (dr == DENGLE_OK) ? 0x00 : 0x01);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
/* 其它命令:整帧透传到 BLE(按 JB cmd 映射 ble_proto cmd) */
|
||||
usr_jb_forward_to_ble(pkt, pkt_len);
|
||||
case USR_JB_CMD_DENGLE_CLR: {
|
||||
Dengle_Ret_t dr = dengle_sched_clear_all();
|
||||
printf("dengle sched clear all ret=%d\n", dr);
|
||||
#if TCFG_USB_SLAVE_CDC_ENABLE
|
||||
usr_jb_send_ack(cmd, sn, (dr == DENGLE_OK) ? 0x00 : 0x01);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
case USR_JB_CMD_DENGLE_QRY:
|
||||
#if TCFG_USB_SLAVE_CDC_ENABLE
|
||||
usr_jb_handle_dengle_query(sn, &pkt[6], payload_len);
|
||||
#else
|
||||
(void)payload_len;
|
||||
#endif
|
||||
break;
|
||||
|
||||
default:
|
||||
/* 其它命令:整帧透传到 BLE(按 JB cmd 映射 ble_proto cmd) */
|
||||
usr_jb_forward_to_ble(pkt, pkt_len);
|
||||
break;
|
||||
}
|
||||
} while (1);
|
||||
|
||||
if (!s_pending_save) {
|
||||
@@ -484,6 +632,16 @@ static void usr_jb_process_timeout(void *priv)
|
||||
}
|
||||
s_pending_save = 0;
|
||||
|
||||
/* code / prodcode 与当前完全一致:ACK 成功,不写 Flash、不重建广播 */
|
||||
if (memcmp(s_prodcode, s_pending_prod, USR_PRODCODE_LEN) == 0 &&
|
||||
memcmp(s_pcode, s_pending_pcode, USR_PCODE_LEN) == 0) {
|
||||
#if TCFG_USB_SLAVE_CDC_ENABLE
|
||||
usr_jb_send_ack(s_pending_cmd, s_pending_sn, 0x00);
|
||||
#endif
|
||||
printf("prodcode unchanged, skip adv rebuild\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (usr_prodcode_save(s_pending_prod, s_pending_pcode) == 0) {
|
||||
#if TCFG_USB_SLAVE_CDC_ENABLE
|
||||
usr_jb_send_ack(s_pending_cmd, s_pending_sn, 0x00);
|
||||
|
||||
@@ -19,7 +19,11 @@
|
||||
|
||||
#define USR_PRODCODE_LEN (4)
|
||||
#define USR_PCODE_LEN (6)
|
||||
#define USR_JB_CMD_SET_PRODCODE (0x64) /**< USB 下发产品码命令 */
|
||||
#define USR_JB_CMD_SET_PRODCODE (0x64) /**< USB 设置产品码;ACK=0x65 */
|
||||
#define USR_JB_CMD_GET_PRODCODE (0x66) /**< USB 查询产品码;RSP=0x67 */
|
||||
#define USR_JB_CMD_DENGLE_SET (0x70) /**< USB 设置 Dengle 定时;ACK=0x71 */
|
||||
#define USR_JB_CMD_DENGLE_CLR (0x72) /**< USB 清除全部 Dengle;ACK=0x73 */
|
||||
#define USR_JB_CMD_DENGLE_QRY (0x74) /**< USB 查询 Dengle 配置;RSP=0x75 */
|
||||
|
||||
/*============================================================================*/
|
||||
/* 外部接口 */
|
||||
|
||||
Reference in New Issue
Block a user