uuguoleilei 发表于 2025-5-23 20:34

【STM32C092RC 测评】canfd的demo

本帖最后由 uuguoleilei 于 2025-5-23 20:47 编辑

根据透视图可以知道 can的引脚是PD0和PD1

!(data/attachment/forum/202505/23/200251jdfjiph0m0hh0j6p.png "image.png")

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

!(data/attachment/forum/202505/23/200723krwvxnbqvr1hsrbh.png "image.png")

看起来是的

!(data/attachment/forum/202505/23/200807wxxxtlxnsldnxxln.png "image.png")

#### 一、硬件连接与电路设计

STM32C092的CAN FD通信需要正确的硬件连接,主要包括以下部分:

1. **引脚分配**

   - CAN_RX: PD0 (复用功能AF7)
   - CAN_TX: PD1 (复用功能AF7)
   - STANDBY: PD2 (普通输出,低电平使能CAN控制器)
2. **CAN收发器连接**

   - CANH和CANL之间必须连接120Ω终端电阻(高速应用)
   - 建议使用隔离型收发器(如ISO1050)提高抗干扰能力
   - STANDBY引脚控制:低电平→正常工作,高电平→待机模式
3. **电源与滤波**

   - 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 = {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 = 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;

/* 从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);
}
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();
}
}
```

#### 五、接收中断处理机制

1. **中断使能流程**

   - 通过 `HAL_CAN_ActivateNotification()`使能指定中断
   - 本例中启用了 `CAN_IT_RX_FIFO0_MSG_PENDING`(FIFO0消息挂起中断)
   - 中断服务函数由HAL库自动调用回调函数 `HAL_CAN_RxFifo0MsgPendingCallback()`
2. **接收数据处理**

   - 使用 `HAL_CAN_GetRxMessage()`从FIFO读取数据
   - 检查消息ID、数据长度和其他属性
   - 处理接收到的数据(如存储、解析或转发)
3. **FIFO管理**

   - STM32C092的CAN控制器有两个接收FIFO(FIFO0和FIFO1)
   - 每个FIFO可存储3个CAN消息
   - 需及时处理接收的数据,避免FIFO溢出

#### 六、循环发送配置与实现

1. **发送流程**

   - 配置 `CAN_TxHeaderTypeDef`结构体设置消息参数
   - 填充数据缓冲区
   - 调用 `HAL_CAN_AddTxMessage()`将消息添加到发送邮箱
   - 硬件自动处理发送过程
2. **发送参数说明**

   - `StdId/ExtId`: 消息ID(标准帧/扩展帧)
   - `IDE`: 帧格式(标准帧/扩展帧)
   - `RTR`: 帧类型(数据帧/远程帧)
   - `DLC`: 数据长度(0-64字节,CAN FD支持)
   - `BRS`: 比特率切换(启用后数据段使用更高波特率)
3. **发送状态管理**

   - 可通过 `HAL_CAN_GetTxMailboxesFreeLevel()`检查可用发送邮箱数量
   - 发送失败时(如邮箱已满)需进行错误处理
   - 支持自动重传(需启用 `AutoRetransmission`)

#### 七、配置注意事项与常见问题

1. **波特率配置**

   - 确保标称位时间和数据位时间配置合理
   - 使用在线波特率计算器验证配置
   - 总线上所有节点的标称波特率必须一致
2. **硬件注意事项**

   - 终端电阻是高速CAN通信的关键,必须正确连接
   - STANDBY引脚控制不当会导致CAN控制器不工作
   - 检查CAN收发器的电源和使能条件
3. **软件调试建议**

   - 使用CAN分析仪监测实际通信波形
   - 检查CAN状态寄存器(如ESR、MSR)获取错误信息
   - 添加调试输出,监控初始化和通信过程
4. **兼容性问题**

   - CAN FD设备可与传统CAN设备通信,但会退化为传统CAN模式
   - 若总线上有传统CAN节点,需考虑禁用BRS功能
   - 数据长度超过8字节时,传统CAN设备会忽略多余数据

#### 八、性能优化与高级应用

1. **中断处理优化**

   - 避免在中断处理函数中执行耗时操作
   - 使用队列缓存接收到的数据,在主循环中处理
   - 合理设置中断优先级,避免干扰关键任务
2. **错误处理机制**

   - 实现完整的错误处理函数
   - 监控错误计数器,检测总线异常
   - 实现总线离线恢复策略
3. **高级应用场景**

   - 实现多帧数据传输协议(如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范围内的标准帧

```c
sFilterConfig.FilterIdHigh = 0x0100 << 5;// 参考ID: 0x100
sFilterConfig.FilterIdLow = 0x0000;
sFilterConfig.FilterMaskIdHigh = 0xF800;   // 只匹配高3位(0x1xx)
sFilterConfig.FilterMaskIdLow = 0xFFFF;
```

**示例3**:接收所有扩展帧

```c
sFilterConfig.FilterIdHigh = 0x0000;
sFilterConfig.FilterIdLow = 0x2000;      // IDE位=1(扩展帧)
sFilterConfig.FilterMaskIdHigh = 0x0000;   // 忽略ID内容
sFilterConfig.FilterMaskIdLow = 0x2000;    // 只匹配IDE位
```

#### **三、过滤器代码实现**

以下是完整的过滤器配置代码,包含多种过滤模式示例:

```c
/* 配置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控制器状态与错误处理**

1. **CAN控制器状态**

   - **准备就绪状态**:CAN控制器初始化完成,可接收/发送
   - **总线活动状态**:CAN控制器正在参与总线通信
   - **错误被动状态**:错误计数器>127,控制器限制发送
   - **总线关闭状态**:错误计数器>255,控制器停止发送
2. **错误类型**

   - 位错误:发送与回读值不一致
   - 填充错误:违反位填充规则
   - CRC错误:帧校验失败
   - 格式错误:帧格式不符合规范
   - ACK错误:未收到ACK位
3. **错误处理代码**

```c
/* 检查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");

/* 其他错误类型... */
}
```

#### **五、过滤器与状态监控的最佳实践**

1. **过滤器配置建议**

   - 优先使用掩码模式,减少过滤器组占用
   - 将频繁接收的ID放在靠前的过滤器组
   - 合理分配FIFO0和FIFO1,平衡负载
2. **状态监控建议**

   - 定期检查CAN控制器状态
   - 实现完善的错误处理机制
   - 记录错误日志,便于故障排查
3. **调试技巧**

   - 使用示波器观察CAN_H和CAN_L波形
   - 利用CAN分析仪捕获总线上的所有帧
   - 在关键位置添加断点,检查变量状态

通过灵活配置CAN过滤器和监控控制器状态,可大幅提高CAN FD通信的可靠性和效率,满足工业自动化、汽车电子等领域的高要求应用场景。

```

```

```c
/* 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类型配置**

```c
sFilterConfig.IdType       = FDCAN_STANDARD_ID;
```

- `IdType`:指定过滤器处理的ID类型
- `FDCAN_STANDARD_ID`:标准ID(11位)
- `FDCAN_EXTENDED_ID`:扩展ID(29位)
- 此处配置为标准ID,因此过滤器只处理11位ID的CAN帧

#### **3. 过滤器索引配置**

```c
sFilterConfig.FilterIndex= 0U;
```

- `FilterIndex`:指定过滤器的编号(0-63)
- FDCAN控制器通常有多个过滤器(如64个)
- 此处使用过滤器0,是第一个过滤器

#### **4. 过滤器类型配置**

```c
sFilterConfig.FilterType   = FDCAN_FILTER_MASK;
```

- `FilterType`:指定过滤器的工作模式
- `FDCAN_FILTER_MASK`:掩码模式(ID与掩码配合使用)
- `FDCAN_FILTER_DUAL`:双ID模式(匹配两个ID之一)
- 此处使用掩码模式,允许灵活匹配一组ID

#### **5. 过滤器动作配置**

```c
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和掩码值配置**

```c
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的帧都将被过滤掉。

这段代码创建了一个过滤器:

1. 仅处理**标准ID**(11位)的CAN帧
2. 使用**掩码模式**进行匹配
3. 将匹配的帧放入**RX FIFO 0**
4. 精确匹配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. 发送消息头结构体初始化**

```c
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;
```

#### **关键参数详解**:

1. **`Identifier = TX_ID`**

   - 帧标识符(标准帧为11位,扩展帧为29位)
   - 示例中假设 `TX_ID`为预定义的发送ID(如0x123)
2. **`IdType = FDCAN_STANDARD_ID`**

   - 帧ID类型:
   - `FDCAN_STANDARD_ID`:标准11位ID
   - `FDCAN_EXTENDED_ID`:扩展29位ID
3. **`TxFrameType = FDCAN_DATA_FRAME`**

   - 帧类型:
   - `FDCAN_DATA_FRAME`:数据帧(携带数据)
   - `FDCAN_REMOTE_FRAME`:远程帧(请求数据)
4. **`DataLength = FDCAN_DLC_BYTES_16`**

   - 数据长度码(Data Length Code, DLC):
   - 传统CAN:支持0-8字节
   - CAN FD:支持0-64字节(需通过 `FDCAN_DLC_*`宏定义)
   - 此处配置为16字节数据
5. **`ErrorStateIndicator = FDCAN_ESI_ACTIVE`**

   - 错误状态指示位(Error State Indicator, ESI):
   - `FDCAN_ESI_ACTIVE`:主动错误状态(发送显性位)
   - `FDCAN_ESI_PASSIVE`:被动错误状态(发送隐性位)
6. **`BitRateSwitch = FDCAN_BRS_ON`**

   - 比特率切换(Bit Rate Switch, BRS):
   - `FDCAN_BRS_ON`:启用数据段更高波特率(CAN FD特性)
   - `FDCAN_BRS_OFF`:保持统一波特率
7. **`FDFormat = FDCAN_FD_CAN`**

   - 帧格式:
   - `FDCAN_FD_CAN`:CAN FD格式(支持DLC扩展和BRS)
   - `FDCAN_CLASSIC_CAN`:传统CAN格式
8. **`TxEventFifoControl = FDCAN_NO_TX_EVENTS`**

   - 发送事件控制:
   - `FDCAN_NO_TX_EVENTS`:不产生发送完成事件
   - `FDCAN_TX_EVENTS`:产生发送完成事件到TX FIFO
9. **`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` 函数

```c
if (HAL_FDCAN_EnableTxDelayCompensation(&hfdcan1) != HAL_OK)
{
Error_Handler();
}
```

- **功能**:启用发送延迟补偿功能。
- **作用**:使能后,FDCAN控制器会根据配置的补偿参数自动调整发送相位,提升高速通信(如2Mbps)的可靠性。

### **二、FDCAN控制器启动**

```c
if (HAL_FDCAN_Start(&hfdcan1) != HAL_OK)
{
Error_Handler();
}
```

- **功能**:启动FDCAN控制器,使其进入工作状态。
- **执行流程**:
1. 初始化控制器内部寄存器,激活通信逻辑。
2. 使能接收和发送功能,开始监听CAN总线。
3. 应用之前配置的波特率、过滤器、延迟补偿等参数。
- **注意**:必须在完成所有配置(如波特率、过滤器、补偿)后调用,否则可能导致通信异常。

### **三、轮询模式接收处理**

#### 1. 等待接收消息

```c
while (HAL_FDCAN_GetRxFifoFillLevel(&hfdcan1, FDCAN_RX_FIFO0) < 1U)
{
/* 空循环等待 */
}
```

- **功能**:通过轮询方式检查RX FIFO 0中的消息数量,直到有消息到达(填充水平≥1)。
- **关键函数**:
- `HAL_FDCAN_GetRxFifoFillLevel`:获取指定RX FIFO(本例为FIFO0)中的消息数量。
- **适用场景**:简单应用或低通信负载场景,无需中断响应,代码逻辑直观但可能占用CPU资源。

#### 2. 读取接收消息

```c
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. 消息验证与响应

```c
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表示接收成功;否则忽略该消息。

### **四、关键注意事项**

1. **发送延迟补偿的必要性**:

   - 在高速CAN FD通信中(如数据段波特率≥1Mbps),线缆长度、收发器延迟等会导致信号到达接收端时相位偏移,延迟补偿通过调整发送时序确保采样点准确。
   - 补偿值需根据实际硬件参数计算(如线缆延迟纳秒级转换为时间量子数)。
2. **轮询模式的局限性**:

   - 轮询会持续占用CPU资源,若总线负载较高,可能导致消息处理不及时。
   - 推荐在中断模式下处理接收(通过 `HAL_FDCAN_ActivateNotification`使能接收中断),提升实时性。
3. **数据验证的完整性**:

   - 工业场景中,干扰可能导致ID、数据长度或内容错误,多重验证(ID、类型、长度、内容)可避免误处理。
   - 对于关键数据,建议添加CRC校验或序列编号。
4. **FIFO管理**:

   - STM32的FDCAN通常有2个RX FIFO(FIFO0和FIFO1),每个可存储多个消息。
   - 需及时读取FIFO中的消息,避免溢出(可通过 `HAL_FDCAN_GetRxFifoStatus`检查溢出状态)。

### **五、代码优化**

1. **切换为中断接收**:

   ```c
   // 使能接收中断(在初始化后调用)
   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);
   // 执行消息验证和处理
   }
   ```

   - 优点:减少CPU占用,适合高频率通信场景。
2. **错误处理增强**:

   - 在轮询中添加超时机制,避免程序卡死。
   - 记录错误类型(如 `hfdcan1.ErrorCode`),便于调试。
3. **动态补偿值计算**:

   ```c
   // 动态计算补偿值(示例)
   uint32_t TxDelayCompensation = hfdcan1.Init.DataPrescaler *
                                 (hfdcan1.Init.DataSyncJumpWidth + hfdcan1.Init.DataTimeSeg1);
   HAL_FDCAN_ConfigTxDelayCompensation(&hfdcan1, TxDelayCompensation, 1U); // 假设固定延迟1个TQ
   ```

   - 根据实际硬件延迟调整固定补偿值(如 `1U`表示1个时间量子)。
   -
   -
   - 在debug中查看数据
   - !(data/attachment/forum/202505/23/204728vib5hb9c7ohiwilb.png "image.png")
页: [1]
查看完整版本: 【STM32C092RC 测评】canfd的demo