打印
[STM32C0]

【STM32C092RC 测评】canfd的demo

[复制链接]
50|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主

[i=s] 本帖最后由 uuguoleilei 于 2025-5-23 20:47 编辑 [/i]<br /> <br />

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

image.png

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

image.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[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();
}
}

五、接收中断处理机制

  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范围内的标准帧

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控制器状态与错误处理

  1. CAN控制器状态

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

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

/* 检查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通信的可靠性和效率,满足工业自动化、汽车电子等领域的高要求应用场景。

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

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

  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. 发送消息头结构体初始化

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 函数

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

四、关键注意事项

  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. 切换为中断接收

    // 使能接收中断(在初始化后调用)
    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. 动态补偿值计算

    // 动态计算补偿值(示例)
    uint32_t TxDelayCompensation = hfdcan1.Init.DataPrescaler * 
                                  (hfdcan1.Init.DataSyncJumpWidth + hfdcan1.Init.DataTimeSeg1);
    HAL_FDCAN_ConfigTxDelayCompensation(&hfdcan1, TxDelayCompensation, 1U); // 假设固定延迟1个TQ
    • 根据实际硬件延迟调整固定补偿值(如 1U表示1个时间量子)。
    • 在debug中查看数据
    • image.png

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

12

主题

61

帖子

3

粉丝