feat(motor_state): 支持多通道电机状态管理并添加推送周期动态调节功能
- 实现 node×channel 双键槽位管理,支持同一节点号在不同CAN通道独立缓存 - 扩展电机状态表容量从9个槽位到27个槽位(9节点×3通道) - 添加SPI命令子命令0xFE用于动态设置0xC5推送周期(50-5000ms) - 增加运行时可调推送周期功能,默认250ms,支持50ms-5s范围调节 - 更新CAN通道配置,启用CH1和CH2通道支持 - 版本号更新至1.5.4
This commit is contained in:
@@ -40,11 +40,12 @@ void onCanFrame(uint8_t channel, uint32_t id, const uint8_t *data, uint8_t len)
|
||||
}
|
||||
|
||||
const uint8_t node = static_cast<uint8_t>((id >> 16) & 0x0FU);
|
||||
if (node < 1U || node > kMotorMaxNodes) {
|
||||
if (node < 1U || node > kMotorMaxNodes || channel >= kMotorChannels) {
|
||||
return;
|
||||
}
|
||||
|
||||
MotorSlot &slot = s_motorTable.slots[node - 1U];
|
||||
/* node×channel 双键槽位:同节点号在不同通道各自独立缓存,互不覆盖 */
|
||||
MotorSlot &slot = s_motorTable.slots[(node - 1U) * kMotorChannels + channel];
|
||||
|
||||
slot.node_id = node;
|
||||
slot.channel = channel;
|
||||
@@ -66,7 +67,7 @@ void checkOffline()
|
||||
const uint32_t now = g_app_uptime_sec;
|
||||
uint8_t active = 0U;
|
||||
|
||||
for (uint8_t i = 0U; i < kMotorMaxNodes; ++i) {
|
||||
for (uint8_t i = 0U; i < kMotorSlotCount; ++i) {
|
||||
MotorSlot &slot = s_motorTable.slots[i];
|
||||
if (slot.node_id == 0U) {
|
||||
continue; /* 空槽 */
|
||||
@@ -115,7 +116,7 @@ uint16_t buildPayload(uint8_t page, uint8_t *buf, uint16_t maxLen)
|
||||
|
||||
for (uint8_t i = 0U; i < kMotorEntriesPerPage; ++i) {
|
||||
const uint8_t slotIdx = static_cast<uint8_t>(startIdx + i);
|
||||
if (slotIdx >= kMotorMaxNodes) {
|
||||
if (slotIdx >= kMotorSlotCount) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -145,4 +146,14 @@ uint16_t buildPayload(uint8_t page, uint8_t *buf, uint16_t maxLen)
|
||||
return offset;
|
||||
}
|
||||
|
||||
uint16_t setPushPeriodMs(uint16_t ms)
|
||||
{
|
||||
if (ms < kMotorPeriodMinMs) { ms = kMotorPeriodMinMs; }
|
||||
if (ms > kMotorPeriodMaxMs) { ms = kMotorPeriodMaxMs; }
|
||||
/* ms→tick:100us/tick,每 ms 10 tick。写 volatile uint16_t 在 Cortex-M 上为原子,
|
||||
* tick100us(ISR) 读该值无擕裂风险。 */
|
||||
g_scheduler.motor_state_period_ticks = static_cast<uint16_t>(ms * kMotorTicksPerMs);
|
||||
return ms;
|
||||
}
|
||||
|
||||
} // namespace MotorState
|
||||
|
||||
@@ -27,9 +27,19 @@ static constexpr uint32_t kMotorStatusIdMask = 0xFFF0FFFFU; /* 屏蔽 node nibb
|
||||
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)。 */
|
||||
@@ -41,7 +51,8 @@ static constexpr uint8_t kMotorActiveBootBit = 0x80U; /* 0xC5 page0 header
|
||||
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 */
|
||||
static constexpr uint8_t kMotorTotalPages =
|
||||
(kMotorSlotCount + kMotorEntriesPerPage - 1U) / kMotorEntriesPerPage; /* ceil(27/5) = 6 */
|
||||
|
||||
/// 单电机状态槽
|
||||
struct MotorSlot {
|
||||
@@ -57,7 +68,7 @@ struct MotorSlot {
|
||||
|
||||
/// 全局电机状态表
|
||||
struct MotorStateTable {
|
||||
MotorSlot slots[kMotorMaxNodes]; ///< 按 node_id-1 索引
|
||||
MotorSlot slots[kMotorSlotCount]; ///< 按 (node_id-1)*kMotorChannels+channel 索引
|
||||
uint32_t global_seq; ///< 全局刷新序号
|
||||
uint32_t last_push_sec; ///< 上次推送完成时间
|
||||
uint8_t active_count; ///< 当前在线电机数
|
||||
@@ -82,6 +93,10 @@ void checkOffline();
|
||||
/// @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 */
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
* - 总开关:sys/config.h 中 CONFIG_MOTOR_STATE_ENABLE
|
||||
*
|
||||
* payload 格式 (每页):
|
||||
* [0] page 页号 (0/1)
|
||||
* [1] total_pages 总页数 (2)
|
||||
* [0] page 页号 (0..kMotorTotalPages-1)
|
||||
* [1] total_pages 总页数 (6,27 槽 = 9 node × 3 ch)
|
||||
* [2] entry_count 本页有效电机数
|
||||
* [3] active_count 全局在线电机数
|
||||
* [4..] 每个 entry 44B:
|
||||
@@ -47,6 +47,18 @@ public:
|
||||
|
||||
void handle(AppSpiCommand &bus, const SpiCommandContext &ctx) override
|
||||
{
|
||||
/* 子命令:payload[0]=0xFE 且 len>=3 → 设置 0xC5 推送周期(payload[1..2]=毫秒 LE)。
|
||||
* 有效页号仅 0..kMotorTotalPages-1,0xFE 作子操作码不冲突。回 ACK 携实际应用毫秒 LE。 */
|
||||
if (ctx.payload_len >= 3U && ctx.payload[0] == kMotorSubCmdSetPeriod) {
|
||||
const uint16_t ms = static_cast<uint16_t>(ctx.payload[1] | (ctx.payload[2] << 8));
|
||||
const uint16_t applied = MotorState::setPushPeriodMs(ms);
|
||||
uint8_t ack[2] = { static_cast<uint8_t>(applied & 0xFFU),
|
||||
static_cast<uint8_t>((applied >> 8) & 0xFFU) };
|
||||
bus.buildResponse(ctx.tx_buf, ctx.tx_buf_size, ctx.tx_len,
|
||||
SpiDefs::kRspAck, ack, sizeof(ack));
|
||||
return;
|
||||
}
|
||||
|
||||
/* 请求 payload[0] = page (可选, 默认 0) */
|
||||
const uint8_t page = (ctx.payload_len >= 1U) ? ctx.payload[0] : 0U;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user