定时器和中断概念的基本认识(上)

学习过程跟随up主@海创电子

  经过一段时间的学习,终于走到了定时器中断的部分了!

  接下来我们主要从两个实验入手:

  实验一:LED小灯的闪烁
  实验二:按键中断法点亮LED

  在实验开始之前,我们先来了解一些概念。

时钟周期

  时钟周期又叫做振荡周期(晶振周期)、节拍周期,定义为时钟晶振频率的倒数。时钟周期是计算机中最基本的、最小的时间单位。在一个时钟周期内,CPU仅完成一个最基本的动作。例如晶振为12M,则时钟周期为1/12us。又可以被定义为节拍。

机器周期

  在计算机中,为了便于管理,常把一条指令的执行过程划分为若干个阶段,每一阶段完成一项工作。例如,取指令、存储器读、存储器写等,这每一项工作称为一个基本操作。完成一个基本操作所需要的时间称为机器周期。一般情况下,一个机器周期由若干个S周期(状态周期)组成。比如,取值周期,取数周期。在80C51内部,机器周期一般包括于6个状态周期,12个时钟周期。例如24M的晶振,机器周期为12/24M秒。

百度百科:时钟周期机器周期

情景引入

例:利用定时器定时20ms

  我们可以将定时器看作是一个容器,若定时的时间为20ms,则由公式计算可知:

  次数X(12/12000000)=0.02s,得出次数需要20000次。

  因为是从0开始计算,所以容器大小为65536,计算得出65536-20000=45536。因此我们需要在“容器”里头预先加载45536大小的“水”。

注意:这种计算方式需要单片机的晶振频率是12M。

  一般我们常用的模式是“定时模式1”——确定“桶”的大小,接下来将45536如上图所示转化为十六进制进行“装载”。

注意:上图当中的45536%256结果应为224。

  上图当中出现的两个寄存器——TH0和TL0含义如下:

  T:time
  H:high(高8位)
  0:定时器0
  L:low(低8位)

寄存器介绍

  这里的TCON(定时器控制寄存器)是可位寻址的,我们既可以把值赋值给整个寄存器(TCON=0xAA),也可以针对该寄存器的单个位进行赋值(TF1=1)。

  上图当中出现的两个寄存器——TF1和TR1含义如下:

  T:time
  F:flag(标志)——发生“溢出”时,标志位则置“1”
  1:定时器1
  R:run(运行)——开始往“桶”里“倒水”

采用查询法配置定时器的四个步骤

设置TMOD寄存器

视频里的教程在使用寄存器时,只使用了定时器0。因此在设置TMOD寄存器的时候设置的初始值是0x01。

  在设置寄存器TMOD时,视频当中出现了一下代码:

TMOD=0x01;
TMOD=TMOD&0xFC;
TMOD=TMOD|0x01;

  因为TMOD寄存器是不可位寻址的,因此第一句则直接将0x01赋值给TMOD。目的是为了设置TMOD寄存器当中的定时器0为“模式1”,一般情况下我们只需要编写第一句就能设置完了。

  第二、三两句则是为了使代码更加严谨。事实上,我们在这里只使用定时器0。我们假设此时的定时器1也有被使用,且被设置为其他模式。而我们只需要设置定时器0,倘若直接使用第一句代码来设置TMOD,则会影响到当前定时器1的模式。因此我们需要进行“与或”运算来确保我们更改的只有定时器0的模式。(只使用定时器1的时候同理)

  在这里就需要用到C语言当中的“与”、“或”这两种逻辑运算的知识了。

  接下来咱们解释一下第二、三行代码的具体作用,两行代码对数据的处理一共分为两步:

  第一步:

TMOD=TMOD&0xFC; 
/*
0x01 —— 0000 0001
&
0xFD —— 1111 1100
=
0x00 —— 0000 0000
*/

  对当前TMOD的值进行与运算,与上数值0xFC,目的是为了使定时器0的M1和M0这两个位的值为0,为第二步做准备。

  第二步:

TMOD=TMOD|0x01;
/*
0x00 —— 0000 0000
|
0x01 —— 0000 0001
=
0x01 —— 0000 0001
*/

  进行或运算,或上数值0x01,目的是为了使定时器0的M1和M0这两个位置的值分别为0和1,即“模式1”。而其他位则恢复为原来的数值。

  接下来我们来解释一下这里的“严谨之处”

  在这里我们假设定时器0和定时器1的模式均被设置为模式1,则:

0x10 —— 0001 0000 //一开始寄存器TMOD的状态,此时定时器1为模式1。

  接下来我们需要执行一下与操作:

TMOD=TMOD&0xFC;
/*
0x10 —— 0001 0001
&
0xFC —— 1111 1100
=
0x10 —— 0001 0000

  注意,执行到这里的时候,定时器1的状态被保留了下来,而定时器0的状态则被重置了。

  接下来我们执行第二步的操作:

TMOD=TMOD|0x01
/*
0x10 —— 0001 0000
|
0x01 —— 0000 0001
=
0x11 —— 0001 0001

  执行到这里可以看出不仅定时器1的状态被保留了下来,定时器0也成功被设置位模式1,这就是只修改对应定时器状态的方法,虽然麻烦了点,但会更加的严谨。

查询法中断程序实现

  以上程序当中的第15——17行:

TF0=0; //寄存器内数值“满”了之后标志位需要重新复位

TH0=0xB1; //寄存器内的数值“满”了之后,寄存器“清零”,需要重新加载初值
TL0=0xF4; //按照之前计算出的20000次,这里的数值应该为0xDF,这里的0xF4可能是算错了

中断法配置定时器

  在使用中断的时候,中断遵循查询次序,优先级按照上图表格从上至下依次排列。表格当中用红色的框框选起来的几个定时器为常用的定时器,在使用时注意中断源所对应的中断号。

中断程序实现

  上图的代码当中出现了这么一行:

static unsigned int cnt;

  这里的static目的是为了保留延时函数里的cnt变量的值,cnt作为延时函数当中的局部变量,在跳出延时函数时cnt的值会被清除,所以在定义时需要加上“static”这个关键字来保留它的值。

按键去抖操作的其他方法

  下图代码属于对上图代码的补充。

if(cnt1!=0x00) //这条语句包括了弹起和抖动状态