3. 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;
}
运行与验证
- 编译烧录后调用
FDCAN_Config(),用FDCAN_VerifyBitrate()确认反算波特率等于 500000 / 2000000。 - 示波器接 CAN_H/CAN_L(差分探头),发送一帧标准数据帧,触发在帧起始下降沿。
- 仲裁段:测量帧起始到 BRS 位前的单个位宽,应约 2 µs(1/500k)。
- 数据段:测量 BRS 之后的位宽,应约 0.5 µs(1/2M)。
- 通过条件:实测位宽与理论值偏差在 ±5% 内,且采样点位置与配置一致(仲裁段约 87.5%、数据段约 75%)。
常见问题
- 数据段位宽仍为 2 µs:
FrameFormat未设为FDCAN_FRAME_FD_BRS,BRS 未生效。 - 通信报错:Seg1/Seg2 超出寄存器范围(Seg1≤256、Seg2≤128),或 SJW 大于 Seg2。
- 采样点偏差:
sp_permille取值需与总线其他节点一致,否则采样点不匹配导致错误帧。