定时器中断学习和简单应用

定时器中断&简单应用

  • 定时器中断
      • 基本介绍
        • 清楚明白
        • 工作原理
        • 定时器结构
      • 主要内容
        • 两个寄存器
        • 初始化程序
  • 简单应用-秒表
      • 基本功能
      • 代码详解

  • 彩蛋:对于独立按键的使用
  • 没啥硬核内容,就是初学者萌新入门,学长大佬请移步

定时器中断

基本介绍

清楚明白

  • 定时器和单片机的CPU是相互独立的。定时器/计数器工作的过程是自动完成的,不需要CPU的参与。
  • 51单片机中的定时器/计数器是根据机器内部的时钟或者是外部的脉冲信号对寄存器中的数据**+1**.
  • 有了定时器/计数器后,可以增加单片机的效率,一些简单重复加1的工作可以交给定时器/计数器去处理。CPU转而处理一些复杂的事情。同时可以实现精确定时作用。
    -本次使用定时器,并用外部的脉冲信号+1

工作原理

定时器实质上是一个+1计数器。它随着计数器的输入脉冲进行自加1,也就是每来一个脉冲,计数器就自动+1,当加到计数器为全1时,再输入一个脉冲就使计数器归零(下文有实例,如果最大装15,此时要再+1达到”16“才归零),且计数器的溢出使相应的中断标志位置1,向CPU发出中断请求(中断允许时)。

定时器结构

定时器的实质是加1计数器(16位),由高8位和低8位两个寄存器THX和TLX组成。TMOD是定时器的工作方式寄存器,确定工作方式和功能;TCON是控制寄存器,控制T0、T1的启动和停止及设置溢出标志。

主要内容

两个寄存器

  • 相当于你给了几个按钮,让它服从你的指令去采用不同方式完成你的要求。
    1,工作方式寄存器TMOD
    因为触发条件和精确读的要求,所以要选择相适应的工作方式。
    定时器中断学习和简单应用_第1张图片
  • 显而易见8个各自分成两组,下面分别介绍一下每组内容
    1.第一个是门控位
GATE 启动定时器工作条件
=0 TCON的TR0/1=1
=1 TCON的TR0/1=1&&外部中断引脚INT0/1=1

-可以看出=0的条件要少一点
2.
C/T:定时/计数模式选择位。 C/T=0为定时模式;C/T=1为计数模式
3.
M1M0工作方式设置位(一个四种)

M1M0 工作方式 说明
00 方式0 13位定时器
01 方式1 16位定时器
10 方式2 8位自动重装定时器
11 方式3 T0分为两个独立的8位计时器;T1停止计数
  1. 控制寄存器TCON
  • 低四位用于控制外部中断(在这里不介绍),高四位控制定时/计数的启动和中断申请。
    在这里插入图片描述
  • TR1:T1运行控制位。置1时,T1开始工作;置0时,T1停止工作。
  • TR0:T0控制位,同上。
  1. 重点介绍方式1
    位数为16位,
    TL0作为低8位TH0作为高8位
    组成了16位+1计数器。
    计数个数与计数初值的关系为:X=2^16-N定时器中断学习和简单应用_第2张图片

-看着花里胡哨,但是不用仔细看懂

初始化程序

  • 对TMOD赋值,以确定T0和T1的工作方式
  • 计算初值,并将其写入TH0、TL0或者TH1、TL1
  • 中断方式时,对EA赋值,开放定时器中断
  • 使TR0或者TR1置位,启动定时器定时
  • 关于服务函数,[上篇博客](这里用interrupt 1||3)(https://blog.csdn.net/weixin_45779702/article/details/103681485)有讲过,再提一次
    定时器中断学习和简单应用_第3张图片

简单应用-秒表

定时器中断学习和简单应用_第4张图片

基本功能

  • 按键S1开始计时
  • 按键S2停止计时
  • 按键S3数据归零

代码详解

#include 
#define uint unsigned int						  //对数据类型进行声明定义
#define uchar unsigned char
/*位定义*/
sbit LSA = P2^2;				 //138芯片A2引脚
sbit LSB = P2^3;				 //138芯片A1引脚
sbit LSC = P2^4;				 //138芯片A0引脚
/*全局变量&&数组*/
uchar DT_SECOND = 0;				 //秒计时
uchar DT_MINUTE = 0;				 //分计时

uchar code duan[] = {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};             //共阴数码管无小数点 显示0~9,十分位,百分位,十位
uchar code duanpoint[] = {0xbf,0x86,0xdb,0xcf,0xe6,0xed,0xfd,0x87,0xff,0xef};        //个位 //共阴数码管有小数点 显示0~9

/*函数部分*/

//延时1ms函数
void delay1ms(uint t)
{
	uint i,j;
	for(i=0;i<t;i++)
	{
		for(j=0;j<100;j++);	
	}
}
//中断函数,初始化
void Timer0Init()
{
	TMOD = 0x01; 		//选择T0定时/计数器,工作在方式1,16位计数器		
	TH0 = 0xFC;	        //计数初始值,计数从64536开始,计1000个数,完成一次计数,时间为1ms
	TL0 = 0x18;		    //65536-1000/256;(65536-1000)%256-->数制知识
	ET0 = 1;			//定时/计数器0中断允许位
	EA = 1;				//总中断开启
}
//独立按键S3函数
void S3()
{
	DT_SECOND = 0;			//秒归零
	DT_MINUTE = 0;			//分归零
	TR0 = 0;			//运行控制位清0,关闭定时器
}

//和运算判断键值
void ScanKey()
{
	uchar Key;                   //临时变量
	Key = P3&0x07;	   //只需要三个按键,分别接的P3^1,P3^2,P3^3,故把P3高五位被屏蔽
	if(Key!=0x07)	   //判断哪个键被按下
	{
		delay1ms(10);	 //消抖10ms
		Key = P3&0x07;	 //再次赋值,避免此时按键状态改变
		if(Key!=0x07)	 //确认按键被按下
		{
			switch(Key)
			{
				case 0x05:				  //S1(P3^1)被按下101
							TR0 = 1;	  //定时器0运行控制位为1,启动定时器0
							break;
				case 0x06:				  //S2(P3^0)被按下110
    
							TR0 = 0;	  //定时器0运行控制位为0,关闭定时器0
							break;
				case 0x03:				  //S3(P3^2)被按下011
							S3();
							break;
			}
		}
		while(Key!=0x07)		//松手检测
		{
			Key = P3&0x07;
		}
	}
	
	
}

//数码管动态扫描函数
void DigDisplay(uchar s,uchar min)                //数码管从右往左
{
	LSA = 0;			       //秒个位位选
	LSB = 0;
	LSC = 0;
	P0 = duan[s%10];		      //发送段码,显示秒个位
	delay1ms(5);	                       //间隔一段时间扫描

	LSA = 1;			      //秒十位位选 
	LSB = 0;
	LSC = 0;
	P0 = duan[s/10];		       //发送段码显示秒十位
	delay1ms(5);			  //间隔一段时间扫描

	LSA = 0;			  //分的个位位选
	LSB = 1;
	LSC = 0;
	P0 = duanpoint[min%10];		  //发送段码,显示分的个位
	delay1ms(5);			  //间隔一段时间扫描

	LSA = 1;			  //分的十位位选
	LSB = 1;			  //间隔一段时间扫描
	LSC = 0;
	P0 = duan[min/10];		  //发送段码,显示分的十位
	delay1ms(5);			  //间隔一段时间扫描

	P0 = 0x00;				  //消隐,清除已有数据
}

//主函数咯
void main(void)
{
	P0 = 0x00;		   //读端口前写1
	P3 = 0xFF;		   //读端口前写1
	Timer0Init();	  //定时器中断初始化函数
	while(1)
	{
		DigDisplay(DT_SECOND,DT_MINUTE);	   //数码管显示函数
		ScanKey();		  //按键扫描函数
	}
}

//服务函数咯
void Timer0() interrupt 1
{
	static uint count_s;	
	static uint count_min;		//采用静态变量,不用赋初值0
	TH0 = 0xFC;				    //计数值初始化(1s计时是连续的下一次还得使),从64536开始计数,计满时为65536,溢出时即为 1ms
	TL0 = 0x18;
	count_s++;				     //秒计数
	count_min++;				 //分计数
	if(count_s==1000)			 //计数到1s(1000*1ms)时,秒计数器开始工作
	{
		count_s = 0;		 //秒计数清零
		DT_SECOND++;			 //显示秒计数值自增
		if(DT_SECOND>59)		 //秒数最大为59
		{
			DT_SECOND = 0;
		}
	}
	if(count_min==60000)		  //计数到60000ms时,秒计数器开始工作
	{
		count_min = 0;		  //分计数清零
		DT_MINUTE++;				  //显示分计数值自增
		if(DT_MINUTE>59)			  //分数最大为59
		{
			DT_MINUTE = 0;
		}
	}
}

  • 详解
  1. 动态数码管(共阴)
    定时器中断学习和简单应用_第5张图片
  • 先说段选,就是让这个8.亮出什么数字的模样
    例如显示0.,就得让图中的A,B,C,D,E,F,DP亮起来,需要用八个二进制数,1是亮,0是不亮,而且A是八位的最右端,所以就是1011 1111,这样对于的两个十六进制就是0XBF.
  • 再说位选,一般开发板是八个数码管,通过芯片74HC245来打开某一数码管的点亮权限。这样由LSA,LSB,LSC来判断,开3个二进制位,正好控制八个数码管。例如,秒表的秒百分位,也就是显示的最右一位,对应的是000,也就是八个数码管最右边那一个(顺序是从右往左)。(放个图大家感受一下)
  • 这里因为有四位,其中第二位(左起)带有小数点,所以它的数组库不太一样(duanpoint)。
    定时器中断学习和简单应用_第6张图片
  1. 初始化和服务函数对1s的计时
  • 晶振是12KHZ
  • 初始化的TMOD是0x01,也就是门控位为0,T0运行控制位,定时模式,工作方式为1.
  • 初始化那里是FC18,对应的十进制也就是2^16(65536)-1000,实现脉冲1000次。16个二进制位其实是2 ^16-1,但是由溢出一词,就像水缸的水一样将将满,还没有满,所以再加一滴才算满。
  • 初始化实现了1000*1us也就是1ms,在服务函数中循环1000次,最终也就是1s。当然我们也可以改变初始化和循环的搭配实现1s的计时,这样做只是一定程度上提高了计时上限和降低一定误差罢了。
  1. 独立按键判断键值
    按常规我们可以通过位定义,消抖来判断哪个按键被按下,但是这里采用和运算。
    我们通过KEY=0X07(0000 0111),由于上拉电阻所以独立按键均置高电平,在按下之后变低电平。然后我们观察四个按键相应的引脚顺序。K2–1,K1–2,K3–3,K4–4(K4用不到)。
    然后按照此规律,当K1被按下,也就是第二位变0,即0000 0101=0x05。
    定时器中断学习和简单应用_第7张图片
  2. 计时
    这里其实蛮好理解了,对应的count_s相当于星际争霸的scv一样,搬运工干活的,而DT_second就是外显在动态(DT)数码管上的,一个主内一个主外。
  3. 如有不懂欢迎私信

你可能感兴趣的:(51单片机红外通信)