输入检测实验演示硬件理论
033.3.1 输入检测实验演示硬件理论
#include "reg52.h"
#include <intrins.h>
typedef unsigned char uchar; // 类型别名
typedef unsigned int uint; // 类型别名
sbit LED1 = P1^0;
sbit LED2 = P1^1;
sbit LED3 = P1^2;
sbit LED4 = P1^3;
sbit KEY1 = P3^2;
sbit KEY2 = P3^3;
sbit SUN = P3^7;
void Delay_xms(uint xms) // @11.0592MHz
{
uchar data i, j;
while (xms)
{
_nop_();
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
xms--;
}
}
void main()
{
while (1)
{
if (0 == KEY1)
{
Delay_xms(20);
while (0 == KEY1);
Delay_xms(20);
LED1 = ~LED1;
}
if (0 == KEY2)
{
Delay_xms(20);
while (0 == KEY2);
Delay_xms(20);
LED2 = ~LED2;
}
if (0 == SUN)
{
LED3 = 1;
}
else
{
LED3 = 0;
}
}
}
一、输入检测实验演示及硬件理论
本节先运行配套的 3.1输入检测_实验演示 工程观察现象,再新建 3.2输入检测 工程讲解输入电路。上方源码是本节实际演示所用的完整 3.1 程序,保留了按键消抖、松手检测和光敏输入处理。
整套实验包含两条输入链路:按键把人的按压动作变成高低电平,光敏模块把光强变化变成高低电平。单片机读取电平后,再控制开发板上的 LED。
flowchart LR
A[按下 K1/K2] --> B[P3.2/P3.3 由高变低]
B --> C[程序消抖并等待松手]
C --> D[翻转 LED1/LED2 状态]
E[环境光变化] --> F[光敏电阻阻值变化]
F --> G[LM393 比较器输出 DO]
G --> H[P3.7 读取高低电平]
H --> I[控制 LED3]
1.1 实验演示及硬件
1.1.1 按键检测实验演示
- 按一次按键 1,LED1 的亮灭状态翻转一次;再按一次,状态再次翻转。
- 按一次按键 2,LED2 的亮灭状态翻转一次。两个按键分别检测,不互相影响。
- 光敏模块控制 LED3:遮住光敏电阻、环境变暗时 LED3 亮;用手机手电筒照射、环境变亮时 LED3 灭。这相当于一个简化的智能路灯模型。
- 如果照射后现象不明显,应先让手机手电筒靠近光敏电阻,再微调模块上的蓝色电位器,使环境光跨过比较器阈值。
这里的“按一次改变一次”不是把按键电平直接复制给 LED,而是检测到一次完整的按下和松开后执行 LED1 = ~LED1 或 LED2 = ~LED2,把原状态取反。
开发板上的两个独立按键分别接到 P3.2、P3.3,另一侧接 GND。P3 口复位后通常处于释放为高电平的准双向输入状态,按下按键则把端口直接接到地:
释放:P3.x -- 内部弱上拉 --> 高电平 1
按下:P3.x -------- 按键 -------- GND
电平变为 0
因此,这两个按键是低电平有效:
| 按键状态 | P3.2/P3.3 电平 | 程序判断 |
|---|---|---|
| 松开 | 1 | 未按下 |
| 按下 | 0 | 已按下 |
开发板 LED 也采用低电平点亮电路。以 LED3 为例,其电流路径是 +5V → LED3 → 10 kΩ 限流电阻 → P1.2。当 P1.2=0 时,单片机引脚吸收电流,LED3 亮;当 P1.2=1 时,两端电位接近,LED3 灭。也就是说,输入和 LED 都存在“0 有效”的情况,读代码时不能把数字 1 想当然地理解为“灯亮”。
1.1.2 光敏传感器连接
光敏传感器模块有四个引脚,本实验只使用 VCC、GND、DO:
| 模块引脚 | 含义 | 本实验连接 | 说明 |
|---|---|---|---|
VCC | 模块电源正极 | 开发板 +5V | 给光敏电阻、LM393 和指示灯供电 |
GND | 电源地 | 开发板 GND | 两块电路必须共地,电平才有共同参考 |
DO | Digital Output,数字输出 | 单片机 P3.7 | 只输出可直接判断的高/低电平 |
AO | Analog Output,模拟输出 | 本实验不连接 | 输出随光强连续变化的电压,需由 ADC 采样 |
DO 应连接单片机端口 P3.7;P3.7 是端口编号,不是 3.7 V 电源。
推荐在断电状态下接线,再依次核对:
光敏模块 STC89C52 开发板
┌────────┐
│ VCC ├────────────────────── +5V
│ GND ├────────────────────── GND
│ DO ├────────────────────── P3.7
│ AO ├──── 本实验悬空,不接
└────────┘
这三根线缺一不可。尤其是 GND,它并不是“检测低电平的输入线”,而是供电和信号共同的零电位参考;没有共地,P3.7 看到的高低电平就不可靠。模块通常是排针,开发板也是排针或排母,可按接口选用母对母、公对母等杜邦线。公头带针,母头带孔,连接前还要核对模块丝印顺序,不能只根据线的颜色判断。
DO 与 AO 的区别如下:
DO只回答“当前光强在阈值的哪一边”,结果只有 0 或 1,51 单片机普通数字口可直接读取。AO输出连续电压,能反映相对光强,但必须通过 ADC 转换成数字量。STC89C52 本身没有片上 ADC,所以本实验不接 AO;后续使用带 ADC 的 STM32 等单片机时可以读取它。- GND 接地不等于 DO 必然为低电平。DO 的高低由光敏分压、电位器阈值和 LM393 的比较结果共同决定。
1.1.3 光敏电阻原理
光敏电阻是一个阻值随入射光变化的元件:光越强,阻值通常越小;光越弱,阻值通常越大。模块则是在光敏电阻之外增加了分压电阻、电位器、LM393 比较器、滤波电容、上拉电阻及指示灯,使其既能输出模拟量 AO,也能输出数字量 DO。
视频展示的模块电路可简化为:
VCC
│
┌────────────┼──────────────┐
│ │ │
[10 kΩ] [电位器 10 kΩ] [10 kΩ 上拉]
│ │←滑片 VREF │
VAO o─────●───────┐ └──────┐ ├────── DO ──> P3.7
│ (+) │(-) │
[光敏电阻] └────── LM393 ──────┘
│ │ 开集电极输出
GND GND
│
[104]
│
GND
另一路状态指示:VCC ── 状态 LED ── 1 kΩ ── DO
图中的连线可分成五个功能部分:
- 光敏分压:固定
10 kΩ电阻在上,光敏电阻在下,中点就是AO,同时接 LM393 的同相输入+。 - 阈值产生:电位器两端接 VCC 和 GND,滑片产生可调参考电压
VREF,接 LM393 的反相输入-。 - 电压比较:LM393 比较
VAO与VREF,把连续变化的光照电压变成 0/1。 - 输出与指示:LM393 为开集电极输出;模块上的
10 kΩ把 DO 上拉到 VCC。DO 被拉低时,状态 LED 的电流流向 DO,因此状态灯点亮。 - 滤波:标注
104的电容通常为100 nF,从 AO 接到 GND,用来滤掉部分高频干扰,避免光线轻微抖动立即影响比较结果。
光敏分压电压为:
$ V_{AO}=V_{CC}\times\frac{R_{LDR}}{10,\text{k}\Omega+R_{LDR}} $
其中 $R_{LDR}$ 是光敏电阻阻值。由公式可以直接看出:
- 光变亮:$R_{LDR}$ 变小,$V_{AO}$ 降低;
- 光变暗:$R_{LDR}$ 变大,$V_{AO}$ 升高。
电位器的作用不是改变光敏电阻,而是改变 VREF。比较器的翻转点满足 $V_{AO}\approx V_{REF}$。按图中的分压方向,阈值处的光敏电阻阻值约为:
$ R_{LDR(th)}=10,\text{k}\Omega\times\frac{V_{REF}}{V_{CC}-V_{REF}} $
所以转动电位器就是在移动“多亮才算亮”的分界线。实际调节时,可在正常环境光下慢慢旋转,找到状态灯刚好翻转的位置,再略微回调,给噪声留一点余量。
LM393 的输出不是主动输出高电平的普通推挽结构,而是一个只能把输出端拉向 GND 的开集电极晶体管:
- 当
V+ > V-时,输出晶体管截止,外部10 kΩ上拉电阻把 DO 拉成高电平; - 当
V+ < V-时,输出晶体管导通,把 DO 拉成低电平; - 没有上拉电阻时,晶体管截止后的 DO 会悬空,无法得到稳定高电平。
把传感器电路、开发板 LED 电路和程序放在一起,可得到本实验的完整状态表:
| 环境 | 光敏电阻 | VAO 与 VREF | DO/SUN | 模块状态灯 | 程序写入 LED3 | 开发板 LED3 |
|---|---|---|---|---|---|---|
| 较亮 | 阻值较小 | VAO < VREF | 0 | 亮 | 1 | 灭 |
| 较暗 | 阻值较大 | VAO > VREF | 1 | 灭 | 0 | 亮 |
这也解释了源码中看似“反着写”的逻辑:SUN == 0 时执行 LED3 = 1,实际上是让低电平有效的 LED3 熄灭;SUN == 1 时执行 LED3 = 0,LED3 才点亮。该结论来自本视频所示电路和配套源码;其他模块若交换了比较器正负输入,DO 极性可能相反,应以模块原理图或实测为准。
模块上通常有两个指示灯:电源指示灯在正确接入 VCC、GND 后常亮;开关/状态指示灯跟随 DO 的吸电流状态变化。若光线在阈值附近波动,DO 可能快速来回翻转,表现为状态灯闪烁。可以重新调整阈值,或在后续程序中加入多次采样、时间确认或迟滞处理。
1.2 代码部分
视频先烧录 3.1输入检测_实验演示 的 HEX 文件观察结果,然后由已有模板/工程建立 3.2输入检测 工程。51 单片机工程在当前工具中可以使用中文名,但为了兼容其他编译器、构建脚本和跨平台路径,后续工程仍建议使用简短英文名。
新建工程时可清理旧例程的业务代码和不再使用的延时函数,但应保留本实验需要的 LED 引脚定义。头文件也要按实际调用管理:上方演示源码的延时函数调用了 _nop_(),因此需要 <intrins.h>;视频中新建的 3.2 工程若暂时没有调用 _nop_(),可以先将该头文件注释掉。不能仅凭“暂时看起来没用”删除必需声明。
上方演示源码的处理顺序是:
循环扫描
├─ KEY1=0?→ 延时20 ms → 等待松手 → 延时20 ms → 翻转LED1
├─ KEY2=0?→ 延时20 ms → 等待松手 → 延时20 ms → 翻转LED2
└─ SUN=0? → LED3=1(灭)
否则 LED3=0(亮)
第一次 20 ms 延时过滤按下时的机械抖动,while (KEYx == 0); 等待松手,第二次 20 ms 延时过滤松手抖动,这样一次按压通常只翻转一次 LED。它的缺点是按键一直不松手时程序会阻塞在 while 中,其他任务暂时不能执行;这是入门实验中简单直观的写法,后续可改成非阻塞状态机。
1.2.1 写代码
本节在新工程中先保留 LED 定义,再根据电路图定义输入端。用 sbit 给位地址起名后,程序可以直接写 KEY1、KEY2、SUN,不必反复书写端口位。
需要区分“定义”和“方向配置”:经典 8051 的 P3 是准双向口,没有现代 MCU 常见的独立输入模式寄存器。要把它当输入读取,对应口线应处于写 1 的释放状态;芯片复位后的端口默认为 1,所以本例可以直接读取。若此前程序把该位写成 0,应先写 1 释放端口,否则外部按键无法把它读成正常高电平。
1.2.1.1 电路图按键定义
根据开发板原理图:
- 按键 1(原理图
SW3)连接P3.2,可定义为sbit KEY1 = P3^2;。P3.2还复用为外部中断INT0,本实验把它当普通 GPIO 使用。 - 按键 2(原理图
SW4)连接P3.3,可定义为sbit KEY2 = P3^3;。P3.3还复用为外部中断INT1,本实验同样把它当普通 GPIO 使用。 - 光敏模块 DO 连接
P3.7,源码定义为sbit SUN = P3^7;。 - LED1~LED4 分别连接
P1.0~P1.3,并且都是低电平点亮。
电路图中的按键支路可以展开为:
STC89C52
┌────────────┐
内部弱上拉 ───┤ P3.2/INT0 ├──── SW3 ──── GND
内部弱上拉 ───┤ P3.3/INT1 ├──── SW4 ──── GND
└────────────┘
松开:开关断开,端口由弱上拉保持为 1
按下:开关闭合,端口经开关接 GND,读到 0
这里没有把 +5V 直接通过按键送入端口,也没有在图上另画外部上拉电阻;高电平主要来自 P3 口内部的弱上拉结构。按键闭合时,电流从弱上拉经按键流向 GND,端口节点随之变为低电平。
1.2.1.2 按键结构讲解
常见四脚轻触按键内部有弹性的金属弹片。按下时弹片形变并跨接两组触点,松手后弹片恢复,因此会产生回弹力和“咔嗒”声。
四只引脚并不是四个独立触点,而是分成两个内部常通组。以开发板原理图 SW3/SW4 的编号为例:
未按下:
P3.x ─── 1 ═════ 2 1 与 2 永久相通
· · 两组之间断开
GND ─── 3 ═════ 4 3 与 4 永久相通
按下后:
P3.x ─── 1 ═════ 2
│ │ 弹片把上下两组接通
GND ─── 3 ═════ 4 四个脚处于同一电位
因此,按照这张开发板原理图采用的编号方式,1、2 同属 P3.x 网络,3、4 同属 GND 网络;按下按键时,两组网络才会接通。引脚的常通组合必须与当前器件采用的编号方式对应,不能混用不同封装图的编号。
另一种常见实物示意图会把同一侧两脚编号为 1、3,另一侧编号为 2、4,因此对应关系是 1、3 常通、2、4 常通。两种图描述的是同一种内部结构,只是编号与观察方向不同。设计 PCB 或接线时不能只背编号,应看当前器件的数据手册和封装图。
最可靠的确认方法是断电后用万用表蜂鸣档实测:
- 任取两脚,未按按键时若持续蜂鸣,它们属于同一常通组。
- 找到另一对未按时也持续蜂鸣的引脚,它们属于另一组。
- 跨两组测量,未按时应断开,按下后才蜂鸣。
- 测量必须在断电状态进行,避免板上其他电路形成并联通路而误判。
机械触点闭合和断开的一瞬间会反复弹跳数毫秒,单片机速度远高于机械动作,可能把一次按压读成多次。配套程序在按下和松手后各延时约 20 ms,就是软件消抖;对于需要同时处理其他任务的程序,应使用定时扫描和状态机,而不是一直阻塞等待松手。