[i=s] 本帖最后由 uuguoleilei 于 2025-5-23 20:47 编辑 [/i]<br />
<br />
根据透视图可以知道 can的引脚是PD0和PD1

当然,稳妥起见还是一看原理图

看起来是的

一、硬件连接与电路设计
STM32C092的CAN FD通信需要正确的硬件连接,主要包括以下部分:
-
引脚分配
- CAN_RX: PD0 (复用功能AF7)
- CAN_TX: PD1 (复用功能AF7)
- STANDBY: PD2 (普通输出,低电平使能CAN控制器)
-
CAN收发器连接
- CANH和CANL之间必须连接120Ω终端电阻(高速应用)
- 建议使用隔离型收发器(如ISO1050)提高抗干扰能力
- STANDBY引脚控制:低电平→正常工作,高电平→待机模式
-
电源与滤波
- CAN收发器电源需稳定(通常5V)
- 关键信号加100nF去耦电容
- 总线建议使用屏蔽双绞线,长度控制在5米以内(高速CAN FD)
#### 二、CAN FD协议基础与配置要点
CAN FD(CAN with Flexible Data Rate)是对传统CAN协议的扩展,主要改进:
1. **关键特性**
- 支持高达8Mbps的数据传输速率(传统CAN最高1Mbps)
- 数据场长度扩展至64字节(传统CAN为8字节)
- 支持动态比特率切换(Bit Rate Switching)
2. **位时序配置**
- 标称位时间(Nominal Bit Time): 用于仲裁段,决定基本通信速率
- 数据位时间(Data Bit Time): 用于数据段,可配置更高的传输速率
- 计算公式: 波特率 = APB1时钟 / (Prescaler × (1 + BS1 + BS2))
3. **典型配置参数**
| 参数 | 描述 | 500kbps/2Mbps配置 |
|--------------------|-------------------------------|-------------------|
| NominalPrescaler | 标称位时间分频器 | 6 |
| NominalTimeSeg1 | 标称时间段1 | 15TQ |
| NominalTimeSeg2 | 标称时间段2 | 2TQ |
| DataPrescaler | 数据位时间分频器 | 3 |
| DataTimeSeg1 | 数据时间段1 | 6TQ |
| DataTimeSeg2 | 数据时间段2 | 1TQ |
| BRS | 比特率切换使能 | ENABLE |
#### 三、STM32CubeIDE配置流程
1. **引脚配置**
- 打开CubeMX,选择STM32C092芯片
- 配置PD0为CAN_RX(AF7)
- 配置PD1为CAN_TX(AF7)
- 配置PD2为GPIO_Output(推挽输出,初始值Low)
2. **CAN模块配置**
- 使能CAN1外设
- 配置时钟源为APB1(最高48MHz)
- 设置模式为CAN FD
- 配置波特率参数:
- 标称位时间: 500kbps
- 数据位时间: 2Mbps
3. **NVIC配置**
- 使能CAN1_RX0_IRQn中断
- 设置适当的优先级(建议抢占优先级0,子优先级0)
4. **生成代码**
- 选择"Generate Code"生成初始化代码
#### 四、代码实现与注释
以下是完整的CAN FD配置代码,包含初始化、接收中断和循环发送功能:
```c
#include "main.h"
#include "stm32c0xx_hal.h"
CAN_HandleTypeDef hcan;
UART_HandleTypeDef huart2; // 用于调试输出
/* CAN FD初始化函数 */
static void MX_CAN_Init(void)
{
hcan.Instance = CAN;
/* 基本CAN配置 */
hcan.Init.Prescaler = 6; // 分频器值,决定波特率
hcan.Init.Mode = CAN_MODE_NORMAL; // 正常模式(非回环/静默)
hcan.Init.SyncJumpWidth = CAN_SJW_1TQ; // 重新同步跳跃宽度
hcan.Init.TimeSeg1 = CAN_BS1_15TQ; // 时间段1
hcan.Init.TimeSeg2 = CAN_BS2_2TQ; // 时间段2
hcan.Init.TimeTriggeredMode = DISABLE; // 禁用时间触发模式
hcan.Init.AutoBusOff = ENABLE; // 启用自动总线关闭恢复
hcan.Init.AutoWakeUp = DISABLE; // 禁用自动唤醒
hcan.Init.AutoRetransmission = ENABLE; // 启用自动重传
hcan.Init.ReceiveFifoLocked = DISABLE; // FIFO不锁定模式
hcan.Init.TransmitFifoPriority = DISABLE; // 发送FIFO优先级禁用
/* CAN FD特定配置 */
hcan.Init.CANFD = ENABLE; // 启用CAN FD功能
hcan.Init.NominalPrescaler = 6; // 标称位时间分频器
hcan.Init.NominalSyncJumpWidth = CAN_SJW_1TQ;
hcan.Init.NominalTimeSeg1 = CAN_BS1_15TQ;
hcan.Init.NominalTimeSeg2 = CAN_BS2_2TQ;
hcan.Init.DataPrescaler = 3; // 数据位时间分频器(更高波特率)
hcan.Init.DataSyncJumpWidth = CAN_SJW_1TQ;
hcan.Init.DataTimeSeg1 = CAN_BS1_6TQ;
hcan.Init.DataTimeSeg2 = CAN_BS2_1TQ;
hcan.Init.MaxBusOffCount = 127;
hcan.Init.TxFifoQueueMode = DISABLE; // 禁用发送FIFO队列模式
if (HAL_CAN_Init(&hcan) != HAL_OK)
{
Error_Handler(); // 初始化失败处理
}
}
/* GPIO初始化 - 包括CAN引脚和STANDBY引脚 */
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* 使能GPIOD时钟 */
__HAL_RCC_GPIOD_CLK_ENABLE();
/* 配置CAN_RX(PD0)和CAN_TX(PD1)为复用功能 */
GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF7_CAN;
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
/* 配置STANDBY(PD2)为输出 */
GPIO_InitStruct.Pin = GPIO_PIN_2;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
/* 初始化为低电平(使能CAN控制器) */
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
}
/* CAN过滤器配置 - 接收所有帧 */
static void CAN_ConfigFilter(void)
{
CAN_FilterTypeDef sFilterConfig = {0};
sFilterConfig.FilterBank = 0; // 过滤器组0
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; // 掩码模式
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; // 32位过滤器
sFilterConfig.FilterIdHigh = 0x0000; // 过滤器ID高16位
sFilterConfig.FilterIdLow = 0x0000; // 过滤器ID低16位
sFilterConfig.FilterMaskIdHigh = 0x0000; // 过滤器掩码高16位(全0接收所有)
sFilterConfig.FilterMaskIdLow = 0x0000; // 过滤器掩码低16位(全0接收所有)
sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0; // 关联到FIFO0
sFilterConfig.FilterActivation = ENABLE; // 启用过滤器
sFilterConfig.SlaveStartFilterBank = 0;
if (HAL_CAN_ConfigFilter(&hcan, &sFilterConfig) != HAL_OK)
{
Error_Handler();
}
}
/* 启动CAN并使能接收中断 */
static void CAN_Start(void)
{
/* 启动CAN控制器 */
if (HAL_CAN_Start(&hcan) != HAL_OK)
{
Error_Handler();
}
/* 使能接收FIFO0消息挂起中断 */
if (HAL_CAN_ActivateNotification(&hcan, CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK)
{
Error_Handler();
}
}
/* 发送CAN FD消息 */
void CAN_SendMessage(void)
{
static uint32_t counter = 0;
CAN_TxHeaderTypeDef TxHeader;
uint8_t TxData[64] = {0}; // 最大支持64字节数据
uint32_t TxMailbox;
/* 配置发送头 */
TxHeader.StdId = 0x123; // 标准帧ID
TxHeader.ExtId = 0x0; // 扩展帧ID(未使用)
TxHeader.IDE = CAN_ID_STD; // 标准帧格式
TxHeader.RTR = CAN_RTR_DATA; // 数据帧(非远程帧)
TxHeader.DLC = 8; // 数据长度(8字节)
TxHeader.FDFormat = ENABLE; // 启用CAN FD格式
TxHeader.BRS = ENABLE; // 启用比特率切换(数据段使用更高波特率)
TxHeader.ESI = DISABLE; // ESI位(错误状态指示)
/* 填充数据 */
for (int i = 0; i < TxHeader.DLC; i++)
{
TxData[i] = counter + i; // 简单递增数据
}
counter++;
/* 将消息添加到发送邮箱 */
if (HAL_CAN_AddTxMessage(&hcan, &TxHeader, TxData, &TxMailbox) != HAL_OK)
{
Error_Handler();
}
}
/* CAN接收FIFO0消息挂起回调函数 */
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
CAN_RxHeaderTypeDef RxHeader;
uint8_t RxData[64];
/* 从FIFO0读取消息 */
if (HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &RxHeader, RxData) != HAL_OK)
{
Error_Handler();
}
/* 处理接收到的数据 */
printf("接收到CAN FD消息 - ID: 0x%03X, DLC: %d, BRS: %s\r\n",
RxHeader.StdId,
RxHeader.DLC,
RxHeader.BRS ? "启用" : "禁用");
/* 打印数据内容 */
printf("数据内容: ");
for (int i = 0; i < RxHeader.DLC; i++)
{
printf("0x%02X ", RxData[i]);
}
printf("\r\n");
}
/* 主函数 */
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_CAN_Init();
MX_USART2_UART_Init(); // 用于调试输出
printf("CAN FD示例程序启动\r\n");
/* 配置CAN过滤器并启动 */
CAN_ConfigFilter();
CAN_Start();
/* 主循环 */
while (1)
{
/* 每100ms发送一次消息 */
HAL_Delay(100);
CAN_SendMessage();
}
}
五、接收中断处理机制
-
中断使能流程
- 通过
HAL_CAN_ActivateNotification()
使能指定中断
- 本例中启用了
CAN_IT_RX_FIFO0_MSG_PENDING
(FIFO0消息挂起中断)
- 中断服务函数由HAL库自动调用回调函数
HAL_CAN_RxFifo0MsgPendingCallback()
-
接收数据处理
- 使用
HAL_CAN_GetRxMessage()
从FIFO读取数据
- 检查消息ID、数据长度和其他属性
- 处理接收到的数据(如存储、解析或转发)
-
FIFO管理
- STM32C092的CAN控制器有两个接收FIFO(FIFO0和FIFO1)
- 每个FIFO可存储3个CAN消息
- 需及时处理接收的数据,避免FIFO溢出
六、循环发送配置与实现
-
发送流程
- 配置
CAN_TxHeaderTypeDef
结构体设置消息参数
- 填充数据缓冲区
- 调用
HAL_CAN_AddTxMessage()
将消息添加到发送邮箱
- 硬件自动处理发送过程
-
发送参数说明
StdId/ExtId
: 消息ID(标准帧/扩展帧)
IDE
: 帧格式(标准帧/扩展帧)
RTR
: 帧类型(数据帧/远程帧)
DLC
: 数据长度(0-64字节,CAN FD支持)
BRS
: 比特率切换(启用后数据段使用更高波特率)
-
发送状态管理
- 可通过
HAL_CAN_GetTxMailboxesFreeLevel()
检查可用发送邮箱数量
- 发送失败时(如邮箱已满)需进行错误处理
- 支持自动重传(需启用
AutoRetransmission
)
七、配置注意事项与常见问题
-
波特率配置
- 确保标称位时间和数据位时间配置合理
- 使用在线波特率计算器验证配置
- 总线上所有节点的标称波特率必须一致
-
硬件注意事项
- 终端电阻是高速CAN通信的关键,必须正确连接
- STANDBY引脚控制不当会导致CAN控制器不工作
- 检查CAN收发器的电源和使能条件
-
软件调试建议
- 使用CAN分析仪监测实际通信波形
- 检查CAN状态寄存器(如ESR、MSR)获取错误信息
- 添加调试输出,监控初始化和通信过程
-
兼容性问题
- CAN FD设备可与传统CAN设备通信,但会退化为传统CAN模式
- 若总线上有传统CAN节点,需考虑禁用BRS功能
- 数据长度超过8字节时,传统CAN设备会忽略多余数据
八、性能优化与高级应用
-
中断处理优化
- 避免在中断处理函数中执行耗时操作
- 使用队列缓存接收到的数据,在主循环中处理
- 合理设置中断优先级,避免干扰关键任务
-
错误处理机制
- 实现完整的错误处理函数
- 监控错误计数器,检测总线异常
- 实现总线离线恢复策略
-
高级应用场景
- 实现多帧数据传输协议(如ISO 15765-2)
- 结合FreeRTOS实现多任务CAN通信
- 实现CAN网络管理协议(NM)
通过以上配置和注意事项,STM32C092可成功实现CAN FD通信,支持接收中断和循环发送功能,满足大多数工业和汽车应用需求。
### **CAN FD过滤器掩码配置详解**
CAN总线的过滤器配置是实现高效通信的关键,STM32C092提供了灵活的过滤机制,允许仅接收必要的CAN帧,减轻CPU负担。
#### **一、过滤器基本概念**
1. **过滤器组与FIFO**
- STM32C092有14个过滤器组(0-13)
- 每个过滤器组可关联到接收FIFO0或FIFO1
- 每个FIFO最多可存储3个CAN帧
2. **过滤模式**
- **ID列表模式**:精确匹配一个或多个ID
- **掩码模式**:使用掩码定义哪些位需要匹配
3. **过滤器位宽**
- 32位宽:可配置为一个32位过滤器或两个16位过滤器
- 16位宽:适用于需要大量小范围过滤的场景
#### **二、掩码模式配置详解**
掩码模式通过设置`FilterId`和`FilterMask`实现灵活匹配:
- **FilterId**:定义参考ID值
- **FilterMask**:定义哪些位需要严格匹配(1=需匹配,0=忽略)
**示例1**:接收ID为0x123的标准帧
```c
sFilterConfig.FilterIdHigh = 0x0123 << 5; // 标准ID左移5位
sFilterConfig.FilterIdLow = 0x0000;
sFilterConfig.FilterMaskIdHigh = 0xFFFF; // 所有位都必须匹配
sFilterConfig.FilterMaskIdLow = 0xFFFF;
示例2:接收所有0x100-0x1FF范围内的标准帧
sFilterConfig.FilterIdHigh = 0x0100 << 5; // 参考ID: 0x100
sFilterConfig.FilterIdLow = 0x0000;
sFilterConfig.FilterMaskIdHigh = 0xF800; // 只匹配高3位(0x1xx)
sFilterConfig.FilterMaskIdLow = 0xFFFF;
示例3:接收所有扩展帧
sFilterConfig.FilterIdHigh = 0x0000;
sFilterConfig.FilterIdLow = 0x2000; // IDE位=1(扩展帧)
sFilterConfig.FilterMaskIdHigh = 0x0000; // 忽略ID内容
sFilterConfig.FilterMaskIdLow = 0x2000; // 只匹配IDE位
三、过滤器代码实现
以下是完整的过滤器配置代码,包含多种过滤模式示例:
/* 配置CAN过滤器 - 示例1: 接收所有标准帧 */
static void CAN_ConfigFilter_AllStdFrames(void)
{
CAN_FilterTypeDef sFilterConfig = {0};
sFilterConfig.FilterBank = 0; // 过滤器组0
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; // 掩码模式
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; // 32位宽
sFilterConfig.FilterIdHigh = 0x0000; // 标准ID不关心
sFilterConfig.FilterIdLow = 0x0000; // IDE=0(标准帧)
sFilterConfig.FilterMaskIdHigh = 0x0000; // 忽略标准ID
sFilterConfig.FilterMaskIdLow = 0x4000; // 只匹配IDE位(0=标准帧)
sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0; // 关联到FIFO0
sFilterConfig.FilterActivation = ENABLE; // 启用过滤器
sFilterConfig.SlaveStartFilterBank = 0;
if (HAL_CAN_ConfigFilter(&hcan, &sFilterConfig) != HAL_OK)
{
Error_Handler();
}
}
/* 配置CAN过滤器 - 示例2: 接收ID为0x123的标准帧和0x12345678的扩展帧 */
static void CAN_ConfigFilter_SpecificIDs(void)
{
CAN_FilterTypeDef sFilterConfig = {0};
/* 过滤器组0: 接收ID=0x123的标准帧 */
sFilterConfig.FilterBank = 0;
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
sFilterConfig.FilterIdHigh = (0x123 << 5); // 标准ID左移5位
sFilterConfig.FilterIdLow = 0x0000; // IDE=0
sFilterConfig.FilterMaskIdHigh = 0xFFFF; // 所有位必须匹配
sFilterConfig.FilterMaskIdLow = 0x4000; // 匹配IDE位
sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0;
sFilterConfig.FilterActivation = ENABLE;
if (HAL_CAN_ConfigFilter(&hcan, &sFilterConfig) != HAL_OK)
{
Error_Handler();
}
/* 过滤器组1: 接收ID=0x12345678的扩展帧 */
sFilterConfig.FilterBank = 1;
sFilterConfig.FilterIdHigh = (0x1234 << 16) | (0x5678 >> 16); // 扩展ID高16位
sFilterConfig.FilterIdLow = ((0x5678 & 0xFFFF) << 16) | 0x2000; // 扩展ID低16位+IDE=1
sFilterConfig.FilterMaskIdHigh = 0xFFFF; // 高16位全部匹配
sFilterConfig.FilterMaskIdLow = 0xE000; // 低16位匹配+IDE=1
sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0;
sFilterConfig.FilterActivation = ENABLE;
if (HAL_CAN_ConfigFilter(&hcan, &sFilterConfig) != HAL_OK)
{
Error_Handler();
}
}
/* 配置CAN过滤器 - 示例3: 使用列表模式接收多个ID */
static void CAN_ConfigFilter_ListMode(void)
{
CAN_FilterTypeDef sFilterConfig = {0};
/* 过滤器组0: 列表模式 - 接收ID=0x100和0x101的标准帧 */
sFilterConfig.FilterBank = 0;
sFilterConfig.FilterMode = CAN_FILTERMODE_IDLIST; // 列表模式
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
sFilterConfig.FilterIdHigh = (0x100 << 5); // ID 0x100
sFilterConfig.FilterIdLow = 0x0000; // IDE=0
sFilterConfig.FilterMaskIdHigh = (0x101 << 5); // ID 0x101
sFilterConfig.FilterMaskIdLow = 0x0000; // IDE=0
sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0;
sFilterConfig.FilterActivation = ENABLE;
if (HAL_CAN_ConfigFilter(&hcan, &sFilterConfig) != HAL_OK)
{
Error_Handler();
}
}
四、CAN控制器状态与错误处理
-
CAN控制器状态
- 准备就绪状态:CAN控制器初始化完成,可接收/发送
- 总线活动状态:CAN控制器正在参与总线通信
- 错误被动状态:错误计数器>127,控制器限制发送
- 总线关闭状态:错误计数器>255,控制器停止发送
-
错误类型
- 位错误:发送与回读值不一致
- 填充错误:违反位填充规则
- CRC错误:帧校验失败
- 格式错误:帧格式不符合规范
- ACK错误:未收到ACK位
-
错误处理代码
/* 检查CAN控制器状态 */
void CheckCANStatus(void)
{
uint32_t status = HAL_CAN_GetState(&hcan);
switch (status)
{
case HAL_CAN_STATE_READY:
printf("CAN状态: 就绪\r\n");
break;
case HAL_CAN_STATE_ERROR:
printf("CAN状态: 错误\r\n");
printf("错误代码: 0x%08X\r\n", hcan.ErrorCode);
break;
case HAL_CAN_STATE_ERROR_PASSIVE:
printf("CAN状态: 错误被动\r\n");
printf("错误计数器(TEC/REC): %d/%d\r\n",
hcan.ErrorCode & 0xFF, (hcan.ErrorCode >> 8) & 0xFF);
break;
case HAL_CAN_STATE_BUSOFF:
printf("CAN状态: 总线关闭\r\n");
/* 尝试恢复总线 */
HAL_CAN_Reset(&hcan);
CAN_Start();
break;
default:
printf("CAN状态: 未知 (0x%08X)\r\n", status);
break;
}
}
/* CAN错误回调函数 */
void HAL_CAN_ErrorCallback(CAN_HandleTypeDef *hcan)
{
printf("CAN错误回调 - 错误代码: 0x%08X\r\n", hcan->ErrorCode);
/* 错误代码解析 */
if (hcan->ErrorCode & HAL_CAN_ERROR_BOF)
printf("错误类型: 总线关闭\r\n");
if (hcan->ErrorCode & HAL_CAN_ERROR_STF)
printf("错误类型: 帧起始错误\r\n");
if (hcan->ErrorCode & HAL_CAN_ERROR_EOF)
printf("错误类型: 帧结束错误\r\n");
/* 其他错误类型... */
}
五、过滤器与状态监控的最佳实践
-
过滤器配置建议
- 优先使用掩码模式,减少过滤器组占用
- 将频繁接收的ID放在靠前的过滤器组
- 合理分配FIFO0和FIFO1,平衡负载
-
状态监控建议
- 定期检查CAN控制器状态
- 实现完善的错误处理机制
- 记录错误日志,便于故障排查
-
调试技巧
- 使用示波器观察CAN_H和CAN_L波形
- 利用CAN分析仪捕获总线上的所有帧
- 在关键位置添加断点,检查变量状态
通过灵活配置CAN过滤器和监控控制器状态,可大幅提高CAN FD通信的可靠性和效率,满足工业自动化、汽车电子等领域的高要求应用场景。
/* Configure reception filter to Rx FIFO 0 */
FDCAN_FilterTypeDef sFilterConfig;
sFilterConfig.IdType = FDCAN_STANDARD_ID;
sFilterConfig.FilterIndex = 0U;
sFilterConfig.FilterType = FDCAN_FILTER_MASK;
sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;
sFilterConfig.FilterID1 = TX_ID;
sFilterConfig.FilterID2 = 0x7FF;
if (HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig) != HAL_OK)
{
Error_Handler();
}
这段代码是针对STM32微控制器的FDCAN(Flexible Data Rate Controller Area Network)外设配置接收过滤器的操作。FDCAN是CAN协议的增强版本,支持更高的数据传输速率和更大的数据长度。以下是对代码的详细解释:
### **代码功能概述**
这段代码配置了一个FDCAN接收过滤器,用于筛选进入的CAN帧并将匹配的帧放入RX FIFO 0中。过滤器使用掩码模式,只允许特定ID的标准帧通过。
### **配置参数详解**
#### **1. 过滤器基础配置**
```c
FDCAN_FilterTypeDef sFilterConfig;
FDCAN_FilterTypeDef
:STM32 HAL库中定义的FDCAN过滤器配置结构体
sFilterConfig
:用于存储过滤器配置参数的实例
2. 过滤器ID类型配置
sFilterConfig.IdType = FDCAN_STANDARD_ID;
IdType
:指定过滤器处理的ID类型
FDCAN_STANDARD_ID
:标准ID(11位)
FDCAN_EXTENDED_ID
:扩展ID(29位)
- 此处配置为标准ID,因此过滤器只处理11位ID的CAN帧
3. 过滤器索引配置
sFilterConfig.FilterIndex = 0U;
FilterIndex
:指定过滤器的编号(0-63)
- FDCAN控制器通常有多个过滤器(如64个)
- 此处使用过滤器0,是第一个过滤器
4. 过滤器类型配置
sFilterConfig.FilterType = FDCAN_FILTER_MASK;
FilterType
:指定过滤器的工作模式
FDCAN_FILTER_MASK
:掩码模式(ID与掩码配合使用)
FDCAN_FILTER_DUAL
:双ID模式(匹配两个ID之一)
- 此处使用掩码模式,允许灵活匹配一组ID
5. 过滤器动作配置
sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;
FilterConfig
:指定匹配后的帧存放位置
FDCAN_FILTER_TO_RXFIFO0
:放入RX FIFO 0
FDCAN_FILTER_TO_RXFIFO1
:放入RX FIFO 1
FDCAN_FILTER_REJECT
:拒绝该帧
- 此处配置为放入RX FIFO 0
6. 过滤器ID和掩码值配置
sFilterConfig.FilterID1 = TX_ID;
sFilterConfig.FilterID2 = 0x7FF;
FilterID1
:过滤器ID值(参考值)
- 此处为
TX_ID
,假设为发送方ID(如0x123)
FilterID2
:过滤器掩码值
- 0x7FF(二进制
11111111111
)表示11位全部需要匹配
- 掩码规则:
1
表示对应ID位必须匹配 FilterID1
中的值
0
表示对应ID位可以忽略(不关心)
过滤器工作原理示例
假设:
TX_ID = 0x123
(二进制 001 0010 0011
)
- 掩码
0x7FF
(二进制 111 1111 1111
)
则该过滤器将匹配且仅匹配ID为0x123的标准帧。任何其他ID的帧都将被过滤掉。
这段代码创建了一个过滤器:
- 仅处理标准ID(11位)的CAN帧
- 使用掩码模式进行匹配
- 将匹配的帧放入RX FIFO 0
- 精确匹配ID为
TX_ID
的CAN帧(因为掩码为0x7FF,所有位都必须匹配)
这种配置常用于只接收特定发送方的CAN帧,提高通信效率和安全性。
if (HAL_FDCAN_ConfigGlobalFilter(&hfdcan1,
FDCAN_REJECT, FDCAN_REJECT,
FDCAN_FILTER_REMOTE, FDCAN_FILTER_REMOTE) != HAL_OK)
{
Error_Handler();
}
/* Prepare Tx message Header */
txHeader.Identifier = TX_ID;
txHeader.IdType = FDCAN_STANDARD_ID;
txHeader.TxFrameType = FDCAN_DATA_FRAME;
txHeader.DataLength = FDCAN_DLC_BYTES_16;
txHeader.ErrorStateIndicator = FDCAN_ESI_ACTIVE;
txHeader.BitRateSwitch = FDCAN_BRS_ON;
txHeader.FDFormat = FDCAN_FD_CAN;
txHeader.TxEventFifoControl = FDCAN_NO_TX_EVENTS;
txHeader.MessageMarker = 0U;
### **FDCAN全局过滤器配置解析**
#### **1. HAL_FDCAN_ConfigGlobalFilter() 函数**
```c
if (HAL_FDCAN_ConfigGlobalFilter(&hfdcan1,
FDCAN_REJECT, FDCAN_REJECT,
FDCAN_FILTER_REMOTE, FDCAN_FILTER_REMOTE) != HAL_OK)
{
Error_Handler();
}
这个函数配置FDCAN控制器的全局过滤策略,控制未被任何过滤器组匹配的帧如何处理。参数含义:
-
FDCAN_REJECT, FDCAN_REJECT
分别配置标准帧和扩展帧的默认处理方式:
FDCAN_REJECT
:拒绝(丢弃)未匹配的帧
FDCAN_ACCEPT
:接受并放入RX FIFO 0
-
FDCAN_FILTER_REMOTE, FDCAN_FILTER_REMOTE
分别配置标准远程帧和扩展远程帧的处理方式:
FDCAN_FILTER_REMOTE
:过滤掉远程帧(只接收数据帧)
FDCAN_FILTER_DATA
:不过滤远程帧
配置效果:
- 所有未被特定过滤器匹配的帧都会被丢弃
- 不接收任何远程帧(Remote Transmission Request, RTR)
FDCAN发送消息头配置解析
2. 发送消息头结构体初始化
txHeader.Identifier = TX_ID;
txHeader.IdType = FDCAN_STANDARD_ID;
txHeader.TxFrameType = FDCAN_DATA_FRAME;
txHeader.DataLength = FDCAN_DLC_BYTES_16;
txHeader.ErrorStateIndicator = FDCAN_ESI_ACTIVE;
txHeader.BitRateSwitch = FDCAN_BRS_ON;
txHeader.FDFormat = FDCAN_FD_CAN;
txHeader.TxEventFifoControl = FDCAN_NO_TX_EVENTS;
txHeader.MessageMarker = 0U;
关键参数详解:
-
Identifier = TX_ID
- 帧标识符(标准帧为11位,扩展帧为29位)
- 示例中假设
TX_ID
为预定义的发送ID(如0x123)
-
IdType = FDCAN_STANDARD_ID
- 帧ID类型:
FDCAN_STANDARD_ID
:标准11位ID
FDCAN_EXTENDED_ID
:扩展29位ID
-
TxFrameType = FDCAN_DATA_FRAME
- 帧类型:
FDCAN_DATA_FRAME
:数据帧(携带数据)
FDCAN_REMOTE_FRAME
:远程帧(请求数据)
-
DataLength = FDCAN_DLC_BYTES_16
- 数据长度码(Data Length Code, DLC):
- 传统CAN:支持0-8字节
- CAN FD:支持0-64字节(需通过
FDCAN_DLC_*
宏定义)
- 此处配置为16字节数据
-
ErrorStateIndicator = FDCAN_ESI_ACTIVE
- 错误状态指示位(Error State Indicator, ESI):
FDCAN_ESI_ACTIVE
:主动错误状态(发送显性位)
FDCAN_ESI_PASSIVE
:被动错误状态(发送隐性位)
-
BitRateSwitch = FDCAN_BRS_ON
- 比特率切换(Bit Rate Switch, BRS):
FDCAN_BRS_ON
:启用数据段更高波特率(CAN FD特性)
FDCAN_BRS_OFF
:保持统一波特率
-
FDFormat = FDCAN_FD_CAN
- 帧格式:
FDCAN_FD_CAN
:CAN FD格式(支持DLC扩展和BRS)
FDCAN_CLASSIC_CAN
:传统CAN格式
-
TxEventFifoControl = FDCAN_NO_TX_EVENTS
- 发送事件控制:
FDCAN_NO_TX_EVENTS
:不产生发送完成事件
FDCAN_TX_EVENTS
:产生发送完成事件到TX FIFO
-
MessageMarker = 0U
全局过滤器效果:
- 只接收被特定过滤器明确允许的帧
- 完全拒绝远程帧请求
发送帧特性:
- 使用标准11位ID(TX_ID)
- 发送16字节数据的CAN FD帧
- 启用比特率切换(数据段使用更高波特率)
- 发送节点处于主动错误状态
- 不使用发送事件通知
这种配置适合需要高效传输大量数据的场景(如汽车电子、工业自动化),同时通过严格的过滤机制提高通信安全性。
if (HAL_FDCAN_ConfigTxDelayCompensation(&hfdcan1,
(hfdcan1.Init.DataPrescaler * hfdcan1.Init.DataTimeSeg1), 0U) != HAL_OK)
{
Error_Handler();
}
if (HAL_FDCAN_EnableTxDelayCompensation(&hfdcan1) != HAL_OK)
{
Error_Handler();
}
/* Start FDCAN controller */
if (HAL_FDCAN_Start(&hfdcan1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* Polling mode: Wait for one message received */
while (HAL_FDCAN_GetRxFifoFillLevel(&hfdcan1, FDCAN_RX_FIFO0) < 1U)
{
/* Do nothing, wait */
}
/* Retrieve Rx message from RX FIFO0 */
if (HAL_FDCAN_GetRxMessage(&hfdcan1, FDCAN_RX_FIFO0, &rxHeader, rxData) != HAL_OK)
{
Error_Handler();
}
/* Compare received RX message to expected data. Ignore if not matching. */
if ((rxHeader.Identifier == RX_ID) &&
(rxHeader.IdType == FDCAN_STANDARD_ID) &&
(rxHeader.DataLength == FDCAN_DLC_BYTES_16) &&
(BufferCmp8b(txData, rxData, COUNTOF(rxData)) == 0U))
{
/* Turn LED1 on */
BSP_LED_On(LED1);
}
/* USER CODE END WHILE */
上述代码是STM32中FDCAN(灵活数据速率CAN)的典型配置与应用,主要实现了**发送延迟补偿配置、控制器启动、轮询接收消息及数据验证**功能。以下是对代码的逐行详细解析:
### **一、发送延迟补偿配置**
#### 1. `HAL_FDCAN_ConfigTxDelayCompensation` 函数
```c
if (HAL_FDCAN_ConfigTxDelayCompensation(&hfdcan1,
(hfdcan1.Init.DataPrescaler * hfdcan1.Init.DataTimeSeg1), 0U) != HAL_OK)
{
Error_Handler();
}
- 功能:配置发送延迟补偿参数,用于补偿CAN总线上的信号传播延迟和收发器延迟。
- 参数解析:
&hfdcan1
:FDCAN控制器句柄。
(hfdcan1.Init.DataPrescaler * hfdcan1.Init.DataTimeSeg1)
:
DataPrescaler
:数据段波特率分频器,用于计算数据位时间的基础时钟分频。
DataTimeSeg1
:数据段时间段1(TSEG1),决定数据位时间的采样点位置。
- 两者乘积表示数据段的时间单位数,用于动态调整发送延迟补偿值。
0U
:固定延迟补偿值(单位:时间量子),通常用于补偿RX/TX引脚间的硬件延迟(本例未使用,设为0)。
- 作用:确保发送节点根据总线延迟调整发送时序,避免因信号传播延迟导致的采样错误。
2. HAL_FDCAN_EnableTxDelayCompensation
函数
if (HAL_FDCAN_EnableTxDelayCompensation(&hfdcan1) != HAL_OK)
{
Error_Handler();
}
- 功能:启用发送延迟补偿功能。
- 作用:使能后,FDCAN控制器会根据配置的补偿参数自动调整发送相位,提升高速通信(如2Mbps)的可靠性。
二、FDCAN控制器启动
if (HAL_FDCAN_Start(&hfdcan1) != HAL_OK)
{
Error_Handler();
}
- 功能:启动FDCAN控制器,使其进入工作状态。
- 执行流程:
1. 初始化控制器内部寄存器,激活通信逻辑。
2. 使能接收和发送功能,开始监听CAN总线。
3. 应用之前配置的波特率、过滤器、延迟补偿等参数。
- 注意:必须在完成所有配置(如波特率、过滤器、补偿)后调用,否则可能导致通信异常。
三、轮询模式接收处理
1. 等待接收消息
while (HAL_FDCAN_GetRxFifoFillLevel(&hfdcan1, FDCAN_RX_FIFO0) < 1U)
{
/* 空循环等待 */
}
- 功能:通过轮询方式检查RX FIFO 0中的消息数量,直到有消息到达(填充水平≥1)。
- 关键函数:
HAL_FDCAN_GetRxFifoFillLevel
:获取指定RX FIFO(本例为FIFO0)中的消息数量。
- 适用场景:简单应用或低通信负载场景,无需中断响应,代码逻辑直观但可能占用CPU资源。
2. 读取接收消息
if (HAL_FDCAN_GetRxMessage(&hfdcan1, FDCAN_RX_FIFO0, &rxHeader, rxData) != HAL_OK)
{
Error_Handler();
}
- 功能:从RX FIFO 0中读取一条消息。
- 参数解析:
FDCAN_RX_FIFO0
:目标接收FIFO(与过滤器配置的FIFO一致)。
&rxHeader
:用于存储消息头部信息(ID、类型、长度等)。
rxData
:用于存储消息的数据内容(最大64字节)。
- 返回值:成功时返回
HAL_OK
,失败时触发错误处理(如FIFO为空或读取超时)。
3. 消息验证与响应
if ((rxHeader.Identifier == RX_ID) &&
(rxHeader.IdType == FDCAN_STANDARD_ID) &&
(rxHeader.DataLength == FDCAN_DLC_BYTES_16) &&
(BufferCmp8b(txData, rxData, COUNTOF(rxData)) == 0U))
{
BSP_LED_On(LED1); // 验证通过,点亮LED1
}
- 验证条件解析:
1.
rxHeader.Identifier == RX_ID
:检查接收帧的ID是否为预期值(如预定义的 RX_ID
)。
2. rxHeader.IdType == FDCAN_STANDARD_ID
:确保接收帧为标准ID格式(11位)。
3. rxHeader.DataLength == FDCAN_DLC_BYTES_16
:验证数据长度为16字节(CAN FD支持0-64字节)。
4. BufferCmp8b(txData, rxData, ...)
:比较接收数据与发送数据是否一致(BufferCmp8b
为自定义字节数组比较函数,返回0表示相等)。
- 响应动作:若所有条件满足,点亮LED1表示接收成功;否则忽略该消息。
四、关键注意事项
-
发送延迟补偿的必要性:
- 在高速CAN FD通信中(如数据段波特率≥1Mbps),线缆长度、收发器延迟等会导致信号到达接收端时相位偏移,延迟补偿通过调整发送时序确保采样点准确。
- 补偿值需根据实际硬件参数计算(如线缆延迟纳秒级转换为时间量子数)。
-
轮询模式的局限性:
- 轮询会持续占用CPU资源,若总线负载较高,可能导致消息处理不及时。
- 推荐在中断模式下处理接收(通过
HAL_FDCAN_ActivateNotification
使能接收中断),提升实时性。
-
数据验证的完整性:
- 工业场景中,干扰可能导致ID、数据长度或内容错误,多重验证(ID、类型、长度、内容)可避免误处理。
- 对于关键数据,建议添加CRC校验或序列编号。
-
FIFO管理:
- STM32的FDCAN通常有2个RX FIFO(FIFO0和FIFO1),每个可存储多个消息。
- 需及时读取FIFO中的消息,避免溢出(可通过
HAL_FDCAN_GetRxFifoStatus
检查溢出状态)。
五、代码优化
-
切换为中断接收:
// 使能接收中断(在初始化后调用)
if (HAL_FDCAN_ActivateNotification(&hfdcan1, FDCAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK)
{
Error_Handler();
}
// 实现中断回调函数
void HAL_FDCAN_RxFifo0MsgPendingCallback(FDCAN_HandleTypeDef *hfdcan)
{
HAL_FDCAN_GetRxMessage(hfdcan, FDCAN_RX_FIFO0, &rxHeader, rxData);
// 执行消息验证和处理
}
-
错误处理增强:
- 在轮询中添加超时机制,避免程序卡死。
- 记录错误类型(如
hfdcan1.ErrorCode
),便于调试。
-
动态补偿值计算:
// 动态计算补偿值(示例)
uint32_t TxDelayCompensation = hfdcan1.Init.DataPrescaler *
(hfdcan1.Init.DataSyncJumpWidth + hfdcan1.Init.DataTimeSeg1);
HAL_FDCAN_ConfigTxDelayCompensation(&hfdcan1, TxDelayCompensation, 1U); // 假设固定延迟1个TQ
- 根据实际硬件延迟调整固定补偿值(如
1U
表示1个时间量子)。
-
-
- 在debug中查看数据
