feat(cproject): 重构项目文件结构并添加EtherCAT通信支持

- 将设备相关文件重新组织为device/spi和comm分组
- 添加EtherCAT从站通信模块(esc_exmc_bus、esc_lan9252、ethercat_slave等)
- 添加EXMC外设组件支持
- 统一文件命名规范为小写

refactor(canfd): 优化CANFD模块中断安全性和配置方式

- 替换自定义临界区函数为RT_CRITICAL_SAVE/RESTORE宏
- 使用配置常量CONFIG_IRQ_PREEMPT_CAN替代硬编码值
- 简化邮箱满时的处理逻辑,移除不必要的SysTick等待
- 移除废弃的中断保存头文件引用

refactor(timer): 更新定时器配置以使用配置常量

- 修复头文件包含路径大小写问题
- 使用CONFIG_NVIC_PRIORITY_GROUP配置项替代硬编码值
This commit is contained in:
cbb
2026-06-26 00:57:59 +08:00
parent 86c0ec730b
commit 1612e326ee
32 changed files with 1378 additions and 147 deletions
+120 -4
View File
@@ -102,9 +102,13 @@
#define CONFIG_TASK_PERIOD_HEARTBEAT_TICKS 10000U
/** @brief 周期温度/电压 UART 打印开关:0=关闭,1=开启。 */
#define CONFIG_UART_TEMP_PERIODIC_PRINT 1
#define CONFIG_UART_TEMP_PERIODIC_PRINT 0
/** @brief UART 诊断打印级别:0=仅温度/电压,1=温度/电压+CAN 统计。 */
#define CONFIG_UART_DIAG_LEVEL 1
/** @brief SPI 命令逐帧日志开关:0=关闭,1=开启。高速 CAN 桥接时应关闭。 */
#define CONFIG_SPI_APP_LOG_ENABLE 0
/** @brief CAN_SEND 成功帧详细日志开关:0=关闭,1=开启。开启会显著影响高速传输。 */
#define CONFIG_SPI_CAN_SEND_VERBOSE_LOG 0
/** @brief FWDGT 独立看门狗预分频值。 */
@@ -123,11 +127,11 @@
/** @brief APP 主版本号。 */
#define APP_FW_VERSION_MAJOR 1U
/** @brief APP 次版本号。 */
#define APP_FW_VERSION_MINOR 0U
#define APP_FW_VERSION_MINOR 1U
/** @brief APP 补丁版本号。 */
#define APP_FW_VERSION_PATCH 1U
#define APP_FW_VERSION_PATCH 0U
/** @brief APP 构建编号。 */
#define APP_FW_VERSION_BUILD 2U
#define APP_FW_VERSION_BUILD 0U
/* ============================================================
* BOOT 版本在 OTA 配置页中的 Flash 存储地址
@@ -142,4 +146,116 @@
/** @brief OTA 配置页总 word 数(6 个 OTA 标志 + 2 个 BOOT 版本)。 */
#define CONFIG_OTA_CONFIG_WORD_COUNT 8U
/* ============================================================
* NVIC 优先级分组与中断优先级分层
* (EtherCAT 实时架构改造:PRE4_SUB0,4 位抢占优先级)
* ============================================================ */
/** @brief NVIC 优先级分组:4 位抢占 + 0 位子优先级。 */
#define CONFIG_NVIC_PRIORITY_GROUP NVIC_PRIGROUP_PRE4_SUB0
/** @brief EtherCAT SYNC0 周期中断抢占优先级(最高,永不被阻塞)。 */
#define CONFIG_IRQ_PREEMPT_ETHERCAT_SYNC 0U
/** @brief 调度器定时器抢占优先级。 */
#define CONFIG_IRQ_PREEMPT_SCHED 1U
/** @brief CAN RX0 抢占优先级。 */
#define CONFIG_IRQ_PREEMPT_CAN 2U
/** @brief EtherCAT ESC 事件中断抢占优先级(可被 SYNC0 抢占)。 */
#define CONFIG_IRQ_PREEMPT_ETHERCAT_ESC 1U
/** @brief EtherCAT SYNC1 备用抢占优先级。 */
#define CONFIG_IRQ_PREEMPT_ETHERCAT_SYNC1 1U
/** @brief SPI / EXTI / USART 抢占优先级(最低)。 */
#define CONFIG_IRQ_PREEMPT_SPI_USART 3U
/* ============================================================
* 主循环与实时策略
* ============================================================ */
/** @brief 主循环 WFI 开关:0=关闭(忙等,适配 EtherCAT),1=开启(省电)。 */
#define CONFIG_MAIN_LOOP_WFI_ENABLE 0
/* ============================================================
* 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
/* ---------- EXMC 引脚配置(GD32E508 EXMC 标准映射) ---------- */
/** @brief EXMC NOR/SRAM Region1 基址(NE1 片选)。 */
#define CONFIG_ESC_EXMC_BASE_ADDR 0x60000000U
/** @brief EXMC BANK 选择(BANK0 Region1)。
* @note EXMC 库未纳入编译时回退为 0U;启用 EtherCAT 时 EXMC 组件将被引入。 */
#ifdef EXMC_BANK0_NORSRAM_REGION1
#define CONFIG_ESC_EXMC_BANK_NOR_SRAM_REGION1 EXMC_BANK0_NORSRAM_REGION1
#else
#define CONFIG_ESC_EXMC_BANK_NOR_SRAM_REGION1 0U
#endif
/* ---------- ESC 中断引脚(PB0/PB1/PB2,三个独立 EXTI 向量) ---------- */
/** @note 保留 PA0 给未来 STANDBY 模式唤醒(WKUP_PIN0)。 */
#define CONFIG_ESC_SYNC0_PORT GPIOB
#define CONFIG_ESC_SYNC0_PIN GPIO_PIN_0
#define CONFIG_ESC_SYNC0_EXTI EXTI_0
#define CONFIG_ESC_SYNC0_IRQn EXTI0_IRQn
#define CONFIG_ESC_SYNC0_EXTI_SOURCE_PORT GPIO_PORT_SOURCE_GPIOB
#define CONFIG_ESC_SYNC0_EXTI_SOURCE_PIN GPIO_PIN_SOURCE_0
#define CONFIG_ESC_IRQ_PORT GPIOB
#define CONFIG_ESC_IRQ_PIN GPIO_PIN_1
#define CONFIG_ESC_IRQ_EXTI EXTI_1
#define CONFIG_ESC_IRQ_IRQn EXTI1_IRQn
#define CONFIG_ESC_IRQ_EXTI_SOURCE_PORT GPIO_PORT_SOURCE_GPIOB
#define CONFIG_ESC_IRQ_EXTI_SOURCE_PIN GPIO_PIN_SOURCE_1
#define CONFIG_ESC_SYNC1_PORT GPIOB
#define CONFIG_ESC_SYNC1_PIN GPIO_PIN_2
#define CONFIG_ESC_SYNC1_EXTI EXTI_2
#define CONFIG_ESC_SYNC1_IRQn EXTI2_IRQn
#define CONFIG_ESC_SYNC1_EXTI_SOURCE_PORT GPIO_PORT_SOURCE_GPIOB
#define CONFIG_ESC_SYNC1_EXTI_SOURCE_PIN GPIO_PIN_SOURCE_2
/* ---------- EXMC 数据/控制线引脚(GD32E508 标准映射) ---------- */
#define CONFIG_EXMC_D0_PORT GPIOD
#define CONFIG_EXMC_D0_PIN GPIO_PIN_14
#define CONFIG_EXMC_D1_PORT GPIOD
#define CONFIG_EXMC_D1_PIN GPIO_PIN_15
#define CONFIG_EXMC_D2_PORT GPIOD
#define CONFIG_EXMC_D2_PIN GPIO_PIN_0
#define CONFIG_EXMC_D3_PORT GPIOD
#define CONFIG_EXMC_D3_PIN GPIO_PIN_1
#define CONFIG_EXMC_D4_PORT GPIOE
#define CONFIG_EXMC_D4_PIN GPIO_PIN_7
#define CONFIG_EXMC_D5_PORT GPIOE
#define CONFIG_EXMC_D5_PIN GPIO_PIN_8
#define CONFIG_EXMC_D6_PORT GPIOE
#define CONFIG_EXMC_D6_PIN GPIO_PIN_9
#define CONFIG_EXMC_D7_PORT GPIOE
#define CONFIG_EXMC_D7_PIN GPIO_PIN_10
#define CONFIG_EXMC_D8_PORT GPIOE
#define CONFIG_EXMC_D8_PIN GPIO_PIN_11
#define CONFIG_EXMC_D9_PORT GPIOE
#define CONFIG_EXMC_D9_PIN GPIO_PIN_12
#define CONFIG_EXMC_D10_PORT GPIOE
#define CONFIG_EXMC_D10_PIN GPIO_PIN_13
#define CONFIG_EXMC_D11_PORT GPIOE
#define CONFIG_EXMC_D11_PIN GPIO_PIN_14
#define CONFIG_EXMC_D12_PORT GPIOE
#define CONFIG_EXMC_D12_PIN GPIO_PIN_15
#define CONFIG_EXMC_D13_PORT GPIOD
#define CONFIG_EXMC_D13_PIN GPIO_PIN_8
#define CONFIG_EXMC_D14_PORT GPIOD
#define CONFIG_EXMC_D14_PIN GPIO_PIN_9
#define CONFIG_EXMC_D15_PORT GPIOD
#define CONFIG_EXMC_D15_PIN GPIO_PIN_10
#define CONFIG_EXMC_A16_PORT GPIOD
#define CONFIG_EXMC_A16_PIN GPIO_PIN_11
#define CONFIG_EXMC_NOE_PORT GPIOD
#define CONFIG_EXMC_NOE_PIN GPIO_PIN_4
#define CONFIG_EXMC_NWE_PORT GPIOD
#define CONFIG_EXMC_NWE_PIN GPIO_PIN_5
#define CONFIG_EXMC_NE1_PORT GPIOD
#define CONFIG_EXMC_NE1_PIN GPIO_PIN_7
#endif /* SYS_CONFIG_H */
+62
View File
@@ -0,0 +1,62 @@
/**
* @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)
#endif /* SYS_CRITICAL_H */
+14 -3
View File
@@ -1,4 +1,4 @@
/**
/**
* @file initial.cpp
* @brief 应用启动初始化实现。
*/
@@ -8,13 +8,18 @@
#include "device/canfd.h"
#include "device/temp_sensor.h"
#include "device/usart0.h"
#include "device/Timer.h"
#include "device/timer.h"
#include "device/adc.h"
#include "device/watchdog.h"
#include "device/app_spi_command.h"
#include "device/spi/app_spi_command.h"
#include "sys/systick.h"
#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 执行应用级外设与调度初始化。
@@ -42,6 +47,12 @@ 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
printf("[APP] init: Watchdog...\r\n");
Watchdog::init();
printf("[APP] init: all done\r\n");
+6 -31
View File
@@ -1,4 +1,4 @@
/**
/**
* @file task_manager.cpp
* @brief 任务调度管理器实现。
*/
@@ -6,32 +6,7 @@
#include "main.h"
#include "sys/task_manager.h"
#include "sys/config.h"
namespace {
/**
* @brief Enter a critical section and preserve the previous interrupt mask.
* @param[in] 无。
* @param[out] 无。
* @retval uint32_t Previous PRIMASK value.
*/
static uint32_t enterCriticalSection()
{
const uint32_t primask = __get_PRIMASK();
__disable_irq();
return primask;
}
/**
* @brief Exit a critical section by restoring the saved interrupt mask.
* @param[in] primask Previous PRIMASK value returned by `enterCriticalSection()`.
* @param[out] 无。
* @retval 无返回值。
*/
static void exitCriticalSection(uint32_t primask)
{
__set_PRIMASK(primask);
}
} // namespace
#include "sys/critical.h"
task_scheduler_t g_scheduler = {0};
@@ -68,9 +43,9 @@ void TaskManager::init()
*/
void TaskManager::set(uint32_t taskMask)
{
const uint32_t primask = enterCriticalSection();
uint32_t save = RT_CRITICAL_SAVE();
g_scheduler.task_flags |= taskMask;
exitCriticalSection(primask);
RT_CRITICAL_RESTORE(save);
}
/**
@@ -83,10 +58,10 @@ void TaskManager::set(uint32_t taskMask)
uint32_t TaskManager::takePending()
{
uint32_t pending;
const uint32_t primask = enterCriticalSection();
uint32_t save = RT_CRITICAL_SAVE();
pending = g_scheduler.task_flags;
g_scheduler.task_flags &= ~pending;
exitCriticalSection(primask);
RT_CRITICAL_RESTORE(save);
return pending;
}
+35 -2
View File
@@ -1,4 +1,4 @@
/**
/**
* @file task_runner.cpp
* @brief 任务执行器实现。
*/
@@ -6,13 +6,16 @@
#include "main.h"
#include "device/adc.h"
#include "device/watchdog.h"
#include "device/app_spi_command.h"
#include "device/spi/app_spi_command.h"
#include "device/canfd.h"
#include "device/temp_sensor.h"
#include "sys/config.h"
#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;
@@ -265,6 +268,34 @@ void TaskRunner::taskDiagnosticPrint()
#endif
}
/**
* @brief 执行 SPI CS release 后推迟的命令解析(从 ISR 移出)。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note ISR 瘦身:onCsRelease() 仅置位 TASK_SPI_CMD_PROCESS,实际解析在此执行。
*/
void TaskRunner::taskSpiCmdProcess()
{
AppSpiCommandBus.processPendingCommand();
}
/**
* @brief 执行 EtherCAT 周期/事件处理(非实时应用层 PDO 映射)。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 由周期 ISR(EXTI0 SYNC0)或 ESC 事件 ISR(EXTI1 IRQ)置位 TASK_ETHERCAT_PROCESS。
*/
void TaskRunner::taskEthercatProcess()
{
#if CONFIG_ETHERCAT_ENABLE
EthercatSlave::process();
#else
/* 未使能 EtherCAT 时此分支不应被置位;保留空操作以避免链接错误。 */
#endif
}
/**
* @brief 初始化任务执行器运行时上下文。
* @param[in] 无。
@@ -309,4 +340,6 @@ void TaskRunner::runPending()
if(pending & (1UL << TASK_HEARTBEAT)) taskHeartbeat();
if(pending & (1UL << TASK_DIAGNOSTIC_PRINT)) taskDiagnosticPrint();
if(pending & (1UL << TASK_SPI_LOG)) AppSpiCommandBus.poll();
if(pending & (1UL << TASK_SPI_CMD_PROCESS)) taskSpiCmdProcess();
if(pending & (1UL << TASK_ETHERCAT_PROCESS)) taskEthercatProcess();
}
+4
View File
@@ -49,6 +49,10 @@ private:
static void taskHeartbeat();
/** @brief 诊断打印任务。 */
static void taskDiagnosticPrint();
/** @brief SPI CS release 后推迟的命令解析(从 ISR 移出)。 */
static void taskSpiCmdProcess();
/** @brief EtherCAT 周期/事件处理(非实时应用层 PDO 映射)。 */
static void taskEthercatProcess();
};
#endif /* SYS_TASK_RUNNER_H */