51单片机的中断和定时(全面)

定时器/计数器

51的定时器/计数器有2个分别是T1和T0,52系列的单片机有3个定时器/计数器,T0和T1是通用定时器/计数器,定时器/计数器2(简称T2)是集定时、计数和捕获三种功能于一体,功能更强。

首先看一下这个简单点的功能,我在实验中用到的定时器的作用是高精度延时的作用,之前使用的通过while和for循环的延时方法都只是大概的时间,而定时器则可以精确设定时间在1微秒(10^-6)左右(以晶振频率为11.0592MHZ来说),其最大的时间取值为0.071,可见已经可以达到钟表的误差水准了。

定时器/计数器0和1的方式控制寄存器TMOD:

D7D6D5D4D3D2D1D0
TMODGATEC/TM1M0GATEC/TM1M0
  T1                                                              T0

T1和T0分别代表单片机两个计数器
GATE:门控制位。当门控制位GATE=1时,定时器/计数器的运行受外部引脚输入电平的控制。其中INT0引脚控制T0,INT1引脚控制T1.当控制引脚为高电平且TR0或TR1置1时,相应的定时器/计数器才被选通。当门控制位GATE=0时,只要TR0或TR1置1,相应的定时器/计数器就被选通,此时不受外部引脚的控制。
C/T:该位为0的时候,用作定时器,该位为1的时候,用做计数器。

M1M0工作模式功能说明
00模式013位定时器/计数器
01模式116为定时器/计数器
10模式2自动重新装入的8位定时器/计数器
11模式3T0分成两个8位计数器,T1停止计数
模式0:   13位的计数器。由一个高8位(0~7)计数器(TH0或TH1)和一个具有32为分频的低8位      计数器中的(TL0或TL1)的低5位(0~4)组合成。
模式1:16位的计数器。(TH1,TL1)
模式2:自动装载8位计数器。主要应用在串口波特率发生器。

模式3:将16位计数器分成两个独立的8位计数器TL0和TH0.定时器/计数器的工作模式3只适用于     T0.
模式0&模式3:几乎不用。


特殊功能寄存器TCON

D7D6D5D4D3D2D1D0
TCONTF1TR1TF0TR0IE1IT1IE0IT0
   T1                               T0                                定时中断  

溢出标志位TF1/TF0:当定时器T1/T0溢出时,硬件自动将TF0/TF1置1,并申请中断。当进入中断服务程序时,硬件又将自动清零TF1/TF0.

启/停控制位TR1/TR0:该位由软件置位和复位。当GATE为0时,TR0/TR1置位为1时T0/T1开始计数,TR1/TR0复位为0时T1/T0停止计数;当GATE为1时,TR1/TR0为1时且INT1/INT0输入高电平时,T1/T0开始计数。


TCON和TMOD复位后都会自动变成0x00.


T2的控制寄存器T2CON和T2MOD及其程序访问

1、控制寄存器T2CON

D7D6D5D4D3D2D1D0
T2CONTF2
EXF2
RCLKTCLKEXEN2TR2C/T
CP/RL2
CP/RL2(D0):定时器/计数器2的捕获或重新再装入选择位。当CP/RL2=1时,如果EXEN2=1,则在T2EX(P1.1)引脚上的负跳变将触发捕获操作,即将TH2和TL2的内容传递给RCAP2H和RCAP2L;当CP/RL2=0时,若EXEN2=1,则T2计数满回0溢出或T2EX(P1.1)引脚上的负跳变,都将触发重新再装入操作,即将RCAP2H和RCAP2L的内容传递给TH2和TL2;当RCLK=1或TCLK=1时,CP/RL2标志位不起作用。T2的计数满回0溢出时,将强制T2进行自动重新再装入操作。重新再装入操作常用于串行口的波特率发生器。CP/RL2位须由软件置位或复位。

C/T2(D1):定时器/计数器2的定时或计数功能选择位。当C/T2=1时,选择计数器工作方式,下降沿触发,计数脉冲来自于外引脚T2CLK;当C/T2=0时,选择为定时器工作方式,做波特率发生器是,对f(osc)/2计数,不做波特率发生器时,对f(osc)/12计数。C/T2位须由软件置位或复位。

TR2(D2):定时器/计数器2的启动停止控制位。当软件置位TR2为1时,启动T2开始计数;复位TR2为0时,停止计数。

EXEN2(D3):定时器/计数器2的 外部触发允许标志位。当EXEN2=1时,如果T2不是正在工作在串口通信端口的时钟,则在T2EX引脚(P1.1)上的负跳变将触发捕获或重新再装入,并置EXF2位为1,请求中断;当EXEN2=0时,在T2EX引脚(P1.1)上的负跳变对T2不起作用。

TCLK(D4):串行口发送时钟标志位。当TCLK=1时,串行通信端使用T2的回0溢出信号作为串行口1和3的发送时钟;当TCLK=0时,使用T1的回0溢出信号作为发送时钟。TCLK位须由软件置位或复位。

RCLK(D5):串行口接受时钟标志位。当RCLK=1时,串行口通信端使用T2的回0溢出信号作为串行口1和3的接收时钟;当RCLK=0时,使用T1的回0溢出脉冲作为接收时钟。RCLK位须由软件置位或复位。

EXF2(D6):定时器/计数器2回外部中断请求标志位。当EXEN2=1时且T2EX引脚上出现负跳变而引起捕获或重新再装入时,则EXEN2置位,并向CPU申请中断,此时若允许T2中断,CPU将响应中断,并转向中断服务程序,EXF2也必须由软件复位。

TF2(D7):定时器/计数器2的溢出中断请求标志位。当T2计数溢出复位为0时,由内部硬件置位TF2,并申请中断。但在波特率发生器方式下,当RCLK位或TCLK置位为1时,T2计数溢出将不对TF2置位,此时必须由软件复位。

定时器/计数器2的工作模式:

T2CON的控制位和T2工作模式的对应关系
RCLK+TCLKCP/RL2TR2工作模式
00116为自动再装入
01116位捕获
1-1波特率发生器
--0工作停止

2、控制寄存器T2MOD:


D7D6D5D4D3D2D1D0
T2MOD------T2OEDCEN
D7~D0:保留位,做其他用途。

T2OE:T2输出启动位。

DCEN:置位为1时,允许T2加1计数或减1计数。



使用定时器的方法

第一:赋值TMOD寄存器,用来指定定时/计数器的工作模式。
第二:赋值TH0、TL0或TH1、TL1,设置计数寄存器的初值,精确设定好定时时间。

第三:如果需要使用中断,则可以对IE赋值,启动定时器中断。
第四:设置特殊功能寄存器TCON,对TR0、TR1置位,通过打开TR来让定时器进行工作。(也可设置为中断模式)

设定初值:

(2^位数-X)*12/晶振频率=时间。

X--->0xXXXX.

例:TH1 = 0xb8;  TL1 = 0X00;
12(65536 – x) /11059200 = 0.02s

例:小灯的1秒间隔闪烁

#include<reg52.h>
typedef unsigned  int    uint16;
typedef unsigned  char   uint8;

sbit LED=P0^0;
void main()
{
uint16 num=0;
TMOD=0x01;//设置使用定时器T0的模式1,此时不受外部输入引脚的控制
TH0=0xB8;//设置TH0
TL0=0x00;//设置TL0
TR0=1;//开始定时
while(1)
{
if(TF0==1)

{

这里只介绍了定时器的一种情况,其他的应该差不多一样,计数器的功能还没遇到,暂时先这样吧。

中断:

中断请求标志及其访问:

1、TCON的中断标志

 D7D6D5D4D3D2D1D0
位地址8F8E8D8C8B8A8988
位符号TF1TR1TF0TR0IE1IT1IE0IT0
TF1:定时器/计数器T1的溢出标志位。当定时器/计数器T1产生溢出时,单片机将自动置TF1=1。此时,CPU响应中断,转向相应的中断服务程序,并自动置TF1=0.

TF0:定时器/计数器T0的溢出标志位。当定时器/计数器T0产生溢出时,单片机将自动置TF0=1。此时,CPU响应中断,转向相应的中断服务程序,并自动置TF0=0.

IE1:外部中断1请求标志位。当单片机INT1端口的中断信号有效的时候,单片机将自动置IE1=1请求中断。CPU响应中断请求,转向对应的中断服务程序,并自动置IE1=0.

IT1:外部中断1的中断触发方式控制位。当IT1=0的时候,为低电平触发方式;当IT1=1的时候,为下降沿触发方式。

IE0:外部中断0请求标志位。当单片机INT0端口的中断信号有效的时候,单片机将自动置IE0=1请求中断。CPU响应中断请求,转向对应的中断服务程序,并自动置IE0=0.

IT0:外部中断0的中断触发方式控制位。当IT0=0的时候,为低电平触发方式;当IT0=1的时候,为下降沿触发方式。

2、SCON的中断标志:

 D7D6D5D4D3D2D1D0
位地址9F9E9D9C9B9A9998
位符号------TIRI
RI:串行接口接受数据中断请求标志位。当单片机的串行接口接受完一个数据后,硬件自动置RI=1.此时,CPU响应中断,转向相应的中断服务程序。注意此时不会自动Ri清零,须在软件置R=0。

TI:串行接口发送数据中断请求标志位。当单片机的串行接口发送完一个数据后,硬件自动置TI=1。此时,CPU响应中断,转向相应的中断服务程序,注意此时不会自动TI清零,须在软件中置TI=0。

中断允许标志及其访问:

中断的SFR
76543210
符号EAECET2ES
ET1
EX1
ET0
EX0

EA:中断允许或禁止总控制位。当置EA=0时,单片机将禁止所有中断,不响应任何中断请求;当EA=1时,单片机允许各个中断,此时还需要有其他标志位确定各个中断的允许或禁止。

ES:串行中断允许或禁止控制位。当置ES=0时,禁止串行口中断;当置ES=1时,允许串行口中断。

ET1:定时器/计数器T1允许或禁止标志位。当置ET1=0时,禁止定时器/计数器T1中断;当置ET1=1时,允许定时器/计数器T1中断。

EX1:外部中断1允许或禁止标志位。当置EX1=0时,禁止外部中断1;当置EX1=1时,允许外部中断1。

ET0:定时器/计数器T0允许或禁止标志位。当置ET0=0时,禁止定时器/计数器T0中断;当置ET0=1时,允许定时器/计数器T0中断。

EX0:外部中断0允许或禁止标志位。当置EX0=0时,禁止外部中断0;当置EX0=1时,允许外部中断0。

中断优先级标志及其访问:

D7D6D5D4D3D2D1D0
位地址BFBEBDBCBBBAB9B8
位符号---PSPT1PX1PT0PX0
PS:串行接口中断优先级设置位。当置PS=0时,该中断源被定义为低优先级;当置PS=1时,该中断源被定义为高优先级。

PT1:定时器/计数器T1优先级设置位。当置PT1=0时,该中断源被定义为低优先级;当置PT1=1时,该中断源被定义为高优先级。

PX1:外部中断1优先级设置位。当置PX1=0时,该中断源被定义为低优先级;当置PX1=1时,该中断源被定义为高优先级。

PT0:定时器/计数器T0优先级设置位。当置PT0=0时,该中断源被定义为低优先级;当置PT0=1时,该中断源被定义为高优先级。

PX0:外部中断0优先级设置位。当置PX0=0时,该中断源被定义为低优先级;当置PX0=1时,该中断源被定义为高优先级。


中断的运行过程大概是:设置中断类型,允许中断(EA)---->满足中断标志位---->开始执行中断程序

执行顺序,如果多个同优先级的中断请求同时发出,则CPU按照一定的查询次序来决定中断执行的顺序。51系列单片机对中断的查询次序为“外部中断0---》定时器/计数器T0---》外部中断1---》定时器/计数器T1---》串行接口中断”。

如果一个低优先级的中断请求正在执行,则可以中断该服务程序,然后执行本次中断请求。如果一个高优先级或同优先级的中断请求正在执行,则当前中断请求不会立即执行。

任何正在执行的指令在未完成前,中断请求都不会响应。

如果程序正在执行读写寄存器IE和IP指令,则执行完该命令后,需要再执行一条其他指令才可以相应中断。

如果程序正在执行返回指令,则执行完该命令后,需要再执行一条其他指令才可以响应中断。

格式:例 void It0(void) interrupt (中断源)X

中断源:0(外部中断0) 1(定时器/计数器T0) 2(外部中断1) 3(定时器/计数器T1) 4(串行接口中断) 5(定时器/计数器T2) 6(PCA中断)。

例:下面是一个利用中断和定时器/计数器T0实现数码管间隔一秒加一的程序。

 #include <reg52.h>
typedef unsigned char uint8;
typedef unsigned int uint16;
typedef unsigned long uint32;


code uint8 number[] = {0x3F,0x06,0x5B,0x4F,
      0x66,0x6D,0x7D,0x07,
      0x7F,0x6F,0x77,0x7C,
      0x39,0x5E,0x79,0x71};
uint32 i=0;
uint16 counter = 0;

sbit BCD6 = P1^5;
sbit BCD5 = P1^4;
sbit BCD4 = P1^3;
sbit BCD3 = P1^2;
sbit BCD2 = P1^1;
sbit BCD1 = P1^0;

void mdelay(void);
void init_delay(void);
void init_interrupt(void);
void show(uint32 temp);
//初始化
void init_delay()
{
TMOD = 0x01;//设置使能T0
TH0 = 0xB8;
TL0 = 0x00;
TR0 = 1;//使能定时器/计数器
}
void init_interrupt()
{
ET0 = 1;//设置定时器/计数器T0中断
EA = 1;//中断使能
}
void mdelay()
{
uint16 n = 100;
for(;n>0;n--);
}

void main(void)
{
init_delay();
init_interrupt();
while(1)
{
if(i == 50)
{
counter++;
i = 0;
}
show(counter);
}
}
//中断程序
void interrupt_timer1() interrupt 3
{
TH0 = 0xB8;
TL0 = 0x00;
i++;
}

void show(uint32 temp)
{
P0 = number[temp/100000%10];
BCD6 = 0;
mdelay();
BCD6 = 1;

P0 = number[temp/10000%10];
BCD5 = 0;
mdelay();
BCD5 = 1;

P0 = number[temp/1000%10];
BCD4 = 0;
mdelay();
BCD4 = 1;

P0 = number[temp/100%10];
BCD3 = 0;
mdelay();
BCD3 = 1;

P0 = number[temp/10%10];
BCD2 = 0;
mdelay();
BCD2 = 1;


P0 = number[temp%10];
BCD1 = 0;
mdelay();
BCD1 = 1;  

}

计算初值的方法:

  • 用C语言实现的,先要定义好定时器的初值,不管你使用多大的晶振,使用51单片机,一般都是12分频出来,也就可以得出一个机器周期,机器周期=12/n(n指晶振频率),假设你要定时的时间为M,那么定时的初值为:M/机器周期=初值;

TH0=(65536-初值)%256;

TL0=(65536-初值)/256;

  • 将(65536-初值)所得的值化成16进制,其高位就是TH0的值,低位为TL0的值,例如用12M晶振做1ms定时计算如下:

机器周期=12/12*10^6=1us(微秒)

定时初值=(1*10^-3)/(1*10^-6)=1000;

所以:TH0=(65536-1000)%256;

TL0=(65536-1000)/256;

  • 将65536-1000=64536化为16进制为:0xFC18,TH0=0xFC,TL0=0X18;

  • 定好初值后要延迟一秒就定一个延时参数,这里使用1000就行了(定时为1ms)中断程序为:timer0() interrupt 1 // 1ms延时(12.0MHz)


发布了8 篇原创文章 · 获赞 26 · 访问量 3万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览