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
+10 -53
View File
@@ -1,13 +1,13 @@
/**
* @file CANFD.cpp
* @file canfd.cpp
* @brief CANFD 通信模块实现。
*/
#include "main.h"
#include "device/canfd.h"
#include "sys/config.h"
#include "sys/critical.h"
#include "sys/task_manager.h"
#include "sys/systick.h"
namespace {
struct TxFrame {
@@ -44,30 +44,6 @@ constexpr CanChannelConfig kChannels[] = {
constexpr size_t kChannelCount = sizeof(kChannels) / sizeof(kChannels[0]);
constexpr uint8_t kTxQueueDepth = 4U;
constexpr uint32_t kCanRxParseTaskMask = (1UL << TASK_CAN_RX_PARSE);
/**
* @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
CanFd::Diag CanFd::s_diag{};
@@ -281,7 +257,7 @@ void CanFd::init()
initFdMode(ch.canPeriph);
configureRxFilter(ch.canPeriph, ch.filterNumber);
can_interrupt_enable(ch.canPeriph, CAN_INT_RFNE0 | CAN_INT_RFF0 | CAN_INT_RFO0);
nvic_irq_enable(ch.rx0Irq, 1U, static_cast<uint8_t>(i));
nvic_irq_enable(ch.rx0Irq, CONFIG_IRQ_PREEMPT_CAN, static_cast<uint8_t>(i));
s_rxQueues[i].head = 0U;
s_rxQueues[i].tail = 0U;
@@ -375,14 +351,14 @@ void CanFd::sendPrepared()
bool CanFd::hasRxFrame()
{
bool found = false;
const uint32_t primask = enterCriticalSection();
uint32_t save = RT_CRITICAL_SAVE();
for(size_t i = 0; i < kChannelCount; ++i) {
if(s_rxQueues[i].count != 0U) {
found = true;
break;
}
}
exitCriticalSection(primask);
RT_CRITICAL_RESTORE(save);
return found;
}
@@ -401,7 +377,7 @@ bool CanFd::consumeRxFrame(uint32_t *id, uint8_t *data, uint8_t *length, uint8_t
RxFrame localFrame{};
bool found = false;
const uint32_t primask = enterCriticalSection();
uint32_t save = RT_CRITICAL_SAVE();
for(size_t n = 0; n < kChannelCount; ++n) {
const uint8_t idx = static_cast<uint8_t>((s_nextConsumeChannel + n) % kChannelCount);
auto &queue = s_rxQueues[idx];
@@ -416,7 +392,7 @@ bool CanFd::consumeRxFrame(uint32_t *id, uint8_t *data, uint8_t *length, uint8_t
found = true;
break;
}
exitCriticalSection(primask);
RT_CRITICAL_RESTORE(save);
if(!found) {
return false;
@@ -467,29 +443,10 @@ bool CanFd::transmitFrame(uint8_t channelIndex, uint32_t ext_id,
const uint32_t canPeriph = kChannels[channelIndex].canPeriph;
uint8_t mailbox = can_message_transmit(canPeriph, &tx_msg);
/* 邮箱全占时的恢复处理:
* 当目标控制器初始化时 auto_retrans = ENABLE,且总线上没有其它节点回 ACK,
* 已入邮箱的帧会无限自动重传,TME 位永不置回,导致后续帧一律 CAN_NOMAILBOX。
*
* 改良策略(两段式,尽量不丢帧):
* 1) 先短自旋 ~2 ms 轮询邮箱状态。健康总线上 1 Mbps + 8 字节帧的实际发送
* 仅约 130 µs,正常重传几百 µs 内即可 drain,绝大多数“偶发 NOMAILBOX”
* 场景到这一步就已经恢复,不会触发 abort,也就不会丢帧。
* 2) 若 2 ms 后仍全部 PENDING(典型:物理层不通、TCAN1057A 没焊或 CAN_L/H
* 断线导致 ACK Error 死循环),只 abort 第一个 PENDING 邮箱腾一个位置,
* 最坏丢 1 帧而不是 3 帧,并通过 s_diag.tx_abort_count 暴露给串口 diag,
* 上层(含 SPI 桥)仍能把后续帧正常下发。 */
/* 邮箱全占时保持非阻塞:本函数可能处于 SPI CS 中断链路,不能等待
* SysTick。若 auto_retrans 因总线无 ACK 卡住全部邮箱,只立即 abort
* 一个 PENDING 邮箱并重投一次,把中断占用时间控制在寄存器访问级别。 */
if (mailbox == CAN_NOMAILBOX) {
constexpr uint8_t kDrainPolls = 2U;
for (uint8_t poll = 0U; poll < kDrainPolls && mailbox == CAN_NOMAILBOX; ++poll) {
SysTickTimer::delayMs(1U);
mailbox = can_message_transmit(canPeriph, &tx_msg);
}
}
if (mailbox == CAN_NOMAILBOX) {
/* 只 abort 一个 PENDING 邮箱:找到首个 stop 成功的就停手,
* 不再扫描后续邮箱,把丢帧上限收敛到 1。stop 失败的邮箱
* (硬件正在物理发送)继续尝试下一个,避免一次 abort 都没成功。 */
for (uint8_t mb = CAN_MAILBOX0; mb <= CAN_MAILBOX2; ++mb) {
if (can_transmit_states(canPeriph, mb) != CAN_TRANSMIT_PENDING) {
continue;