feat(ethercat): 添加EtherCAT从站功能支持并优化同步机制
- 添加CAN反馈和ID快照SPI命令文件 - 移除EXMC组件依赖,精简项目配置 - 添加RAM函数段链接脚本,优化Flash等待周期抖动 - 实现EscExmcBus类,提供16位并行总线访问LAN9252 - 实现EscLan9252驱动,支持寄存器和PDI内存访问 - 实现EthercatSlave类,采用三缓冲机制进行PDO数据交换 - 实现EthercatSync同步管理,支持SYNC0/SYNC1中断和定时器模式 - 添加ISR执行时间统计和诊断功能 - 分离周期路径和事件路径通知机制,避免RMW竞态
This commit is contained in:
@@ -102,7 +102,7 @@
|
||||
#define CONFIG_TASK_PERIOD_HEARTBEAT_TICKS 10000U
|
||||
|
||||
/** @brief 周期温度/电压 UART 打印开关:0=关闭,1=开启。 */
|
||||
#define CONFIG_UART_TEMP_PERIODIC_PRINT 0
|
||||
#define CONFIG_UART_TEMP_PERIODIC_PRINT 1
|
||||
/** @brief UART 诊断打印级别:0=仅温度/电压,1=温度/电压+CAN 统计。 */
|
||||
#define CONFIG_UART_DIAG_LEVEL 1
|
||||
/** @brief SPI 命令逐帧日志开关:0=关闭,1=开启。高速 CAN 桥接时应关闭。 */
|
||||
@@ -129,7 +129,7 @@
|
||||
/** @brief APP 次版本号。 */
|
||||
#define APP_FW_VERSION_MINOR 1U
|
||||
/** @brief APP 补丁版本号。 */
|
||||
#define APP_FW_VERSION_PATCH 0U
|
||||
#define APP_FW_VERSION_PATCH 2U
|
||||
/** @brief APP 构建编号。 */
|
||||
#define APP_FW_VERSION_BUILD 0U
|
||||
|
||||
@@ -175,13 +175,24 @@
|
||||
/* ============================================================
|
||||
* EtherCAT LAN9252 使能与配置
|
||||
* ============================================================ */
|
||||
/** @brief EtherCAT 使能开关:0=禁用(未焊接 LAN9252),1=启用(焊接后)。
|
||||
* 禁用时 comm/ 全部代码不编译,初始化不调用 EtherCAT,EXMC/EXTI0-2 不配置。 */
|
||||
#define CONFIG_ETHERCAT_ENABLE 0
|
||||
|
||||
/** @brief SYNC0 同步模式:0=EXTI 外部触发(ESC 硬件同步,默认),1=TIMER0 降级(DC 未同步时)。 */
|
||||
#define CONFIG_ETHERCAT_SYNC_MODE 0
|
||||
|
||||
/** @brief periodicIsr 最大执行时间告警阈值(µs)。实测超过此值需评估减小 PDO 大小或优化 EXMC 时序。
|
||||
* 当前阈值 5µs:基于 32 字节 PDO + EXMC 6 HCLK 时序的估算上限。
|
||||
* preempt=0 ISR 阻塞窗口 ≤ 此阈值时,CAN/SPI 中断需容忍。 */
|
||||
#define CONFIG_ETHERCAT_ISR_MAX_US_THRESHOLD 5U
|
||||
|
||||
/** @brief 输入 PDO(TxPDO)在 PDI 内存区的起始偏移。
|
||||
* 对应 LAN9252 SyncManager 内存映射,需与主站 ESI 配置匹配。
|
||||
* @note 占位值,实际数值待主站配置确定后在集成阶段填入。 */
|
||||
#define CONFIG_ESC_PDI_INPUT_OFFSET 0x1000U
|
||||
|
||||
/** @brief 输出 PDO(RxPDO)在 PDI 内存区的起始偏移。
|
||||
* 对应 LAN9252 SyncManager 内存映射,需与主站 ESI 配置匹配。
|
||||
* @note 占位值,实际数值待主站配置确定后在集成阶段填入。 */
|
||||
#define CONFIG_ESC_PDI_OUTPUT_OFFSET 0x1200U
|
||||
|
||||
/* ---------- EXMC 引脚配置(GD32E508 EXMC 标准映射) ---------- */
|
||||
/** @brief EXMC NOR/SRAM Region1 基址(NE1 片选)。 */
|
||||
#define CONFIG_ESC_EXMC_BASE_ADDR 0x60000000U
|
||||
|
||||
@@ -16,10 +16,8 @@
|
||||
#include "sys/ota_boot_request.h"
|
||||
#include "sys/task_manager.h"
|
||||
#include "sys/config.h"
|
||||
#if CONFIG_ETHERCAT_ENABLE
|
||||
#include "comm/ethercat_slave.h"
|
||||
#include "comm/ethercat_sync.h"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief 执行应用级外设与调度初始化。
|
||||
@@ -30,6 +28,10 @@
|
||||
*/
|
||||
void AppInitializer::init()
|
||||
{
|
||||
/* 必须最先设置 NVIC 优先级分组:确保后续 CanFd/SPI0 等外设 init 中
|
||||
* 编码抢占/子优先级字节时使用正确的分组(PRE4_SUB0),避免在错误分组
|
||||
* 下编码后被重新解释。EtherCAT 实时架构整改 Task3。 */
|
||||
nvic_priority_group_set(CONFIG_NVIC_PRIORITY_GROUP);
|
||||
printf("[APP] init: SysTick...\r\n");
|
||||
SysTickTimer::initMs();
|
||||
printf("[APP] init: USART0...\r\n");
|
||||
@@ -47,12 +49,14 @@ void AppInitializer::init()
|
||||
TaskManager::init();
|
||||
printf("[APP] init: SchedulerTimer...\r\n");
|
||||
SchedulerTimer::init();
|
||||
#if CONFIG_ETHERCAT_ENABLE
|
||||
printf("[APP] init: EthercatSlave...\r\n");
|
||||
EthercatSlave::init();
|
||||
printf("[APP] init: EthercatSync...\r\n");
|
||||
EthercatSync::init();
|
||||
#endif
|
||||
if (EthercatSlave::isEscReady()) {
|
||||
printf("[APP] init: EthercatSync...\r\n");
|
||||
EthercatSync::init();
|
||||
} else {
|
||||
printf("[APP] init: EthercatSync SKIPPED (ESC not ready)\r\n");
|
||||
}
|
||||
printf("[APP] init: Watchdog...\r\n");
|
||||
Watchdog::init();
|
||||
printf("[APP] init: all done\r\n");
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/**
|
||||
/**
|
||||
* @file task_runner.cpp
|
||||
* @brief 任务执行器实现。
|
||||
*/
|
||||
@@ -13,9 +13,7 @@
|
||||
#include "sys/ota_boot_request.h"
|
||||
#include "sys/task_manager.h"
|
||||
#include "sys/task_runner.h"
|
||||
#if CONFIG_ETHERCAT_ENABLE
|
||||
#include "comm/ethercat_slave.h"
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
constexpr uint32_t kAdcFilterWindow = CONFIG_ADC_FILTER_SIZE + 1U;
|
||||
@@ -214,7 +212,7 @@ void TaskRunner::taskStatusLed() { /* 预留 */ }
|
||||
* @retval 无返回值。
|
||||
* @note 当前仅保留任务入口。
|
||||
*/
|
||||
void TaskRunner::taskHeartbeat() { OtaBootRequest::process(); }
|
||||
void TaskRunner::taskHeartbeat() { OtaBootRequest::process(); CanFd::refreshFeedbackSnapshot(); }
|
||||
|
||||
/**
|
||||
* @brief 输出当前温度、电压与 CAN 诊断信息。
|
||||
@@ -266,6 +264,23 @@ void TaskRunner::taskDiagnosticPrint()
|
||||
static_cast<unsigned long>(diag->tx_abort_count),
|
||||
static_cast<unsigned long>(diag->error_count));
|
||||
#endif
|
||||
|
||||
{
|
||||
if (EthercatSlave::isEscReady()) {
|
||||
uint32_t maxCyc = 0U, avgCyc = 0U, cnt = 0U;
|
||||
EthercatSlave::getIsrStats(&maxCyc, &avgCyc, &cnt);
|
||||
/* 180MHz: cycles / 180 = µs;用 *1000/180 获得毫微秒精度避免浮点 */
|
||||
const uint32_t maxUs_x1000 = maxCyc * 1000U / 180U;
|
||||
const uint32_t avgUs_x1000 = avgCyc * 1000U / 180U;
|
||||
printf("[ECAT] periodicIsr cnt=%lu max=%lu.%03luus avg=%lu.%03luus\r\n",
|
||||
(unsigned long)cnt,
|
||||
(unsigned long)(maxUs_x1000 / 1000U), (unsigned long)(maxUs_x1000 % 1000U),
|
||||
(unsigned long)(avgUs_x1000 / 1000U), (unsigned long)(avgUs_x1000 % 1000U));
|
||||
} else {
|
||||
printf("[ECAT] ESC not detected (chip id=0x%08lX), EtherCAT disabled\r\n",
|
||||
(unsigned long)EthercatSlave::getChipId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -289,11 +304,7 @@ void TaskRunner::taskSpiCmdProcess()
|
||||
*/
|
||||
void TaskRunner::taskEthercatProcess()
|
||||
{
|
||||
#if CONFIG_ETHERCAT_ENABLE
|
||||
EthercatSlave::process();
|
||||
#else
|
||||
/* 未使能 EtherCAT 时此分支不应被置位;保留空操作以避免链接错误。 */
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -328,6 +339,16 @@ void TaskRunner::runPending()
|
||||
{
|
||||
Watchdog::feed();
|
||||
|
||||
/* 周期路径( SYNC0 ISR preempt=0 )用 s_periodicSeq 序列号通知主循环,
|
||||
* 不再走 task_flags,避免 BASEPRI 临界区与 preempt=0 的 RMW 竞态。
|
||||
* 优先于 takePending() 处理,确保周期数据及时消费。
|
||||
* ESC 未焊接时 selfTest 失败,isEscReady()=false,中断未使能,
|
||||
* s_periodicSeq 不会递增,此分支自然不触发——双重守卫仅为防御性编程。 */
|
||||
if (EthercatSlave::isEscReady() && EthercatSlave::hasPendingPeriodic()) {
|
||||
EthercatSlave::markPeriodicProcessed();
|
||||
taskEthercatProcess();
|
||||
}
|
||||
|
||||
const uint32_t pending = TaskManager::takePending();
|
||||
if(pending == 0U) return;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user