新闻中心

EEPW首页 > 嵌入式系统 > 设计应用 > 关于单片机的计时器与中断系统

关于单片机的计时器与中断系统

作者: 时间:2016-11-23 来源:网络 收藏
关于嵌入式,我就自己的XXX长话短说一下,说来话就长了,学过Java、JavaWebandsoon,也做过一些东西,说实话成就感还是蛮大的,但是,学过之后却总是感觉这些东西都太浅显了,用另一总话说来,就是感觉学这些东西,站在巨人的肩膀上,却看不到巨人。什么东西都是封装好的,知道怎么用就行了,说实话,确实,学这些东西压力很小,但是总感觉不是真本事。所以生产实习的时候自己报的是Java,但是老是跑到嵌入式的去听课,之前也没听过嵌入式的课,所以是云里雾里。Java的课也没听过几节,最后做东西的时候根本不知道老师叫做个什么东西。对于Java的图形化编程我还是比较讨厌的,没什么技术含量还麻烦的要死,到最后XXX说让我帮帮忙,说实话我是真的不会。生产实习的结果就是最后两头都没捞着好(嵌入式写了一些驱动,但是他们说跟他们用的接口不一样,最后发现老师给的有范例,直接按照上面改就行了,搞的我跟傻X一样,Java直接写都不想写,真不知道那段能两个星期不洗澡的日子是怎么熬过来的)。我自己感觉最后的大体方向就是:底层开发,包括操作系统什么的这一系列开发,当然,扎实的硬件、汇编、语言、算法等基础是少不了的。为什么写这些东西呢?因为在家大冬天的这么冷,手都不想伸出来写代码,只能学学理论,实际实践缺少了很多,所以写下加深印象,也为以后查资料方便(课程设计的时候我的博客真是一大宝库啊!!!:-),下面是以我的51单片机为例说明的)。
下面进入正题:

1、计时器

下面先看一个图:


其中可以清楚的看到,TH0和TL0是两个8位寄存器,这两个寄存器组合成T0加1计数器,所以计数器为16位计数器。同理,TL1和TH1组合成了T1加1计数器。
再看TMOD寄存器,它是一个8位寄存器,名字叫做工作方式寄存器,显然它是控制工作方式的,看电路图上看它有两条灰色的线条延伸到T0和T1,即它可控制T0和T1的工作方式,也可以清楚的看到,其低四位控制T0的工作方式,高4位控制T1的工作方式。
那么什么叫做工作方式呢?工作方式就是指开或者关,芯片是工作在定时模式还是计数模式,和寄存器的使用情况。总体如下:
GATE是门控位,GATE=0时,TCON中TR0/TR1=1时可启动。
GATE=1时,TCON中TR0/TR1=1,且外部中断引脚为高电平时可启动。

C/T=0:定时模式。C/T=1:计数模式。

M1M0组合使用,其值如下:

0013位定时/计数器
0116位定时/计数器
108位自动重装定时/计数器
11T0分成两个定时/计数器,T1停止
关于这四种方式的使用下面再介绍。

再看TCON寄存器,它与TMOD一样,叫做控制寄存器,它是用于控制外部中断启动、申请的一系列工作的。定时器/计数器的工作要依赖于溢出中断,在中断处理中处理相关事件T0或者T1寄存器溢出时会申请中断,然后再处理。比如所以如果想要使用T0计数100,那么就用16位寄存器的溢出值减去初始值,既是计数值,那么就是65536-100=65436(65536-65436=100),所以T0要首先置初始值65436。这里先介绍TCON的高4位。

TF1:T1溢出中断请求标志位,T1溢出时TF1为1,相应中断后自动清0,也可使用软件控制。

TR1:T1运行控制位,TR1为1时,T1工作,TR1为0时,T1停止工作。

TF0:T0溢出中断请求标志位。同理TF1。

TR0:T0运行控制位,同理TR1。

M1M0控制的四种工作方式如下:
0013位计数,使用了TL0的低5位和TH0的8位组成,TH0溢出置TF0中断。
0116位计数,使用T0。同00。
10自动重装初值的8位计数方式。
11T1停止,TH0和TL0分开计数。

计时器使用步骤:
·对TMOD赋值,确定T1、T0的工作方式。
·计算初值,并将其写入T0或者T1。
·对IE(IE后面再介绍)赋值,开放中断。
·使TR0或者TR1置位,启动计数。

二、中断

中断估计都知道是干啥的,这些就不啰嗦了,这里先上一幅图:


看这图估计都没心情,下面剖开来看:


其他的先不看,先看这点图,这是一个中断源,其中IT0是选择中断的方式,IT0=1时为选择下降沿有效,IT0=0时为低电平有效(有一个非门)。当中断触发时就将IE0置1,此时,向CPU申请中断。那么,IT0和IE0是在什么地方呢?
还记得上面讲的TCON吗?现在把低四位也加进去:
TF1TR1TF0TR0IE1IT1IE0IT0
2、




参照1:INT0。
3、


TF0中断,上面已经介绍。
4、


TF1中断,上面已经介绍。
5、(RI或TI中断)


串行口中断请求标志,当串行口接收完一帧串行数据时,置位RI或当串行口发送完一帧串行数据时置位TI,向CPU申请中断。

TCON中断

其中,TCON中的中断标志有如下几个:
TF1TR1TF0TR0IE1IT1IE0IT0
IT0:外部中断0触发方式门控位。
·IT0=0为下降沿有效
·IT0=1为负边沿触发
IE0:外部中断0中断请求标志位。
IT1:外部中断1触发方式控制位(同IT0)。
IE1:外部中断1中断请求标志位。
TF0:T0溢出中断。
TF1:T1溢出中断。

SCON中断

SCON中的中断如下:
TIRI
RI:串行口接收中断标志位。当允许串行口接收数据时,每接收完一个串行帧,由硬件置位RI,同样,RI必须由软件清除。
TI:串行口发送中断标志位。当CPU将一个发送数据写入串行口发送缓冲器时,就启动了发送过程,每发送完一个串行帧,由硬件置位TI,CPU响应中断时,不能自动清除TI,TI必须由软件清除。

下面再看上图的下一部分:


这部分叫做中断允许控制,当一个中断发生时,必须由中断允许控制来检测是否允许,如果允许则转入中断处理,否则不处理。
先看右边的EA,这个是总的中断允许控制位,CPU想要处理中断,必须开此中断允许。
再看左边:
其中各个中断允许控制如下:
·EX0:外部中断0允许位
·ET0:T0中断允许位
·EX1:外部中断1允许位
·ET1:T1中断允许位
·ES:串行口中断允许位
其中,这几个位都是由中断允许寄存器IE控制的,IE寄存器具体如下:
EAESET1EX1ET0EX0

很明了,中断响应条件如下:
1、有中断请求
2、中断源中断允许位为1
3、CPU开中断(EA=1)

其中,8051有两个中断优先级,可以实现二级中断嵌套。每个中断源的中断优先级都是由中断优先级寄存器IP中的相应位的状态来规定的。
IP寄存器如下:


PT2PSPT1PX1PT0PX0
PX0:外部中断0优先级设定位,当将其设置为1时中断优先级较高。
可以按照上图以此类推。
(这里,PT2是80C52的中断,不介绍)
优先级高的中断可以打断优先级低的中断而先执行,实现中断嵌套。那么同一优先级之间不能打断,如果多个同优先级中断同时申请,则按照自然优先级顺序执行中断,自然优先级如下:
中断源中断标志中断服务程序入口优先级顺序
外部中断0IE00003H
T0TF0000BH
外部中断1IE10013H
T1TF1001BH
串行口中断RI或者TI0023H

中断使用方法如下所示:
函数名interruptxusingy
例如:voidfun()interrupt0using1
那么,这里的x代表的是何种中断具体如下:
0:代表外部中断0
1:定时/计数器0
2:外部中断1
3:定时/计数器1
4:串行口中断
这里y代表寄存器组,可取0~7,也可以不写。
例如,外部中断0可以写:
interrupt0using1
外部中断1可写
interrupt2using1


下面看一个使用定时器和中断的数字时钟的例子:

#include

#defineucharunsignedchar

ucharsecond=1; //秒,全局变量
ucharcount=0; //中断次数
ucharcodetab[10]={0xbf,0x86,0xdb,0xcf,0xe6,0xed,0xfd,0x87,0xff,0xe7};

voidDelay(inta){ //延迟
inti;
while(a--){
for(i=0;i<148;i++);
}
}

//次函数表明使用的是定时器,定时为50ms
voidTime0_Init()
{
TMOD=0x01;
//TMOD为00000001 表明定时器处于定时模式,
//且为16位定时器,GATE=0,配合下面TR0=1,则启动寄存器
TH0=0x4c;
//设定初值,即设定定时时间
TL0=0x00;
//设定初值,即设定定时时间
IE =0x82;
//0x82二进制为10000010,即RA=1,ET0=1,
//设定允许响应总中断和T0中断
TR0=1;
//设置TCON中TR0=1,允许T0工作
}

voidTime0_Int()interrupt1 //中断处理
{
TH0=0x4c;
//重新设定初值
TL0=0x00;
count++;
if(count==20){
count=0;
second++;
//时间秒数加1
}
}

ucharkeyscan(){//4×4键盘扫描检测
ucharhang[4]={0xfe,0xfd,0xfb,0xf7};
uchartemp;
inti,j;
for(i=0;i<4;i++){
P1=hang[i];
temp=0x10;
for(j=0;j<4;j++){
if(!(temp&P1)){
returni*4+j;
}
temp<<=1;
}
}
return0;
}

ucharshow_hour(ucharh){ //显示小时
ucharx=h/24;
uchary=h$;
P2=0;
P0=tab[y/10]-0x80;
Delay(1);
P2=1;
P0=tab[y];
returnx;
}

ucharshow_minute(ucharm){ //显示分钟
ucharx=m/60;
uchary=m`;
P2=3;
P0=tab[y/10]-0x80;
Delay(1);
P2=4;
P0=tab[y];
returnx;
}

ucharshow_second(uchars){ //显示秒
ucharx=s/60;
uchary=s`;
P2=6;
P0=tab[y/10]-0x80;
Delay(1);
P2=7;
P0=tab[y];
returnx;
}

voidmain()
{
ucharhour=1,minute=1;
ucharh,m,s;
uchark,mk=0;
Time0_Init();
while(1){
Delay(1);
if(mk==0){
s=show_second(second);
second%=60;
mk++;
}
elseif(mk==1){
minute+=s;
m=show_minute(minute);
minute%=60;
mk++;
}else{
hour+=m;
h=show_hour(hour);
hour%=24;
mk++;
mk=mk%3;
}
k=keyscan();//检测按键
if(12==k||13==k||14==k){
Delay(50);
k=keyscan(); //软件消抖
if(12==k||13==k||14==k){
if(12==k)hour++;
elseif(13==k)minute++;
elseif(14==k)second++;
}
}
}
}



评论


技术专区

关闭