Files
DCU_APP/GD32E508_DCU_APP/device/motor_state.cpp
T
cbb 079c6442d4 feat(sys): 添加AES加密和电机OTA中继功能模块
添加了AES128加密算法实现和电机OTA中继相关文件,包括:
- ./sys/aes128.h 和 ./sys/aes128.cpp - AES128加密算法头文件和实现
- ./sys/motor_ota_relay.h 和 ./sys/motor_ota_relay.cpp - 电机OTA中继功能
- 更新了项目配置文件包含这些新模块
2026-07-26 20:39:59 +08:00

149 lines
4.6 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.cpp
* @brief 多电机状态表实现 — CAN STATUS 帧识别、内存缓存、SPI 推送序列化。
*
* 所有函数仅在主循环上下文调用(taskCanRxParse / taskHeartbeat / pushStep),
* 无 ISR 竞态,不需要临界区保护。
*/
#include "device/motor_state.h"
#include "device/spi/app_spi_command_defs.h"
#include "main.h"
#include <cstring>
/* ── 全局状态表实例 ── */
static MotorStateTable s_motorTable = {};
namespace MotorState {
MotorStateTable &table()
{
return s_motorTable;
}
void onCanFrame(uint8_t channel, uint32_t id, const uint8_t *data, uint8_t len)
{
/* BOOT 心跳 0x103(固定 ID,不带节点号):电机在 Bootloader IDLE 每 ~10ms 发一次。
* 记录最近收到时刻与通道,供网页显示独立 BOOT 状态(不归属任何 Nx)。
* 注意:必须在 STATUS 掩码过滤之前拦截,否则 0x103 会被下面的掩码判断丢弃。 */
if (id == kOtaBootHeartbeatId) {
s_motorTable.boot_hb_sec = g_app_uptime_sec;
s_motorTable.boot_channel = channel;
s_motorTable.boot_active = 1U;
/* 0x103 仅更新 boot_active 状态,不再串口打印(高频心跳会刷屏)。 */
return;
}
/* 掩码匹配 STATUS ID: 0x000N0100 */
if ((id & kMotorStatusIdMask) != kMotorStatusIdMatch) {
return;
}
const uint8_t node = static_cast<uint8_t>((id >> 16) & 0x0FU);
if (node < 1U || node > kMotorMaxNodes) {
return;
}
MotorSlot &slot = s_motorTable.slots[node - 1U];
slot.node_id = node;
slot.channel = channel;
slot.online = 1U;
slot.dlc = (len <= kMotorStatusDlc) ? len : kMotorStatusDlc;
slot.rx_count++;
slot.last_rx_sec = g_app_uptime_sec;
slot.seq = ++s_motorTable.global_seq;
/* 拷贝原始 payload */
std::memcpy(slot.data, data, slot.dlc);
if (slot.dlc < kMotorStatusDlc) {
std::memset(&slot.data[slot.dlc], 0, kMotorStatusDlc - slot.dlc);
}
}
void checkOffline()
{
const uint32_t now = g_app_uptime_sec;
uint8_t active = 0U;
for (uint8_t i = 0U; i < kMotorMaxNodes; ++i) {
MotorSlot &slot = s_motorTable.slots[i];
if (slot.node_id == 0U) {
continue; /* 空槽 */
}
if ((now - slot.last_rx_sec) >= kMotorOfflineSec) {
slot.online = 0U;
}
if (slot.online != 0U) {
active++;
}
}
s_motorTable.active_count = active;
/* BOOT 心跳超时:>kBootHeartbeatTimeoutSec 无 0x103 → 电机已退出 BOOT。 */
if (s_motorTable.boot_active != 0U &&
(now - s_motorTable.boot_hb_sec) >= kBootHeartbeatTimeoutSec) {
s_motorTable.boot_active = 0U;
}
}
uint16_t buildPayload(uint8_t page, uint8_t *buf, uint16_t maxLen)
{
if (page >= kMotorTotalPages || buf == nullptr) {
return 0U;
}
/* 需要的最小空间: header + 至少 1 entry */
const uint16_t needed = kMotorPageHeaderLen + kMotorEntrySize;
if (maxLen < needed) {
return 0U;
}
const uint8_t startIdx = static_cast<uint8_t>(page * kMotorEntriesPerPage);
uint8_t count = 0U;
/* Header (4B) */
buf[0] = page;
buf[1] = kMotorTotalPages;
buf[2] = 0U; /* entry_count, 稍后回填 */
/* bit[6:0]=在线电机数,bit7=BOOT 心跳(0x103)活跃标志(供网页显示独立 BOOT 状态)。
* active_count 上限 9,bit7 空闲可复用,旧解析方读到会多算 128,故 ESP32 侧需掩 0x7F。 */
buf[3] = static_cast<uint8_t>((s_motorTable.active_count & 0x7FU)
| (s_motorTable.boot_active ? kMotorActiveBootBit : 0U));
uint16_t offset = kMotorPageHeaderLen;
for (uint8_t i = 0U; i < kMotorEntriesPerPage; ++i) {
const uint8_t slotIdx = static_cast<uint8_t>(startIdx + i);
if (slotIdx >= kMotorMaxNodes) {
break;
}
const MotorSlot &slot = s_motorTable.slots[slotIdx];
if (slot.node_id == 0U) {
continue; /* 跳过空槽 */
}
/* 检查剩余空间 */
if (offset + kMotorEntrySize > maxLen) {
break;
}
buf[offset + 0U] = slot.node_id;
buf[offset + 1U] = slot.channel;
buf[offset + 2U] = slot.online;
buf[offset + 3U] = slot.dlc;
writeU32Le(&buf[offset + 4U], slot.rx_count);
writeU32Le(&buf[offset + 8U], slot.seq);
std::memcpy(&buf[offset + 12U], slot.data, kMotorStatusDlc);
offset += kMotorEntrySize;
count++;
}
buf[2] = count;
return offset;
}
} // namespace MotorState