PUYA CANFD — 经典帧 基址 ID 轮询收发

PUYA PY32F403_Firmware V1.4.5 CANFD 经典帧基址 ID 轮询收发(CANFD_Classic_BaseID_polling)示例教学

CANFD — 经典帧 基址 ID 轮询收发

固件库: PY32F403_Firmware V1.4.5
芯片: PY32F403
源文件: PUYA/PY32F403_Firmware_V1.4.5/Projects/PY32F403-STK/Example/CANFD/CANFD_Classic_BaseID_polling/Src/main.c


功能简介

CAN / CAN-FD 是汽车与工业中最可靠的串行总线,采用差分信号(CANH/CANL),多主、带仲裁、抗干扰强。PY32F403 内置 CANFD 控制器,兼容经典 CAN 2.0B 与 CAN-FD。

本示例演示经典帧(Classic,非 FD)模式 + 轮询收发

  • 配置 CANFD 为经典帧、正常模式,波特率 250Kbps(Nominal 段配置);
  • 设置过滤器:只接收 ID 高 11 位为 0x12F 的报文(MaskID = 0 表示仅比较 ID 本身);
  • 往发送 FIFO(PTB)放入一帧 ID=0x123、8 字节数据 {1..8},激活发送;
  • 轮询等待接收 FIFO 非空,读回后用 printf 打印长度与数据。

这是“自收发”验证:同一控制器既发又收(需外部接好终端电阻,或回环模式)。经典帧即普通 CAN 2.0,每帧最多 8 字节;FD 帧可到 64 字节。

硬件准备

  • PY32F403 开发板(带 CANFD 收发器,或外接 TJA1050/MCP2551)
  • CANH、CANL 双绞线,总线两端各接 120Ω 终端电阻
  • 接好调试串口(用于 printf 观察)
  • 单板自测时可启用回环(修改 ModeCANFD_MODE_LOOPBACK

完整代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#include "main.h"

CANFD_HandleTypeDef CanfdHandle;
CANFD_FilterTypeDef CanfdFilter;
CANFD_TxHeaderTypeDef CanfdTxHeader;
uint8_t TxData[] = {0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8};

CANFD_RxHeaderTypeDef CanfdRxHeader;
uint8_t RxData[8] = {0};
uint8_t DLCtoBytes[] = {0,1,2,3,4,5,6,7,8,12,16,20,24,32,48,64}; /* DLC→字节数 */

int main(void)
{
  uint32_t i = 0;
  HAL_Init();
  APP_SystemClockConfig();     /* 系统时钟:HSE */
  BSP_USART_Config();          /* 调试串口(printf 用) */

  /* CANFD 基本配置 */
  CanfdHandle.Instance                   = CANFD;
  CanfdHandle.Init.FrameFormat           = CANFD_FRAME_CLASSIC;  /* 经典帧 */
  CanfdHandle.Init.Mode                  = CANFD_MODE_NORMAL;    /* 正常模式 */
  CanfdHandle.Init.Prescaler             = 1U;
  CanfdHandle.Init.NominalSyncJumpWidth  = 16U;  /* 250KHz */
  CanfdHandle.Init.NominalTimeSeg1       = 64U;
  CanfdHandle.Init.NominalTimeSeg2       = 16U;
  CanfdHandle.Init.DataSyncJumpWidth     = 16U;
  CanfdHandle.Init.DataTimeSeg1          = 64U;
  CanfdHandle.Init.DataTimeSeg2          = 16U;
  CanfdHandle.Init.SecondSamplePointOffset = 0U;
  if (HAL_CANFD_Init(&CanfdHandle) != HAL_OK) APP_ErrorHandler();

  /* 过滤器:仅接收 ID = 0x12F(标准 11 位) */
  CanfdFilter.IdType        = CANFD_STANDARD_ID;
  CanfdFilter.FilterChannel = CANFD_FILTER_CHANNEL_0;
  CanfdFilter.Rank          = CANFD_FILTER_RANK_CHANNEL_NUMBER;
  CanfdFilter.FilterID      = 0x12F;
  CanfdFilter.FilterFormat  = 0xFFFFFFFF;
  CanfdFilter.MaskID        = 0x0;  /* 只比较 ID 本身 */
  CanfdFilter.MaskFormat    = 0xFFFFFFFF;
  if (HAL_CANFD_ConfigFilter(&CanfdHandle, &CanfdFilter) != HAL_OK) APP_ErrorHandler();

  if (HAL_CANFD_Start(&CanfdHandle) != HAL_OK) APP_ErrorHandler();

  /* 组装并发送一帧(PTB 优先发送缓冲区) */
  CanfdTxHeader.Identifier  = 0x123;
  CanfdTxHeader.IdType      = CANFD_STANDARD_ID;
  CanfdTxHeader.TxFrameType = CANFD_DATA_FRAME;
  CanfdTxHeader.FrameFormat = CANFD_FRAME_CLASSIC;
  CanfdTxHeader.Handle      = 0x0;
  CanfdTxHeader.DataLength  = CANFD_DLC_BYTES_8;
  if (HAL_CANFD_AddMessageToTxFifo(&CanfdHandle, &CanfdTxHeader, TxData,
                                    CANFD_TX_FIFO_PTB) != HAL_OK) APP_ErrorHandler();
  if (HAL_CANFD_ActivateTxRequest(&CanfdHandle, CANFD_TXFIFO_PTB_SEND) != HAL_OK)
    APP_ErrorHandler();

  /* 轮询等待接收 FIFO 有数据 */
  while (__HAL_CANFD_GET_RX_FIFO_FILL_LEVEL(&CanfdHandle) == CANFD_RX_FIFO_EMPTY) {}
  if (HAL_CANFD_GetRxMessage(&CanfdHandle, &CanfdRxHeader, RxData) != HAL_OK)
    APP_ErrorHandler();

  /* 打印结果 */
  printf("decimal data length: %d\r\n", DLCtoBytes[CanfdRxHeader.DataLength]);
  printf("hexadecimal data: \r\n");
  for (i = 0; i < DLCtoBytes[CanfdRxHeader.DataLength]; i++)
    printf("%x ", RxData[i]);

  while (1) {}
}

代码分段讲解

1. 经典帧 vs FD 帧

  • CANFD_FRAME_CLASSIC:经典 CAN 2.0,DLC 最大 8 字节。
  • CANFD_FRAME_FD:CAN-FD,支持更长数据段与更高数据波特率(Data 段可独立配置 DataTimeSeg1/2)。

2. 波特率配置(BIT Timing)

CAN 波特率由同步段 + BS1 + BS2 与分频决定。本例 Nominal 段 (SJW16 + TS1 64 + TS2 16) 配合 Prescaler=1 得到 250K。实际配置需对照 PY32F403 参考手册的时钟树(CANFD 时钟源)。

3. 过滤器(Filter)

CANFD 用过滤器决定“哪些报文进 RX FIFO”。MaskID = 0 表示只按 FilterID(0x12F)精确匹配;若想接收某一范围,可设 Mask 屏蔽某些位(如只比较高 8 位)。

4. 发送 FIFO 与轮询

  • AddMessageToTxFifo(..., PTB):把报文放进优先发送缓冲区(PTB),然后 ActivateTxRequest(PTB_SEND) 触发发送。
  • 接收用 __HAL_CANFD_GET_RX_FIFO_FILL_LEVEL 轮询是否有数据,再 GetRxMessage 取出。

实验现象

  • 串口打印:

    1
    2
    3
    
    decimal data length: 8
    hexadecimal data:
    1 2 3 4 5 6 7 8
    
  • 在回环/正常模式下,发出的 8 字节被自身(或另一节点)正确接收。

常见问题

  1. 收不到数据?

    • 检查 CANH/CANL 是否接反、是否接了 120Ω 终端电阻、总线是否正常(示波器看差分波形)。
    • 过滤器 FilterID/MaskID 与发送 ID 不匹配会被硬件丢弃。
    • 单板自测请用 CANFD_MODE_LOOPBACK,可无需外部收发器。
  2. 波特率不对/错误帧多?

    • 仔细核算 BIT Timing 与 CANFD 的时钟源频率;经典帧与 FD 帧的 Nominal/Data 段参数要分别正确。
  3. 想用中断/DMA?

    • 同目录有 CANFD_ExtendedID_IT 等中断版;轮询版最简单,适合入门。

小结

  • CANFD 流程:Init → ConfigFilter → Start → AddMessageToTxFifo → ActivateTxRequest → 等待/读取 Rx
  • 经典帧每帧 ≤8 字节;FD 帧更长更快。
  • 过滤器决定接收范围;总线必须差分接线 + 终端电阻。

拓展应用

  • 汽车/工业节点通信:定义 ID 优先级与报文矩阵。
  • 多节点组网:用过滤器分组,主从轮询采集传感器。
  • 升级为 CAN-FD:提高数据段波特率,传输大块数据(如固件、图像)。
  • 配合中断实现“收到即处理”的实时通信。
世界是你们
使用 Hugo 构建
主题 StackJimmy 设计