Files
DCU_APP/GD32E508_DCU_APP/sys/config.h
T

465 lines
50 KiB
C++
Raw Normal View History

2026-07-20 02:36:56 +08:00
/**
* @file config.h
* @brief 应用级集中�置项定义。
* @note 用于统一管�当�项目中�调的外设�时��通信与算法常�。
*/
#ifndef SYS_CONFIG_H
#define SYS_CONFIG_H
#include <stdint.h>
#include <stddef.h>
#include "gd32e50x.h"
2026-07-20 02:36:56 +08:00
/* ============================================================
* 功能开关(调试/特性总闸 — 改这里��开关,改完�编)
* 0/false=关,1/true=开。需� #if 的�留 #define。
* ============================================================ */
/**
* @brief 5s 诊断任务是�输出全� UART 摘�(task_runner)。
* @note 任务本身始终 5s 调度:0=默认仅状��化/错误增�;1=周期打� ADC/CAN/ECAT 全�行。
* 现场抓��/温度时改回 1 ��编��。
*/
#define CONFIG_UART_TEMP_PERIODIC_PRINT 0
/**
* @brief 全�摘�详细级别(仅 PERIODIC_PRINT=1 时生效;0=ADC 精简 1=� CAN 统计)。
* @note 默认 0;需� can_tx/rx/drop 等列时与 PERIODIC_PRINT 一并打开。
*/
#define CONFIG_UART_DIAG_LEVEL 0
2026-07-20 02:36:56 +08:00
/**
* @brief SPI 调试�馈 0xC4:GD32 ADC 温度/VDDA/VIN 1Hz 主动推�。
* @note 调试用;0=�注册 handler��推�。与 ESP32 GD32_ADC_FEEDBACK_ENABLE �对。
*/
#define CONFIG_SPI_ADC_FEEDBACK_ENABLE 1
/**
* @brief 多电机状�表 0xC5:CAN STATUS 帧缓存 + 250ms 主动推�。
* @note 0=�注册 handler��推���解� STATUS 帧。与 ESP32 端 motor_state_task �对。
*/
#define CONFIG_MOTOR_STATE_ENABLE 1
/**
* @brief 多电机版本 0xC6:CAN 版本应答帧(0x00FF)缓存 + 事件驱动推�。
* @note 0=�识别版本帧��推�。与 ESP32 端 motor_version 缓存�对。
* 版本为按需查询的低频数�,独立于 0xC5 周期推�。
*/
#define CONFIG_MOTOR_VERSION_ENABLE 1
/** @brief 最大电机节点数 (node ID 1-9)。 */
constexpr uint8_t CONFIG_MOTOR_MAX_NODES = 9U;
/** @brief 电机离线判定超时 (ms)。超过此时间未收到 STATUS 帧则标记 offline。 */
constexpr uint16_t CONFIG_MOTOR_OFFLINE_MS = 3000U;
/** @brief SPI 命令�帧日志(#if)。1.6.29 诊断:开��仅异常路径会输出
* (�v2帧/未知type/SEQ跳�/session�匹�),�帧"收到�刷写"行已在
* app_spi_command.cpp 关闭,正常状� UART �默。定�完改回 0。 */
#define CONFIG_SPI_APP_LOG_ENABLE 1
2026-07-20 02:36:56 +08:00
/** @brief CAN_SEND �功帧详细日志(#if)。 */
#define CONFIG_SPI_CAN_SEND_VERBOSE_LOG 0
/** @brief CAN-FD BRS 使能(canfd.cpp #if)。
* 关闭 BRS: 数�段用仲�段�速率,牢牢性更高。
* 电机 BOOT 无法正确接收 BRS 高速段→nonce(16B FD+BRS)被拒�→�手失败。
* 1.6.1 起:�� 1U 也�——motor_ota_relay 在 OTA 期间� CanFd::setChannelBrsSuppress
* 按通�压制 BRS(与 ARD 切��一 enterState 钩�,结�必��),日常����影�。 */
#define CONFIG_CAN_FD_BRS_ENABLE 1U
2026-07-20 02:36:56 +08:00
/** @brief �动时总线探测帧;正常固件关。 */
#define CONFIG_CAN_STARTUP_BUS_TEST_ENABLE 0
/** @brief �动时内部回环自检;正常固件关。 */
#define CONFIG_CAN_STARTUP_LOOPBACK_TEST_ENABLE 0
/** @brief CAN �动详细诊断 dump;默认关。 */
#define CONFIG_CAN_STARTUP_DIAG_VERBOSE 0
/**
* @brief 周期测试��任务(taskCanTxSend,� 0x18FF5000 64B FD 帧)。
* @note 调试用:该 PGN 无业务接收节点 ACK。auto_retrans=ENABLE 时无 ACK 被
* 无����进 TEC(TEC 一直涨);DISABLE 时��失败� MTEn→error_count++。
* 电机拓扑下无对端应答,默认关以消除�续错误帧。需�回放测试波形时打开。
*/
#define CONFIG_CAN_PERIODIC_TEST_TX_ENABLE 0
/** @brief CAN0/1/2 通�使能。CH1/CH2 未接设备,关闭���续 CAN 错误 + � SPI 带宽。 */
2026-07-20 02:36:56 +08:00
constexpr bool CONFIG_CAN_CH0_ENABLE = true;
constexpr bool CONFIG_CAN_CH1_ENABLE = true;
constexpr bool CONFIG_CAN_CH2_ENABLE = true;
2026-07-20 02:36:56 +08:00
/** @brief 主循环 WFI(main.cpp #if)。 */
#define CONFIG_MAIN_LOOP_WFI_ENABLE 0
/** @brief EtherCAT SYNC0 �步模�(#if)。 */
#define CONFIG_ETHERCAT_SYNC_MODE 0
/** @brief MOTOR-OTA INFO 级日志(�手/进度/�控 chatter);错误与关键事件��留。 */
#define CONFIG_OTA_RELAY_LOG_ENABLE 1
/** @brief CAN �始化/自检 INFO 级日志;�始化失败与硬件错误��留。 */
#define CONFIG_CAN_INIT_LOG_ENABLE 1
/** @brief EtherCAT/EXMC �始化 INFO 级日志;FAIL ��留。 */
#define CONFIG_ECAT_INIT_LOG_ENABLE 1
/** @brief HBI bring-up 诊断探针(bit-bang 探活 + predrive/回显 + 20s 周期�跑)。
* @note 硬件层已确认全通(IDX_0=0x1234�BYTE_TEST=0x87654321),默认关闭以清�上电日志,
* 并���次上电/� 20s 把 EXMC 引脚切� GPIO 手动拉一�。��/补焊需�新排查浮空时置 1。
* 网页万用表通断测试 continuityDrive() ��此开关影�,始终�用。 */
#define CONFIG_ESC_EXMC_DIAG 0
/** @brief [DIFF-STATS] 输入镜�差分统计(0x2000 sub13..23,�化差分写能�多少字节�
* run 碎片有多严��比对自身�多少钱)。临时仪表,��完置 0 �与改造��字节等价。
* @note 开�时��在 PDI_Isr 内多一次 600B 字粒度比对 + 一次快照拷�(约 10µs �级,
* 实测� sub23),因此 **sub9/sub10 的写侧耗时会包�这部分**,�能直接与历�
* �能与历�写侧基准对�——�看差分潜力就�看 sub13..22,�看真实写侧耗时请置 0 �测。 */
#define CONFIG_ECAT_DIFF_STATS 0
/**
* @brief 输入 PDO 稀�写 ESC(1.6.22 P1):按密排 count �写「段1+段2有效区�与「段3+段5�,
* 跳过未用到的空槽区(40+count*56 .. 543),� HW_EscWriteIsr 的 PRAM 字节数。
* @note 0=� 600B 整包写(A/B 基线);1=分段写。� 1.6.21 密排布局(magic≠0xA5)或
* count 撑满时自动回退整包。**总长/SII/主站解����**;空洞区在 ESC 中为该
* SM 缓冲上次残留,主站必须继续�按 count 迭代(1.6.21 已如此)。
* 验收看 0x2000 sub10(EscWrite 周期)与 load_perm,�工况开关�测一轮。
*/
#define CONFIG_ECAT_SPARSE_ESC_WRITE 1
/**
* @brief 输入 PDO 差分写 ESC(1.6.25 P1):在稀�写之上,按「段头(0..543)+段尾(544..599)�
* 与上次写入影�比对;内容稳定且冲刷完�则整段跳过,� PRAM 字节。
* @note 3-buffer 安全:SM3 三缓冲轮转,�能�对「上一��布镜��比——目标缓冲
* 里是 2 ��的内容。采用「�化�连续写 3 �冲刷��议:�化当�写并置
* flush=2,�� 2 �强制写完 3 个缓冲,稳定���许跳过。全段跳过时强制
* 写末 2 字节完� SM 事件应答。0=关闭,退回稀�写/整包。SII/主站解���。
* 验收:CONFIG_ECAT_DIFF_STATS �数�对� sub10;或与 1.6.22 基线 A/B。
*/
#define CONFIG_ECAT_DIFF_ESC_WRITE 1
/** @brief �动横幅与 main 进度打�。 */
#define CONFIG_APP_BANNER_LOG_ENABLE 1
/* ============================================================
* 日志分级�:*_LOGI=INFO 级,由上方模�开关编译期�剪,
* 格�串�之被链接器回收;LOG_ERR=错误/关键事件,��留。
* 使用文件需自行�� printf 声明(<cstdio>)。
* ============================================================ */
#define LOG_ERR(...) printf(__VA_ARGS__)
#if CONFIG_OTA_RELAY_LOG_ENABLE
#define OTA_LOGI(...) printf(__VA_ARGS__)
#else
#define OTA_LOGI(...) ((void)0)
#endif
#if CONFIG_CAN_INIT_LOG_ENABLE
#define CAN_LOGI(...) printf(__VA_ARGS__)
#else
#define CAN_LOGI(...) ((void)0)
#endif
#if CONFIG_ECAT_INIT_LOG_ENABLE
#define ECAT_LOGI(...) printf(__VA_ARGS__)
#else
#define ECAT_LOGI(...) ((void)0)
#endif
#if CONFIG_APP_BANNER_LOG_ENABLE
#define APP_LOGI(...) printf(__VA_ARGS__)
#else
#define APP_LOGI(...) ((void)0)
#endif
2026-07-20 02:36:56 +08:00
/* ============================================================
* APP 固件版本�
* ============================================================ */
constexpr uint32_t APP_FW_VERSION_MAJOR = 1U;
constexpr uint32_t APP_FW_VERSION_MINOR = 6U;
/* 1.6.34:EtherCAT 侧补「APP→BOOT 交接�+ 版本自�。
* (1) 新增 CoE 0x2502「GD32 boot request�(VAR UINT32, ACCESS_READWRITE,
* comm/ssc/port/dc_app.c):主站写 1 就请求本 APP 回引导程�。动作�用�产
* SPI 0xB0 那�异步链(回调�置 pending,�置页 CHECK/AVAL/EN 三字的 Flash 写
* + NVIC_SystemReset 在主循环 OtaBootRequest::process() 下一圈�),所以 SDO
* �应先�����到,�会把 SM1 �包留在总线上。APP 写的 EN=NOT_AVAL 正好命中
* BOOT 救砖门 ecatArmed()=!(CHECK==AVAL&&EN==AVAL) ⇒ 直�自动解�,�需 word8 令牌。
* 动因:0x2500/0x2501 �在 BOOT,实测对跑�的 APP 写 0x2500 回
* abort=0x06020000「对象�存在�,跑�的机器必须先交接�能走总线。
* (2) CoE 0x100A 从 SSC �� "1.0" 改�真版本串(DEVICE_SW_VERSION_LEN 0x3→0x10�
* acSoftwareversion 显� LEN+1 防越界读�initial.cpp 起�时写),主站刷完�以在
* 总线上自�"新 APP 起�了�是哪一版",�必��串�或 SPI PING 身份�。 */
/* 1.6.40:HBI 起�探针放行判�从「HBI_IDX_0==0x1234���「BYTE_TEST 往返�,
* 根治 warm jump �自检「�级 ethercat / EXMC 总线未就绪�+ 0xA2 全回 0xE1。
* 实测链(2026-09-16):BOOT 1.1.0.18 短路跳转� RST# �,刷完 warm jump 必现
* fault_codes[10]=1�module_mask 缺 bit10/11;reset(芯片�掉电)�正常。
* 根因�在 BOOT �没���:cold 路径芯片自带 POR�IDX_0 天然 0x1234,与
* PB10 那�有没有�地无关;warm 路径 BOOT 的 bootEcatDeinit 最�一� HBI 写
* 把索引寄存器留在 0x0054(IRQ_CFG),「读��默认值�的探针对 warm 必然�阴性。
* 本版:BYTE_TEST(索引写+数�读完整往返)为唯一放行门;任一判��过补一�
* RST# �探一轮(SSC 未起,零代价);两轮��通=总线物��通,维��级;
* BYTE_TEST 通但 IDX_0 异常=放行+诊断。s_busReady 语义�步改写(0xE1 门��)。 */
constexpr uint32_t APP_FW_VERSION_PATCH = 40U; /* 1.6.33: OP 输入新鲜度ä¸�å�˜é‡�(EcatDbg_OpInputFreshness)——SM-Synchron 下输入 PDO å�ªé�  PDI_Isr,通路一å�œä¸»ç«™è¯»åˆ°å†»ç¼“冲(WKC/AL ä»�正常=å�‡è¿žæŽ¥);现 OP 内输入å�œæ›´>5ms 由主循环强制 PDO_InputMapping+é‡�开门。根治"连接看起æ�¥æ­£å¸¸ä½† CPU/CAN ä¸�æ›´æ–°ã€�å¤�ä½� GD32 æ‰�好"。 */ /* 1.6.32: SPI CS EXTI æ��到抢å� çº§0(è§� app_spi_command configureExti),根治 CS release 被 PDI/RT_CRITICAL 延迟导致的 RX 撕裂(0xE8) *//* /* 1.6.28:ESC ç�¡çœ æŒ‚起门控(EscLan9252::pmSuspended)。pmDrive è¿› D1-D3 ç½®ä½�å�Žå…¨å›ºä»¶ HBI 消费者é�™é»˜â€”—task_runner å�œè°ƒ MainLoop/AL 守护/AL 心跳(MainLoop æ¯�圈 HBI 读 0x220,ç�¡çœ ä¸­ä»»ä½• HBI 访问å�«è¯»éƒ½æŠŠèŠ¯ç‰‡æ‰“å›ž D0,2026-09-12 实测休眠 ~20s å�³è¢«ä¸»ç«™"å¤�æ´»":芯片毫秒级被 GD32 自己å�«é†’ã€�SM 失效å�Žä¸»ç«™ wedge é‡�建 OP);ethercat_sync 三 EXTI ISR 与 timer Sync0 路径清 pending å�³è¿”。解除:pmDrive 唤醒æˆ�功/op0 读到 D0 自愈/nrstDrive 释放å¤�ä½�/hardwareReset。é…�套主站侧 ecat_native /api/pm/hold 楔死看门狗è±�å…�。 */ /* A/B 结æ�Ÿ: æ�¢å¤� 1.6.25 稀ç–�+差分写(用户è£�定, 主站侧已有å�Žå�°æ�¢å¤�兜底) */ /* A/B C组: SPARSE=0+DIFF=0, 回 1.6.21 æ�’å…¨é‡� 600B 写 ESC(查开机楔死) */ /* 1.6.25:P1 输入 PDO 差分写 ESC(三区:段1 桥状æ€�/段2 电机密排/段3+5 快照诊断;å�˜åŒ–å�Ž 3-buffer 冲刷å��议,稳定段跳过;全跳过写末 2 字节完æˆ� SM)。P2 csrRead 分级:verify å�‚数,SM/AL/邮箱默认å�Œè¯»ï¼Œè¯Šæ–­/MII busy/init 轮询å�•读。开关 CONFIG_ECAT_DIFF_ESC_WRITE。 */ /* 1.6.24:pmDrive è¿› D1/D2/D3 移除读回验è¯�。æ�¿çº§å®žæµ‹ï¼šè¿›å…¥ç�¡çœ å�Žä»»ä½• HBI 访问(å�«è¯»ï¼Œæ‰‹å†Œ 6.3.4.2 å�ªè¯´å†™å”¤é†’,芯片实际按主机 CS 活动判定)都会 <50ms 触å�‘唤醒回 D0——读回验è¯�自è¯�失败,改为置信构造返回值(READY=0|SLEEP_EN=1|MODE=op);「读状æ€�ã€�语义改为"若在ç�¡çœ åˆ™ä¸»åŠ¨æ‰“ç ´ç�¡çœ "。 */ /* 1.6.23:LAN9252 芯片级电æº�管ç�†è¯Šæ–­è·¯å¾„。EscLan9252::pmDrive()(0xA2 action 30~34:30 读状æ€�/31 è¿› D1/32 è¿› D2/33 è¿› D3/34 手动唤醒),按 DS 6.3 写 PMT_CTRL(0x084) PM_MODE+PM_SLEEP_EN(分两次写);D3 å‰�置两 PHY General Power Down(ç»� ESC 核心 MII 管ç�† 0x0510-0x0514,PHY A 地å�€ 0/2 è¿�行期判定),唤醒=HBI 写 BYTE_TEST å�Žè½®è¯¢ BYTE_TEST/READY(DWT CYCCNT 忙等,50µs 步进ã€�上é™� 10ms——0xA2 在 CS EXTI ISR 上下文执行,ç¦�用 SysTickTimer::delayMs 会死等;HBI 段 maskEnter å±� EXTI0/1 防 SYNC0 çª�入)。唤醒å�Žè‡ªåЍæ�¢å¤� PHY 上电(自å��商需数秒)。休眠期间 EtherCAT 通信中断属预期,主站 wkc 报错。 */ /* 1.6.22:P1 稀ç–�写 ESC。PDO_InputMapping 在密排布局下按 count å�ªå†™ã€Œæ®µ1+段2有效区(0..40+count*56)ã€�与「段3+段5(544..599)ã€�,跳过空槽区,çœ� HW_EscWriteIsr çš„ PRAM 字节。开关 CONFIG_ECAT_SPARSE_ESC_WRITE=1ï¼›é�žå¯†æŽ’/count 撑满自动回退整包。总长 600B/SII/主站解ç �ä¸�å�˜ã€‚A/B:置 0 é‡�ç¼–å�³ 1.6.21 写侧。 */ /* 1.6.21:段 2 改「一帧=总线电机整轮状æ€�ã€�。用户è£�定旧需求ä¸�对:ä¸�è¦� win_base 滑窗扫 27 槽,也ä¸�è¦�段 4 历å�²å¸§è¦†ç›–窗å��å¤�é‡�å�‘。实测病ç�†ï¼šæŽ¥ 3 个电机网页å�´çœ‹åˆ° 5 个状æ€�帧——固定 5 槽里空槽 node_id=0 也å� ä½�,主站ä¸�滤就多报。本版:(1) 段 2 密排,count=本æ‹�实际电机数(0..9),空节点ä¸�å� ä½�,槽åº�按 node_id å�‡åº�,å�Œå�·å¤šé€šé�“å�– seq 最新;主站 `for i in 0..count` 按槽内 node_id 自动识别,无需滑窗。(2) 删段 4 覆盖窗å�£ï¼ˆfillRxWindow/RxWinSlot 整套),é�ž STATUS ä»�走段 1/3 最近一帧。(3) 差分填充:节点集å�ˆä¸Žå�„ seq 未å�˜æ—¶æ•´æ®µå¤�用影å­�,跳过 MotorState é‡�读与é€�æ§½ 48B æ‹·è´�。(4) 输入 PDO 总长ä»� 600B,SII 无需é‡�刷;段 2 扩到 32..543ã€�段 3 å‰�移到 544ã€�段 5 ä»� 584。**主站解ç �å¿…é¡»å�Œæ­¥**(decode_input/transport-ecat.js:count 密排 + 段 3 å��ç§
/* 1.6.19:[DIFF-STATS] 仪表�本终于测对了,并推翻 1.6.18 自己的判断。1.6.18 认定"是 -Oz 把 memcpy(&a,p,4) 编�函数调用",改�手拼 4 字节移�,结果�汇编出**�字 8 � ldrb.w**�比开销从 21.3µs/�涨到 41µs/� —— 方�完全�了。真根因�在这里:alignas(4) 标在数组声明上�到调用点为止,而 observe(const uint8_t*) 的形�签��带对�信�,优化器无从�明指针 4 字节对�,两版都�能�字节走。修法是对两个入���一次 __builtin_assume_aligned(p,4) �走 memcpy —— �汇编确认循环体已缩� ldr.w/ldr.w/cmp/bne �字 4 � (150 字 ≈ 750 cycle ≈ 4.2µs/�,与最�估算��级),且整个函数 bl 归零(600B 快照拷�也被折�字��)。教训:**测�工具的开销会直接��结论**,"差分判断很贵"这��差一点就被我自己造的�象�实。被测�(�化字节数 25%�run 峰值 12)��此影�,两版读数一致,�然有效。 */
/* 1.6.18:修 [DIFF-STATS] 仪表自己的�象 —— 比对循环里 `memcpy(&a,p,4)` 在 -Oz 下被编�**函数调用**而��� LDR,首轮实测 compare 开销虚报� 21.3µs/�(≈300 次调用×995 �),若拿它当"差分判断很贵"的��就结论�了。改��端 4 字节移�拼装:�法��碰严格别��且是 Clang 认得折 LDR 的模�。顺带把 alignas(4) 的�由改�"�务整� memcpy",比对本身���赖它。Code 52716→52768(+52B),代价是体积�是速度。 */
/* 1.6.17:新增 [DIFF-STATS] 临时仪表,为"输入 PDO 改差分写"��,**零行为�更**。动机:差分写的收益完全�决于��真正�了多少字节��化点碎�几个 run,而 run 碎片数决定它会�会把一次 600B 整�写退化�几百次带 PRAM_AVAIL 轮询的�写——这两个数今天没有实测��,所以先�上仪表�决定���。实现:comm/ethercat_diff_stats.{h,cpp},��在 PDI_Isr 里与上一份已�布镜�� u32 粒度比对,按 1s 窗�(DWT 周期,� cpu_load 手法)滚动,getter �回报"上一个完整窗�"以�主站读到�累积值;�为 cycles / changedMax / runsTotal / runsMax / 段1..段5 �自�化字节 / 比对自身 DWT 开销。�对象字典 0x2000 sub13..23 上报(新增 11 个�读 UINT32)。**�加 SDO �索引�PDO 长度与映射�字节�动 ⇒ 无需�生� SII�无需断电冷�动**,�与主站独立�级。整模�由 CONFIG_ECAT_DIFF_STATS 门控,置 0 �与 1.6.16 等价;挂接点统一 [DIFF-STATS] 标记,删除=删 2 文件+删标记行。已知副作用:开�时 sub9/sub10 写侧耗时�比对开销,�能直接与旧基线对�,真实写侧基准须置 0 �测。 */
/* 1.6.16:修段 1/段 3 快照 drop 的计数�径(纯判�修�,PDO 布局�字节�动)。病�:`onCanFrame` 里判的是 `if (s_rxValid) ++s_dropCount`,而 s_rxValid 置��**永�清零**(�有 flags bit0 清统计会归零计数,�动 valid),于是开机收到第一帧之�**�一帧都算一次丢弃**,drop ≡ rx−1 (mod 2^16)。实测 period=1ms�CAN 侧 6000 帧/s 时 drop �样 6000/s,与 rx_count �� 1:1(Δ 完全相等),(rx−1) mod 65536 与上报值精确��。�果:主站 `/api/motor/state` 的 bridge.drop 被 transport-ecat.js 当状�� **ERR** 显示�且它是桥全局值三通��数 → 网页"ERR 多得离谱"其实�是收帧速率,零��度�纯�仪表;calibrate_ecat_metrics 一类拿 drop �分�分�的判��得 100%。本版加 s_rxPublished(写侧置 false / buildInput 段 1 �布分支置 true,与段 4 的 s_rxWinSeen �一套"未被任何一��走�算丢"约定),判�改 `s_rxValid && !s_rxPublished`;计数�在 xrxSeq 奇数窗�内,消费者判奇会跳过段 1/段 3 故�会并�置�,读+自增对消费者等效原�。flags bit0 清统计时一并置 published=true,�得清零�的第一帧被误计。s_rxValid 语义��("有过接收",flags bit0 与 backlog �通�都�赖它常驻),故 ecat.html 短快照�daemon 的 xrx 分派�ESP32 侧 0xC1 全部零改动。**修完�会归零**:段 1/段 3 是�槽����能�布 1 帧,6000 帧/s @1ms 下�约 5000/s(≈rx 的 0.83),这个数自此真正的�义是"快照槽�跟�上收帧速率",�是总线错误;真·CAN 错误�看 ESP32 `/can/feedback` 的 err/TEC/REC,真·入窗丢帧看段 4 的 OVF(362) 与 seq 空洞。 */
/* 1.6.15:批�接收改**定长覆盖窗�**,输入 PDO 496→600(75×64bit)。动机:1.6.1 瘦身�批�区是"环形队列 + �长紧排排空",容� 3 帧/�(=3000 帧/s),现场 4000 帧/s 下���丢 1 帧(can_cadence lost=512/drain FAIL);更糟的是排空赤字把队列长期压在饱和�,而 1.6.9~1.6.13 追的错标/拼接竞�正是饱和�喂出�的(判满用的 tail 一旦滞�,生产者就写进消费者刚读过的槽)。本版按用户�定改�覆盖�:生产者(onCanFrame)按自己游标写�绕圈覆盖,消费者(fillRxWindow)��整��值 —— 没有 tail/判满/pending,那个滞��手的载体直接�存在。定长(5×44B@364)让主站�必推游标,1.6.10 那类边界越界写诊断区的错在结构上��能�犯。**�槽必须带 u32 seqlock**:buildInput 跑在 PDI_Isr(preempt=0) 会抢�主循环写侧,没有它主站能拿到"新 ID + 旧数�"拼出的�帧(�是"差一个周期",是现实中�存在但看起��法的帧,与刚修的拼接�形)。seq 兼作主站去�与丢帧判�(空洞数=被覆盖未�的帧),与从站侧新加的 OVF("未�先覆"次数) 互为对账仪表。语义�更:批�区从"���丢的事件�"��"最近 N 帧窗�"——�一帧会连续几���出现,低频一次性帧(版本/节点应答)在��下�能被�默覆盖;主站 daemon 层按 seq 去��归一�事件�,故 motor.js/ecat.html/can_cadence.py 判�����端零改动。容�:5 槽=5000 帧/s@1ms,对 4000 帧/s 留 25% 余�(STATUS 自 1.6.9 起�入窗,实际入窗�是 CONFIG �级);覆盖窗�**�创造容�**,槽数 < 帧/� �会�默丢帧。段1..3(0..359) �字节�动,段5 诊断区 484→584。三端�步:ethercat_pdo_size.h / bridge.h+cpp / dc_app.c(62→75) / gen_eeprom.py / DCU_ESI.xml / ecat_native.c decode_input(新增 600..639 分支,��自适应 496 与 640)。必须�烧固件 + 烧 EEPROM + 断电冷�动,�烧一边 SAFEOP 报 AL 0x001A/0x0011。 */
/* 1.6.14:CAN-FD 数�段 2Mbps→5Mbps(CONFIG_CAN_DATA_PRESCALER 9→3�CONFIG_CAN_DATA_BS1 3TQ→4TQ ⇒ 90MHz/(3×6TQ),采样点 80%→83.3%;TDC filter/offset 4→12 tq 维�约 400ns,因 TDCF/TDCO 以数�段 tq 为��)。动机:电机 STATUS48B+CONFIG24B×2 节点@1kHz 加 1kHz 下行 CMD,2Mbps 下负载约 120%(实测总线帧数被钉在 ~4993 帧/s),高频下�时 RX FIFO 拥塞。仲�段� 1Mbps ��。�套:电机 APP 与 BOOT 的 data_prescaler �步改 2(60MHz/(2×6TQ)=5Mbps)——FD 数�段速率必须全总线一致,节点 TX 都带 fd_brs。 */
/* 1.6.13:根治 —— 1.6.12 的 PDI 入�预抽实测(60s 正弦指令负载,链路活性已断言):overfull 50/s→0 但指纹�规� ~92/s�torn 在无溢出阶段 1-7/s �续涨 —— 判决:溢出�是共犯之一,**357µs 的 PDI 阻塞窗�本身**�是病��体(窗�内 FIFO 满溢�窗� + 被冻结的�帧路径留���赖溢出的�帧路径,MI/MP �抓到少数)。本版釜底抽薪:CAN RX ISR �到 preempt=0(CONFIG_IRQ_PREEMPT_CAN_RX,与 SYNC0 �级互�抢�)—— RX ��被 PDI/SYNC1/SCHED 压�,FIFO 深度 ≤2�溢出与冻结�时消失,receiveFrameChecked 的多字�数自此真正原�;TX ISR 留在 2(PDI 上下文有 TX �交,� 0 会引入�交/释放竞争)。代价:SYNC0/PDI �� µs 级延迟(RX ISR ~3µs/帧×8kHz≈2.4% �空),相对 1ms 周期是零头。PDI 预抽 pumpAllChannels �之撤除(失去�义且引入 PDI/ISR �消费者风险)。�留 1.6.12 的诚实管�(校验�计�丢)与诊断布局(overfull@490�relto@492�seq u16@488)作为验�仪表:修好 = reject/torn/overfull � 0。 */
/* 1.6.12:�头修� —— 用户�定"过滤是�羞布,修采集�头,�端�许诚实�馈"。1.6.11 上线�实测(repro_cmdload 正弦负载)reject@494 �以 ~384/s 增长:RFOD=DISABLE 挡��溢出�窗�(本 IP 满溢时 DISCARD 语义存疑,或�帧�生在满�下的读并�路径),内容校验丢弃�是把�数�拦在计数�。本版两处:(1) **�头**:CanFd::pumpAllChannels() 挂进 onEscIrqExti 的 PDI_Isr 入� —— 在 buildInput 357µs 长阻塞(CAN ISR preempt=2 全程被压)开始之�把三路 FIFO 抽干,阻塞窗�起点深度=0�窗�内到达 ≤2-3 帧,3 深度 FIFO 从"必然打满"��"必然�满";preempt=1 + SYNC0 已关的上下文��与 CAN ISR �帧互斥,与主循环批�队列生产零交集。(2) **诚实管�**:内容校验改为�计�丢(reject@494 � 0 = �头修好;还在涨 = �头未愈且页�如实显示),诊断区加挂判决性测�:seq 488 拆 u16�overfull(RFO0)@490�release_timeout@492(原�入计数�,� 0 让出)。reject 与 overfull 是��涨 = "残余拼接是���自溢出"的判决。 */
/* 1.6.11:根治残余拼接 —— 1.6.10 的 settle 延迟实测无效(正弦指令负载下拼接率��),节点指纹实验(两电机�相正弦+速度� 200/150 打指纹)实锤拼接是**跨节点**的:node3 的 STATUS 帧头 24B = node2 的完整 CONFIG(速度指纹 200.0�角度 1142° �属 node2 轨迹),且 CONFIG↔CONFIG �型拼接结构校验抓�到(两边都�法)—— 电机 TX ��能�出别的电机的字节 ⇒ 混�点在 GD32 RX 侧。真根因:can_init 的 rec_fifo_overwrite=ENABLE —— FIFO 满时覆盖最旧邮箱,而最旧邮箱正是 CPU 正在读的输出窗�;buildInput 在 PDI_Isr � ~357µs 压� CAN ISR,�指令时总线 ~8kHz�阻塞窗�内 ~3 帧打满 FIFO 深度 → 覆盖进行中被读走 = 拼接(MI/MP 回读挡��:MI 是覆盖�列最�更新的一环;settle 延迟也挡��:�时间竞�而是溢出策略)。本版:(1) rec_fifo_overwrite=DISABLE —— 溢出丢新帧置 RFO0(rx_fifo_overfull_count 已统计),周期�丢一�下��补,与软件队列"满丢新�覆旧"�则;(2) 撤 settle 延迟(无效且白烧 CPU);(3) �留 1.6.10 的��循环删除与边界修正,并新增入队�内容校验(STATUS 温度/电压�程+帧间斜率�CONFIG 未使用区/模�)丢结构矛盾帧�计数段5@494 —— 作为溢出之外的纵深防御(实测能挡跨型拼接,挡���型 CONFIG↔CONFIG,故必须�� (1))。验�:正弦指令负载 60s,指纹�规 40→0。 */
/* 1.6.10:根治拼接帧 —— 1.6.9 的"队列覆写"定性被自测推翻为**次�**:段 2 电机槽(MotorState 路径,与批�环形队列完全独立)出现�签�拼接(node2 槽头 = node3 CONFIG �缀 03c12200,解出 mcuTemp=-161.25),且 rx_torn/pdo_reentry 自�动� 0 —— 两�独立下游�时中招 ⇒ 混�点在共�上游 CAN RX ISR;校验��报 ⇒ 拼接�生在 MI/MP 判�盲区。真根因=1.6.7 的"校验失败原地��":RFIFOMDATA0 读指针�读自动�进且�因�帧��,attempt1 失败� attempt2 从错�指针开读,MI/MP 比对的是窗�身份�管��指针错�,于是"校验通过�内容为上一帧尾巴拼下一帧开头"。实测(自测脚本 scripts/selftest_torn.py,按�去�+��度自�):空闲 0%(35k �),电机活跃时 9.155%(56835/620838,STATUS 错标为 CONFIG ��对多数)。本版:(1) 去掉��循环,校验失败� release+丢帧+rx_torn 计数(1.6.6 语义,STATUS/CONFIG � 1kHz 周期�,丢一�下��补);(2) fillRxBatch 边界改判段 5 起点 ECAT_DIAG_OFF_LOAD(484) 而� PDO 全长(496)——潜�越界写诊断区,当�容�下触�到,容�放宽�咬人;(3) releaseFifo0 自清�补 settle 延迟 —— 烧(1)(2)�实测活跃��下�残余 0.11% 跨节点拼接(node2 槽头=node3 CONFIG �缀,且 rx_torn 已正常计数 146,�明窗��帧路径已被挡�),定性为 RFD 自清�代表 dequeue �手完��下一帧 MDATA 还在�字��(MI/MP 先�数�区��),while 连排的第二次读紧跟 release �拿到陈旧头+新鲜尾,MI/MP 回读对这�陈旧全盲;空闲� ISR �帧故 0%�活跃连排�中招,与实测分布��。 */
/* 1.6.9:拿到实�并定�真根因 —— 给主站加了�� raw+parsed 的�读诊断路由(/api/motor/rxraw)�抓到:**�帧在原始 PDO 字节里就已�是错的**(如 off=72 处�目声称 id=0x20101/dlc=24,数��是�法 STATUS:mcuTemp=35.92 motTemp=33.55 pwrV=23.972),而主站 parsed 与 raw �字节一致 —— �在从站写入侧,与主站解�无关。根因:批�接收环形队列长期饱和下,生产者覆写了消费者正在读的槽。实测��:CAN 侧约 4000 帧/s 而批�区��最多 3 �(=3000 帧/s),净积压使 pending � 8/12�lost 几乎��都有�count ��满 3;消费者 fillRxBatch �攒到循环末��布 s_rxRingTail,生产者判满用的正是这个滞�值,于是把新帧写进消费者刚读过但未�布的槽,拷进 PDO 的 id/dlc/data ��自��帧(错标率 1.77%)。两处修�:(1) onCanFrame 入队**过滤 STATUS 帧** —— 完整 STATUS 本就由段 2 按槽上报,主站 drainRx() 拿到 STATUS 也直接 continue 丢掉,让它�队列纯属��,正是它把队列压爆;(2) fillRxBatch 改为整�拷�到局部副本�用,并���布 tail,使生产者判满始终基于真实进度。
1.6.8:曾判定为"输入 PDO 填充跨上下文�入"并加了�缓冲�布 + �入计数(段5@492)。**该�说事�被�伪**:SM-Synchron 稳�下 MainLoop 分支的门控 (!bEscIntEnabled||!bEcatFirstOutputsReceived)&&!bDcSyncActive �为��执行,Sync0 因 free-run 无 DC 脉冲�触�,Sync1 与 PDI_Isr �为 preempt=1 �能抢� —— buildInput 实际�在 PDI_Isr �上下文跑。�缓冲�布本身是正确加固,予以�留。
1.6.7:接� 1.6.6 治 CAN �帧拼接。1.6.6 的校验�读 MP 的 TS/DLENC,TS 是 16 �计数器�相邻帧�能�值,DLENC 更常�为 48,于是"ID 报 STATUS�数��是 CONFIG 帧"这�错�从校验�了过去。本版三处改动:(1) 回读校验�加上 MI(完整 ID/帧格�),ID �了就一定是�了帧,没有巧�空间;(2) 校验失败改为原地��最多 3 次而�是直接丢弃 —— 失败时窗�里躺�的正是完整的新帧,�读一次通常就能拿全;(3) 整段�数放进 BASEPRI 临界区,挡� preempt>=1 的 EtherCAT 中断。注:事�(1.6.8)�明本版方��是主因,但这三�本身�是正确的加固,予以�留。
1.6.6:根治 ECAT/CAN ��乱� —— �帧��用厂商库 can_message_receive。一� 48B CANFD 报文�对�一地� CAN_RFIFOMDATA0 连读 12 次��完,整个过程�原�;而厂商库置 RFD0 �既�等硬件自清也�校验,�数过程会横跨 dequeue�读出两帧拼接。现改为本地 receiveFrameChecked():�数���读一次 MP,比对 TS[31:16] 与 DLENC,�一致�判定邮箱窗�被�帧,整帧丢弃并计入 Diag::rx_torn_count(�输入 PDO 段 5 �移 486 原�留�上报,PDO 长度���EEPROM 无需�刷);releaseFifo0() 置��轮询等自清,且改用直接赋值 —— 原�的 |= 是读改写,会把期间硬件置起的 RFF0/RFO0(rc_w1) 顺手清掉,FIFO 溢出��就是这么丢的。�修 ISR 里 frame.id/stats.last_rx_id �自三路共享的 s_diag.last_rx_id(而 data �自局部 rx_message,会串出"A 通� id � B 通�数�"),一律改用本帧局部 rxId。
1.6.5:段 2 电机槽补 seqlock(MotorSlot.wr_seq)。buildInput 跑在 PDI_Isr,会抢�主循环的 MotorState::onCanFrame,而那里是"先 seq=++global_seq�� memcpy(slot.data,48)"——抢��在 memcpy 中途就会�布"新 seq + �新�旧 48B",主站�按 seq 判新鲜度分辨�出,混�边界若�在 float 的符�/指数字节(B16/B20/B24)就是网页上的��离谱值。现消费者� wr_seq 为奇数�跳过本槽(该 56B 留零,主站按 node_id==0 跳过�下�自愈),win_count 改报窗�跨度而�填充数以��按 i 定�。CONFIG 路径�套�:它�写 config_* 与�等的 node_id/channel,buildInput ��读这些。槽布局�� ⇒ 输入 PDO � 496B,EEPROM/SII 无需�刷。
1.6.4:电机槽 40B→56B(8B 头 + v0.74 的 48B STATUS),��槽数 7→5(段 2 的 280B 正好 5×56)。修掉两件事:buildInput 按 48B 拷�时溢出 16B 盖�段 3 长帧快照;主站侧 STATUS 被截到 32B 使 InitStatus(B32-35) 永远拿�到�网页把"无数�"显示� NORMAL。槽数×槽长�� ⇒ 输入 PDO � 496B,EEPROM/SII 无需�刷;主站需�步 MOT_SLOT_SIZE / MOT_SLOTS_PER_PDO / MOT_STATUS_DLC。
1.6.3:processOutput ��从 out 回读 WIN@8——1.6.0 起 out �在 seq 沿那�新鲜,无沿周期里它是上一次全�读的残值,于是��用开机首�的 0 盖掉 peekOutput 刚�到的新值,电机状�扫�窗�被钉死在基� 0,槽� 7..26(node3 �以上)永�上报,�有主站真�一帧���。WIN 的��刷新改由 peekOutput 独�。判�:��任何帧,从站回显 win_base 也应等于主站请求值。 */
2026-07-15 02:49:37 +08:00
constexpr uint32_t APP_FW_VERSION_BUILD = 0U;
/** @brief USART0 默认波特率。 */
constexpr uint32_t CONFIG_USART0_BAUDRATE = 3000000U; /* 3M:诊断打�高速输出,printf 阻塞����拖慢 relay */
/** @brief ADC 外设实例。 */
constexpr uint32_t CONFIG_ADC_INSTANCE = ADC0;
/** @brief ADC 外部 VIN 检测通��(原�图:PA6 -> VIN_ADC)。 */
constexpr uint32_t CONFIG_ADC_VIN_CHANNEL = ADC_CHANNEL_6;
/** @brief ADC 内部温度通��。 */
constexpr uint32_t CONFIG_ADC_TEMP_CHANNEL = ADC_CHANNEL_16;
/** @brief ADC 内部�考电压通��。 */
constexpr uint32_t CONFIG_ADC_VREFINT_CHANNEL = ADC_CHANNEL_17;
/** @brief ADC 满�程计数值。 */
constexpr float CONFIG_ADC_MAX_VALUE = 4095.0f;
/** @brief 内部�考电压典型值,�� V。 */
constexpr float CONFIG_ADC_VREFINT_TYPICAL_VOLTAGE = 1.20f;
/** @brief 温度传感器 25℃ 对应电压,�� V。 */
constexpr float CONFIG_ADC_TEMP_V25 = 1.43f;
/** @brief 温度传感器平�斜率,�� V/℃。 */
constexpr float CONFIG_ADC_TEMP_AVG_SLOPE = 0.0043f;
/** @brief ADC 上电�稳定等待时间,�� ms。 */
constexpr uint32_t CONFIG_ADC_STABILIZE_DELAY_MS = 2U;
/** @brief ADC 中值滤波窗�长度(最终样本数为该值 + 1)。 */
constexpr uint32_t CONFIG_ADC_FILTER_SIZE = 6U;
/** @brief CAN 状�报文标准帧 ID。 */
constexpr uint32_t CONFIG_CAN_STATUS_ID = 0x321U;
/** @brief ESP32 请求 DCU_APP 进入 Bootloader OTA 窗�的扩展帧 ID(备用 CAN 入�)。 */
constexpr uint32_t CONFIG_CAN_OTA_BOOT_REQ_ID = 0x00000320U;
/** @brief 进入 Bootloader OTA 请求魔术字节 0~3。 */
constexpr uint8_t CONFIG_CAN_OTA_BOOT_MAGIC0 = 0x4FU;
constexpr uint8_t CONFIG_CAN_OTA_BOOT_MAGIC1 = 0x54U;
constexpr uint8_t CONFIG_CAN_OTA_BOOT_MAGIC2 = 0x41U;
constexpr uint8_t CONFIG_CAN_OTA_BOOT_MAGIC3 = 0x21U;
/** @brief ESP32 在 APP �行期通过 SPI ��的进入 Bootloader OTA 命令。 */
constexpr uint8_t CONFIG_SPI_APP_OTA_BOOT_CMD = 0xB0U;
/** @brief SPI 进入 Bootloader OTA 命令确认状�:�功。 */
constexpr uint8_t CONFIG_SPI_APP_OTA_BOOT_ACK_OK = 0xB5U;
/** @brief SPI DATA_READY 输出端�(PB6 → ESP32;v2:低=有待读,高=空闲)。 */
constexpr uint32_t CONFIG_SPI_READY_PORT = GPIOB;
/** @brief SPI DATA_READY 输出引脚。 */
constexpr uint32_t CONFIG_SPI_READY_PIN = GPIO_PIN_6;
/** @brief APP SPI DMA/软件缓冲(字节)。业务 payload 上� = 本值 − V2_OVERHEAD(20)。 */
constexpr uint16_t CONFIG_SPI_TX_BUF_SIZE = 256U;
constexpr uint16_t CONFIG_SPI_RX_BUF_SIZE = 256U;
constexpr uint16_t CONFIG_SPI_LOG_BUF_SIZE = 256U;
/** @brief 主动按铃超时:ESP32 未在该时间内�读则 cancelRing(ms)。 */
constexpr uint32_t CONFIG_SPI_RING_TIMEOUT_MS = 50U;
/** @brief SPI 命令 handler 注册表容�。
*
* 当� init 注册 12 个:boot_request / ota_wait / ping / init_status / can_bridge /
* can_burst / can_feedback / id_snapshot / adc_feedback / motor_state / motor_ota / exmc_diag。
* registerHandler 表满时�默丢弃,motor_ota 曾因容�=8 被丢弃(0xD0 回 0xEE)。
* � 14 留余�;新增 handler 时须确认本容�足够。 */
constexpr uint8_t CONFIG_SPI_MAX_COMMANDS = 14U;
/** @brief �通���� ID 快照表最大�目数(与 ESP32 CANFD_ID_SNAPSHOT_MAX 对�)。 */
constexpr uint8_t CONFIG_CANFD_ID_SNAPSHOT_MAX = 16U;
/** @brief 快照�目最大数�字节数(CAN FD DLC 上�)。 */
constexpr uint8_t CONFIG_CANFD_ID_SNAPSHOT_DATA_MAX = 64U;
/** @brief ESP32 硬件��控制端�(PC9 接 ESP32 EN/RESET,对应原�图 WLESS_EN)。
* @note ��� PC9 浮空,ESP_CHIP_EN 会被 C41/U11 拉低;BOOT 也必须主动拉高。 */
constexpr uint32_t CONFIG_ESP32_RESET_PORT = GPIOC;
/** @brief ESP32 硬件��控制引脚。 */
constexpr uint32_t CONFIG_ESP32_RESET_PIN = GPIO_PIN_9;
/* ============================================================
* CAN-FD �时��置(三路 CAN0/CAN1/CAN2 共用)
* 时钟链: SYSCLK=180MHz → AHB=/1=180MHz(RCU_MODIFY_UP 强制)
* → APB1=/2=90MHz (CAN_CLK)
*
* 仲�段目标: 1.0 Mbps = 90MHz / (9 × 10 TQ)
* 数�段目标: 5.0 Mbps = 90MHz / (3 × 6 TQ),采样点 83.3%
*
* @note RCU_MODIFY_UP �在 system_clock_180m_hxtal() 末尾将 AHB 从 /4
* 临时切到 /1,这是 GigaDevice 官方行为,��删除。
* 最终 AHB=180MHz, APB1=90MHz, APB2=180MHz, TIMER2_CLK=180MHz。
* ============================================================ */
/** @brief 仲�段预分频值。 */
constexpr uint32_t CONFIG_CAN_NOMINAL_PRESCALER = 9U;
/** @brief 仲�段��步跳跃宽度。
* 1TQ→2TQ:总线空闲期��续 STUFF 接收错误(REC 冲 120+),OTA 数�帧被错误帧
* 击�触�应用层��;放宽接收��步容差(�改波特率/采样点,上�� BS2=2TQ �制)。 */
constexpr uint32_t CONFIG_CAN_NOMINAL_SJW = CAN_BT_SJW_2TQ;
/** @brief 仲�段时间段 1(PHSEG1)。 */
constexpr uint32_t CONFIG_CAN_NOMINAL_BS1 = CAN_BT_BS1_7TQ;
/** @brief 仲�段时间段 2(PHSEG2)。 */
constexpr uint32_t CONFIG_CAN_NOMINAL_BS2 = CAN_BT_BS2_2TQ;
/** @brief 数�段预分频值。
* CAN_DBT 的 DBAUDPSC[9:0] 独立于仲�段 BAUDPSC,数�段�自�分频。 */
constexpr uint32_t CONFIG_CAN_DATA_PRESCALER = 3U;
/** @brief 数�段��步跳跃宽度。上�� BS2=1TQ 约�,��放宽。 */
constexpr uint32_t CONFIG_CAN_DATA_SJW = CAN_BT_SJW_1TQ;
/** @brief 数�段时间段 1。4TQ + 1TQ(�步) + 1TQ(BS2) = 6TQ → 5 Mbps。 */
constexpr uint32_t CONFIG_CAN_DATA_BS1 = CAN_BT_BS1_4TQ;
/** @brief 数�段时间段 2。 */
constexpr uint32_t CONFIG_CAN_DATA_BS2 = CAN_BT_BS2_1TQ;
2026-07-20 02:36:56 +08:00
/* BRS / �动探测 / 通�使能:�文件顶部「功能开关� */
/** @brief CAN 接收过滤器目标标准帧 ID。 */
constexpr uint32_t CONFIG_CAN_RX_FILTER_ID = CONFIG_CAN_STATUS_ID;
/** @brief CAN 接收过滤器标准帧掩�;0x7FF 表示精确匹� 11-bit ID。 */
constexpr uint32_t CONFIG_CAN_RX_FILTER_MASK = 0x7FFU;
/** @brief �次 CAN 接收解�任务最多消费的软件队列帧数。 */
constexpr uint32_t CONFIG_CAN_RX_PARSE_BUDGET = 48U;
/** @brief 用户调度定时器实例。 */
constexpr uint32_t CONFIG_SCHED_TIMER_INSTANCE = TIMER2;
/** @brief 用户调度定时器中断�。 */
constexpr IRQn_Type CONFIG_SCHED_TIMER_IRQn = TIMER2_IRQn;
/** @brief 用户调度定时器时钟使能��数。 */
constexpr rcu_periph_enum CONFIG_SCHED_TIMER_RCU = RCU_TIMER2;
/** @brief 用户调度定时器预分频值。
* 时钟链: SYSCLK=180MHz → AHB=/1=180MHz(RCU_MODIFY_UP 强制) → APB1=/2=90MHz → TIMER2_CLK=APB1×2=180MHz。
* �� 100us tick: (PSC+1)×(PER+1)=18000 → 180×100 → PSC=179, PER=99。
*/
constexpr uint32_t CONFIG_SCHED_TIMER_PRESCALER = 179U;
/** @brief 用户调度定时器周期�装值。 */
constexpr uint32_t CONFIG_SCHED_TIMER_PERIOD = 99U;
/** @brief 用户调度定时器 NVIC 抢�优先级。 */
constexpr uint32_t CONFIG_SCHED_TIMER_IRQ_PREEMPT_PRIO = 1U;
/** @brief 用户调度定时器 NVIC �优先级。 */
constexpr uint32_t CONFIG_SCHED_TIMER_IRQ_SUB_PRIO = 0U;
/** @brief 调度基准节�:ADC VREF 采样周期,�� 100us tick。 */
constexpr uint32_t CONFIG_TASK_PERIOD_VREF_SAMPLE_TICKS = 5U;
/** @brief 调度基准节�:ADC 温度采样/处�周期,�� 100us tick。 */
constexpr uint32_t CONFIG_TASK_PERIOD_TEMP_SAMPLE_TICKS = 10U;
/** @brief 调度基准节�:10ms 计数周期,�� 100us tick。 */
constexpr uint32_t CONFIG_TASK_PERIOD_CAN_RX_TICKS = 100U;
/** @brief 调度基准节�:CAN 周期��任务周期,100 tick = 10ms。
* @note 原 1ms/帧过密,改为 10ms/帧以�低 CAN 总线负载与 GD32 邮箱压力。 */
constexpr uint32_t CONFIG_TASK_PERIOD_CAN_TX_TICKS = 100U;
/** @brief 调度基准节�:心跳周期,�� 100us tick。 */
constexpr uint32_t CONFIG_TASK_PERIOD_HEARTBEAT_TICKS = 10000U;
/** @brief 调度基准节�:多电机状� 0xC5 主动推��动周期,2500 tick = 250ms。
* @note 独立于 1Hz heartbeat,��连带加速 OTA Boot 轮询 / CAN �馈快照刷新 /
* ADC 推� / checkOffline 离线判定(3s 阈值)。�端 age 应稳定在 ~120ms。 */
constexpr uint32_t CONFIG_TASK_PERIOD_MOTOR_STATE_TICKS = 2500U;
/** @brief 调度基准节�:APP 状�/诊断打�周期,50000 tick = 5s。 */
constexpr uint32_t CONFIG_TASK_PERIOD_DIAG_PRINT_TICKS = 50000U;
2026-07-20 02:36:56 +08:00
/* UART/SPI 调试开关:�文件顶部「功能开关� */
/** @brief FWDGT 独立看门狗预分频值。 */
constexpr uint32_t CONFIG_FWDGT_PSC = FWDGT_PSC_DIV64;
/** @brief FWDGT 独立看门狗�载值。
* è¶…æ—¶ = (64 / 40000) * (RLD + 1) = 1.6ms * (RLD+1)
* RLD=1874 → 1.6ms * 1875 ≈ 3.0s
*/
constexpr uint32_t CONFIG_FWDGT_RLD = 1874U;
/** @brief FWDGT 超时近似值(毫秒),仅供文档�考。 */
constexpr uint32_t CONFIG_FWDGT_TIMEOUT_MS = 3000U;
/* ============================================================
* BOOT 版本在 OTA �置页中的 Flash 存储地�
* 与 BOOT 工程 sys/config.h ��一致
* ============================================================ */
constexpr uint32_t CONFIG_OTA_CONFIG_PAGE_ADDR = 0x0807E000U;
constexpr uint32_t CONFIG_BOOT_VERSION_ADDR0 = CONFIG_OTA_CONFIG_PAGE_ADDR + 0x18U;
constexpr uint32_t CONFIG_BOOT_VERSION_ADDR1 = CONFIG_OTA_CONFIG_PAGE_ADDR + 0x1CU;
constexpr uint32_t CONFIG_OTA_CONFIG_WORD_COUNT = 8U;
/* ============================================================
* NVIC 优先级分组与中断优先级分层
* (EtherCAT 实时架构改造:PRE4_SUB0,4 �抢�优先级)
* ============================================================ */
constexpr uint32_t CONFIG_NVIC_PRIORITY_GROUP = NVIC_PRIGROUP_PRE4_SUB0;
constexpr uint32_t CONFIG_IRQ_PREEMPT_ETHERCAT_SYNC = 0U;
constexpr uint32_t CONFIG_IRQ_PREEMPT_SCHED = 1U;
constexpr uint32_t CONFIG_IRQ_PREEMPT_CAN = 2U;
/* 1.6.13:CAN RX �到 preempt=0(与 EtherCAT SYNC0 �级)。
* 1.6.12 的 PDI 入�预抽实测:溢出确实大�(overfull 50/s→0)但拼接照旧
* (指纹�规 ~92/s,且 torn 在无溢出阶段� 1-7/s 在涨)—— 357µs 的 PDI
* 阻塞窗�本身就是病�:窗�内 FIFO 满溢�窗�的�时,被冻结的�帧路径
* 还留���赖溢出的�帧路径(MI/MP �抓到少数)。preempt=0 一并�断:
* RX ISR ��被 PDI/SYNC1/SCHED 压�(FIFO 深度 ≤2,溢出与冻结�时消失),
* 且与 SYNC0 �级互�抢� —— receiveFrameChecked 的多字�数自此真正原�。
* TX ISR 留在 2:PDI 上下文有 TX �交,�到 0 会引入�交/释放竞争。
* 代价:SYNC0/PDI �� µs 级延迟(RX ISR ~3µs/帧 × ~8kHz ≈ 2.4% �空),
* 相对 1ms 周期与 165µs 总线时间是零头。 */
constexpr uint32_t CONFIG_IRQ_PREEMPT_CAN_RX = 0U;
constexpr uint32_t CONFIG_IRQ_PREEMPT_ETHERCAT_ESC = 1U;
constexpr uint32_t CONFIG_IRQ_PREEMPT_ETHERCAT_SYNC1 = 1U;
constexpr uint32_t CONFIG_IRQ_PREEMPT_SPI_USART = 3U;
/* ============================================================
2026-07-20 02:36:56 +08:00
* EtherCAT LAN9252 �置(开关�顶部 CONFIG_MAIN_LOOP_WFI / ETHERCAT_SYNC_MODE)
* ============================================================ */
/* 1.5.27:CONFIG_ETHERCAT_ISR_MAX_US_THRESHOLD / CONFIG_ESC_PDI_INPUT_OFFSET /
* CONFIG_ESC_PDI_OUTPUT_OFFSET / CONFIG_ETHERCAT_PDO_SIZE �死代� EthercatSlave::
* periodicIsr 三缓冲一并删除;PDO 交�由 SSC(dc_app.c APPL_*, 4×64bit=32B) 完�。 */
/* ---------- EXMC 引脚�置(GD32E508 EXMC 标准映射) ---------- */
constexpr uint32_t CONFIG_ESC_EXMC_BASE_ADDR = 0x60000000U;
/* Region0 = NE0 @ 0x60000000(GD32E50x 用户手册 Rev2.2 图25-3: Bank0 四 Region � 64MB,
* HADDR[27:26]=00 选中 NE0)。LAN9252 CS 接 PD7=EXMC_NE0(数�手册 GD32E508xx
* Datasheet Rev1.8 LQFP100 引脚表: PD7 Alternate2 = EXMC_NE0/EXMC_NCE1)。
* 注�: GD32E508Vx (LQFP100) �装�引出 NE0 一个 NOR/SRAM 片选引脚,
* NE1~NE3 仅在 LQFP144 (Zx) 上存在;因此必须使用 Region0(SNCTL0),�能用 Region1。 */
#ifndef EXMC_BANK0_NORSRAM_REGION0
#define EXMC_BANK0_NORSRAM_REGION0 ((uint32_t)0x00000000U)
#endif
#define CONFIG_ESC_EXMC_BANK_NOR_SRAM_REGION0 EXMC_BANK0_NORSRAM_REGION0
/* ---------- ESC 中断引脚(PB0/PB1/PB2,三个独立 EXTI ��) ---------- */
/** @note �留 PA0 给未� STANDBY 模�唤醒(WKUP_PIN0)。 */
constexpr uint32_t CONFIG_ESC_SYNC0_PORT = GPIOB;
constexpr uint32_t CONFIG_ESC_SYNC0_PIN = GPIO_PIN_0;
constexpr exti_line_enum CONFIG_ESC_SYNC0_EXTI = EXTI_0;
constexpr IRQn_Type CONFIG_ESC_SYNC0_IRQn = EXTI0_IRQn;
constexpr uint32_t CONFIG_ESC_SYNC0_EXTI_SOURCE_PORT = GPIO_PORT_SOURCE_GPIOB;
constexpr uint32_t CONFIG_ESC_SYNC0_EXTI_SOURCE_PIN = GPIO_PIN_SOURCE_0;
constexpr uint32_t CONFIG_ESC_IRQ_PORT = GPIOB;
constexpr uint32_t CONFIG_ESC_IRQ_PIN = GPIO_PIN_1;
constexpr exti_line_enum CONFIG_ESC_IRQ_EXTI = EXTI_1;
constexpr IRQn_Type CONFIG_ESC_IRQ_IRQn = EXTI1_IRQn;
constexpr uint32_t CONFIG_ESC_IRQ_EXTI_SOURCE_PORT = GPIO_PORT_SOURCE_GPIOB;
constexpr uint32_t CONFIG_ESC_IRQ_EXTI_SOURCE_PIN = GPIO_PIN_SOURCE_1;
constexpr uint32_t CONFIG_ESC_SYNC1_PORT = GPIOB;
constexpr uint32_t CONFIG_ESC_SYNC1_PIN = GPIO_PIN_2;
constexpr exti_line_enum CONFIG_ESC_SYNC1_EXTI = EXTI_2;
constexpr IRQn_Type CONFIG_ESC_SYNC1_IRQn = EXTI2_IRQn;
constexpr uint32_t CONFIG_ESC_SYNC1_EXTI_SOURCE_PORT = GPIO_PORT_SOURCE_GPIOB;
constexpr uint32_t CONFIG_ESC_SYNC1_EXTI_SOURCE_PIN = GPIO_PIN_SOURCE_2;
/* ---------- ESC 硬件��(PB10 → LAN9252 Pin11 RST#,SCH 第1页 Pin47) ---------- */
/** @note 2026-08-22(1.5.25) �固件从未�始化 PB10,RST# 仅� R44 10k 上拉自然释放,
* �满足 LAN9252 手册 18.6.3 tpurstd(电�稳定→RST#释放)≥25ms�trstia(低脉宽)≥200µs。
* 由 EscLan9252::hardwareReset() 执行�控���列;释放���推挽输出高,
* GD32 自身��期间引脚回浮空,由 R44 上拉维�高,�会误�� ESC。 */
constexpr uint32_t CONFIG_ESC_NRST_PORT = GPIOB;
constexpr uint32_t CONFIG_ESC_NRST_PIN = GPIO_PIN_10;
constexpr uint32_t CONFIG_ESC_NRST_ASSERT_MS = 30U; /* 低脉宽,�时覆盖 tpurstd 25ms 与 trstia 200µs */
constexpr uint32_t CONFIG_ESC_HBI_POLL_TIMEOUT_MS = 300U; /* RST# 释放�等待 ESC �动/EEPROM 加载 */
constexpr uint32_t CONFIG_ESC_HBI_POLL_STEP_MS = 5U;
/* ---------- EXMC 数�/控制线引脚(GD32E508 标准映射) ---------- */
constexpr uint32_t CONFIG_EXMC_D0_PORT = GPIOD; constexpr uint32_t CONFIG_EXMC_D0_PIN = GPIO_PIN_14;
constexpr uint32_t CONFIG_EXMC_D1_PORT = GPIOD; constexpr uint32_t CONFIG_EXMC_D1_PIN = GPIO_PIN_15;
constexpr uint32_t CONFIG_EXMC_D2_PORT = GPIOD; constexpr uint32_t CONFIG_EXMC_D2_PIN = GPIO_PIN_0;
constexpr uint32_t CONFIG_EXMC_D3_PORT = GPIOD; constexpr uint32_t CONFIG_EXMC_D3_PIN = GPIO_PIN_1;
constexpr uint32_t CONFIG_EXMC_D4_PORT = GPIOE; constexpr uint32_t CONFIG_EXMC_D4_PIN = GPIO_PIN_7;
constexpr uint32_t CONFIG_EXMC_D5_PORT = GPIOE; constexpr uint32_t CONFIG_EXMC_D5_PIN = GPIO_PIN_8;
constexpr uint32_t CONFIG_EXMC_D6_PORT = GPIOE; constexpr uint32_t CONFIG_EXMC_D6_PIN = GPIO_PIN_9;
constexpr uint32_t CONFIG_EXMC_D7_PORT = GPIOE; constexpr uint32_t CONFIG_EXMC_D7_PIN = GPIO_PIN_10;
constexpr uint32_t CONFIG_EXMC_D8_PORT = GPIOE; constexpr uint32_t CONFIG_EXMC_D8_PIN = GPIO_PIN_11;
constexpr uint32_t CONFIG_EXMC_D9_PORT = GPIOE; constexpr uint32_t CONFIG_EXMC_D9_PIN = GPIO_PIN_12;
constexpr uint32_t CONFIG_EXMC_D10_PORT = GPIOE; constexpr uint32_t CONFIG_EXMC_D10_PIN = GPIO_PIN_13;
constexpr uint32_t CONFIG_EXMC_D11_PORT = GPIOE; constexpr uint32_t CONFIG_EXMC_D11_PIN = GPIO_PIN_14;
constexpr uint32_t CONFIG_EXMC_D12_PORT = GPIOE; constexpr uint32_t CONFIG_EXMC_D12_PIN = GPIO_PIN_15;
constexpr uint32_t CONFIG_EXMC_D13_PORT = GPIOD; constexpr uint32_t CONFIG_EXMC_D13_PIN = GPIO_PIN_8;
constexpr uint32_t CONFIG_EXMC_D14_PORT = GPIOD; constexpr uint32_t CONFIG_EXMC_D14_PIN = GPIO_PIN_9;
constexpr uint32_t CONFIG_EXMC_D15_PORT = GPIOD; constexpr uint32_t CONFIG_EXMC_D15_PIN = GPIO_PIN_10;
constexpr uint32_t CONFIG_EXMC_A16_PORT = GPIOD; constexpr uint32_t CONFIG_EXMC_A16_PIN = GPIO_PIN_11;
2026-07-20 02:36:56 +08:00
/* A17 (PD12) 用于 HBI Indexed Mode 访问 HBI_DATA_0 寄存器,已将 LAN9252 A2 (Pin 29) 接到 PD12。
* 注� LAN9252 引脚顺�:Pin25=A1, Pin26=A3, Pin27=A4, Pin28=CS, Pin29=A2。
* A2 是 IDX/DATA 选择�,必须接到 PD12 �能访问 DATA 寄存器,EEPROM 烧录功能�用。
* 注� EXMC 16-bit 模�地�左移 1 �(用户手册 p869):A16(PD11)=CPU �移 0x20000�
* A17(PD12)=CPU �移 0x40000,HBI_DATA_0 访问�移� esc_lan9252.cpp。 */
2026-07-20 02:36:56 +08:00
constexpr uint32_t CONFIG_EXMC_A17_PORT = GPIOD; constexpr uint32_t CONFIG_EXMC_A17_PIN = GPIO_PIN_12;
constexpr uint32_t CONFIG_EXMC_NOE_PORT = GPIOD; constexpr uint32_t CONFIG_EXMC_NOE_PIN = GPIO_PIN_4;
constexpr uint32_t CONFIG_EXMC_NWE_PORT = GPIOD; constexpr uint32_t CONFIG_EXMC_NWE_PIN = GPIO_PIN_5;
/* LQFP100 �有 NE0(PD7) 一个 NOR/SRAM 片选,归 Region0(0x60000000);命�沿用硬件真值 NE0。 */
constexpr uint32_t CONFIG_EXMC_NE0_PORT = GPIOD; constexpr uint32_t CONFIG_EXMC_NE0_PIN = GPIO_PIN_7;
#endif /* SYS_CONFIG_H */