P87LPC764单片机UART串口与看门狗配置实战指南
1. 项目概述与核心价值
在嵌入式开发领域,尤其是面对那些资源受限、成本敏感的低功耗微控制器时,如何高效、可靠地实现设备间的数据通信和系统自恢复,是每个工程师绕不开的课题。今天,我们就来深入聊聊一款经典芯片——飞利浦(现恩智浦)的P87LPC764。这款20引脚、4KB OTP的80C51兼容单片机,以其“三低”(低功耗、低价格、低引脚数)特性,在早期的消费电子、工业控制和智能传感器中应用广泛。其内置的增强型UART(通用异步收发器)和独立的看门狗定时器,是保障系统通信稳定性和运行可靠性的两大基石。很多朋友在初次接触这类老芯片的数据手册时,可能会被里面大量的寄存器描述和时序图搞得头大,觉得配置起来很繁琐。但实际上,一旦你理解了其底层的工作机制和设计逻辑,就会发现它非常优雅和高效。这篇文章,我将结合自己多年在8051平台上的踩坑经验,为你彻底拆解P87LPC764的UART串口通信与看门狗定时器,不仅告诉你寄存器怎么配,更会解释“为什么要这么配”,并分享一些从实际项目中总结出来的配置技巧和避坑指南。无论你是正在维护一个老项目,还是想深入理解经典单片机外设的设计哲学,相信都能从中获得启发。
2. UART串口通信深度解析
2.1 UART基础与P87LPC764的增强特性
UART,即通用异步收发传输器,其核心思想非常简单:在没有时钟线的情况下,通信双方依靠预先约定好的速率(波特率)和格式,来解析一串由高低电平组成的比特流。一个标准的UART帧通常包含一个起始位(低电平)、5-9个数据位(通常8位)、可选的奇偶校验位以及1-2个停止位(高电平)。P87LPC764的UART完全兼容标准的80C51 UART,这意味着你之前为8051写的串口驱动,稍作调整就能移植过来。但它也有几个关键的增强点,这也是我们选择它而非更基础型号的原因。
首先,它支持帧错误(Framing Error)检测。在标准80C51中,如果接收方因为噪声或波特率不匹配而漏掉或误判了停止位,程序可能无从知晓,导致数据解析错误。P87LPC764通过SCON寄存器中的FE位(与SM0位复用)提供了硬件级的帧错误标志,一旦检测到无效的停止位,FE位会被置1,直到软件手动清除。这为通信可靠性增加了一道保险。
其次,它支持自动地址识别(Automatic Address Recognition)。这在多机通信(一主多从)场景下非常有用。传统方式需要每个从机在中断服务程序中软件比对地址,消耗CPU时间。P87LPC764通过硬件比较接收到的地址字节与预设的地址(SADDR)及地址掩码(SADEN),自动判断该数据帧是否是发给自己的,从而决定是否产生接收中断。这极大地减轻了CPU负担,特别适合在轮询或低功耗应用中快速过滤无关数据。
最后,它的全双工和接收缓冲特性与标准8051一致,意味着可以同时收发,并且能在读取上一个接收到的数据之前,开始接收下一个字节(但要注意,如果第二个字节接收完成时第一个还未读取,第一个字节会被覆盖)。
2.2 核心控制寄存器:SCON与PCON详解
配置UART,我们主要跟两个特殊功能寄存器(SFR)打交道:SCON(串口控制寄存器)和PCON(电源控制寄存器,其中某些位用于串口)。
SCON寄存器(地址98h,可位寻址)是整个UART的控制核心。我们逐位分析其功能与配置逻辑:
SM0/FE (SCON.7): 这是一个复用位,由PCON寄存器中的SMOD0位决定其功能。
- 当
SMOD0 = 0(复位默认),该位是SM0,与SM1共同决定UART的工作模式。 - 当
SMOD0 = 1,该位是FE(帧错误标志)。当检测到停止位为0(无效)时,由硬件置1,必须由软件清零。这个设计很巧妙,通过一个配置位,在有限的SFR资源里挤出了一个错误标志位。在实际应用中,如果你所处的电磁环境复杂或对通信可靠性要求高,建议在初始化时将SMOD0置1,启用FE检测功能,并在中断服务程序中检查此位。
- 当
SM1 (SCON.6): 与SM0共同设置工作模式。
SM0, SM1 模式选择:
SM0 SM1 模式 功能描述 波特率 0 0 0 同步移位寄存器模式 Fosc / 6 0 1 1 8位UART,波特率可变 由定时器1溢出率决定 1 0 2 9位UART,波特率固定 Fosc / 32 或 /16 (由SMOD1决定) 1 1 3 9位UART,波特率可变 由定时器1溢出率决定 实操心得:模式0很少用于常规串口通信,更多用于扩展I/O(如接74HC595/165移位寄存器)。最常用的是模式1(8位数据)和模式3(9位数据,用于多机通信)。模式2因为波特率固定,灵活性较差,使用场景较少。
SM2 (SCON.5): 多机通信使能位。
- 在模式2和3(9位数据)下:
SM2=1时,只有当接收到的第9位数据(RB8)为1(表示地址帧)时,才会置位RI产生中断;为0(数据帧)则不中断。SM2=0时,无论第9位是0是1,收到数据都会中断。这是实现自动地址识别和多机通信的关键。 - 在模式1下:
SM2=1时,只有接收到有效的停止位(即停止位为1),RI才会被置位。这可以用于简单的帧校验。 - 在模式0下:
SM2应设为0。
- 在模式2和3(9位数据)下:
REN (SCON.4): 串行接收使能位。
1:允许接收;0:禁止接收。这是一个很容易被忽略的坑:即使你配置好了所有参数,如果忘记将REN置1,接收功能是完全不工作的。TB8 (SCON.3): 在模式2和3中,这是要发送的第9位数据。你可以用它来传输奇偶校验位,或者在多机通信中,用
TB8=1表示发送的是地址帧,TB8=0表示数据帧。RB8 (SCON.2): 在模式2和3中,这是接收到的第9位数据。在模式1中,如果
SM2=0,则RB8存放的是接收到的停止位。在模式0中,RB8未使用。TI (SCON.1): 发送中断标志。当一帧数据发送完成时,由硬件置1。必须由软件清零。常见的编程错误是发送数据后不断查询TI,但忘记清零,导致程序判断发送一直“未完成”而卡死。
RI (SCON.0): 接收中断标志。当一帧数据接收完成(在模式0是第8位结束,其他模式是停止位中间)时,由硬件置1。同样必须由软件清零。
PCON寄存器中与UART相关的主要是:
- SMOD1 (PCON.7): 波特率加倍位。在模式1、2、3中,当
SMOD1=1时,波特率是SMOD1=0时的两倍。这为你微调波特率提供了更多选择,尤其是在使用非标准晶振时。 - SMOD0 (PCON.6): 如前所述,用于选择SCON.7是SM0还是FE功能。
2.3 四种工作模式的实战配置与差异
理解了寄存器,我们来看看四种模式的具体应用场景和配置要点。
模式0:同步移位寄存器模式这不是一个典型的异步串口模式。TxD引脚输出移位时钟,RxD引脚用于数据的输入/输出。每次传输8位数据,波特率固定为CPU时钟频率的1/6。这个模式主要用于扩展并行I/O口。例如,你可以用TxD时钟线驱动一串74HC595(输出扩展),同时用RxD数据线读取一串74HC165(输入扩展)。配置非常简单:SM0=0, SM1=0,然后使能REN即可开始接收,写SBUF即启动发送。
模式1:10位异步收发模式(最常用)这是最标准的8N1(8数据位,无校验,1停止位)格式。一帧包含1位起始位、8位数据位(LSB先发)、1位停止位。波特率由定时器1的溢出率决定,因此非常灵活。配置步骤如下:
- 确定波特率,根据公式计算定时器1的重装值(TH1)。
- 配置定时器1为模式2(8位自动重装),并关闭其中断(
ET1=0)。 - 设置
SM0=0, SM1=1。 - 根据需要设置
SMOD1位以选择是否波特率加倍。 - 置位
REN允许接收。 - 如果需要中断,则置位
ES(串口中断使能)和EA(总中断使能)。
模式2:11位异步收发模式(固定波特率)一帧包含1位起始位、8位数据位、1位可编程第9位、1位停止位。第9位可用于多机通信或奇偶校验。其波特率固定为Fosc / (32 * 2^(SMOD1))。当SMOD1=0,波特率为Fosc/64;SMOD1=1,波特率为Fosc/32。这个模式适合在CPU主频固定且对波特率精度要求不高,又需要第9位功能的场合。
模式3:11位异步收发模式(可变波特率)帧格式与模式2完全相同,区别在于波特率像模式1一样,由定时器1的溢出率决定。因此,模式3是功能最全的模式:既支持可变波特率(灵活),又支持第9位数据(可用于多机通信或校验)。在多机通信系统中,主机和从机通常都工作在模式3。
注意事项:在模式2和3中,发送的第9位数据来自TB8位。你需要在发送前,根据数据性质(地址/数据)或计算的奇偶校验位,手动设置TB8。例如,发送地址帧前
TB8 = 1;,发送数据帧前TB8 = 0;。
2.4 波特率计算:从公式到查表实践
波特率配置是串口应用的第一个门槛,配不准会导致乱码。P87LPC764的波特率生成逻辑与标准8051略有不同,主要因为其CPU时钟分频机制有差异。
核心公式(模式1和3,使用定时器1):波特率 = (2^SMOD1 / 32) * (Fosc / (12 * [256 - TH1]))这是标准8051的公式。但在P87LPC764中,定时器1的时钟源可以是Fosc/6或Fosc/12(由T1M位在CKCON寄存器中控制,默认为Fosc/12,与标准8051一致)。因此,更通用的公式是:波特率 = (2^SMOD1 / 32) * (Timer1_Clock / (256 - TH1))其中Timer1_Clock = Fosc / 12(默认)或Fosc / 6。
通常我们使用定时器1的模式2(8位自动重装),此时重装值TH1决定了溢出率。将公式变形,可以求解TH1:TH1 = 256 - (2^SMOD1 * Timer1_Clock) / (32 * 波特率)
举个例子:假设系统晶振Fosc = 11.0592MHz(这是一个非常经典的频率,因为它能产生非常精确的常用波特率),SMOD1=0,定时器1时钟为Fosc/12,目标波特率为9600。 计算过程:
Timer1_Clock = 11.0592MHz / 12 = 921.6 kHzTH1 = 256 - (1 * 921600) / (32 * 9600)TH1 = 256 - 921600 / 307200TH1 = 256 - 3 = 253 (0xFD)
查数据手册中的表(即你提供的Table 9/10),在SMOD1=0,波特率9600一行,找到CPU时钟频率为11.0592MHz(表中标*),对应的Timer Count值为-3(即256-3=253),与我们的计算完全吻合。
避坑指南:
- 晶振选择:11.0592MHz是串口应用的“黄金频率”,因为它能被9600、19200、38400、57600等常用波特率整除,计算出的
TH1是整数,没有误差。如果使用12MHz晶振,计算9600波特率时TH1=256-3.125≈252.875,取整253会有误差,可能导致通信不稳定,尤其在长距离或高速时。- 误差累积:即使使用11.0592MHz,在更高的波特率(如115200)下,也可能需要特定的
SMOD1和TH1组合来降低误差。务必查表或精确计算,确保波特率误差在可接受范围(通常要求<2%)。- 定时器1模式:务必设置为模式2(8位自动重装),
TMOD寄存器的高4位应配置为0010B。同时,切记关闭定时器1的中断(ET1=0),因为它现在被用作波特率发生器,我们不需要它的溢出中断。
3. 看门狗定时器(WDT)原理与抗干扰设计
3.1 看门狗的本质:系统运行的“保险丝”
看门狗定时器是一个独立的计数器,其核心思想是“信任但要核查”。在程序正常运行时,你需要定期向看门狗发送一个“喂狗”信号,重置这个计数器,防止其溢出。一旦程序跑飞、陷入死循环或发生其他不可预知的故障,导致无法按时“喂狗”,看门狗计数器就会溢出,并触发一个系统复位信号,强制整个单片机重启,从而让系统从故障中恢复到一个已知的初始状态。对于P87LPC764这类用于工业或关键场合的MCU,看门狗不是可选功能,而是必须启用的可靠性保障。
P87LPC764的看门狗有一个非常重要的特点:当通过配置位WDTE使能其看门狗功能后,它由一个完全独立的片内RC振荡器驱动。这意味着即使主CPU的时钟(外部晶振)因为某种原因停振了,看门狗依然在工作!这提供了真正的“振荡器失效检测”能力。当然,这个RC振荡器的精度不高(典型±60%),所以看门狗的定时时间是一个范围,而非精确值。
3.2 看门狗控制寄存器(WDCON)配置详解
看门狗的所有行为都由WDCON寄存器(地址A7h)控制。我们重点看几个关键位:
- WDOVF (WDCON.5): 看门狗溢出标志。当看门狗定时器溢出(无论是导致复位还是作为间隔定时器)时,此位由硬件置1。它不会自动清零,必须由软件在喂狗时或初始化时手动清零。这个标志位非常有用,可以用来判断本次复位源是否是看门狗超时,从而在程序启动时执行不同的恢复逻辑(例如,从备份参数中恢复数据,而不是使用默认值)。
- WDRUN (WDCON.4): 看门狗运行控制。
1=启动/运行,0=停止。但是请注意!如果配置位WDTE=1(使能看门狗功能),那么这一位会被强制为1,也就是说你无法通过软件停止看门狗,这保证了看门狗一旦启用就无法被意外关闭,增强了可靠性。 - WDCLK (WDCON.3): 看门狗时钟源选择。
0=使用独立的内部RC振荡器(默认且在看门狗模式下强制使用);1=使用CPU时钟/6。当WDTE=1时,此位强制为0。只有当WDTE=0(看门狗作为普通间隔定时器使用时),你才能选择CPU时钟作为源。 - WDS2, WDS1, WDS0 (WDCON.2-0): 看门狗超时时间选择位。这三位组合选择看门狗计数器的分频系数,从而决定从开始计数到溢出复位的时间间隔。时间范围从标称25ms到3.2秒(考虑±60%误差,实际范围更宽)。
超时时间选择表(基于独立RC振荡器,典型频率~500kHz):
| WDS2 | WDS1 | WDS0 | 计数时钟数 | 最小时间 | 标称时间 | 最大时间 |
|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 8,192 | 10 ms | 25 ms | 40 ms |
| 0 | 0 | 1 | 16,384 | 20 ms | 50 ms | 80 ms |
| 0 | 1 | 0 | 32,768 | 41 ms | 100 ms | 160 ms |
| 0 | 1 | 1 | 65,536 | 82 ms | 200 ms | 320 ms |
| 1 | 0 | 0 | 131,072 | 165 ms | 400 ms | 640 ms |
| 1 | 0 | 1 | 262,144 | 330 ms | 800 ms | 1280 ms |
| 1 | 1 | 0 | 524,288 | 660 ms | 1.60 sec | 2.60 sec |
| 1 | 1 | 1 | 1,048,576 | 1.3 sec | 3.20 sec | 5.30 sec |
配置心得:超时时间的选择是一门艺术。时间太短(如25ms),可能会因为某个稍长的但正常的任务(如复杂的计算或等待某个外设响应)导致误复位。时间太长(如3.2秒),则意味着系统发生故障后需要很长时间才能恢复。通常,我会选择一个比主循环最长执行时间(加上所有可能的中断服务时间)长2-3倍的时间。例如,如果主循环保证在100ms内能跑完一圈,那么选择200ms或400ms的标称超时时间是比较安全的。务必在程序实际运行中,用示波器或IO口翻转的方法,测量一下主循环和关键任务的实际耗时,再确定这个值。
3.3 正确的喂狗序列与程序结构设计
喂狗不是简单地写一个值到寄存器,而是一个特定的序列:先写0x1E,再写0xE1到WDRST寄存器。这两个写操作不需要是连续的指令,中间可以插入其他代码,但必须在超时之前完成。
; 汇编语言喂狗示例 WDT_Feed: MOV WDRST, #1Eh ; 第一步:写入0x1E MOV WDRST, #0E1h ; 第二步:写入0xE1 RET// C语言喂狗示例(假设已通过sfr定义WDRST) void FeedWatchdog(void) { WDRST = 0x1E; WDRST = 0xE1; }喂狗程序的设计哲学与常见陷阱:
- 单一喂狗点:强烈建议在整个程序中只在一个地方喂狗,通常放在主循环的末尾。这样可以清晰地表明:只要程序能正常执行完一圈主循环,系统就是健康的。避免在多个中断服务程序或分散的子程序中喂狗,否则一旦某个分支出现死循环,其他分支可能还在正常喂狗,导致看门狗失效,无法检测出该故障。
- 初始化顺序:芯片复位后,看门狗可能已经启动(取决于
WDTE配置位)。你有一段有限的时间(通常是最大超时时间,如几十毫秒)来完成硬件初始化和看门狗本身的配置。推荐的初始化顺序是:上电 → 进行最关键的硬件初始化(如设置堆栈指针)→立即进行一次喂狗→ 然后配置WDCON寄存器(设置超时时间等)→ 继续其他初始化。这个“先喂狗,再配置”的顺序可以防止在冗长的初始化过程中看门狗溢出。 - 低功耗模式下的处理:如果程序会进入空闲(Idle)或掉电(Power Down)模式,需要特别注意。在掉电模式下,CPU时钟停止,但如果看门狗由独立RC振荡器驱动,它仍在运行!如果你打算在掉电模式下停留时间超过看门狗超时时间,必须在进入低功耗模式之前禁用看门狗(如果
WDTE=0则可以,如果WDTE=1则无法禁用),或者确保有一种机制(如外部中断)能定期唤醒CPU并喂狗。在空闲模式下,CPU时钟暂停但外设可能还在运行,需根据具体设计判断。 - 喂狗指令的原子性:在复杂的、可能被高优先级中断打断的程序中,要确保喂狗序列(两条写指令)不被拆散。虽然数据手册说两条指令不必连续,但如果刚写完0x1E就被打断,中断服务程序执行了很久才返回,此时看门狗可能已经超时。因此,在喂狗前后临时关闭中断是一个常见的谨慎做法。
// 更安全的喂狗函数(关闭中断保护) void Safe_FeedWatchdog(void) { EA = 0; // 关闭总中断 WDRST = 0x1E; WDRST = 0xE1; EA = 1; // 重新开启中断 }3.4 看门狗复位与系统恢复策略
当看门狗溢出时,会产生一个持续约1微秒的内部复位信号。这个复位会将大多数特殊功能寄存器(SFR)复位到它们的默认状态,程序计数器(PC)归零,从0x0000地址重新开始执行。
如何区分看门狗复位和其他复位?这是实现智能系统恢复的关键。P87LPC764的WDCON寄存器中的WDOVF标志位在发生看门狗溢出复位后会被置1,并且不会被硬件自动清零。因此,在程序启动代码(main函数最开始的地方)可以检查这个标志:
bit systemWasResetByWDT; // 定义一个位变量记录复位类型 void main(void) { // 检查看门狗溢出标志 if (WDCON & 0x20) { // 检查WDOVF位 (WDCON.5) systemWasResetByWDT = 1; WDCON &= ~0x20; // 必须手动清除WDOVF标志! // 执行看门狗复位后的恢复操作,例如: // - 从备份RAM中恢复关键数据 // - 增加复位计数,超过阈值后报警 // - 初始化到一种安全状态 } else { systemWasResetByWDT = 0; // 正常上电/外部复位,执行常规初始化 } // ... 其他公共初始化代码 ... while(1) { // 主循环 // ... Safe_FeedWatchdog(); // 在主循环末尾喂狗 } }通过区分复位源,你可以设计更健壮的系统。例如,如果是正常上电,则使用默认参数;如果是看门狗复位,则可能意味着上次运行出现了异常,可以尝试恢复之前保存的运行状态,或者切换到更保守的安全模式。
4. 自动地址识别与多机通信实战
4.1 硬件地址过滤机制解析
多机通信是UART模式2和3的高级应用。传统软件方式需要每个从机接收所有地址帧并进行比对,消耗CPU资源。P87LPC764的自动地址识别功能将这部分工作交给了硬件。
其核心是两个特殊功能寄存器:
- SADDR (地址寄存器):存储本机的硬件地址。
- SADEN (地址掩码寄存器):定义SADDR中哪些位是必须匹配的(掩码位为1),哪些位是“无关位”(掩码位为0)。
硬件比较的逻辑是:当SM2=1且接收到一帧数据(第9位RB8=1,表示是地址帧)时,硬件会进行如下操作:(接收到的地址字节) & (SADEN) == (SADDR) & (SADEN)如果比较结果为真,则置位RI,产生中断;否则,硬件直接忽略该帧,不产生中断。
广播地址则是通过SADDR和SADEN的逻辑或运算得出:Broadcast_Addr = SADDR | SADEN。任何与此广播地址匹配的地址帧(即对于SADEN中为0的“无关位”,接收地址可以是任意值),都会触发所有从机中断。
4.2 灵活寻址方案设计实例
假设我们有一个主机和三个从机(Slave 0, 1, 2)。我们希望实现:
- 主机可以单独寻址任何一个从机。
- 主机可以同时寻址Slave 0和Slave 1,但排除Slave 2。
- 主机可以向所有从机广播。
我们可以这样配置(以下为示例,地址可自定义):
// 假设我们使用8位地址,第9位TB8/RB8用于标识地址帧(1)/数据帧(0) // Slave 0 配置 #define SLAVE0_SADDR 0xC0 // 1100 0000 #define SLAVE0_SADEN 0xFD // 1111 1101 // Given Address = 1100 00X0 (X表示无关位) // 这意味着Slave 0要求地址的bit7,6,5,4,3,1必须为1,0,0,0,0,0;bit0无关。 // 其唯一地址可以是:0xC2 (1100 0010),因为bit0=1,其他位符合要求。 // Slave 1 配置 #define SLAVE1_SADDR 0xC0 // 1100 0000 #define SLAVE1_SADEN 0xFE // 1111 1110 // Given Address = 1100 000X // Slave 1要求地址的bit7,6,5,4,3,2,1必须为1,0,0,0,0,0,0;bit0无关。 // 其唯一地址可以是:0xC1 (1100 0001),因为bit0=1。 // Slave 2 配置 #define SLAVE2_SADDR 0xE0 // 1110 0000 #define SLAVE2_SADEN 0xFC // 1111 1100 // Given Address = 1110 00XX // Slave 2要求地址的bit7,6,5,4必须为1,1,1,0;bit3,2无关。 // 其唯一地址可以是:0xE3 (1110 0011),因为bit3,2=0,0? 这里需要根据掩码计算,实际上掩码0xFC(1111 1100)表示bit1,0是无关位。 // 更准确地说,Slave 2关心的是高6位(1110 00),低2位任意。所以0xE0到0xE3都是它的给定地址范围。 // 为了唯一寻址,我们可以选一个不在其他从机给定范围内的地址,例如0xE3。 // 初始化代码片段 void UART_Init_Slave(uint8_t saddr, uint8_t saden) { SCON = 0xF0; // 模式3 (SM0=1, SM1=1), REN=1, SM2=1 (使能地址识别) PCON &= 0x3F; // 确保SMOD0=0, SMOD1=0 (根据波特率需要设置) // ... 配置定时器1产生波特率 ... SADDR = saddr; SADEN = saden; ES = 1; // 使能串口中断 EA = 1; TR1 = 1; // 启动定时器1 }通信流程:
- 所有从机初始化时
SM2=1,只响应地址帧(RB8=1)。 - 主机要发送数据给某个从机(如Slave 0): a. 发送一个地址帧(
TB8=1),地址内容为Slave 0的唯一地址(如0xC2)。 b. 所有从机收到地址帧,硬件自动比较。只有Slave 0比较成功,产生中断。在中断中,Slave 0软件清除自己的SM2位(SM2=0),准备接收后续数据帧。 c. 主机接着发送数据帧(TB8=0)。此时,只有SM2=0的Slave 0会接收数据并中断。其他从机因SM2=1且RB8=0,硬件忽略此帧。 d. Slave 0接收完所有数据后,应重新置位SM2=1,等待下一次地址呼叫。 - 主机要广播:发送地址帧,地址为广播地址(通常为0xFF,因为
SADDR|SADEN的结果中0位经或运算后通常为1)。所有从机都会响应,并清除各自的SM2以接收后续数据。
注意事项:自动地址识别极大地简化了多机通信的软件协议,但要求通信协议严格遵循“地址帧-数据帧”的格式。一旦某个从机在接收数据后忘记将
SM2置回1,它将会接收所有后续的数据帧,造成混乱。因此,在从机程序中,处理完一包数据后,务必重置SM2=1。
5. 常见问题排查与调试技巧实录
5.1 UART通信不通?按此清单逐项排查
物理层检查:
- 线序:TX接RX,RX接TX,GND共地。这是最常犯的低级错误。
- 电平:确认是TTL电平(0V/3.3V或5V)还是RS232电平(±12V)。P87LPC764是TTL电平,直接接电脑串口需要USB-TTL转换器,不能接DB9的RS232口。
- 电源与接地:确保系统供电稳定,地线连接良好。噪声可能导致数据错误。
软件配置检查:
- 波特率:计算是否正确?晶振频率是否准确?
SMOD1位设置是否正确?用示波器测量TxD引脚输出的位周期(1/波特率),验证实际波特率。 - 工作模式:SCON寄存器中的
SM0和SM1设置对吗?REN位置1了吗? - 定时器1:是否配置为模式2(8位自动重装)?
TR1启动了吗?ET1中断关闭了吗? - 中断系统:如果使用中断,
ES和EA打开了吗?中断服务函数名和寄存器组设置对吗?(对于Keil C,常用void Serial_ISR(void) interrupt 4 using 1)
- 波特率:计算是否正确?晶振频率是否准确?
发送问题:
- TI标志:采用查询方式发送时,是否在写入SBUF后等待
TI置1,并及时将TI清零?一个典型错误是:while(!TI);之后没有TI = 0;。 - 缓冲区:连续发送时,是否等待前一个字节发送完成(TI置位)再写入下一个字节?快速写入SBUF会导致数据覆盖。
- TI标志:采用查询方式发送时,是否在写入SBUF后等待
接收问题:
- RI标志:查询方式下是否检查RI?中断方式下中断服务程序是否清除了RI?
- 数据覆盖:UART只有一个字节的接收缓冲。如果接收中断服务程序处理太慢,第二个字节接收完成时第一个还未被读走,第一个字节会丢失。确保中断服务程序尽可能高效,或者在主循环中轮询RI并及时读取SBUF。
- 帧错误:如果启用了FE检测(
SMOD0=1),检查SCON.7是否为1。帧错误通常意味着波特率不匹配、线路噪声或对方发送格式错误。
5.2 看门狗误复位或不起作用?深入分析
看门狗频繁复位(误触发):
- 喂狗间隔过长:主循环执行时间是否超过了看门狗的超时时间?使用IO口翻转+示波器测量主循环周期。
- 喂狗点被阻塞:程序是否在某些地方(如
while循环等待标志位、延时函数)卡住,导致无法执行到喂狗代码?检查所有循环是否有超时退出机制。 - 中断服务程序过长:高优先级中断是否执行时间太久?虽然喂狗在主循环,但如果中断频繁发生且执行时间长,会变相延长主循环周期。考虑在长中断中也加入喂狗(需谨慎设计,避免掩盖问题)。
- 低功耗模式:进入Idle或Power Down模式前,是否考虑了看门狗仍在运行?需要在睡眠前喂狗或配置更长的超时时间。
看门狗不复位(失效):
- 未正确使能:芯片的配置位
WDTE是否编程为1?对于OTP芯片,需要在烧录时设置。 - 喂狗序列错误:喂狗顺序必须是先0x1E,后0xE1。写反了、只写了一个、或者写入的值不对,都无效。
- WDCON配置后未生效:是否在系统初始化时过早配置WDCON,而看门狗在配置前就溢出了?遵循“先喂狗,再配置”的顺序。
- 软件死循环但仍在喂狗:这是最危险的情况。如果程序跑飞但恰好飞到一个包含正确喂狗指令的循环里,看门狗就失效了。这需要通过良好的代码结构(如将喂狗仅放在主循环唯一路径的末尾)和软件陷阱来尽量避免。
- 未正确使能:芯片的配置位
5.3 调试工具与技巧分享
- “软件串口”辅助调试:当硬件UART调不通时,可以用两个普通IO口模拟UART(位翻转延时实现),先确保上层通信协议是正确的,再集中精力排查硬件UART配置问题。
- IO口状态指示:在程序关键节点(如初始化完成、进入主循环、喂狗前、中断触发时)用不同的IO口输出脉冲或电平变化。用逻辑分析仪或示波器同时捕捉这些IO和串口信号,可以直观看到程序执行流和通信时序的关系,对于排查死锁、时序问题非常有效。
- 利用WDOVF标志:在程序开头读取并清除
WDOVF,然后通过串口或将IO口状态发送出来,可以帮助你确认复位是否由看门狗引起,以及复位的频率。 - 计算波特率误差:使用公式
误差(%) = [(实际波特率 - 目标波特率) / 目标波特率] * 100%。确保误差在芯片允许范围内(通常<2%,理想<1%)。11.0592MHz晶振在大多数标准波特率下误差为0%,是首选。 - 逻辑分析仪是神器:一个简单的逻辑分析仪(甚至某些示波器的串行解码功能)可以直观显示串口线上每一位的波形、电平、时间,直接验证起始位、数据位、停止位是否正确,波特率是否精准,是调试串口通信的最高效工具。
