1. 项目概述与核心价值
在嵌入式开发领域,尤其是基于经典8051架构的单片机项目中,定时器/计数器(Timer/Counter)的地位堪比心脏之于人体。它不仅是系统精准计时的基石,更是实现PWM波形生成、输入事件捕获、实时调度乃至串口通信等复杂功能的核心引擎。对于许多从标准8051入门,进而接触到增强型51内核(如NXP的P89V52X2)的开发者而言,Timer 2往往是一个既熟悉又陌生的存在。熟悉,是因为它继承了8051定时器的基本思想;陌生,则在于其功能远比基础的Timer 0/1强大和复杂,尤其是其与UART(通用异步收发器)的深度耦合,常常成为项目调试中的“拦路虎”。
我接触过不少项目,开发者能熟练使用Timer 0做延时,用Timer 1产生波特率,但一旦涉及到需要高精度时间戳记录、可编程时钟输出或者为串口提供独立且灵活的收发时钟时,就不得不直面Timer 2。数据手册上那些密密麻麻的寄存器位和模式切换表,初看确实令人头疼。本文将以NXP P89V52X2单片机中的Timer 2为蓝本,结合我多年的调试经验,为你彻底拆解它的四种工作模式——捕获、自动重载(含双向计数)、可编程时钟输出以及波特率发生器,并深入剖析其如何作为UART的“心脏”,精准驱动串口通信。我的目标不仅是让你看懂手册,更是让你能避开我踩过的坑,在项目中自信、高效地驾驭这个强大的外设。
2. Timer 2的架构与核心寄存器解析
在深入四种模式之前,我们必须先理解Timer 2的“控制中心”——那几个关键的寄存器。如果把Timer 2比作一台多功能机床,那么这些寄存器就是它的控制面板和状态显示屏。
2.1 核心控制寄存器:T2CON
T2CON(地址C8H)是Timer 2的总指挥,其每一位都直接决定了定时器的工作状态。它是一个可位寻址的寄存器,这意味着我们可以像操作布尔变量一样单独设置或清除其中的某一位,非常方便。
表:T2CON寄存器位功能详解
| 位 | 符号 | 名称与功能 |
|---|---|---|
| 7 | TF2 | Timer 2溢出标志。当Timer 2计数溢出时由硬件置1。关键点:在波特率发生器模式或时钟输出模式下,此位不会被置1。必须由软件清零。 |
| 6 | EXF2 | Timer 2外部标志。在捕获、自动重载或波特率发生器模式下,当EXEN2=1且外部引脚T2EX出现下降沿时,此位被置1。它和TF2共享同一个中断向量,在中断服务程序中需要查询是哪个标志触发了中断。 |
| 5 | RCLK | 接收时钟选择。这是Timer 2与UART联动的关键位之一。置1:UART在模式1和3下,接收波特率时钟源采用Timer 2的溢出脉冲。清零:采用Timer 1的溢出作为接收时钟。 |
| 4 | TCLK | 发送时钟选择。与RCLK对应,控制UART的发送波特率时钟源。置1用Timer 2,清零用Timer 1。 |
| 3 | EXEN2 | Timer 2外部使能。此位为1时,如果Timer 2未用于串口时钟(即RCLK和TCLK不同时为1),则T2EX引脚上的下降沿可以触发捕获或重载事件。 |
| 2 | TR2 | Timer 2运行控制。相当于定时器的“启动/停止”按钮。写1启动定时器,写0停止。 |
| 1 | C/T2 | 定时器或计数器选择。0:定时器模式,对内部时钟(fosc/6)进行计数。1:计数器模式,对T2引脚(P1.0)的外部下降沿进行计数,最高频率为fosc/12。 |
| 0 | CP/RL2 | 捕获/重载选择。此位是模式切换的枢纽之一。1:选择捕获模式(需EXEN2=1且T2EX有下降沿)。0:选择自动重载模式。特别注意:当RCLK=1或TCLK=1(即用作波特率发生器)时,此位被忽略,定时器强制工作在自动重载模式。 |
实操心得:在配置Timer 2时,我习惯先规划好最终用途(比如是做输入捕获还是波特率生成),然后从下往上配置
T2CON。先确定C/T2选择时钟源,再根据是否需要外部触发设置EXEN2,接着设置CP/RL2选择基本模式,最后才设置TR2启动。RCLK和TCLK通常放在最后配置,因为它们会覆盖其他模式设置。务必记住,TF2和EXF2不会自动清零,必须在中断服务程序(ISR)中手动清除,否则会连续进入中断。
2.2 模式控制寄存器:T2MOD
T2MOD(地址C9H)相对简单,主要扩展了Timer 2的计数方向控制和时钟输出功能。它不可位寻址,只能进行字节操作。
表:T2MOD寄存器位功能详解
| 位 | 符号 | 名称与功能 |
|---|---|---|
| 7:2 | - | 保留位。用户程序应将其写0。 |
| 1 | T2OE | Timer 2输出使能。仅在可编程时钟输出模式下使用。置1时,允许将50%占空比的时钟信号从T2引脚(P1.0)输出。 |
| 0 | DCEN | 向下计数使能。此位开启了Timer 2的双向计数能力。0:定时器仅向上计数(默认)。1:允许Timer 2根据T2EX引脚的电平进行向上或向下计数。 |
注意事项:
T2MOD的复位值不是0,而是XX00 0000B(X表示不确定)。因此,在程序初始化时,绝不能假设它的高6位是0。安全的做法是,在修改T2OE或DCEN位时,先读取整个寄存器,使用“与”或“或”操作只修改目标位,或者直接写入一个确定的值(如0x00或0x02),以避免意外改变保留位的状态,导致不可预测的行为。
2.3 数据与重载/捕获寄存器
这是Timer 2的“工作内存”:
TH2,TL2:16位计数寄存器。Timer 2的核心,其值随着时钟脉冲递增或递减。RCAP2H,RCAP2L:16位重载/捕获寄存器。这是一个多功能寄存器:- 在自动重载模式和波特率发生器模式下,它存放着重载值。当
TH2/TL2溢出(或T2EX触发)时,RCAP2的值会被自动装入TH2/TL2。 - 在捕获模式下,当
T2EX引脚出现有效下降沿时,TH2/TL2的当前值会被“捕获”(复制)到RCAP2中,从而记录下事件发生的时刻。
- 在自动重载模式和波特率发生器模式下,它存放着重载值。当
理解这些寄存器是灵活运用Timer 2四种模式的基础。接下来,我们将逐一深入每种模式的工作机制和配置要点。
3. 模式一:捕获模式——精准的事件时间戳
捕获模式的核心功能是“抓拍”。当某个外部事件(通常对应T2EX引脚上的下降沿)发生时,它能瞬间将Timer 2当前计数值TH2/TL2冻结并保存到RCAP2H/RCAP2L寄存器中。这就像高速相机在精彩瞬间按下快门,记录下精确的时间点。
3.1 工作原理与配置流程
捕获模式的逻辑框图清晰地展示了其数据流:内部或外部时钟驱动着TH2/TL2不断累加。当EXEN2位被使能(设为1)时,T2EX引脚上的下降沿检测电路被激活。一旦检测到下降沿,硬件会立即执行两个动作:1) 将TH2/TL2的当前值复制到RCAP2H/RCAP2L;2) 将外部标志EXF2置位。
配置捕获模式的关键步骤如下:
- 确定时钟源:设置
C/T2位。C/T2=0,使用内部时钟(fosc/6);C/T2=1,使用T2引脚的外部事件计数。 - 选择模式:设置
CP/RL2 = 1,明确选择捕获功能。 - 使能外部触发:设置
EXEN2 = 1,允许T2EX引脚的下降沿触发捕获。 - 确保串口时钟未占用:检查并确保
RCLK和TCLK均为0。如果它们任何一个为1,Timer 2会被强制用于波特率生成,CP/RL2位失效。 - 启动定时器:最后,设置
TR2 = 1,Timer 2开始计数。
此时,Timer 2会自由运行。溢出时,TF2置位;T2EX有下降沿时,发生捕获且EXF2置位。两者都可以独立申请中断(如果中断已开启)。
3.2 应用场景与代码示例
捕获模式的典型应用是测量脉冲宽度或频率。例如,测量一个正脉冲的宽度:
- 将脉冲信号接入
T2EX引脚。 - 配置Timer 2为捕获模式,内部时钟源,预分频根据脉冲预期宽度设置。
- 在脉冲的上升沿(可通过外部中断0/1或查询其他IO口获得)到来时,软件记录下此时
TH2/TL2的值(或清零定时器重新开始)。 - 在脉冲的下降沿,捕获事件自动发生,
RCAP2中记录的就是下降沿的时刻。 - 两次时间的差值,乘以计时器的时钟周期,就是脉冲宽度。
下面是一个简化的初始化代码片段(以C语言为例,假设使用SDCC或Keil编译器):
void Timer2_CaptureMode_Init(void) { // 1. 停止Timer 2 T2CON &= 0xFD; // 清除TR2 (bit2),也可直接赋值 T2CON = 0x00; // 2. 设置模式:捕获模式,内部定时,使能T2EX触发 // CP/RL2=1, EXEN2=1, C/T2=0 (定时), TR2暂为0 T2CON = 0x09; // 二进制 0000 1001,即 CP/RL2=1, EXEN2=1 // 3. 清除T2MOD中可能影响的方向控制位,确保向上计数 T2MOD = 0x00; // DCEN=0, T2OE=0 // 4. 初始化计数器和捕获寄存器(可选,通常从0开始) TH2 = 0x00; TL2 = 0x00; RCAP2H = 0x00; // 捕获寄存器初始值无关紧要,会被覆盖 RCAP2L = 0x00; // 5. 清除中断标志(安全做法) T2CON &= 0x2F; // 清除TF2 (bit7)和EXF2 (bit6),即与上 0010 1111 // 6. 开启Timer 2中断(如果需要) IE |= 0x20; // 设置ET2=1,允许Timer 2中断 // EA = 1; // 如果需要,开启总中断 // 7. 启动Timer 2 T2CON |= 0x04; // 设置TR2=1 }对应的中断服务程序需要判断中断源:
void Timer2_ISR(void) interrupt 5 { // Timer 2中断向量号为5 if (T2CON & 0x80) { // 检查TF2 // 处理溢出中断 // ... 用户代码,例如扩展计时范围 T2CON &= 0x7F; // 必须软件清除TF2 } if (T2CON & 0x40) { // 检查EXF2 // 捕获事件发生! unsigned int capture_value = (RCAP2H << 8) | RCAP2L; // 使用capture_value进行脉冲宽度计算等操作 // ... T2CON &= 0xBF; // 必须软件清除EXF2 } }避坑指南:这里有一个手册中明确指出的重要隐患:捕获寄存器
RCAP2没有写保护。当中断事件发生后,如果程序没有及时读取RCAP2的值,而T2EX引脚上又来了一个新的下降沿,那么TH2/TL2的当前值(可能是新的计数值)会再次捕获并覆盖RCAP2中的旧值,导致之前的数据丢失。因此,在捕获模式的中断服务程序中,第一要务就是读取RCAP2的值并保存到安全变量中,然后再进行其他处理或清除标志位。否则,你可能会得到混乱的时间戳数据。
4. 模式二:自动重载模式——灵活的周期定时与双向计数
自动重载模式是定时器最常用的功能之一,它让定时器在达到设定值后自动复位,周而复始地产生精确的时间间隔。Timer 2的自动重载模式更加强大,它支持双向计数,这是Timer 0/1所不具备的。
4.1 向上计数与基本重载
当DCEN=0(默认)时,Timer 2工作在简单的向上自动重载模式。其工作流程如下:
TH2/TL2从初始值开始,每个时钟周期加1。- 当计数从
0xFFFF溢出到0x0000时,溢出标志TF2被置1。 - 同时,硬件自动将
RCAP2H/RCAP2L中预先设置好的重载值装入TH2/TL2,定时器从这个新值开始下一轮计数。 - 如果
EXEN2=1,那么T2EX引脚的下降沿也可以触发一次重载(同时置位EXF2),这为实现外部同步或额外触发提供了可能。
重载频率(即溢出频率)的计算公式为:重载频率 = 时钟源频率 / (65536 - (RCAP2H, RCAP2L))其中,(RCAP2H, RCAP2L)表示将两个8位寄存器看作一个16位无符号整数。例如,时钟源为12MHz(fosc),C/T2=0,则定时器时钟为12MHz / 6 = 2MHz。若想产生100Hz的溢出中断,则重载值计算如下:
所需计数值 = 时钟频率 / 目标频率 = 2,000,000 Hz / 100 Hz = 20,000 重载值 = 65536 - 20000 = 45536 = 0xB1E0 因此,RCAP2H = 0xB1, RCAP2L = 0xE0。配置代码示例如下:
void Timer2_AutoReload_Up_Init(unsigned int reload_val) { T2CON = 0x00; // 确保停止,模式为自动重载(CP/RL2=0),内部定时,禁止T2EX触发 T2MOD = 0x00; // 确保DCEN=0,向上计数 // 设置重载值 RCAP2H = (unsigned char)(reload_val >> 8); RCAP2L = (unsigned char)(reload_val & 0xFF); // 初始化计数器,可以从0开始,也可以从重载值开始 TH2 = RCAP2H; TL2 = RCAP2L; // 清除标志位 T2CON &= 0x2F; // 清除TF2和EXF2 // 开启中断(可选) IE |= 0x20; // ET2=1 // 启动定时器 T2CON |= 0x04; // TR2=1 }4.2 双向计数(上下计数)模式
这是Timer 2的一大特色。当DCEN=1时,Timer 2的计数方向由T2EX引脚的电平控制,实现了真正的可逆计数器。
T2EX = 1:Timer 2向上计数。计数到0xFFFF后溢出,置位TF2,并将RCAP2中的值重载到TH2/TL2。T2EX = 0:Timer 2向下计数。当TH2/TL2的值减少到等于RCAP2中的值时,发生“下溢”,同样置位TF2,但此时重载的值是0xFFFF。
一个精妙的设计:在此模式下,外部标志EXF2的行为发生了变化。它不再由T2EX的下降沿触发,而是会在每次溢出(上溢或下溢)时翻转。这意味着EXF2可以作为一个第17位分辨率的扩展!结合TF2(溢出标志)和EXF2(方向指示/扩展位),你实际上拥有了一个17位的计数器,极大地扩展了计数范围或精度。
应用场景:双向计数模式非常适合用于正交编码器解码。编码器的A、B相信号可以分别接到T2(计数时钟)和T2EX(方向控制)引脚。当电机正转时,T2EX为高,计数器递增;反转时,T2EX为低,计数器递减。计数器最终的值就代表了编码器的累计位置。这种方式比用软件在外部中断中判断方向并加减计数要高效、准确得多。
实操心得:在双向计数模式下,中断服务程序需要同时检查
TF2和EXF2。TF2告诉你发生了溢出事件,EXF2的当前状态则指示了溢出发生时的计数方向(EXF2在每次溢出时翻转,所以其电平代表了上一次溢出的方向?这里需要仔细推敲:实际上,EXF2是溢出时翻转,而非保持方向。要获取实时方向,应直接读取T2EX引脚电平或根据TH2/TL2的变化趋势判断)。另外,初始重载值RCAP2的设置需要仔细考虑,它决定了向下计数时的“靶心”。如果你希望计数器在一个中心值附近对称地上下计数,可以将RCAP2设置为这个中心值,并从0开始计数。
5. 模式三:可编程时钟输出——将单片机变成信号发生器
这个模式非常实用,它允许你从单片机的T2引脚(P1.0)输出一个占空比为50%的方波时钟信号,频率范围很宽(例如在16MHz晶振下,可从122Hz到8MHz)。这样,你就不需要额外的晶振或时钟芯片来为其他器件提供时钟了。
5.1 工作原理与配置
时钟输出模式可以看作是自动重载模式的一个特殊应用。其核心公式为:输出时钟频率 = 振荡器频率 / [2 * (65536 - (RCAP2H, RCAP2L))]注意,这里的时钟源是振荡器频率(fosc),而不是定时器模式下的fosc/6。Timer 2在该模式下以fosc/2的速度递增(见手册框图),因此能产生更高的输出频率。
配置步骤非常固定:
- 清除
C/T2:C/T2 = 0,使用内部时钟。 - 设置
T2OE:在T2MOD寄存器中,设置T2OE = 1,使能时钟输出功能。 - 设置
CP/RL2和RCLK/TCLK:CP/RL2应设为0(自动重载模式),且RCLK和TCLK必须都为0,否则Timer 2会被串口占用。 - 计算并设置重载值:根据所需输出频率和系统晶振,利用上述公式计算
RCAP2的值。 - 启动定时器:设置
TR2 = 1。
重要特性:在该模式下,Timer 2的溢出不会置位TF2,也不会产生中断。它就像一个专注的时钟发生器,默默地在后台工作。
5.2 计算示例与配置代码
假设系统晶振fosc = 11.0592MHz(这是一个非常经典的串口波特率适配晶振),我们想从P1.0引脚输出一个精确的115200Hz的时钟信号(常用于驱动另一片UART芯片)。
首先,根据公式计算重载值:
输出频率 Fout = 115200 Hz Fout = fosc / [2 * (65536 - RCAP2)] => 65536 - RCAP2 = fosc / (2 * Fout) = 11059200 / (2 * 115200) = 11059200 / 230400 = 48 => RCAP2 = 65536 - 48 = 65488 = 0xFFD0因此,RCAP2H = 0xFF,RCAP2L = 0xD0。
配置代码如下:
void Timer2_ClockOut_Init(unsigned int reload_val) { // 1. 停止Timer 2 T2CON = 0x00; // 确保TR2=0, C/T2=0, CP/RL2=0 // 2. 配置T2MOD,使能时钟输出,禁止双向计数 T2MOD = 0x02; // 二进制 0000 0010, 即T2OE=1, DCEN=0 // 3. 设置重载值 RCAP2H = (unsigned char)(reload_val >> 8); RCAP2L = (unsigned char)(reload_val & 0xFF); // 初始化计数器,通常从0或重载值开始均可 TH2 = 0; // 从0开始计数 TL2 = 0; // 4. 启动Timer 2 T2CON |= 0x04; // TR2=1 // 此时,P1.0 (T2) 引脚就会输出指定频率的方波 }注意事项:使能时钟输出后,P1.0引脚的第二功能(
T2)被激活,它将不再是普通的I/O口。在初始化其他外设或进行IO操作时,要避免与这个引脚冲突。另外,输出的时钟频率受限于fosc和重载值的精度,并非所有频率都能精确产生。如果需要非常精确的频率,可能需要调整晶振或结合PLL等其他手段。
6. 模式四:波特率发生器模式——UART通信的精准心脏
这是Timer 2与UART协同工作的核心模式,也是项目中最常使用的功能之一。它能为串口通信提供高度独立且灵活的波特率时钟,特别是可以实现收发波特率独立设置,这在某些特殊通信协议中非常有用。
6.1 工作原理与独特之处
波特率发生器模式在行为上类似于自动重载模式,但有以下几个关键区别:
- 时钟源不同:作为波特率发生器时,Timer 2的计数时钟是振荡器频率(
fosc),而不是fosc/6。这使得在相同系统时钟下,它能产生比定时器模式更高的溢出率,从而获得更宽的波特率选择范围。 - 溢出不中断:在此模式下,Timer 2的溢出不会置位
TF2,也不会产生中断。它完全服务于UART,像一个无声的守护者。 - 外部触发行为变化:如果
EXEN2=1,T2EX引脚的下降沿仍然会置位EXF2,但不会触发重载。这意味着,你可以把T2EX引脚当作一个额外的外部中断输入来使用,而不会干扰波特率的生成。 - 寄存器访问限制:当Timer 2正在运行(
TR2=1)并作为波特率发生器时,不要去读取或写入TH2和TL2。因为此时它们的值正在被硬件高速更新,读出的值可能不准确,写入则可能破坏当前的计时。可以安全地读取RCAP2寄存器,但写入操作也可能与自动重载过程冲突。安全的做法是,在修改这些寄存器前,先停止Timer 2(TR2=0)。
波特率计算公式为:波特率 = fosc / [16 * (65536 - (RCAP2H, RCAP2L))]对于常用的fosc = 11.0592MHz,这个公式能产生非常整齐的标准波特率值。
6.2 配置流程与经典波特率计算
配置Timer 2为波特率发生器的核心是设置T2CON寄存器中的RCLK和/或TCLK位。
TCLK=1:UART的发送波特率时钟由Timer 2提供。RCLK=1:UART的接收波特率时钟由Timer 2提供。- 两者可以独立设置。例如,
TCLK=1, RCLK=0表示发送用Timer 2,接收用Timer 1,这在需要不对称波特率的场合非常有用。
配置步骤:
- 根据所需波特率,计算
RCAP2重载值。 - 停止Timer 2(
TR2=0)。 - 配置
T2CON:设置RCLK和/或TCLK为1,C/T2=0(通常使用内部时钟),CP/RL2位被忽略,TR2暂为0。 - 将计算好的重载值写入
RCAP2H和RCAP2L。 - (可选)如果需要使用
T2EX作为额外中断,设置EXEN2=1。 - 启动Timer 2(
TR2=1)。
计算示例:系统晶振fosc=11.0592MHz,需要产生9600波特率。
波特率 = 11059200 / [16 * (65536 - RCAP2)] => 65536 - RCAP2 = 11059200 / (16 * 9600) = 11059200 / 153600 = 72 => RCAP2 = 65536 - 72 = 65464 = 0xFFD8因此,RCAP2H = 0xFF,RCAP2L = 0xD8。这与手册中提供的常用波特率表(Table 20)完全吻合。
配置代码:
void UART_Init_Timer2_BRG(unsigned long baud_rate) { unsigned int reload; // 1. 计算重载值 (假设fosc=11059200L) reload = 65536UL - (11059200UL / (16UL * baud_rate)); // 2. 停止Timer 2 T2CON &= 0xFB; // TR2 = 0 // 3. 配置Timer 2为波特率发生器模式,同时用于发送和接收 // RCLK=1, TCLK=1, 内部定时(C/T2=0) T2CON = 0x34; // 0011 0100,即 RCLK=1, TCLK=1, TR2=0, C/T2=0 // 4. 设置重载值 RCAP2H = (unsigned char)(reload >> 8); RCAP2L = (unsigned char)(reload & 0xFF); // 5. 启动Timer 2 T2CON |= 0x04; // TR2=1 // 6. 配置UART为模式1或3(8位/9位可变波特率) SCON = 0x50; // 模式1,8位UART,允许接收 // 如果使用模式3,则 SCON = 0xD0; }6.3 与UART模式的协同工作
UART有4种工作模式(0, 1, 2, 3),其中模式1和3的波特率是可变的,且由定时器溢出率决定。
- 模式1:10位帧(1起始位 + 8数据位 + 1停止位),波特率可变。
- 模式3:11位帧(1起始位 + 8数据位 + 1可编程第9位 + 1停止位),波特率可变。
当RCLK或TCLK设为1,Timer 2就接管了UART在模式1或3下的时钟生成。此时,Timer 1可以被解放出来用于其他定时任务,提高了系统资源利用率。特别需要注意的是,由于Timer 2的时钟源是fosc,而Timer 1的时钟源是fosc/12(标准8051)或fosc/6(增强型如P89V52),因此即使重载值相同,两者产生的波特率也是不同的。在移植代码或混合使用Timer 1和Timer 2作为波特率源时,必须重新计算。
避坑指南:最大的坑莫过于在波特率发生器模式下误操作
TH2/TL2。我曾调试过一个通信不稳定的问题,最终发现是在串口发送过程中,某个后台任务不小心读取了TH2的值用于其他计算,导致波特率时钟出现微小扰动,累积成帧错误。黄金法则:一旦Timer 2作为波特率发生器启动,就把它当作一个黑盒,只通过RCAP2设置初始值,绝不在运行时读写TH2/TL2。如果需要改变波特率,正确的流程是:停止Timer 2 -> 修改RCAP2-> 可选地重置TH2/TL2-> 重新启动Timer 2。
7. UART与Timer 2的深度集成应用
理解了Timer 2作为波特率发生器的工作原理后,我们再来看看UART本身的一些高级特性,这些特性在与Timer 2配合时能发挥更大效用。
7.1 帧错误检测与自动地址识别
P89V52X2的UART在标准8051基础上进行了增强,两个非常实用的功能是帧错误(FE)检测和自动地址识别(AAR)。
帧错误检测:当PCON寄存器中的SMOD0位被置1时,SCON寄存器的最高位(SCON.7)的功能从SM0变为帧错误标志FE。当接收器检测到一个无效的停止位(例如期望为高电平的停止位却是低电平)时,FE会被硬件置1。这非常有助于在噪声较大的通信环境中诊断链路问题。注意:手册建议先配置好SM0和SM1(即UART模式),再设置SMOD0=1来启用FE功能。
自动地址识别:这是一种硬件级的从机地址过滤机制,特别适用于多机通信(一主多从)。它通过两个寄存器SADDR(从机地址)和SADEN(地址掩码)来实现。其工作原理是:
- 主机发送的地址帧,其第9位(TB8)必须为1,数据帧的第9位为0。
- 从机UART在模式2或3下,且
SM2=1时,只有收到第9位为1的帧(即地址帧)才会触发接收中断(RI)。 - 在中断中,从机将接收到的地址字节与
(SADDR & SADEN)进行比对。如果匹配,则该从机清零自己的SM2位,准备接收后续的数据帧。不匹配的从机则保持SM2=1,忽略后续数据帧。
这个功能极大地减轻了CPU的负担,避免了软件对每一个地址字节进行比对。Timer 2提供的稳定波特率,是这种硬件过滤机制可靠运行的基础。
7.2 实际项目中的配置策略与排错
在实际项目中,如何选择和配置Timer 2与UART?以下是我总结的一些策略:
资源分配策略:
- 如果系统只有一个UART,且波特率固定,优先使用Timer 1作波特率发生器,把功能更强大的Timer 2留给PWM、输入捕获等任务。
- 如果UART通信波特率很高,或者需要非常精确的波特率(特别是非标准值),使用Timer 2,因为它基于
fosc,分辨率更高。 - 如果系统有多个UART(通过软件模拟或硬件扩展),或者UART收发需要不同的波特率(如某些红外协议),那么必须使用Timer 2来提供其中一个时钟源,因为系统只有一个Timer 1。
初始化顺序很重要:
// 推荐的初始化顺序 void System_Init(void) { // 1. 首先停止所有可能冲突的定时器 T2CON = 0; // 2. 配置UART模式(但先不开启接收) SCON = 0x40; // 模式1,禁止接收 // 3. 计算并配置Timer 2重载值(停止状态下) // ... 计算reload_val RCAP2H = ...; RCAP2L = ...; // 4. 配置Timer 2控制寄存器(包括RCLK/TCLK) T2CON = 0x34; // 例如,Timer 2作波特率源 // 5. 最后才启动Timer 2和UART接收 T2CON |= 0x04; // TR2=1 SCON |= 0x10; // REN=1,允许接收 // 6. 开启中断(如果需要) IE = 0x90; // 开启串口中断, EA=1? EA = 1; }常见问题排查:
- 通信全双工乱码,但半双工(只发不收)正常:检查
RCLK和TCLK设置是否一致。如果不一致,收发时钟不同源,必然出错。 - 波特率偏差大:首先确认系统晶振频率是否准确。用示波器测量
T2引脚(如果配置为时钟输出)或UART的TX引脚,计算实际波特率。检查C/T2位是否设置正确(波特率发生器应为0)。核对重载值计算公式,注意是16位减法。 - 能发送不能接收,或反之:检查
SCON寄存器中的REN位(接收使能)是否已置1。检查中断是否开启(ES位和EA位)。在自动地址识别模式下,检查从机的SM2位和地址掩码设置是否正确。 - 帧错误(FE)频繁:除了检查线路噪声和电平匹配,重点检查双方波特率是否真正一致。如果使用Timer 2,确认在通信过程中没有其他代码误修改了
RCAP2、TH2或TL2。
- 通信全双工乱码,但半双工(只发不收)正常:检查
通过将Timer 2的精准定时能力与UART的灵活帧格式、高级功能相结合,可以构建出极其稳定可靠的串行通信系统。理解每一处细节,严格遵循配置和访问规范,是避免那些难以调试的通信问题的关键。希望这篇结合了手册原理与实战经验的解析,能成为你项目中的得力助手。