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:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user