中断,定时器相关内容
- 单片机中断
- 什么是单片机的中断
- 中断嵌套
- 中断的优点
- 中断结构
- 中断相关寄存器
- 中断优先级IP
- 中断号
- 中断响应条件
- 中断使用实例`在这里插入代码片`
- 定时器
- CPU 时序的有关知识
- 定时器原理
- 定时计数结构
- 定时/计数器的寄存器
- 定时器配置
- 功能实现
单片机中断
高位跟低位如何计算得来的
时钟周期的计算:
1. 给定时钟频率是 11.0592 MHz,也就是每秒 11,059,200 个时钟周期。计算每个时钟周期的时间:
1s/11,059,200= 90.514纳秒
2. 定时器溢出时间:
我们希望定时器的溢出时间为 10 毫秒(即 0.01 秒)。计算需要的定时器计数值:
10ms/90.514ns ~~ 110000
所以,我们需要定时器计数 110,000 次才能产生 10 毫秒的时间。
3. 计算定时器初值:
51 单片机定时器是一个 16 位的计数器,最大计数值为 65535。所以定时器从初始值开始计数,直到溢出。
初始值= 65536 - 110000 = -44464
因为定时器的计数器是无符号数,负数要转换为 16 位数。因此:
初始值 = 65536 - 44464 = 21072
转换为十六进制:
21072(十进制)转换为十六进制为:
21072 = 0x52D8
什么是单片机的中断
对于单片机来讲,中断是指 CPU 在处理某一事件 A 时,发生了另一事件 B 请求 CPU 迅速去处理(中断发生);CPU 暂时停止当前的工作(中断响应), 转去 处理事件 B(中断服务);待 CPU 将事件 B 处理完毕后,再回到原来事件 A 被 中断的地方继续处理事件 A(中断返回),这一过程称为中断 。
通俗来讲就是MCU在正在做的事情强行中断事件从而执行中断的事件
,注意 中断事件最好不要有延时函数,在中断过后会立马返回去执行主线的函数
中断嵌套
当 CPU 正在处理一个中断源请求的时候(执行相应的中断服务程序),发生 了另外一个优先级比它还高的中断源请求。如果 CPU 能够暂停对原来中断源的 服务程序,转而去处理优先级更高的中断请求源,处理完以后,再回到原低级中 断服务程序,这样的过程称为中断嵌套。
中断的优点
①分时操作。
②实时响应。
③可靠性高。
中断结构
STC89C5X 系列单片机提供了 6 个中断请求源,它们分别是:外部中断 0(INT0)、外部中断 1(INT1)、定时器 0 中断、定时器 1 中断、串口(UART)中断。
所有的中断都具有四个中断优先级(基本型只有两个)。用户可以用关总中断允许位 (EA)或相应中断的允许位来屏蔽所有的中断请求,也可以用打开相应的中断允许位来使 CPU 响应相应的中断申请。
①INT0 对应的是 P3.2 口的附加功能,可由 IT0(TCON.0)选择其为高电平有效还是下降沿有效。当 CPU 检测到 P3.2 引脚上出现有效的中断信号时,中断标志 IE0(系统会自动设置)置 1,向 CPU 申请中断。
②INT1 对应的是 P3.3 口的附加功能,可由 IT1(TCON.2)选择其为高电平有效还是下降沿有效。当 CPU 检测到 P3.3 引脚上出现有效的中断信号时,中断标 志 IE1(TCON.3)置 1,向 CPU 申请中断。
③T0 对应的是 P3.4 口的附加功能,TF0(TCON.5),片内定时/计数器 T0 溢出中断请求标志。当定时/计数器 T0 发生溢出时,置位 TF0,并向 CPU 申请中断。
④T1 对应的是 P3.5 口的附加功能,TF1(TCON.7),片内定时/计数器 T1 溢出中断请求标志。当定时/计数器 T1 发生溢出时,置位 TF1,并向 CPU 申请中断。
⑤RXD 和 TXD 对应的是 P3.0 和 P3.1 口的附加功能,RI(SCON.0)或 TI (SCON.1),串行口中断请求标志。当串行口接收完一帧串行数据时置位 RI 或 当串行口发送完一帧串行数据时置位 TI,向 CPU 申请中断。
中断相关寄存器
EX0(IE.0),外部中断 0 允许位;
ET0(IE.1),定时/计数器 T0 中断允许位;
EX1(IE.2),外部中断 0 允许位;
ET1(IE.3),定时/计数器 T1 中断允许位;
ES(IE.4),串行口中断允许位;
EA (IE.7), CPU 中断允许(总允许)位。
IT0(TCON.0),外部中断 0 触发方式控制位。 当 IT0=0 时,为电平触发方式。 当 IT0=1 时,为边沿触发方式(下降沿有效)。
IE0(TCON.1),外部中断 0 中断请求标志位。
IT1(TCON.2),外部中断 1 触发方式控制位。
IE1(TCON.3),外部中断 1 中断请求标志位。
TF0(TCON.5),定时/计数器 T0 溢出中断请求标志位。
TF1(TCON.7),定时/计数器 T1 溢出中断请求标志位。
中断优先级IP
中断号
中断响应条件
- ①中断源有中断请求;
- ②此中断源的中断允许位为 1;
- ③CPU 开中断(即 EA=1)。
以上三条同时满足时,MCU 才有可能响应中断。
在使用中断时我们需要做什么呢?
①你想使用的中断是哪个?选择相应的中断号;
②你所希望的触发条件是什么?
③你希望在中断之后干什么?
注意:中断号一定要去中断源相匹配。
中断使用实例在这里插入代码片
格式:
普通函数 interrupt 中断号
//普通函数一般没有返回值也没有参数
//功能:使用独立按键 K3,K4 控制 LED 或者直流电机亮灭。
#include "reg52.h"
typedef unsigned int u16;
typedef unsigned char u8;
typedef unsigned long u32;
//定义LED3,4的控制引脚
sbit LED3 = P2^2;
sbit LED4 = P2^3;
void Int0_Init(void); //外部中断0的初始化
void Int1_Init(void); //外部中断1的初始化
//以毫秒为单位的延时
void delay_ms(unsigned int ms){
unsigned int i,j;
for(i=ms;i>0;i--)
for(j=123;j>0;j--);
}
//以10微秒为单位的延时
void delay_10us(u16 ten_us)
{
while(ten_us--);
}
void main()
{
Int0_Init();//触发中断 P3 引脚比较特殊 P3 ^2 引脚触发中断 0中断
Int1_Init();//触发中断 P3 引脚比较特殊 P3 ^3 引脚触发中断 1中断
while(1) //保持应用程序不退出
{
}
}
void Int0_Init(void)
{
IT0=1;//跳变沿触发方式(下降沿)触发方式
EX0=1;//打开 INT0 的中断允许 打开中断开关不然就算触发了也不执行中断方法
EA=1;//打开总中断 总开关
}
void Int1_Init(void)
{
IT1=1;//跳变沿触发方式(下降沿)
EX1=1;//打开 INT1 的中断允许
EA=1;//打开总中断
}
void Int0(void) interrupt 0
{
LED3 = !LED3;
}
void Int1(void) interrupt 2
{
LED4 = !LED4;
}
注意:
中断响应函数中绝对不允许出现delay或其它耗时的函数
//功能:点击独按键K3,直流电机转10s后自停,同理K4按下后,直流电机转20s后自停,数码管显示直流电机转动时间的倒计时。(使用中断)
#include "reg52.h"
typedef unsigned int u16;
typedef unsigned char u8;
typedef unsigned long u32;
//定义直流电机的控制引脚
sbit DC_MOTOR = P1^0;
//0-F共阴八段数码管编码
u8 smg_code[] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};
//定义数码管段选引脚
#define SMG P0
//定义数码管的位选引脚
sbit SMG_22 = P2^2;
sbit SMG_23 = P2^3;
sbit SMG_24 = P2^4;
//u8code表示8位数码管要显示的编码
void SMG_Display(u8 u8code[]);
void Int0_Init(void); //外部中断0的初始化
void Int1_Init(void); //外部中断1的初始化
static u8 g_u8TimeCNT = 10;
//以毫秒为单位的延时
void delay_ms(unsigned int ms){
unsigned int i,j;
for(i=ms;i>0;i--)
for(j=123;j>0;j--);
}
//以10微秒为单位的延时
void delay_10us(u16 ten_us)
{
while(ten_us--);
}
void main()
{
u8 i = 0;//时间计数器
u8 u8code[8] = {0};//数码管编码
Int0_Init();
Int1_Init();
DC_MOTOR = 0;
while(1) //保持应用程序不退出
{ //判断电机是否工作,时间时候倒计时为0
if(DC_MOTOR && (g_u8TimeCNT == 0))
{
DC_MOTOR = 0;
}
//计时模块
if(i == 100)
{
i = 0;
//如果说电机工作并且倒计时还没到0
if(DC_MOTOR && g_u8TimeCNT)
{
g_u8TimeCNT--;//倒计时
}
}
//数码管显示
u8code[0] = smg_code[g_u8TimeCNT/10];
u8code[1] = smg_code[g_u8TimeCNT%10];
SMG_Display(u8code);
i++;
}
}
void Int0_Init(void)
{
IT0=1;//跳变沿触发方式(下降沿)
EX0=1;//打开 INT0 的中断允许
EA=1;//打开总中断
}
void Int1_Init(void)
{
IT1=1;//跳变沿触发方式(下降沿)
EX1=1;//打开 INT1 的中断允许
EA=1;//打开总中断
}
void Int0(void) interrupt 0
{
g_u8TimeCNT = 10;
DC_MOTOR = 1;
}
void Int1(void) interrupt 2
{
g_u8TimeCNT = 20;
DC_MOTOR = 1;
}
void SMG_Display(u8 u8code[])
{
u8 i;
for(i=0;i<8;i++)
{
switch(i)
{
case 0: SMG_24 = 1;SMG_23 = 1;SMG_22 = 1;break;//7位
case 1: SMG_24 = 1;SMG_23 = 1;SMG_22 = 0;break;//6位
case 2: SMG_24 = 1;SMG_23 = 0;SMG_22 = 1;break;//5位
case 3: SMG_24 = 1;SMG_23 = 0;SMG_22 = 0;break;//4位
case 4: SMG_24 = 0;SMG_23 = 1;SMG_22 = 1;break;//3位
case 5: SMG_24 = 0;SMG_23 = 1;SMG_22 = 0;break;//2位
case 6: SMG_24 = 0;SMG_23 = 0;SMG_22 = 1;break;//1位
case 7: SMG_24 = 0;SMG_23 = 0;SMG_22 = 0;break;//0位
default:break;
}
//数码管段选
SMG = u8code[i];
delay_ms(1);//当前位的数码管显示保持时间
SMG = 0;//消影
}
}
定时器
CPU 时序的有关知识
①振荡周期:为单片机提供定时信号的振荡源的周期(晶振周期或外加振荡 周期)。
②状态周期:2 个振荡周期为 1 个状态周期,用 S 表示。振荡周期又称 S 周 期或时钟周期。
③机器周期:1 个机器周期含 6 个状态周期,12 个振荡周期。
④指令周期:完成 1 条指令所占用的全部时间,它以机器周期为单位。
学习定时器前需要明白的几点
①51 单片机有两组定时器/计数器,因为既可以定时,又可以计数,故称之 为定时器/计数器。
②定时器/计数器和单片机的 CPU 是相互独立的。
③51 单片机中的定时器/计数器是根据机器内部的时钟或者是外部的脉冲信号对寄存器中的数据加 1。
定时器原理
STC89C52RC单片机内有两个可编程的定时/计数器 T0、T1 和一个特殊功能定 时器 T2(针对增强型的)。
定时/计数器的实质是加 1 计数器(16 位),由高 8 位和低 8 位两 个寄存器 THx 和 TLx 组成。
它随着计数器的输入脉冲进行自加 1,也就是每来一 个脉冲,计数器就自动加 1,当加到计数器为全 1 时,再输入一个脉冲就使计数器回零,且计数器的溢出使相应的中断标志位置 1,向 CPU 发出中断请求(定时 /计数器中断允许时)。
如果定时/计数器工作于定时模式,则表示定时时间已到; 如果工作于计数模式,则表示计数值已满。可见,由溢出时计数器的值减去计数 初值才是加 1 计数器的计数值。
定时计数结构
上图中的 T0 和 T1 引脚对应的是单片机 P3.4 和 P3.5 管脚。
51 单片机定时/ 计数器的工作由两个特殊功能寄存器控制。
TMOD 是定时/计数器的工作方式寄存 器,确定工作方式和功能;
TCON 是控制寄存器,控制 T0、T1 的启动和停止及 设置溢出标志。
定时/计数器的寄存器
工作方式寄存器 TMOD 用于设置定时/计数器的工作方式,低四位用于 T0,高 四位用于 T1。其格式如下:
- GATE 是门控位, GATE=0 时,用于控制定时器的启动是否受外部中断源信号 的影响。只要用软件使 TCON 中的 TR0 或 TR1 为 1,就可以启动定时/计数器工作;
- GATA=1 时,要用软件使 TR0 或 TR1 为 1,同时外部中断引脚 INT0/1 也为高电平 时,才能启动定时/计数器工作。即此时定时器的启动条件,加上了 INT0/1 引脚 为高电平这一条件。
- C/T :定时/计数模式选择位。C/T =0 为定时模式;C/T =1 为计数模式。
- M1M0:工作方式设置位。定时/计数器有四种工作方式。
内部计时器工作原理
溢出就开始执行外部申请中断
TMOD |= 0x01; // 设置定时器0为模式1(16位定时器)
TL0 = 0x00; // 设置定时初始值
TH0 = 0xDC; // 设置定时初始值
TF0 = 0; // 清除TF0标志
TR0 = 1; // 启动定时器0
ET0 = 1; // 使能定时器0中断
加了一个自动重载,一个周期完毕自动复位重载
定时器配置
在使用定时器时,应该如何配置使其工作?其步骤如下(各步骤顺序可任 意):
①对 TMOD 赋值,以确定 T0 和 T1 的工作方式,如果使用定时器 0 即对 T0 配 置,如果使用定时器 1 即对 T1 配置。
②根据所要定时的时间计算初值,并将其写入 TH0、TL0 或 TH1、TL1。
设初值:TH0 = 0xD8,就是U16的高8位,TL0 = 0xF0,就是U16的低8位
③如果使用中断,则对 EA 赋值,开放定时器中断。
④使 TR0 或 TR1 置位,启动定时/计数器定时或计数。
上述中有一个定时/计数器初值的计算,下面我们来看下如何计算定时/计数 器初值。 前面我们介绍过机器周期的概念,它是 CPU 完成一个基本操作所需要的时间。
其计算公式是:机器周期=1/单片机的时钟频率。
51 单片机内部时钟频率是外部 时钟的 12 分频,也就是说当外部晶振的频率输入到单片机里面的时候要进行 12 分频。比如说你用的是 12MHZ 晶振,那么单片机内部的时钟频率就是 12/12MHZ, 当你使用 12MHZ 的外部晶振的时候,机器周期=1/1M=1us。
功能实现
实现的功能是:启用定时器0,让LED1每500ms闪烁,启用定时器1,让LED2每1秒闪烁
//功能:启用定时器0,让LED1每500ms闪烁,启用定时器1,让LED2每1秒闪烁
#include "reg52.h"
typedef unsigned int u16;
typedef unsigned char u8;
typedef unsigned long u32;
//定义LED3,4的控制引脚
sbit LED3 = P2^2;
sbit LED4 = P2^3;
void Time0_Init(void); //定时器0的初始化
void Time1_Init(void); //定时器1的初始化
void main()
{
Time0_Init();
Time1_Init();
while(1) //保持应用程序不退出
{
}
}
void Time0_Init(void)//配置一个10ms上溢的定时器
{
//配置TMOD寄存器
TMOD |= 0x01;//选择定时器0的工作方式为1
//配置TCOND寄存器
TR0 = 1; //定时器0的使能
ET0 = 1; //定时器0开启中断
TF0 = 0; //清除TF0标志 满载溢出执行中断会把TF变为1 所以我们在这里重置为0没问题
EA=1;//打开总中断
TL0 = 0x00; // 设置定时初始值 低8位重载定时
TH0 = 0xDC; // 设置定时初始值 高8位重载定时
}
void Time1_Init(void) //配置一个50ms上溢的定时器
{
//配置TMOD寄存器
TMOD |= 0x10;//选择定时器1的工作方式为1
//配置TCOND寄存器
TR1 = 1; //定时器1的使能
ET1 = 1; //定时器1开启中断
TF1 = 0; //清除TF0标志
EA=1;//打开总中断
TL1 = 0x00; //设置定时初始值
TH1 = 0x4C; //设置定时初始值
}
void Time0(void) interrupt 1
{
static u8 u8cnt0 = 0; //每10ms加1的计数器
TF0 = 0; //清除TF0标志
TL0 = 0x00; //设置定时初始值
TH0 = 0xDC; //设置定时初始值
if(u8cnt0 == 50) //每500ms改变LED3的状态
{
u8cnt0 = 0;
LED3 = !LED3;
}
u8cnt0++;
}
void Time1(void) interrupt 3
{
static u8 u8cnt1 = 0; //每50ms加1的计数器
TF1 = 0; //清除TF0标志
TL1 = 0x00; //设置定时初始值
TH1 = 0x4C; //设置定时初始值
if(u8cnt1 == 20) //每500ms改变LED3的状态
{
u8cnt1 = 0;
LED4 = !LED4;
}
u8cnt1++;
}