2. CMS32 CAN模块硬件架构
做CAN通信,第一件事不是写代码,而是把硬件架构搞清楚。我见过太多人上来就调寄存器,结果发现收发器没焊、时钟配错了、引脚复用没开——折腾半天全是白费功夫。
这一章,咱们就把CMS32的CAN模块硬件底子摸透。说白了,就是三件事:内部长什么样、时钟怎么走、引脚怎么接。
2.1 CMS32 CAN控制器内部框图
先看整体结构。CMS32的CAN控制器是一个独立的硬件模块,挂在APB总线上。我习惯把它分成三块来看:
- 总线接口单元:负责和CPU、内存打交道。你读写寄存器、配置报文,都走这里。
- 核心协议引擎:这是CAN协议的硬件实现。它处理帧格式、位时序、CRC校验、错误检测。你不需要手撸协议,它全包了。
- 收发缓冲与滤波:包括发送邮箱、接收FIFO、验收滤波。这部分决定了你能同时处理多少报文。
下面这张图,是我自己画的一个简化框图,把核心模块和它们之间的数据流标出来了。
图2-1 CMS32 CAN控制器内部结构框图
注意看,验收滤波是在接收FIFO之前做的。这意味着不符合滤波规则的报文,根本进不了FIFO,CPU也不用管。这个设计能大幅降低中断负载——我在一个500kbps的工程里,靠合理配置滤波,把CPU占用率从37%降到了5%以下。
2.2 CAN模块时钟树与波特率配置
时钟是CAN通信的命脉。波特率不对,总线上一片乱码。CMS32的CAN时钟源通常来自APB时钟(PCLK),经过一个预分频器后生成CAN内核时钟。
我直接给公式:
CAN内核时钟 = PCLK / (CAN_BRP + 1)
位时间 = (Sync_Seg + Prop_Seg + Phase_Seg1 + Phase_Seg2) × CAN内核时钟周期
波特率 = 1 / 位时间
其中,CAN_BRP是波特率预分频器,范围0~1023。位时间各段的配置在寄存器BTR和BTR2里。
举个例子。假设PCLK=48MHz,目标波特率500kbps,采样点75%:
位时间 = 1 / 500k = 2000ns
CAN内核时钟周期 = 2000ns / 16 = 125ns → CAN内核时钟 = 8MHz
BRP = 48MHz / 8MHz - 1 = 5
位时间分配(共16个tq):
Sync_Seg = 1 tq
Prop_Seg = 3 tq
Phase_Seg1 = 8 tq
Phase_Seg2 = 4 tq
采样点 = (1 + 3 + 8) / 16 = 75%
我的经验:采样点建议设在75%~87.5%之间。总线越长、节点越多,采样点越靠后。我曾经在一条50米长的CAN总线上,把采样点从75%调到80%,误码率直接降了一个数量级。
下面这个表格,是我常用的波特率配置速查表,基于PCLK=48MHz:
| 目标波特率 | BRP | 位时间(tq) | Sync_Seg | Prop_Seg | Phase_Seg1 | Phase_Seg2 | 采样点 |
|---|---|---|---|---|---|---|---|
| 1 Mbps | 2 | 16 | 1 | 2 | 7 | 6 | 62.5% |
| 500 kbps | 5 | 16 | 1 | 3 | 8 | 4 | 75% |
| 250 kbps | 11 | 16 | 1 | 4 | 7 | 4 | 75% |
| 125 kbps | 23 | 16 | 1 | 5 | 6 | 4 | 75% |
注意:BRP寄存器是写时生效的。如果你在CAN运行中改了BRP,必须重新初始化CAN控制器,否则位时序会乱套。我曾经在调试时踩过这个坑——改了BRP没复位,结果总线上一堆错误帧,查了半天才发现。
2.3 CAN模块引脚功能与外部收发器连接
CMS32的CAN控制器输出的是CAN Tx和CAN Rx两个数字信号。它们不能直接驱动差分总线,必须外接CAN收发器。
引脚功能很简单:
- CAN_Tx:控制器输出,连接到收发器的TxD引脚。高电平对应隐性位,低电平对应显性位。
- CAN_Rx:控制器输入,连接到收发器的RxD引脚。收发器把总线差分信号转成单端数字信号。
收发器选型上,我常用的是TJA1050、SN65HVD230这类。它们把CAN控制器的逻辑电平转换成CANH/CANL差分信号。
典型连接电路:
CMS32 CAN_Tx ──→ 收发器 TxD
CMS32 CAN_Rx ←── 收发器 RxD
收发器 CANH ──→ 总线 CANH (双绞线)
收发器 CANL ──→ 总线 CANL (双绞线)
收发器 VCC ──→ 3.3V 或 5V (根据型号)
收发器 GND ──→ 系统地
收发器 STB/EN ──→ GPIO (控制工作/待机模式)
关键提醒:CAN总线两端必须各接一个120Ω终端电阻。没有终端电阻,信号反射会导致通信失败。我见过一个案例,整条总线20个节点,一个终端电阻都没焊,结果波特率一上500k就全崩。
另外,CMS32的CAN_Tx和CAN_Rx引脚通常是复用功能。你需要配置GPIO的复用模式,把引脚切换到CAN功能上。具体哪个引脚对应哪个CAN实例,要看数据手册的引脚功能表。
举个例子,假设CAN1的Tx在PA12,Rx在PA11:
// 使能GPIOA时钟
RCC->AHBENR |= RCC_AHBENR_GPIOAEN;
// 配置PA11为CAN1_RX (输入, 浮空)
GPIOA->MODER &= ~(3U << 22);
GPIOA->MODER |= (2U << 22); // 复用功能
GPIOA->AFR[1] &= ~(0xF << 12);
GPIOA->AFR[1] |= (9U << 12); // AF9 = CAN1
// 配置PA12为CAN1_TX (输出, 推挽)
GPIOA->MODER &= ~(3U << 24);
GPIOA->MODER |= (2U << 24); // 复用功能
GPIOA->AFR[1] &= ~(0xF << 16);
GPIOA->AFR[1] |= (9U << 16); // AF9 = CAN1
GPIOA->OSPEEDR |= (3U << 24); // 高速
一个小技巧:调试时可以在CAN_Tx和CAN_Rx上各串一个100Ω电阻再连收发器。万一收发器短路,这个电阻能保护单片机引脚。我吃过一次亏,收发器烧了,直接把单片机CAN引脚也拉坏了,从那以后我设计电路必加这个保护电阻。
嗯,硬件架构这部分就这些。内部框图让你知道模块怎么分工,时钟树让你算对波特率,引脚连接让你把信号送到总线上。这三样搞定了,CAN通信的硬件基础就算打牢了。