三、功能模块
4、中断系统
中断系统
 1)中断的概念:这是大家需要在脑子里有的一个印象,我们大致可以将中断描述成CPU在处理某一事件A时,发生了另一事件B请求CPU迅速去处理(中断发生);CPU暂时中断当前的工作,转去处理事件B(中断响应和中断服务);待CPU将事件B处理完毕后,再回到原来事件A被中断的地方继续处理事件A(中断返回),这一过程我们称为中断。
 2)中断源:我们将引起CPU中断的根源或原因,称为中断源。中断源向CPU提出的处理请求,称为中断请求或中断申请。(传统的51单片机有5个中断源,而我们使用的IAP15有14个中断源)
 3)使用中断我们一定要知道中断优先级和中断嵌套这两个概念。
 ①中断优先级:当有几个中断源同时申请中断时,那么就存在CPU先响应哪个中断请求的问题?为此,CPU要对各中断源确定一个优先等级,称为中断优先级。中断优先级高的中断请求优先响应。
 ②中断嵌套:中断优先级高的中断请求可以中断CPU正在处理的优先级更低的中断服务程序,待完成了中断优先权高的中断服务程序之后,再继续执行被打断的优先级低的中断服务程序,这就是中断嵌套。
我们使用的IAP15F2K61S2单片机的中断系统有14个中断源,2个优先级,可实现二级中断服务嵌套。由片内特殊功能寄存器中的中断允许寄存器IE、IE2、INT_CLKO控制CPU是否响应中断请求;由中断优先级寄存器IP、IIP2安排各中断源的优先级;同一优先级内2个以上中断同时提出中断请求时,由内部的查询逻辑确定其响应次序。
中断请求标志:AP15F2K61S2单片机的10个中断源的中断请求标志分别寄存在TCON、SCON、PCON、S2CON、ADC_CONTR、SPSTAT、CCON中,其中,外部中断2(/INT2)、外部中断3(/INT3)和外部中断4(/INT4)的中断请求标志位被隐藏起来了,对用户是不可见的。当相应的中断被响应后或(EXn)=0(n=2、3、4),这些中断请求标志位会自动被清0;定时器T2的中断请求标志位也被隐藏起来了,对用户是不可见的,当T2的中断被响应后或(ET2)=0,这些中断请求标志位会自动被清0。
 IAP15F2K61S2单片机的中断请求
中断允许的控制:计算机中断系统有两种不同类型的中断:一类称为非屏蔽中断,另一类称为可屏蔽中断。IAP15F2K61S2单片机的14个中断源都是可屏蔽中断,其中断系统内部设有3个专用寄存器(IE、IE2、INT_CLKO)用于控制CPU对各中断源的开放或屏蔽。
中断优先的控制:IAP15F2K61S2单片机除外部中断2(/INT2)、外部中断3(/INT3)、定时器T2中断和外部中断4(/INT4)为固定最低优先级中断外,其他中断都具有2个中断优先级,可实现二级中断服务嵌套。IP、IP2为中断优先级寄存器,锁存各中断源优先级控制位。
中断源自然优先级从高到低排序
 中断源
 外部中断0
 定时器T0
 中断
 外部中断1
 定时器T1中断
 串行口中断
 A/D转换中断
 LVD中断
 PCA中断
 串行口2中断
 SPI中断
 外部中断2
 外部中断3
 定时器T2中断
 外部中断4
 IAP15F2K61S2单片机中断响应
 中断响应:是CPU对中断源中断请求的响应,包括保护断点和将程序转向中断服务程序的入口地址(通常称矢量地址)。CPU并非任何时刻都响应中断请求,而是在中断响应条件满足之后才会响应。
 中断服务与中断返回:中断服务与中断返回就是通过执行中断服务程序完成的。中断服务程序从中断入口地址开始执行,到返回指令“RETI”为止,一般包括四部分内容,其结构是:保护现场,中断服务,恢复现场、中断返回。
 保护现场:用入栈操作指令将需保护寄存器的内容压入堆栈。
 中断服务:中断服务程序的核心部分,是中断源中断请求之所在。
 恢复现场:在中断服务结束之后,中断返回之前,用出栈操作指令将保护现场中压入堆栈的内容弹回到相应的寄存器中,注意弹出顺序必须与压入顺序相反。
 中断返回:中断返回是指中断服务完成后,计算机返回原来断开的位置(即断点),继续执行原来的程序。中断返回由中断返回指令RETI来实现。
5、定时器/计数器(Timer0/1)
定时器(Timer0/1)
 首先我们需要知道定时器Timer需要使用的寄存器有哪些:
我们先来简单的说一下TMOD、 TCON两个寄存器的功能和区别:
 1)TMOD:就是运行模式配置寄存器,也就是配置定时器是工作在定时器状态还是计数器状态,以及定时器的工作模式是13位定时器,16位定时器,双8位定时器自动重装,还是双8位定时器等等。
 2)TCON:就是定时/计数器的控制寄存器,包括控制定时器的的启动与停止,是否允许外部的中断请求判断定时器是否已经溢出等等。
 简而言之,一句话2个寄存器 一个是配置模式功能 一个是控制功能,具体的运用理解还需要大家多看多用。
 定时器/计数器0/1控制寄存器TCON
 TCON为定时器/计数器T0、T1的控制寄存器,同时也锁存T0、T1溢出中断源和外部请求中断源等,TCON格式如下:
 TCON为定时器/计数器中断控制寄存器(可位寻址)
各寄存器功能
定时器/计数器0/1工作模式寄存器TMOD
 定时和计数功能由特殊功能寄存器TMOD的控制位C/T进行选择,TMOD寄存器的各位信息。
可以看出,2个定时/计数器有4钟操作模式,通过TMOD的M1和M0选择。2个定时/计数器模式0、1和2都相同,模式3不同,各模式下的功能请参见datasheet P593。
 定时器0/1的中断控制寄存器:IE和IP
 IE:中断允许寄存器(可位寻址)
EA:CPU的总中断允许控制位,EA = 1,CPU开放中断,EA = 0,CPU屏蔽所有的中断申请。
 ET1:定时/计数器T1的溢出中断允许位,ET1 = 1,允许T1中断,ET1 = 0禁止T1中断。
 ET0:同上。
 IP:中断优先级控制寄存器(可位寻址)
PT1: 定时器1中断优先级控制位。
 当PT1 = 0时,定时器1中断为最低优先级中断(优先级0)
 当PT1 = 1时,定时器1中断为最高优先级中断(优先级1)
 PT0: 定时器1中断优先级控制位。
 当PT0 = 0时,定时器0中断为最低优先级中断(优先级0)
 当PT0 = 1时,定时器0中断为最高优先级中断(优先级1)
 注意事项
辅助寄存器AUXR
 普通8051单片机是12T的机器周期,STC15的是1T的这个辅助寄存器的功能就是用来选择STC15工作在1T还是12T的模式下。
定时器/计数器0工作模式
 通过对寄存器TMOD中的M1(TMOD.1)、(TMOD.0)的设置,定时器/计数器0有4种不同的工作模式。
 模式0(16位自动重装载模式)(只介绍该模式,其余三种模式见datasheet)
定时器/计数器0工作模式0(16位自动重装载模式)
 M1和M0:方式选择控制位
 C/ T:功能选择位。
 1:计数器功能(对T0或T1引脚的负跳变进行计数)。
 0:定时器功能(对时钟周期进行计数)。
 GATE:门控位。GATE用于选通控制。
 1:INTX为高电平且TRX置位时,启动定时器工作。
 0:每当TRX置位时,就启动定时器工作。
 注意:TMOD寄存器不能进行位寻址,设置时只 能对整个寄存器赋值。
 IAP15F2K61S2的定时器有两种计数速率:一种是12T模式,每12个时钟加1;另一种是1T模式,每个时钟加1。
 T0和T1的速率分别由特殊功能寄存器AUXR中的T0x12和T1x12决定。
 T0x12=0,T0工作在12T模式;
 T0x12=1,T0工作在1T模式。
 T1x12=0,T1工作在12T模式;
 T1x12=1,T1则工作在1T模式。
 定时器0和定时器1分别有2个隐藏的寄存器RL_THx和RL_TLx。RL_THx与THx共有同一个地址,RL_TLx与TLx共有同一个地址。当TRx=0即定时器/计数器被禁止工作时,对TLx、THx写入的内容会同时写入RL_TLx、RL_THx。 当TRx=1即定时器/计数器工作时,对TLx 、THx写入的内容不会写入RL_TLx 、RL_THx。
 当定时器工作在模式0时,[TLx,THx]的溢出 不仅置位TFx,而且会自动将[RL_TLx,RL_THx] 的内容重新装入[TLx,THx]。
 当T0CLKO=1时,T1/P3.5引脚配置为定时器0的时钟输出CLKOUT0。
 当T1CLKO=1时,T0/P3.4引脚配置为定时器1的时钟输出CLKOUT1。
 详细介绍
定时器的最大定时能力
 当工作于定时状态时,定时/计数器是对时钟周期进行计数,若对时钟进行12分频,则对每12个时钟周期计数一次。
 当晶振频率为6MHz,采用12分频时,计数的单位时间为:
 单位时间为:
 定时时间为: TC=XTu。
 其中,Tu为单位时间,TC为定时时间,X为所需计数次数。
 STC15F2K60S2单片机的定时/计数器是加1计数器。因此,不能直接将实际的计数值作为计数初值送入计数寄存器THX、TLX中,而必须将实际计数值以28、216为模求补,以补码作为计数初值设置THX和TLX。
 即应装入计数/定时器的初值为:,其中,n=8或16。
 例如:已知Tu=2μs,要求定时TC=1ms,则
 对方式0和方式1,时间常数为:216-500=65036=FE0CH (THX装入FEH,TLX装入0CH)。
 设系统时钟频率为6MHz,12分频时,
 8位定时器的最大定时能力为:T=(28 -0)×2μs=512μs
 16位定时器的最大定时能力为:T=(216 -0)×2μs=131072μs=131.072ms
 最后补充一下,初学阶段对定时器的使用可以直接使用我第一章中所讲的方法直接自动生成所需定时器,用以程序使用,但是还是希望大家理解最重要。
 以上是定时器的原理,接下来我们简单运用定时器来写个控制LED量灭的程序:
#include “stc15f2k60s2.h”#define LED P0unsigned char t;void Timer0Init(void);void main()
{P2 = ((P2&0x1f)|0xA0); //不再重复解释,前两章已解释多次P0 = 0X00;P2 = ((P2&0x1f)|0x80);P0 = 0Xff;//先关闭LEDTimer0Init();EA = 1; //打开定时器0ET0 = 1;while(1){if(t == 200){LED = ~LED;t = 0;//初始化标志位}}
}void Timer0Init(void)//5毫秒@11.0592MHz
{AUXR |= 0x80;//定时器时钟1T模式TMOD &= 0xF0;//设置定时器模式TL0 = 0x00;//设置定时初值TH0 = 0x28;//设置定时初值TF0 = 0;//清除TF0标志TR0 = 1;//定时器0开始计时
}void Timer0() interrupt 1 //定时器0的开启中断1
{t++;
}6、静态数码管和动态数码管
共阴/共阳数码管
 使用的是两个4位八段共阳数码管。
 
八段数码管:八个发光二极管组成的如上图的结构形态,
 共阴数码管:八个LED阴极相连组成公共端接GND,
 共阳数码管:八个LED阳极相连组成公共端接VCC。
 4位八段数码驱动原理:其中八个管脚作为(A,B,C,D,E,F,G,DP)的驱动端口,选择哪一段数码管亮或不亮,我们称之为数码管段选,还有4个管脚作为选择四位数码管中的哪一位亮或不亮的驱动端口,也就是四个公共端,我们称之为数码管位选。
 驱动原理
图中a1、b1……dp1就是数码管的段选端口,com1、com2……com8就是数码管的位选端口,我们可以看到,蓝桥杯开发板的数码管段选和位选都是通过单片机的P0寄存器来驱动的,但是因为P0不能同一时刻既做段选又做位选,所以这里就要在一个很短的时间内在段选和位选之间切换选择操作。
 静态数码管数码管所有公共端接GND或VCC,在该开发板上不是接GND和VCC而是接在两个573三态锁存器上,全亮全灭不单独选择端口操作。优点:不闪烁,亮度高。缺点:占用端口多。
 示例程序:
/******************//* 数码管程序示例 *//******************/
#include “stc15f2k60s2.h”
#define uchar unsigned char
#define uint unsigned intuchar code tab[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xbf,0xff};
/*这里如果不加code,就会占用寄存器,允许读取和修改寄存器的值,
但是寄存器的数量有限,故我们在这里加上code,将其定义为代码的
类型,只能读取不能修改*/
uchar t = 0,i;void Timer0Init(void);void main()
{P2 = ((P2&0x1f)|0xa0);//不再重复解释,前两章已解释多次P0 = 0x00;P2 = ((P2&0x1f)|0xc0);//打开位选选573P0 = 0xff;//选择8位数码管P2 = ((P2&0x1f)|0xff);//打开段选573Timer0Init();EA=1;ET0=1;while(1){if(t == 100){t = 0;P0 = tab[i];i++;if(i == 12)i = 0;}}
}void Timer0Init(void)//5毫秒@11.0592MHz
{AUXR |= 0x80;//定时器时钟1T模式TMOD &= 0xF0;//设置定时器模式TL0 = 0x00;//设置定时初值TH0 = 0x28;//设置定时初值TF0 = 0;//清除TF0标志TR0 = 1;//定时器0开始计时
}void Timer0() interrupt 1
{t++;
}动态数码管
 数码管公共端依次扫描接通。优点:占用端口少。缺点:摄像,拍照时闪烁,在同样电压下与静态相比,亮度较低。示例程序:
#include "stc15f2k60s2.h"
#include "intrins.h"#define uchar unsigned char
#define uint unsigned intuchar code tab[] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xbf,0x7f};
/*这里如果不加code,就会占用寄存器,允许读取和修改寄存器的值,
但是寄存器的数量有限,故我们在这里加上code,将其定义为代码的
类型,只能读取不能修改*/
uchar t = 0;
uchar c5, c6, c7, c8;
uint num = 2330;void Timer0Init(void);
void display(uchar c5, uchar c6, uchar c7, uchar c8);
void Delay1ms();void main()
{P2 = ((P2&0x1f)|0xa0);//不再重复解释,前两章已解释多次P0 = 0x00;Timer0Init();EA=1;ET0=1;while(1){if(t == 200){t = 0;num--;c5 = (num / 1000) % 10;c6 = (num / 100) % 10;c7 = (num /10) % 10;c8 = num % 10;}display(c5, c6, c7, c8);}
}void display(uchar c5, uchar c6, uchar c7, uchar c8)
{P2 = ((P2&0x1f)|0xc0);P0 = 0x80;P2 = ((P2&0x1f)|0xff);P0 = tab[c8];Delay1ms();P2 = ((P2&0x1f)|0xc0);P0 = 0x40;P2 = ((P2&0x1f)|0xff);P0 = tab[c7];Delay1ms();P2 = ((P2&0x1f)|0xc0);P0 = 0x20;P2 = ((P2&0x1f)|0xff);P0 = tab[c5];Delay1ms();P2 = ((P2&0x1f)|0xc0);P0 = 0x10;P2 = ((P2&0x1f)|0xff);P0 = tab[c6];Delay1ms();
}定时器中断实现实时时钟程序
使用单片机定时器制作一个以00-00-00模式显示,可以通过四个独立按键进行时间修改的24小时制时钟。
 定时器、中断、数码管、按键等功能
#include "stc15f2k60s2.h" #define uchar unsigned char 
#define uint unsigned sbit key1=P3^0;//定义端口参数
sbit key2=P3^1;
sbit key3=P3^2;
sbit key4=P3^3;
/*key1用于小时 +1key2用于小时 -1key3用于分钟 +1key4用于分钟 -1
*/int uchar code tab[] = 
{0xc0,0xf9,0xa4,0xb0,0x99,0x92, 
0x82,0xf8,0x80,0x90,0xbf,0x7f}; 
uchar strtab[8];//定义缓冲区域
uchar hour = 10, minute = 25, second = 12;
//初始化起始时间10-25-12void display(void);
void delayms(int ms);
void Timer0Init(void);
void Timer1Init(void);void main()
{P2 = ((P2&0x1f)|0xa0);P0 = 0x00;Timer0Init();Timer1Init();ET0 = 1;//开启定时器0ET1 = 1;//同上EA = 1;//定时器使能display();while(1){if (!key1){delayms(5);//按键消抖if (!key1){hour++;if (hour == 24)hour = 0;display();while(!key1);//松手检测}}//其余三个按键功能请自行编写,如遇问题请下载参考源程序}
}void display(void)
{strtab[0] = tab[hour / 10];strtab[1] = tab[hour % 10];strtab[2] = tab[10];strtab[3] = tab[minute % 10];strtab[4] = tab[minute % 10];strtab[5] = tab[10];strtab[6] = tab[second / 10];strtab[7] = tab[second % 10];
}void delayms(int ms)
{int i,j;for(i=ms;i>0;i--)for(j=845;j>0;j--);
}void Timer0Init(void) //5毫秒@11.0592MHz
//内容请自行计算void Timer1Init(void) //1毫秒@11.0592MHz
//内容请自行计算void Timer1_int() interrupt 3 using 0
{static uchar num;TH1 = 0xcd;TL1 = 0xd4;switch(num)//分别调用缓冲区的值进行扫描{case 0: P2 = ((P2&0x1f)|0xc0); P0 = 0x01;P2 = ((P2&0x1f)|0xff); P0 = strtab[num];break;case 1: P2 = ((P2&0x1f)|0xc0); P0 = 0x02;P2 = ((P2&0x1f)|0xff); P0 = strtab[num];break;case 2: P2 = ((P2&0x1f)|0xc0); P0 = 0x04;P2 = ((P2&0x1f)|0xff); P0 = strtab[num];break;case 3: P2 = ((P2&0x1f)|0xc0); P0 = 0x08;P2 = ((P2&0x1f)|0xff); P0 = strtab[num];break;case 4: P2 = ((P2&0x1f)|0xc0); P0 = 0x10;P2 = ((P2&0x1f)|0xff); P0 = strtab[num];break;case 5: P2 = ((P2&0x1f)|0xc0); P0 = 0x20;P2 = ((P2&0x1f)|0xff); P0 = strtab[num];break;case 6: P2 = ((P2&0x1f)|0xc0); P0 = 0x40;P2 = ((P2&0x1f)|0xff); P0 = strtab[num];break;case 7: P2 = ((P2&0x1f)|0xc0); P0 = 0x80;P2 = ((P2&0x1f)|0xff); P0 = strtab[num];break;default:break;}num++;if (num == 8)num = 0;
}
void Timer0_int() interrupt 1 using 1
{static uchar count;//定义内部静态变量TL0 = 0x00; //初始化初值TH0 = 0x28;<count++;switch (count);{case 0:case 40:case 80:case 120:case 160:display();break;}if (count == 200){count = 0;second++;//加一秒if (second == 60){second = 0;minute++;//加一分if (minute == 60){minute = 0;hour++;//加一小时if (hour == 24)hour = 0;}}}
}
习题:使用单片机定时器制作一个以00-00-00为初始可以通过四个独立按键进行时间修改的24小时制时钟。