Files
ESP32_COMM/main/monitor/motor_state.cpp
T
小龙虾 f9cbcc396b feat(spi): 增强 SPI master:性能剖析与执行耗时跟踪
- 新增 `lastWorkerExecUs()`:获取最近一次 worker 任务执行耗时(µs)。
- 引入 `SpiProfile` 结构体:事务级详细剖析,记录 SPI 事务各阶段的时间戳。
- 实现 `lastProfile()`:诊断用,获取最近一次事务剖析快照。
- SPI 时钟配置提升至 13.3 MHz,改善性能。
- 修改前端 JavaScript 轮询逻辑:新增 uptime 检查,更好地处理 ESP32 重启状态。
- SPI 测速 HTML 的计时单位由毫秒改为微秒显示。
- 新建 `spi_ring_cache.h`:共享 SPI ring push 缓存工具。
- 新增 `http_util.h`:共享 HTTP 处理器工具,含 JSON 拼接与查询参数解析。
- 引入 `json_parse.h`:轻量级 JSON 解析器,针对扁平 JSON 对象设计。
- 新增 Windows 环境下的项目构建批处理脚本。
- 重构 CAN 反馈处理:从推送式改为请求驱动架构,降低内存占用、提升效率。
2026-08-02 17:37:19 +08:00

115 lines
3.3 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.
/*
* motor_state.cpp - GD32 多电机状态推送解析与缓存 (SPI TYPE 0xC5)
*/
#include "monitor/motor_state.h"
#include "monitor/spi_ring_cache.h"
#include <string.h>
namespace {
static SemaphoreHandle_t s_mux = nullptr;
static motor_state_table_t s_table = {};
} // namespace
bool onMotorStateEvent(const uint8_t *payload, size_t len)
{
if (payload == nullptr || len < MOTOR_PAGE_HEADER) {
return false;
}
const uint8_t page = payload[0];
const uint8_t total_pages = payload[1];
const uint8_t entry_count = payload[2];
const uint8_t active_cnt = payload[3];
if (page >= MOTOR_TOTAL_PAGES || total_pages == 0 || total_pages > MOTOR_TOTAL_PAGES) {
return false;
}
/* 验证长度足够容纳声明的 entry 数 */
const size_t expected = MOTOR_PAGE_HEADER + static_cast<size_t>(entry_count) * MOTOR_ENTRY_SIZE;
if (len < expected) {
return false;
}
ringCacheEnsureMux(&s_mux);
if (s_mux == nullptr) return false;
if (xSemaphoreTake(s_mux, pdMS_TO_TICKS(20)) != pdTRUE) return false;
/* page 0 时刷新全局字段。
* active 字节 bit7 = boot_active(总线上有电机在 BOOT,0x103 心跳),
* 低 7 位才是真实在线电机数。 */
if (page == 0) {
s_table.active_count = static_cast<uint8_t>(active_cnt & 0x7FU);
s_table.boot_active = (active_cnt & 0x80U) ? 1U : 0U;
}
/* 逐条解析 entry:node×channel 双键槽位,同节点号多通道互不覆盖 */
size_t offset = MOTOR_PAGE_HEADER;
for (uint8_t i = 0; i < entry_count; ++i) {
const uint8_t *e = &payload[offset];
const uint8_t node = e[0];
const uint8_t ch = e[1];
if (node < 1 || node > MOTOR_MAX_NODES || ch >= MOTOR_CHANNELS) {
offset += MOTOR_ENTRY_SIZE;
continue;
}
motor_slot_t &slot = s_table.slots[(node - 1) * MOTOR_CHANNELS + ch];
slot.node_id = node;
slot.channel = ch;
slot.online = e[2];
slot.dlc = e[3];
slot.rx_count = rdU32Le(&e[4]);
slot.seq = rdU32Le(&e[8]);
memcpy(slot.data, &e[12], MOTOR_STATUS_DLC);
offset += MOTOR_ENTRY_SIZE;
}
/* 统计有效槽位 */
uint8_t valid = 0;
for (uint8_t i = 0; i < MOTOR_MAX_SLOTS; ++i) {
if (s_table.slots[i].node_id != 0) valid++;
}
s_table.valid_slots = valid;
s_table.recv_ms = esp_timer_get_time() / 1000;
s_table.have_data = true;
xSemaphoreGive(s_mux);
return true;
}
bool getLatestMotorState(motor_state_table_t *out)
{
if (out == nullptr) return false;
ringCacheEnsureMux(&s_mux);
if (s_mux == nullptr) return false;
bool ok = false;
if (xSemaphoreTake(s_mux, pdMS_TO_TICKS(20)) == pdTRUE) {
if (s_table.have_data) {
*out = s_table;
ok = true;
}
xSemaphoreGive(s_mux);
}
return ok;
}
uint32_t motorStateAgeMs(void)
{
ringCacheEnsureMux(&s_mux);
if (s_mux == nullptr) return UINT32_MAX;
int64_t recv = 0;
bool have = false;
if (xSemaphoreTake(s_mux, pdMS_TO_TICKS(20)) == pdTRUE) {
have = s_table.have_data;
recv = s_table.recv_ms;
xSemaphoreGive(s_mux);
}
return ringCacheAgeMs(have, recv);
}