88 lines
3.7 KiB
C++
88 lines
3.7 KiB
C++
/**
|
||||
|
|
* @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;
|
|||
|
|
static constexpr uint8_t kMotorStatusDlc = 32U;
|
|||
|
|
static constexpr uint16_t kMotorOfflineSec = CONFIG_MOTOR_OFFLINE_MS / 1000U;
|
|||
|
|
|
|||
|
|
/* ── 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 = 2U; /* ceil(9/5) = 2 */
|
|||
|
|
|
|||
|
|
/// 单电机状态槽
|
|||
|
|
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[kMotorMaxNodes]; ///< 按 node_id-1 索引
|
|||
|
|
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);
|
|||
|
|
|
|||
|
|
} // namespace MotorState
|
|||
|
|
|
|||
|
|
#endif /* DEVICE_MOTOR_STATE_H */
|