news 2026/9/16 3:33:23

STM32F429双CAN测试程序详解:从引脚映射到Bus Off恢复

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
STM32F429双CAN测试程序详解:从引脚映射到Bus Off恢复

简介:面向STM32F429嵌入式开发者的双CAN通道测试程序,用于验证CAN1与CAN2两个独立控制器的初始化配置、报文收发和中断处理,适合汽车电子、工业自动化领域的中初级开发者学习。程序基于HAL库实现,围绕ISO 11898协议标准,详细覆盖位定时与波特率设定、报文头结构体填充、发送与接收FIFO管理、滤波器规则配置、中断服务函数编写,以及位错误、帧错误等异常的检测与恢复流程。通过该程序,读者可以直观理解双路CAN并行工作时的消息仲裁机制,学习如何让两个CAN通道同时收发不同优先级的报文,并快速搭建起自己的双通道通信测试环境。资源压缩包约9.52MB,已有169人学习下载;开发者可借这套示例程序掌握双CAN通道的初始化与收发流程,并将其中的代码逻辑直接移植到实际项目的第二路CAN应用场景。

1. STM32F429双CAN通道测试程序,为什么先要处理CAN2依赖CAN1这个前提

STM32F429内部带有两个bxCAN控制器,但设计成主从结构:CAN1为主,CAN2为从。所谓“从”不只是名称上的区别——CAN2没有独立完整的过滤器存储与位时序配置,必须依赖CAN1已经被正确使能并退出初始化状态,才能进入正常工作模式。我见过不少工程师拿到“CAN2Channels_STM32F429的双CAN通道测试程序”这类需求时,直接在HAL库中单独初始化CAN2,结果启动时报错,最后发现CAN1根本没有使能。本文按最常见的STM32CubeMX加HAL库方案,把双CAN测试程序的落地路径讲清楚:引脚怎么映射、位时序参数怎么填、过滤器如何在CAN1与CAN2之间切分,最后落到波形验证与Bus Off恢复策略。适合做车载网关、工业CAN节点或产线工装板的软硬件工程师,新手也能按步骤在F429开发板上跑通双通道互收。

2. STM32F429双CAN测试程序的引脚映射与收发器电路

2.1 CAN1/CAN2在STM32F429上的引脚复用选项对比

搭建测试程序的第一步,是确定CAN1与CAN2各自用哪组引脚。STM32F429上两个CAN控制器都复用AF9功能,每个控制器都有多组可选引脚,但不同封装和不同板卡上可用的组合不一样。CAN1常用PA11(RX)/PA12(TX)、PB8/PB9,以及144脚及以上封装的PD0/PD1;CAN2常用PB5/PB6和PB12/PB13。下表是常用组合。

CAN通道RX引脚TX引脚AF编号板级注意点
CAN1PA11PA12AF9多数F429核心板默认引出
CAN1PB8PB9AF9与I2C1、SPI1部分引脚重叠
CAN1PD0PD1AF9144脚及以上封装才可用
CAN2PB5PB6AF9与I2C1、SPI1引脚邻近
CAN2PB12PB13AF9与SPI2的NSS/SCK接近

我在实际项目中的选择顺序是:CAN1用PA11/PA12,CAN2用PB5/PB6。原因是这两组在大多数开发板上已经引出或容易飞线。选引脚时先看复用冲突,再看PCB布线是否方便。CubeMX中的操作很简单,在Pinout视图里把对应引脚用鼠标拉成CAN1_RX、CAN1_TX或CAN2_RX、CAN2_TX,软件会自动把复用功能设成AF9。需要注意的是,同一个控制器不要两组引脚混用,比如CAN1的RX用PA11、TX用PB9,这在功能上可行但容易让后续排查的人困惑,测试程序里尽量保持一对引脚。

2.2 双路CAN收发器与120Ω终端电阻怎么接

CAN控制器输出的是3.3V逻辑电平,不能直接挂总线,必须经过收发器。常用的有TJA1050、SN65HVD230或隔离型的ISO1050。TJA1050是5V供电,输入阈值兼容3.3V逻辑,可以直接与F429的GPIO相连;SN65HVD230是3.3V供电,适合低压系统。双通道的连接方式完全一致,以TJA1050为例:

PA12 (CAN1_TX) --> TJA1050 TXD PA11 (CAN1_RX) <-- TJA1050 RXD PB6 (CAN2_TX) --> 另一片TJA1050 TXD PB5 (CAN2_RX) <-- 另一片TJA1050 RXD CANH 与 CANL 之间跨接120Ω终端电阻

这里的核心是终端电阻布局。CAN总线协议要求物理线路两端各有一个120Ω电阻,而不是每个节点都加。当两个CAN通道通过同一根双绞线互测时,只需要在双绞线两端各跨接一个120Ω电阻。如果只在板内测试且线缆很短,可以先不接终端电阻,但此时总线信号反射会让波形边沿变差,逻辑分析仪看到的位宽度会不稳定。要验证物理层时,终端电阻不能省。

2.3 共地与供电对can通信测试的影响

测试程序跑不通时,我一般先量三个点:收发器供电电压是否正确、TXD引脚静态电平是否为高、两个测试节点是否真正共地。CAN是差分信号,但收发器有一个共模输入范围,TJA1050约为-7V到+12V。如果两端设备的地电位差超出这个范围,总线上会出现大量错误帧,表现就是“can初始化失败”或者发送一直报错。双通道在同一块板上互测时,两片收发器必须共地;不同板卡通过长线连接时,最好使用隔离收发器。还有一个经常被忽略的点:CAN控制器空闲时TXD应为高电平,如果量到低电平,说明硬件复位后引脚状态没有正确配置,或者控制器正处于Bus Off状态。

3. 用CubeMX完成STM32F429双CAN配置与位时序设定

3.1 使能CAN2前必须使能CAN1:时钟树与初始化顺序

在CubeMX中勾选CAN1和CAN2只解决了外设时钟使能问题。F429的bxCAN模块中,CAN2作为从控制器,只有在CAN1正常工作后才能真正参与总线通信。具体到CubeMX的Clock Configuration页面,需要确认CAN时钟源来自APB1,且APB1时钟不超过42MHz。当SYSCLK设为168MHz时,APB1分频系数为4,得到42MHz,这是F429上最常见的组合。如果外部晶振是25MHz,需要在RCC配置里选择对应的HSE值,否则PLL算出来的系统时钟会偏离预期。代码层面,初始化顺序是先HAL_CAN_Init(&hcan1),再HAL_CAN_Init(&hcan2),不要反过来。启动顺序也一样:先HAL_CAN_Start(&hcan1),再HAL_CAN_Start(&hcan2)。关闭时顺序反过来,先停CAN2,再停CAN1。

3.2 500kbps下BS1、BS2、SJW参数与采样点计算

CAN位时间由四段组成:同步段、传播时间段、相位缓冲段1和相位缓冲段2。在STM32的HAL库中,传播时间段和相位缓冲段1合并为TimeSeg1,也就是BS1;相位缓冲段2单独为TimeSeg2,即BS2。同步段固定为1个时间量子tq。采样点位于BS1结束后、BS2开始前,这个位置直接关系通信的可靠性。核心公式是:

  • tq = (BRP + 1) / fAPB1
  • 位时间 = (1 + BS1 + BS2) 个tq
  • 波特率 = fAPB1 / ((BRP+1) × (1+BS1+BS2))
  • 采样点 = (1 + BS1) / (1 + BS1 + BS2) × 100%

以fAPB1=42MHz、目标波特率500kbps为例。令BS1=11、BS2=2,位时间就是14个tq,此时BRP+1 = 42MHz / (500k × 14) = 6,所以BRP取5。采样点 = (1+11)/14 = 85.7%,落在CAN协议推荐的80%到87.5%区间内。按这个思路可以得到一组常用的完整参数。

目标波特率Prescaler(BRP)BS1BS2SJW实际波特率采样点
1 Mbps211211.000 Mbps85.7%
500 kbps51121500 kbps85.7%
250 kbps111121250 kbps85.7%
125 kbps231121125 kbps85.7%

SJW(同步跳跃宽度)用于补偿时钟偏差,取值一般不超过BS2,这里设成1tq即可。如果追求更低的采样点,可以改成BS1=10、BS2=3,位时间同样是14tq,采样点变为78.6%,适合线缆较长、信号边沿较缓的场景。这里需要说明,STM32F429的双CAN只支持CAN2.0帧,若项目后续要评估canfd和can的区别,CAN FD的采样点和位速率切换逻辑与这里不同,需要换支持CAN FD的控制器和收发器。

3.3 28个过滤器在CAN1与CAN2之间的切分

F429的bxCAN模块共享28个硬件过滤器组,编号0到27,不是每个CAN独立拥有。硬件通过CAN_FMR寄存器中的CAN2SB位决定CAN2从哪一组开始使用。例如CAN2SB=14时,绝对编号0到13给CAN1,绝对编号14到27给CAN2。CubeMX的NVIC设置页里会自动生成这部分配置,但测试程序中还要手动确认结构体字段一致。HAL库中给CAN2配置过滤器时,FilterBank字段写的是相对CAN2起始组的偏移量,CAN2的FilterBank=0对应绝对组号14。给CAN1配置时才是绝对编号。这个差异几乎每个双CAN项目都会遇到,很多“can初始化失败”其实只是过滤器配置越界。

4. STM32F429双CAN测试程序的核心HAL代码:从回环到交叉收发

4.1 先跑Loopback回环自测,验证控制器本身没问题

不要在未验证控制器的情况下就去检查外部总线。先把CAN1和CAN2都配置为Loopback模式,此时发送报文不经过收发器,直接在芯片内部回到本节点的接收FIFO。回环模式可以验证CAN内核、FIFO、中断和过滤器配置是否正常。需要特别注意的是,回环模式仍然受硬件过滤器控制,因此要先配一个全通过滤器,掩码全部置0表示接收所有ID。

CAN_FilterTypeDef filterAll; filterAll.FilterActivation = ENABLE; filterAll.FilterBank = 0; filterAll.FilterMode = CAN_FILTERMODE_IDMASK; filterAll.FilterScale = CAN_FILTERSCALE_32BIT; filterAll.FilterIdHigh = 0x0000U; filterAll.FilterIdLow = 0x0000U; filterAll.FilterMaskIdHigh = 0x0000U; filterAll.FilterMaskIdLow = 0x0000U; filterAll.FilterFIFOAssignment = CAN_RX_FIFO0; HAL_CAN_ConfigFilter(&hcan1, &filterAll);

这段代码的作用是让CAN1接收所有标准帧。FilterMode选择IDMASK,掩码为0表示不漏过任何ID;如果把掩码设为0x7FF左移5位,则只匹配一个指定ID。FilterBank设为0,是CAN1可用的过滤器组。回环模式下发送端和接收端是同一个CAN节点,所以需要同时调用HAL_CAN_Start启动控制器,并打开接收中断。

CAN_TxHeaderTypeDef txHeader; txHeader.StdId = 0x123; txHeader.ExtId = 0; txHeader.IDE = CAN_ID_STD; txHeader.RTR = CAN_RTR_DATA; txHeader.DLC = 4; uint8_t txData[4] = {0x11, 0x22, 0x33, 0x44}; uint32_t mailbox = 0; if (HAL_CAN_AddTxMessage(&hcan1, &txHeader, txData, &mailbox) == HAL_OK) { // 回环模式下,发送完成后本节点会收到同一帧 }

HAL_CAN_AddTxMessage的四个参数分别是CAN句柄、发送报头结构体、数据缓冲区和邮箱索引。函数执行成功后,报文首先进入硬件邮箱,总线仲裁成功后发送。回环模式下CANH/CANL引脚上不会有信号波形,属于预期行为,不要拿示波器去量。

4.2 Normal模式交叉测试:CAN1发送、CAN2接收中断

回环验证通过后,将两个CAN都切换到Normal模式,接入外部收发器和双绞线。同一个总线上有两个发送节点时,can总线仲裁由ID位值决定,ID小的优先级高。我们的测试程序固定由CAN1发送ID=0x123的报文,CAN2设置过滤器只接收这个ID,从而验证双通道链路。

// CAN1 发送节点 hcan1.Init.Mode = CAN_MODE_NORMAL; HAL_CAN_Init(&hcan1); HAL_CAN_Start(&hcan1); // CAN2 接收节点 hcan2.Init.Mode = CAN_MODE_NORMAL; HAL_CAN_Init(&hcan2); HAL_CAN_Start(&hcan2); HAL_CAN_ActivateNotification(&hcan2, CAN_IT_RX_FIFO0_MSG_PENDING);

启动CAN2后调用HAL_CAN_ActivateNotification,把FIFO0有报文挂起的中断通知打开。这一步容易漏,漏掉后即使硬件接收到了报文,也不会进入中断回调。

void CAN2_RX_IRQHandler(void) { HAL_CAN_IRQHandler(&hcan2); } void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan) { CAN_RxHeaderTypeDef rxHeader; uint8_t rxData[8]; if (hcan->Instance == CAN2) { HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &rxHeader, rxData); // 检查 rxHeader.StdId,应为 0x123 } }

中断回调里通过判断hcan->Instance区分是哪个CAN控制器触发的,避免CAN1的报文进入接收逻辑。HAL_CAN_GetRxMessage每次从FIFO0弹出一帧,并把报头信息填入rxHeader、数据拷贝到rxData。由于FIFO深度只有3个邮箱,回调中应尽快把rxData复制到用户缓冲区,否则连续大量报文到来时可能溢出。

4.3 双CAN过滤器配置:FilterBank从相对编号写

下面这段是给CAN2配置只接收ID=0x123的过滤器。双CAN环境下,过滤器组划分必须与CAN2SB一致,否则CAN2的报文会落到CAN1的FIFO里。

CAN_FilterTypeDef filter; filter.FilterActivation = ENABLE; filter.FilterBank = 0; // 相对CAN2起始组的偏移,对应绝对组号14 filter.FilterMode = CAN_FILTERMODE_IDMASK; filter.FilterScale = CAN_FILTERSCALE_32BIT; filter.FilterIdHigh = (0x123U << 5) & 0xFFFFU; filter.FilterIdLow = 0x0000U; filter.FilterMaskIdHigh = (0x7FFU << 5) & 0xFFFFU; filter.FilterMaskIdLow = 0x0000U; filter.FilterFIFOAssignment = CAN_RX_FIFO0; filter.SlaveStartFilterBank = 14; // CAN2从绝对组号14开始 HAL_CAN_ConfigFilter(&hcan2, &filter);

为什么ID要左移5位?因为bxCAN的32位过滤器寄存器布局中,标准ID占用位28到位18,写入高16位时需要将11位ID整体左移5位。屏蔽位的0x7FF左移5位表示只比较ID字段,其他位全部置1表示不关心。FilterBank字段在CAN2句柄下写的是相对偏移,HAL库内部会自动加上SlaveStartFilterBank的值。如果HAL_CAN_ConfigFilter返回HAL_ERROR,优先检查该组号是否超出当前通道可用的范围。

4.4 双CAN初始化失败的高频原因排查

如果HAL_CAN_Init或HAL_CAN_Start返回错误,按顺序检查五项:一是CAN1有没有同时使能并先启动;二是APB1时钟是否超过42MHz,超出后CAN位时序计算基准就错了;三是两个hcan句柄的Instance是否接反;四是过滤器组索引是否越界或重叠;五是波特率参数中BS1加BS2是否至少为2tq。这五点在单CAN项目里不一定全部触发,在双CAN测试中却是高频根因。另有一个容易忽略的坑:CAN2启动失败时,hcan2.ErrorCode可能标记为HAL_CAN_ERROR_NOT_INITIALIZED,容易让人误以为初始化函数本身没执行,实际原因是CAN1还未进入正常模式。

5. 双CAN测试程序的验证技巧:波形检查与Bus Off恢复

5.1 在TXD/RXD上抓位时序,核对2微秒位宽

上位机显示“发送成功”不代表物理总线没有问题。把逻辑分析仪接到CAN1_TXD引脚,触发方式选下降沿,因为帧起始SOF是显性电平,对应逻辑0。以500kbps为例,单个位时间应为2微秒,发送数据0xAA时波形应当是10101010的交替序列。如果测量到的位宽偏差超过5%,或波形边沿明显抖动,回到第三章的参数表检查BS1和BS2,必要时把采样点调整为80%。双通道互测时,可以在CAN2的RXD引脚上抓到几乎相同的帧,与CAN1_TXD之间的延迟只有几十纳秒。若在CAN2_RXD上抓不到帧,问题出在总线连接或收发器,而不是CAN1的发送代码。

5.2 主动制造Bus Off并编写恢复回调

测试Bus Off恢复的常用方法,是把CANH与CANL短接一到两秒,造成持续显性电平错误。发送节点的TEC寄存器会不断累加,超过255后进入Bus Off状态,表现为TXD引脚持续高电平,不再发送任何帧。HAL库通过错误回调上报该状态,回调中可以对故障通道做重新初始化。

void HAL_CAN_ErrorCallback(CAN_HandleTypeDef *hcan) { if (hcan->Instance == CAN2 && (hcan->ErrorCode & HAL_CAN_ERROR_BOF) != 0U) { HAL_CAN_Stop(hcan); HAL_CAN_Start(hcan); } }

这段代码的作用是将Bus Off状态的CAN2重新拉回正常。HAL_CAN_Stop先停止控制器并清除错误状态,HAL_CAN_Start再重新激活。也可以在Init结构体中把AutoBusOff设为ENABLE,由硬件在总线空闲后自动恢复,但回调中的日志记录仍然有用,方便确认哪一路发生过故障。实际项目中我倾向AutoBusOff=ENABLE加回调记录,兼顾自动恢复与可观测性。

5.3 连续压测与负载率自检

只跑一帧不能说明问题。把发送端改成分批次循环发送1000帧,接收端累加收到的帧数,最后比对计数并记录最大连续丢帧数。若计数一致,说明双CAN链路稳定。也可以借助CAN上位机工具,观察总线负载率是否与理论计算一致。例如每秒发送200帧、每帧约120位时,理论负载率为200×120/500k = 4.8%。实测值如果明显高于这个数字,往往是有其他节点在不停重发错误帧,这时优先检查终端电阻和电位差。测试程序的最后一步,把上述波形、计数和负载率三个数据填入测试记录,双CAN通道的验证才算完整。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/16 3:33:14

茶器艺科智造HarmonyOS应用实战-24-导出的数组和默认草稿都可被消费者改写:用readonly与工厂函数守住HAR边界

茶器艺科智造HarmonyOS应用实战-24-导出的数组和默认草稿都可被消费者改写&#xff1a;用readonly与工厂函数守住HAR边界 HAR 把 SHOP_FILTER_KEYS、预设标签、快捷指令、雷达参考值和 DEFAULT_CHAQI_DRAFT_STATE 统一导出后&#xff0c;消费者拿到的不只是“常量名字”&#x…

作者头像 李华
网站建设 2026/9/16 3:32:37

系统镜像制作实战:从dd到Clonezilla的Linux/Windows迁移指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/16 3:32:16

Linux WiFi设备驱动开发实战:从设备树到数据通路的完整指南

写WiFi驱动之前&#xff0c;先把我踩过的坑说在前面。别指望内核文档能救你&#xff0c;也别指望芯片原厂SDK能直接跑起来。大多数时候你面对的是一个只给了数据手册、几个补丁和一堆BSP的WiFi模组&#xff0c;然后要在Linux下让它稳定工作。这篇文章从驱动框架、环境搭建、设备…

作者头像 李华
网站建设 2026/9/16 3:29:12

服务器CPU飙高?用top快速定位与持续监控实战指南

服务器CPU报警或者负载飙高的时候&#xff0c;大多数人第一个动作就是敲top。我自己也一样&#xff0c;不管现在有多少花里胡哨的监控平台&#xff0c;遇到性能问题第一反应还是top——它快、轻、哪台机器都有&#xff0c;不需要装任何东西。但top这个命令有个尴尬的地方&#…

作者头像 李华
网站建设 2026/9/16 3:28:11

RAG与AI Agents工程实践:生产级大模型应用开发地图

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/16 3:26:06

CSS :has() 父选择器实战指南:从语法到性能优化

做前端这些年&#xff0c;论CSS里最让我惦记的一个特性&#xff0c;就是父选择器。不是说你非要用它不可&#xff0c;而是当你遇到"根据子元素的状态去改变父元素样式"这种需求时&#xff0c;你才会发现CSS这门语言的严苛——它只允许样式从祖先流向后代&#xff0c;…

作者头像 李华