feat(canfd): 切换 CANFD 接收为中断驱动和环形缓冲机制
- 将 CANFD 接收机制由轮询改为 FIFO0 的中断事件驱动处理 - 实现循环取空硬件 FIFO0,提高接收效率和响应速度 - 引入环形缓冲区缓存接收帧,支持多路通道多帧队列管理 - 置位解析任务标志位,实现接收数据的异步解析 - 增加接收中断统计,包括 FIFO 满和溢出计数 - 重构临界区代码,使用自定义函数保护中断状态 - 修改接收过滤器配置,支持按掩码匹配标准帧进入 FIFO0 - 使能 CAN FIFO0 多种中断标志,包括非空、满和溢出中断
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
#include "main.h"
|
||||
#include "device/canfd.h"
|
||||
#include "sys/config.h"
|
||||
#include "sys/task_manager.h"
|
||||
|
||||
namespace {
|
||||
struct TxFrame {
|
||||
@@ -40,8 +41,32 @@ constexpr CanChannelConfig kChannels[] = {
|
||||
};
|
||||
|
||||
constexpr size_t kChannelCount = sizeof(kChannels) / sizeof(kChannels[0]);
|
||||
constexpr uint8_t kRxQueueDepth = 4U;
|
||||
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{};
|
||||
@@ -72,23 +97,23 @@ void CanFd::initGpio()
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 为指定 CAN 控制器配置全接收过滤器。
|
||||
* @brief 为指定 CAN 控制器配置接收过滤器。
|
||||
* @param[in] canPeriph 目标 CAN 外设实例。
|
||||
* @param[in] filterNumber 过滤器编号。
|
||||
* @param[out] 无。
|
||||
* @retval 无返回值。
|
||||
* @note 当前使用 32 位 mask 模式放行全部报文,并统一进入 FIFO0。
|
||||
* @note 当前默认按 `CONFIG_CAN_RX_FILTER_ID`/`CONFIG_CAN_RX_FILTER_MASK` 对标准帧做掩码匹配,并统一进入 FIFO0。
|
||||
*/
|
||||
void CanFd::filterAcceptAll(uint32_t canPeriph, uint8_t filterNumber)
|
||||
void CanFd::configureRxFilter(uint32_t canPeriph, uint8_t filterNumber)
|
||||
{
|
||||
can_filter_parameter_struct can_filter{};
|
||||
can_filter.filter_number = filterNumber;
|
||||
can_filter.filter_mode = CAN_FILTERMODE_MASK;
|
||||
can_filter.filter_bits = CAN_FILTERBITS_32BIT;
|
||||
can_filter.filter_list_high = 0x0000U;
|
||||
can_filter.filter_list_high = static_cast<uint16_t>((CONFIG_CAN_RX_FILTER_ID & 0x7FFU) << 5U);
|
||||
can_filter.filter_list_low = 0x0000U;
|
||||
can_filter.filter_mask_high = 0x0000U;
|
||||
can_filter.filter_mask_low = 0x0000U;
|
||||
can_filter.filter_mask_high = static_cast<uint16_t>((CONFIG_CAN_RX_FILTER_MASK & 0x7FFU) << 5U);
|
||||
can_filter.filter_mask_low = static_cast<uint16_t>(1U << 2U);
|
||||
can_filter.filter_fifo_number = CAN_FIFO0;
|
||||
can_filter.filter_enable = ENABLE;
|
||||
can_filter_init(canPeriph, &can_filter);
|
||||
@@ -237,7 +262,7 @@ bool CanFd::enqueueTxFrame(uint8_t channelIndex, const StatusPayload *status)
|
||||
* @param[in] 无。
|
||||
* @param[out] 无。
|
||||
* @retval 无返回值。
|
||||
* @note 初始化后默认将三路 TCAN1057A 收发器置为 Normal,并清零收发诊断状态。
|
||||
* @note 初始化后默认将三路 TCAN1057A 收发器置为 Normal,并使能 FIFO0 的非空/满/overfull 中断统计。
|
||||
*/
|
||||
void CanFd::init()
|
||||
{
|
||||
@@ -253,8 +278,8 @@ void CanFd::init()
|
||||
can_deinit(ch.canPeriph);
|
||||
initNominalTiming(ch.canPeriph);
|
||||
initFdMode(ch.canPeriph);
|
||||
filterAcceptAll(ch.canPeriph, ch.filterNumber);
|
||||
can_interrupt_enable(ch.canPeriph, CAN_INT_RFNE0);
|
||||
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));
|
||||
|
||||
s_rxQueues[i].head = 0U;
|
||||
@@ -348,12 +373,16 @@ void CanFd::sendPrepared()
|
||||
*/
|
||||
bool CanFd::hasRxFrame()
|
||||
{
|
||||
bool found = false;
|
||||
const uint32_t primask = enterCriticalSection();
|
||||
for(size_t i = 0; i < kChannelCount; ++i) {
|
||||
if(s_rxQueues[i].count != 0U) {
|
||||
return true;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
exitCriticalSection(primask);
|
||||
return found;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -371,7 +400,7 @@ bool CanFd::consumeRxFrame(uint32_t *id, uint8_t *data, uint8_t *length, uint8_t
|
||||
RxFrame localFrame{};
|
||||
bool found = false;
|
||||
|
||||
__disable_irq();
|
||||
const uint32_t primask = enterCriticalSection();
|
||||
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];
|
||||
@@ -386,7 +415,7 @@ bool CanFd::consumeRxFrame(uint32_t *id, uint8_t *data, uint8_t *length, uint8_t
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
__enable_irq();
|
||||
exitCriticalSection(primask);
|
||||
|
||||
if(!found) {
|
||||
return false;
|
||||
@@ -447,31 +476,44 @@ void CanFd::setSilentMode()
|
||||
* @param[in] channelIndex 目标 CAN 通道索引(0/1/2)。
|
||||
* @param[out] 无。
|
||||
* @retval 无返回值。
|
||||
* @note 从对应控制器 FIFO0 取出一帧报文,更新诊断统计,并写入该通道软件接收队列。
|
||||
* @note 从对应控制器 FIFO0 循环取空所有待处理报文,更新诊断统计,写入该通道软件接收队列,并置位解析任务。
|
||||
*/
|
||||
void CanFd::handleRxInterrupt(uint8_t channelIndex)
|
||||
{
|
||||
if(channelIndex >= kChannelCount) return;
|
||||
const auto &ch = kChannels[channelIndex];
|
||||
|
||||
if(can_interrupt_flag_get(ch.canPeriph, CAN_INT_FLAG_RFL0) == RESET) return;
|
||||
const bool fifoFull = (can_interrupt_flag_get(ch.canPeriph, CAN_INT_FLAG_RFF0) != RESET);
|
||||
const bool fifoOverfull = (can_interrupt_flag_get(ch.canPeriph, CAN_INT_FLAG_RFO0) != RESET);
|
||||
if(fifoFull) {
|
||||
s_diag.rx_fifo_full_count++;
|
||||
}
|
||||
if(fifoOverfull) {
|
||||
s_diag.rx_fifo_overfull_count++;
|
||||
}
|
||||
|
||||
can_receive_message_struct rx_message{};
|
||||
can_struct_para_init(CAN_RX_MESSAGE_STRUCT, &rx_message);
|
||||
can_message_receive(ch.canPeriph, CAN_FIFO0, &rx_message);
|
||||
bool hadTraffic = false;
|
||||
while(can_interrupt_flag_get(ch.canPeriph, CAN_INT_FLAG_RFL0) != RESET) {
|
||||
hadTraffic = true;
|
||||
|
||||
s_diag.rx_count++;
|
||||
s_diag.last_rx_channel = channelIndex;
|
||||
s_diag.last_payload_length = rx_message.rx_dlen;
|
||||
s_diag.last_rx_fd_flag = rx_message.fd_flag;
|
||||
s_diag.last_rx_brs = rx_message.fd_brs;
|
||||
s_diag.last_rx_esi = rx_message.fd_esi;
|
||||
s_diag.last_rx_id = (rx_message.rx_ff == CAN_FF_STANDARD) ? rx_message.rx_sfid : rx_message.rx_efid;
|
||||
can_receive_message_struct rx_message{};
|
||||
can_struct_para_init(CAN_RX_MESSAGE_STRUCT, &rx_message);
|
||||
can_message_receive(ch.canPeriph, CAN_FIFO0, &rx_message);
|
||||
|
||||
s_diag.rx_count++;
|
||||
s_diag.last_rx_channel = channelIndex;
|
||||
s_diag.last_payload_length = rx_message.rx_dlen;
|
||||
s_diag.last_rx_fd_flag = rx_message.fd_flag;
|
||||
s_diag.last_rx_brs = rx_message.fd_brs;
|
||||
s_diag.last_rx_esi = rx_message.fd_esi;
|
||||
s_diag.last_rx_id = (rx_message.rx_ff == CAN_FF_STANDARD) ? rx_message.rx_sfid : rx_message.rx_efid;
|
||||
|
||||
auto &queue = s_rxQueues[channelIndex];
|
||||
if(queue.count >= kRxQueueDepth) {
|
||||
s_diag.rx_drop_count++;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto &queue = s_rxQueues[channelIndex];
|
||||
if(queue.count >= kRxQueueDepth) {
|
||||
s_diag.rx_drop_count++;
|
||||
} else {
|
||||
auto &frame = queue.frames[queue.tail];
|
||||
frame.channel = channelIndex;
|
||||
frame.id = s_diag.last_rx_id;
|
||||
@@ -479,11 +521,20 @@ void CanFd::handleRxInterrupt(uint8_t channelIndex)
|
||||
for(uint8_t i = 0; i < rx_message.rx_dlen; ++i) {
|
||||
frame.data[i] = rx_message.rx_data[i];
|
||||
}
|
||||
|
||||
queue.tail = static_cast<uint8_t>((queue.tail + 1U) % kRxQueueDepth);
|
||||
queue.count++;
|
||||
}
|
||||
|
||||
can_interrupt_flag_clear(ch.canPeriph, CAN_INT_FLAG_RFL0);
|
||||
if(fifoFull) {
|
||||
can_interrupt_flag_clear(ch.canPeriph, CAN_INT_FLAG_RFF0);
|
||||
}
|
||||
if(fifoOverfull) {
|
||||
can_interrupt_flag_clear(ch.canPeriph, CAN_INT_FLAG_RFO0);
|
||||
}
|
||||
if(hadTraffic) {
|
||||
TaskManager::set(kCanRxParseTaskMask);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user