一、GPIO --- 通用输入输出
GPIO(General Purpose Input Output)是单片机与外部世界交互的基本通道,每个引脚均可配置为输入或输出模式。
1.1 GPIO 输入模式(检测引脚电平变化)
|----------|--------------------------|
| 输入模式 | 说明 |
| 悬空输入 | 引脚未连接任何上下拉,电平不确定,容易受干扰 |
| 上拉输入 | 通过电阻连接到VCC,默认高电平;按键按下时拉低 |
| 下拉输入 | 通过电阻连接到GND,默认低电平 |
| 模拟输入 | 用于ADC采样,读取模拟电压值 |
1.2 GPIO 输出模式(给定引脚高电平/低电平,控制引脚输出高电平/低电平)
|----------|------------------|
| 输出模式 | 说明 |
| 推挽输出 | 可主动输出高/低电平,驱动能力强 |
| 开漏输出 | 只能拉低,高电平需外部上拉电阻 |
1.3 检测引脚电平 --- 实例
检测 P3_2 是否为低电平(按键是否按下):
bash
// 读取P3第2位
if ((P3 & (1 << 2)) == 0)
{
// P3_2 为低电平
}
// 位运算示意:
// P3 = 1111 1011
// (1<<2) = 0000 0100
// 结果 = 0000 0000 → 等于0,说明该位为低
|--------|-----------------------------------------------------------|
| 💡 | 51单片机的P3口引脚默认为高电平(上拉输入)。检测按键时,按键一端接GND,另一端接P3引脚,按下时引脚被拉低。 |
二、中断系统
中断是单片机实时响应外部事件的核心机制,避免了CPU不断轮询的资源浪费。
2.1 核心概念
|--------|-----------------------------------|
| 概念 | 解释 |
| 中断 | CPU暂停当前任务,转去处理更紧急的事件,处理完后回到原处继续执行 |
| 中断源 | 能够触发中断的事件或硬件信号 |
| 中断优先级 | 多个中断同时请求时,优先处理级别高的 |
| 中断嵌套 | 处理一个中断时,被更高优先级中断再次打断(51最多嵌套2层) |
| 中断向量表 | 存放各中断服务函数入口地址的数组 |
| 中断向量 | 中断服务函数在向量表中的编号标识 |
2.2 51单片机的5个中断源
|---------------------|---------------------|
| 中断源 | 中断号 interrupt N |
| 外部中断 0(INT0,P3.2引脚) | interrupt 0 |
| 定时器 0 溢出中断 | interrupt 1 |
| 外部中断 1(INT1,P3.3引脚) | interrupt 2 |
| 定时器 1 溢出中断 | interrupt 3 |
| 串口中断(UART) | interrupt 4 |
2.3 中断处理流程
-
中断源发起中断请求
-
CPU检查中断总开关(EA)及子开关是否打开
-
比较中断优先级,决定是否响应
-
保护现场(自动压栈保存PC、PSW等寄存器)
-
跳转并执行中断服务函数(ISR)
-
恢复现场,返回被打断处继续执行
图1 中断处理流程
2.4 外部中断0 --- 代码实现
bash
// 外部中断0 初始化
void int0_init(void)
{
P3 |= (1 << 2); // P3.2 拉高(上拉输入,等待下降沿)
IE |= (1 << 7); // EA=1 打开CPU中断总开关
IE |= (1 << 0); // EX0=1 打开外部中断0子开关
TCON |= (1 << 0); // IT0=1 设置为下降沿触发
}
// 外部中断0 服务函数(下降沿触发一次,g_n自增)
void int0_handler(void) interrupt 0
{
g_n++;
}
|---------------|---------------------------------------------------------------------|
| ⚠️ 注意 | 中断服务函数不能有参数和返回值,函数名后必须加 interrupt N(N为中断号)。TCON寄存器的IE0位由硬件在响应后自动清零。 |
三、定时器
定时器用于产生精准时间间隔,对PWM、通信时序等时间敏感任务至关重要。
3.1 定时器原理
- 51单片机有 Timer0 和 Timer1,均为 16位自增计数器(0 ~ 65535)
- 从初值开始向上计数,溢出(超过65535归0)后向CPU发起中断请求
- 计数速率 = 晶振频率 ÷ 12(机器周期)
3.2 定时器计算公式(晶振 11.0592MHz)
|----------|------------------|----------------|
| 工作频率 | 11.0592 MHz ÷ 12 | 0.9216 MHz |
|------------|----------------|----------------|
| 每次计数时间 | 1 ÷ 0.9216 MHz | ≈ 1.085 μs |
|--------------------|------------------|-----------------|
| 1ms 定时计数次数 | 1000μs ÷ 1.085μs | ≈ 922 次 |
|------------------|-------------|-----------|
| 1ms 定时初值 | 65535 - 922 | 64613 |
例1:

3.3 关键寄存器
|-----------------|--------------------------|
| 寄存器 / 位 | 作用 |
| TH0 / TL0 | 定时器0高8位/低8位初值,溢出后需重新装载 |
| TMOD bit0 | 设置Timer0工作模式(1 = 16位定时器) |
| TCON TR0 (bit4) | 置1启动Timer0开始计数 |
| IE EA (bit7) | CPU中断总开关,必须置1才能响应任何中断 |
| IE ET0 (bit1) | 允许Timer0产生中断 |
3.4 工作模式2 --- 8位自动重装
- 实际计数只用 TL0(0~255),溢出后自动把 TH0 的值重新装入 TL0
- 优点:无需在中断服务函数中手动重装初值,精度更高
3.5 定时器0 初始化代码
bash
void timer0_init(void)
{
TMOD &= ~(0x0F << 0); // 清除低4位
TMOD |= (1 << 0); // 设置16位定时器模式(M0=1)
TH0 = g_i >> 8; // 装入初值高8位
TL0 = g_i; // 装入初值低8位
TCON |= (1 << 4); // TR0=1 启动定时器
IE |= (1 << 7); // EA=1 总中断开关
IE |= (1 << 1); // ET0=1 允许Timer0中断
}
// 定时器0 中断服务函数
void timer0_handler(void) interrupt 1
{
TH0 = g_i >> 8; // 重装初值(防止误差累积)
TL0 = g_i;
P2 ^= (1 << 5); // 翻转P2.5引脚(产生方波)
}
四、PWM --- 脉冲宽度调制
PWM(Pulse Width Modulation)通过控制方波的高/低电平时间比,实现对亮度、速度、音调等的调节。
|----------------|-----------------------------|
| PWM 参数 | 定义 |
| PWM 周期 | 一个完整方波所经历的时间(从一个上升沿到下一个上升沿) |
| PWM 占空比 | 高电平时间 ÷ 周期 × 100% |
| 频率 | 1 ÷ 周期,单位 Hz |

|---------------|----------------------------------------------------------------------|
| 🔑 关键 | 占空比决定平均电压。LED: 占空比50% → 半亮;电机: 占空比越大转速越高。51单片机通过定时器中断翻转引脚电平来软件模拟PWM。 |
五、蜂鸣器
5.1 两种蜂鸣器对比
|--------|---------------------------|
| 类型 | 特点 |
| 有源蜂鸣器 | 内置震荡源,通电即持续发出固定频率声音,控制简单 |
| 无源蜂鸣器 | 无内置震荡源,需外部提供PWM方波驱动,可改变音调 |
5.2 蜂鸣器音调计算(200Hz 为例)
|--------|-----------|------------------|
| 周期 | 1 ÷ 200Hz | 0.005s = 5ms |
|---------------|---------|---------------------|
| 半周期(定时时间) | 5ms ÷ 2 | 0.0025s = 2.5ms |
|----------|-----------------|------------------|
| 计数次数 | 2.5ms ÷ 1.085μs | ≈ 2304 次 |
|-----------|--------------|-----------|
| 定时器初值 | 65535 - 2304 | 63231 |
5.3 常用频率初值对照
|-------------|----------------|
| 频率 (Hz) | 定时器初值(宏定义) |
| 200 Hz | HZ_200 = 63231 |
| 400 Hz | HZ_400 = 64382 |
| 600 Hz | HZ_600 = 64767 |
| 800 Hz | HZ_800 = 64959 |
六、综合示例:按键控制蜂鸣器音调
本示例通过4个按键选择不同频率,驱动无源蜂鸣器发出不同音调。
6.1 按键检测 --- key.c
bash
void key_init(void)
{
P3 |= (0x0F << 0); // P3.0~P3.3 设为高电平(上拉输入)
}
int key_press(void)
{
if ((P3 & (1 << 1)) == 0) return 1; // KEY1: P3.1低 → 按下
if ((P3 & (1 << 0)) == 0) return 2; // KEY2: P3.0低 → 按下
if ((P3 & (1 << 2)) == 0) return 3; // KEY3: P3.2低 → 按下
if ((P3 & (1 << 3)) == 0) return 4; // KEY4: P3.3低 → 按下
return 0;
}
6.2 主程序 --- main.c
bash
#define HZ_200 63231
#define HZ_400 64382
#define HZ_600 64767
#define HZ_800 64959
int main(void)
{
timer0_init();
key_init();
while (1)
{
int ret = key_press();
if (ret == 1) g_i = HZ_200; // 低音
else if (ret == 2) g_i = HZ_400;
else if (ret == 3) g_i = HZ_600;
else if (ret == 4) g_i = HZ_800; // 高音
else g_i = 0; // 无按键,停止发声
}
}
|----------|--------------------------------------------------------------------------------------|
| 工作原理 | g_i 是全局变量,在定时器中断中用于重装初值。g_i越大,定时时间越短,翻转越快,频率越高,音调越高。g_i=0时不重装初值,定时器停止产生规律方波,蜂鸣器停止发声。 |
七、LED 驱动 --- led.c 补充
bash
void led_all_on(void) { P2 = 0x00; } // 全亮(低电平有效)
void led_all_off(void) { P2 = 0xFF; } // 全灭
// 点亮第n个LED(其余熄灭)
void led_on(unsigned char n) { P2 = ~(1 << n); }
// 所有LED取反(全亮变全灭,全灭变全亮)
void led_nor(void) { P2 = P2 ^ 0xFF; }
八、关键寄存器速查表
|---------------|----------------------------------------------------------|
| 寄存器 | 功能描述 |
| P0 ~ P3 | 4组通用I/O口(8位),可读可写,操控外设引脚 |
| IE(中断允许) | bit7=EA总开关;bit0=EX0;bit1=ET0;bit2=EX1;bit3=ET1;bit4=ES |
| TCON(定时/中断控制) | TR0(bit4)=启动Timer0;TR1(bit6)=启动Timer1;IT0(bit0)=INT0触发方式 |
| TMOD(定时器工作模式) | 低4位控制Timer0,高4位控制Timer1;bit0置1=16位定时器模式 |
| TH0 / TL0 | Timer0高8位/低8位计数寄存器 |
| TH1 / TL1 | Timer1高8位/低8位计数寄存器 |
附录:
一,TCON --- 定时器/中断控制寄存器
地址:88H 可位寻址(每个bit可以单独用位地址操作)
作用:控制 Timer0 / Timer1 的启停,以及外部中断 INT0 / INT1 的触发方式和请求标志。
|---------|---------|---------|---------|---------|---------|---------|---------|
| B7 | B6 | B5 | B4 | B3 | B2 | B1 | B0 |
| TF1 | TR1 | TF0 | TR0 | IE1 | IT1 | IE0 | IT0 |
|--------|---------|-------------------------------------------------------------------------------------------------------|
| 位 | 名称 | 功能说明 |
| B7 TF1 | TF1 | Timer1 溢出标志位。Timer1 计数溢出时由硬件自动置1,CPU 响应中断后自动清0。也可软件清0。 |
| B6 TR1 | TR1 | Timer1 运行控制位。置1 → 启动 Timer1 开始计数;清0 → 停止 Timer1。 操作:TCON |= (1 << 6) 启动 / TCON &= ~(1 << 6) 停止 |
| B5 TF0 | TF0 | Timer0 溢出标志位。Timer0 计数溢出时由硬件自动置1,CPU 响应中断后自动清0。也可软件清0。 |
| B4 TR0 | TR0 | Timer0 运行控制位。置1 → 启动 Timer0 开始计数;清0 → 停止 Timer0。 操作:TCON |= (1 << 4) 启动 / TCON &= ~(1 << 4) 停止 |
| B3 IE1 | IE1 | 外部中断1(INT1,P3.3)请求标志。检测到触发信号后由硬件置1,CPU 响应后自动清0。 |
| B2 IT1 | IT1 | 外部中断1 触发方式。IT1=0:低电平触发;IT1=1:下降沿触发(推荐,不会误触发)。 操作:TCON |= (1 << 2) 设置下降沿触发 |
| B1 IE0 | IE0 | 外部中断0(INT0,P3.2)请求标志。检测到触发信号后由硬件置1,CPU 响应后自动清0。 |
| B0 IT0 | IT0 | 外部中断0 触发方式。IT0=0:低电平触发;IT0=1:下降沿触发(推荐)。 操作:TCON |= (1 << 0) 设置下降沿触发 |
|---------------|----------------------------------------------------------------------------------------|
| ⚠️ 注意 | TCON 可位寻址,也可用位运算直接操作整个字节。TF0/TF1/IE0/IE1 均由硬件自动置1、自动清0,不需要手动操作。TR0/TR1/IT0/IT1 需要手动配置。 |
二、TMOD --- 定时器工作模式寄存器
地址:89H 不可位寻址(只能整字节操作,不能单独访问某一位)
作用:设置 Timer0 / Timer1 的工作模式(16位/8位/自动重装等)。
|----------|---------|--------|--------|----------|---------|--------|--------|
| B7 | B6 | B5 | B4 | B3 | B2 | B1 | B0 |
| GATE | C/T | M1 | M0 | GATE | C/T | M1 | M0 |
← 高4位控制 Timer1 → ← 低4位控制 Timer0 →
|--------------|----------|-----------------------------------------------------------------------------------------------------|
| 位 | 名称 | 功能说明 |
| B7 / B3 GATE | GATE | 门控位。GATE=0(常用):只要 TR0/TR1 置1,定时器就启动。GATE=1(少用):需同时满足 TR0/TR1=1 且 INT0/INT1 引脚为高电平,定时器才启动(用于测量脉冲宽度)。 |
| B6 / B2 C/T | C/T | 功能选择位。C/T=0:定时器模式(对内部机器周期计数,用于定时)。C/T=1:计数器模式(对外部引脚T0/T1的脉冲计数,用于测量外部事件次数)。 |
| B5 / B1 M1 | M1 | 工作模式高位(与M0组合选择模式,见下表) |
| B4 / B0 M0 | M0 | 工作模式低位(与M1组合选择模式,见下表) |
M1 M0 工作模式选择
|--------|--------|----------------------|----------------------------------------------------------|
| M1 | M0 | 模式 | 说明 |
| 0 | 0 | 模式0 --- 13位定时器 | 由TH(高8位)+ TL(低5位)组成13位计数器,兼容老式8048,很少用 |
| 0 | 1 | 模式1 --- 16位定时器 ★ | TH(高8位)+ TL(低8位)组成16位计数器,范围0~65535,最常用!溢出后需手动重装初值 |
| 1 | 0 | 模式2 --- 8位自动重装 ★ | 只有TL(0~255)参与计数,溢出后自动把TH的值重新装入TL,无需中断里手动重装,适合波特率发生器/PWM |
| 1 | 1 | 模式3 --- 分裂为两个8位 | 仅Timer0支持:TL0独立作8位定时器,TH0也独立作8位定时器,较少使用 |
三、IE --- 中断允许寄存器(顺带整理)
地址:A8H 可位寻址
|--------|---------|---------|--------|---------|---------|---------|---------|
| B7 | B6 | B5 | B4 | B3 | B2 | B1 | B0 |
| EA | --- | ET2 | ES | ET1 | EX1 | ET0 | EX0 |
|--------|---------|-------------------------------------------------------------------------------|
| 位 | 名称 | 功能说明 |
| B7 EA | EA | 中断总开关。EA=1 → 允许CPU响应所有中断(还需各子开关打开);EA=0 → 关闭所有中断,无论子开关状态。操作:IE |= (1 << 7) |
| B6 --- | 保留 | 该位保留,无实际功能,读取值为0。 |
| B5 ET2 | ET2 | Timer2 中断允许位(STC89C52 有 Timer2)。ET2=1 允许 Timer2 中断。 |
| B4 ES | ES | 串口(UART)中断允许位。ES=1 允许串口中断。操作:IE |= (1 << 4) |
| B3 ET1 | ET1 | Timer1 中断允许位。ET1=1 允许 Timer1 溢出中断。操作:IE |= (1 << 3) |
| B2 EX1 | EX1 | 外部中断1(INT1,P3.3)允许位。EX1=1 允许外部中断1。操作:IE |= (1 << 2) |
| B1 ET0 | ET0 | Timer0 中断允许位。ET0=1 允许 Timer0 溢出中断。操作:IE |= (1 << 1) |
| B0 EX0 | EX0 | 外部中断0(INT0,P3.2)允许位。EX0=1 允许外部中断0。操作:IE |= (1 << 0) |
