FlexCAN寄存器完全解读
目录
-
[FlexCAN 寄存器概述](#FlexCAN 寄存器概述)
-
[FIFO 相关寄存器](#FIFO 相关寄存器)
引言
FlexCAN 是 NXP 微控制器中广泛使用的 CAN 总线控制器,而寄存器是 FlexCAN 的核心组成部分,负责控制和监控 CAN 总线的所有操作。深入理解 FlexCAN 寄存器的功能和配置方法,是掌握 FlexCAN 编程的关键。
寄存器的重要性
-
硬件控制:直接控制 FlexCAN 硬件的工作模式和行为
-
状态监控:实时监控 CAN 总线的状态和错误情况
-
数据管理:管理发送和接收的 CAN 报文
-
中断控制:配置和管理中断事件
-
性能优化:通过寄存器配置优化 CAN 通信性能
寄存器的分类
FlexCAN 寄存器主要分为以下几类:
-
控制寄存器:控制 FlexCAN 的基本工作模式
-
状态寄存器:显示 FlexCAN 的当前状态
-
邮箱寄存器:管理 CAN 报文的发送和接收
-
中断寄存器:配置和管理中断事件
-
错误寄存器:监控和处理 CAN 总线错误
-
位定时寄存器:配置 CAN 总线的波特率和时序
FlexCAN 寄存器概述
寄存器地址映射
FlexCAN 寄存器映射到微控制器的内存空间,通常从基地址开始,按功能分组:
Plain
地址范围 | 功能描述
---------|----------
0x0000-0x004F | 核心控制和状态寄存器
0x0080-0x027F | 消息缓冲区(MB)寄存器
0x0880-0x08BC | 接收掩码寄存器
寄存器速查表
| 地址偏移 | 寄存器名称 | 功能分类 | 主要功能 |
|---|---|---|---|
| 0x0000 | CAN_MCR | 控制寄存器 | 模块配置和模式控制 |
| 0x0004 | CAN_CTRL1 | 控制寄存器 | 基本控制和时钟配置 |
| 0x0008 | CAN_TIMER | 状态寄存器 | 自由运行计数器 |
| 0x0010 | CAN_RXMGMASK | 过滤寄存器 | 接收邮箱全局掩码 |
| 0x0014 | CAN_RX14MASK | 过滤寄存器 | MB14 专用掩码 |
| 0x0018 | CAN_RX15MASK | 过滤寄存器 | MB15 专用掩码 |
| 0x001C | CAN_ECR | 错误寄存器 | 错误计数器 |
| 0x0020 | CAN_ESR1 | 状态寄存器 | 错误和状态标志 |
| 0x0028 | CAN_IMASK1 | 中断寄存器 | 中断掩码控制 |
| 0x0030 | CAN_IFLAG1 | 状态寄存器 | 中断标志状态 |
| 0x0034 | CAN_CTRL2 | 控制寄存器 | 扩展控制功能 |
| 0x0038 | CAN_ESR2 | 状态寄存器 | 扩展错误状态 |
| 0x0044 | CAN_CRCR | 状态寄存器 | CRC 计算结果 |
| 0x0048 | CAN_RXFGMASK | 过滤寄存器 | FIFO 全局掩码 |
| 0x004C | CAN_RXFIR | 状态寄存器 | FIFO 信息 |
| 0x0050 | CAN_CBT | 定时寄存器 | CAN 位定时配置 |
| 0x0080-0x017F | CAN_MB0-15 | 邮箱寄存器 | 消息缓冲区 0-15 |
| 0x0880-0x08BC | CAN_RXIMR0-15 | 过滤寄存器 | 接收单独掩码 |
核心控制寄存器详解
1. 模块配置寄存器 (CAN_MCR)
地址偏移: 0x0000
访问权限: 读 / 写(部分位仅在冻结模式下可写)
| 位段 | 位名称 | 功能描述 | 复位值 |
|---|---|---|---|
| 31 | SOFTRST | 软件复位 | 0 |
| 30 | FRZACK | 冻结模式确认 | 0 |
| 29 | LPMACK | 低功耗模式确认 | 0 |
| 28 | WRNEN | 警告中断使能 | 0 |
| 27 | NOTRDY | 模块未准备好状态 | 1 |
| 26 | HALT | 冻结模式请求 | 1 |
| 25 | FRZ | 冻结模式使能 | 0 |
| 24 | RXFRM | 接收帧状态 | 0 |
| 23 | RXACT | 接收活动状态 | 0 |
| 22 | CSWAK | 时钟停止唤醒 | 0 |
| 21 | SIDL | 休眠模式下停止 | 0 |
| 20 | IRMQ | 单独掩码模式 | 0 |
| 19 | SRXDIS | 自接收禁用 | 0 |
| 18 | DOZE | 休眠模式使能 | 0 |
| 17 | PEDN | 协议引擎禁用 | 0 |
| 16 | LPRIO_EN | 本地优先级使能 | 0 |
| 15-14 | IDAM | ID 过滤器模式 | 00 |
| 13-8 | MAXMB | 最大消息缓冲区 | 0x0F |
| 7 | DMA | DMA 使能 | 0 |
| 6 | PNET_EN | 假装网络使能 | 0 |
| 5 | RFEN | 接收 FIFO 使能 | 0 |
| 4 | AEN | 中止使能 | 0 |
| 3 | BCC | 总线关闭恢复 | 0 |
| 2 | EACEN | 扩展 ID 接受使能 | 0 |
| 1 | ECCEN | ECC 使能 | 0 |
| 0 | SUPV | 监控模式 | 0 |
| 关键位详解: |
-
SOFTRST (31 位):
-
0:正常操作
-
1:触发软件复位,复位后自动清零
-
-
HALT (26 位):
-
0:正常模式
-
1:进入冻结模式,允许配置寄存器
-
-
FRZ (25 位):
-
0:正常模式
-
1:调试模式下自动进入冻结模式
-
-
IRMQ (20 位):
-
0:使用全局掩码模式
-
1:使用单独掩码模式
-
-
SRXDIS (19 位):
-
0:允许自接收
-
1:禁止自接收
-
-
LPRIO_EN (16 位):
-
0:禁用本地优先级
-
1:启用本地优先级
-
-
MAXMB (13-8 位):
-
配置参与仲裁的最大消息缓冲区编号
-
0x0F:使用 16 个 MB (MB0-15)
-
-
RFEN (5 位):
-
0:禁用接收 FIFO
-
1:启用接收 FIFO
-
-
AEN (4 位):
-
0:禁用中止功能
-
1:启用中止功能
-
2. 控制寄存器 1 (CAN_CTRL1)
地址偏移: 0x0004
访问权限: 读 / 写(部分位仅在冻结模式下可写)
| 位段 | 位名称 | 功能描述 | 复位值 |
|---|---|---|---|
| 31-24 | PRESDIV | 预分频器 | 0x00 |
| 23 | CLKSRC | 时钟源选择 | 0 |
| 22 | LPB | 回环模式 | 0 |
| 21 | PROPSEG | 传播段 | 0x07 |
| 20-19 | RJW | 重同步跳转宽度 | 0x01 |
| 18-16 | PSEG1 | 相位段 1 | 0x04 |
| 15-13 | PSEG2 | 相位段 2 | 0x01 |
| 12 | SMP | 采样点 | 1 |
| 11 | BCC | 总线关闭恢复 | 0 |
| 10-8 | TSEG2 | 时间段 2 | 0x01 |
| 7-5 | TSEG1 | 时间段 1 | 0x0B |
| 4-3 | SJW | 同步跳转宽度 | 0x01 |
| 2 | WAKMSK | 唤醒掩码 | 0 |
| 1 | WAKFIL | 唤醒过滤器 | 0 |
| 0 | LBUF | 本地缓冲区模式 | 0 |
| 关键位详解: |
-
PRESDIV (31-24 位):
-
位时钟预分频器
-
计算公式:Bit Clock = CAN_CLK / (PRESDIV + 1)
-
-
CLKSRC (23 位):
-
0:选择 SOSCDIV2_CLK 为时钟源
-
1:选择 SYS_CLK 为时钟源
-
-
LPB (22 位):
-
0:正常模式
-
1:回环模式(自测模式)
-
-
PROPSEG (21 位):
-
传播段长度,范围:0-7
-
实际长度:PROPSEG + 1
-
-
RJW (20-19 位):
-
重同步跳转宽度,范围:0-3
-
实际宽度:RJW + 1
-
-
PSEG1 (18-16 位):
-
相位段 1 长度,范围:0-7
-
实际长度:PSEG1 + 1
-
-
PSEG2 (15-13 位):
-
相位段 2 长度,范围:0-7
-
实际长度:PSEG2 + 1
-
-
SMP (12 位):
-
0:每位采样一次
-
1:每位采样三次(推荐)
-
-
LBUF (0 位):
-
0:基于 ID 和优先级仲裁
-
1:基于邮箱编号仲裁
-
3. 控制寄存器 2 (CAN_CTRL2)
地址偏移: 0x0034
访问权限: 读 / 写
| 位段 | 位名称 | 功能描述 | 复位值 |
|---|---|---|---|
| 31-16 | 保留 | 保留 | - |
| 15-8 | RFFN | 接收 FIFO 过滤器数量 | 0x00 |
| 7-0 | 保留 | 保留 | - |
| 关键位详解: |
-
RFFN (15-8 位):
-
配置接收 FIFO 的过滤器数量
-
每个 RFFN 值对应 8 个过滤器
-
总过滤器数量 = RFFN * 8 + 8
-
邮箱结构寄存器
消息缓冲区结构
FlexCAN 的消息缓冲区 (MB) 是存储 CAN 报文的核心结构,每个 MB 占用 16 字节的连续内存空间:
Plain
地址偏移 | 字段 | 功能描述
---------|------|----------
0x0000 | CS | 控制和状态字
0x0004 | ID | 标识符字段
0x0008 | DATA[0-3] | 数据字节0-3
0x000C | DATA[4-7] | 数据字节4-7
控制和状态字 (CS)
| 位段 | 位名称 | 功能描述 |
|---|---|---|
| 31 | EDL | 扩展数据长度位 |
| 30 | BRS | 比特率切换位 |
| 29 | ESI | 错误状态指示位 |
| 28-24 | CODE | 消息缓冲区代码 |
| 23 | SRR | 替代远程请求位 |
| 22 | IDE | ID 扩展位 |
| 21 | RTR | 远程传输请求位 |
| 20-16 | DLC | 数据长度码 |
| 15-8 | PRIO | 本地优先级 |
| 7-0 | 保留 | 保留字段 |
| 关键位详解: |
-
EDL (31 位):
-
0:CAN 格式帧
-
1:CAN FD 格式帧
-
-
BRS (30 位):
-
0:不切换比特率
-
1:切换到更快的比特率(仅 CAN FD)
-
-
ESI (29 位):
-
0:错误主动状态
-
1:错误被动状态
-
-
CODE (28-24 位):
-
接收缓冲区代码:
-
0b0000:INACTIVE - 缓冲区未激活
-
0b0100:EMPTY - 缓冲区激活且为空
-
0b0010:FULL - 缓冲区已满
-
0b0110:OVERRUN - 缓冲区溢出
-
0b1010:RANSWER - 远程请求响应
-
-
发送缓冲区代码:
-
0b1000:INACTIVE - 缓冲区未激活
-
0b1001:ABORT - 发送中止
-
0b1100:DATA - 发送数据帧
-
0b1110:TANSWER - 发送响应帧
-
-
-
SRR (23 位):
-
仅用于扩展帧格式
-
1:强制使用扩展帧传输
-
-
IDE (22 位):
-
0:标准帧 (11 位 ID)
-
1:扩展帧 (29 位 ID)
-
-
RTR (21 位):
-
0:数据帧
-
1:远程请求帧
-
-
DLC (20-16 位):
-
数据长度码,范围:0-8
-
表示数据字段的字节数
-
-
PRIO (15-8 位):
-
本地优先级,范围:0-7
-
0:最低优先级,7:最高优先级
-
标识符字段 (ID)
| 位段 | 功能描述 |
|---|---|
| 31-3 | 保留 |
| 2-0 | 扩展帧 ID 的低 3 位 |
| 标准帧格式 (IDE=0): |
-
使用 ID 字段的高 11 位 (28-18 位)
-
低 18 位被忽略
扩展帧格式 (IDE=1):
-
使用 ID 字段的全部 29 位 (28-0 位)
-
SRR 位必须设置为 1
中断和错误寄存器
1. 错误计数器寄存器 (CAN_ECR)
地址偏移: 0x001C
访问权限: 读 / 写
| 位段 | 位名称 | 功能描述 | 复位值 |
|---|---|---|---|
| 31-16 | 保留 | 保留 | - |
| 15-8 | TEC | 发送错误计数器 | 0x00 |
| 7-0 | REC | 接收错误计数器 | 0x00 |
| 关键位详解: |
-
TEC (15-8 位):
-
发送错误计数器
-
范围:0-255
-
超过 127 时进入错误被动状态
-
超过 255 时进入总线关闭状态
-
-
REC (7-0 位):
-
接收错误计数器
-
范围:0-255
-
超过 127 时进入错误被动状态
-
2. 错误和状态寄存器 1 (CAN_ESR1)
地址偏移: 0x0020
访问权限: 读 / 写(清除位)
| 位段 | 位名称 | 功能描述 |
|---|---|---|
| 31 | BOFF_INT | 总线关闭中断 |
| 30 | EWARN_INT | 错误警告中断 |
| 29 | ELP_INT | 错误被动中断 |
| 28 | RX_WARN_INT | 接收警告中断 |
| 27 | TX_WARN_INT | 发送警告中断 |
| 26 | 保留 | 保留 |
| 25 | LEC | 最后错误代码 |
| 24 | TX_ERR | 发送错误状态 |
| 23 | RX_ERR | 接收错误状态 |
| 22 | 保留 | 保留 |
| 21 | TPM | 发送暂停模式 |
| 20 | SLP | 睡眠模式状态 |
| 19 | 保留 | 保留 |
| 18 | WUP_INT | 唤醒中断 |
| 17 | 保留 | 保留 |
| 16 | BOH_INT | 总线关闭恢复中断 |
| 15-0 | 保留 | 保留 |
| 关键位详解: |
-
BOFF_INT (31 位):
-
0:无总线关闭事件
-
1:检测到总线关闭事件
-
-
EWARN_INT (30 位):
-
0:无错误警告
-
1:错误计数器超过警告阈值
-
-
LEC (25 位):
-
最后错误代码:
-
000:无错误
-
001:位错误
-
010:格式错误
-
011:填充错误
-
100:CRC 错误
-
101:ACK 错误
-
110:位错误 (仲裁场)
-
111:位错误 (数据场)
-
-
3. 中断掩码寄存器 1 (CAN_IMASK1)
地址偏移: 0x0028
访问权限: 读 / 写
| 位段 | 位名称 | 功能描述 |
|---|---|---|
| 31-16 | 保留 | 保留 |
| 15-0 | BUF15I-BUF0I | 缓冲区 15-0 中断掩码 |
| 功能描述: |
-
每位对应一个消息缓冲区的中断使能
-
1:使能对应缓冲区的中断
-
0:禁用对应缓冲区的中断
4. 中断标志寄存器 1 (CAN_IFLAG1)
地址偏移: 0x0030
访问权限: 读 / 写(写 1 清除)
| 位段 | 位名称 | 功能描述 |
|---|---|---|
| 31-16 | 保留 | 保留 |
| 15-0 | BUF15I-BUF0I | 缓冲区 15-0 中断标志 |
| 功能描述: |
-
每位对应一个消息缓冲区的中断状态
-
1:对应缓冲区有中断事件
-
0:对应缓冲区无中断事件
-
写 1 清除对应位的中断标志
位定时和时钟寄存器
1. CAN 位定时寄存器 (CAN_CBT)
地址偏移: 0x0050
访问权限: 读 / 写(仅在冻结模式下可写)
| 位段 | 位名称 | 功能描述 | 复位值 |
|---|---|---|---|
| 31-24 | 保留 | 保留 | - |
| 23-16 | SJW | 同步跳转宽度 | 0x01 |
| 15-8 | TSEG1 | 时间段 1 | 0x0B |
| 7-0 | TSEG2 | 时间段 2 | 0x01 |
| 关键位详解: |
-
SJW (23-16 位):
-
同步跳转宽度,范围:0-255
-
实际宽度:SJW + 1
-
-
TSEG1 (15-8 位):
-
时间段 1 长度,范围:0-255
-
实际长度:TSEG1 + 1
-
-
TSEG2 (7-0 位):
-
时间段 2 长度,范围:0-255
-
实际长度:TSEG2 + 1
-
2. 自由运行定时器 (CAN_TIMER)
地址偏移: 0x0008
访问权限: 读
| 位段 | 位名称 | 功能描述 |
|---|---|---|
| 31-16 | 保留 | 保留 |
| 15-0 | TIMER | 自由运行计数器 |
| 功能描述: |
-
16 位自由运行计数器
-
用于时间戳功能
-
每个位时钟周期加 1
过滤器和掩码寄存器
1. 接收邮箱全局掩码 (CAN_RXMGMASK)
地址偏移: 0x0010
访问权限: 读 / 写(仅在冻结模式下可写)
| 位段 | 位名称 | 功能描述 |
|---|---|---|
| 31-0 | MASK | 32 位掩码 |
| 功能描述: |
-
用于 MB0-MB13 的接收过滤
-
1:对应位需要精确匹配
-
0:对应位可以是任意值
2. 接收缓冲区 14 掩码 (CAN_RX14MASK)
地址偏移: 0x0014
访问权限: 读 / 写(仅在冻结模式下可写)
| 位段 | 位名称 | 功能描述 |
|---|---|---|
| 31-0 | MASK | 32 位掩码 |
| 功能描述: |
-
专用于 MB14 的接收过滤
-
1:对应位需要精确匹配
-
0:对应位可以是任意值
3. 接收缓冲区 15 掩码 (CAN_RX15MASK)
地址偏移: 0x0018
访问权限: 读 / 写(仅在冻结模式下可写)
| 位段 | 位名称 | 功能描述 |
|---|---|---|
| 31-0 | MASK | 32 位掩码 |
| 功能描述: |
-
专用于 MB15 的接收过滤
-
1:对应位需要精确匹配
-
0:对应位可以是任意值
4. 接收单独掩码寄存器 (CAN_RXIMR0-15)
地址偏移: 0x0880-0x08BC
访问权限: 读 / 写(仅在冻结模式下可写)
| 位段 | 位名称 | 功能描述 |
|---|---|---|
| 31-0 | MASK | 32 位掩码 |
| 功能描述: |
-
每个 MB 对应一个单独的掩码寄存器
-
当 MCR [IRMQ]=1 时启用
-
1:对应位需要精确匹配
-
0:对应位可以是任意值
5. 接收 FIFO 全局掩码 (CAN_RXFGMASK)
地址偏移: 0x0048
访问权限: 读 / 写(仅在冻结模式下可写)
| 位段 | 位名称 | 功能描述 |
|---|---|---|
| 31-0 | MASK | 32 位掩码 |
| 功能描述: |
-
用于 FIFO 模式的接收过滤
-
1:对应位需要精确匹配
-
0:对应位可以是任意值
FIFO 相关寄存器
1. 接收 FIFO 信息寄存器 (CAN_RXFIR)
地址偏移: 0x004C
访问权限: 读
| 位段 | 位名称 | 功能描述 |
|---|---|---|
| 31-16 | 保留 | 保留 |
| 15-8 | IDHIT | ID 过滤器命中指示 |
| 7-0 | 保留 | 保留 |
| 功能描述: |
-
IDHIT 字段指示哪个过滤器匹配成功
-
用于调试和性能优化
2. FIFO 状态标志
FIFO 状态通过 CAN_IFLAG1 寄存器的以下位指示:
| 位名称 | 功能描述 |
|---|---|
| BUF7I | Rx FIFO 溢出 |
| BUF6I | Rx FIFO 警告 |
| BUF5I | Rx FIFO 中有可用帧 |
| BUF0I | 清除 FIFO 位 |
| 功能描述: |
-
BUF7I:FIFO 溢出,数据丢失
-
BUF6I:FIFO 达到警告阈值
-
BUF5I:FIFO 中有数据可读
-
BUF0I:写 1 清除 FIFO 状态
寄存器配置示例
1. FlexCAN 初始化配置
c
#include "fsl_common.h"
#include "fsl_can.h"
void FlexCAN_Init(void)
{
flexcan_config_t flexcanConfig;
/* 使能FlexCAN时钟 */
CLOCK_EnableClock(kCLOCK_Can0);
/* 进入冻结模式 */
CAN0->MCR = CAN_MCR_HALT_MASK;
while (!(CAN0->MCR & CAN_MCR_FRZACK_MASK));
/* 配置MCR寄存器 */
CAN0->MCR = 0;
CAN0->MCR |= CAN_MCR_MAXMB(15); // 使用16个MB(MB0-15)
CAN0->MCR |= CAN_MCR_IRMQ_MASK; // 启用单独掩码模式
CAN0->MCR |= CAN_MCR_RFEN_MASK; // 启用接收FIFO
CAN0->MCR |= CAN_MCR_AEN_MASK; // 启用中止功能
CAN0->MCR |= CAN_MCR_LPRIO_EN_MASK; // 启用本地优先级
/* 配置CTRL1寄存器 */
CAN0->CTRL1 = 0;
CAN0->CTRL1 |= CAN_CTRL1_PRESDIV(0); // 预分频器=0
CAN0->CTRL1 |= CAN_CTRL1_PROPSEG(7); // 传播段=8
CAN0->CTRL1 |= CAN_CTRL1_RJW(1); // 重同步跳转宽度=2
CAN0->CTRL1 |= CAN_CTRL1_PSEG1(4); // 相位段1=5
CAN0->CTRL1 |= CAN_CTRL1_PSEG2(1); // 相位段2=2
CAN0->CTRL1 |= CAN_CTRL1_SMP_MASK; // 每位采样三次
/* 配置CTRL2寄存器 */
CAN0->CTRL2 = 0;
CAN0->CTRL2 |= CAN_CTRL2_RFFN(0); // FIFO过滤器数量=8
/* 配置位定时寄存器 */
CAN0->CBT = 0;
CAN0->CBT |= CAN_CBT_SJW(1); // 同步跳转宽度=2
CAN0->CBT |= CAN_CBT_TSEG1(11); // 时间段1=12
CAN0->CBT |= CAN_CBT_TSEG2(1); // 时间段2=2
/* 配置全局掩码 */
CAN0->RXFGMASK = CAN_RXFGMASK_MASK(0x1FFFFFFF); // 扩展帧掩码
/* 配置邮箱 */
for (int i = 0; i < 16; i++) {
/* 禁用所有邮箱 */
CAN0->RAMn[i * 16 + 0] = 0;
CAN0->RAMn[i * 16 + 0] |= CAN_MB_CS_CODE(0b1000); // INACTIVE
}
/* 配置接收邮箱 */
CAN0->RAMn[2 * 16 + 0] = 0;
CAN0->RAMn[2 * 16 + 0] |= CAN_MB_CS_CODE(0b0100); // EMPTY
CAN0->RAMn[2 * 16 + 0] |= CAN_MB_CS_IDE_MASK; // 扩展帧
CAN0->RAMn[2 * 16 + 4] = CAN_MB_ID_ID(0x18FF0000); // ID=0x18FF0000
/* 配置发送邮箱 */
CAN0->RAMn[0 * 16 + 0] = 0;
CAN0->RAMn[0 * 16 + 0] |= CAN_MB_CS_CODE(0b1000); // INACTIVE
CAN0->RAMn[0 * 16 + 0] |= CAN_MB_CS_IDE_MASK; // 扩展帧
CAN0->RAMn[0 * 16 + 4] = CAN_MB_ID_ID(0x18FF0001); // ID=0x18FF0001
/* 配置单独掩码 */
CAN0->RXIMR[2] = 0x1FFFFFFF; // MB2精确匹配
/* 配置中断 */
CAN0->IMASK1 = 0;
CAN0->IMASK1 |= CAN_IMASK1_BUF5I_MASK; // 使能FIFO中断
CAN0->IMASK1 |= CAN_IMASK1_BUF0I_MASK; // 使能MB0中断
CAN0->IMASK1 |= CAN_IMASK1_BUF2I_MASK; // 使能MB2中断
/* 退出冻结模式 */
CAN0->MCR &= ~CAN_MCR_HALT_MASK;
while (CAN0->MCR & CAN_MCR_FRZACK_MASK);
while (CAN0->MCR & CAN_MCR_NOTRDY_MASK);
}
2. 发送报文配置
c
void FlexCAN_SendMessage(uint32_t mbId, uint32_t msgId,
uint8_t *data, uint8_t length)
{
/* 检查邮箱是否空闲 */
if ((CAN0->RAMn[mbId * 16 + 0] & CAN_MB_CS_CODE_MASK) != CAN_MB_CS_CODE(0b1000)) {
/* 邮箱忙,请求中止 */
CAN0->RAMn[mbId * 16 + 0] = CAN_MB_CS_CODE(0b1001); // ABORT
while ((CAN0->RAMn[mbId * 16 + 0] & CAN_MB_CS_CODE_MASK) != CAN_MB_CS_CODE(0b1000));
}
/* 配置邮箱 */
CAN0->RAMn[mbId * 16 + 0] = 0;
CAN0->RAMn[mbId * 16 + 0] |= CAN_MB_CS_CODE(0b1100); // DATA
CAN0->RAMn[mbId * 16 + 0] |= CAN_MB_CS_IDE_MASK; // 扩展帧
CAN0->RAMn[mbId * 16 + 0] |= CAN_MB_CS_DLC(length); // 数据长度
CAN0->RAMn[mbId * 16 + 0] |= CAN_MB_CS_PRIO(7); // 最高优先级
/* 设置ID */
CAN0->RAMn[mbId * 16 + 4] = CAN_MB_ID_ID(msgId);
/* 设置数据 */
for (int i = 0; i < length; i++) {
if (i < 4) {
CAN0->RAMn[mbId * 16 + 8] |= data[i] << (i * 8);
} else {
CAN0->RAMn[mbId * 16 + 12] |= data[i] << ((i - 4) * 8);
}
}
/* 等待发送完成 */
while (!(CAN0->IFLAG1 & (1 << mbId)));
/* 清除中断标志 */
CAN0->IFLAG1 = 1 << mbId;
}
3. 接收报文配置
c
void FlexCAN_ReceiveMessage(uint32_t mbId, flexcan_msgbuff_t *msg)
{
/* 等待接收完成 */
while (!(CAN0->IFLAG1 & (1 << mbId)));
/* 读取控制和状态字 */
uint32_t cs = CAN0->RAMn[mbId * 16 + 0];
/* 读取ID */
uint32_t id = CAN0->RAMn[mbId * 16 + 4];
/* 读取数据 */
uint32_t dataLow = CAN0->RAMn[mbId * 16 + 8];
uint32_t dataHigh = CAN0->RAMn[mbId * 16 + 12];
/* 填充消息结构体 */
msg->cs = cs;
msg->msgId = id;
msg->dataLen = (cs >> CAN_MB_CS_DLC_SHIFT) & 0x1F;
/* 复制数据 */
for (int i = 0; i < msg->dataLen; i++) {
if (i < 4) {
msg->data[i] = (dataLow >> (i * 8)) & 0xFF;
} else {
msg->data[i] = (dataHigh >> ((i - 4) * 8)) & 0xFF;
}
}
/* 清除中断标志 */
CAN0->IFLAG1 = 1 << mbId;
/* 重新启用接收 */
CAN0->RAMn[mbId * 16 + 0] = 0;
CAN0->RAMn[mbId * 16 + 0] |= CAN_MB_CS_CODE(0b0100); // EMPTY
CAN0->RAMn[mbId * 16 + 0] |= CAN_MB_CS_IDE_MASK; // 扩展帧
}
4. FIFO 配置和使用
c
void FlexCAN_FIFO_Init(void)
{
/* 进入冻结模式 */
CAN0->MCR |= CAN_MCR_HALT_MASK;
while (!(CAN0->MCR & CAN_MCR_FRZACK_MASK));
/* 配置FIFO */
CAN0->MCR |= CAN_MCR_RFEN_MASK; // 启用接收FIFO
CAN0->CTRL2 |= CAN_CTRL2_RFFN(2); // FIFO过滤器数量=24
/* 配置FIFO过滤器 */
for (int i = 0; i < 24; i++) {
uint32_t filterAddr = 0x00E0 + i * 8;
/* 配置过滤器ID */
CAN0->RAMn[filterAddr] = CAN_MB_ID_ID(0x18FF0000 + i);
/* 配置过滤器掩码 */
CAN0->RAMn[filterAddr + 4] = CAN_MB_ID_ID(0x1FFFFFFF);
}
/* 退出冻结模式 */
CAN0->MCR &= ~CAN_MCR_HALT_MASK;
while (CAN0->MCR & CAN_MCR_FRZACK_MASK);
}
void FlexCAN_FIFO_Read(flexcan_msgbuff_t *msg)
{
/* 等待FIFO有数据 */
while (!(CAN0->IFLAG1 & CAN_IFLAG1_BUF5I_MASK));
/* 读取FIFO数据 */
msg->cs = CAN0->RAMn[0x0080];
msg->msgId = CAN0->RAMn[0x0084];
msg->data[0] = (CAN0->RAMn[0x0088] >> 0) & 0xFF;
msg->data[1] = (CAN0->RAMn[0x0088] >> 8) & 0xFF;
msg->data[2] = (CAN0->RAMn[0x0088] >> 16) & 0xFF;
msg->data[3] = (CAN0->RAMn[0x0088] >> 24) & 0xFF;
msg->data[4] = (CAN0->RAMn[0x008C] >> 0) & 0xFF;
msg->data[5] = (CAN0->RAMn[0x008C] >> 8) & 0xFF;
msg->data[6] = (CAN0->RAMn[0x008C] >> 16) & 0xFF;
msg->data[7] = (CAN0->RAMn[0x008C] >> 24) & 0xFF;
msg->dataLen = (msg->cs >> CAN_MB_CS_DLC_SHIFT) & 0x1F;
/* 清除FIFO中断 */
CAN0->IFLAG1 = CAN_IFLAG1_BUF5I_MASK;
}
常见问题与解决方案
1. 无法进入冻结模式
问题描述: 设置 MCR [HALT] 位后,FRZACK 位不置位
可能原因:
-
FlexCAN 时钟未正确配置
-
其他模块正在访问 FlexCAN
-
硬件故障
解决方案:
c
/* 正确的冻结模式进入流程 */
CAN0->MCR = CAN_MCR_HALT_MASK;
for (int i = 0; i < 1000; i++) {
if (CAN0->MCR & CAN_MCR_FRZACK_MASK) {
break;
}
}
if (!(CAN0->MCR & CAN_MCR_FRZACK_MASK)) {
/* 重试或错误处理 */
CAN0->MCR = CAN_MCR_SOFTRST_MASK; // 软件复位
for (int i = 0; i < 100000; i++); // 等待复位完成
}
2. 邮箱配置不生效
问题描述: 配置邮箱后无法发送或接收报文
可能原因:
-
邮箱未正确激活
-
ID 或掩码配置错误
-
未退出冻结模式
解决方案:
c
/* 正确的邮箱配置流程 */
void ConfigureMailbox(uint32_t mbId, uint32_t msgId, bool isTx)
{
/* 进入冻结模式 */
CAN0->MCR |= CAN_MCR_HALT_MASK;
while (!(CAN0->MCR & CAN_MCR_FRZACK_MASK));
/* 配置邮箱 */
CAN0->RAMn[mbId * 16 + 0] = 0;
if (isTx) {
CAN0->RAMn[mbId * 16 + 0] |= CAN_MB_CS_CODE(0b1000); // INACTIVE
} else {
CAN0->RAMn[mbId * 16 + 0] |= CAN_MB_CS_CODE(0b0100); // EMPTY
}
CAN0->RAMn[mbId * 16 + 0] |= CAN_MB_CS_IDE_MASK; // 扩展帧
CAN0->RAMn[mbId * 16 + 4] = CAN_MB_ID_ID(msgId); // 设置ID
/* 配置掩码 */
if (!isTx) {
CAN0->RXIMR[mbId] = CAN_RXIMR_MASK(0x1FFFFFFF); // 精确匹配
}
/* 退出冻结模式 */
CAN0->MCR &= ~CAN_MCR_HALT_MASK;
while (CAN0->MCR & CAN_MCR_FRZACK_MASK);
}
3. 中断不触发
问题描述: 配置中断后,中断标志位不置位
可能原因:
-
中断掩码未正确配置
-
邮箱未正确激活
-
优先级配置错误
解决方案:
c
/* 正确的中断配置 */
void ConfigureInterrupts(void)
{
/* 配置中断掩码 */
CAN0->IMASK1 = 0;
CAN0->IMASK1 |= CAN_IMASK1_BUF0I_MASK; // 使能MB0中断
CAN0->IMASK1 |= CAN_IMASK1_BUF1I_MASK; // 使能MB1中断
CAN0->IMASK1 |= CAN_IMASK1_BUF5I_MASK; // 使能FIFO中断
/* 配置NVIC */
NVIC_SetPriority(CAN0_IRQn, 5);
NVIC_EnableIRQ(CAN0_IRQn);
}
/* 中断服务函数 */
void CAN0_IRQHandler(void)
{
uint32_t flags = CAN0->IFLAG1;
if (flags & CAN_IFLAG1_BUF0I_MASK) {
/* 处理MB0中断 */
CAN0->IFLAG1 = CAN_IFLAG1_BUF0I_MASK;
}
if (flags & CAN_IFLAG1_BUF1I_MASK) {
/* 处理MB1中断 */
CAN0->IFLAG1 = CAN_IFLAG1_BUF1I_MASK;
}
if (flags & CAN_IFLAG1_BUF5I_MASK) {
/* 处理FIFO中断 */
CAN0->IFLAG1 = CAN_IFLAG1_BUF5I_MASK;
}
}
4. FIFO 溢出问题
问题描述: FIFO 经常溢出,数据丢失
可能原因:
-
FIFO 深度配置不足
-
读取 FIFO 不及时
-
总线负载过高
解决方案:
c
/* 优化的FIFO配置 */
void OptimizeFIFOConfig(void)
{
/* 进入冻结模式 */
CAN0->MCR |= CAN_MCR_HALT_MASK;
while (!(CAN0->MCR & CAN_MCR_FRZACK_MASK));
/* 增加FIFO深度 */
CAN0->CTRL2 = CAN_CTRL2_RFFN(12); // FIFO深度=104
/* 配置FIFO警告阈值 */
// 警告阈值 = FIFO深度 - 预留空间
// 例如:104 - 16 = 88
/* 启用DMA */
CAN0->MCR |= CAN_MCR_DMA_MASK;
/* 退出冻结模式 */
CAN0->MCR &= ~CAN_MCR_HALT_MASK;
while (CAN0->MCR & CAN_MCR_FRZACK_MASK);
}
/* 高效的FIFO读取 */
void EfficientFIFORead(void)
{
static flexcan_msgbuff_t fifoBuffer[32];
static uint32_t bufferIndex = 0;
/* 批量读取FIFO */
while (CAN0->IFLAG1 & CAN_IFLAG1_BUF5I_MASK) {
/* 读取FIFO数据 */
fifoBuffer[bufferIndex].cs = CAN0->RAMn[0x0080];
fifoBuffer[bufferIndex].msgId = CAN0->RAMn[0x0084];
fifoBuffer[bufferIndex].data[0] = (CAN0->RAMn[0x0088] >> 0) & 0xFF;
fifoBuffer[bufferIndex].data[1] = (CAN0->RAMn[0x0088] >> 8) & 0xFF;
fifoBuffer[bufferIndex].data[2] = (CAN0->RAMn[0x0088] >> 16) & 0xFF;
fifoBuffer[bufferIndex].data[3] = (CAN0->RAMn[0x0088] >> 24) & 0xFF;
fifoBuffer[bufferIndex].data[4] = (CAN0->RAMn[0x008C] >> 0) & 0xFF;
fifoBuffer[bufferIndex].data[5] = (CAN0->RAMn[0x008C] >> 8) & 0xFF;
fifoBuffer[bufferIndex].data[6] = (CAN0->RAMn[0x008C] >> 16) & 0xFF;
fifoBuffer[bufferIndex].data[7] = (CAN0->RAMn[0x008C] >> 24) & 0xFF;
fifoBuffer[bufferIndex].dataLen =
(fifoBuffer[bufferIndex].cs >> CAN_MB_CS_DLC_SHIFT) & 0x1F;
bufferIndex = (bufferIndex + 1) % 32;
/* 清除FIFO中断 */
CAN0->IFLAG1 = CAN_IFLAG1_BUF5I_MASK;
}
/* 处理接收到的数据 */
ProcessReceivedData(fifoBuffer, bufferIndex);
}
5. 波特率配置错误
问题描述: 波特率配置后通信不稳定
可能原因:
-
位定时参数计算错误
-
时钟源配置错误
-
硬件问题
解决方案:
c
/* 精确的波特率配置 */
void ConfigureBaudrate(uint32_t baudrate)
{
uint32_t canClock = CLOCK_GetFreq(kCLOCK_Osc0);
uint32_t presdiv, propseg, rjw, pseg1, pseg2;
uint32_t bestError = UINT32_MAX;
/* 计算最佳位定时参数 */
for (presdiv = 0; presdiv < 256; presdiv++) {
for (propseg = 1; propseg < 8; propseg++) {
for (rjw = 1; rjw < 4; rjw++) {
for (pseg1 = 1; pseg1 < 8; pseg1++) {
for (pseg2 = 1; pseg2 < 8; pseg2++) {
uint32_t totalTq = 1 + propseg + pseg1 + pseg2;
uint32_t calculatedBaud = canClock / ((presdiv + 1) * totalTq);
uint32_t error = abs((int)(calculatedBaud - baudrate));
if (error < bestError) {
bestError = error;
// 保存最佳参数
}
}
}
}
}
}
/* 进入冻结模式 */
CAN0->MCR |= CAN_MCR_HALT_MASK;
while (!(CAN0->MCR & CAN_MCR_FRZACK_MASK));
/* 配置位定时参数 */
CAN0->CTRL1 = 0;
CAN0->CTRL1 |= CAN_CTRL1_PRESDIV(presdiv);
CAN0->CTRL1 |= CAN_CTRL1_PROPSEG(propseg - 1);
CAN0->CTRL1 |= CAN_CTRL1_RJW(rjw - 1);
CAN0->CTRL1 |= CAN_CTRL1_PSEG1(pseg1 - 1);
CAN0->CTRL1 |= CAN_CTRL1_PSEG2(pseg2 - 1);
CAN0->CTRL1 |= CAN_CTRL1_SMP_MASK;
/* 退出冻结模式 */
CAN0->MCR &= ~CAN_MCR_HALT_MASK;
while (CAN0->MCR & CAN_MCR_FRZACK_MASK);
}