CAN总线波特率计算方法

CAN总线波特率的计算基于以下几个关键参数:

1. 波特率计算公式

波特率 = CAN时钟频率 / (Prescaler × (1 + BS1 + BS2))

其中:

  • ​CAN时钟频率​​:代码中为54MHz

  • ​Prescaler​​:预分频值(代码中的PreScale)

  • ​BS1​​:时间段1的时间量子数

  • ​BS2​​:时间段2的时间量子数

2. 代码中的波特率配置示例

以代码中的1Mbps配置为例:

{CAN_BT_SJW_1TQ, CAN_BT_BS1_6TQ, CAN_BT_BS2_2TQ, 6}, // 1M

计算过程:

  • 预分频值(Prescaler) = 6

  • BS1 = 6TQ (时间量子)

  • BS2 = 2TQ (时间量子)

  • 总时间量子数 = 1(Sync_Seg) + BS1 + BS2 = 1 + 6 + 2 = 9TQ

波特率 = 54MHz / (6 × 9) = 54,000,000 / 54 = 1,000,000 bps (1Mbps)

3. 各波特率配置验证

波特率

Prescaler

BS1

BS2

计算式

结果

1M

6

6

2

54MHz/(6×9)=1M

正确

500K

12

7

1

54MHz/(12×9)=500K

正确

250K

27

6

1

54MHz/(27×8)=250K

正确

200K

15

15

2

54MHz/(15×18)=200K

正确

125K

54

6

1

54MHz/(54×8)=125K

正确

100K

60

7

1

54MHz/(60×9)=100K

正确

代码

/**
	CAN初始化
	CAN0->与电池模组BMS通信
	CAN1->与UPS或其他外部设备通信

	CAN使用APB1时钟=54MHz
	
	尽可能地把采样点设置为CiA推荐的值
	CiA		波特率
	75%		>800K
	80%		>500K
	87.5%	<=500K
	CiA计算方式:(1+CAN_BS1)/(1+CAN_BS1+CAN_BS2)
	
	使用CAN波特率计算工具得来
*/

#include "can.h"
#include "main.h"

CAN0_QUEUE Can0_Rcv_Msg;	//接收BUF
CAN0_QUEUE Can0_Snd_Msg;	//发送BUF

CAN1_QUEUE Can1_Rcv_Msg;	//接收BUF
CAN1_QUEUE Can1_Snd_Msg;	//发送BUF

//波特率函数列表
typedef  struct{
	uint8_t   SJW;
	uint8_t   BS1;
	uint8_t   BS2;
	uint16_t  PreScale;
}TCAN_BaudRate;

TCAN_BaudRate  CAN_BaudRateInitTab[]= {      // CLK=54MHz
	{CAN_BT_SJW_1TQ, CAN_BT_BS1_6TQ, CAN_BT_BS2_2TQ, 6},	// 1M
	{CAN_BT_SJW_1TQ, CAN_BT_BS1_7TQ, CAN_BT_BS2_1TQ, 12},	// 500K
	{CAN_BT_SJW_1TQ, CAN_BT_BS1_6TQ, CAN_BT_BS2_1TQ, 27},	// 250K
	{CAN_BT_SJW_1TQ, CAN_BT_BS1_15TQ, CAN_BT_BS2_2TQ, 15},	// 200K
	{CAN_BT_SJW_1TQ, CAN_BT_BS1_6TQ,  CAN_BT_BS2_1TQ, 54},	// 125K
	{CAN_BT_SJW_1TQ, CAN_BT_BS1_7TQ, CAN_BT_BS2_1TQ, 60},	// 100K
};

//单位K
uint32_t CAN_GetBaudRateNum(uint32_t BaudRate)
{
    switch(BaudRate){
        case 1000:return 0;
        case 500 :return 1;
        case 250 :return 2;
        case 200 :return 3;
        case 125 :return 4;
        case 100 :return 5;
        default	 :return 0;
    }
}


void CAN0_Init(uint16_t Baud)
{
    can_parameter_struct			can_parameter;
    can_filter_parameter_struct 	can_filter;
	
//端口设置
    /* enable can clock */
    rcu_periph_clock_enable(RCU_CAN0);
    rcu_periph_clock_enable(RCU_GPIOD);
	rcu_periph_clock_enable(RCU_AF);

    gpio_pin_remap_config(GPIO_CAN0_FULL_REMAP, ENABLE);	
	
    /* configure CAN0 GPIO, CAN0_TX(PD1) and CAN0_RX(PD0) */
    gpio_init(GPIOD, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_1);
    gpio_init(GPIOD, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, GPIO_PIN_0);
	
//功能配置
    /* initialize CAN register */
    can_deinit(CAN0);
    
    /* initialize CAN */
	can_parameter.working_mode = CAN_NORMAL_MODE;		//正常模式
	
    can_parameter.resync_jump_width = CAN_BaudRateInitTab[CAN_GetBaudRateNum(Baud)].SJW;
    can_parameter.time_segment_1 = CAN_BaudRateInitTab[CAN_GetBaudRateNum(Baud)].BS1;
    can_parameter.time_segment_2 = CAN_BaudRateInitTab[CAN_GetBaudRateNum(Baud)].BS2;
    can_parameter.prescaler = CAN_BaudRateInitTab[CAN_GetBaudRateNum(Baud)].PreScale;	//波特率预分频器
	
    can_parameter.time_triggered = DISABLE;				//时间触发通信模式
    can_parameter.auto_bus_off_recovery = ENABLE;		//自动总线关闭恢复
    can_parameter.auto_wake_up = DISABLE;				//自动唤醒模式
    can_parameter.no_auto_retrans = DISABLE;			//自动重传模式禁用
    can_parameter.rec_fifo_overwrite = DISABLE;			//接收 FIFO 覆盖模式
    can_parameter.trans_fifo_order = DISABLE;			//发送先进先出顺序

    can_init(CAN0, &can_parameter);

// 滤波器配置
    /* initialize filter */
    /* CAN0 filter number */
    can_filter.filter_number = 0;						//过滤器号

    /* initialize filter */    
    can_filter.filter_mode = CAN_FILTERMODE_MASK;		//过滤模式、列表或掩码
    can_filter.filter_bits = CAN_FILTERBITS_32BIT;		//过滤器位宽
    can_filter.filter_list_high = 0x0000;				//过滤列表编号高位
    can_filter.filter_list_low = 0x0000;				//过滤列表编号低位
    can_filter.filter_mask_high = 0x0000;				//过滤器掩码数高位
    can_filter.filter_mask_low = 0x0000;  				//过滤器掩码数低位
    can_filter.filter_fifo_number = CAN_FIFO0;			//接收与过滤器关联的 FIFO
    can_filter.filter_enable = ENABLE;					//过滤工作与否
    can_filter_init(&can_filter);

	nvic_priority_group_set(NVIC_PRIGROUP_PRE1_SUB3);
	nvic_irq_enable(CAN0_RX0_IRQn,0,0);

//中断使能
    /* enable CAN receive FIFO0 not empty interrupt */
    can_interrupt_enable(CAN0, CAN_INT_RFNE0);
}


//CAN0接收中断			    
void CAN0_RX0_IRQHandler(void)
{
	can_receive_message_struct Can0_Rcv_Msg_Struct;
	uint8_t CanRxLen, *DestBufPtr, *SourBufPtr;

    /* check the receive message */
    can_message_receive(CAN0, CAN_FIFO0, &Can0_Rcv_Msg_Struct);
	if((Can0_Rcv_Msg_Struct.rx_ff == CAN_FF_EXTENDED)	//扩展帧
	&& (Can0_Rcv_Msg_Struct.rx_ft == CAN_FT_DATA))		//数据帧
	{
		Can0_Rcv_Msg.CanMsg[Can0_Rcv_Msg.Rin].Id.All = Can0_Rcv_Msg_Struct.rx_efid;		//取标示符			
		Can0_Rcv_Msg.CanMsg[Can0_Rcv_Msg.Rin].DLC = Can0_Rcv_Msg_Struct.rx_dlen;		//数据长度 
		
		CanRxLen = Can0_Rcv_Msg.CanMsg[Can0_Rcv_Msg.Rin].DLC;
		
		DestBufPtr = (uint8_t *)&Can0_Rcv_Msg.CanMsg[Can0_Rcv_Msg.Rin].Data.Bytes.Byte1;
		SourBufPtr = (uint8_t *)&Can0_Rcv_Msg_Struct.rx_data[0];
		while (CanRxLen--)						  	//取数据
		{
			*DestBufPtr++ = *SourBufPtr++;	
		}
		Can0_Rcv_Msg.Rin++;
		Can0_Rcv_Msg.Rin = Can0_Rcv_Msg.Rin % CAN_MSGS;
		if(Can0_Rcv_Msg.Nums < CAN_MSGS)
		{
			Can0_Rcv_Msg.Nums++;	
		}
	}
}


void CAN1_Init(uint16_t Baud)
{
    can_parameter_struct			can_parameter;
    can_filter_parameter_struct 	can_filter;

    /* enable can clock */
	rcu_periph_clock_enable(RCU_CAN0);
    rcu_periph_clock_enable(RCU_CAN1);
    rcu_periph_clock_enable(RCU_GPIOB);
	rcu_periph_clock_enable(RCU_AF);
	
	gpio_pin_remap_config(GPIO_CAN1_REMAP, ENABLE);	//重映射PB5,PB6
	
//端口设置
    /* configure CAN1 GPIO, CAN1_TX(PB6) and CAN1_RX(PB5) */
    gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_6);
    gpio_init(GPIOB, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, GPIO_PIN_5);

//功能配置
    /* initialize CAN register */
    can_deinit(CAN1);
    
    /* initialize CAN */
	can_parameter.working_mode = CAN_NORMAL_MODE;		//正常模式
	
    can_parameter.resync_jump_width = CAN_BaudRateInitTab[CAN_GetBaudRateNum(Baud)].SJW;
    can_parameter.time_segment_1 = CAN_BaudRateInitTab[CAN_GetBaudRateNum(Baud)].BS1;
    can_parameter.time_segment_2 = CAN_BaudRateInitTab[CAN_GetBaudRateNum(Baud)].BS2;
    can_parameter.prescaler = CAN_BaudRateInitTab[CAN_GetBaudRateNum(Baud)].PreScale;	//波特率预分频器
	
    can_parameter.time_triggered = DISABLE;				//时间触发通信模式
    can_parameter.auto_bus_off_recovery = ENABLE;		//自动总线关闭恢复
    can_parameter.auto_wake_up = DISABLE;				//自动唤醒模式
    can_parameter.no_auto_retrans = DISABLE;			//自动重传模式禁用
    can_parameter.rec_fifo_overwrite = DISABLE;			//接收 FIFO 覆盖模式
    can_parameter.trans_fifo_order = DISABLE;			//发送先进先出顺序

    can_init(CAN1, &can_parameter);

// 滤波器配置
    /* initialize filter */
    /* CAN1 filter number */
    can_filter.filter_number = 14;						//过滤器号

    /* initialize filter */    
    can_filter.filter_mode = CAN_FILTERMODE_MASK;		//过滤模式、列表或掩码
    can_filter.filter_bits = CAN_FILTERBITS_32BIT;		//过滤器位宽
    can_filter.filter_list_high = 0x0000;				//过滤列表编号高位
    can_filter.filter_list_low = 0x0000;				//过滤列表编号低位
    can_filter.filter_mask_high = 0x0000;				//过滤器掩码数高位
    can_filter.filter_mask_low = 0x0000;  				//过滤器掩码数低位
    can_filter.filter_fifo_number = CAN_FIFO1;			//接收与过滤器关联的 FIFO
    can_filter.filter_enable = ENABLE;					//过滤工作与否
    can_filter_init(&can_filter);

	nvic_priority_group_set(NVIC_PRIGROUP_PRE1_SUB3);
	nvic_irq_enable(CAN1_RX1_IRQn,0,0);

//中断使能
    /* enable CAN receive FIFO1 not empty interrupt */
    can_interrupt_enable(CAN1, CAN_INT_RFNE1);
}


//CAN1接收中断		
//void CAN1_RX0_IRQHandler(void)
void CAN1_RX1_IRQHandler(void)
{
	can_receive_message_struct Can1_Rcv_Msg_Struct;
	uint8_t CanRxLen, *DestBufPtr, *SourBufPtr;

    /* check the receive message */
    can_message_receive(CAN1, CAN_FIFO1, &Can1_Rcv_Msg_Struct);
	if((Can1_Rcv_Msg_Struct.rx_ff == CAN_FF_EXTENDED)	//扩展帧
	&& (Can1_Rcv_Msg_Struct.rx_ft == CAN_FT_DATA))		//数据帧
	{
		Can1_Rcv_Msg.CanMsg[Can1_Rcv_Msg.Rin].Id.All = Can1_Rcv_Msg_Struct.rx_efid;		//取标示符			
		Can1_Rcv_Msg.CanMsg[Can1_Rcv_Msg.Rin].DLC = Can1_Rcv_Msg_Struct.rx_dlen;		//数据长度 
		
		CanRxLen = Can1_Rcv_Msg.CanMsg[Can1_Rcv_Msg.Rin].DLC;
		
		DestBufPtr = (uint8_t *)&Can1_Rcv_Msg.CanMsg[Can1_Rcv_Msg.Rin].Data.Bytes.Byte1;
		SourBufPtr = (uint8_t *)&Can1_Rcv_Msg_Struct.rx_data[0];
		while (CanRxLen--)						  	//取数据
		{
			*DestBufPtr++ = *SourBufPtr++;	
		}
		Can1_Rcv_Msg.Rin++;
		Can1_Rcv_Msg.Rin = Can1_Rcv_Msg.Rin % CAN_MSGS;
		if(Can1_Rcv_Msg.Nums < CAN_MSGS)
		{
			Can1_Rcv_Msg.Nums++;	
		}
	}
}

4. 波特率配置要点

  1. ​同步段(Sync_Seg)​​:固定为1TQ,用于同步

  2. ​时间段1(BS1)​​:包括传播时间段和相位缓冲段1

  3. ​时间段2(BS2)​​:相位缓冲段2

  4. ​同步跳转宽度(SJW)​​:限制重新同步时相位缓冲段的调整量

5. 实际应用中的考虑

  1. ​采样点选择​​:通常建议采样点在75%-85%之间

    • 采样点位置 = (1 + BS1) / (1 + BS1 + BS2)

    以1Mbps配置为例:

    (1 + 6) / (1 + 6 + 2) = 7/9 ≈ 77.8%

  2. ​时钟精度要求​​:CAN总线对时钟精度要求较高,通常需要±0.5%以内的精度

  3. ​总线长度限制​​:更高的波特率意味着更短的最大总线长度

这种波特率配置方法通过预定义表格简化了配置过程,开发者只需选择所需的波特率,代码会自动查找对应的参数组合。



CAN总线采样点详解

采样点是CAN总线通信中一个非常重要的概念,它决定了在哪个时间点读取总线上的电平状态。我来详细解释你提到的这段内容:

1. 采样点的定义

采样点(Sample Point)是指在一个CAN位时间内,控制器实际读取总线电平状态的时间点。正确的采样点设置对可靠通信至关重要。

2. 采样点计算公式

采样点位置 = (1 + BS1) / (1 + BS1 + BS2)

其中:

  • ​1​​ 代表同步段(Sync_Seg),固定为1个时间量子(TQ)

  • ​BS1​​ 是时间段1的时间量子数

  • ​BS2​​ 是时间段2的时间量子数

3. 1Mbps配置示例分析

代码中的1Mbps配置:

{CAN_BT_SJW_1TQ, CAN_BT_BS1_6TQ, CAN_BT_BS2_2TQ, 6}, // 1M

计算过程:

  • 同步段 = 1TQ

  • BS1 = 6TQ

  • BS2 = 2TQ

  • 总位时间 = 1 + 6 + 2 = 9TQ

采样点位置 = (1 + 6) / (1 + 6 + 2) = 7/9 ≈ 77.8%

这意味着在这个配置下,控制器会在位时间的77.8%处采样总线电平。

4. 为什么建议75%-85%

这个范围是基于以下考虑:

  1. ​信号稳定​​:确保信号已经稳定(经过传播延迟和上升/下降时间)

  2. ​噪声容限​​:避开信号边沿可能不稳定的区域

  3. ​行业经验​​:大多数CAN设备和控制器在这个范围内工作最佳

5. 采样点与总线质量的关系

  • ​采样点过早​​:信号可能未稳定,容易受到噪声干扰

  • ​采样点过晚​​:可能错过有效信号窗口,特别是在长距离传输时

  • ​理想采样点​​:应在信号最稳定的中点稍后位置

6. 实际应用中的调整

当遇到通信问题时,可以尝试:

  1. 增加BS1 - 将采样点后移

  2. 减少BS1 - 将采样点前移

  3. 保持总位时间不变的情况下调整BS1/BS2比例

例如,要调整1Mbps配置的采样点到80%:

  • 总位时间保持9TQ

  • 解方程 (1 + BS1)/9 = 0.8 ⇒ BS1 = 6.2 ⇒ 取整6

  • 实际上保持77.8%也是可以接受的,因为已经很接近80%

理解采样点的概念对于CAN总线调试非常重要,特别是在高波特率或长距离通信场景下。



CAN位时间(Bit Time)详解

CAN位时间是指CAN总线传输​​一个数据位​​所需要的时间长度,它是CAN总线通信的基本时间单位。理解位时间对于正确配置CAN总线参数至关重要。

1. 位时间的组成

一个完整的CAN位时间由​​4个段​​组成(按时间顺序):

  1. ​同步段(Sync_Seg)​

    • 固定为1个时间量子(1 TQ)

    • 用于总线节点间的硬同步

    • 边沿跳变应该发生在这个段内

  2. ​传播时间段(Prop_Seg)​

    • 包含在BS1中

    • 补偿总线上的物理延迟

  3. ​相位缓冲段1(Phase_Seg1)​

    • BS1的剩余部分

    • 用于补偿正向的相位误差

  4. ​相位缓冲段2(Phase_Seg2)​

    • 即BS2

    • 用于补偿负向的相位误差

2. 位时间的计算

位时间 = (Sync_Seg + BS1 + BS2) × TQ

= (1 + BS1 + BS2) × TQ

其中:

  • ​TQ(Time Quantum)​​:时间量子,由CAN时钟分频得到

  • ​总TQ数​​:通常选择在8-25个TQ之间

3. 位时间与波特率的关系

波特率 = 1 / 位时间

例如:

  • 1Mbps波特率 → 位时间 = 1μs

  • 500Kbps波特率 → 位时间 = 2μs

4. 位时间的实际意义

  1. ​通信可靠性​​:合适的位时间设置确保在采样点时信号已稳定

  2. ​同步机制​​:CAN总线通过调整相位缓冲段来实现位同步

  3. ​错误检测​​:位时间定义了判断位错误的时间窗口

5. 代码中的位时间示例

以代码中的1Mbps配置为例:

{CAN_BT_SJW_1TQ, CAN_BT_BS1_6TQ, CAN_BT_BS2_2TQ, 6}, // 1M

计算:

  • 预分频值 = 6

  • 时间量子(TQ) = (1/54MHz)×6 ≈ 111.1ns

  • 位时间 = (1+6+2)×111.1ns ≈ 1μs

  • 波特率 = 1/1μs = 1Mbps

6. 位时间调整原则

  1. ​总TQ数​​:通常8-25个TQ,高速率用较少TQ,低速率用较多TQ

  2. ​采样点​​:保持在75%-85%之间

  3. ​同步跳转宽度(SJW)​​:通常设为BS1和BS2中较小的那个

理解位时间是CAN总线配置的基础,它直接影响到通信的可靠性和稳定性。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐