1.中断系统
1.中断系统是为使CPU具有对外界紧急事件的实时处理能力而设置的;当中央处理机CPU正在处理某件事的时候外界发生了紧急事件请求,要求CPU暂停当前的工作,转而去处理这个紧急事件,处理完以后,再回到原来被中断的地方,继续原来的工作,这样的过程称为中断。实现这种功能的部件称为中断系统,请示CPU中断的请求源称为中断源。微型机的中断系统一般允许多个中断源,当几个中断源同时向CPU请求中断,要求为它服务的时候,这就存在CPU优先响应哪一个中断源请求的问题。通常根据中断源的轻重缓急排队,优先处理最紧急事件的中断请求源,即规定每一个中断源有一个优先级别。CPU总是先响应优先级别最高的中断请求。
2.定时器
1.作用
(1)用于计时系统,可实现软件计时,或使程序每隔一段时间完成完成一项操作。
(2)替代长时间的Delay,提高CPU的运行效率和处理速度。
2.个数
传统的STC89C52单片机的定时器只有T0和T1两种定时器。
3.模式
STC89C52单片机的T0与T1均有4种模式
(模式0):13位定时器/计数器
(模式1):16位定时器/计数器(常用)
(模式2):8位自动重装模式
(模式3):两个8位计数器
4. 定时器如何定时?
原理:每经过一个机器周期计数器就+1:寄存器。
晶振:是数字电路的“心脏”,所有工作都离不开时钟。
时钟周期:晶振上面写的频率的倒数,一般是11.0592MHZ,也就是1/11.0592,时钟周期相当于人类时间的秒,时钟周期是最小的时间单位。
机器周期:也称CPU周期,为了方便管理,常把一条指令的执行过程划分为诺干个阶段(如取指,译码,执行等),每一阶段完成一个基本操作所需的时间称为机器周期,一般情况下,一个机器周期由诺干个时钟周期组成。
加1经历的时间:
(1)11.0592MHZ=11059.2KHZ=11059200HZ;
(2) 跳一次的时间等于一个机器周期;
(3)一个机器周期等于12个时钟周期(T就是时钟周期;计速频率:时钟模式分为12T和6T,这里我们选用12T)————也就是每过12时钟周期,就+1,也就是机器周期+1;
(4)一个时钟周期等于晶振的倒数:1/11059200s;
(5)跳一下=12/11059200s=12000000/11059200us=1.085us(1s=1000ms,1ms=1000us,1s=1000000us)
5.定时器与中断系统有什么关联?
(1)像闹钟一样,,根据时钟的输出信号,每隔一秒,计数单元的数字增一,当计数单元数值增加到设定的提醒时间时,计数单元会自动向系统发出中断申请,产生闹铃提醒,使程序跳转到中断服务函数去执行。
(2)TF0或者TF1是计时器溢出标志,TR0或者TR1是计时器开始计时标志位,爆表后如果是定时器T0,TCON的bit5——TF0会置1,如果是定时器1,TCON的bit7——TF1会置1,如果是定时器查询的方式(无中断),需要手动用软件清0,如果定时器爆表用硬件中断方式,则不需要手动清0。
6.如何设置定时10ms?(定时器0模式1—16位)
10ms=10000us 10000us/1.085us=9216下
定时器的初值不让寄存器从0数到9216下,因为这样子想设置10ms就爆表然后中断,设置不了,因为要到65536下才爆表,所以让寄存器从65535-9216=56320下开始数起,51单片机是向上计数法,从56320一直+1到65535然后爆表就中断,56320换成十六进制就是0xDC00,TH0和TL0是高八位和低八位的意思,他们两个组成就是十六位,也就是0xDC00,TH0和TL0是记录定时器初值的,也就是记录一开始从第几下开始跳的;所以TH0=0xDC,TL0=0x00。
7.定时器模式
按位操作时:(1)需要清0则位逻辑与0,不需要的不会改变则位逻辑与1。
(2)需要置1则位逻辑与1,不需要的不会改变则位逻辑或0。
GATE:门控位,当GATE=0时,TCON寄存器中的TR0或者TR1为1,才能启动定时器工作。当GATE为1时,TCON寄存器中的TR0或者TR1为1,且需要外部引脚为高电平,定时器才能工作。
C/T:为0时为定时器模式,为1时为计时模式。
M1,M0:选定工作方式,一般为16位计时计数器。
8.定时器模式配置
假设现在需要用到定时器0,稍后在用定时器1,为了不影响定时器1的值,所以不能改变定时器1的值,则TMOD&=0xF0(1111 0000),TMOD|=0x01。
三.中断系统打开配置
由图可知,定时器0触发条件是溢出(爆表),中断开关ET0=1时则打开,ET0=0时则关闭,总中断开关EA=1时则打开,EA=0时则关闭(打开就是开关闭合);外部中断0触发的条件是:当IT0=0时,单片机的IO口则由低电平触发,当IT0=1时,单片机的IO口则由低电平触发,单片机上面会标注外部中断的IO口。
四.定时器控制led亮灭(无中断)——查询方式(软件)
#include <REGX52.H>
sbit led=P2^4;
void Time0Init(void)
{
//配置定时器0工作模式16位计时
TMOD&=0xF0;
TMOD|=0x01;
//给初值,定制10ms
TL0=0x00;
TH0=0xDC;
TR0=1;//开始计时
TF0=0;
}
void main()
{
int cnt=0;
led=1;
Time0Init();
while(1)
{
if(TF0==1)//爆表时硬件会修改bit5(TF0)位上面的数据,改成1
{
TF0=0;//软件清0,因为不用中断
cnt++;//cnt++用来记录第一次爆表,cnt是1次
//重新给初值
TL0=0x00;
TH0=0xDC;
if(cnt==100)//爆表100次,经过1s,led就亮
{
cnt=0;//当1s时,让count从0开始,计算下一次的1s
led=!led;//每经过1s,翻转led状态
}
}
}
}
五.定时器中断方式控制led亮灭——硬件
#include <REGX52.H>
sbit led=P2^4;
int cnt=0;
void Time0Init(void)
{
//配置定时器0工作模式16位计时
TMOD&=0xF0;
TMOD|=0x01;
//给初值,定制10ms
TL0=0x00;
TH0=0xDC;
TR0=1;//开始计时
TF0=0;
//打开定时器0中断
ET0=1;
//打开总中断
EA=1;
}
void main()
{
led=1;
Time0Init();
while(1)
{
//当在看电视的时候,如果爆表就中断跳到TimeRountime() interrupt 1中,然后重新初值以后又回到了这里,
}
}
void Time0Rountime() interrupt 1
{
cnt++;//cnt++用来记录第一次爆表,cnt是1次
//重新给初值
TL0=0x00;
TH0=0xDC;
if(cnt==100)//爆表100次,经过1s,led就亮
{
cnt=0;//当1s时,让count从0开始,计算下一次的1s
led=!led;//每经过1s,翻转led状态
}
}
六.震动传感器通过外部中断方式控制led亮灭
//由震动传感器通过外部中断控制led亮灭,震动传感器DO接单片机P3^2口
#include <REGX52.H>
#include <INTRINS.H>
sbit led4=P2^3;
void Delay1000ms() //@11.0592MHz
{
unsigned char i, j, k;
_nop_();
i = 8;
j = 1;
k = 243;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void EX0_init()
{
EA=1;
EX0=1;
IT0=0;//低电平触发,震动时就是低电平
}
void main()
{
EX0_init();
while(1)
{
led4=1;
}
}
void EX0_Rountime() interrupt 0
{
led4=0;
Delay1000ms();
}
七.按键通过外部中断方式控制led亮灭
//51普中开发板按键k3和k4连着单片机IO口P3^2和P3^3,也就是外部中断口IT0和IT1
#include <REGX52.H>
sbit led4=P2^3;
void EX0_init()
{
EA=1;
EX0=1;
IT0=0;//低电平触发,按键按下时就是低电平
}
void EX1_init()
{
EA=1;
EX1=1;
IT1=0;//低电平触发,按键按下时就是低电平
}
void main()
{
EX0_init();
EX1_init();
while(1);
}
void EX0_Rountime() interrupt 0//按下k3
{
led4=0;
}
void EX1_Rountime() interrupt 2//按下k4
{
led4=1;
}
//按键按下k3,led亮,一秒后led灭
#include <REGX52.H>
#include <INTRINS.H>
sbit led4=P2^3;
void EX0_init()
{
EA=1;
EX0=1;
IT0=0;//低电平触发,按键按下时就是低电平
}
void Delay1000ms() //@11.0592MHz
{
unsigned char i, j, k;
_nop_();
i = 8;
j = 1;
k = 243;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void main()
{
EX0_init();
while(1)
{
led4=1;//灭
}
}
void EX0_Rountime() interrupt 0//按下k3亮一s就灭
{
led4=0;
Delay1000ms();
}
八.定时器做闹钟(LCD1602显示)
#include <REGX52.H>
#define LCE_Dataport P0
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_EN=P2^7;
unsigned char Sec,Min,Hour;
void LCD_WriteCommand(unsigned char Command)//写指令
{
LCD_RS=0;
LCD_RW=0;
LCE_Dataport=Command;
LCD_EN=1;
Delay(1);
LCD_EN=0;
Delay(1);
}
void LCD_WriteData(unsigned char Data)//写数据
{
LCD_RS=1;
LCD_RW=0;
LCE_Dataport=Data;
LCD_EN=1;
Delay(1);
LCD_EN=0;
Delay(1);
}
void LCD_Init(void)//初始化
{
LCD_WriteCommand(0x38);
LCD_WriteCommand(0x0C);
LCD_WriteCommand(0x06);
LCD_WriteCommand(0x01);
}
void LCD_Setcursor(unsigned char line,unsigned char column)
{
if(line==1)//设置光标位置
{
//第一列是数字1,但是位置是0x00,要把数字1转换0x00,就把数字1-1就0,所以转十六进制就是0x00
LCD_WriteCommand(0x80|(column-1));
}
else
{
//第二行的第一列是从0x40开始,第二列是0x41,相当于0x40+0x01,0x01就是与上面一致
LCD_WriteCommand(0x80|(column-1)+0x40);
}
}
void LCD_Showstring(unsigned char line,unsigned char column,unsigned char *string)//line行,column列,string字符串
{//显示字符串,字符串直接等于指针,不用数组形式等于字符串,在等于指针形式,那传给指针的就是字符串的第一个字符的地址,然后string【i】一直往下就可以知道字符串,数组是连续的,如果baby的b在第一行第一列,那a就会在第一行,第二列。
unsigned char i;
LCD_Setcursor(line,column);
for(i=0;string[i]!='\0';i++)
{
LCD_WriteData(string[i]);//字符串本身就是地址,所以传进来的字符串用指针string来接
}
}
int LCD_Pow(int X,int Y)
{
unsigned char i;
int Result=1;
for(i=0;i<Y;i++)
{
Result*=X;
}
return Result;
}
void LCD_Shownum(unsigned char line,unsigned char column,unsigned int number,unsigned char length)
{
unsigned char i;
LCD_Setcursor(line,column);
for(i=length;i>0;i--)
{
LCD_WriteData('0'+number/LCD_Pow(10,i-1) %10);
}
}
void main()
{
LCD_Init();
Timer0_Init();
LCD_Showstring(1,1,"Clock");
LCD_Showstring(2,1," : :");
while(1)
{
LCD_Shownum(2,1,Hour,2);
LCD_Shownum(2,4,Min,2);
LCD_Shownum(2,7,Sec,2);
}
}
void Time0_Rountime() interrupt 1
{
static unsigned int count=0;//不加static的话函数调用后退出时自动销毁
count++;//爆表一次就中断一次
TH0=0xDC;
TL0=0x00;
if(count==100)
{
count=0;
Sec++;
if(Sec>=60)
{
Sec=0;
Min++;
if(Min>=60)
{
Min=0;
Hour++;
if(Hour>=24)
{
Hour=0;
}
}
}
}
}
补充知识:_nop_()是延时函数,一个nop延时就是一个机器指令周期,延时的时间由晶振决定,如果晶振是11.0592MHZ,那么一个nop就是1.085us,如果是12MHZ,一个nop约为1us。