本帖最后由 MindMotion 于 2023-11-14 11:58 编辑
1. FlexCAN简介
FlexCAN模块是一个通信控制器,扩展了CAN FD功能,遵循ISO 11898-1标准、CAN FD和CAN 2.0B协议规范。 CAN协议主要被设计用作车载串行总线,满足实时处理,带宽要求,车辆在电磁干扰环境下的可靠操作,该模块支持标准和扩展帧,支持最大64字节有效负载,传输速率高达 8Mbps,并且具有非常灵活的用于传输和接收的邮箱系统和RxFIFO接收机制。邮箱系统由 32 个报文缓冲区(MB)组成。
2. FlexCAN的功能框图
FlexCAN 的功能框图如下图1所示,包括用于存储报文缓冲区(MB)、接收全局掩码寄存器、接收私有掩码寄存器、接收(Rx) FIFO 过滤器以及接收 FIFO 标识符过滤器的内存。
图1 FlexCAN功能框图
如上图1所示为FlexCAN的功能框图,协议引擎(PE)子模块管理 CAN 总线上的串行通信:
- 请求存取 RAM 接收和传输帧
- 验收接收到的报文
- 进行错误处理
- 检测 CAN FD 报文
控制器主机接口(CHI)子模块负责选择接收和传输的报文缓冲区,以及对报文的仲裁和 ID 匹配算法。
总线接口单元(BIU)子模块控制内部接口总线的访问,建立与 CPU 和其他模块的连接。时钟、地址和数据总线、中断输出、 DMA 都通过 BIU 进行访问。
3. FlexCAN的时钟
如下图2所示为产生 PE 时钟的电路结构。时钟源选择位(CAN_CTRL1.CLKSRC)定义了内部时钟为异步时钟或同步时钟。其中,同步时钟为外设时钟(由APB1总线时钟提供);异步时钟的时钟源可选(细节请参考 RCC 章节 RCC_CFGR2寄存器)。为保证可靠运行,应在模块禁止模式时(CAN_MCR.MDIS 置位)选择时钟源。
图2 FlexCAN的PE时钟结构
4. FlexCAN的协议时序
FlexCAN支持多种方式来设置 CAN 协议所要求的位时序参数。控制寄存器 1(CAN_CTRL1)有各种用于控制位时序参数的字段: PRESDIV、 PROPSEG、 PSEG1、 PSEG2 和 RJW。 CAN 位时序寄存器(CAN_CBT)扩展了 CAN_CTRL1 中 CAN 位时序变量的范围。 CAN_FDCBT 提供了用于 BRS 置位的 CAN FD 帧数据段的位时序变量。
CAN FD 使能时,应始终置位 CAN_CBT.BTF 或 CAN_CTRL2.BTE,并在 CAN_CBT 中配置CAN 位时序变量。
PRESDIV字段(及其扩展范围 EPRESDIV 和用于 CAN FD 报文数据段的 FDPRESDIV)定义了串行时钟(Sclock)的预分频(见下列方程)。串行时钟的周期定义了用于构成 CAN 波形的时间单位 Tq(Time Quantum)。 Tq 为 CAN 引擎所能处理的最小时间单元。
比特率定义了接收或传输 CAN 报文的速率,公式如下:
FlexCAN的位时间可细分为三个部分:
- 同步段(SYNC_SEG): 1Tq 的固定长度;信号边沿出现在该段内
- 时间段 1:包括 CAN 标准的传播段和相位段1。该段可通过设置 CAN_CTRL1 寄存器的PROPSEG和PSEG1 字段来编程,其总和(+2)为 2 ~ 16Tq。当 CAN_CBT.BTF 被置位时,FlexCAN 使用来自 CAN_CBT 寄存器的EPROPSEG和EPSEG1 字段,其总和(+2)为 2~ 96Tq 。对于BRS置位的CAN FD报文 , FlexCAN使用CAN_FDCBT寄存器的FDPROPSEG 和 FDPSEG1 字段,其总和为 2 ~ 39Tq。
- 时间段 2:CAN 标准的相位段2。该段可通过设置CAN_CTRL1寄存器的PSEG2字段来编程,其值(+1)为 2 ~ 8Tq。当CAN_CBT.BTF被置位时, FlexCAN使用来自CAN_CBT寄存器的EPSEG2字段,其值(+1)为 2 ~ 32Tq。对于BRS置位的 CAN FD 报文, FlexCAN使用CAN_FDCBT寄存器的FDPSEG2字段,其值(+1)为 2 ~ 8Tq。时间段2不能小于信息处理时间(IPT),IPT 在 FlexCAN 中为 2Tq。
- 注意事项:FPRESDIV 定义了 BRS 置位的 CAN FD 帧数据比特率部分的PE时钟频率和串行时钟(Sclock)频率之间的比率。 Sclock 周期定义了CAN FD协议数据比特率的 Tq。Sclock 频率 = PE 时钟频率 /(FPRESDIV + 1)注:为避免处理FD帧时出错, FPRESDIV 和PRESDIV(CAN_CBT 或 CAN_CTRL1)请使用相同的值。FPRESDIV 只能在冻结模式下写入,其他模式下被硬件锁定。
如下图3所示为FlexCAN位时间内的段使用 CAN_CTRL1位时序变量的经典 CAN 格式
图3
如下图4所示为FlexCAN FD位时间内的段,使用CAN FD格式的CAN_CBT和CAN_FDCBT位时序变量,其中FlexCAN FD的仲裁段使用经典CAN格式配置位时间,即使用CAN_CBT位时序变量用于配置仲裁段的位时间,FlexCAN FD的可变速率的位时间使用CAN_FDCBT位时序变量配置。
图4
FlexCAN的语法说明如下表1所示:
表1
当采用CAN位作为持续时间的衡量标准时(例如,评估报文中的CAN位事件),一个 CAN 位的外设时钟个数(NumClkBit)为:
其中:
- NumClkBit 为一个 CAN 位的外设时钟个数
- fSYS 为系统(CHI)时钟频率,单位 Hz。
- PSEG1 为 CAN_CTRL1.PSEG1 的值
- PSEG2 为 CAN_CTRL1.PSEG2 的值
- PROPSEG 为 CAN_CTRL1.PROPSEG 的值
- PRESDIV 为 CAN_CTRL1.PRESDIV 的值
上述公式也适用于 CAN 位时序寄存器(CAN_CBT)所述的 CAN 位时间变量。
因此,FlexCAN FD仲裁段使用经典CAN位的速率计算公式为:
FlexCAN FD可变速率数据段的计算公式为:
- fCANCLK为PE时钟,单位Hz
- BITRATEN是由CAN 标称位时间变量计算出的 CAN 位速率,单位bps
- BITRATEF是由CAN 数据位时间变量计算出的CAN位速率,单位bps
- EPSEG1为CAN_CBT.EPSEG1的值(也可使用 CAN_CTRL1.PSEG1)
- EPSEG2 为 CAN_CBT.EPSEG2 的值(也可使用 CAN_CTRL1.PSEG2)
- EPROPSEG 为CAN_CBT.EPROPSEG 的值(也可使用 CAN_CTRL1.PROPSEG)
- EPRESDIV 为CAN_CBT.EPRESDIV 的值(也可使用 CAN_CTRL1.PRESDIV)
那么CAN FD帧标称比特率相应的每个CAN位的外设时钟数量CPCBN为:
CAN FD 帧数据比特率相应的每个CAN位的外设时钟数量CPCBF为:
因此在已知FlexCAN FD时钟和预分频系数以及波特率的情况下可以计算出FlexCAN FD的标称位时间TqN总数和可变速率数据位时间TqF总数,计算公式分别如下所示:
fcanclk / BITRATEN x (EPRESDIV+1) = [1+(EPSEG1+1)+(EPSEG2+1)+(EPROPSEG+1)]
fcanclk / BITRATEF x (FPRESDIV+1) = [1+(FPSEG1+1)+(FPSEG2+1)+FPROPSEG]
根据以上FlexCAN FD的位时序可知可以分别根据标称位时间TqN总数和可变速率数据位时间TqF总数分别计算出其标称位时间的采样点和可变速率数据位时间的采样点,即FlexCAN FD标称位时间的采样点的计算公式为:
FlexCAN_samplepoint = (SYNC_SEG+(EPROPSEG+1) +(EPSEG1+1) )/ (SYNC_SEG+(EPROPSEG+1) +(EPSEG1+1)+ (EPSEG2+1))
FlexCAN FD可变速率位时间的采样点的计算公式为:
FlexCAN_FDsamplepoint = (SYNC_SEG+FPROPSEG+(FPSEG1+1) )/ (SYNC_SEG+FPROPSEG+(FPSEG1+1)+ (FPSEG2+1))
5. FlexCAN FD位时间采样点的计算
根据以上4章节FlexCAN的位时间描述以及FlexCAN FD位时间和采样点的计算公式推导,在给定FlexCAN FD时钟和波特率以及分频系数的情况下,可以使用FlexCAN的库函数通过循环遍历的方式实现自动计算FlexCAN FD的位时间的采样点。
FlexCAN FD库函数定义的协议时序代码如下所示:
typedef struct _flexcan_timing_config {
u16 preDivider; /*!< Clock Pre-scaler Division Factor. */
u8 rJumpwidth; /*!< Re-sync Jump Width. */
u8 phaseSeg1; /*!< Phase Segment 1. */
u8 phaseSeg2; /*!< Phase Segment 2. */
u8 propSeg; /*!< Propagation Segment. */
/*!< FlexCAN has flexible data rate. */
uint16_t fpreDivider; /*!< Fast Clock Pre-scaler Division Factor. */
uint8_t frJumpwidth; /*!< Fast Re-sync Jump Width. */
uint8_t fphaseSeg1; /*!< Fast Phase Segment 1. */
uint8_t fphaseSeg2; /*!< Fast Phase Segment 2. */
uint8_t fpropSeg; /*!< Fast Propagation Segment. */
} flexcan_timing_config_t;*/
FlexCAN FD标称位时间采样点和可变速率采样点的位时间的计算,可以通过循环遍历方式实现自动计算,代码如下所示:
bool FLEXCAN_FDCalculateImprovedTimingValues(uint32_t baudRate, uint32_t baudRateFD, uint32_t sourceClock_Hz, flexcan_timing_config_t *pTimingConfig)
{
bool fgRet = false;
pTimingConfig->preDivider = 0U;
pTimingConfig->fpreDivider = 0U;
if(FLEXCAN_CalculateImprovedTimingValuesByFDCBT(baudRateFD, sourceClock_Hz, pTimingConfig))
{
if(FLEXCAN_CalculateImprovedTimingValuesByCBT(baudRate, sourceClock_Hz, pTimingConfig))
{
fgRet = true;
}
}
return (fgRet);
}
static bool FLEXCAN_CalculateImprovedTimingValuesByFDCBT(uint32_t baudRate, uint32_t sourceClock_Hz, flexcan_timing_config_t *pTimingConfig)
{
uint32_t clk; /* the clock is tqNumb x baudRateFD. */
uint32_t tqNum; /* Numbers of TQ. */
bool fgRet = false;
tqNum = FDCBT_MAX_TIME_QUANTA;
/* Auto Improved Protocal timing. */
do
{
clk = baudRate * tqNum;
if (clk > sourceClock_Hz)
{
continue; /* tqNum too large, clk has been exceed sourceClock_Hz. */
}
if ((sourceClock_Hz / clk * clk) != sourceClock_Hz)
{
continue; /* Non-supporting: the frequency of clock source is not divisible by target baud rate, the user
should change a divisible baud rate. */
}
/* Make sure the new calculated divider value is greater than the previous one. */
if (pTimingConfig->fpreDivider > ((uint16_t)(sourceClock_Hz / clk) - 1U))
{
continue;
}
else
{
pTimingConfig->fpreDivider = (uint16_t)(sourceClock_Hz / clk) - 1U;
}
if (pTimingConfig->fpreDivider > MAX_FPRESDIV)
{
break; /* The frequency of source clock is too large or the baud rate is too small, the pre-divider could
not handle it. */
}
/* Try to get the best timing configuration. */
if (FLEXCAN_FDGetSegmentswithBRS(baudRate, tqNum, pTimingConfig))
{
fgRet = true;
break;
}
}
while(--tqNum >= FDCBT_MIN_TIME_QUANTA);
return (fgRet);
}
static bool FLEXCAN_FDGetSegmentswithBRS(uint32_t baudRatebrs, uint32_t tqNum, flexcan_timing_config_t *pTimingConfig)
{
uint32_t ideal_sp;
uint32_t p1;
bool fgRet = false;
/* get ideal sample point. */
if (baudRatebrs >= 1000000U)
{
ideal_sp = IDEAL_SP_LOW;
}
else if (baudRatebrs >= 800000U)
{
ideal_sp = IDEAL_SP_MID;
}
else
{
ideal_sp = IDEAL_SP_HIGH;
}
/* distribute time quanta. */
p1 = tqNum * (uint32_t)ideal_sp;
pTimingConfig->fpropSeg = (uint8_t)(p1 / (uint32_t)IDEAL_SP_FACTOR - 2U);
if (pTimingConfig->fpropSeg <= (MAX_FPSEG1 + MAX_FPROPSEG))
{
if (pTimingConfig->fpropSeg > MAX_FPROPSEG)
{
pTimingConfig->fphaseSeg1 = pTimingConfig->fpropSeg - MAX_FPROPSEG;
pTimingConfig->fpropSeg = MAX_FPROPSEG;
}
else
{
pTimingConfig->fphaseSeg1 = 0;
}
if(pTimingConfig->fphaseSeg1 <= MAX_PSEG1)
{
if ((pTimingConfig->fpropSeg + pTimingConfig->fphaseSeg1) < ((uint8_t)tqNum - 3U))
{
pTimingConfig->fphaseSeg2 = (uint8_t)tqNum - (pTimingConfig->fphaseSeg1 + pTimingConfig->fpropSeg + 3U);
if (pTimingConfig->fphaseSeg2 <= MAX_PSEG2)
{
if ((pTimingConfig->fphaseSeg1 < pTimingConfig->fphaseSeg2) && (pTimingConfig->fpropSeg > (pTimingConfig->fphaseSeg2 - pTimingConfig->fphaseSeg1)))
{
pTimingConfig->fpropSeg -= (pTimingConfig->fphaseSeg2 - pTimingConfig->fphaseSeg1);
pTimingConfig->fphaseSeg1 = pTimingConfig->fphaseSeg2;
}
/* subtract one TQ for sync seg. */
/* sjw is 20% of total TQ, rounded to nearest int. */
pTimingConfig->frJumpwidth = ((uint8_t)tqNum + 4U) / 5U - 1U;
if (pTimingConfig->frJumpwidth > MAX_FRJW)
{
pTimingConfig->frJumpwidth = MAX_FRJW;
}
fgRet = true;
}
}
}
}
return (fgRet);
}
static bool FLEXCAN_CalculateImprovedTimingValuesByCBT(uint32_t baudRate, uint32_t sourceClock_Hz, flexcan_timing_config_t *pTimingConfig)
{
uint32_t clk; /* the clock is tqNumb x baudRateFD. */
uint32_t tqNum; /* Numbers of TQ. */
bool fgRet = false;
tqNum = CBT_MAX_TIME_QUANTA;
/* Auto Improved Protocal timing. */
do
{
clk = baudRate * tqNum;
if (clk > sourceClock_Hz)
{
continue; /* tqNum too large, clk has been exceed sourceClock_Hz. */
}
if ((sourceClock_Hz / clk * clk) != sourceClock_Hz)
{
continue; /* Non-supporting: the frequency of clock source is not divisible by target baud rate, the user
should change a divisible baud rate. */
}
/* Make sure the new calculated divider value is greater than the previous one. */
if (pTimingConfig->preDivider > ((uint16_t)(sourceClock_Hz / clk) - 1U))
{
continue;
}
else
{
pTimingConfig->preDivider = (uint16_t)(sourceClock_Hz / clk) - 1U;
}
/* To minimize errors when processing FD frames, try to calculate the same value for FPRESDIV and PRESDIV (in CBT). */
if (pTimingConfig->preDivider != pTimingConfig->fpreDivider)
{
continue;
}
if (pTimingConfig->preDivider > MAX_EPRESDIV)
{
break; /* The frequency of source clock is too large or the baud rate is too small, the pre-divider could
not handle it. */
}
/* Try to get the best timing configuration. */
if (FLEXCAN_FDGetSegments(baudRate, tqNum, pTimingConfig))
{
fgRet = true;
break;
}
}
while(--tqNum >= CBT_MIN_TIME_QUANTA);
return (fgRet);
}
FlexCAN FD位时间采样点应用举例,本文示例以MM32F0160的FlexCAN FD外设为例,其中FlexCAN FD的标称波特率为500K,可变速率波特率为2MHz。fCAN的时钟为72MHz,把FlexCAN FD的fCAN时钟,标称波特率500K,可变速率波特率2MHz代入库函数FLEXCAN_FDCalculateImprovedTimingValues分别计算得出FlexCAN FD标称波特率和可变速率波特率的位时间参数如下:
标称波特率的位时间参数:
preDivider = 0x01;
propSeg = 0x34;
phaseSeg1 = 0x08;
phaseSeg2 = 0x08;
rJumpwidth = 0x0E
可变速率波特率位时间参数:
fpreDivider = 0x01;
fpropSeg = 0x07;
fphaseSeg1 = 0x04;
fphaseSeg2 = 0x04;
frJumpwidth = 0x03
注:再同步参数rJumpwidth 和frJumpwidth不参与FlexCANFD采样点的计算
又因为在已知FlexCAN FD时钟和预分频系数,以及波特率的情况下可以计算出FlexCAN FD的标称位时间TqN总数和可变速率数据位时间TqF总数,计算公式分别如下所示:
标称位时间TqN总数:
fcanclk / BITRATEN x (EPRESDIV+1) = [1+(EPSEG1+1)+(EPSEG2+1)+(EPROPSEG+1)]
代入自动计算得出的标称波特率500K的位时间参数,计算出TqN
TqN = 72000000/500000x(1+1)
= 72
可变速率数据位时间TqF总数:
fcanclk / BITRATEF x (FPRESDIV+1) = [1+(FPSEG1+1)+(FPSEG2+1)+FPROPSEG]
代入自动计算得出的可变波特率2MHz的位时间参数,计算出TqF
TqF = 72000000/2000000x(1+1)
= 18
根据以上FlexCAN FD使用库自动计算出的位时间参数,以及标称位时间TqN总数和可变速率位时间TqF总数,可以分别计算出其标称位时间的采样点和可变速率数据位时间的采样点,即FlexCAN FD标称位时间的采样点的计算公式为:
FlexCAN_samplepoint = (SYNC_SEG+(EPROPSEG+1) +(EPSEG1+1) )/ (SYNC_SEG+(EPROPSEG+1) +(EPSEG1+1)+ (EPSEG2+1))
代入标称波特率500K的位时间参数计算标称波特率的位时间的采样点如下:
FlexCAN_samplepoint =(1+(52+1)+(8+1))/(1+(52+1)+(8+1)+(8+1))
= 87.5%
FlexCAN FD可变速率位时间的采样点的计算公式为:
FlexCAN_FDsamplepoint = (SYNC_SEG+FPROPSEG+(FPSEG1+1) )/ (SYNC_SEG+FPROPSEG+(FPSEG1+1)+ (FPSEG2+1))
代入可变速率2MHz波特率的位时间参数计算可变速率的位时间的采样点如下:
FlexCAN_FDsamplepoint = (1+7+(4+1))/(1+7+(4+1)+(4+1))
= 72%
CAN采样点一般设置在75%—80%之间,具体要根据CAN通信波特率大小配置。当波特率不超过500K时,建议采样点设置在87.5%;当波特率大小在500K—800K之间的时候,建议采样点设置在80%;当波特率大于800K的时候,建议采样点设置在75%。以上建议并不是绝对的,需根据应用环境复杂程度通过调整FlexCAN FD的再同步参数rJumpwidth和frJumpwidth使得phaseSeg1和phaseSeg2以及fphaseSeg1和fphaseSeg2在标称波特率和可变波特率的位时间上前移或后移N(0-15)个Tq值来微调采样点(虽然rJumpwidth和frJumpwidth不参与采样点计算)。
注意事项:
关于采样点的配置的一些额外建议:当用户在实际车用环境中出现FlexCAN无正常收发数据时除了根据错误标志查找问题定位问题外,还可使用专业的CAN诊断仪测试出多节点总线上的rJumpwidth和frJumpwidth,phaseSeg1和phaseSeg2以及fphaseSeg1和fphaseSeg2参数,参考这些位时间参数值来调整适配rJumpwidth和frJumpwidth,phaseSeg1和phaseSeg2以及fphaseSeg1和fphaseSeg2参数到CAN通信的合适的采样点。
|