2. CMS32 CAN模块硬件架构

做CAN通信,第一件事不是写代码,而是把硬件架构搞清楚。我见过太多人上来就调寄存器,结果发现收发器没焊、时钟配错了、引脚复用没开——折腾半天全是白费功夫。

这一章,咱们就把CMS32的CAN模块硬件底子摸透。说白了,就是三件事:内部长什么样、时钟怎么走、引脚怎么接。

2.1 CMS32 CAN控制器内部框图

先看整体结构。CMS32的CAN控制器是一个独立的硬件模块,挂在APB总线上。我习惯把它分成三块来看:

  • 总线接口单元:负责和CPU、内存打交道。你读写寄存器、配置报文,都走这里。
  • 核心协议引擎:这是CAN协议的硬件实现。它处理帧格式、位时序、CRC校验、错误检测。你不需要手撸协议,它全包了。
  • 收发缓冲与滤波:包括发送邮箱、接收FIFO、验收滤波。这部分决定了你能同时处理多少报文。

下面这张图,是我自己画的一个简化框图,把核心模块和它们之间的数据流标出来了。

图2-1 CMS32 CAN控制器内部结构框图

CMS32 CAN控制器内部结构框图 APB 总线接口 配置寄存器组 (CR1/CR2/BTR/BTR2) CAN 核心协议引擎 位时序 · 帧编码 · CRC · 错误管理 发送邮箱 (TxMailbox x3) 优先级仲裁 · 自动重发 接收 FIFO (RxFIFO x2) FIFO 溢出保护 · 时间戳 验收滤波单元 (Filter Bank x28, 可配置为屏蔽位/列表模式) Tx/Rx 引脚控制 & 模式选择 数据流方向:自上而下

注意看,验收滤波是在接收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通信的硬件基础就算打牢了。