1.中断系统
什么是中断系统?
计算机执行某程序时,发生了紧急事件或有特殊请求,CPU暂停某程序的执行, 转而去处理上述事件或请求,处理完毕后再重新执行某程序的过程叫做中断。
简而言之:就是计算机做一下突发程序,而这种突发就需要某种条件来触发。
数据的输入/输出传送方式:
无条件传送方式: 一方对另一方来说总是准备好的。
查询传送方式(LOOK UP): 传送前一方先查询另一方的状态,若已经准备好就传送,否则就继
续查询/等待。 中断传送方式(IRQ): 一方通过申请中断的方式与另一方进行数据传送。 直接存
储器存取方式(DMA): 双方直接通过总线传送数据, 不经CPU中转。适用于数据量大高速通讯的设备不占用CPU时间。
中断系统结构
引起CPU中断的根源,称为中断源。中断源向CPU提出的中断请求。CPU暂时中断原来的事务A,转去处理事件B。对事件B处理完毕后,再回到原来被中断的地方(即断点),称为中断返回。实现上述中断功能的部件称为中断系统(中断机构)----->可以理解为整套流程为中断系统
如何去使用单片机的中断系统?
1.51单片机的基础知识
51子系列允许5个中断源:----->(看触发中断的条件)
外部中断源(2个):
INT0——由P3.2端口线引入,低电平或下降沿引起。
INT1——由P3.3端口线引入,低电平或下降沿引起。
这两个外部中断源标志和它们的触发方式控制位由特殊功能寄存器TCON的低4位控制。
内部中断源(3个):
T0——定时器/计数器0中断,由T0回零溢出引起。
T1——定时器/计数器1中断,由T1回零溢出引起。
TI/RI——串行I/O中断,串行端口完成一帧字符发送/接收后引起。
这3个内部中断源的控制位分别锁存在特殊功能寄存器TCON和SCON中。
2.允许中断
理解:EA是总开关;ET0、ET1、ET2是各定时器中断的小开关;EX0、EX1是外部中断的开关;
3.中断配置
解释:IT0、IT1是外部中断触发选择类型,TF0、TF1是定时器溢出标志
4.编写中断处理函数
中断是处理紧急任务的,那么就需要中断处理函数
void int1() interrupt 2 //中断处理函数,加关键字interrupt和入口号
{
中断处理语句
}
interrupt 入口号
实例:
/*********************************************************************************
* 【作 者】: 清翔电子:向量
* 【版 本】: V1.0
* 【网 站】: http://www.qxmcu.com/
* 【淘宝店铺】: http://qxmcu.taobao.com/
* 【实验平台】: 清翔 QX-MCS51 单片机开发板
* 【外部晶振】: 11.0592mhz
* 【主控芯片】: STC89C52
* 【编译环境】: Keil μVisio4
* 【程序功能】: 外部中断0低电平触发方式,每按下一次S2按键就会进入一次中断,
LED灯会发生状态变化。
* 【使用说明】: 用1根杜邦线把P32和P37相接,每按下一次S2按键就会进入一次中断。
**********************************************************************************/
#include <reg52.h>
#define uint unsigned int
#define uchar unsigned char
sbit key_s2 = P3^0;//独立按键S2
sbit flag = P3^7; //外部中断信号产生脚
/*====================================
函数 : delay(uint z)
参数 :z 延时毫秒设定,取值范围0-65535
返回值 :无
描述 :12T/Fosc11.0592M毫秒级延时
====================================*/
void delay(uint z)
{
uint x,y;
for(x = z; x > 0; x--)
for(y = 114; y > 0 ; y--);
}
//外部中断0初始化
void int0Init()
{
EA = 1; //开总中断
EX0 = 1;//开外部中断0
IT0 = 0;//外部中断0低电平触发
}
void main()//main函数自身会循环
{
int0Init();//外部中断0初始化
while(1)
{
if(key_s2 == 0)//判断S2是否被按下
{
delay(20);//按键消抖
if(key_s2 == 0)
{
flag = 0;
flag = 0;//产生低电平,CPU会采样2次P3.2的状态,两次都为低才触发
flag = 1;//结束低电平
while(!key_s2);//松手检测
}
}
}
}
//外部中断1中断服务程序
void int0() interrupt 0
{
P1 = ~P1;//P1的状态按位取反
}
代码解释
p3.4、p3.5是两个外部中断,p3.2,、p3.3是两个内部定时中断,现在我们用外部中断0,
也就是p3.4,初始化选择低电平触发,那这个触发源从哪来?
需要我们人为制造一个发生高低电平变化的信号,也就是按键按下信号发生变化,将这个信号给p3.7引脚,然后将p3.4和p3.7相接,让p3.4引脚读取信号
flag = 0;
flag = 0;
用了两次是为了避免机器周期太快形成的干扰,可以改成为
flag = 0;
delay(5);
好了,我们现在试试用外部中断1试试
/*********************************************************************************
* 【作 者】: 清翔电子:向量
* 【版 本】: V1.0
* 【网 站】: http://www.qxmcu.com/
* 【淘宝店铺】: http://qxmcu.taobao.com/
* 【实验平台】: 清翔 QX-MCS51 单片机开发板
* 【外部晶振】: 11.0592mhz
* 【主控芯片】: STC89C52
* 【编译环境】: Keil μVisio4
* 【程序功能】: 外部中断1下降沿触发方式,每按下一次S2按键就会进入一次中断,LED
灯会发生状态变化。
* 【使用说明】: 用1根杜邦线把P33和P37相接,每按下一次S2按键就会进入一次中断。
**********************************************************************************/
#include <reg52.h>
#define uint unsigned int
#define uchar unsigned char
sbit key_s2 = P3^0;//独立按键S2
sbit flag = P3^7; //外部中断信号产生脚
/*====================================
函数 : delay(uint z)
参数 :z 延时毫秒设定,取值范围0-65535
返回值 :无
描述 :12T/Fosc11.0592M毫秒级延时
====================================*/
void delay(uint z)
{
uint x,y;
for(x = z; x > 0; x--)
for(y = 114; y > 0 ; y--);
}
//外部中断1初始化
void int1Init()
{
EA = 1; //开总中断
EX1 = 1;//开外部中断1
IT1 = 1;//外部中断1下降沿触发
}
void main()//main函数自身会循环
{
int1Init();//外部中断1初始化
while(1)
{
if(key_s2 == 0)//判断S2是否被按下
{
delay(20);//按键消抖
if(key_s2 == 0)
{
flag = 1;
flag = 0;//产生下降沿
while(!key_s2);//松手检测
}
}
}
}
//外部中断1中断服务程序
void int1() interrupt 2
{
P1 = ~P1;
}
2.定时器
什么是定时/计数器以及用处?
就是一个寄存器,会有机器自动按时产生一个信号,然后该寄存器+1,直到溢出为止,
我们可以通过改变计数的起始值从而改变溢出的时间,完成不同时间的定时
1.51单片机定时器基础
51单片机有2个16位定时器/计数器:定时器0(T0为P3.4)和定时器1(T1为P3.5)
这里所说的16位是指定时/计数器内部分别有16位的计数寄存器。 当工作在定时模式时,每经过一个机器周期内部的16位计数寄存器的值就会加1,当这个寄存器装满时溢出。 我们可以算出工作在定时模式时最高单次定时时间为65535*1.085us=时间(单位us) 当工作在计数器模式时,T0(P3.4引脚),T1(P3.5引脚)每来一个脉冲计数寄存器加1
定时器作用:定时计数器可以用于精确事件定时,PWM脉宽调制,波形发生,信号时序测量的方面。
2.启动定时器(打开开关)
3.设置定时器
现在不好理解很正常,直接看代码就非常容易理解了
/*********************************************************************************
* 【作 者】: 清翔电子:向量
* 【版 本】: V1.0
* 【网 站】: http://www.qxmcu.com/
* 【淘宝店铺】: http://qxmcu.taobao.com/
* 【实验平台】: 清翔 QX-MCS51 单片机开发板
* 【外部晶振】: 11.0592mhz
* 【主控芯片】: STC89C52
* 【编译环境】: Keil μVisio4
* 【程序功能】: 定时器0工作模式1 16位定时模式,数码管动态显示0-10,秒表。
* 【使用说明】:
**********************************************************************************/
#include <reg52.h>
#include <intrins.h>
#define uint unsigned int
#define uchar unsigned char
sbit DU = P2^6;//数码管段选
sbit WE = P2^7;//数码管段选
//共阴数码管段选表0-9
uchar code tabel[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};
/*====================================
函数 : delay(uint z)
参数 :z 延时毫秒设定,取值范围0-65535
返回值 :无
描述 :12T/Fosc11.0592M毫秒级延时
====================================*/
void delay(uint z)
{
uint x,y;
for(x = z; x > 0; x--)
for(y = 114; y > 0 ; y--);
}
/*====================================
函数 :display(uchar i)
参数 :i 显示数值,取值范围0-255
返回值 :无
描述 :三位共阴数码管动态显示
====================================*/
void display(uchar i)
{
uchar bai, shi, ge;
bai = i / 100; //236 / 100 = 2
shi = i % 100 / 10; //236 % 100 / 10 = 3
ge = i % 10;//236 % 10 =6
//第一位数码管
P0 = 0XFF;//清除断码
WE = 1;//打开位选锁存器
P0 = 0XFE; //1111 1110
WE = 0;//锁存位选数据
DU = 1;//打开段选锁存器
P0 = tabel[bai];//
DU = 0;//锁存段选数据
delay(5);
//第二位数码管
P0 = 0XFF;//清除断码
WE = 1;//打开位选锁存器
P0 = 0XFD; //1111 1101
WE = 0;//锁存位选数据
DU = 1;//打开段选锁存器
P0 = tabel[shi];//
DU = 0;//锁存段选数据
delay(5);
//第三位数码管
P0 = 0XFF;//清除断码
WE = 1;//打开位选锁存器
P0 = 0XFB; //1111 1011
WE = 0;//锁存位选数据
DU = 1;//打开段选锁存器
P0 = tabel[ge];//
DU = 0;//锁存段选数据
delay(5);
}
//定时器0初始化
void timer0Init()
{
TR0 = 1; //启动定时器0
TMOD = 0X01; //定时器工作模式1,16位定时器计数模式
TH0 = 0x4b;
TL0 = 0xfd; //定时50ms
}
void main()//main函数自身会循环
{
uchar mSec, Sec;//毫秒和秒储存变量
timer0Init();//定时器0初始化
while(1)
{
if(TF0 == 1)//判断是否溢出
{
TF0 = 0;//软件清零溢出标志位
TH0 = 0x4b;
TL0 = 0xfd; //定时50ms
mSec++;//50ms到
if(mSec == 20)
{
mSec = 0;
Sec++;//1秒时间到
}
}
display(Sec); //数码管显示函数
if(Sec > 10)
Sec = 0;//秒清零
}
}
主要的步骤
初始化: void timer0Init()
{
TR0 = 1; //启动定时器0
TMOD = 0X01; //定时器工作模式1,16位定时器计数模式
TH0 = 0x4b;
TL0 = 0xfc; //定时50ms
}
TMOD 是怎么来的?
0000 0001
TH0 和TL0是前后8位
也就是说要计数46083次溢出
那就要从65535-46083 = 19452开始计数
19452的16进制是4B FC
4.定时器计数
/*********************************************************************************
* 【作 者】: 清翔电子:向量
* 【版 本】: V1.0
* 【网 站】: http://www.qxmcu.com/
* 【淘宝店铺】: http://qxmcu.taobao.com/
* 【实验平台】: 清翔 QX-MCS51 单片机开发板
* 【外部晶振】: 11.0592mhz
* 【主控芯片】: STC89C52
* 【编译环境】: Keil μVisio4
* 【程序功能】: 定时器0工作模式1,16位计数模式,//定时器1工作模式1,16位定时模式
* 【使用说明】: 用一根杜邦线把P34引脚对应排针与P10引脚对应的排针链接到一起
P10产生方波 数码管显示方波次数
**********************************************************************************/
#include <reg52.h>
#include <intrins.h>
#define uint unsigned int
#define uchar unsigned char
sbit DU = P2^6;//数码管段选
sbit WE = P2^7;//数码管段选
sbit LED1 = P1^0;//LED1
//共阴数码管段选表0-9
uchar code tabel[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};
/*====================================
函数 : delay(uint z)
参数 :z 延时毫秒设定,取值范围0-65535
返回值 :无
描述 :12T/Fosc11.0592M毫秒级延时
====================================*/
void delay(uint z)
{
uint x,y;
for(x = z; x > 0; x--)
for(y = 114; y > 0 ; y--);
}
/*====================================
函数 :display(uchar i)
参数 :i 显示数值,取值范围0-255
返回值 :无
描述 :三位共阴数码管动态显示
====================================*/
void display(uchar i)
{
uchar bai, shi, ge;
bai = i / 100; //236 / 100 = 2
shi = i % 100 / 10; //236 % 100 / 10 = 3
ge = i % 10;//236 % 10 =6
//第一位数码管
P0 = 0XFF;//清除断码
WE = 1;//打开位选锁存器
P0 = 0XFE; //1111 1110
WE = 0;//锁存位选数据
DU = 1;//打开段选锁存器
P0 = tabel[bai];//
DU = 0;//锁存段选数据
delay(5);
//第二位数码管
P0 = 0XFF;//清除断码
WE = 1;//打开位选锁存器
P0 = 0XFD; //1111 1101
WE = 0;//锁存位选数据
DU = 1;//打开段选锁存器
P0 = tabel[shi];//
DU = 0;//锁存段选数据
delay(5);
//第三位数码管
P0 = 0XFF;//清除断码
WE = 1;//打开位选锁存器
P0 = 0XFB; //1111 1011
WE = 0;//锁存位选数据
DU = 1;//打开段选锁存器
P0 = tabel[ge];//
DU = 0;//锁存段选数据
delay(5);
}
//定时器0初始化
void timer0Init()
{
TR0 = 1; //启动定时器0
TMOD |= 0X05; //定时器0工作模式1,16位计数模式
TH0 = 0;
TL0 = 0;
}
//定时器1初始化
void timer1Init()
{
TR1 = 1; //启动定时器1
TMOD |= 0X10; //定时器1工作模式1,16位定时模式
TH1 = 0x4b;
TL1 = 0xfd; //定时50ms
}
void main()//main函数自身会循环
{
uchar mSec, Sec;//毫秒和秒储存变量
timer0Init();//定时器0初始化
timer1Init();//定时器1初始化
while(1)
{
if(TF1 == 1)//判断是否溢出
{
TF1 = 0;//软件清零溢出标志位
TH1 = 0x4b;
TL1 = 0xfd; //定时50ms
mSec++;//50ms到
if(mSec == 10) //定时50毫秒到
{
mSec = 0;
LED1 = ~LED1;//产生方波
}
}
display(TL0); //数码管显示函数
}
}
解释:计数器就是读取电平变化来计数,将p3.4和p1相接
TMOD |= 0X05;TMOD |= 0X10;防止取代原本位置,等同于TM0D = 0X15;
5.定时器中断
/*
定时器0工作模式1 16位定时计数
数码管动态显示0-10,每隔1秒显示一次
*/
/*********************************************************************************
* 【作 者】: 清翔电子:向量
* 【版 本】: V1.0
* 【网 站】: http://www.qxmcu.com/
* 【淘宝店铺】: http://qxmcu.taobao.com/
* 【实验平台】: 清翔 QX-MCS51 单片机开发板
* 【外部晶振】: 11.0592mhz
* 【主控芯片】: STC89C52
* 【编译环境】: Keil μVisio4
* 【程序功能】: 定时器0工作模式1 16位定时计数,中断方式响应。
三位数码管动态扫描,定时器0中断方式扫描。
* 【使用说明】: 按下S2按键数码管值加一,最大加到120.
按下S3数码管值减一,最小减到0.
**********************************************************************************/
#include <reg52.h>
#include <intrins.h>
#define uint unsigned int
#define uchar unsigned char
sbit DU = P2^6;//数码管段选
sbit WE = P2^7;//数码管段选
sbit key_s2 = P3^0;//独立按键S2
sbit key_s3 = P3^1;//独立按键S3
uchar num;//数码管显示的值
uchar mSec, Sec;//毫秒和秒储存变量
//共阴数码管段选表0-9
uchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};
//数码管位选码
uchar code SMGwei[] = {0xfe, 0xfd, 0xfb};
/*====================================
函数 : delay(uint z)
参数 :z 延时毫秒设定,取值范围0-65535
返回值 :无
描述 :12T/Fosc11.0592M毫秒级延时
====================================*/
void delay(uint z)
{
uint x,y;
for(x = z; x > 0; x--)
for(y = 114; y > 0 ; y--);
}
/*====================================
函数 :display(uchar i)
参数 :i 显示数值,取值范围0-255
返回值 :无
描述 :三位共阴数码管动态显示
====================================*/
void display(uchar i)
{
static uchar wei;
P0 = 0XFF;//清除断码
WE = 1;//打开位选锁存器
P0 = SMGwei[wei];
WE = 0;//锁存位选数据
switch(wei)
{
case 0: DU = 1; P0 = SMGduan[i / 100]; DU = 0; break;
case 1: DU = 1; P0 = SMGduan[i % 100 / 10]; DU = 0; break;
case 2: DU = 1; P0 = SMGduan[i % 10]; DU = 0; break;
}
wei++;
if(wei == 3)
wei = 0;
}
//定时器0初始化
void timer0Init()
{
EA = 1; //打开总中断
ET0 = 1;//打开定时器0中断
TR0 = 1; //启动定时器0
TMOD = 0X01; //定时器工作模式1,16位定时模式
TH0 = 0xED;
TL0 = 0xFF; //定时5ms
}
void main()//main函数自身会循环
{
timer0Init();//定时器0初始化
while(1)
{
if(key_s2 == 0)//判断S2是否被按下
{
delay(20);//按键消抖
if(key_s2 == 0)
{
if(num != 120)
num++;
while(!key_s2);//松手检测
}
}
if(key_s3 == 0)//判断S3是否被按下
{
delay(20);//按键消抖
if(key_s3 == 0)
{
if(num > 0)
num--;
while(!key_s3);//松手检测
}
}
}
}
//定时器0中断函数
void timer0() interrupt 1
{
TH0 = 0xED;
TL0 = 0xFF; //定时5ms
display(num); //数码管显示函数
}