1. 从零开始:理解CAN总线与波特率的核心概念
如果你刚开始接触STM32H7的CAN总线开发,可能会被一堆术语搞晕:波特率、位时序、时间片、预分频……别急,咱们先抛开代码,用最生活化的方式来理解它。你可以把CAN总线想象成一条双向的高速公路,而波特率就是这条公路上车辆(也就是数据位)的通行速度。波特率越高,单位时间内能跑过去的数据就越多,通信自然就越快。
但这条“公路”的限速(波特率)不是随便定的,它取决于几个关键因素:路面的基础条件(时钟频率)、每个车道的宽度(一个数据位占用的时间片数)以及交通规则(位时序)。在STM32H7里,CAN外设的“路面基础”就是它的输入时钟源。很多教程和例程默认告诉你,CAN挂在APB1总线上,所以时钟就是APB1的时钟。这个说法在大多数情况下没错,但STM32H7给了我们更灵活的选择,尤其是当你需要更高精度的波特率,或者APB1的时钟频率不凑巧时,你可以选择使用PLL(锁相环)输出的时钟作为CAN的时钟源。这就好比,你不仅可以利用市政供电(APB1),还可以自己接一个更稳定、频率可调的发电机(PLL)来给你家的精密设备(CAN)供电。
我刚开始用H7做CAN通信时就踩过这个坑。当时项目要求一个非常精确的250kbps波特率,直接用APB1的100MHz时钟怎么算都有微小误差,导致长时间通信后偶尔会出现错帧。后来才发现,可以切换时钟源到PLL,通过精细调整PLL的分频系数,得到了一个完美整除的时钟频率,问题才彻底解决。所以,理解时钟源是配置波特率的第一步,也是最关键的一步。
2. 庖丁解牛:深入STM32H7的CAN时钟树
知道了时钟源的重要性,我们得亲手“拆开”STM32H7的时钟树看看。很多朋友一看到时钟树那张复杂的图就头疼,其实我们只需要关注和CAN相关的几条路径。STM32H7的FDCAN(Flexible Data-rate CAN)外设,其时钟源可以通过软件配置选择,主要来源有三个:HSE(外部高速晶振)、PLL1Q、PLL2Q。而默认情况下,很多工程和STM32CubeMX生成的代码,可能会让它直接使用所在总线(APB1或APB2)的时钟。
那么,怎么知道当前工程里CAN用的是哪个时钟呢?你不能只看SystemClock_Config()函数里给APB1设置了多大的频率。关键是要找到明确配置外设时钟源的函数调用。在HAL库中,这个函数通常是 HAL_RCCEx_PeriphCLKConfig()。我们需要在工程里搜索这个函数,看看它的参数里对 PeriphClkInit.ClkSrc 字段是如何设置的。
举个例子,在我参考的一个野火H743工程里,我就在bsp_can.c文件的GPIO配置函数后面,发现了这么几行关键的代码:
RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_FDCAN;
PeriphClkInit.FdcanClockSelection = RCC_FDCANCLKSOURCE_PLL;
HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit);
看到没?这里明确地将FDCAN的时钟源选择为 RCC_FDCANCLKSOURCE_PLL,也就是PLL。那么问题又来了,PLL的输出频率那么多,具体是PLL的哪一个输出呢?对于STM32H7,当选择PLL作为源时,默认使用的是 PLL1的“q”输出,也就是PLL1Q。这个“q”是PLL1的一个分频输出通道,它的频率可以在时钟配置函数里独立设置。
我们继续在main.c的SystemClock_Config()函数里找,通常会看到类似下面的配置:
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
...
// 配置PLL1
RCC_OscIni

&spm=1001.2101.3001.5002&articleId=153555251&d=1&t=3&u=e8dc38c91b4b4ab6a86dfcaaef073b2c)
41

被折叠的 条评论
为什么被折叠?



