在接下来的学习中,将会介绍定时器、串口通信等外设的基本使用,而这些外设的使用都要涉及中断,而且中断是单片机开发中一个相当重要的概念。不能掌握中断系统,就无法灵活应用这些外设。
你正在追电视剧,正看得入迷的时候,电话响了,你暂停电视剧,去接电话,在接电话的过程中,门铃又响了,你暂时放下电话,去把门打开。如果追电视剧是在执行主程序,那么电话就是中断源,电话铃响了就是中断请求,暂停电视就是现场保护,接电话就是中断响应,门铃响了是更高一级的中断请求,去把门打开,那就是中断嵌套。开完门回来接着聊电话,那是中断返回,接完电话把电视剧暂停打开是现场恢复。
内核与外设之间的主要交互方式有两种:轮询和中断。轮询的方式看似公平,但实际的工作效率很低,且不能及时响应紧急事件;而中断系统使得内核具备了应对突发事件的能力。
中断有个特点,就是你不知道什么时候中断发生。因此,每个中断都需要有一个中断入口地址,也称为中断向量。这样,不管中断在什么时候发生,它都有一个确定的程序执行起始点。中断响应之后,执行的那段程序,我们称作中断服务函数,也就是这个函数专门是为该中断服务的。
一般来说,51单片机有5个中断源(忽略定时/计数器2),分两个优先级,这5个中断源按照自然优先级从高到低依次为:
外部中断0:INT0
定时/计数器0:TF0
外部中断1:INT1
定时/计数器1:TF1
串口中断:RI/TI
下面一图将充分说明51单片机的中断系统结构:
每个中断源都对应着一个固定的入口地址,也就是中断向量,它们依次是:
0 0x0003: INT0
1 0x000B: TF0
2 0x0013: INT1
3 0x001B: TF1
4 0x0023: RI/TI
也就是说,不管主程序执行到什么地方,只要外部中断1产生请求,内核要响应该中断,就会到0x0013这个地址去执行代码。如果你是使用汇编语言进行程序开发,需要记住每个中断源对应的地址;如果使用的是c语言,只需要记住中断源的顺序就可以了,也就是最左边的中断号。
中断相关的寄存器有4个,每个寄存器都是可以位寻址的,这给编程带来了方便。其中2个为控制寄存器,IE寄存器与IP寄存器:
另外2个为中断请求标志:TCON寄存器与SCON寄存器:
一般情况下。中断的处理函数有两个,其一为中断初始化函数,其二为中断服务函数。初始化函数就是一个普通的函数,而中断服务函数却有特殊的格式要求:
1.中断函数没有返回值,也不能带参数。
2.函数名之后要跟一个关键字interrupt,说明这是一个中断服务函数。
3.在关键字interrupt后面要跟上中断号,说明这个中断服务函数是为哪个中断服务的。
中断服务函数的格式为:
void 函数名() interrupt 中断号
{ 函数体 }
如果我们要利用定时器0进行间隔定时,中断程序架构我们可以这样写:
定时/计数器,是一种能够对内部时钟信号或外部输入信号进行计数,当计数值达到设定要求时,向CPU提出中断处理请求,从而实现定时或者计数功能的外设。定时/计数器的最基本工作原理是进行计数。作为定时器时,计数信号的来源选择周期性的内部时钟脉冲;用作计数器时,计数信号的来源选择非周期性的外部输入信号。
不管是定时器还是计数器,本质上都是计数器。
51单片机有两个定时/计数器T0和T1,为16位加法计数器,由低8位TLx和高8位THx两个寄存器组成,最大计数值为65535个计数脉冲。
该加1计数器的计数脉冲来源有两个:
<1> 系统时钟振荡器输出的12分频。
<2> T0或T1引脚输入的外部脉冲信号。
每接收到一个计数脉冲,计数器就会加1,当计数值累计至全为1时(8位255,13位8191,16位65535),再输入一个计数脉冲,计数器便会溢出回零,并且计数器的溢出是TCON寄存器的TF0或TF1位置1,同时向内核提出中断请求。如果定时/计数器工作于定时模式,则表示间隔定时时间到,如果工作与计数模式,则表示计数值已满。
假设单片机的外部晶振为12MHz,那么,经过12分频后输入计数器的计数脉冲为1MHz,即每个脉冲的周期为1us。因此定时器T0的16位工作模式最大的定时时间为65535us,65.5ms。如果要定时10ms的话,计数器就不能够从0开始计数了,必须给它一个计数初值。怎么计算这个初值呢?
要定时10ms,则相当于计数10000个脉冲后计数器的值就到达65535了,那么开始计数的这个地方就是计数初值。
65535 - 10000 = 55535 = 0xd8ef
把这个计算得到的初值写入TH0和TL0寄存器即可:
TH0 = 0xd8;或者 TH0 = (65535 - 10000) / 256;
TL0 = 0xef; 或者 TL0 = (65535 - 10000) % 256;
与定时/计数器相关的寄存器除了计数初值寄存器THx和TLx之外,就是TMOD寄存器和TCON寄存器。
1.TMOD模式控制寄存器,不能进行位寻址,只能字节操作。
2.TCON中断标志寄存器
在定时/计数器的程序设计中,通常有两个函数:初始化函数和中断服务函数。
在初始化函数中,一般需要进行以下几个配置:
<1> 配置工作模式,即对TMOD寄存器编程。
<2> 计算技术初值,即对THx和TLx寄存器进行赋值。
<3> 使能定时/计数器中断,即ET0或ET1置1。
<4> 打开总中断,即EA =1。
<5> 启动定时器,即TR0或TR1置1。
在中断服务函数中,一般需要进行以下的编程:
<1> 如果不是自动重装模式,需要对THx和TLx重新赋值。
<2> 进行间隔定时到达的逻辑处理(越少越好)。
其程序框架和代码编写基本上差不多:
<1> 串行通信是指数据一位接一位地顺序发送或接收。
<2> 串行通信有SPI、IIC、UART等多种,最常见最通用的是指UART。
<3> 串行通信的制式有:单工、半双工、全双工三种。
<4> 计算机的串行通信接口是RS-232的标准接口,而单片机的UART接口则是TTL电平,两者的电气规范不一致,所以要完成两者之间的数据通信,就需要借助接口芯片在两者之间进行电平转换,常用的有MAX232芯片。
<5> 波特率:每秒钟传输的位数,9600波特率就是指每秒钟传输9600位。
注意:在51单片机中需要使用定时器1来产生波特率,因此,如果使用串口通信,则定时器1就不能做其他用途,在初始化串行接口模块的时候,除了要配置SCON寄存器之外,还有根据波特率参数设置定时器1的技术初值。
对于传统的51单片机,与串口相关的寄存器有:
TH1和TL1:设置波特率参数。
TMOD:设置定时器1的工作模式。
SBUF:串行通信数据的发送和接收缓冲器。
SCON:串行接口控制寄存器。
在这里主要是掌握SCON,跟串口有关的各种属性都在这个寄存器里进行配置:
在串口通信的程序设计中,主要有串口初始化和数据收发两个部分。
在初始化函数中,基本步骤如下:
<1> 设置定时器1的工作模式,也就是对TMOD寄存器赋值。
<2> 计算波特率参数,并赋值给TH1和TL1寄存器。
<3> 打开定时器1。
<4> 设置SCON寄存器。
<5> 使能串口中断ES。
<6> 使能总中断EA。
数据的发送通常采用查询的方式,而数据的接收则采用中断。
一般情况下,上位机的命令可能不是一个字节,而是多个字节组成的命令帧,有的长度固定,有的长度变化;而且要求返回的数据可能也不是一个字节,可能是一个数组,也有可能是一个字符串等。因此,在串口这一个单元中,必须多加一个强化环境,掌握多字节的数据帧收发应用。