Files
DCU_APP/GD32E508_DCU_APP/device/spi/app_spi_diag.h
T
cbb f3d7fc0c30 feat(canfd): 添加收发器静默模式支持和SPI命令接口
添加了CAN FD收发器静默模式功能,允许通过硬件引脚控制发送使能:
- 新增setTxSilent/isTxSilent接口控制TCAN1057收发器Silent模式
- 静默时驱动器关断但接收机继续工作,支持只听不发场景
- 添加SPI命令0xC8用于ESP32远程控制静默模式切换
- 在总线状态位图中增加bit5表示静默状态
- 新增canBusyWaitUs函数用于ISR安全的微秒延时

fix(esc_lan9252): 改进warm jump后LAN9252探测逻辑

修复warm jump导致的LAN9252初始化问题:
- 识别warm jump后芯片非POR状态(idx0!=0x1234)的情况
- 自动补发硬件复位对齐到POR状态后再进行正常探测
- 改进调试信息输出,区分POR状态和warm jump残值情况
- 修正失败路径的复位标志初始化逻辑

refactor(spi): 整理SPI命令文件结构

调整SPI命令相关文件的组织结构:
- 移动诊断相关文件到更合适的位置
- 重新整理SPI命令处理文件的分组
2026-10-10 02:25:56 +08:00

123 lines
6.1 KiB
C++

#ifndef DEVICE_APP_SPI_DIAG_H
#define DEVICE_APP_SPI_DIAG_H
#include <cstdint>
#include <cstddef>
#include "sys/config.h"
/**
* @file app_spi_diag.h
* @brief SPI 链路全量硬件与协议诊断模块声明。
*
* 用于快速量化与定位 SPI 通信故障根因:
* - 物理/时序层:CS 边沿跳变、丢失下降沿、0 字节事务(SCK未走/DMA落后)、短包(<20B)、全FF/全00
* - 硬件状态:SPI0 RXORERR (接收FIFO溢出)、FERR (格式错误)、SPI_STAT 寄存器值
* - 协议/帧层:SOF丢失、相位偏移(SOF drift)、版本错误、超长/截断、CRC32位跳变(附带期望值与计算值)
* - 状态机/会话:Session ID脱节、序号跳变(丢包)、序号重复(重发)、未处理命令TYPE、空响应
* - 握手/流控:PB6 DATA_READY 引脚电平、PA15 CS 引脚电平、主动按铃超时、按铃竞争碰撞
* - 现场快照:记录最近一次异常发生时刻、实际接收长度、前导原始字节及 CRC 对比
*/
/**
* @brief SPI 帧解析失败原因细分
*/
enum SpiParseResult : uint8_t {
SPI_PARSE_OK = 0U, ///< 成功解析合法 v2 帧
SPI_PARSE_ERR_TOO_SHORT = 1U, ///< 事务长度小于 v2 最小帧头 (rxLen < 20)
SPI_PARSE_ERR_ALL_FF = 2U, ///< 接收数据全为 0xFF (从机浮空/DMA未就绪/MISO未连接)
SPI_PARSE_ERR_ALL_00 = 3U, ///< 接收数据全为 0x00 (地电平短路或总线死锁)
SPI_PARSE_ERR_NO_SOF = 4U, ///< 未扫描到 SOF 同步字 (0xA5 0x5A)
SPI_PARSE_ERR_BAD_VERSION = 5U, ///< 找到 SOF,但 VERSION != 0x02
SPI_PARSE_ERR_BAD_LEN = 6U, ///< 长度异常 (payload_len > 236 或帧总长超出实际收到的 rxLen)
SPI_PARSE_ERR_CRC_MISMATCH = 7U, ///< CRC32 校验失败 (物理信号干扰/位跳变)
};
/**
* @brief SPI 链路全量诊断计数器与状态快照
*/
struct SpiDiagStats {
/* 1. CS 边沿与事务统计 */
uint32_t total_cs_assert; ///< CS 下降沿次数
uint32_t total_cs_release; ///< CS 上升沿次数
uint32_t missed_cs_assert; ///< 丢失 CS 下降沿异常次数
uint32_t zero_rx_len_count; ///< RX 收到 0 字节 (SCK 未时钟化或 DMA 启动严重落后)
uint32_t short_rx_len_count; ///< RX 收到 1~19 字节 (<20B 残包)
uint32_t all_ff_count; ///< 全 0xFF 事务数
uint32_t all_00_count; ///< 全 0x00 事务数
/* 2. 硬件外设与状态 */
uint32_t rx_overrun_count; ///< SPI0 RXORERR (接收 FIFO 溢出)
uint32_t format_error_count; ///< SPI0 FERR (帧格式错误)
uint16_t last_spi_stat; ///< 最近一笔事务结束时的 SPI_STAT 寄存器值
/* 3. 帧解析统计 */
uint32_t valid_v2_frames; ///< 成功解析的合法 v2 帧总数
uint32_t parse_fail_count; ///< 帧解析失败总数
uint32_t err_no_sof; ///< 未找到 SOF0(0xA5)+SOF1(0x5A)
uint32_t err_sof_drift; ///< SOF 偏移不在索引 0 (前导毛刺或字节时序偏移)
uint32_t err_bad_version; ///< 版本不匹配
uint32_t err_bad_len; ///< 长度字段错误或事务截断
uint32_t err_crc_mismatch; ///< CRC32 校验失败
/* 4. 会话与序号流控 */
uint32_t hello_count; ///< 收到 LINK_HELLO 次数
uint32_t session_mismatch; ///< Session ID 不匹配 (对端重置或会话脱节)
uint32_t seq_ok_count; ///< 序号严格递增正常次数
uint32_t seq_jump_count; ///< 序号跳变 (丢帧)
uint32_t seq_dup_count; ///< 序号重复 (重发包)
uint16_t last_rx_len; ///< 最近收到的 rx_len (字节数)
uint8_t last_rx_seq; ///< 最近收到的 rx_seq
uint8_t expected_rx_seq; ///< 当前期望的 rx_seq
uint8_t last_tx_seq; ///< 最近发送的 tx_seq
/* 5. 命令路由与处理 */
uint8_t last_rx_type; ///< 最近收到的命令 TYPE
uint8_t last_tx_type; ///< 最近构建的响应 TYPE
uint32_t unknown_type_count; ///< 未知命令 TYPE (无 handler 注册)
uint8_t last_unknown_type; ///< 最近未知的 TYPE 值
uint32_t handler_empty_count; ///< Handler 执行但返回 tx_len == 0
uint8_t last_empty_type; ///< 最近产出 0 字节的 TYPE 值
uint32_t error_rsp_count; ///< GD32 主动返回 0x82 ERROR 响应次数
uint8_t last_error_code; ///< 最近一次返回 0x82 的错误码 (0xE8/0xE9/0xEA/V2_ERR_*)
/* 6. 握手引脚与流控 */
uint32_t ring_request_count; ///< 主动按铃请求次数
uint32_t ring_timeout_count; ///< 按铃超时 (ESP32 未在期限内读走)
uint32_t ring_collision_count; ///< 按铃与 ESP32 主动发帧竞争碰撞次数
uint8_t current_local_credit; ///< 当前 local credit (0 或 1)
/* 7. 最近异常快照 (用于定位具体错误现场) */
uint32_t last_error_uptime_ms; ///< 异常发生时刻 SysTick 毫秒数
uint16_t last_error_rx_len; ///< 异常事务收到的字节数
uint8_t last_error_bytes[8]; ///< 异常事务前 8 字节原始数据
uint32_t last_error_calc_crc; ///< CRC 错误时: 计算出的 CRC
uint32_t last_error_expected_crc;///< CRC 错误时: 帧内携带的 CRC
uint8_t last_error_sof_offset; ///< SOF 所在的偏移位置 (255 表示未找到)
uint8_t last_error_reason; ///< SpiParseResult 错误原因枚举
};
#if (CONFIG_SPI_DIAG_ENABLE != 0)
/** @brief 获取当前全局 SPI 诊断统计快照引用 */
const SpiDiagStats& getSpiDiagStats();
/** @brief 复位清零所有 SPI 诊断统计计数器 */
void resetSpiDiagStats();
/** @brief 将解析结果枚举转换为可读字符串 */
const char* spiParseResultToString(SpiParseResult res);
/** @brief 打印完整 SPI 链路诊断报告到串口 (ASCII 表格) */
void dumpSpiDiag();
#else
inline const SpiDiagStats& getSpiDiagStats()
{
static const SpiDiagStats s_empty{};
return s_empty;
}
inline void resetSpiDiagStats() {}
inline const char* spiParseResultToString(SpiParseResult /*res*/) { return "DISABLED"; }
inline void dumpSpiDiag() {}
#endif
#endif /* DEVICE_APP_SPI_DIAG_H */