678 lines
20 KiB
C
678 lines
20 KiB
C
/******************************************************************************
|
||
* @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) 开闭控制暂停/恢复
|
||
* - V1.2.2, 2026.08.01, cyWu, 设置后立即验发不受 COM 暂停限制
|
||
*
|
||
* 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);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief 发送指定槽位
|
||
* @param slot 槽位
|
||
* @param ignore_pause 1=配置验发,忽略 COM 暂停;0=周期发送,COM 开则跳过
|
||
*/
|
||
static void dengle_send_slot(const DengleSlot_t *slot, uint8_t ignore_pause)
|
||
{
|
||
if (!slot || !slot->enable || slot->data_n == 0) {
|
||
return;
|
||
}
|
||
|
||
/* 周期发送:COM 打开时暂停;设置后验发不受此限制 */
|
||
if (!ignore_pause && dengle_is_usb_pause()) {
|
||
return;
|
||
}
|
||
|
||
if (usr_get_conn_service() == 0) {
|
||
if (ignore_pause) {
|
||
printf("dengle verify skip: BLE not connected\n");
|
||
}
|
||
return;
|
||
}
|
||
|
||
if (slot->mode == DENGLE_MODE_INT32) {
|
||
dengle_send_int32(slot);
|
||
} else {
|
||
dengle_send_bytes(slot);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief 延时立即验发:与 USB 解析/ACK 错开,降低栈峰值
|
||
* @note 即使 COM 打开也发一次,方便设置时确认 BLE 是否收到
|
||
*/
|
||
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) {
|
||
printf("dengle imm verify send slot=%u\n", slot_id);
|
||
dengle_send_slot(&s_slots[slot_id], 1);
|
||
}
|
||
}
|
||
|
||
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, 0);
|
||
}
|
||
}
|
||
}
|
||
|
||
/*============================================================================*/
|
||
/* 对外接口 */
|
||
/*============================================================================*/
|
||
|
||
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 < 1) {
|
||
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;
|
||
}
|