feat(can): 添加CAN总线错误计数器和总线状态监控

添加了硬件发送错误计数器(tec)、接收错误计数器(rec)和总线状态位图(bus_state)字段,
用于更全面的CAN总线状态监控。同时新增CAN_RX_MODE_BRIDGE_0_TO_12模式,支持CAN0到
CAN1和CAN2的桥接功能。

BREAKING CHANGE: CAN反馈协议格式扩展,增加了3个字节的状态信息。
This commit is contained in:
小龙虾
2026-07-02 02:02:27 +08:00
parent 6335d05198
commit 2fe5393769
13 changed files with 1054 additions and 244 deletions
+4 -4
View File
@@ -4,11 +4,11 @@
// 固件版本
#define FW_VERSION_MAJOR 0
#define FW_VERSION_MINOR 3
#define FW_VERSION_PATCH 35
#define FW_VERSION_STRING "0.3.35"
#define FW_VERSION_PATCH 36
#define FW_VERSION_STRING "0.3.36"
// 编译时间
#define BUILD_TIME "2026-06-29 01:50:43"
#define BUILD_TIME "2026-07-02 01:59:27"
// Git 信息
#define GIT_HASH "a93dba6"
#define GIT_HASH "6335d05"
+1 -13
View File
@@ -57,19 +57,7 @@ static void taskLoop(void *)
if (rc == 0) {
publish(ch, fb);
const bool prevOk = s_prev_poll_ok[ch];
const bool changed = (!s_prev_valid[ch]) ||
(fb.stats.rx_count != s_prev_stats[ch].rx_count) ||
(fb.stats.tx_count != s_prev_stats[ch].tx_count) ||
(fb.stats.rx_drop_count != s_prev_stats[ch].rx_drop_count) ||
(fb.stats.error_count != s_prev_stats[ch].error_count) ||
(fb.stats.mode != s_prev_stats[ch].mode) ||
(fb.stats.snapshot_seq != s_prev_stats[ch].snapshot_seq);
const bool abnormal = (fb.stats.rx_drop_count != 0U) ||
(fb.stats.error_count != 0U) ||
(fb.stats.mode != 0U);
if (!prevOk || changed || abnormal) {
if (fb.stats.error_count != 0U) {
DEBUG_INFO(kTag,
"CH%u seq=%lu mode=%u rx=%lu tx=%lu drop=%lu err=%lu ids=%u",
static_cast<unsigned>(ch),
+8 -3
View File
@@ -477,13 +477,17 @@ esp_err_t OtaCoordinator::beginEsp32OtaWithRetry(uint32_t total_size, ota_channe
// 两通道并发写同一 OTA 分区(之前 BLE 与 WiFi 可同时 OTA 的根因)。
// 避免连续 isIdle() + getSystemState() 各加锁一次(共 2 次锁)。
ota_system_state_t cur = getSystemState();
if (!isIdleState(cur)) {
if (!isIdleState(cur) && cur != OTA_SYS_ERROR) {
DEBUG_WARN(TAG, "ESP32 OTA 被拒绝且系统非空闲 (state=%d),不强制清理,直接失败",
(int)cur);
return err;
}
// 协调器空闲但 Esp32OtaCore.begin 仍被拒 → 锁残留,强制清理后重试
DEBUG_WARN(TAG, "ESP32 OTA 被拒绝但系统空闲,判定为锁残留,强制清理状态机后重试");
if (cur == OTA_SYS_ERROR) {
DEBUG_WARN(TAG, "ESP32 OTA 被拒绝但协调器处于 ERROR,清理底层残留句柄后重试");
} else {
DEBUG_WARN(TAG, "ESP32 OTA 被拒绝但系统空闲,判定为锁残留,强制清理状态机后重试");
}
Esp32OtaCore::instance().forceCleanup();
err = beginEsp32Ota(total_size, channel);
}
@@ -738,6 +742,7 @@ OtaSnapshot OtaCoordinator::getSnapshot() const
bool h = lock();
if (h) {
snap.sys_state = m_sysState;
snap.channel = m_otaChannel;
snap.storage_done = m_gd32StorageDone;
// 就地把状态枚举转字符串,与 sys_state 来自同一时刻
snap.sys_state_str = stateToString(snap.sys_state);
+1
View File
@@ -57,6 +57,7 @@ typedef enum {
struct OtaSnapshot {
ota_system_state_t sys_state = OTA_SYS_IDLE; ///< 系统级 OTA 状态
const char *sys_state_str = "IDLE"; ///< 状态字符串
ota_channel_t channel = OTA_CHANNEL_WIFI; ///< 当前/最近一次 OTA 通道
bool storage_done = false; ///< GD32 固件存储完成标志
ota_meta_t meta = {}; ///< GD32 NVS 元数据
RelaySnapshot relay; ///< 中继进度快照
+15 -5
View File
@@ -1,4 +1,4 @@
/*
/*
* http_handlers.cpp - HTTP 请求处理器实现
* 从 wifi_ota.cpp 提取的 HTTP handler 函数
*/
@@ -746,13 +746,17 @@ esp_err_t can_feedback_handler(httpd_req_t *req)
if (valid[i]) {
p += sprintf(p,
",\"mode\":%u,\"seq\":%lu,\"rx\":%lu,\"tx\":%lu,"
"\"drop\":%lu,\"err\":%lu,\"count\":%u,\"entries\":[",
"\"drop\":%lu,\"err\":%lu,\"tec\":%u,\"rec\":%u,"
"\"bus_state\":%u,\"count\":%u,\"entries\":[",
(unsigned)r.stats.mode,
(unsigned long)r.stats.snapshot_seq,
(unsigned long)r.stats.rx_count,
(unsigned long)r.stats.tx_count,
(unsigned long)r.stats.rx_drop_count,
(unsigned long)r.stats.error_count,
(unsigned)r.stats.tec,
(unsigned)r.stats.rec,
(unsigned)r.stats.bus_state,
(unsigned)r.total_count);
for (uint8_t j = 0; j < r.total_count && j < CANFD_ID_SNAPSHOT_MAX; ++j) {
const id_snapshot_entry_t &e = r.entries[j];
@@ -836,8 +840,12 @@ esp_err_t can_mode_handler(httpd_req_t *req)
int requested = (int)cJSON_GetNumberValue(mode_item);
cJSON_Delete(root);
can_rx_mode_t target = (requested == 1) ? CAN_RX_MODE_FILTER_ID
: CAN_RX_MODE_ALL_PASS;
can_rx_mode_t target = CAN_RX_MODE_ALL_PASS;
if (requested == 1) {
target = CAN_RX_MODE_FILTER_ID;
} else if (requested == 2) {
target = CAN_RX_MODE_BRIDGE_0_TO_12;
}
can_rx_mode_t actual = CAN_RX_MODE_ALL_PASS;
int rc = canFdSetRxMode(target, &actual, 500);
@@ -1258,7 +1266,7 @@ esp_err_t system_status_handler(httpd_req_t *req)
"\"internal_heap_total\": %lu, \"default_heap_total\": %lu, "
"\"temperature\": %.1f, \"uptime_sec\": %lu, "
"\"wifi\": {\"mode\": \"%s\", \"ip\": \"%s\", \"rssi\": %d, \"connected\": %s}, "
"\"ota\": {\"sys_state\": \"%s\", \"sys_state_code\": %d, \"total_size\": %lu, \"stored_size\": %lu, \"storage_done\": %s, "
"\"ota\": {\"sys_state\": \"%s\", \"sys_state_code\": %d, \"channel\": %d, \"channel_name\": \"%s\", \"total_size\": %lu, \"stored_size\": %lu, \"storage_done\": %s, "
"\"relay_state\": %d, \"relay_seq\": %lu, \"relay_total_packets\": %lu, \"gd32_app_alive\": %s, "
"\"gd32_app\": \"v%d.%d.%d.%d\", \"gd32_boot\": \"v%d.%d.%d.%d\"}, ",
FW_VERSION, FW_BUILD_TIME, FW_GIT_HASH, HealthMonitor::instance().getCurrent(),
@@ -1278,6 +1286,8 @@ esp_err_t system_status_handler(httpd_req_t *req)
connected ? "true" : "false",
snap.sys_state_str,
(int)snap.sys_state,
(int)snap.channel,
snap.channel == OTA_CHANNEL_BLE ? "BLE" : "WIFI",
(unsigned long)snap.meta.total_size,
(unsigned long)snap.meta.stored_size,
snap.storage_done ? "true" : "false",
+6 -3
View File
@@ -1,4 +1,4 @@
/*
/*
* canfd_bridge.cpp - CANFD 3通道通信桥接实现(M4: 从 SpiMaster 提取)
*/
#include <string.h>
@@ -109,7 +109,7 @@ int canFdPollFeedback(can_channel_t channel,
out->valid = 0;
uint8_t entryIdx = 0U;
constexpr uint8_t kFixedHeader = 25U; /* ch+page+total+cnt+mode+seq+rx+tx+drop+err */
constexpr uint8_t kFixedHeader = 28U; /* ch+page+total+cnt+mode+seq+rx+tx+drop+err+tec+rec+bus_state */
for (uint8_t page = 0U; page < 2U; ++page) {
uint8_t req[2] = { static_cast<uint8_t>(channel & CAN_CHANNEL_MASK), page };
@@ -134,6 +134,9 @@ int canFdPollFeedback(can_channel_t channel,
out->stats.tx_count = readU32Le(&resp_buf[13]);
out->stats.rx_drop_count = readU32Le(&resp_buf[17]);
out->stats.error_count = readU32Le(&resp_buf[21]);
out->stats.tec = resp_buf[25];
out->stats.rec = resp_buf[26];
out->stats.bus_state = resp_buf[27];
out->total_count = resp_buf[2];
}
@@ -250,7 +253,7 @@ int canFdSetRxMode(can_rx_mode_t mode,
can_rx_mode_t *actual,
uint32_t timeout_ms)
{
if (mode != CAN_RX_MODE_ALL_PASS && mode != CAN_RX_MODE_FILTER_ID) {
if (mode != CAN_RX_MODE_ALL_PASS && mode != CAN_RX_MODE_FILTER_ID && mode != CAN_RX_MODE_BRIDGE_0_TO_12) {
return -1;
}
+5 -1
View File
@@ -1,4 +1,4 @@
/*
/*
* canfd_bridge.h - CANFD 3通道通信桥接(M4: 从 SpiMaster 提取)
*
* 封装 CANFD 帧格式构建 + SPI 传输,与 SpiMaster 的 OTA 协议职责解耦。
@@ -91,6 +91,7 @@ typedef struct {
typedef enum {
CAN_RX_MODE_ALL_PASS = 0, ///< 默认:禁用 ID 过滤,三路全部接收
CAN_RX_MODE_FILTER_ID = 1, ///< 按 CONFIG_CAN_RX_FILTER_ID/MASK 做标准帧过滤
CAN_RX_MODE_BRIDGE_0_TO_12 = 2, ///< CAN0 桥接:CAN0 收到的每一帧按原帧格式自动转发到 CAN1 与 CAN2
} can_rx_mode_t;
/**
@@ -104,6 +105,9 @@ typedef struct {
uint32_t tx_count;
uint32_t rx_drop_count;
uint32_t error_count;
uint8_t tec; /**< 硬件发送错误计数器 (0~255) */
uint8_t rec; /**< 硬件接收错误计数器 (0~255) */
uint8_t bus_state; /**< 总线状态位图:bit0=init,bit1=sleep,bit2=error_passive,bit3=bus_off,bit4=error_warning */
} can_channel_feedback_t;
/**