51单片机基础学习(十):定时器&中断的应用

发布者:DelightfulWish最新更新时间:2025-09-19 来源: cnblogs关键字:51单片机  定时器  中断 手机看文章 扫描二维码
随时随地手机看文章

一、前期准备


1. 定时器工作模式的设置


        由于定时器工作模式寄存器TMOD是不允许位寻址的,所以对在两个定时器模式的设置上存在的一些技巧进行介绍。


        ① 直接对寄存器TMOD进行十六进制形式的赋值。


        ② 对寄存器进行“按位与 按位或”形式的赋值。


例如:定时器1保持原来的工作方式,使定时器0工作在模式1下。(假设此时定时器1工作在模式0下)


//方式1

TMOD = 0x01; //0000 0001 同时对两个定时器的工作模式进行设置

AI运行代码

//方式2

TMOD = TMOD & 0xF0; //1111 0000 将TMOD的低4位清零,高4位保持不变。

TMOD = TMOD | 0x01; //0000 0001 将TMOD的最低位置1,高4位保持不变。


        上面的方式②看似没有改变定时器1的工作模式,只改变了定时器0的工作模式。但是由于定时器工作模式寄存器TMOD是不允许位寻址的,所以虽然Timer1的工作模式没有被改变,但是它的工作模式是被重新定义过的。实际上,它的新模式与原来的模式是一样的,所以这个改变并没有引起任何实际效果。直接对TMOD进行赋值的方法更加简洁明了,而按位与和按位或操作更加灵活和精细。


2. 初值的计算&初值的设定


        1)初值的计算:


        在51单片机中,定时器初值的计算公式根据定时器的模式和需要定时的时间不同而有所不同。


公式:(2^n-初值)×(12 ÷ 晶振频率)= 定时时间


        n:定时器位数 ,由定时器模式进行决定;时钟周期 = 1 / 晶振频率;机械周期 = 12 × 时钟周期 = 12 / 晶振频率,如果这里认定晶振频率为12MHz,那么对应的机械周期就是12/12MHz=1us。那么进而可以得到上述公式的变形:初值 = 2^n - 定时时间,单位均为us。


        2)初值的设定:


        ① 上面得到的“初值”是十进制形式,可以将得到的十进制数转换成16进制数,然后分别对THn和TLn进行初值的设定。


        ② 同时还可以通过对“初值”进行256取整,得到THn的值,即THn = (2^n - 定时时间) / 256;通过对“初值”进行256取余,得到TLn的值,即TLn = (2^n - 定时时间) % 256。(定时器是8位的,最大计数值位255,即2^8-1,记到256时TLn向THn进1。)


例如:使工作在模式1下的定时器0工作50ms后产生溢出。


        利用上面公式,(2^16 - x) × (12 ÷ 12) = 50ms = 50000us,解得 x = 15536,对应16进制形式为3cb0H。


//方式1

TH0 = 0x3c;

TL0 = 0xb0;

AI运行代码

        利用“取整”+“取余”的方法,TH0 = (65536 - 50000) / 256,TL0 = (65536 - 50000) % 256。


//方式2

TH0 = (65536 - 50000) / 256;

TH0 = (65536 - 50000) % 256;


        3)给定的定时时间超过了最大定时时间65.536ms怎么办?


        定时器从开始计时到最后的溢出,期间会经历65536个机器周期,按照每个机器周期为1us来算,那么65536个机器周期相当于计时65536us,也就是65.536ms。如果想要定时的时间超过了65.536ms,可以通过添加循环进行实现。


        例如通过“TH0 = (65536 - 50000) / 256; TH0 = (65536 - 50000) % 256;”语句可以实现50ms定时,如果想要实现1s的定时,可以通过1s=50ms×20,即添加“循环”的方法进行实现。(下方实例中有相应代码详解)   


3. 中断允许设置


        由于中断使能(允许)寄存器IE是允许位寻址的,所以在对中断源允许中断的设置上存在的一些不同的实现方法。


        ① 直接对寄存器IE进行十六进制形式的赋值。


        ② 对所使用的中断源对应的控制位的状态进行单独设置。


例如:同时使用定时器T0和T1的中断。


//方法1

IE = 0x8A;   //对IE寄存器进行整体赋值,D6位和D5位默认取0。


//方式2

EA=0;

ET0=1;

ET1=1;


4. 中断服务函数


        中断服务函数是一种特殊的函数,用于处理单片机内部的中断事件。当中断事件发生时,单片机会暂停当前正在执行的程序,转而去执行中断服务函数,处理完中断事件后再返回原来被中断的地方,继续执行原来的程序。中断服务函数的特点是,它属于后台触发、前台执行的函数体。与其他函数不同的是,其他函数都是前台调用执行的函数体。


//中断服务函数的格式

void ISR_Name(void) interrupt ISR_Number  

{  

    // 中断处理代码  

}

//ISR_Name 是中断服务函数的名称,可以根据需求进行自定义。

//interrupt ISR_Number 指定了中断号,即中断源的唯一标识。

 

例如:中断服务函数来处理外部中断0(INT0)。

void Int0_Routine(void) interrupt 0

{  

    // 中断处理代码  

}


5. 外部中断的触发方式


        ① 低电平触发:指当外部中断引脚的电平为低电平时,中断请求被激活。


        ② 下降沿触发:指当外部中断引脚的电平从高电平变为低电平时,中断请求被激活。


        1)当采用低电平触发的方式时,外部中断源(输入到INT0/1)必须一直保持低电平有效,直到该中断被CPU 响应,同时在该中断服务程序执行完之前,外部中断源必须被清除(P3.2/P3.3要变为高电平),否则将产生另一次中断。即:


        · 只要P3.2(或P3.3)保持低电平,IE0(或IE1)就保持为1,并请求中断。


        · CPU响应该中断后,即使硬件会立刻将IE0(或IE1)清零,但如果P3.2(或P3.3)还保持为低电平,IE0(或IE1)将又会被置1,进而继续请求中断。


        · 中断服务程序执行完返回后,将会继续执行下一次中断,即使期间使用软件对IE0(或IE1)置零也不会使下一次中断停止。


        · 只有当P3.2(或P3.3)恢复高电平,在最后一次中断服务程序执行完,IE0(或IE1)由硬件清零之后,才不会再引发新的中断请求。


        2)当采用下降沿触发的方式时,要考虑按键抖动所产生的的影响,必须进行按键的消抖。


6. 自定义中断优先级


        由于中断优先级寄存器IP是允许位寻址的,所以在对中断优先级的设置上存在的一些不同的实现方法。


        ① 直接对寄存器IP进行十六进制形式的赋值。


        ② 对所使用的中断源对应的控制位的状态进行单独设置。


例如:将定时器T0的优先级设置为高优先级


//方式1

IP=0x02


//方式2

PT0=0;


二、应用实例


1. 定时器T0在模式1下实现流水灯,闪烁间隔为500ms。(定时器的应用)


#include

  

void main()

{

unsigned char k,n,i; 


    n=0x01;   //由LED模块的公共端电平所决定


    k=0;

    

    // 1.报备:将定时器T0的工作模式设置为模式1(定时功能)

TMOD=0x01;   

 

    // 2.置初值:设置定时时间为50ms,后续通过循环实现目标定时时间

TH0=(65536-50000)/256;   //TH0 = 0x3c;

    TL0=(65536-50000)%256;   //TL0 = 0xb0;


    // 3.启动:使定时器T0开始工作

    TR0=1;


    while(1)

{

n=0x01;

        //循环8次之后,进入下一个while(1)循环

for(i=0;i<8;i++)   //根据LED的个数,来决定i的取值范围

{

P0=~n;

while(k<10)   //此处通过一个while循环实现闪烁间隔为500ms=50ms×10

{

                // 4.等待:当定时器最高位产生溢出时,TF0会由硬件使其置1

while(TF0==0);   //当TF0=0时,一直执行while循环,直到TF0=1

 

                // 5.重置初值:使每次的溢出时间保持相同

                TH0=(65536-50000)/256;

TL0=(65536-50000)%256;

 

                // 6.清溢出:将中断标志位重新置0

TF0=0;


k++;

}

k=0;

n=n<<1;

}

}

}


2. 定时器T0的中断法实现流水灯,工作模式为模式1,闪烁间隔为100ms。(定时器+中断)


#include

 

unsigned char k;   //main函数和中断服务函数中均用到了k,所以k不能在main函数中进行定义      

 

//中断服务函数

void Timer0_Routine (void) interrupt 1

{

TH0=(65536-50000)/256;

TL0=(65536-50000)%256;   //重置初值,再次进行50ms计时

 

k++;

}

 

void main()

{

unsigned char n,i;      

                  

k=0;

 

P0=n;

 

TMOD=0x01;   //将定时器T0的工作模式设置为模式1(定时功能)

 

TH0=(65536-50000)/256;

TL0=(65536-50000)%256;   //设置定时时间为50ms,通过多次执行中断服务函数实现目标定时时间

 

    //配置定时器T0的中断开关

EA=1;   //闭合中断总开关

ET0=1;   //闭合定时器T0的中断允许开关

 

TR0=1;   //定时器T0开始工作

 

while(1)

{

n=0x01;   //由LED模块的公共端电平决定

 

        //循环8次之后,进入下一个while(1)循环

for(i=0;i<8;i++)   //根据LED的个数,来决定i的取值范围

{

P0=~n;

 

while(k<2);   //此处通过中断服务函数实现闪烁间隔为100ms=50ms×2

 

k=0;

n=n<<1;

}

}

}


        问:和上面的程序1相比,程序2中并没有“清溢出”这一步骤。那是不是在“定时器”和“中断”同时使用时,定时器溢出标志位TFn不起作用?如果定时器溢出标志位TFn起作用,那为什么不用对其进行清零?


        答:显然,定时器溢出标志位 TFn 在定时加中断的情况下依然会被使用。TFn 是用来指示定时器是否溢出的标志位,当定时器计数达到设定值时,TFn 会被设置为 1,表示定时器已经溢出。这时,如果开启了中断,那么中断会被触发,TFn会被硬件电路自动清零。程序1中,之所以要进行手动TF0清零,是因为程1序没有使用中断,无法被自动清零。


3. 定时器T0在模式2下实现流水灯,闪烁间隔为100ms。(模式2下的定时器中断)


#include

 

unsigned int k;   //main函数和中断服务函数中均用到了k,所以k不能再main函数中进行定义

 

//中断服务函数

void Timer0_Routine (void) interrupt 1

{

k++;

}

 

void main()

{

unsigned char n,i;  

                      

k=0;

 

TMOD=0x02;   //将定时器T0的工作模式设置为模式2(定时功能) 

 

TH0=56;   //初值=256-定时时间   56=256-200

TL0=56;   //设置定时时间为200us

 

    //配置定时器T0的中断开关

EA=1;   //闭合总中断开关

ET0=1;   //闭合定时器T0的中断允许开关

 

TR0=1;   //定时器T0开始工作

 

while(1)

{

n=0x01;   //由LED模块的公共端电平决定

 

        //循环8次之后,进入下一个while(1)循环

for(i=0;i<8;i++)   //根据LED的个数,来决定i的取值范围

{

P0=~n;

 

while(k<500);   //此处通过中断服务函数实现闪烁间隔为100ms=200us×500

 

k=0;

n=n<<1;

}

}

}


注意:


        1) 定时器在模式2下工作时,TLi的溢出不仅置位TFi,而且将THi中的内容重新装入TLi,该过程是由硬件自动实现的,不需要再利用软件进行重置初值。THi中的内容由软件预置,重装时THi内容保持不变,因此计算出初值后对THn和TLn同时赋初值即可。


        2) 由于此处实现闪烁间隔为100ms是利用100ms=200us×500进行实现的,对于变量k的取值范围需要大于500,我们知道unsigned char类型所能表示的数值范围为0~255,unsigned int类型所能表示的数值范围为0~65535,因此将k定义为unsigned int类型。


4. 按键采用外部中断法实现流水灯的启停控制(外部中断与定时器中断)


#include

 

sbit key1=P3^2;   //将P3端口的第2位定义为名为key1的位变量

 

unsigned char k;   //main函数和中断服务函数中均用到了k,所以k不能再main函数中进行定义

 

unsigned char led[]={0xff,0xfe,0xfc,0xf8,0xf0,0xe0,0xc0,0x80};   //8个状态,形成流水灯现象

 

//延时函数 实现1ms的延时

void Delay(unsigned int xms) //@12.0000MHz

{

unsigned char i, j;

while(xms)

{ //_nop_();

i = 2;

j = 239;

do

{

while (--j);

} while (--i);

xms--;

}

}

 

//外部中断Int0的中断服务程序 —— 控制流水灯的启停

void Int0_Routine (void) interrupt 0

{

Delay(10);   //检测到下降沿在进入到Int0()后,首先进行10ms的延时,防止按键抖动造成的影响

if(!key1)   //检测按键是否按下,如果key1为低电平表示按键按下,程序就会继续执行

TR0=~TR0;   //对定时器运行控制位进行取反,实现“启停”功能

}

 

//定时器中断T0的中断服务程序 —— 实现50ms延时

void Timer0_Routine (void) interrupt 1

{

TH0=(65536-50000)/256;

TL0=(65536-50000)%256;   //重置初值,再次进行50ms计时

 

k++;

}

 

void main()

{

unsigned char i;  

                      

k=0;

 

TMOD=0x01;   //将定时器T0的工作模式设置为模式1(定时功能)

   

TH0=(65536-50000)/256;

TL0=(65536-50000)%256;   //设置定时时间为50ms,通过多次执行中断服务函数实现目标定时时间

 

    //配置中断开关

EA=1;   //闭合中断总开关

ET0=1;   //闭合定时器T0的中断允许开关

EX0=1;   //闭合外部中断Int0的中断允许开关   


    IT0=1;   //将外部中断设置为下降沿触发

 

TR0=1;   //定时器T0开始工作

 

while(1)   //循环上面设置的LED的8种状态

{

for(i=0;i<8;i++)

{

P0=led[i];

 

while(k<4);   //使状态转换时长为200ms=50ms×4

 

k=0;

}

}

}


注意:        


        1) 上面的程序中“流水灯的启停”和“状态转换时长”分别由“外部中断Int0”和“内部定时器T0”进行控制,因此“外部中断Int0的中断服务程序”中的“Delay(10);”语句并不会影响LED的状态转换时长。


        2) 同时,在“流水灯的启停”过程中,其“状态转换时长”也不会发生改变。LED各状态之间转换的时间为200ms,若在按键按下之前,距离上一次的状态改变已经经过了100ms,那么当按键再次按下时,计时时间会继续从100ms开始累加计时,等过了100ms后LED的状态就会再次发生改变。(可以将上述代码中的变量k定义为unsigned int类型,并将while(k<4)中的4换为一个较大的数,然后观察现象即可。比如将4换为40,状态转换时间就变为了:50ms×40=2000ms=2s,方便观察。)


5. 按键采用计数器中断实现LED灯的点亮和熄灭(计数器中断)


//按键按下3次,LED灯的状态变化一次

 

#include

 

//中断服务函数

void Timer0_Routine () interrupt 1 

{

P0=~P0;   //通过对LED的状态进行取反,来实现LED的亮灭                              

}

 

void main()                           

{

P0=0x00;   //LED灯状态初始化,全亮

                           

TMOD=0x06;   //T0作为计数器,工作模式为模式3  P3.4引脚接收到一次下降沿,计数器加1

                          

[1] [2]
关键字:51单片机  定时器  中断 引用地址:51单片机基础学习(十):定时器&中断的应用

上一篇:51单片机基础学习(三):按键的消抖
下一篇:51单片机基础学习(九):中断

推荐阅读最新更新时间:2026-03-22 12:13

51单片机学习(3)-中断概念和定时器中断
这一次学习的单片机内部资源,中断。主要了解51单片所具有的中断,和定时器中断的使用。 目的:利用定时器中断,结合前面学习的led灯和数码管,让两个led灯中的一个以250ms时间间隔闪烁,另一个以1s的时间间隔闪烁,两位数码管实现一分钟的循环计时 实验材料:郭天祥TX-1C单片机 编译器:keil4 烧录软件:STC-ISP 关键点: 1 中断概念 中断是单片机为了应对突发事件而设计的功能,在CPU处理某一事件A时,发生了B事件,请求CPU快速处理;CPU暂时停止处理当前的事件A,保存好事件A的处理进度,转而去处理事件B,在处理完事件B后,重新回到事件A的断点处继续处理。 单片中断过程 51单片机内部有5个中断中断源,52单片
[单片机]
51单片机STC89C52】定时器中断)控制LED
一、定时器/计数器T0 1、定时器/计数器的相关寄存器 2、定时器/计数器控制寄存器TCON TCON格式如下: 3、定时器/计数器工作模式寄存器TMOD 模式选择: 二、配置相关寄存器 1、使用STC-ISP工具 2、配置寄存器 设置定时器模式(16位定时器) 配寄存器推荐使用按位操作: 需要清零的位与等于0,不清零的位与等于1 需要置1的位或等于1,不需要置一的位或等于0 TMOD &= 0xF0; //清零低四位、高四位不变 TMOD |= 0x01; //低四位的bit0置1 设置定时器初值 TL0=0x00; //低八位、需要计算 TH0=0xDC
[单片机]
【<font color='red'>51单片机</font>STC89C52】<font color='red'>定时器</font>(<font color='red'>中断</font>)控制LED
51单片机基础之定时器中断(一)
TH和TL定时换算: 定时器说白了倒数的,时间到了触发一次中断,那么必须设置倒数时间, TH0和TL0就是高八位和第八位 假设定时1ms一次,那么2的16次方等于65536,定时1ms,就等于65536-1000=64536,再把十进制换成十六进制,如下图的例子。 代码: #include reg51.h sbit LED=P2^0; //等下要用到,会看到LED闪烁,闪烁一次说明定时器中断触发了100次 typedef unsigned char u8;//typedef关键字的作用是自己重定义数据类型 typedef unsigned int u16; u16 i=0; //定义
[单片机]
<font color='red'>51单片机</font>基础之<font color='red'>定时器</font><font color='red'>中断</font>(一)
51单片机~定时器和外部中断(各个位控制作用详解)
(一)中断 (二)定时器,计数器中断 TL0低八位先进行存储,达到0XF,向上进一,直到高低八位都满时就可以产生中断或者控制TF0口。 (1). TMOD低四位控制T0,高四位控制T1。 GATE:(门控位) (2)控制寄存器TCON:(低四位控制外部中断,高四位控制计数器启动和中断申请) (3)定时器的四种工作方式: 定时器开启工作原理: 四种工作方式:(机器周期(脉冲)和T0引脚来绝定电路触发) 区别在于TH0和TL0的位数和输出) 常用1和2 1. 2.用于比较精确的脉冲信号发射器: 3. 程序化步奏: 计算:初值==2^n-N(公式要根据你使用那种方式0.1
[单片机]
<font color='red'>51单片机</font>~<font color='red'>定时器</font>和外部<font color='red'>中断</font>(各个位控制作用详解)
51单片机学习笔记———6.中断法配置定时器
#include reg52.h sbit LED P0^4; void main() { EA = 1;//打开总中断 TMOD&=0xFC; TMOD|=0x01; TH0 = (65535-2000)/256;//定时2ms TL0 = (65535-2000)/%256; ET0 = 1; TR0 = 1; while(1) { ... } void interruptTime() interrupt 1 { static unsigned int n = 0; TH0 = (65535-2000)/256; TL0 = (65535-200
[单片机]
51单片机中断主要关于计时--定时--计算定时器初值--的简介
看到的关于中断 计时器定时器的介绍,个人看明白了。 目录: 1、单片机中断简介 2、中断允许寄存器IE 3、中断优先级寄存器IP 4、定时器中断 TMOD:定时器/计数器的工作方式寄存器,确定工作方式和功能。 TCON:控制寄存器,控制T0,T1的启动和停止及设置溢出标志。 工作方式 5、如何计算定时器的初值 6、中断服务程序的写法 7、代码示例 单片机中断简介 52单片机一共有6个中断源,它们的符号,名称以及各产生的条件分别如下: INT0 - 外部中断0,由P3.2端口线引入,低电平或下降沿引起 INT1 - 外部中断1,由P3.3端口线引入,低电平或下降沿引起 T0 - 定时器/计数器0中断, 由
[单片机]
<font color='red'>51单片机</font><font color='red'>中断</font>主要关于计时--定时--计算<font color='red'>定时器</font>初值--的简介
51单片机定时器T0的使用1-中断
/********************************************** 方法1:延时法 硬件:11.0592MHz晶振,STC89C52,RXD P1.0 TXD P1.1 波特率:9600 描述:T0用于定时,方式1,定时时间50ms,中断方式,定时时间到,TF1=1,利用模拟串口发送字符0x67 **********************************************/ #include reg52.h #define uchar unsigned char sbit P1_0 = 0x90; sbit P1_1 = 0x91; sbit P1_2 = 0x92; #de
[单片机]
51单片机-定时器1中断
************************************************************************************** *定时器1实验* 实现现象:下载程序后数码管最后一位间隔一秒循环显示0-F。使用单片机内部定时器可以实现准确延时。 注意事项:如果不想让点阵模块显示,可以将74HC595模块上的JP595短接片拔掉。 ***************************************************************************************/ #include reg52.h //此文件中定义了单片机
[单片机]
小广播
最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

厂商技术中心

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

 
机器人开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2026 EEWORLD.com.cn, Inc. All rights reserved