简介这份资源面向嵌入式开发初学者与需要快速上手CAN通信的工程师提供基于STM32F103系列微控制器的CAN实例代码帮助解决从CAN控制器配置到收发调试的入门难题。压缩包共81个文件约280KB以28个c源文件与30个h头文件为主体配合12个s启动文件及工程配置类文件覆盖应用层、CMSIS与标准外设库等目录结构完整可直接导入Keil工程。内容涉及CAN1/CAN2控制器初始化、波特率与滤波器寄存器配置、发送接收邮箱操作、中断回调及错误状态查询等关键环节读者可对照代码理解差分总线物理层、数据帧格式与HAL库API调用方式并在此基础上调试自己的CAN节点。目前已有2876人学习下载适合作为工业控制、汽车电子等场景下CAN通信实践的参考起点。1. 拿到一份 STM32F103 CAN 实例代码先别急着烧录CAN 总线调试最磨人的地方在于代码编译通过、板子上电、示波器看波形也正常但收发就是不通最后发现是终端电阻没接、波特率算错一个分频或者过滤器掩码把帧全挡了。这份 STM32F103 CAN 实例代码的价值就在这儿——它把 bxCAN 外设的初始化、过滤器配置、收发中断和回环自测串成一条能跑通的链路省去你从寄存器手册第 600 页开始啃的时间。适合手里有 F103 板子、要接车载或工控 CAN 节点、又不想在标准库和 HAL 之间反复试错的从业者。下面按「代码怎么组织、参数怎么定、坑在哪」拆开讲新手能照着改熟手能直接对参数边界。2. 拆开代码包bxCAN 初始化与过滤器配置的真实结构2.1 先看清这份实例的模块划分拿到代码包别一头扎进 main.c。F103 的 CAN 实例通常按「硬件抽象层 业务层」两层组织先摸清文件职责改起来才不会牵一发动全身。常见结构是一个 can.c/can.h 负责外设时钟、GPIO 复用、位时序和过滤器一个中断服务文件处理 RX0/RX1 和发送完成回调main 里只做初始化和一个最小收发循环。我一般会先看 can.h 里暴露了哪些函数——如果只有CAN_Init()一个入口说明过滤器是写死的接多节点时大概率要改如果拆出了CAN_FilterConfig()和CAN_SendMsg()说明作者考虑过复用这种包更值得留。以标准外设库版本为例核心初始化大致长这样HAL 版本逻辑等价、函数名不同/* can.c —— F103 bxCAN 初始化骨架标准库风格 */ void CAN_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; CAN_InitTypeDef CAN_InitStructure; CAN_FilterInitTypeDef CAN_FilterInitStructure; /* 1. 时钟CAN 挂在 APB1GPIO 挂在 APB2 */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); /* 2. PA11CAN_RX 上拉输入PA12CAN_TX 复用推挽 */ GPIO_InitStructure.GPIO_Pin GPIO_Pin_11; GPIO_InitStructure.GPIO_Mode GPIO_Mode_IPU; GPIO_Init(GPIOA, GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin GPIO_Pin_12; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStructure); /* 3. 位时序36MHz APB1500kbps采样点约 87.5% */ CAN_InitStructure.CAN_TTCM DISABLE; CAN_InitStructure.CAN_ABOM ENABLE; /* 自动离线恢复调试期必开 */ CAN_InitStructure.CAN_AWUM DISABLE; CAN_InitStructure.CAN_NART DISABLE; /* 允许自动重传 */ CAN_InitStructure.CAN_RFLM DISABLE; CAN_InitStructure.CAN_TXFP DISABLE; CAN_InitStructure.CAN_Mode CAN_Mode_Normal; CAN_InitStructure.CAN_SJW CAN_SJW_1tq; CAN_InitStructure.CAN_BS1 CAN_BS1_12tq; CAN_InitStructure.CAN_BS2 CAN_BS2_2tq; CAN_InitStructure.CAN_Prescaler 4; /* 36M / (1122) / 4 500k */ CAN_Init(CAN1, CAN_InitStructure); /* 4. 过滤器16 位掩码模式只放行 ID0x123 */ CAN_FilterInitStructure.CAN_FilterNumber 0; CAN_FilterInitStructure.CAN_FilterMode CAN_FilterMode_IdMask; CAN_FilterInitStructure.CAN_FilterScale CAN_FilterScale_16bit; CAN_FilterInitStructure.CAN_FilterIdHigh 0x123 5; CAN_FilterInitStructure.CAN_FilterIdLow 0x0000; CAN_FilterInitStructure.CAN_FilterMaskIdHigh 0xFFE0; CAN_FilterInitStructure.CAN_FilterMaskIdLow 0x0000; CAN_FilterInitStructure.CAN_FilterFIFOAssignment CAN_Filter_FIFO0; CAN_FilterInitStructure.CAN_FilterActivation ENABLE; CAN_FilterInit(CAN1, CAN_FilterInitStructure); }这段代码里最容易被忽略的是第 3 步的位时序和第 4 步的过滤器。位时序决定波特率算错一个 tq 就整条总线失步过滤器决定哪些帧能进 FIFO配错就是「发送正常、接收永远空」。参数含义后面两节展开这里先记住CAN_Prescaler是分频BS1BS2SJW决定一个位占多少 tq三者共同锁死波特率。2.2 位时序参数怎么算别照抄分频值F103 的 CAN 时钟来自 APB1默认 36MHz系统 72MHz 二分频。一个 CAN 位被拆成若干时间份额 tq公式是波特率 APB1时钟 / (Prescaler × (1 BS1 BS2))上面代码里 Prescaler4、BS112、BS22代入 36M/(4×15)600k不对是 36M/(4×15)600k这里就翻车了——正确写法应是 36M/(4×(1122))36M/60600k。要 500k 得让分母等于 72比如 Prescaler4、BS112、BS251125184×1872或者 Prescaler6、BS19、BS22。这就是血泪经验网上抄来的分频值经常和你的 APB1 时钟对不上必须自己代公式验算。采样点位置同样关键。CAN 规范建议采样点在位的 75%~87.5% 之间计算公式是(1BS1)/(1BS1BS2)。BS112、BS22 时采样点在 13/15≈86.7%偏晚但可接受如果 BS2 取太小、采样点逼近 90% 以上长线缆下容易误采样。我一般把目标定在 80%~85%BS1 取大、BS2 取 2~3 个 tqSJW 保持 1tq 用于时钟容差补偿。改完参数别只看编译通过拿示波器量一位宽度500k 对应 2μs量出来偏差超过 2% 就回去查分频。2.3 过滤器掩码模式和列表模式别混用F103 有 14 组过滤器每组可配成 16 位或 32 位、掩码或列表模式。实例代码默认多半是「16 位掩码、只收一个 ID」够跑通但不够用。掩码模式的逻辑是(收到的ID Mask) (FilterId Mask)Mask 里为 1 的位参与比较为 0 的位不关心。想收 0x120~0x12F 这一段就把低 4 位掩码置 0/* 收 0x120~0x12F高 11 位固定低 4 位不关心 */ CAN_FilterInitStructure.CAN_FilterIdHigh (0x120 5); CAN_FilterInitStructure.CAN_FilterMaskIdHigh (0xFFF0 5) 0xFFFF;注意 16 位模式下 ID 要左移 5 位对齐到寄存器高位这是标准库和 HAL 都一样的坑漏了移位就会「明明 ID 对却收不到」。列表模式则相反每个过滤器寄存器存一个精确 ID适合只关心几个固定报文的场景但数量受 14 组限制。选型上节点多、ID 连续用掩码ID 离散、数量少用列表。改过滤器后一定要清一次 FIFO 再使能否则残留帧会干扰判断。3. 收发链路跑通中断、回环与总线电平三道关3.1 发送与接收中断的最小闭环初始化过了不代表能收发。实例代码里通常有CAN_Transmit()和 RX 中断回调但很多人卡在「发了没反应、收了进不去中断」。先确认 NVIC 里开了USB_LP_CAN1_RX0_IRQnF103 的 CAN RX0 和 USB 共用中断向量名字容易看漏再确认CAN_ITConfig(CAN1, CAN_IT_FMP0, ENABLE)使能了 FIFO0 非空中断。发送侧建议先轮询邮箱状态再发避免三个邮箱全满时静默丢帧/* 发送一帧标准数据帧ID0x1238 字节 */ uint8_t CAN_SendStd(uint32_t id, uint8_t *data, uint8_t len) { CanTxMsg tx; tx.StdId id; tx.ExtId 0; tx.IDE CAN_Id_Standard; tx.RTR CAN_RTR_Data; tx.DLC len; for (uint8_t i 0; i len; i) tx.Data[i] data[i]; uint8_t box CAN_Transmit(CAN1, tx); /* 返回使用的邮箱号CAN_TxStatus_NoMailBox 表示满 */ if (box CAN_TxStatus_NoMailBox) return 1; uint32_t t0 0; while (CAN_TransmitStatus(CAN1, box) ! CAN_TxStatus_Ok) { if (t0 0xFFFFF) return 2; /* 超时保护别死等 */ } return 0; }接收中断里别做重活只把CAN_Receive()取出的帧塞进环形缓冲主循环再解析。中断里直接跑协议解析是另一个常见翻车点解析耗时超过下一帧到达间隔FIFO 溢出标志 OVR 置位之后所有帧都丢现象是「跑几分钟就断」。环形缓冲深度按总线负载估500k 满载时一秒约 4000 帧缓冲给 64~128 帧足够。3.2 先用回环模式自测再上真实总线板子单独调试时最省事的验证是回环模式CAN_Mode_LoopBack下发送的帧不经过收发器直接内部回环到接收过滤器能验证「位时序 过滤器 中断」整条软件链路排除硬件接线干扰。步骤是把CAN_InitStructure.CAN_Mode改成CAN_Mode_LoopBack发一帧 ID0x123看 RX 中断是否收到同样内容。收到说明软件链路通收不到就查过滤器和中断使能别去怀疑收发器。回环通过后切CAN_Mode_Normal接上收发器如 TJA1050 一类和另一节点。这时要量总线电平CAN_H 与 CAN_L 之间隐性态约 2.5V 附近、显性态差分约 2V两端各接 120Ω 终端电阻总线总阻值约 60Ω。只接一端电阻、或者节点多了没加电阻现象是「短距离能通、线一长就误码」。我习惯上电先用万用表量 CAN_H-CAN_L 阻值60Ω 左右才继续往下调。3.3 波特率与终端电阻的联合排查表收发不稳时按下面顺序过一遍比盲目改代码快检查项正常值/现象异常时的典型表现APB1 时钟36MHz72M 系统波特率整体偏移所有节点失步位时序分母Prescaler×(1BS1BS2) 匹配目标示波器位宽偏差 2%采样点75%~87.5%长线缆偶发误帧终端电阻总线约 60Ω短距通、长距断过滤器掩码目标 ID 能通过发送正常、接收恒空FIFO 溢出OVR 不置位跑一段时间后全丢帧这张表是我调试时的固定动作尤其「发送正常、接收恒空」这一条九成是过滤器 ID 没左移或掩码写反先查这里再查别的。4. 避坑清单五个让 CAN 调试原地打转的常见问题4.1 现象编译通过、发送无报错但总线上量不到波形原因多半是 GPIO 复用没配对。F103 的 CAN 默认走 PA11/PA12但这两个脚同时是 USB 引脚如果工程里开了 USB 时钟或重映射没关引脚会被抢。解决确认RCC_APB2PeriphClockCmd只开了 GPIOA检查有没有误开RCC_APB1Periph_USB并用GPIO_PinRemapConfig确认没有把 CAN 重映射到 PB8/PB9 却没改接线。改完重新上电量波形别只信编译结果。4.2 现象回环模式能收正常模式收不到原因通常是收发器供电或使能脚没拉对。很多收发器模块有 STB/EN 引脚悬空时处于待机总线呈隐性但不转发。解决查模块原理图把使能脚拉到有效电平量收发器 VCC 是否 5V/3.3V 正常再量 TX 脚在发送瞬间有没有跳变。这一步翻车最多因为代码完全正确问题全在板子外面。4.3 现象跑几分钟后接收中断再也不进原因是 FIFO 溢出后没清 OVR 标志。F103 的 RX FIFO 溢出时 OVR 置位若中断里只读帧不清标志后续帧继续丢。解决在 RX0 中断里判断CAN_GetFlagStatus(CAN1, CAN_FLAG_FOV0)置位时先CAN_ClearFlag再继续同时检查主循环解析是否太慢。血泪经验是中断里只搬数据、不解析把重活挪出去。4.4 现象多节点组网时随机丢帧单节点正常原因是波特率容差叠加或终端电阻缺失。每个节点的晶振误差、位时序采样点不一致单对单能容忍多节点就暴露。解决统一各节点位时序参数尤其采样点总线两端各 120Ω中间节点不接电阻用示波器看显性位宽度是否一致。别用「能通就行」的心态放过随机丢帧在量产里就是黑匣子。4.5 现象改了过滤器后完全收不到改回去也不行原因是过滤器激活顺序或 FIFO 残留。F103 要求先配过滤器再使能且改配置前最好进初始化模式。解决把CAN_FilterInit()放在CAN_Init()之后、使能中断之前改完调一次CAN_ClearFlag清 FIFO若仍不行把CAN_FilterActivation先 DISABLE 再 ENABLE 走一遍。这个坑隐蔽在于「看起来改了没生效」其实是旧配置还在寄存器里。5. 进阶把实例代码改成可复用的 CAN 收发框架实例代码跑通只是起点真正落地要把它变成能接多 ID、能统计错误、能自恢复的框架。我的做法是抽三层底层can_driver只管初始化和裸收发中间can_router按 ID 分发到回调上层业务注册回调。过滤器改成掩码收一段 ID 区间RX 中断里按 ID 查表调对应函数表项用静态数组避免动态内存。错误处理上开CAN_IT_ERR中断在回调里读CAN_GetLastErrorCode()遇到被动错误或总线关闭就触发重初始化——CAN_ABOM虽然能自动恢复但恢复后位时序和过滤器状态建议重走一遍别赌它。验证方法我固定用三步先回环自测软件链路再双节点对发压测连续发 10 万帧统计丢帧率最后接真实总线跑一晚看 OVR 和错误计数。压测脚本可以简单到主循环里for循环发递增值接收端比对序号丢一帧记一次。下面这段是分发骨架改 ID 表就能复用/* can_router.c —— 按标准 ID 分发表驱动无动态内存 */ typedef void (*can_handler_t)(uint8_t *data, uint8_t len); typedef struct { uint16_t id; can_handler_t handler; } can_route_t; static const can_route_t route_table[] { {0x123, on_motor_status}, {0x124, on_sensor_data}, /* 新增报文只加一行不动中断逻辑 */ }; void CAN_RX0_IRQHandler(void) { CanRxMsg rx; if (CAN_GetITStatus(CAN1, CAN_IT_FMP0) ! RESET) { CAN_Receive(CAN1, CAN_FIFO0, rx); for (uint8_t i 0; i sizeof(route_table)/sizeof(route_table[0]); i) { if (route_table[i].id rx.StdId) { route_table[i].handler(rx.Data, rx.DLC); break; } } CAN_ClearITPendingBit(CAN1, CAN_IT_FMP0); } }表驱动的好处是新增报文不改中断坏处是 ID 多了线性查找变慢超过 32 个 ID 就换二分或哈希。参数上注意rx.DLC最大 8别越界读回调里别阻塞耗时操作打标记回主循环。从那以后我每次拿到新的 CAN 实例代码都强制先跑回环、再量终端电阻、最后压测丢帧率三步不过不接真实总线。希望帮到你。本文还有配套的精品资源点击获取
企业数字化 ERP 产品动态
相关推荐
端侧AI大模型硬件选型指南:RK3588部署LLM实战与量化策略 1. 端侧AI LLM硬件选型的真实决策逻辑1.1 为什么端侧部署突然成了刚需过去两年,大模型推理基本都在云端完成,调用API按token计费,省心省力。但真正做过产品落地的团队会发现,云端方案在几个场景下会直接卡死项目:工业质… · 2026/11/2 19:44:36
0-1型整数线性规划:数学建模国赛美赛核心考点与MATLAB实战 简介:面向数学建模参赛者与运筹优化学习者的MATLAB 0-1型整数线性规划实践资料,聚焦决策变量仅取0或1的离散优化问题,可用于生产计划、资源分配、网络设计等场景的建模求解训练。资源包共6个文件,以4个m脚本和2个asv自动备份文件为… · 2026/11/13 23:15:30
DeSpend DAO:重构电商信任机制的区块链治理方案 1. 传统电商信任危机的结构性根源在中心化电商平台主导的二十年里,我们目睹了一个悖论:平台规模越大,其作为"信任中介"的角色反而越显脆弱。去年双十一期间,某头部平台单日投诉量突破12万件,其中62%涉及平台… · 2026/11/14 6:40:42
天河北高压电工证考试在哪考?实操考试心里没底怕挂科 天河北高压电工证考试在哪考?实操考试心里没底怕挂科 实操考试心里没底怕挂科?你不是一个人。尤其是对刚入行或准备跨行业转岗的电工从业者来说,高压电工证不仅是工作的“通行证”,更是安全操作的“护航证”。而【天河北高压电工证考试】在哪里考、怎么准… · 2026/11/14 9:29:22
湖北八大员特种工证过期了怎么处理?官方报名入口全攻略 湖北八大员特种工证过期了怎么处理?官方报名入口全攻略 证书过期了不知道怎么复审延期?别急,这篇全干货帮你解决! 湖北八大员特种工证作为建筑和施工领域的重要凭证,不仅关系到从业人员的职业资格,还直接关系到岗位的安全责任和法律风险。很多人在证书… · 2026/11/14 9:29:22
电工证复审找 找谁报名内部消息全公开 鹤壁施工企业怎么避坑 电工证复审找 找谁报名内部消息全公开 鹤壁施工企业怎么避坑 你是不是也遇到过这样的情况:电工证快到期了,想找个靠谱的机构去复审,结果一搜信息五花八门,不知道哪个是真的?更怕被那些打着“内部渠道”旗号的中介骗钱。别急,今天就给你一套… · 2026/11/14 9:29:22
中山电工证在那年审考前押题攻略,别让培训费打水漂 中山电工证在那年审考前押题攻略,别让培训费打水漂 你是不是也遇到过这种情况?花了几千块报了电工证培训班,结果考试一次没过,心里既着急又不甘心,怕钱打水漂,更怕耽误工作。别急,今天我就拿【中山电工证在那年审】这个关键词,给你讲讲怎么利用【考前… · 2026/11/14 9:29:22
泸县考电工证在哪里考 2026河南最新政策下怎么快速搞定 泸县考电工证在哪里考 2026河南最新政策下怎么快速搞定 工地太忙根本没时间复习考试,特别是那些每天在施工现场穿梭的电工,时间紧、任务重,考个证比登天还难。2026年河南最新政策出台后,很多工友都开始关注泸县考电工证在哪里考,想趁着政策变化… · 2026/11/14 9:29:22
应急管理特种作业证怎么查?查电工证进什么网站良心建议来了 应急管理特种作业证怎么查?查电工证进什么网站良心建议来了 你现在急需特种作业证才能上岗找工作,但不知道从哪儿开始?别慌,今天我就来给你讲讲怎么查电工证进什么网站,还有关于电工证报考的良心建议,绝对实用。… · 2026/11/14 9:28:22
电工证丢失还能补吗?千万别踩坑,丢了证等于失业 电工证丢失还能补吗?千万别踩坑,丢了证等于失业 你正在找工作,但手里的电工证突然丢了,这下怎么办?别急,先别慌, 电工证丢失还能补吗 ?答案是肯定的,但 千万别踩坑 ,补办流程如果不了解,可能会耽误你上岗、甚至影响到你的收入。… · 2026/11/13 22:30:50
电力电缆作业证直播报名入口与办理流程良心建议 电力电缆作业证直播报名入口与办理流程良心建议 你是不是也担心学历不高报不了名?别急,今天就给你讲讲 电力电缆作业证直播 的报名流程和那些你必须知道的干货, 良心建议 在这里,别错过。 电力电缆作业证直播报名需要什么条件?… · 2026/11/13 5:40:45
英德电工证报考费多少钱,怕考不过白交培训费?看完这篇别再踩坑 英德电工证报考费多少钱,怕考不过白交培训费?看完这篇别再踩坑 怕考不过白交培训费?英德电工证报考费多少钱,你真的了解吗?现在市面上培训机构五花八门,价格参差不齐,很多人一不小心就花了冤枉钱,结果还考不过。别急,今天就来给你说清楚英德电工证的… · 2026/11/12 23:27:43
有初级电工证怎么工作 2026河南最新政策怎么应对 有初级电工证怎么工作 2026河南最新政策怎么应对 之前考的证在外省能不能转过来?这个问题我每天被问上百次。2026年河南最新政策对证书跨省转移有了明确说明,特别是南阳市的从业者,需要特别关注。… · 2026/11/14 0:00:31
电工证过期了不知道怎么复审?考电工证自学书+官方报名入口全攻略 电工证过期了不知道怎么复审?考电工证自学书+官方报名入口全攻略 证书过期了不知道怎么复审延期?很多人遇到这种情况,要么是工作太忙没时间处理,要么是不知道具体流程,导致证书失效,影响工作和项目进展。其实复审并不难,关键是要找准官方报名入口,掌… · 2026/11/14 0:00:31
为什么要低压电工证实操考试考什么 为什么低压电工证值不值得考?别再被中介骗了 你是不是也经常看到网上有人在问“低压电工证到底值不值得考”?或者在找报名点时,担心被中间人骗钱?这些问题,我懂,我也经历过。作为一名在应急管理行业摸爬滚打多年的从业者,我深知低压电工证不仅是一个从… · 2026/11/14 0:00:31
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/13 6:12:21
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/13 6:11:51
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/13 22:31:30