LPC21xx/22xx ARM7 RTC与CAN控制器寄存器配置实战指南
2026/6/20 23:19:00 网站建设 项目流程

1. 项目概述与核心价值

在嵌入式系统开发中,尤其是工业控制、汽车电子和智能仪表领域,有两个外设模块的稳定性和精确性直接决定了系统的可靠性与智能化水平:实时时钟(RTC)控制器局域网(CAN)。前者是系统感知物理时间的“心跳”,后者则是设备间高效、可靠通信的“神经网络”。NXP的LPC21xx/22xx系列ARM7微控制器,作为一代经典,其内部集成的RTC与CAN控制器设计精妙,功能强大,但官方数据手册(UM10114)的寄存器描述往往分散且偏重理论,对于一线工程师而言,如何将这些寄存器位转化为稳定运行的代码,中间隔着不少“坑”。

我接触LPC21xx/22xx系列超过十年,从早期的LPC2106到后来的LPC2294,在多个量产项目中深度调校过这两个模块。我发现,很多开发者仅仅满足于调用库函数,一旦遇到时钟不准、CAN通信丢帧或验收滤波失灵等问题,往往束手无策。究其根源,是对寄存器底层工作机制理解不透。本文的目的,就是带你穿透数据手册的表格,结合我踩过的坑和积累的经验,将LPC21xx/22xx的RTC与CAN控制器寄存器“掰开揉碎”,讲清楚每一个关键位背后的设计逻辑、配置时的“为什么”以及实操中的“怎么办”。无论你是正在评估该系列芯片,还是正在调试相关功能,这篇文章都能为你提供从原理到实战的完整参考。

2. LPC21xx/22xx RTC模块深度解析与实战配置

实时时钟模块的价值在于为无操作系统的单片机或需要低功耗运行的复杂系统提供一个独立于CPU主频的、持续运行的时间基准。LPC21xx/22xx的RTC设计相对传统但完整,包含时间计数、闹钟、中断和时钟校准等核心功能。

2.1 RTC整体架构与时钟链分析

LPC21xx/22xx的RTC并非由一个独立的32.768kHz晶振直接驱动,这是一个关键认知点。它的时钟源来自APB总线时钟(PCLK)。这意味着RTC的精度和稳定性与系统主时钟同源,如果PCLK因PLL重配置或电源模式切换而中断,RTC计时就会产生累积误差。因此,在系统设计初期,就必须规划好PCLK的稳定供给方案,例如在进入低功耗模式前,确保RTC已切换到不受影响的时钟源(如果有的话),或者接受此时的时间漂移。

时钟链的核心是预分频器(Prescaler)。它的任务是将高频的PCLK(例如常见的10MHz, 15MHz)精确分频成32.768kHz的RTC基准时钟(Clk1)。这个分频不是简单的整数除法,而是通过一个包含整数部分(PREINT)和小数部分(PREFRAC)的算法,来逼近理论分频比,确保每秒精确产生32768个时钟滴答。理解这个分频原理,是解决RTC走时快慢问题的钥匙。

注意:数据手册强调,芯片掉电将导致所有RTC寄存器内容丢失。这意味着你必须设计后备电源电路(如纽扣电池+二极管)来维持RTC供电,或者在每次上电时通过其他途径(如网络授时、用户设置)来初始化时间。这是产品化设计中必须考虑的硬件环节。

2.2 关键寄存器详解与配置流程

官方手册列出了十多个寄存器,但在实际应用中,我们通常按功能分组来理解和操作。

2.2.1 时钟控制与初始化流程

配置RTC的第一步是停止时钟、进行初始化,这通过时钟控制寄存器(CCR,地址0xE0024008)完成。

  • CCR[0] - CLKEN(时钟使能):此位为1时,时间计数器开始递增。关键操作:在初始化或修改时间值前,必须先将其清零以禁用计数器,否则在计数器运行时写入可能产生不可预知的结果。
  • CCR[1] - CTCRST(时钟滴答计数器复位):此位置1会复位内部的时钟滴答计数器(CTC)。通常我们在初始化时,将其置1后再清零,以确保计时从绝对零点开始。CTTEST位(CCR[3:2])为测试保留,必须保持为0。

标准的RTC初始化代码流程如下:

// 假设:PCLK = 10MHz (10,000,000 Hz) #define RTC_BASE 0xE0024000 #define CCR (*(volatile unsigned int *)(RTC_BASE + 0x08)) #define PREINT (*(volatile unsigned int *)(RTC_BASE + 0x80)) #define PREFRAC (*(volatile unsigned int *)(RTC_BASE + 0x84)) void RTC_Init(void) { // 1. 禁用RTC时钟,准备初始化 CCR &= ~(1 << 0); // CLKEN = 0 // 2. 复位时钟滴答计数器 CCR |= (1 << 1); // CTCRST = 1 // 短暂延时,确保复位完成 for(int i=0; i<10; i++); CCR &= ~(1 << 1); // CTCRST = 0 // 3. 配置预分频器,将PCLK分频至32.768kHz // PREINT = int(PCLK / 32768) - 1 // PREFRAC = PCLK - ((PREINT + 1) * 32768) unsigned int pclk = 10000000; unsigned int preint = (pclk / 32768) - 1; unsigned int prefrac = pclk - ((preint + 1) * 32768); PREINT = preint & 0x1FFF; // 13位整数部分 PREFRAC = prefrac & 0x7FFF; // 15位小数部分 // 4. 设置初始时间(例如:2023年1月1日 00:00:00, 周日) // 注意:需要先写入各独立时间计数器寄存器(SEC, MIN, HOUR等) // 地址:SEC 0xE0024020, MIN 0xE0024024, ... YEAR 0xE002403C // 此处省略具体写入代码... // 5. 使能RTC时钟 CCR |= (1 << 0); // CLKEN = 1 }

实操心得:计算PREFRAC时,务必使用整数运算。如果PREFRAC不为0,意味着产生的32.768kHz时钟存在“抖动”(某些时钟周期会比其他的长一个PCLK),但这种抖动被均匀分布,对于仅用于计时的RTC而言完全可接受,不影响秒、分等高级别计数的精度。

2.2.2 时间读取:独立寄存器与合并寄存器

读取当前时间有两种方式,各有优劣。

  1. 独立时间计数器寄存器(SEC, MIN, HOUR, DOM, DOW, DOY, MONTH, YEAR):地址从0xE0024020开始。这种方式直观,但读取一个完整时间需要8次总线操作。在读取过程中,如果恰好遇到计数器进位(如23:59:59到00:00:00),可能会读到不一致的时间(比如59秒、0分、23小时)。因此,必须实现“两次读取校验法”:连续读取两次时间,如果两次读取的秒数相同,则认为数据有效;否则重新读取,直到连续两次秒数一致。

  2. 合并时间寄存器(CTIME0, CTIME1, CTIME2):这是LPC RTC的一个贴心设计。它将多个时间值打包到32位寄存器中,只需3次读操作就能获取全部时间信息,极大提高了读取效率,也降低了在读取瞬间因进位导致数据错位的概率。

    • CTIME0 (0xE0024014):包含秒、分、时、星期。
    • CTIME1 (0xE0024018):包含日、月、年。
    • CTIME2 (0xE002401C):包含一年中的第几天。

推荐在大多数应用中使用合并寄存器读取时间。示例代码:

unsigned int ctime0, ctime1; unsigned char second, minute, hour, day, month, year; ctime0 = *((volatile unsigned int *)0xE0024014); ctime1 = *((volatile unsigned int *)0xE0024018); second = (ctime0 >> 0) & 0x3F; // 位[5:0] minute = (ctime0 >> 8) & 0x3F; // 位[13:8] hour = (ctime0 >> 16) & 0x1F; // 位[20:16] day = (ctime1 >> 0) & 0x1F; // 位[4:0] month = (ctime1 >> 8) & 0x0F; // 位[11:8] year = (ctime1 >> 16) & 0xFFF; // 位[27:16]
2.2.3 中断与闹钟功能实战

RTC的中断系统主要由三个寄存器控制:计数器增量中断寄存器(CIIR)闹钟屏蔽寄存器(AMR)和一组闹钟值寄存器(ALSEC, ALMIN等)

  • CIIR寄存器(0xE002400C):每一位对应一个时间单位(秒、分、时等)。将该位置1,则对应计数器每次递增时都会产生中断。例如,将IMSEC置1,则每秒都会产生一次中断。这适用于需要极高精度定时(如每秒执行一次任务)的场景,但中断频率高,需谨慎使用。

  • 闹钟功能:这是更常用的定时触发方式。你需要:

    1. 闹钟值寄存器中设置期望触发的时间点(如ALSEC=30, ALMIN=15, ALHOUR=9)。
    2. 通过AMR寄存器(0xE0024010)选择需要匹配的时间单位。AMR的某位为1,表示忽略该单位的比较。例如,你只想在每天9点15分触发,而不关心秒和日期,则应设置AMRSEC=1,AMRMIN=0,ALRHOUR=0,并设置AMRDOM=1,AMRDOW=1等以屏蔽日期比较。
    3. 当所有未被屏蔽的闹钟寄存器值与对应的时间计数器值匹配时,RTC会产生一个中断。

中断处理流程: 当RTC中断发生时,你需要查询中断位置寄存器(ILR,地址0xE0024000)来确定中断源。

  • ILR[0] (RTCCIF):为1表示计数器增量中断(由CIIR配置引起)。写入1清除此中断标志。
  • ILR[1] (RTCALF):为1表示闹钟中断。写入1清除此中断标志。

一个完整的闹钟设置示例(设置每天9点15分30秒触发中断):

void RTC_SetAlarmDaily(unsigned char hour, unsigned char min, unsigned char sec) { // 1. 禁用RTC中断(可选,防止配置过程中误触发) // 2. 设置闹钟时间值 *((volatile unsigned int *)0xE0024060) = sec & 0x3F; // ALSEC *((volatile unsigned int *)0xE0024064) = min & 0x3F; // ALMIN *((volatile unsigned int *)0xE0024068) = hour & 0x1F; // ALHOUR // 其他日期相关闹钟寄存器可以不设置或随意设置,因为会被屏蔽 // 3. 配置闹钟屏蔽寄存器:只比较时、分、秒,忽略所有日期字段 // AMR bit = 1 表示屏蔽(不比较) unsigned int amr_value = 0; amr_value |= (1 << 3); // AMRDOM = 1, 屏蔽日 amr_value |= (1 << 4); // AMRDOW = 1, 屏蔽星期 amr_value |= (1 << 5); // AMRDOY = 1, 屏蔽年日 amr_value |= (1 << 6); // AMRMON = 1, 屏蔽月 amr_value |= (1 << 7); // AMRYEAR = 1, 屏蔽年 // AMRSEC, AMRMIN, AMRHOUR 保持为0,表示需要比较 *((volatile unsigned int *)0xE0024010) = amr_value; // 4. 使能RTC闹钟中断(需要配合NVIC设置) // 5. 在RTC中断服务程序中,判断ILR[1]并清除标志 }

2.3 RTC常见问题排查与经验总结

  1. RTC走时不准

    • 首要检查PCLK频率:确认提供给RTC的PCLK频率计算是否正确,PREINTPREFRAC寄存器配置是否与理论值一致。用示波器测量PCLK频率是最直接的方法。
    • 检查电源稳定性:如果使用了后备电池,测量其电压是否在芯片要求范围内(通常2.0V-3.6V)。电压过低可能导致RTC工作异常。
    • 软件干扰:确认在系统运行中,没有其他程序(如配置PLL)意外改变了PCLK的频率或导致其短暂中断。
  2. 闹钟不触发

    • 检查AMR配置:这是最常见的原因。确保你真正想比较的时间单位对应的AMR位是0(使能比较),而非1(屏蔽)。
    • 检查中断使能:配置了闹钟寄存器后,是否在中断控制器(如VIC)中使能了RTC中断?是否在RTC模块本身正确清除了可能存在的旧中断标志?
    • 时间设置错误:确认你设置的时间是24小时制,且日期值在有效范围内(如月份为1-12)。
  3. 读写时间数据不一致

    • **始终使用“两次读取校验法”**读取独立计数器,或直接使用合并时间寄存器(CTIME0/1/2)。
    • 在修改时间前,务必先将CCR寄存器的CLKEN位清零,停止计数器。
  4. 闰年计算:LPC21xx/22xx的RTC使用简单的“年份低2位为0即为闰年”的算法,这在2000-2099年间是准确的,但在2100年会产生错误(2100年不是闰年)。如果你的产品生命周期很长,需要在软件中对此进行修正。

3. LPC21xx/22xx CAN控制器架构与寄存器精讲

CAN总线因其高可靠性、多主结构和优秀的错误处理机制,成为汽车和工业网络的基石。LPC21xx/22xx的CAN控制器设计借鉴了Philips SJA1000,但针对32位ARM内核做了优化,并将验收滤波功能独立出来,形成了一个全局的验收滤波器(Acceptance Filter),这是其一大特色。

3.1 CAN控制器整体架构与工作流程

该系列CAN控制器的核心特点在于总线接口单元与验收滤波的分离。每个CAN控制器(CAN1-CAN4)负责处理CAN协议的核心功能:帧发送、接收、位定时、错误管理等。而所有控制器的报文验收过滤工作,都交给一个独立的、共享的全局验收滤波器来完成。

工作流程简述

  1. 发送:CPU将待发送的报文(标识符、数据、帧信息)写入对应CAN控制器的发送缓冲区(Tx Buffer),然后通过命令寄存器(CMR)发起发送请求。
  2. 接收:CAN总线上的报文被控制器接收后,其标识符(ID)会先被送到全局验收滤波器进行匹配判断。
  3. 滤波:验收滤波器根据用户预设的过滤规则(存储在它的RAM中),判断该报文是否应该被接收。如果匹配,则将该报文存入对应CAN控制器的接收缓冲区(Rx Buffer),并可能产生中断。
  4. CPU读取:CPU通过读取接收缓冲区相关的寄存器(RFS, RID, RDA, RDB)来获取报文内容。

这种架构的优势在于,多个CAN控制器可以共享同一套复杂的过滤规则,节省了CPU进行软件过滤的开销,尤其适合作为CAN网关或需要处理大量不同ID报文的节点。

3.2 核心寄存器配置详解

CAN控制器的寄存器数量较多,我们按功能分组,重点讲解最关键的几个。

3.2.1 模式寄存器(MOD)与总线定时寄存器(BTR)

这是CAN通信的基石,配置错误将导致根本无法通信或通信极不稳定。

模式寄存器(MOD, 如CAN1MOD: 0xE0044000)

  • RM (Bit 0)复位模式。这是最重要的位。任何对CAN控制器的配置(如BTR, EWL等),都必须在该位置1的情况下进行。配置完成后,需将其清零,控制器才能进入正常工作模式。
  • LOM (Bit 1):只听模式。置1后,控制器只接收报文,不发送任何报文(包括ACK应答位)。用于网络监听、波特率检测。
  • STM (Bit 2):自测试模式。置1后,控制器不需要来自总线的ACK即可认为发送成功。通常与CMR寄存器的SRR位结合,用于控制器内部回环测试,在不连接物理总线的情况下验证CAN驱动代码。
  • 其他位:如睡眠模式(SM)、反向极性模式(RPM)等,根据具体硬件和应用需求配置。

总线定时寄存器(BTR, 如CAN1BTR: 0xE0044014): 这个寄存器决定了CAN通信的波特率、采样点位置等关键时序参数,配置极为重要。它主要包含两个部分:波特率预分频器(BRP)位时序段(Tseg1, Tseg2, SJW)

波特率计算公式CAN波特率 = PCLK / [(BRP) * (1 + Tseg1 + Tseg2)]

其中:

  • BRP(BTR[5:0]):波特率预分频值,范围为1-64。
  • Tseg1(BTR[10:8]):时间段1,包含传播时间段和相位缓冲段1。
  • Tseg2(BTR[13:11]):相位缓冲段2。
  • SJW(BTR[15:14]):同步跳转宽度,用于时钟同步微调。

一个配置1Mbps波特率的实例(假设PCLK=12MHz)

  1. 计算所需的总时间份额(Tq)数量:Tq总数 = PCLK / (波特率 * BRP)。先假设BRP=1,则Tq总数 = 12,000,000 / (1,000,000 * 1) = 12
  2. 分配Tseg1和Tseg2。遵循规则:Tseg1 >= Tseg2Tseg2 >= SJW。通常采样点建议在75%-80%处。我们设Tseg1=8,Tseg2=3,则总和为12。采样点位于(1+Tseg1)/ (1+Tseg1+Tseg2) = 9/13 ≈ 69%,对于1Mbps勉强可用,但更优的是Tseg1=7, Tseg2=4(采样点72%)。
  3. 设置SJW,通常取Tseg2和4中的较小值,这里设为1。
  4. 组合成BTR值:SJW=01b,Tseg2=011b,Tseg1=111b,BRP=000001b。同时,BTR[7](SAM)位通常置1,表示对总线采样3次以抗干扰。因此,最终BTR = 0x001C 0001
// 配置CAN1为1Mbps, PCLK=12MHz, 采样点约72% void CAN1_InitBitTiming(void) { volatile unsigned int *can1mod = (unsigned int *)0xE0044000; volatile unsigned int *can1btr = (unsigned int *)0xE0044014; // 进入复位模式以配置BTR *can1mod |= (1 << 0); // 设置RM=1 // 配置BTR寄存器 // BRP = 1 (0+1) -> 分频系数为1 // Tseg1 = 7 (编程值6) -> 实际时间段为 7+1=8个Tq // Tseg2 = 4 (编程值3) -> 实际时间段为 4+1=5个Tq? 注意:数据手册中Tseg2的编程值是实际值-1。 // 更正:根据SJA1000惯例,Tseg1编程值 = 实际Tseg1 - 1, Tseg2编程值 = 实际Tseg2 - 1。 // 设实际 Tseg1=8, Tseg2=3。则编程值 Tseg1=7, Tseg2=2。 // 总和Tq = 1(固定) + 8 + 3 = 12。 // 波特率 = 12MHz / (1 * 12) = 1Mbps。 // SJW = 1 (编程值0) // SAM=1 (三次采样) unsigned int btr_value = 0; btr_value |= (0 << 14); // SJW = 0 (实际跳转宽度=1) btr_value |= (2 << 11); // Tseg2 = 2 (实际段长=3) btr_value |= (7 << 8); // Tseg1 = 7 (实际段长=8) btr_value |= (1 << 7); // SAM = 1 btr_value |= (0 << 0); // BRP = 0 (实际分频=1) *can1btr = btr_value; // 退出复位模式,进入工作模式 *can1mod &= ~(1 << 0); // 清除RM=0 }

重要提示:位时序配置是CAN稳定通信的关键。不同厂商的CAN控制器对Tseg1、Tseg2的定义可能略有差异。上述计算基于常见的SJA1000/PeliCAN模式。最可靠的方法是参考NXP官方提供的配置工具或示例代码进行计算。在复杂电磁环境中,可能需要调整采样点(后移)或增加SJW来增强同步能力。

3.2.2 命令寄存器(CMR)与状态寄存器(SR/GSR)

这两个寄存器用于控制发送、接收和获取控制器状态。

命令寄存器(CMR, 写仅)

  • TR (Bit 0):发送请求。将配置好的发送缓冲区数据提交到发送队列。
  • RRB (Bit 2):释放接收缓冲区。读取完一帧报文后,必须写此位为1,以释放缓冲区,准备接收下一帧。这是最常用的命令之一
  • CDO (Bit 3):清除数据溢出标志。
  • SRR (Bit 4):自接收请求。用于自测试模式,控制器自己发送的报文也能被自己接收(如果ID匹配滤波器)。

状态寄存器(SR, 0xE004401C)与全局状态寄存器(GSR, 0xE0044008)

  • SR[0] - RBS:接收缓冲区状态。为1表示有新的报文已接收并可用。
  • SR[2] - TBS:发送缓冲区状态。为1表示至少有一个发送缓冲区空闲,可以写入新的发送帧。
  • SR[3] - TCS:发送完成状态。为1表示所有挂起的发送请求均已完成。
  • GSR[24:31] / GSR[16:23]:分别包含发送错误计数器(TEC)和接收错误计数器(REC)。监控这两个值有助于诊断总线故障。当TEC超过255,控制器会进入“Bus-Off”状态(GSR[7] BS=1),需要软件干预(进入复位模式再退出)才能恢复。

3.3 全局验收滤波器(AF)配置实战

这是LPC21xx/22xx CAN模块的精华所在,也是配置难点。它本质上是一个可编程的规则表,存储在片内RAM中(地址0xE0038000 - 0xE00387FF)。

3.3.1 滤波器工作模式

验收滤波器支持四种工作模式,由验收滤波器模式寄存器(AFMR, 0xE003C000)控制:

  • Bypass模式:关闭滤波,所有接收到的报文都存入缓冲区。用于调试。
  • 操作模式:正常过滤模式,使用用户定义的过滤表。
  • FulCAN模式:一种特殊模式,可以为特定的标准帧ID(最多2个)提供类似“邮箱”的自动接收功能,报文直接存入特定位置,无需CPU频繁干预释放缓冲区。注意:/01版本器件对此模式有优化,中断行为不同。
3.3.2 过滤表结构

过滤表由一系列“条目”组成,每个条目可以是:

  • 标准帧单个ID:匹配一个特定的11位ID。
  • 标准帧组ID:匹配一个ID范围(通过掩码实现)。
  • 扩展帧单个ID:匹配一个特定的29位ID。
  • 扩展帧组ID:匹配一个29位ID范围。

CPU需要按照特定格式,将这些条目编写到验收滤波器RAM中。RAM的布局由四个“起始地址寄存器”定义:

  • SFF_sa:标准帧单个ID表起始地址。
  • SFF_GRP_sa:标准帧组ID表起始地址。
  • EFF_sa:扩展帧单个ID表起始地址。
  • EFF_GRP_sa:扩展帧组ID表起始地址。
  • ENDofTable:标识整个过滤表的结束。

配置一个简单的标准帧过滤表示例(只接收ID为0x123和0x456的报文)

#define AF_RAM_BASE 0xE0038000 #define AFMR (*(volatile unsigned int *)0xE003C000) #define SFF_sa (*(volatile unsigned int *)0xE003C004) #define ENDofTable (*(volatile unsigned int *)0xE003C014) void AF_ConfigSimple(void) { volatile unsigned short *af_ram = (unsigned short *)AF_RAM_BASE; // 1. 设置AFMR为复位/配置模式 AFMR = 0x01; // 设置AccBP=1, 暂时旁路滤波器 // 2. 配置标准帧单个ID表 SFF_sa = 0; // 表从RAM开始处存放 // 条目格式:16位。高11位为ID, bit11为0, bit12为0, bit13为0, bit14为0, bit15为1(表示这是单个ID条目) // ID 0x123 af_ram[0] = (0x123 << 5) | (1 << 15); // ID左移5位到高11位, 设置bit15=1 // ID 0x456 af_ram[1] = (0x456 << 5) | (1 << 15); // 3. 配置标准帧组ID表起始地址(紧接着单个ID表) // 我们不需要组ID,所以将其起始地址设置为下一个未使用的位置,并置空。 unsigned short *sff_grp_start = &af_ram[2]; // 实际上,我们需要设置SFF_GRP_sa寄存器,但这里为了简单,假设我们不使用组过滤。 // 更规范的做法是计算地址偏移。 // 假设我们只使用单个ID表,共2个条目。 unsigned int table_end_offset = 2; // 两个16位条目 // 4. 设置结束表寄存器 ENDofTable = table_end_offset; // 指示表结束的位置(以16位字为单位) // 5. 使能滤波器,进入操作模式 AFMR = 0x00; // 清除AccBP, 使能滤波器 }

这个例子仅配置了标准帧单个ID接收。实际应用可能复杂得多,需要混合使用多种条目类型。关键点:每个条目的格式必须严格按照数据手册定义来组织,偏移地址的计算要精确。

3.3.3 FullCAN模式应用

FullCAN模式对于需要高速、确定性地处理某些固定ID报文的场景非常有用(如汽车中的某些控制指令)。在该模式下,为指定的标准帧ID分配了专用的存储位置(FCANIC0FCANIC1寄存器),新报文会自动覆盖旧报文,并产生中断。这省去了软件“释放接收缓冲区”的操作,减少了延迟。

配置FullCAN模式需要:

  1. 在验收滤波器RAM中设置FullCAN条目(格式不同,包含ID和控制器选择位)。
  2. 使能AFMR寄存器中的FullCAN位。
  3. 使能FCANIE寄存器中对应位的全局中断。
  4. 在CAN控制器的IER寄存器中使能“FullCAN中断”。

4. CAN控制器应用实战与问题排查

理解了寄存器之后,我们来看一个完整的CAN发送接收流程,并总结常见问题。

4.1 发送一帧标准数据帧流程

  1. 检查发送缓冲区状态:读取CANSR寄存器,确认TBS位为1(缓冲区空闲)。
  2. 填写发送缓冲区:向CANTFI1CANTID1CANTDA1CANTDB1(以缓冲区1为例)写入帧信息和数据。
    • CANTFI1:设置数据长度码(DLC, bit[3:0]), 远程帧请求位(RTR)等。
    • CANTID1:写入11位或29位标识符。
    • CANTDA1/CANTDB1:写入最多8字节数据。
  3. 启动发送:向CANCMR寄存器写入命令,将TR位置1。如果需要选择特定发送缓冲区,同时设置STB1/STB2/STB3位。
  4. 等待发送完成:轮询CANSR寄存器的TCS位,或使用发送中断。

4.2 接收一帧报文流程(中断方式)

  1. 配置验收滤波器:如前所述,设置好需要接收的ID范围。
  2. 使能中断
    • 在CAN控制器的CANIER寄存器中,使能“接收中断”(通常对应某一位)。
    • 在ARM的向量中断控制器(VIC)中,使能CAN控制器的中断通道。
  3. 编写中断服务程序(ISR)
    • 读取CANICR寄存器,确定中断源(接收完成、发送完成、错误等)。
    • 如果是接收中断,则从CANRFSCANRIDCANRDACANRDB寄存器中读取报文内容。
    • 关键步骤:读取完毕后,必须向CANCMR寄存器的RRB位写1,以释放接收缓冲区。
    • 清除中断标志(在CANICR中相应位写1清除)。

4.3 典型问题排查速查表

问题现象可能原因排查步骤
无法通信,无波形1. 控制器未退出复位模式(MOD.RM=1)。
2. 波特率配置错误(BTR)。
3. 物理层故障(终端电阻、线缆)。
4. 引脚配置错误(未设置为CAN功能)。
1. 检查MOD寄存器RM位是否为0。
2. 用示波器测量CAN_TX引脚,看是否有输出。确认BTR计算值与实际PCLK匹配。
3. 检查总线是否有120Ω终端电阻,测量CAN_H和CAN_L之间的差分电压。
4. 检查PINSELx寄存器,将对应引脚设置为CAN功能。
能发送,不能接收1. 验收滤波器配置错误或未使能。
2. 接收中断未使能。
3. 接收缓冲区未释放(RRB命令未执行)。
1. 将AFMR设置为旁路模式(AccBP=1),看是否能收到所有报文。若能,则问题在滤波表。
2. 检查CANIER和VIC的中断使能位。
3. 在接收ISR中,确认执行了RRB命令。检查RBS状态位。
通信不稳定,错误帧多1. 位时序配置不佳(采样点不合理)。
2. 总线波特率不匹配。
3. 电磁干扰(EMI)。
4. 节点同步问题。
1. 调整BTR中的Tseg1、Tseg2和SJW,尝试将采样点后移(如到80%)。
2. 确保网络所有节点的波特率设置绝对一致。
3. 检查布线,避免与强干扰源平行走线,使用屏蔽双绞线。
4. 监控GSR中的TEC和REC计数器,看是否持续增长。
特定ID报文收不到1. 验收滤波表条目格式错误。
2. ID或掩码计算错误。
3. FullCAN模式与其他模式冲突。
1. 使用旁路模式验证报文是否在总线上。
2. 仔细核对滤波RAM中写入的数据,特别是ID的位对齐和条目类型位。
3. 检查AFMR模式,确认FullCAN模式是否被意外启用。
进入Bus-Off状态1. 总线持续错误(如短路、开路)。
2. 硬件故障。
1. 检查GSR的BS位。进入Bus-Off后,必须先将MOD.RM置1,再清0,才能让控制器重新参与总线通信。
2. 监控TEC,如果增长过快,检查硬件连接和共模电压。

最后一点经验:在调试CAN时,一个好的CAN总线分析仪(如PCAN-USB, ZLG CANalyst-II)是必不可少的。它能让你直观地看到总线上的原始报文、错误帧和负载率,是定位硬件问题还是软件问题的利器。不要只依赖芯片的寄存器状态进行猜测。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询