Files
cbb d8b3c9767c feat(ota): 电机 OTA 支持 EtherCAT 模式(CoE 邮箱进料 + 通道独占锁)
进料口从「只有 ESP32 SPI 一条」扩成两条,中继引擎不变:主站经 CoE 0x2511 数据窗
把电机固件明文灌进 GD32 的 48KB SRAM 暂存区,再由 comm/ecat_motor_ota 泵进原有
4KB 环缓;AES 握手、CAN 单发停等、重发、0AAC 运行确认全部沿用 MotorOtaRelay。
纯网线(ESP32 WiFi 关断、/motor/ota 打不通)场合因此也能刷电机。
偏移/幂等判据仍只有一份:feedData 拆出 feedAt,两个进料口共用。

同时把审计报告 VULN-12/13/14 落地(此前只存在于文档,一行未实现):start() 第一条
语句在 PRIMASK 内原子置位 s_channel_lock_mask,EtherCAT 桥接 sendBatch/processOutput
与 SPI 0xC0/0xC7 对锁定通道回新码 0xE7=「被电机 OTA 独占」,不冒充「邮箱满」0xE4。
反向互斥一并补上:电机 OTA 活跃时 requestEnterBoot() 直接拒绝——SPI 0xB0、CoE 0x2502、
CAN 0x320 三条交接入口共用这一个判据,避免复位把刷写腰斩在 erase 之后。

完整性口径按链路分开,不互相冒充:块级 CRC32 + 整镜像 CRC32 都在从站核,且核过才
让电机进 BOOT——失败时电机仍跑旧 APP、擦除没开始,不存在「先擦再验」的变砖路径。
CRC 为 CRC-32/B(poly 0x04C11DB7,初值 0xFFFFFFFF,无反射无末尾异或),与 v2_crc32
同算法(用公开向量 123456789=0x0376E6E7 对过)。

块长取 1024 而不是 BOOT 用的 4096:SSC 的分段下载缓冲是全工程唯一的 malloc 用户,
而 APP 的 __HEAP_SIZE 只有 3KB,4096 会被从站当场回 abort 0x05040005 Out of memory
(BOOT 的堆是 16KB 才撑得住 4096——块长能抄,堆抄不了)。这是跨仓常量,须与
tools/ecat 的 MFO_CHUNK_OBJ 同步。

APP 1.7.4(RAM ZI 97156)。已实测:0x2510 参数写与一块 1016B 数据写均被从站接受
(staged=1016、phase bit4);端到端电机刷写待复测(native 块长修复后未跑完整一轮)。
2026-10-11 02:47:19 +08:00

78 lines
3.4 KiB
C
Raw Permalink 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 critical.h
* @brief 实时临界区基础设施:基于 BASEPRI 寄存器的中断屏蔽。
* @note EtherCAT 实时架构改造专用。GD32E508 为 Cortex-M4F,支持 BASEPRI 寄存器。
*
* @details 设计说明:
* - BASEPRI 阈值屏蔽抢占优先级 >= 1 的中断,仅保留 preempt=0(EtherCAT SYNC0
* 周期中断)可立即响应,保证 EtherCAT 周期抖动 <1us。
* - 取代原 PRIMASK 方案(`__disable_irq` / `__set_PRIMASK`),后者会屏蔽全部中断
* (含 preempt=0),无法满足 EtherCAT 实时性要求。
* - NVIC 优先级分组为 `NVIC_PRIGROUP_PRE4_SUB0`(4 位抢占 + 0 位子优先级),
* 因此 `__NVIC_PRIO_BITS = 4`,阈值 `(1U << (8U - 4U)) = 0x10`,即屏蔽优先级
* 数值 0x1x 及以上(对应 preempt 1..15),保留 preempt=0(优先级 0x0x)。
* - 阈值定义依赖 `__NVIC_PRIO_BITS`,该宏由 CMSIS 设备头文件提供(GD32E508 = 4)。
* - 项目使用 AC6(armclang) gnu++17,支持 GCC 语句表达式(statement expression),
* `RT_CRITICAL_SAVE()` 利用该特性返回保存的 BASEPRI 值,便于嵌套恢复。
*/
#ifndef SYS_CRITICAL_H
#define SYS_CRITICAL_H
#include "RTE_Components.h"
#include CMSIS_device_header
/** @brief BASEPRI 屏蔽阈值:屏蔽 preempt>=1,保留 preempt=0(EtherCAT SYNC0)。
* 对于 4 位抢占优先级(__NVIC_PRIO_BITS=4),该值为 0x10。 */
#define RT_CRITICAL_BASEPRI_VALUE (1U << (8U - __NVIC_PRIO_BITS))
/**
* @brief 进入临界区:屏蔽抢占优先级 >= 1 的中断。
* @note preempt=0 的 EtherCAT SYNC0 周期中断仍可立即抢占执行。
*/
#define RT_CRITICAL_ENTER() __set_BASEPRI(RT_CRITICAL_BASEPRI_VALUE)
/**
* @brief 退出临界区:恢复全部中断响应(BASEPRI 清零)。
*/
#define RT_CRITICAL_EXIT() __set_BASEPRI(0U)
/**
* @brief 保存当前 BASEPRI 并进入临界区,返回进入前的 BASEPRI 值。
* @return 进入临界区前的 BASEPRI 寄存器值(供 RT_CRITICAL_RESTORE 恢复)。
* @note 利用 GCC 语句表达式返回值,用于嵌套临界区场景:
* @code
* uint32_t save = RT_CRITICAL_SAVE();
* // ... 临界区代码 ...
* RT_CRITICAL_RESTORE(save);
* @endcode
*/
#define RT_CRITICAL_SAVE() \
({ \
uint32_t __basepri_save = __get_BASEPRI(); \
__set_BASEPRI(RT_CRITICAL_BASEPRI_VALUE); \
__basepri_save; \
})
/**
* @brief 恢复 BASEPRI 到指定值(用于 RT_CRITICAL_SAVE() 配套的嵌套恢复)。
* @param[in] val RT_CRITICAL_SAVE() 返回的 BASEPRI 值。
*/
#define RT_CRITICAL_RESTORE(val) __set_BASEPRI(val)
/**
* @brief Preempt-0 专用临界区:PRIMASK 全局屏蔽,用于与 preempt=0 中断共享的
* 单字标志(读-改-写)。
* @note 严格限定 O(1) 状态操作(置位/清位/单字赋值)。**严禁**在其中调用
* printf/LOG_*:串口入队实测 36us,TX FIFO 满时可达 123us,会把 EtherCAT
* SYNC0 抖动顶出 DC 容差。日志一律放临界区外。
*/
#define RT_CRITICAL_PREEMPT0_SAVE() \
({ \
uint32_t __primask_save = __get_PRIMASK(); \
__disable_irq(); \
__primask_save; \
})
#define RT_CRITICAL_PREEMPT0_RESTORE(val) __set_PRIMASK(val)
#endif /* SYS_CRITICAL_H */