Files
DCU_APP/GD32E508_DCU_APP/device/motor_state.h
T
cbb e06f5eb9a9 feat(motor_state): 支持多通道电机状态管理并添加推送周期动态调节功能
- 实现 node×channel 双键槽位管理,支持同一节点号在不同CAN通道独立缓存
- 扩展电机状态表容量从9个槽位到27个槽位(9节点×3通道)
- 添加SPI命令子命令0xFE用于动态设置0xC5推送周期(50-5000ms)
- 增加运行时可调推送周期功能,默认250ms,支持50ms-5s范围调节
- 更新CAN通道配置,启用CH1和CH2通道支持
- 版本号更新至1.5.4
2026-07-30 02:11:49 +08:00

103 lines
4.8 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* @file motor_state.h
* @brief 多电机状态表 — CAN STATUS 帧识别、内存缓存、SPI 推送序列化。
*
* 设计要点:
* - 仅在主循环上下文 (taskCanRxParse / taskHeartbeat / pushStep) 中读写,
* 无 ISR 竞态,不需要临界区。
* - GD32 不做浮点解码,只缓存原始 32B STATUS payload,由前端按 DBC 解码。
* - 离线判定:3s 无 STATUS 帧 → online=0。
*/
#ifndef DEVICE_MOTOR_STATE_H
#define DEVICE_MOTOR_STATE_H
#include <cstdint>
#include <cstring>
#include "sys/config.h"
/* ── CAN ID 匹配规则 (29-bit 扩展帧) ──
* 物理 ID = 0x000N_TT_SS
* N = 节点号 (1-9), 位于 bits[19:16]
* TT = 消息类型: 01=STATUS, 02=CMD, 03=CONFIG
* SS = 子类型
* STATUS = 0x000N0100
*/
static constexpr uint32_t kMotorStatusIdMask = 0xFFF0FFFFU; /* 屏蔽 node nibble [19:16] */
static constexpr uint32_t kMotorStatusIdMatch = 0x00000100U; /* STATUS 类型码 */
static constexpr uint8_t kMotorMaxNodes = CONFIG_MOTOR_MAX_NODES;
/* 同一节点号可能同时挂在多个 CAN 通道上(两台电机同号异线),
* 状态表按 node×channel 双键存储,各通道独立槽位互不覆盖。 */
static constexpr uint8_t kMotorChannels = 3U;
static constexpr uint8_t kMotorSlotCount = kMotorMaxNodes * kMotorChannels; /* 9×3 = 27 */
static constexpr uint8_t kMotorStatusDlc = 32U;
static constexpr uint16_t kMotorOfflineSec = CONFIG_MOTOR_OFFLINE_MS / 1000U;
/* ── 0xC5 推送周期运行时可调(ESP32 经 0xC5 子命令 0xFE 下发)── */
static constexpr uint16_t kMotorPeriodMinMs = 50U; /* 最快 20Hz */
static constexpr uint16_t kMotorPeriodMaxMs = 5000U; /* 最慢 0.2Hz */
static constexpr uint16_t kMotorTicksPerMs = 10U; /* 100us/tick → 每 ms 10 tick */
static constexpr uint8_t kMotorSubCmdSetPeriod = 0xFEU; /* 0xC5 请求 payload[0]=0xFE:设推送周期 */
/* ── BOOT 心跳(0x103)──
* 电机在 Bootloader IDLE 每 ~10ms 发一次 0x00000103(固定 ID,不带节点号)。
* 网页据此显示一个独立 BOOT 状态(不归属任何 Nx)。 */
static constexpr uint32_t kOtaBootHeartbeatId = 0x00000103U;
static constexpr uint32_t kBootHeartbeatTimeoutSec = 3U; /* >3s 无 0x103 → 视为退出 BOOT */
static constexpr uint8_t kMotorActiveBootBit = 0x80U; /* 0xC5 page0 header active 字节 bit7 = boot_active */
/* ── 序列化常量 (0xC5 payload) ── */
static constexpr uint8_t kMotorEntrySize = 44U; /* 1+1+1+1+4+4+32 */
static constexpr uint8_t kMotorPageHeaderLen = 4U; /* page+total+count+active */
static constexpr uint8_t kMotorEntriesPerPage = 5U; /* (236-4)/44 = 5.27 → 5 */
static constexpr uint8_t kMotorTotalPages =
(kMotorSlotCount + kMotorEntriesPerPage - 1U) / kMotorEntriesPerPage; /* ceil(27/5) = 6 */
/// 单电机状态槽
struct MotorSlot {
uint8_t node_id; ///< 1-9, 0=空槽
uint8_t channel; ///< 最后收到帧的 CAN 通道 (0/1/2)
uint8_t online; ///< 1=在线, 0=离线
uint8_t dlc; ///< 最后帧 DLC (正常=32)
uint32_t rx_count; ///< 累计收到 STATUS 帧数
uint32_t last_rx_sec; ///< 最后收到帧时的 g_app_uptime_sec
uint32_t seq; ///< 更新序号 (每次刷新 +1)
uint8_t data[kMotorStatusDlc]; ///< 原始 STATUS payload
};
/// 全局电机状态表
struct MotorStateTable {
MotorSlot slots[kMotorSlotCount]; ///< 按 (node_id-1)*kMotorChannels+channel 索引
uint32_t global_seq; ///< 全局刷新序号
uint32_t last_push_sec; ///< 上次推送完成时间
uint8_t active_count; ///< 当前在线电机数
uint32_t boot_hb_sec; ///< 最后收到 0x103 BOOT 心跳时的 uptime(0=从未)
uint8_t boot_channel; ///< 最后 0x103 所在 CAN 通道
uint8_t boot_active; ///< 1=最近 kBootHeartbeatTimeoutSec 内有 0x103
};
/// 电机状态管理命名空间
namespace MotorState {
/// 获取全局状态表引用
MotorStateTable &table();
/// CAN 帧到达时调用 (主循环上下文, taskCanRxParse 内)
void onCanFrame(uint8_t channel, uint32_t id, const uint8_t *data, uint8_t len);
/// 1Hz 离线检测 (taskHeartbeat 内)
void checkOffline();
/// 序列化一页电机状态到 payload 缓冲区
/// @return 实际写入字节数, 0=参数错误
uint16_t buildPayload(uint8_t page, uint8_t *buf, uint16_t maxLen);
/// 设置 0xC5 主动推送周期(毫秒,钳制到 [kMotorPeriodMinMs, kMotorPeriodMaxMs])。
/// @return 实际应用的毫秒值。
uint16_t setPushPeriodMs(uint16_t ms);
} // namespace MotorState
#endif /* DEVICE_MOTOR_STATE_H */