目录

1.什么是定时器

2.什么是中断

3.什么是定时器中断

4.配置定时器

4.1学习建议

4.2查看手册定时器章节

4.3定时时间计算

4.4示例代码

5.配置定时器0的中断

5.1查看手册中断章节

5.2示例代码

6.定时器0中断配置完整代码示例

7.定时器实现50%亮度的LED

8.定时器中断实现呼吸灯

9.定时器中断搭建前后台任务系统

10.定时器中断实现无延时按键消

1.什么是定时器

        定时器,顾名思义,就是用来定时的;计数器,顾名思义,就是用来计数的。初学的小伙伴就会有疑问了,为什么定时器又叫计数器呢?其实就是同一个外设能用作两种功能罢了。

2.什么是中断

        中断即中途打断。因某条件触发而打断当前的事,转而去干别的事情,完后回来继续干。单片机的中断也同理:

3.什么是定时器中断

        上面的这个比例跟单片机的定时器中断是一样的,首先我们在程序上配置定时器(设闹钟),然后程序进入主循环(睡觉)等待中断的到来,若中断标志位被硬件置一(闹钟响了),程序则跳转到中断服务函数执行里面的代码(弹射起来关闹钟),然后回到主循环内被中断的地方继续执行(继续睡觉)。

4.配置定时器

4.1学习建议

        在这里我想给初学的小伙伴一个建议,那就是一定要学会找手册读手册!点开这个链接去STC官网下载对应型号的芯片用户手册,人家的手册写的很详细,上面那个中断的解释就是从里面截取的。手册里什么都有,外设的说明,寄存器说明,应用举例,汇编代码以及C代码示例等等。

        在学习的时候,你如果是看的人家的视频教程,可以边看视频边对照着手册学习。就算是以后学STM32也是一样,一定要学会找手册,读手册!只有这样你才能学的明白,学的透彻,打好基础!

4.2查看手册定时器章节

        1.先看第一页,信息量巨大,解释了为什么定时器又是计数器,以及其本质(提示:单片机原理课要考哦!)

        其实没有高亮出的那条也很有意思,因为51单片机只有3个定时器(定时器0,1,2),当定时器1用作波特率发生器时,可能导致定时器不够用了,这时定时器0就可以配置到工作模式3:双8位定时器/计数器,即拆分成两个8位定时器,只不过要从定时器1借用中断标志位。

        2.紧接着手册里面列出了与定时器相关的寄存器,由于我们要配置的是定时器0中断,所有我就高亮出了只与定时器0相关的位与寄存器。看图:

也就只有TCON寄存器的TF0位,TR0位TMOD寄存器的低四位寄存器TL0寄存器TH0。我们需要配置关心的也就这几位而已。

        3.在这里我就先贴出定时器0的结构图,好对照,看看上面涉及的位与寄存器在下图哪里。

        4.我们接着手册看,第二页就开始讲解每个寄存器位的作用,首先是TCON寄存器,请对照上图。

TF0解析:他说TF0为定时器/计数器溢出中断标志,这个就类比是闹钟响了嘛,系统会不断的查询此位,如果TF0=1,就会跳转到中断服务函数。他说T0被允许计数:看上图,其实就是control那个开关闭合,前面的脉冲能输入到寄存器TL0,每来一个脉冲,TL0就加一,每当TL0计数到2^8=256后TH0就加一,这样就组成一个16位的计数器,最大能计到2^16=65536。计到最大值后就溢出了嘛,此时TF0就会被硬件置一产生溢出中断。

TR0解析:他说当GATE=0,TR0=1时定时器0就开始计数,对应上图:GATE=0,经过非门(取反)或门(只要有输入为1,输出则为1)输入1,那么输出则为1,INT0位无效。由于或门输出接到了与门(只有输入都为1,输出才为1)输入,与门的另一个输入TR0也=1,那么与门输出=1,control开关闭合,前面的脉冲才能输入到TL0。

    总结TCON初始配置: TF0=0;TR0=1;

         5.继续看手册,TMOD寄存器,也是需要对照定时器0结构图看哦,这里每一位他都讲的很清楚了,我就不再扣了。只是需要注意这个寄存器是不可位寻址的!不知道什么意思请百度

我后面的示例代码配置的是模式2:8位自动重装载所以TMOD=0x02;

        6.然后手册就对定时器的四种模式进行了解析其实也就是TL0与TH0的组合。都用则是16位,最大计数到65536,但是这样的话就不能硬件自动重装载初始值。只用TL0用来计数的话,就只有8位,最大计数到256发生溢出中断,此时就能把TH0的值自动硬件装载到TL0。也可以TL0只用低5位,TH0全用。

TL0跟TH0是可以赋初值的,比如定时器配置成工作模式2:8位自动重装载,你想计数100就溢出,那么往TL0写入256-100=156就🆗。TH0为重新装载的值,如果你想要每计数100就发生溢出中断,那么TH0也要写入156。

4.3定时时间计算

        假如我们配置定时器0工作在模式1:16位定时/计数模式,每1ms产生一中断。

        SYSCLK即晶振频率,假如用的是12MHZ晶振,那么SYSclk=12MHZ,读下图黄色高亮部分,默认是12T模式,则计数脉冲频率=SYSclk/12=1MHZ。对应计数周期即为1us,TL0每1us自加1。我们想要定时1ms,那计数器初值是不是等于65536-1000?那么TL0=(65536-1000)%256;TH0=(65536-1000)/256。

4.4示例代码

示例:定时器0中断

晶振频率:12Mhz

工作模式:2

定时时间:100us

/*********定时器0初始化***************/
void Timer0_Init()	
{
	TMOD=0x02; 	//8位自动重装载定时器/计数器模式
	TH0=156;		//重装载值
	TL0=156;	  //定时器初始值 0~256
	TF0=0;			//清定时器0中断标志位
	ET0=1;      //开定时器0中断
	EA=1; 			//开总中断
	TR0=1;			//打开定时器0
}

注:ET0与EA是下节内容,先不管。

5.配置定时器0的中断

5.1查看手册中断章节

        1.先理解一下啥是中断嵌套

      

  

        2.中断查询次序即默认中断优先级。在相同优先级下,将按默认优先级处理

        3.中断号是啥?有啥用?其实中断查询次序号就是中断号,中断号是用来标明中断服务函数的,告诉系统这个函数是中断服务函数,发生中断后你就跳转到这个函数执行里面的代码。

        4.中断结构图,看下面图里红色部分

ET0:就一个单独的开关,默认是断开状态。前面定时器0的溢出中断信号作为开关的输入

EA:所有中断信号的总开关,默认也是断开状态。

只有这两个开关都闭合,定时器01的溢出中断信号才能被系统检测到,单片机才会执行相应的中断服务函数。

中断优先级控制寄存器:IP,XICON,IPH。可以看到默认都设置到了最高优先级中断1,1。这里具体配置就不再详解,请自行阅读手册。

5.综上所述,我们只需要闭合开关ET0和EA就行使能定时器0中断。那我们继续看手册这两个位位于哪个寄存器吧,具体需要怎么配置。

可以看到,这两个控制位位于中断允许寄存器IE,这个寄存器是可位寻址的,ET0=1,EA=1就能使能定时器0中断

5.2示例代码

可以看到就只是在前面示例代码的基础上加上ET0=1;EA=1;允许定时器0中断而已。

/*********定时器0初始化***************/
void Timer0_Init()	
{
	TMOD=0x02; 	//8位自动重装载定时器/计数器模式
	TH0=156;		//重装载值
	TL0=156;	  //定时器初始值 0~256
	TF0=0;			//清定时器0中断标志位
	ET0=1;      //开定时器0中断
	EA=1; 			//开总中断
	TR0=1;			//打开定时器0
}

6.定时器0中断配置完整代码示例

此示例代码只是配置了定时器0在工作模式2下每100us中断一次,不做任何任务。

/*************************************
			STC89C52RC定时器中断测试
*************************************			
* @author:NachoNEKO
* @date:2023/10/30
* @brief:STC89C52RC定时器0中断测试
**************************************/

#include <STC89C5xRC.H>

/**************变量声明***************/


/***************函数声明**************/


/*********定时器0初始化***************/
void Timer0_Init()	
{
	TMOD=0x02; //8位自动重装载
	TH0=156;   //自动重装载值
	TL0=156;   //装填初值
	TF0=0;	   //清定时器0中断标志位
	ET0=1;     //开定时器0中断
	EA=1; 	   //开总中断
	TR0=1;	   //打开定时器0
}

void main()
{
	Timer0_Init();
	while(1)
	{
			
	}
}

void Timer0_irt() interrupt 1	 //定时器0中断 0.1ms/次
{

}

7.定时器实现50%亮度的LED

/*************************************
			STC89C52RC定时器中断测试
*************************************			
* @author:NachoNEKO
* @date:2023/10/30
* @brief:STC89C52RC定时器0中断测试
**************************************/

#include <STC89C5xRC.H>

/**************变量声明***************/
sbit LED = P2^0; //#define LED P20


/***************函数声明**************/


/*********定时器0初始化***************/
void Timer0_Init()	
{
	TMOD=0x02; //8位自动重装载
	TH0=156;   //自动重装载值
	TL0=156;   //装填初值
	TF0=0;	   //清定时器0中断标志位
	ET0=1;     //开定时器0中断
	EA=1; 	   //开总中断
	TR0=1;	   //打开定时器0
}

void main()
{
	Timer0_Init();
	while(1)
	{
			
	}
}

void Timer0_irt() interrupt 1	//定时器0中断 0.1ms/次
{
	static unsigned int counter1=0;
	
	if(++counter1==100) counter1=0;	//10ms

	if(counter1<50) LED=0;	//模拟100HZ 50%占空比PWM
	else LED=1;
}

8.定时器中断实现呼吸灯

在上面代码的基础上,思考,若让比较值慢慢变化起来会达到什么效果?怎样才能让比较值逐渐变起来?

解析:定义一个全局变量compare代替50,compare在主循环里面每10ms自加/减一次。compare自加到100则自减,自减到0则自加,如此循环。是不是就实现呼吸灯的效果了?

/*************************************
			STC89C52RC定时器中断测试
*************************************			
* @author:NachoNEKO
* @date:2023/10/30
* @brief:STC89C52RC定时器0中断测试
**************************************/

#include <STC89C5xRC.H>

/**************变量声明***************/
sbit LED = P2^0;	//#define LED P20
bit turn_flag=1;	//呼吸状态翻转标志
unsigned char compare=0;

/***************函数声明**************/
void delay_ms(unsigned char xms);
void LED_Breathing(void);

/*********定时器0初始化***************/
void Timer0_Init()	
{
	TMOD=0x02;  //8位自动重装载
	TH0=156;	//自动重装载值
	TL0=156;	//装填初值
	TF0=0;		//清定时器0中断标志位
	ET0=1;      //开定时器0中断
	EA=1; 		//开总中断
	TR0=1;	    //打开定时器0
}

void main()
{
	Timer0_Init();
	while(1)
	{
		LED_Breathing(); //呼吸灯
		delay_ms(10);	 //阻塞延时10ms
	}
}

void Timer0_irt() interrupt 1	//定时器0中断 0.1ms/次
{
	static unsigned int counter1=0;
	
	if(++counter1==100) counter1=0;	//10ms

	if(counter1<compare) LED=0;	//模拟100HZ 50%占空比PWM
	else LED=1;
}

void LED_Breathing(void)	//呼吸灯
{
	if(turn_flag){
		compare++;
		if(compare>100) turn_flag=0;
	}
	else{
		compare--;
		if(compare<1) turn_flag=1;
	}
}


void delay_ms(unsigned char xms)
{
	while(xms--){
		unsigned char data i, j;
		i = 2;
		j = 199;
		do
		{
			while (--j);
		} while (--i);
	}
}

9.定时器中断搭建前后台任务系统

        现在我们再思考,如果上述的主循环内还有其他业务函数,此业务函数要求每5ms被执行一次,那么此处的10ms阻塞延时函数还能用嘛?程序应该如何改才能实现10ms渐变呼吸灯的同时5ms执行一次此业务函数呢?

模拟前后台任务系统:这里把定时器中断服务函数当作后台,主要负责分配调度任务;主函数当作前台处理任务。

/*************************************
			STC89C52RC定时器中断测试
*************************************			
* @author:NachoNEKO
* @date:2023/10/30
* @brief:STC89C52RC定时器0中断测试,利
*	用定时器中断搭建前后台系统,充分释放C51性能
**************************************/

#include <STC89C5xRC.H>

/**************变量声明***************/
sbit LED1= P2^0;	//#define LED P20
sbit LED2= P2^1;
bit turn_flag = 1;
bit task2_flag=0;
bit task3_flag=0;
unsigned char compare=0;

/***************函数声明**************/
void LED_Breathing(void);

/*********定时器0初始化***************/
void Timer0_Init()	
{
	TMOD=0x02; 	 //8位自动重装载
	TH0=156;     //自动重装载值
	TL0=156;	 //初始值
	TF0=0;	     //清定时器0中断标志位
	ET0=1;       //开定时器0中断
	EA=1; 		 //开总中断
	TR0=1;	     //打开定时器0
}

void main()
{
	Timer0_Init();
	while(1)
	{
		/***********长时间,周期型任务**********/
		
		if(task2_flag){	//任务2:呼吸灯
			LED_Breathing();
			task2_flag=0;
		}
		
		if(task3_flag){	//任务3:闪灯
			LED2=!LED2;
			task3_flag=0;
		}
		
	}
}

void Timer0_irt() interrupt 1	 //定时器0中断 0.1ms/次
{
	static unsigned char counter1=0;
	static unsigned int counter2=0;

	counter1++;
	if(counter1>100){  //10ms  100HZ PWM
		counter1=0;	
		task2_flag=1;
	}

	counter2++;
	if(counter2>5000){ //500ms
		counter2=0;
		task3_flag=1;
	}
	
	/**********短时间任务************/
	if(counter1<compare) LED1=0;	//任务1:软件模拟PWM
	else LED1=1;
}

void LED_Breathing(void)	//呼吸灯
{
	if(turn_flag){
		compare++;
		if(compare>100) turn_flag=0;
	}
	else{
		compare--;
		if(compare<1) turn_flag=1;
	}
}

可以看到,代码里面没有用到任何的阻塞延时函数。

        还有需要注意的是,执行中断内程序的总时间不能超过每次中断的时间,否则连续不停的中断会导致主循环内的程序得不到执行,程序卡顿。也不要在中断内执行除法运算,51速度有限,执行一条除法运算时间大概在0.6ms左右。

10.定时器中断实现无延时按键消抖

接上述,我们加入按键任务,实现以下操作:

示例代码:https://download.csdn.net/download/qq_64346054/88517434?spm=1001.2014.3001.5503

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐