输入输出复盘

Tutorial: 电子电路 Category: 嵌入式 Published: 2026-08-29 08:09:36 Views: 0 Likes: 0 Comments: 0

036.3.4 输入输出复盘

#include "reg52.h"
#include <intrins.h>

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;

typedef unsigned char uchar;
typedef unsigned int uint;

//void Delay20ms(void) // @11.0592MHz
//{
//    unsigned char data i, j;

//    i = 36;
//    j = 217;
//    do
//    {
//        while (--j);
//    } while (--i);
//}

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)
    {
        //Delay_xms(1000);
        if (0 == KEY1)
        {
            Delay_xms(20);
            while (0 == KEY1); // while(1);松手检查
            Delay_xms(20);
            LED1 = ~LED1;
        }

        if (0 == KEY2)
        {
            Delay_xms(20);
            while (0 == KEY2); // while(1);松手检查
            Delay_xms(20);
            LED2 = ~LED2;
        }

        if (SUN == 0)
        {
            LED3 = 1;
        }
        else
        {
            LED3 = 0;
        }
    }
}

一、输入输出复盘

本节从开发板的上电现象出发,结合 STC89C52 数据手册、开发板原理图和程序,复盘 51 单片机的输入、输出、准双向口以及 P0 开漏结构。理解这些内容时,始终要分清三个概念:

  1. 端口锁存器中的数据:程序写入端口寄存器的 01
  2. 引脚的驱动方式:芯片内部是主动拉高、弱上拉、主动拉低,还是高阻释放。
  3. 引脚上实际测得的电压:由芯片驱动、电源、上拉电阻和外接器件共同决定。

端口寄存器复位为 1,不一定意味着引脚一定由芯片主动输出高电平。P1~P4 在准双向口模式下可以凭内部上拉呈现高电平;P0 复位后则是开漏状态,锁存器虽然为 1,物理引脚却处于高阻释放状态,必须依靠外部上拉才能获得确定的高电平。

1.1 思维导图任务资源导图

STC89C52 上电或复位后,P1、P2、P3、P4 的端口锁存器默认为全 1,并处于准双向口/弱上拉模式。在本开发板上,可以通过三种途径验证端口状态:

  • 查数据手册中的特殊功能寄存器复位值和 I/O 口结构说明。
  • 用万用表直流电压挡测量引脚与 GND 之间的电压。
  • 根据原理图分析 LED、蜂鸣器等负载的电流路径,再观察上电现象。

开发板的四个用户 LED 均为低电平有效。以 LED1 为例,实际连接关系是:

+5V ──► LED1 ──► R8(10 kΩ)──► P1.0
                                     │
                          P1.0 内部下拉晶体管
                                     │
                                    GND
  • P1.0 = 0:端口把电流吸入 GND,形成 +5V → LED1 → R8 → P1.0 → GND 的闭合回路,LED 点亮。
  • P1.0 = 1:P1.0 接近 +5V,LED 两端电压很小,几乎没有电流,LED 熄灭。
  • 上电复位时 P1.0 的锁存器为 1,因此 LED 默认不亮。

这里的“低电平点亮”不是 LED 自身规定的逻辑,而是由 LED 的接法决定的。如果把 LED 和电阻改接在端口与 GND 之间,就可能变成高电平点亮。

蜂鸣器使用 S9012 PNP 三极管 Q1 作高端开关,P1.6 通过 R3(1 kΩ)控制三极管基极:

                         +5V
                          │
                     E ┌──┴──┐
P1.6 ── R3(1 kΩ)── B │ Q1  │ S9012(PNP)
                       └──┬──┘ C
                          │
                       BELL1
                          │
                         GND

工作过程如下:

  • P1.6 输出高电平时,基极与发射极都接近 +5V,PNP 管的发射结没有得到足够的正向偏置,Q1 截止,蜂鸣器无电流,因此不响。
  • P1.6 输出低电平时,电流经 +5V → Q1 发射极 → 基极 → R3 → P1.6 → GND 流动,Q1 导通;负载电流再经 +5V → Q1 发射极—集电极 → 蜂鸣器 → GND 流动,蜂鸣器发声。
  • R3 用于限制基极电流。C6(100 nF)跨接在 +5V 与 GND 之间,主要用于电源去耦,并不是蜂鸣器的串联限流元件。

“PNP 低电平导通、NPN 高电平导通”只适合描述这类常见开关接法,不能脱离电路直接套用。三极管是否导通,根本上取决于基极—发射极是否获得正确方向的电压和足够的基极电流:

  • PNP 常用于高端开关,发射极接正电源,基极电位比发射极低时导通。
  • NPN 常用于低端开关,发射极接 GND,基极电位比发射极高时导通。

本开发板利用“P1.6 复位为高电平、PNP 高端开关低电平有效”这一组合,使蜂鸣器在单片机刚上电、程序尚未初始化时保持关闭,避免上电误响。这也是硬件设计中常见的安全默认状态思路:先确定 MCU 复位期间的端口状态,再选择外设的有效电平和驱动拓扑。

1.2 51单片机数据手册

观察现象只能说明“当前这块板子表现为何如此”,数据手册才能说明芯片自身保证了什么。查阅 STC89C52 数据手册时,应重点关注两处:

  • 特殊功能寄存器表:确定端口寄存器的地址、位定义和复位值。
  • I/O 口结构章节:确定端口采用准双向、开漏还是高阻输入,以及外部是否需要上拉。

电路图负责回答“板上怎样连接”,程序负责回答“软件写入或读取了什么”,数据手册负责回答“芯片内部怎样执行”。三者结合,才能准确判断引脚电平。

1.2.1 目录

在数据手册目录中找到“特殊功能寄存器”或“I/O 口结构”章节。特殊功能寄存器表通常包含以下信息:

字段含义示例
符号寄存器名称P0P1P2P3
地址寄存器在 SFR 空间中的地址P0 = 80HP1 = 90HP2 = A0HP3 = B0H
位地址及符号每一位对应的引脚P1.7P1.0
复位值上电或复位后锁存器中的初值1111 1111B,即 0xFF

P0、P1、P2、P3 的复位值都是 1111 1111B,P4 已实现的低四位复位后也是 1。这个表描述的是端口锁存器的复位数据,还要结合端口结构判断物理电压:

端口复位后的锁存器复位工作方式引脚电压结论
P1~P41准双向口/弱上拉无外部强下拉时通常为高电平
P01开漏、高阻释放芯片自身不能把它主动拉高;有外部上拉才是确定的高电平

本开发板给 P0.0~P0.7 全部配置了 10 kΩ 外部上拉,所以板上 P0 引脚复位后也能测得接近 +5V。若把同一芯片放到没有 P0 上拉电阻的最小系统中,P0 锁存器仍然是 0xFF,但引脚可能悬空,不能把它当作可靠的逻辑高电平。

1.2.2 特殊功能寄存器

判断一个 I/O 引脚的状态可以采用“手册—电路—测量”三步法:

  1. 从手册确定端口的复位值和内部驱动结构。
  2. 从原理图确认外部上拉、下拉、按键、LED、三极管和传感器的接法。
  3. 用万用表或示波器测量实际电压,验证分析结果。

还要区分“输出状态”和“输入状态”:

  • 输出状态描述 MCU 如何驱动引脚,例如拉低、弱拉高或释放为高阻。
  • 输入状态描述 MCU 输入缓冲器此刻读到的是 0 还是 1

同一个准双向口可以先写 1 释放引脚,再由外部按键把它拉低,此时锁存器中仍是 1,输入缓冲器却读到 0。这不是矛盾,而是锁存数据与引脚实际电平属于两个不同层次。

1.2.2.1 按键的电平与状态

本开发板的两个独立按键连接如下:

P3.2 / KEY1 ── SW3 ── GND
P3.3 / KEY2 ── SW4 ── GND

P3.2、P3.3 复位后处于准双向口状态,内部弱上拉使松开时的引脚保持高电平。按下按键后,开关闭合,把引脚直接接到 GND,因此:

按键状态引脚电压程序读取值
松开接近 +5V1
按下接近 0V0

所以代码用 if (0 == KEY1)if (0 == KEY2) 判断按键按下。准双向口作为输入前,应先向对应锁存位写 1,相当于释放引脚,让外部电路决定电平。复位后 P3 锁存器本来就是 0xFF,本例没有提前把 P3.2、P3.3 写成 0,所以能够直接读取按键。

完整的按键处理过程是“检测按下 → 延时 20 ms 消抖 → 等待松手 → 再延时 20 ms 消抖 → 翻转 LED”。while (0 == KEY1); 末尾的分号表示循环体为空,它会停在这里反复读取 KEY1,直到按键松开。这是有意的空循环,不应误删。

1.2.2.2 准双向口输出配置

准双向口不需要像许多 32 位单片机那样,在每次输入、输出切换时重新配置方向,但它并不是“任何情况下都不用处理”。正确用法是:

  • 0:内部下拉晶体管强力导通,引脚主动输出低电平,具有较强的灌电流能力。
  • 1:下拉晶体管截止,引脚由内部上拉维持高电平,同时允许外部电路把它拉低。
  • 读取外部信号前:必须先把端口锁存器写成 1;若锁存器仍为 0,内部下拉一直导通,外部信号无法正常改变引脚电平,还可能产生电流冲突。

P1~P4 的准双向口内部包含三种上拉通路,其职责不同:

                           VCC
                  ┌────────┼────────┐
               强上拉    极弱上拉    弱上拉
                  └────────┼────────┘
                           ●────── 端口引脚 ───► 输入缓冲器
                           │
                       下拉晶体管
                           │
                          GND
  • 强上拉:端口由 0 跳变到 1 时短暂开启,约持续两个 CPU 时钟,加快引脚及外部电容的充电速度。
  • 弱上拉:锁存器为 1 且引脚本身为高电平时提供基本的上拉电流。
  • 极弱上拉:在引脚被外部装置拉低时仍保持极小的上拉作用,使外部装置能够轻松覆盖内部高电平。
  • 下拉晶体管:写 0 时导通,把引脚有力地拉到 GND。

因此,准双向口的高、低驱动能力并不对称:输出低电平较强,持续输出高电平依靠的是较弱的内部上拉。这也解释了为什么它既能输出高电平,又能在锁存器为 1 时被按键可靠拉低。

1.2.2.3 32单片机IO口输入输出原理

以 STM32 为代表的 32 位单片机通常要先初始化 GPIO,再使用引脚。课程中常见的输入、输出分类如下:

类别模式作用
输入浮空输入不启用内部上拉、下拉,电平完全由外部电路决定;输入端不能悬空
输入上拉输入内部电阻把空闲电平拉高,外部信号可将其拉低
输入下拉输入内部电阻把空闲电平拉低,外部信号可将其拉高
输入模拟输入关闭数字输入路径,供 ADC 等模拟外设使用
输出推挽输出上管和下管都能主动驱动,可主动输出高、低电平
输出开漏输出只能主动拉低;输出高电平要靠上拉电阻
复用复用推挽引脚由串口、定时器等片上外设控制,并采用推挽驱动
复用复用开漏引脚由片上外设控制,并采用开漏驱动,常用于共享总线

不同 STM32 系列的寄存器组织可能把“输入/输出/复用模式”“上拉/下拉”和“推挽/开漏”拆成多个配置项,但电气原理不变。

51 单片机使用起来更直接:P1~P4 复位后已经是准双向口,可通过写 0、写 1和读引脚完成常用输入输出。P0 是例外,它复位后为开漏结构,作为普通 GPIO 时需要外部上拉,不能笼统地说 P0、P1、P2、P3 的电气特性完全相同。

1.2.2.4 I/O口各种不同的工作模式及配置介绍
  • 1.2.2.4.1 I/O口配置

    • STC89C52 的 P1、P2、P3、P4 在上电复位后为准双向口/弱上拉模式。
    • 该系列 I/O 口还支持仅输入(高阻)和开漏输出等工作类型,具体端口及配置能力应以所用芯片型号的数据手册为准。
    • 准双向口写 1 后既能呈现内部弱上拉的高电平,也能读取外部装置拉低后的状态,所以适合按键等低电平有效输入。
  • 1.2.2.4.2 开漏输出配置

    • P0 上电复位后处于开漏模式,它和 P1~P4 的复位结构不同。
    • P0 作为普通 GPIO 时,需要为每个使用的引脚配置 4.7 kΩ~10 kΩ 外部上拉电阻。
    • P0 作为外部地址/数据复用总线时,芯片内部的总线驱动电路会按总线时序驱动引脚,不需要再按照普通 GPIO 的方法外加上拉。

不能只依据端口名称选择用法。使用任何开发板前,都应同时查看芯片型号、数据手册和板级原理图,因为不同 8051 兼容芯片的端口模式、驱动能力和配置寄存器可能不同。

1.2.2.5 IO口的输入和输出

从外部电路的角度看,输入和输出的方向如下:

输入:按键/传感器 ──电压信号──► MCU 引脚 ──► 输入缓冲器 ──► 程序读取
输出:程序写端口 ──► 输出驱动器 ──电流──► LED/三极管/其他负载

本节程序中的对应关系为:

引脚方向外设有效电平程序作用
P3.2输入SW3按下为 0控制 LED1 翻转
P3.3输入SW4按下为 0控制 LED2 翻转
P3.7输入光敏模块 DO由模块比较器决定控制 LED3 状态
P1.0输出LED1点亮为 0记录 KEY1 的翻转状态
P1.1输出LED2点亮为 0记录 KEY2 的翻转状态
P1.2输出LED3点亮为 0显示光敏模块状态

读取准双向口时要留意“读引脚”和“读锁存器”的差别。像 if (KEY1 == 0) 这样的直接位测试用于读取引脚电平;部分 8051 读—改—写指令为了正确修改端口的某几位,会读取端口锁存器。遇到端口状态与预期不一致时,应结合编译器生成的指令和芯片手册分析。

1.2.2.6 51单片机I/O口配置

P0 作为普通 GPIO 时,外部上拉的基本连接如下:

          +5V
           │
      4.7 kΩ~10 kΩ
           │
P0.x ──────●────► 外部电路/输入缓冲器
           │
      P0 内部开漏管
           │
          GND

上拉电阻有两个作用:

  1. P0 内部开漏管关闭时,把引脚拉到确定的高电平。
  2. P0 内部开漏管导通时限制从 +5V 流向 GND 的电流,避免电源短路。

以 5V 电源估算,P0 输出低电平时的上拉电流约为:

R = 10 kΩ:I ≈ 5 V / 10 kΩ = 0.5 mA
R = 4.7 kΩ:I ≈ 5 V / 4.7 kΩ = 1.06 mA

较小的电阻上拉更强、上升沿更快、抗干扰能力通常更好,但输出低电平时耗电更大;较大的电阻耗电较小,但与引脚和线路电容构成的 RC 充电时间更长,也更容易受到漏电和干扰影响。4.7 kΩ~10 kΩ 是该数据手册给出的常用范围,实际还要结合供电电压、总线速度、负载电容和外设输入电流选择。

1.2.2.7 51单片机开漏输出配置

“开漏”可以理解为输出端只保留一个通向 GND 的受控开关,没有负责连接 VCC 的上管:

                        +VCC
                          │
                    外部上拉电阻
                          │
端口引脚 P0.x ────────────●────► 输出/输入
                          │
                       ┌──┴──┐
锁存器 ──控制信号──────►│ NMOS │
                       └──┬──┘
                          │
                         GND
写入锁存器内部 NMOS无外部上拉时的引脚有外部上拉时的引脚
0导通低电平低电平
1截止高阻、悬空高电平

因此,“开漏输出高电平”实际上是芯片关闭下拉管、释放总线,再由外部电阻把线路拉高;芯片并没有主动向外提供高电平电流。这个结构允许多个器件共用一根信号线:任一器件拉低,线路就是低电平;只有所有器件都释放时,线路才由上拉电阻变高。I²C 等总线正是利用了这一特点。

P0 在普通 GPIO 模式下符合上述规律。P0 用作 8051 外部存储器的地址/数据复用总线时会启用专用总线驱动,不能把普通 GPIO 的开漏等效图机械套到总线周期中。

1.2.2.8 51单片机准双向口输出配置

P1~P4 的准双向口内部具有上拉通路,写 1 后无需外接电阻便可形成高电平;P0 的普通 GPIO 开漏结构没有这一通路。开发板使用 RN1、RN2 两个 4 联 10 kΩ 排阻,为八个 P0 引脚分别提供上拉:

                         +5V(两个排阻的公共端)
                          │
       ┌──────────────────┼──────────────────┐
       │                  │                  │
   RN1:4 × 10 kΩ                         RN2:4 × 10 kΩ
       │  │  │  │                         │  │  │  │
      P0.0 P0.1 P0.2 P0.3                 P0.4 P0.5 P0.6 P0.7

排阻并不是“一个 10 kΩ 电阻同时串在八个引脚上”,而是在一个封装中集成多只电阻。本板的 RN1、RN2 每组各含四只 10 kΩ 电阻,每只电阻的一端接 +5V,另一端分别接一个 P0 引脚。八根端口线彼此没有短接,只共享 +5V 公共端。

以 P0.0 为例:

  • 0 时,P0.0 内部开漏管导通,电流经 +5V → RN1 中的一只 10 kΩ 电阻 → P0.0 → GND 流动,引脚为低电平。
  • 1 时,内部开漏管截止,RN1 把 P0.0 拉到 +5V,引脚为高电平。
  • 外部设备也可以把已释放的 P0.0 拉低,MCU 的输入缓冲器便能读到 0
1.2.2.9 51单片机淘宝搜索及开发板介绍

选购或分析 51 单片机开发板时,不要只根据元件外形判断功能。P0 附近的黑色长条器件常常是排阻,但也可能采用贴片电阻阵列或八只分立电阻。最可靠的方法是查看商品原理图、PCB 丝印和元件标号,确认每个 P0 引脚是否都通过电阻连接到 VCC。

开发板可用于电子秤、环境检测、声光报警、数码管显示等课程设计。学习时可以先参考成熟项目的功能拆分,再独立完成“输入采集—逻辑判断—输出控制—故障排查”的全过程。模仿的是系统设计思路,而不是仅复制一份无法解释的代码。

1.2.2.10 思维导图内容回顾

输入输出部分可以归纳为三个关键问题:

  1. 51 单片机上电后端口寄存器是什么值,物理引脚又是什么电平?
  2. 什么叫输出状态、什么叫输入状态,准双向口为什么能兼顾两者?
  3. P0 与 P1~P4 是否完全相同,P0 作普通 GPIO 时为什么要外接上拉?

对应本开发板的核心结论如下:

P1~P4:复位锁存器 = 1
        准双向口/内部弱上拉
        无外部强拉低时,引脚通常为高电平

P0:    复位锁存器 = 1
        普通 GPIO 为开漏、高阻释放
        RN1 + RN2 共八只 10 kΩ 电阻连接 +5V,产生可靠高电平

读懂端口电路时,应沿着电流路径依次寻找 VCC → 上拉或负载 → 引脚 → 下拉开关 → GND。能找到完整回路,才能判断器件是否工作;找不到主动上拉路径时,就要检查是否需要外部上拉电阻。

1.2.2.11 常见问题总结

常见问题可以按“编译错误、逻辑错误、引脚不一致、硬件异常”四类排查:

现象常见原因检查方法
P1 或变量未定义大小写错误;变量未声明C 语言区分大小写,核对 P1KEY1 等符号
找不到头文件缺少 reg52.h;文件名或包含路径错误检查 #include "reg52.h"、芯片包和工程包含路径
分号附近报语法错误输入了中文 ,或上一行括号未闭合切换英文输入法并检查报错行前后几行
大量连锁语法错误圆括号、花括号数量不匹配使用编辑器括号配对功能,从第一个错误开始处理
判断条件总是不对== 写成 =写成 if (0 == KEY1),误写 0 = KEY1 时编译器会直接报错
光敏检测无反应程序定义 P3.7,实际信号接到了 P3.6;DO 极性理解相反同时核对 sbit SUN = P3^7;、模块接线和电平测量
程序下载成功但外设不动作HEX 不是当前工程生成;芯片型号或时钟设置不对;负载接法与有效电平相反清理并重新编译,确认下载文件、芯片配置和原理图
P0 高电平不可靠缺少外部上拉;上拉阻值或连接错误测量 P0 电压,检查 RN1、RN2 或分立上拉电阻

建议按以下顺序定位问题:

  1. 先处理编译器报告的第一个错误,因为后面的错误可能只是连锁反应。
  2. 确认当前工程确实生成了最新 HEX,并下载到正确型号的芯片。
  3. 对照原理图逐一核对端口号、VCC、GND、输入输出方向和有效电平。
  4. 用万用表测量静态电压,用示波器或逻辑分析仪观察快速变化信号。
  5. 使用已验证的最小测试程序分别测试输入与输出,以缩小故障范围。

一份已验证的程序只能帮助缩小排查范围,不能单独证明硬件一定正常;同样,看到正确电压也不代表程序逻辑一定正确。遇到头文件、函数或寄存器问题时,应优先查编译器诊断、芯片手册和官方工具文档,再用搜索结果补充案例。

1.2.2.12 变量取值范围

在常见的 8051 C 编译环境中,char 通常为 8 位,int 通常为 16 位。是否有符号会直接决定可表示的范围:

类型常见位宽典型取值范围
signed char8 位-128~127
unsigned char8 位0~255
signed int16 位-32768~32767
unsigned int16 位0~65535

本程序定义:

typedef unsigned char uchar;
typedef unsigned int uint;

因此 uchar 可表示 0~255,适合保存延时函数内部的短计数值;uint 可表示 0~65535,用于 Delay_xms() 的毫秒参数。char 是 8 位整数类型,字符只是其中一种解释方式,它当然可以参与数值计算,并不是只能保存字母。

例如字符 'A' 在兼容 ASCII 的执行字符集中对应一个整数编码,程序既可以把它作为字符显示,也可以把该编码作为数值参与运算。嵌入式开发中还应确认具体编译器的数据模型,不能把 PC 编译器上 int 的常见位宽直接套到 8051 工程中。

1.2.2.13 烟雾传感器产品展示

许多 MQ 系列气体传感器模块、光敏模块和红外检测模块都提供四类常见引脚:

引脚含义51 单片机连接方式
VCC模块电源接符合模块规格的电源,不能只凭接口外形判断电压
GND公共地必须与 MCU 共地
DO数字输出接普通数字 I/O,读取比较器输出的 01
AO模拟输出接 ADC;STC89C52RC 本身没有通用 ADC,需要外接 ADC 或改用 DO

DO 通常来自模块上的比较器,电位器用于设置阈值。被测量超过阈值后 DO 到底输出高还是低,取决于模块电路,必须查看资料或实测,不能假定所有模块极性相同。AO 则反映传感器模拟电压,可用于观察连续变化,但要经过 ADC 才能被纯数字型 51 单片机量化读取。

常见 MQ 系列的大致用途如下:

型号常见检测方向
MQ-2可燃气体与烟雾的综合检测
MQ-3酒精蒸气检测
MQ-4甲烷、天然气检测
MQ-5液化气、天然气等可燃气体检测
MQ-6液化气、丙烷等检测
MQ-135空气质量及多种气体的趋势检测

MQ 系列并不是“插上就能精确测浓度”的通用数字传感器。其敏感元件带有加热器,需要预热;要得到可靠的浓度结果,还要根据具体型号的数据手册、负载电阻、温湿度影响和基准环境进行标定。教学中可以先用 DO 实现超阈值报警,再学习用 AO、ADC 和标定曲线估算变化趋势。

其他常见传感器也不能仅因接口类似就直接互换:

  • 电阻应变式称重传感器输出的是很小的差分电压,通常要经过 HX711 等高精度放大与 ADC 芯片,不能直接接普通 51 I/O 口。
  • 红外避障模块通常利用发射管、接收器和比较器检测反射光,输出有效电平与阈值由模块电路决定;它适合判断一定范围内“有/无障碍”,并不一定能给出准确距离。
  • 不同气体传感器可能具有相似的 VCC、GND、DO、AO 排针,但敏感材料、工作电压、加热要求、检测范围和标定方法都不同。

学习新模块时,可以沿用同一套分析顺序:先确认供电和共地,再判断 DO/AO 的信号类型与有效电平,最后对照 MCU 是否具备相应数字输入或 ADC 能力。这样既能快速上手,也能避免因接口外形相同而接错电路。