进料口从「只有 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 块长修复后未跑完整一轮)。
78 lines
3.4 KiB
C
78 lines
3.4 KiB
C
/**
|
||
* @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 */
|