需求
CAN总线的波特率一般在5K~1M之间,典型值为5K,10K,20K,50K,125K,250K,500K,1M。要求是做一个函数,根据输入值可以调整CAN的波特率,以适配至少满足典型值的波特率。
CAN基本知识
本来不想说这个的,但不说的话后面的东西没法讲。如果已熟悉的同学,请跳过这一段。
这张图中的数字单位是TQ,也就是CAN总线时钟分频后的时间长度。关于分频后面会讲。
TQ是CAN总线的最小时间单位,若干个TQ组成一个数据段,下图中的数字就是TQ的数量。
那么芯片如何知道这一个TQ是高电平还是低电平呢?是在采样点判断的,在这一时刻如果是高电平就判断为1,低电平则判断为低。
准确的讲是显性电平,还是隐性电平,不过那样又讲麻烦了,简单理解就好。
采样点的位置是可以设置的,下图中TQ总时间是9个时钟节拍,采样点在66.66%,不过建议值是85%,所以下图并不是最优设置。但这个85%吧,可先的范围相当大,从50%到95%问题都不大,所以差不多就好了。我的采样点设置在85%最优点。
另外说一个知识点,在STM32中,将下图合计为2段,PTS+PSB合为Seg1,PSB2为Seg2,SS始终为1,所以一个TQ的时钟节拍为Seg1+Seg2+1,这个后面会讲到。
下图来自正点原子,展示的是一个TQ的宽度,下图中的Tq应为时钟节拍,特此注明。
配置
FDCAN外设的基础时钟为40MHz,也就是说当不分频时,一个时钟节拍为25ns。当一个TQ等于40个节拍时,总线的波特率就是1MHz,达到CAN总线的最高波特率。其他典型的波特率都可以在其基础上分频实现。
一般来说,将仲裁段与数据段的速率,采样点配置为相同会比较合理。
下面就以STM32H750为例,本配置FDCAN外设。将其配置为标准CAN设备,而不是FDCAN,以便和其他设备兼容。
首先使能FDCAN1和FDCAN2
上图是基本参数的设置
Frame Format 帧格式,将其配置为传统模式,这时仲裁段与数据段用相同的波特率。如果配置为FD模式的话,则二者允许不同的波特率。
Mode 工作模式 常规模式,静默模式,环回,静默环回啥的,配置为常规就好了。
Auto Retransmission 自动重传 当发生错误时,会自动重传,根据需要自行决定是否开启。
Transmit Pause 传输暂停,一般设置为禁止,也就是把一帧数据一次性传输完。
Protocol Exception 异常协议的处理,禁止。即不处理异常协议。
Nominal Sync Jump Width 采样点的偏移 2 为什么是2,下文有说明。
Data Prescaler 数据段的时钟分频 1,也就是时钟频率除以1,那么一个节拍就是25ns
Data Time Seg1 数据段的Seg1 31节拍
Data Time Seg2 数据段的Seg2 8节拍
波特率计算
所以数据段的时间长度为1+31+8=40节拍,每个节拍的时间是25ns(前面配置),所以1TQ=25*40=1000ns=1us,总线时钟频率就是1MHz。
采样点的偏移是2,所以采样点在1+31+2=34。 34/40=0.85,所以采样点刚好落在85%处(此处是个人理解,如有误请指正)
Message Ram Offset 接收到的消息在内存中的起始点偏移,设为0,不偏移。
Std Filters Nbr 标准帧过滤器 1
Ext Filters Nbr 扩展帧过滤器 11
Rx Fifo0 Elmts Nbr 接收的Fifo0编号 1
Rx Fifo0 Elmt Size 接收的Fifo0数据长度 8字节
Rx Fifo1 Elmts Nbr 接收的Fifo1编号 11
Rx Fifo1 Elmt Size 接收的Fifo1数据长度 8字节
Rx Buffers Nbr 接收缓冲区编号 1
Rx Buffer Size 接收缓冲区长度 8字节
Tx Events Nbr 发送事件编号 0 不用事件发送,所以这个值无所谓
Tx Buffers Nbr 发送缓冲区编号 11
Tx Fifo Queue Elmts Nbr 发送Fifo队列编号
Tx Fifo Queue Mode 发送模式 Fifo模式,即先入先出
Tx Elmt Size 发送长度 8字节
Clock Calibration 时钟校准,使用外部晶振时,这个可有可无,因为外部晶振已经很准了。用内部RC晶振才需要用到这个,而且温漂很大,不建议用内部晶振。
Bit Timings Parameters 位时序参数设置 个人理解这是仲裁段的时序设置,与前面保持一致就好。
Nominal Prescaler 分频值1 不分频
Nominal Time Seg1 Seg1的长度,单位是时钟节拍
Nominal Time Seg2 Seg2的长度,单位是时钟节拍
再说一遍,1 + Seg1 + Seg2 的数字乘以时钟节拍,就是1TQ的时间长度=1+31+8=40个时钟节拍。
灰字的25ns就是当前时钟频率下,一个时钟节拍的时间。所以1TQ = 25ns * 40时钟节拍 = 1000ns = 1us,即1MHz波特率。
这里是设置仲裁段的采样点,与前面保持一致。
发灰的文字可以看到你设置的节拍值,位时长,波特率。
开启中断0
可变波特率
在设置好最高1MHz的波特率后,其他典型波特率如500K就设置分频为2,200K就设置分频为5,125K就设置分频为8,其他以此类推。
分频数就是用1MHz除以你要的波特率(can_rate),再写入这里就行了。
也可以自己写一个函数,来实现,以实现可变的波特率。
在红圈处用表达式代替原来的数字即可。
但是要注意,需要先点击箭头所指的小齿轮,将其修改为No check,否则无法使用表达式。
CubeMX的Bug
当我添加好表达式之后,却发现CubeMX进入要死机的状态,这在以前是不会的。
强行保存并退出之后,却发现无法再打开该工程文件了。
我用了6.13和最新的6.14版本,均有此问题。
已向ST发邮件报告这个Bug,等ST工程师的回复,后续版本应该能解决这个问题,至少现在不能这样搞。
得自己写一个函数,内容就复制MX_FDCAN1_Init()就好,将相关的2行改成如下
can_rate就是你的目标波特率
软件编写
添加初始化代码
void FDCAN_Config(void)
{
//--------------------------------------------------------------
// FDCAN1 初始化
//--------------------------------------------------------------
FDCAN_FilterTypeDef sFilterConfig;
HAL_FDCAN_ActivateNotification(&hfdcan_out, FDCAN_IT_RX_FIFO0_NEW_MESSAGE, 0);
sFilterConfig.IdType = FDCAN_STANDARD_ID; // 配置为过滤标准帧
sFilterConfig.FilterIndex = 1; // 过滤器的索引号
sFilterConfig.FilterType = FDCAN_FILTER_RANGE; // 过滤方式为范围,即从FilterID1~FilterID2之间的值
sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;
sFilterConfig.FilterID1 = 0x0000;
sFilterConfig.FilterID2 = 0x07ff; // 标准帧为11位ID,即0x7ff,本例配置为接收所有帧
if(HAL_FDCAN_ConfigFilter(&hfdcan_out, &sFilterConfig) != HAL_OK)
Error_Handler();
sFilterConfig.IdType = FDCAN_EXTENDED_ID; // 配置为过滤扩展帧
sFilterConfig.FilterIndex = 1; // 过滤器的索引号
sFilterConfig.FilterType = FDCAN_FILTER_RANGE_NO_EIDM; // 过滤方式为范围,即从FilterID1~FilterID2之间的值
sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO1;
sFilterConfig.FilterID1 = 0x00000000;
sFilterConfig.FilterID2 = 0x01ffffff; // 扩展帧为29位ID,即0x1fffffff,本例配置为接收所有帧
HAL_FDCAN_ConfigFilter(&hfdcan_out, &sFilterConfig);
outcanTxHeader.Identifier = 0x222; //FDCAN1,编号为222
outcanTxHeader.IdType = FDCAN_STANDARD_ID;
outcanTxHeader.TxFrameType = FDCAN_DATA_FRAME;
outcanTxHeader.DataLength = FDCAN_DLC_BYTES_8;
outcanTxHeader.ErrorStateIndicator = FDCAN_ESI_ACTIVE;
outcanTxHeader.BitRateSwitch = FDCAN_BRS_OFF;
outcanTxHeader.FDFormat = FDCAN_CLASSIC_CAN;
outcanTxHeader.TxEventFifoControl = FDCAN_NO_TX_EVENTS;
outcanTxHeader.MessageMarker = 0x52;
HAL_FDCAN_Start(&hfdcan_out); //fdcan1
}
中断内添加数据接收
void FDCAN1_IT0_IRQHandler(void)
{
/* USER CODE BEGIN FDCAN1_IT0_IRQn 0 */
/* USER CODE END FDCAN1_IT0_IRQn 0 */
HAL_FDCAN_IRQHandler(&hfdcan1);
/* USER CODE BEGIN FDCAN1_IT0_IRQn 1 */
HAL_FDCAN_GetRxMessage(&hfdcan1, FDCAN_RX_FIFO0, &outcanRxHeader, outcan_rxdata); //数据接收
/* USER CODE END FDCAN1_IT0_IRQn 1 */
}
数据收发
码字不易,都看到这里了,点赞收藏一下吧 ^_^。
调试上位机Cangaroo
这个软件用起来很方便,个人感觉比Busmaster更方便,主要是他可以集成多个CAN调试器的一体化显示,用Channel栏标识出不同调试器的ID号,这样多个设备联调时,就再也不用开多个窗口了。
Cangaroo也有一个极为不便的麻烦,它不能像Busmaster那样有多条备选的发送消息,只能有一条。如果要发送不同的消息也早要搞死人。
而Busmaster虽然也很强大,但不支持多个设备,即使开多个窗口也没用,目前只能这两个软件混用。
Cangaroo也有一个毛病,就是字体太小,在我的4K显示器上,字小得几乎看不清。
解决办法是生成一个快捷方式,右击,属性,在兼容性中选择“更高的DPI设置”,按下图操作即可,还不够就调节Windows的缩放。
操作完成后,界面大了很多,看着很舒服了。
Cangaroo下载地址:https://gitcode.com/gh_mirrors/ca/cangaroo