栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

(指南者)(二)寄存器、定时器和中断

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

(指南者)(二)寄存器、定时器和中断

(指南者)(二)寄存器、定时器和中断
  • 寄存器
  • 定时器
    • 时钟
    • 定时器寄存器
      • 01
      • 10
    • 代码例程
  • 定时器中断
    • 中断
    • 定时器中断
    • 代码例程
    • 代码例程
    • 时间计算
  • 定时器流水灯实现
  • 定时器按键实现

寄存器

在单片机中,具有很多用来储存数据的单元,当我们的代码下载进单片机时,就是将代码转化成二进制的机械码并储存在这种单元中,在这些单元中,有一部分特殊的单元,对里面储存不同的值会导致单片机工作的方式不同,这部分具有特殊功能的单元我们称为寄存器(由多个寄存器组成的一个功能整体我们称之为外设)。(例如 P0 , P0 这个寄存器的作用就是改变 IO 的输出状态。)

链接:这篇文章对寄存器的定义写的更加清晰,可以作为参考

不同的芯片内部具有的寄存器是不完全相同的,所以当我们使用不熟悉的芯片时,需要根据芯片数据手册来编写代码。

接下来我们介绍关于 STC12C5A60S2 这个系列的芯片上常用的寄存器。

定时器

在大多数通信或者实际项目中,对于信息处理的时间是有严格要求的,但是由于不同的芯片在功能等方面的差异,我们不能精准的知道运行一行代码的时间长短,所以大多数芯片都会具有定时器这个外设,来满足我们项目需求中的时间需求。

时钟

已经知道芯片中有定时器了,但是定时器是由什么作为参考来计时呢?
在大多数电路中,芯片附近可以找到晶振存在,晶振的作用就是提供一个固定的时钟信号。

像上图这样的我们就称之为时钟信号,晶振所发出的时钟信号的频率时固定且精准的,我们就可以通过读取这个时钟信号的周期次数来计时。

定时器寄存器

我们打开芯片手册

目录中可以直接找到定时器,计数器。

里面对于定时器的介绍有这一段话,通过记录时钟周期的次数来决定计时脉冲的时间,每一次的时钟周期的时间一定,我们改变一次计时脉冲所需要的时钟周期次数,就可以改变一次计时脉冲的时间,我们使用的芯片的定时器支持一个时钟周期就等同于一个计数脉冲,也支持十二个时钟周期等同于一个计数脉冲,默认情况下,是十二个时钟周期等同于一个计数脉冲。

接下来我们可以看到定时器外设所含有的寄存器,我们先讲解 TH 和 TL 寄存器(先不管 0 和 1 的区别,后面会讲到)
记录计数脉冲的次数就是使用的这两个寄存器,两个寄存器都是只有 8 位宽,没过一次计数脉冲, TL 的值就会增加 1 ,随着时间增加, TL 加到 255 时再次加 1 就会溢出,此时 TH 的值会加 1 , TL 的值会清零,随后 TH 的值加到 255 后再次加 1 也会溢出,此时 TH 也会清零。所以定时器不能无限记录时间的长短,是有范围的,在使用定时器时我们就要去考虑定时器的计时范围。
在 STC12C5A60S2 中,一共有两个定时器,分别是 T0 和 T1,所以我们可以看到 TH 和 TL 也被分为了 0 和 1 。

我们再来介绍 TMOD 寄存器的作用,我们可以看到这个寄存器也是八位,被分成了两个四位分别给 T0 和 T1 两个定时器使用。、
每单独的四位里前两位我们不用去深究,有兴趣可以看一下,我们只需要去配置后两位 M0, M1 ,作用时配置定时器的工作方式。
我们可以看到,后两位可以组成四种不同的工作方式。
这儿介绍常用的工作方式。

01

这个工作方式下,TH 和 TL 两个八位的寄存器全部使用,组成一个十六位的寄存器,计数溢出后计数清零。

10

在这个工作方式下,计数器只用了 TL 的八位,计数 256 次后溢出,但是溢出后的值会把 TH 的值存入 TL ,TL 就不被清零。


我们可以看到,在 TCON 这个寄存器中,每一位都被给予了新的名字,其中 TR 的作用时允许定时器开始计时,我们可以把它当作一个开关的功能,当每次计数溢出后, TF 会被置 1,当产生中断(后面会讲)时,这个位才会被清零。

我们需要配置的寄存器也就这些了,其他的默认就行。

代码例程
//所以,我们要开启定时器计时,就对这些寄存器配置就行
void Init(void) {
	TCOM = 0x01;	//把定时器1配置成01的模式
	TR0 = 1;		//允许定时器1计数
	//定时器计数的初始化就完成了,我们可以通过读取TH和TL的值来判断代码运行时间
}
定时器中断 中断

当我们正在做一件事情的时候,突然被另一件重要的事情打断,所以我们选择优先完成重要的事情,再回来接着完成之前没做完的事情,这个过程我们就称之为发生了中断,重要的事我们称之为中断事件。

定时器中断

由定时器的计数寄存器溢出,从而产生的中断,我们又称之为定时器中断。
上面我们讲到 TF 寄存器,当定时器溢出,这个寄存器会被置 1 ,此时就会发生中断,并且将此寄存器自动清 0 。

从 IE 寄存器中可以看到,定时器中断的允许位为 ET ,总中断的允许位为 EA ,我们将两个同时打开就可以打开我们的定时器中断。

代码例程
void Init(void) {
	TCOM = 0x01;
	TR0 = 1;
	ET0 = 1;		//允许定时器中断
	EA = 1;			//打开总中断
}

//打开了中断,那我们怎么跳转到中断事件去呢?可以通过中断函数
void Int0(void) interrupt 1 {	//中断函数是特殊的函数,不需要在main函数之中调用,并且需要通过interrupt来标注这是一个中断函数,后面的数字代表了不同的中断函数,后面会讲。
	//这儿就可以写我们需要做的中断事件。
}


我们可以看到,默认情况下,定时器 0 所对应的就是 interrupt 1 ,这些我们都可以根据芯片手册看到。
现在我们以及有了计时,有了中断,那我们怎么才能使每次进入中断的时间相等呢?
我们可以通过对 TH 和 TL 赋值的方法来控制进入中断的时间。

代码例程
//当使用模式 1 0 时
//我们只赋值一次就可以,每次溢出后TL的值会变成我们设置好的TH的值
void Init(void) {
	TCOM = 0x02;
	TH0 = 10;	//每次TL溢出后讲TH的值装进去
	TL0 = 10;	//TL一开始就从10开始计数人为的控制进入中断的间隔时间
	TR0 = 1;
	ET0 = 1;
	EA = 1;
}

void Int0(void) interrupt 1 {
}

//当使用模式 0 1 时
//我们只赋值一次就可以,每次溢出后TL的值会变成我们设置好的TH的值
void Init(void) {
	TCOM = 0x01;
	TH0 = 10;	//TH一开始就从10开始计数人为的控制进入中断的间隔时间
	TL0 = 10;	//TL一开始就从10开始计数人为的控制进入中断的间隔时间
	TR0 = 1;
	ET0 = 1;
	EA = 1;
}

void Int0(void) interrupt 1 {
	TH0 = 10;	//由于不会自动的赋值,所以我们需要在每次进入中断函数(也就是刚刚好溢出时)重新赋值
	TL0 = 10;
}
时间计算

我们怎么去控制进入中断的时间呢?
我是用的晶振是 11.0592M 的晶振,所以可以知道 1s 有 11059200 个时钟周期,我们对其除 12 就可以得到一个计数周期在 1s 内的次数,此时我们再除定时器的溢出次数就可以得到 1s 内的中断次数,用 1 除这个次数就可以得到时间。在这儿我们是可以把 TH 和 TL 赋值为负数,正数取反加一这个过程反过来就可以将负数转化成正数,最后发现,当我们赋值成正数时,计数到 256 溢出,当赋值为负数时,计数到 0 溢出。

定时器流水灯实现
#include 

unsigned int nTimer = 0;			//定义一个变量作为后面计时使用

void main(void) {
	unsigned char i = 0;		//定义一个变量作为后面流水使用
	
	TMOD = 0x01;				//定时器模式设置成定时器1 01模式
	TH0 = -9;					//通过计算我们可以得到 115200/12/256/9 = 400,1/400 = 0.0025s定时器溢出,进入一次中断
	ET0 = 1;					//打开定时器中断
	TR0 = 1;					//允许中断计数
	EA = 1;						//打开总中断
	while (1) {
		if(nTimer >= 200) {		//当定时器溢出200次时流水一次,流水灯以200*0.0025 = 0.5s速度流水
			P0 = 0x01 << i++;	//流水灯流水
			i &= 7;				//当i>=8时,将i清零
			nTimer = 0;			//次数清零
		}
	}
}

void Int0(void) interrupt 1 {	//中断函数
	TH0 = -9;					//重新赋初值
	nTimer++;					//每次进入中断函数计数加1
}
定时器按键实现
#include 

unsigned int nTimer = 0;
unsigned char cKey, cKeyCode;
unsigned int nDelayKey, nLED_Time = 200;
bit bLoose, bStop;

void DisposeKey(void);	//按键函数

void main(void) {
	unsigned char i = 0;

	TMOD = 0x01;
	TH0 = -9;
	ET0 = 1;
	TR0 = 1;
	EA = 1;
	while (1) {
		if (nTimer >= nLED_Time) {
			if (bStop == 0) P0 = 0x01 << i++;
			i &= 7;
			nTimer = 0;
		}
		if (cKeyCode != 0) DisposeKey();	//判断按键按下后执行按键函数
	}
}

void Int0(void) interrupt 1 {
	TH0 = -9;
	nTimer++;
	if (nDelayKey == 0) {	//当没有按键按下时,此时恒为0
		cKey = P2 & 0x07	//将按键的值保存在cKey中
		if (cKey != 0x07) nDelayKey = 4;	//当按键按下后,值不等于0x70,将nDelayKey赋4用来消抖
		else bLoose = 0;	//松开按键后清零松手标志
	} else {
		if (--nDelayKey == 0) {	//定时器进入四次,相当于10ms按键消抖
			cKeyCode = P2 & 0x07;	//再次存入按键的值
			if (cKeyCode != cKey) cKeyCode = 0;	//如果前后两次按键值不相等,就证明按键没有按下,将按键值清零
		}
	}
}

void DisposeKey(void) {
	if(bLoose == 0) {	//判断是否松手,没有松手就只会执行一次按键的作用
		if (cKeyCode == 6) {	//按键具体值
			if (nLED_Time <= 400) nLED_Time += 40;	//流水速度加快
		} else if (cKeyCode == 5) {
			if (nLED_Time >= 40) nLED_Time -= 40;	//流水速度减慢
		} else if (cKeyCode == 3) bStop = !bStop;	//流水停止
		bLoose = 1;	//表示此次按键按下按键作用已经执行了一次了
	}
	cKeyCode = 0;	//对按键进行清零
}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/510446.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号