3. CAN FD位时间参数配置与波特率验证

CAN FD位时间参数配置与波特率验证 学习卡片

任务说明

本案例以 STM32 FDCAN(HAL 库)为例,根据仲裁段 500 kbit/s、数据段 2 Mbit/s 的要求,计算并写入位时间参数,再用示波器测量实际位宽验证配置。核心公式:

  • 位时间 = SyncSeg(1Tq) + Seg1 + Seg2,采样点 = (1 + Seg1) / 总Tq
  • 波特率 = FDCAN 时钟 / 总Tq 数

取时钟 80 MHz、Tq 时钟分频 1(即 80 MHz 作为 Tq 时钟):仲裁段总 Tq = 160 → 500 kbit/s,采样点 87.5%;数据段总 Tq = 40 → 2 Mbit/s,采样点 75%。

位时间参数计算与配置代码

#include "stm32g4xx_hal.h"

/* FDCAN 内核时钟 80 MHz,Tq 时钟 = 80 MHz / (Prescaler=1) */
#define FDCAN_CLK_HZ        80000000UL
#define ARB_BITRATE         500000UL    /* 仲裁段 500 kbit/s */
#define DATA_BITRATE        2000000UL   /* 数据段 2 Mbit/s   */

/* 由目标波特率反推总 Tq 数,再按采样点拆分 Seg1/Seg2 */
typedef struct {
    uint32_t prescaler;   /* 时钟分频,1..512 */
    uint32_t seg1;        /* 传播段+相位段1,1..256 */
    uint32_t seg2;        /* 相位段2,1..128 */
    uint32_t sjw;         /* 同步跳转宽度 */
} BitTiming;

static BitTiming calc_timing(uint32_t clk, uint32_t bitrate, uint32_t sp_permille)
{
    BitTiming bt = {0};
    /* 总 Tq 数 = clk / (prescaler * bitrate),先取 prescaler=1 */
    uint32_t total_tq = clk / bitrate;          /* 80M/500k=160, 80M/2M=40 */
    uint32_t seg1 = (total_tq * sp_permille / 1000) - 1; /* 去掉 SyncSeg */
    uint32_t seg2 = total_tq - 1 - seg1;

    bt.prescaler = 1;
    bt.seg1 = seg1;
    bt.seg2 = seg2;
    bt.sjw  = (seg2 < 4) ? seg2 : 4;            /* SJW 不超过 Seg2 */
    return bt;
}

/* 校验:反算实际波特率与采样点 */
static uint32_t actual_bitrate(uint32_t clk, BitTiming bt)
{
    uint32_t total_tq = 1 + bt.seg1 + bt.seg2;
    return clk / (bt.prescaler * total_tq);
}

void FDCAN_Config(void)
{
    FDCAN_HandleTypeDef hfdcan;
    FDCAN_InitTypeDef  init;

    hfdcan.Instance = FDCAN1;
    init.ClockDivider          = FDCAN_CLOCK_DIV1;
    init.FrameFormat           = FDCAN_FRAME_FD_BRS;   /* 使能位速率切换 */
    init.Mode                  = FDCAN_MODE_NORMAL;
    init.AutoRetransmission    = ENABLE;
    init.TransmitPause         = DISABLE;
    init.ProtocolException     = DISABLE;

    /* 仲裁段:500 kbit/s,采样点 87.5% */
    BitTiming arb = calc_timing(FDCAN_CLK_HZ, ARB_BITRATE, 875);
    init.NominalPrescaler      = arb.prescaler;
    init.NominalSyncJumpWidth  = arb.sjw;
    init.NominalTimeSeg1       = arb.seg1;
    init.NominalTimeSeg2       = arb.seg2;

    /* 数据段:2 Mbit/s,采样点 75% */
    BitTiming dat = calc_timing(FDCAN_CLK_HZ, DATA_BITRATE, 750);
    init.DataPrescaler         = dat.prescaler;
    init.DataSyncJumpWidth     = dat.sjw;
    init.DataTimeSeg1          = dat.seg1;
    init.DataTimeSeg2          = dat.seg2;

    if (HAL_FDCAN_Init(&hfdcan) != HAL_OK) {
        Error_Handler();
    }
    HAL_FDCAN_Start(&hfdcan);
}

/* 上电自检:打印/断言实际波特率是否等于目标 */
void FDCAN_VerifyBitrate(void)
{
    BitTiming arb = calc_timing(FDCAN_CLK_HZ, ARB_BITRATE, 875);
    BitTiming dat = calc_timing(FDCAN_CLK_HZ, DATA_BITRATE, 750);
    uint32_t arb_act = actual_bitrate(FDCAN_CLK_HZ, arb);
    uint32_t dat_act = actual_bitrate(FDCAN_CLK_HZ, dat);
    /* 期望:arb_act==500000, dat_act==2000000 */
    (void)arb_act; (void)dat_act;
}

运行与验证

  1. 编译烧录后调用 FDCAN_Config(),用 FDCAN_VerifyBitrate() 确认反算波特率等于 500000 / 2000000。
  2. 示波器接 CAN_H/CAN_L(差分探头),发送一帧标准数据帧,触发在帧起始下降沿。
  3. 仲裁段:测量帧起始到 BRS 位前的单个位宽,应约 2 µs(1/500k)。
  4. 数据段:测量 BRS 之后的位宽,应约 0.5 µs(1/2M)。
  5. 通过条件:实测位宽与理论值偏差在 ±5% 内,且采样点位置与配置一致(仲裁段约 87.5%、数据段约 75%)。

常见问题

  • 数据段位宽仍为 2 µs:FrameFormat 未设为 FDCAN_FRAME_FD_BRS,BRS 未生效。
  • 通信报错:Seg1/Seg2 超出寄存器范围(Seg1≤256、Seg2≤128),或 SJW 大于 Seg2。
  • 采样点偏差:sp_permille 取值需与总线其他节点一致,否则采样点不匹配导致错误帧。