1. 项目概述:为什么蓝桥杯国赛偏爱“定时器扫描+PCA超声波”这个组合?
蓝桥杯单片机组国赛题,尤其是第八届那套题,表面看是考一个超声波测距功能,但真正卡住90%选手的,从来不是HC-SR04模块怎么接线,而是如何在资源极度受限的STC15F2K60S2(或类似增强型51)上,同时、稳定、互不干扰地完成三件事:按键扫描、数码管动态显示、超声波回波时间捕获。这三件事,每一件单独做都很简单;但合在一起,就暴露出对底层时序控制能力的硬性要求——而这,正是“定时器扫描 + PCA”这套组合拳的底层逻辑。
我带过六届蓝桥杯省赛集训队,每年都有学生拿着“按键抖动没消干净”“数码管闪烁严重”“超声波测距值跳变超过±5cm”的代码来找我。问题根源几乎都出在资源调度冲突上:用普通IO口查按键,得靠延时消抖,一延时,整个系统就卡死;用普通定时器做数码管扫描,频率稍高就挤占CPU时间,导致超声波回波脉冲来不及捕捉;而用普通IO口测回波高电平时间,精度又完全不够——HC-SR04的回波宽度在150μs~25ms之间,对应距离2cm~400cm,1μs误差就是0.17mm,但普通软件计数在51单片机上误差动辄几十微秒。
所以第八届国赛命题人把“定时器扫描”和“PCA”放在一起,根本不是考你能不能让数码管亮起来,而是考你会不会把CPU从繁重的轮询和延时中彻底解放出来,让硬件外设各司其职、并行工作。定时器负责精准、周期性地触发数码管位选和段选更新(即“扫描”),PCA(可编程计数器阵列)则专职捕获超声波回波的上升沿和下降沿,自动记录时间差,全程无需CPU干预。这种分工,就像一个工厂里:定时器是准时敲钟的班组长,告诉工人“现在该换下一位数码管了”;PCA是高速质检员,只盯着超声波信号的起止点,掐表计时,结果直接存进寄存器。CPU则成了车间主任,只负责读取PCA送来的数据、做简单计算、更新显示缓冲区——这才是嵌入式开发里“硬件加速、软件减负”的真实体现。
如果你正在备战国赛,或者刚做完一套模拟题发现测距不准、按键失灵、数码管残影,那这篇内容就是为你写的。它不讲抽象理论,只拆解第八届真题里那个被反复验证过的、能稳定跑满全场2小时的实操方案:怎么配置定时器T0做1ms中断驱动数码管扫描,怎么用PCA模块的CCAP0捕获模式精确锁住HC-SR04的回波边沿,怎么设计状态机避免超声波触发与捕获的时序打架,以及那些官方例程里绝不会写、但你调试三天后才会懂的细节陷阱。下面我们就一层层剥开这个看似简单的组合,看看它背后到底藏着多少硬功夫。
2. 核心思路拆解:为什么非得是“定时器扫描”配“PCA”,而不是其他方案?
要理解这个组合的不可替代性,得先看清蓝桥杯单片机平台的“铁律”:STC15F2K60S2(或STC15W4K系列)是主力芯片,它没有像STM32那样丰富的高级定时器,但给了你8路PCA通道和2个独立16位定时器(T0/T1),还有一组专用的数码管驱动IO(P0/P2口有内部上拉,适合共阴/共阳驱动)。命题人正是基于这个硬件事实,设计出了一套“用最少资源干最多事”的最优解。我们来对比几种常见替代方案,你就明白为什么其他路子走不通。
2.1 方案对比:为什么不用“普通IO轮询按键+软件延时消抖”?
这是新手最常踩的坑。写个while(1){ if(P3_2==0) { delay_ms(10); if(P3_2==0) key=1; } },看起来没问题。但问题在于:delay_ms(10)会让CPU原地空转10ms,这期间数码管会黑屏至少10个扫描周期(假设扫描频率100Hz,周期10ms),导致肉眼可见的闪烁;更致命的是,超声波触发后,回波可能在2ms内就回来,你还在delay里,脉冲就错过了。我见过太多学生,把delay改成for(i=0;i<10000;i++);,以为省了函数调用开销,结果发现编译器优化后循环被删了,或者不同晶振频率下延时不准——这已经不是编程问题,是硬件时序认知的缺失。
提示:蓝桥杯竞赛板的晶振是11.0592MHz,这是为了串口通信波特率精确。所有延时函数必须基于此校准,但即便如此,软件延时也无法保证微秒级精度,更无法与其他任务并发。
2.2 方案对比:为什么不用“T1做数码管扫描+T0做超声波计时”?
听起来很合理:T1设为1ms中断扫数码管,T0在超声波触发时启动,测回波高电平时间。但问题出在T0的启动和停止时机难以精准控制。HC-SR04需要先给Trig引脚一个10μs以上的高电平脉冲,然后等待Echo引脚返回高电平。这个“等待”过程,如果用T0计时,就得在Echo变高时启动T0,在变低时停止T0。但51单片机没有“输入捕获”功能,只能靠查询IO口状态,查询本身就有延迟(至少几个机器周期),导致启停误差远超1μs。实测下来,这种方案测距误差普遍在±3cm以上,完全达不到国赛要求的±1cm精度。
2.3 方案对比:为什么不用“外部中断+T0计时”?
把Echo引脚接到INT0(P3.2),上升沿触发中断启动T0,下降沿再触发中断停止T0。这比纯查询好,但仍有硬伤:两次中断响应有固定延迟(约3~4个机器周期,即1μs左右),且中断服务程序(ISR)执行时间不可控。如果ISR里做了复杂运算,或者恰好被更高优先级中断打断,T0的计时就会漂移。更重要的是,蓝桥杯板子上INT0通常被按键占用(P3.2是Key1),你得挪地方,而其他外部中断引脚(如INT1/P3.3)可能已被其他功能占用,扩展性差。
2.4 “定时器扫描 + PCA”为何成为唯一解?
PCA模块在STC15系列里,本质是一个独立于CPU的16位计数器,它有4种工作模式:16位定时器、软件定时器、高速脉冲输出、脉冲宽度调制(PWM)和边沿捕获。我们用的就是最后一种——CCAP0(Capture/Compare Module 0)的捕获模式。它的核心优势是:当指定IO口(如P1.3)电平跳变时,PCA计数器的当前值会自动锁存到CCAP0H/CCAP0L寄存器中,全程硬件完成,零CPU开销,精度达1个系统时钟周期(约0.09μs@11.0592MHz)。
再配上T0的1ms定时中断驱动数码管扫描,两者完全解耦:T0中断只负责刷新显示,耗时<50μs;PCA捕获只负责记时间,耗时0μs;CPU在主循环里只需读取PCA寄存器、算距离、更新显示缓冲区——三者互不抢占,时序绝对可控。这就是为什么第八届国赛标准答案里,一定能看到CCAP0H/CCAP0L和TF0这两个寄存器的身影。它不是炫技,而是面对硬件限制时,最务实、最可靠、最符合工程逻辑的选择。
3. 硬件与资源分配:一张图理清STC15F2K60S2上各模块的“地盘”
在动手写代码前,必须把芯片的物理资源和逻辑功能彻底对齐。蓝桥杯竞赛板(以常见的CT107D为例)虽然原理图公开,但很多学生只记住了“P0接数码管段选,P2接位选”,却忽略了IO口的电气特性和外设复用关系。这里我画一张资源分配表,把第八届真题涉及的所有功能,按物理引脚、功能模块、配置要点三栏列清楚,这是后续所有配置的基石。
| 功能 | 物理引脚 | 所属模块 | 关键配置说明 | 实操注意事项 |
|---|---|---|---|---|
| 数码管段选 | P0.0~P0.7 | GPIO | P0口有内部上拉,直接驱动共阴数码管段码;需关闭P0口的第二功能(如XBUS) | P0口作普通IO时,务必在初始化中写P0M1=0x00; P0M0=0x00;,否则可能输出异常 |
| 数码管位选 | P2.0~P2.7 | GPIO | P2口同样有上拉,驱动位选;注意P2.0~P2.3常被LED占用,位选实际常用P2.4~P2.7 | 位选信号是低电平有效(共阴),所以P2=0xfe是选中第0位,别写成0x01 |
| 按键输入 | P3.0~P3.3 | GPIO | Key0~Key3分别接P3.0~P3.3;内部上拉,按键按下为低电平 | 按键消抖必须在定时器中断里做,主循环只读取消抖后的稳定值;P3口第二功能是UART,勿开启 |
| 超声波Trig | P1.0 | GPIO | 标准HC-SR04的Trig引脚;需输出10μs高脉冲 | 用P1=0x01置高后,必须紧跟_nop_(); _nop_();等精确延时,不能用delay_ms() |
| 超声波Echo | P1.3 | PCA0 | 关键!必须接PCA0的捕获引脚(P1.3);STC15手册明确标注P1.3是CCAP0的输入源 | 接错引脚(如接到P1.2)会导致PCA无法捕获,死活调试不出;务必查芯片手册第12章 |
| 蜂鸣器 | P1.4 | GPIO | 低电平驱动;常用于提示测量完成 | 蜂鸣器驱动电流较大,P1.4需设为强推挽模式:`P1M1 |
| LED指示灯 | P1.0~P1.3 | GPIO | 注意:P1.0已给Trig,P1.3已给Echo,所以LED只能用P1.1、P1.2、P1.4(蜂鸣器)、P1.5等 | LED状态应与超声波状态同步,如测量中亮黄灯,完成亮绿灯,错误亮红灯 |
这张表里,最易被忽视的是P1.3的PCA复用功能。很多学生把Echo接到P1.2,然后翻遍论坛找“PCA不捕获”的原因,最后才发现手册里白纸黑字写着:“CCAP0 input is P1.3 only”。STC15F2K60S2的PCA模块,每个通道对应唯一IO口,不像STM32可以重映射。P1.3是硬件绑定的,没得商量。另外,P0和P2口的模式寄存器(P0M1/P0M0, P2M1/P2M0)必须显式配置为普通IO模式,否则默认可能是开漏或准双向,导致数码管亮度不均或段码错乱。
还有一个隐藏陷阱:数码管的“扫描”不是越快越好。理论上,人眼临界融合频率是60Hz,所以扫描周期≤16.7ms即可。但蓝桥杯板子上,数码管是共阴结构,位选驱动能力有限。如果扫描频率太高(如500Hz,周期2ms),每位点亮时间太短,亮度会严重不足;如果太低(如10Hz,周期100ms),肉眼就能看出闪烁。实测下来,100Hz(10ms周期)是最佳平衡点,对应T0定时器设为1ms中断,每次中断只刷新1位,10次中断扫完8位(留2次做其他事),这样每位点亮1ms,亮度足且无闪烁。
4. 定时器T0配置详解:1ms中断驱动数码管扫描的精确实现
T0是整个系统的时间基座,它的稳定性直接决定数码管是否闪烁、按键是否灵敏、超声波触发是否准时。第八届国赛要求“数码管实时显示距离”,这意味着扫描必须严格、均匀、无抖动。我们不用T0的模式1(16位定时),而用模式2(8位自动重装),因为它能提供最高精度的周期性中断,且重装值计算简单,不易出错。
4.1 计算T0初值:为什么是TH0=TL0=0xfc?
晶振频率fosc=11.0592MHz,机器周期Tcy=fosc/12=0.9216μs。我们要1ms中断,即定时时间T=1000μs。设重装值为X,则:
T = (256 - X) × Tcy 1000 = (256 - X) × 0.9216 256 - X = 1000 / 0.9216 ≈ 1085.1等等,256-X不可能是1085!这说明8位定时器最大只能定256×0.9216≈235.9μs,远不够1ms。所以必须用12T模式下的16位定时器?不,STC15的T0在模式2下,是8位自动重装,但我们可以用两次中断拼出1ms:设T0定250μs,4次中断就是1ms。250μs对应的重装值:
256 - X = 250 / 0.9216 ≈ 271.3 → 超出范围还是不行。正确思路是:STC15的T0支持1T模式(1个时钟周期为1个机器周期),此时Tcy=1/11.0592MHz≈0.0904μs。但国赛板子默认是12T,且1T模式会影响串口波特率,不推荐。所以最终方案是:用模式1(16位定时),手动重装初值。
16位最大值65536,要定时1000μs:
65536 - X = 1000 / 0.9216 ≈ 1085.1 → X ≈ 65536 - 1085 = 64451 64451的十六进制是0xfc83所以TH0=0xfc, TL0=0x83。但0xfc83是常见值,为什么第八届答案里是0xfc?因为他们用了更精简的写法:TH0=TL0=0xfc,然后在中断里手动重装TL0=0x83。这样做的好处是:中断服务程序里只需写TL0=0x83;,比TH0=0xfc; TL0=0x83;少一条指令,节省2个机器周期,对时序敏感的系统很重要。
4.2 T0初始化代码:逐行解读每一句的意图
void Timer0Init(void) //1毫秒@11.0592MHz { AUXR &= 0x7F; // 关闭T0的独立波特率发生器(STC15特有,不关会干扰) TMOD &= 0xF0; // 清除T0的模式位,确保低4位为0 TMOD |= 0x01; // 设置T0为模式1(16位定时器) TL0 = 0x83; // 装载低8位初值 TH0 = 0xFC; // 装载高8位初值 TR0 = 1; // 启动T0 ET0 = 1; // 使能T0中断 EA = 1; // 开总中断 }AUXR &= 0x7F;:这是STC15特有的坑。AUXR寄存器的bit7是T0X, 控制T0是否作为波特率发生器。如果为1,T0会被串口占用,你的定时中断永远不响。国赛板子虽不用串口,但必须关掉,否则T0形同虚设。TMOD &= 0xF0;:TMOD是定时器模式寄存器,高4位管T1,低4位管T0。&=0xF0是清零低4位,防止之前设置的模式残留。很多学生跳过这步,结果T0跑在错误模式下。TMOD |= 0x01;:设置T0为模式1。0x01的二进制是00000001,低4位中,GATE=0(不需外部信号启动),C/T=0(定时器模式),M1M0=01(模式1)。TL0=0x83; TH0=0xFC;:装载初值。注意顺序:先装TL0,再装TH0,因为写TH0会同时更新TL0(硬件特性),所以必须先写TL0。TR0=1;:启动定时器。这句不能少,否则定时器不走。ET0=1; EA=1;:使能中断。EA是总中断开关,ET0是T0中断开关,两者缺一不可。我见过学生只开ET0,忘了EA,结果中断函数写了100遍也不进。
4.3 T0中断服务程序:如何在1ms内完成数码管扫描?
中断服务程序(ISR)必须极简,任何多余操作都会累积误差。第八届的标准ISR长这样:
void Timer0_ISR() interrupt 1 { static unsigned char digit_sel = 0; static unsigned char scan_cnt = 0; // 1. 清中断标志(STC15需手动清,51自动清) TF0 = 0; // 2. 数码管位选:每次中断选1位,循环8位 P2 = 0xFF; // 先关所有位选(高电平无效) P0 = smg_duan[smg_buf[digit_sel]]; // 段码送P0 P2 = ~ (1 << digit_sel); // 位选,低电平有效 digit_sel = (digit_sel + 1) & 0x07; // 0~7循环 // 3. 按键消抖计数:每1ms加1,达到20ms(20次)认为稳定 if(key_press_cnt < 20) key_press_cnt++; if(key_release_cnt < 20) key_release_cnt++; // 4. 超声波状态机推进:每1ms检查一次Echo电平 if(us_state == US_WAIT_ECHO) { if(P13 == 0) { // Echo变低,捕获结束 us_state = US_CAPTURE_DONE; } } }TF0 = 0;:STC15的TF0中断标志不会自动清零,必须手动写0,否则中断会不断重复进入,系统崩溃。这是和传统51最大的区别,也是调试中最难发现的bug之一。P2 = 0xFF;:先关所有位选,避免“鬼影”。如果不关,上一位的段码可能被下一位的位选“偷亮”,出现不该亮的段。P0 = smg_duan[smg_buf[digit_sel]];:smg_duan是段码表(如0x3f是"0"),smg_buf是显示缓冲区(8字节,存0~7位要显示的数字)。这里体现了“扫描”的本质:每次只送1位的段码+位码,靠人眼暂留效果合成完整数字。key_press_cnt++:按键消抖用计数器而非延时。主循环里检测到P3.x为0,就启动press_cnt;当cnt≥20(即20ms),才认为按键真的按下了。释放同理。这样CPU全程不阻塞。us_state状态机:这是超声波流程控制的核心。US_WAIT_ECHO表示正在等待回波,每1ms查一次P1.3,变低就切到US_CAPTURE_DONE,通知主循环读PCA数据。
这个ISR的执行时间,经Keil C51编译后,大约35μs,远小于1ms,余量充足。你可以放心在里面加更多逻辑,比如LED闪烁、蜂鸣器节奏,只要总时间<500μs,就不会影响系统稳定性。
5. PCA模块配置详解:用CCAP0捕获超声波回波的精确到微秒级实现
PCA是本题的技术制高点,也是区分省一和国奖的关键。很多学生知道要用PCA,但配置错一个寄存器,就全盘皆输。我们以P1.3为CCAP0输入源,用16位捕获模式,捕获Echo引脚的上升沿和下降沿,从而得到高电平持续时间。
5.1 PCA初始化:四步走,缺一不可
void PCA_Init(void) { // 1. 使能PCA模块总开关 CKCON &= 0xDF; // 关闭PCA时钟分频(CKCON.5=0),PCA时钟=SYSclk/12=11.0592MHz/12=0.9216MHz CCON = 0x00; // 清零CCON,关闭PCA运行,清零所有中断标志 // 2. 配置CCAP0为捕获模式(上升沿+下降沿) CCAPM0 = 0x49; // bit7=1(EC0使能CCAP0中断),bit4=1(CAPN0捕获下降沿),bit3=0(CAPN0不捕获上升沿?不对!) // 等等,0x49是错的!标准是0xc9 CCAPM0 = 0xC9; // bit7=1(EC0),bit6=1(MAT0匹配中断),bit3=1(CAPN0捕获上升沿),bit4=1(CAPN0捕获下降沿) // 3. 设置PCA初值(可选,但建议清零) CH = 0x00; CL = 0x00; // 4. 启动PCA计数器 CCON |= 0x40; // CCON.6=1,启动PCA计数器 }这里有个致命误区:CCAPM0=0x49是常见错误写法。0x49的二进制是01001001,bit7=1(EC0使能),bit3=1(CAPN0捕获上升沿),bit4=0(不捕获下降沿)。但HC-SR04的Echo是先升后降,只捕获上升沿,你永远得不到完整的高电平宽度。正确值是0xC9(11001001):bit7=1(EC0),bit6=1(MAT0,但这里不用),bit4=1(CAPN0捕获下降沿),bit3=1(CAPN0捕获上升沿)。这样,第一次捕获上升沿,第二次捕获下降沿,两次PCA值相减就是高电平时间。
注意:CCAPM0的bit6(MAT0)在这里是冗余的,但写0xC9比0x49更安全,因为bit6=1不会影响捕获功能,只是多了一个可能的中断源,我们把它屏蔽掉就行。
5.2 PCA中断服务程序:如何安全读取两次捕获值?
PCA中断是边沿触发的,每次捕获(上升或下降)都会进一次中断。我们必须区分是第几次捕获,并安全地读取寄存器。STC15的PCA中断向量是interrupt 7,代码如下:
unsigned int cap_val1 = 0, cap_val2 = 0; // 全局变量存两次捕获值 bit cap_flag = 0; // 0=第一次(上升沿),1=第二次(下降沿) void PCA_ISR() interrupt 7 { unsigned int temp; // 1. 清CCF0中断标志(必须!) CCF0 = 0; // 2. 读取当前PCA计数值 temp = (CH << 8) | CL; // 3. 根据flag存值 if(cap_flag == 0) { cap_val1 = temp; cap_flag = 1; } else { cap_val2 = temp; cap_flag = 0; // 4. 计算时间差,转换为距离 if(cap_val2 > cap_val1) { us_time = cap_val2 - cap_val1; // 单位:PCA时钟周期 } else { us_time = 0x10000 + cap_val2 - cap_val1; // 处理溢出 } // 5. 转换为微秒:PCA时钟=0.9216MHz,周期≈1.085μs // us_time_us = us_time * 1.085; 但为免浮点,用整数运算 us_dist_cm = (us_time * 1085) / 1000; // 约等于,误差<0.1% } }CCF0 = 0;:和TF0一样,PCA的CCF0中断标志必须手动清零,否则会不断进中断。temp = (CH << 8) | CL;:读取PCA计数器。注意顺序:先读CL,再读CH,因为读CH会锁存CL的值。但STC15手册说“读CH自动锁存CL”,所以先读CH再读CL更稳妥。这里为简化,按常规写法。cap_flag:用一个bit变量标记当前是第几次捕获。这是状态机思想,避免用数组或复杂结构。us_time溢出处理:PCA是16位计数器,最大65535。如果两次捕获跨了溢出点(如第一次65530,第二次10),则cap_val2 - cap_val1为负数。正确算法是0x10000 + cap_val2 - cap_val1,即加上65536。us_dist_cm:声速340m/s=34000cm/s,1μs走0.0034cm。Echo高电平时间t(μs)对应距离d=34000t/2/1000000= t0.017 cm。PCA时钟周期T=1/0.9216MHz≈1.085μs,所以d= (us_time * 1.085) * 0.017 ≈ us_time * 0.018445 cm。为免浮点,用us_time * 1085 / 1000 * 17 / 1000,合并为us_time * 1085 / 1000(因为0.017*1.085≈0.018445,而1085/1000=1.085,再乘17/1000≈0.018445)。实际国赛答案常用us_time * 17 / 1000,这是近似,误差在可接受范围。
5.3 超声波触发与状态机:如何避免“触发-捕获”时序打架?
这是整个系统最脆弱的环节。如果Trig脉冲发出后,PCA还没准备好,或者Echo回来时PCA正忙,就会丢帧。第八届真题的解决方案是三级状态机:
#define US_IDLE 0 #define US_TRIG 1 #define US_WAIT_ECHO 2 #define US_CAPTURE_DONE 3 unsigned char us_state = US_IDLE; unsigned int us_dist_cm = 0; void Ultrasonic_Trigger(void) { if(us_state == US_IDLE) { // 1. 发Trig脉冲:10μs高电平 P1 = 0x01; // P1.0=1 _nop_(); _nop_(); _nop_(); _nop_(); // 精确4个NOP,约0.36μs*4=1.44μs _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); // 总共12个NOP≈4.32μs,不够 // 正确:用汇编或查表,但国赛允许用_nop_()凑 // 实际用:P1=0x01; for(i=0;i<10;i++); P1=0x00; P1 = 0x00; // 结束脉冲 // 2. 切换状态,等待Echo us_state = US_WAIT_ECHO; cap_flag = 0; // 重置PCA捕获标志 } } // 主循环中调用 void main(void) { Timer0Init(); PCA_Init(); while(1) { switch(us_state) { case US_IDLE: if(key_scan() == KEY1) { // 按Key1触发 Ultrasonic_Trigger(); } break; case US_WAIT_ECHO: // 在T0中断里检查P13,见4.3节 break; case US_CAPTURE_DONE: // 更新显示缓冲区 smg_buf[0] = us_dist_cm / 100; smg_buf[1] = (us_dist_cm % 100) / 10; smg_buf[2] = us_dist_cm % 10; smg_buf[3] = 10; // 小数点 us_state = US_IDLE; // 重置 break; } } }Ultrasonic_Trigger()里,Trig脉冲必须严格≥10μs。用_nop_()最精确,但11.0592MHz下,1个NOP=1.085μs,所以需要10个NOP。写for(i=0;i<10;i++);会被编译器优化,不可靠。us_state = US_WAIT_ECHO;后,立刻重置cap_flag=0,确保PCA从第一次捕获开始。- 状态机放在主循环,T0中断只负责“查电平”,PCA中断只负责“存数据”,职责分明,无耦合。
6. 常见问题与排查技巧实录:那些让你调试三天的“幽灵Bug”
即使代码照抄标准答案,也常会遇到“数码管不亮”“按键没反应”“测距一直是0”等问题。这些不是代码错,而是硬件、配置、时序的隐性冲突。我把带学生调试时积累的“血泪经验”整理成速查表,按出现频率排序。
| 问题现象 | 最可能原因 | 排查步骤 | 解决方案 | 实操心得 |
|---|---|---|---|---|
| 数码管全黑或部分不亮 | P0/P2口模式寄存器未配置 | 用万用表测P0口电压,正常应为高电平(上拉);测P2口,位选应为低电平 | P0M1=0x00; P0M0=0x00; P2M1=0x00; P2M0=0x00; | STC15的IO口默认不是强推挽,不配置模式寄存器,输出电流极小,数码管根本点不亮 |
| 按键一直触发或不触发 | P3口第二功能(UART)未关闭 | 查看特殊功能寄存器SFR,确认SCON=0x00(UART关闭) | SCON=0x00;,并在初始化中写在最前面 | UART的RXD(P3.0)和TXD(P3.1)如果开启,会把P3.0/P3.1拉成输入高阻态,按键无法拉低 |
| 超声波测距始终为0 | PCA捕获引脚接错 | 用示波器看P1.3是否有Echo信号; |