当前位置: 首页 > news >正文

51单片机:中断、定时器与PWM整合手册

一、中断系统

中断系统是单片机实现实时处理的核心机制,用于响应内外紧急事件,核心包含定义、源、流程、嵌套、向量表及控制寄存器六大模块。

1. 中断与中断源
  • 中断:为使CPU具备对外界紧急事件的实时处理能力而设计的机制。当CPU执行当前程序时,被内外事件触发,暂停当前任务转去处理该事件(执行中断服务程序),处理完后返回原程序继续执行,类似“工作时接紧急电话”。
  • 中断源:触发中断的“事件来源”,即导致CPU暂停的具体原因。结合STC89C51RC/RD+系列,常见中断源如下:
    • 外部中断:INT0(P3.2)、INT1(P3.3)、INT2、INT3
    • 定时器中断:Timer0、Timer1、Timer2(溢出触发)
    • 串口中断:UART(接收/发送完成触发)
2. 中断处理流程

遵循“请求-判断-处理-恢复”逻辑,核心步骤共6步,需经过CPU内核的多层判断:

  1. 中断请求:中断源(如Timer0溢出、INT0按键)向CPU发送触发信号;
  2. 中断判断:CPU内核执行完当前指令后,先检查全局中断使能(EA)及该中断是否被屏蔽,
  3. 确认优先级:确认中断优先级(如高优先级中断优先响应);
  4. 保护现场:保存当前CPU寄存器(如累加器A、程序计数器PC)的值,避免后续处理覆盖原数据;
  5. 执行中断服务程序:通过中断向量表找到对应服务函数地址,跳转执行(如读取传感器、翻转IO口);
  6. 恢复现场:将保存的寄存器值还原,恢复CPU执行原程序的初始状态;
  7. 中断返回:跳回原程序被暂停的位置(恢复PC值),继续执行原任务。
3. 中断嵌套

高优先级中断可打断正在执行的低优先级中断服务程序,处理完高优先级中断后,返回继续执行低优先级中断,最终恢复原程序,本质是“优先级抢占”机制。

  • 示例:CPU正在处理“INT1按键中断”(低优先级),此时“Timer0溢出中断”(高优先级)触发,CPU会暂停INT1处理,优先执行Timer0服务程序,执行完后回到INT1中断,最后返回原程序。
  • 优先级控制:通过IP(中断优先级寄存器)、IPH(高优先级寄存器) 配置,STC89C51默认优先级从高到低为:INT0 > Timer0 > INT1 > Timer1 > UART > Timer2 > INT2 > INT3。
4. 中断向量表

存储“中断号(查询次序号)”与“中断服务程序入口地址”的指针数组,相当于CPU的“中断导航目录”,用于快速定位服务程序。

  • 51单片机C语言编程中,中断号直接对应interrupt关键字后的参数,具体映射如下:
    中断类型中断号服务函数示例
    外部中断0(INT0)0void Int0_Routine(void) interrupt 0;
    定时器0(Timer0)1void Timer0_Routine(void) interrupt 1;
    外部中断1(INT1)2void Int1_Routine(void) interrupt 2;
    定时器1(Timer1)3void Timer1_Routine(void) interrupt 3;
    串口(UART)4void UART_Routine(void) interrupt 4;
    定时器2(Timer2)5void Timer2_Routine(void) interrupt 5;
    外部中断2(INT2)6void Int2_Routine(void) interrupt 6;
    外部中断3(INT3)7void Int3_Routine(void) interrupt 7;
5. 中断控制寄存器

中断的使能、类型(电平/边沿触发)需通过专用寄存器配置,核心寄存器包括IE(中断允许寄存器)TCON(定时器/中断控制寄存器)

(1)IE寄存器(中断允许,地址:A8H,可位寻址)
位序号位名称功能描述
B7EA全局中断使能位:1=允许全局中断;0=禁止所有中断
B6-保留位,无实际功能
B5-保留位,无实际功能
B4ES串口中断使能位:1=允许;0=禁止
B3ET1Timer1中断使能位:1=允许;0=禁止
B2EX1INT1中断使能位:1=允许;0=禁止
B1ET0Timer0中断使能位:1=允许;0=禁止
B0EX0INT0中断使能位:1=允许;0=禁止
(2)TCON寄存器(定时/中断控制,地址:88H,可位寻址)
位序号位名称功能描述
B7TF1Timer1溢出标志:计数溢出时硬件置1,CPU响应中断后硬件清0(也可软件清0)
B6TR1Timer1运行控制位:1=启动Timer1;0=停止Timer1
B5TF0Timer0溢出标志:功能同TF1,对应Timer0
B4TR0Timer0运行控制位:功能同TR1,对应Timer0
B3IE1INT1请求标志:INT1触发时硬件置1,CPU响应后硬件清0
B2IT1INT1触发类型位:1=下降沿触发;0=低电平触发
B1IE0INT0请求标志:功能同IE1,对应INT0
B0IT0INT0触发类型位:功能同IT1,对应INT0
二、定时器系统

51单片机定时器本质是“可编程计数器”,基于机器周期(1机器周期=12晶振周期)实现定时或计数功能,核心包含工作原理、控制寄存器及实战代码。

1. 定时器工作原理

定时器核心是16位计数器(由高8位THx和低8位TLx组成) ,支持两种工作模式,需通过TMOD寄存器配置模式,TRx启动计数:

  • 定时模式:计数器对内部机器周期计数。从预设初值(THx/TLx)开始加1,计数到65535(0xFFFF)后溢出,触发TFx标志,向CPU请求中断(如定时1ms);
  • 计数模式:计数器对外部引脚脉冲计数。引脚(T0=P3.4、T1=P3.5)每检测到1个脉冲上升沿/下降沿,计数加1,溢出后触发中断(如统计按键次数)。
2. 核心控制寄存器

除上述TCON外,定时器模式由TMOD配置,初值由THx/TLx设置:

(1)TMOD寄存器(定时器模式,地址:89H,不可位寻址)
位序号位名称功能描述(高4位对应Timer1,低4位对应Timer0)
B7/B3GATE门控位:1=由INTx引脚和TRx共同控制启动;0=仅由TRx控制启动
B6/B2C/T模式选择位:1=计数模式(外部脉冲);0=定时模式(内部机器周期)
B5~B4/B1~B0M1~M0工作模式位:00=13位定时/计数;01=16位定时/计数;10=8位自动重装;11=双8位

(2)THx/TLx寄存器(定时器初值寄存器)
  • TH0(地址8CH)/TL0(地址8AH):Timer0的高8位/低8位初值寄存器;
  • TH1(地址8DH)/TL1(地址8BH):Timer1的高8位/低8位初值寄存器;
  • 初值计算:若定时$t$ ms,晶振频率$f_{\text{osc}}$(如11.0592MHz),则初值 = $65536 - \frac{t \times 10^{-3} \times f_{\text{osc}} \times 10^{6}}{12}$。
3. 实战代码示例
(1)定时器0控制P2口LED闪烁(定时500ms翻转)
(1)定时器0控制P2口LED闪烁(定时500ms翻转)#include <reg52.h>// 定时器0初始化:16位定时模式,允许中断
void init_timer0(void) 
{IE |= (1 << 7) | (1 << 1);  // 使能全局中断(EA=1)和Timer0中断(ET0=1)TCON |= (1 << 4);           // 启动Timer0(TR0=1)TMOD &= ~(3 << 2);          // 清除Timer0模式位(M1~M0)TMOD &= ~(1 << 1);          TMOD |= (1 << 0);           // Timer0设为16位定时模式(M1=0,M0=1)TH0 = 64535 >> 8;           // 加载初值(定时约1ms,晶振11.0592MHz)TL0 = 64535;                
}// Timer0中断服务函数:计数500次(500ms)翻转P2口
void timer0_handler(void) interrupt 1 
{static int t = 0;++t;if (t >= 500) {             // 累计500msP2 ^= 0xFF;             // 翻转P2所有引脚(LED闪烁)t = 0;}TH0 = 65035 >> 8;           // 重新加载初值(避免溢出后计数不准)TL0 = 65035;
}int main(void) 
{init_timer0();P2 = 0xFF;                  // 初始P2口高电平(LED灭)while (1){}                 // 主循环空转,等待中断return 0;
}

(2)定时器控制和五个按键控制蜂鸣器不同频率的声音

#include <reg52.h>
#include "key.h"  // 需确保该头文件中实现了init_key()(按键初始化)和key_pressed()(按键检测)// 蜂鸣器频率对应的定时器0初值(晶振频率默认11.0592MHz,1机器周期=12/11059200≈1.085μs)
// 计算逻辑:定时时间=(65536-初值)×机器周期,蜂鸣器方波频率=1/(2×定时时间)(翻转一次为半个周期)
#define Hz200    63035   // 200Hz频率对应的定时器初值
#define Hz400    64285   // 400Hz频率对应的定时器初值
#define Hz800    64910   // 800Hz频率对应的定时器初值
#define Hz1000   65035   // 1000Hz频率对应的定时器初值
#define Hz2000   65285   // 2000Hz频率对应的定时器初值unsigned short n = Hz200;  // 蜂鸣器初始频率对应的定时器初值(默认200Hz)/*** @brief 定时器0初始化函数(16位定时模式,用于产生蜂鸣器驱动方波)* 配置说明:* 1. 使能全局中断(EA=1)和定时器0中断(ET0=1)* 2. 定时器0工作模式:模式1(16位定时,需手动重装初值)* 3. 初始加载默认频率(200Hz)的初值,定时器暂不启动(TR0=0,由按键控制启动)*/
void init_timer0(void)
{IE |= (1 << 7) | (1 << 1);  // EA=1(全局中断使能),ET0=1(定时器0中断使能)// TCON |= (1 << 4);  // 注释:定时器0运行控制位TR0暂不置1,由按键触发启动// 配置定时器0工作模式(模式1:16位定时)TMOD &= ~(3 << 2);  // 清除定时器1的模式位(TMOD高4位,避免干扰)TMOD &= ~(3 << 0);  // 清除定时器0的模式位(TMOD低4位:M1M0初始化为00)TMOD |= (1 << 0);   // 定时器0模式1:M1=0,M0=1(16位定时模式)TH0 = n >> 8;       // 加载定时器0高8位初值(n为16位,右移8位取高字节)TL0 = n;            // 加载定时器0低8位初值
}/*** @brief 定时器0中断服务函数(核心:翻转P2.1引脚,产生蜂鸣器驱动方波)* 逻辑说明:* 1. 每次定时器0溢出(达到定时时间),触发中断* 2. 翻转P2.1电平(高→低/低→高),产生方波信号* 3. 重新加载初值(16位模式无自动重装,需手动补初值,确保定时精度)*/
void timer0_handler(void) interrupt 1
{P2 ^= (1 << 1);  // 翻转P2.1引脚电平(蜂鸣器接此引脚,方波驱动发声)TH0 = n >> 8;    // 重新加载定时器高8位初值(避免下一次定时时间偏差)TL0 = n;         // 重新加载定时器低8位初值
}/*** @brief 主函数(按键检测+频率切换+定时器启停控制)* 功能流程:* 1. 初始化定时器0和按键* 2. 循环检测按键:*    - 按键1~5:设置对应频率的初值,启动定时器0(蜂鸣器按对应频率发声)*    - 无按键(key=0):停止定时器0(蜂鸣器静音)*/
int main(void)
{int key;  // 优化:将key定义在函数开头,兼容部分51编译器的局部变量处理规则init_timer0();  // 初始化定时器0init_key();     // 初始化按键(需在key.h中实现,如配置按键引脚、消抖等)while(1)  // 主循环:持续检测按键{key = key_pressed();  // 读取按键值(返回0=无按键,1~5=对应按键)// 按键1:蜂鸣器频率设为200Hz,启动定时器0if(key == 1){n = Hz200;TCON |= (1 << 4);  // TR0=1(启动定时器0,开始产生方波)}// 按键2:蜂鸣器频率设为400Hz,启动定时器0else if(key == 2){n = Hz400;TCON |= (1 << 4);}// 按键3:蜂鸣器频率设为800Hz,启动定时器0else if(key == 3){n = Hz800;TCON |= (1 << 4);}// 按键4:蜂鸣器频率设为1000Hz,启动定时器0else if(key == 4){n = Hz1000;TCON |= (1 << 4);}// 按键5:蜂鸣器频率设为2000Hz,启动定时器0else if(key == 5){n = Hz2000;TCON |= (1 << 4);}// 无按键:停止定时器0(蜂鸣器静音)else if(key == 0){TCON &= ~(1 << 4);  // TR0=0(停止定时器0,方波中断,蜂鸣器不发声)}}return 0;  // 主函数返回(实际51单片机中此句可省略,循环永不退出)
}

key.c

#include <reg52.h>void init_key(void)
{P1 |= (0x0f << 4);
}int key_pressed(void)
{int ret = 0;if((P1 & (1 << 4)) == 0){ret = 1;}else if((P1 & (1 << 5)) == 0){ret = 2;}else if((P1 & (1 << 6)) == 0){ret = 3;}else if((P1 & (1 << 7)) == 0){ret = 4;}else if((P3 & (1 << 5)) == 0){ret = 5;}return ret;
}
http://www.dtcms.com/a/366879.html

相关文章:

  • spring.profiles.active配置的作用
  • 设计模式六大原则2-里氏替换原则
  • 短视频运营为什么需要代理 IP
  • JS函数进阶
  • 【可信数据空间-连接器状态监控】
  • 【面试题】如何构造排序模型训练数据?解决正负样本不均?
  • matlab实现希尔伯特变换(HHT)
  • 批量获取1688商品详情图及API接口调用实操指南
  • 【Kubernetes】知识点4
  • 卫生间异味来源难察觉?这款传感器为你精准探测并预警
  • 从设计到落地:校园图书馆系统的面向对象实现全流程
  • 多个docker compose启动的容器之间通信实现
  • Oracle 数据库如何查询列
  • (论文速读)Navigation World Models: 让机器人像人类一样想象和规划导航路径
  • 子串:最小覆盖子串
  • 深度学习中的学习率优化策略详解
  • UE5 制作游戏框架的部分经验积累(持续更新)
  • Kubernetes知识点(三)
  • AWS中为OpsManage配置IAM权限:完整指南
  • 深入剖析Spring Boot / Spring 应用中可自定义的扩展点
  • 力扣654:最大二叉树
  • AI+Java 守护你的钱袋子!金融领域的智能风控与极速交易
  • .NET 开发者的“Fiddler”:Titanium.Web.Proxy 库的强大魅力
  • 以数据与自动化驱动实验室变革:智能化管理整体规划
  • “乾坤大挪移”:耐达讯自动化RS485转Profinet解锁HMI新乾坤
  • 数据安全章节考试考点及关系梳理
  • Hadoop(七)
  • 服务器数据恢复—服务器断电,RAID数据恢复大揭秘
  • Python - 通用滑块验证码识别库 Captcha-Recognizer
  • MySQL复制技术的发展历程