8051单片机实战分析(以STC89C52RC为例) | 12 - 串行口中断的使用

Posted Neutionwei

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了8051单片机实战分析(以STC89C52RC为例) | 12 - 串行口中断的使用相关的知识,希望对你有一定的参考价值。

在第一篇到第九篇博文中,我们认识到了一些基于IO口输入与输出的基础电子器件使用:
8051单片机实战分析(以STC89C52RC为例) | 01 - 点亮一个LED
8051单片机实战分析(以STC89C52RC为例) | 02 - LED延时约5s闪烁
8051单片机实战分析(以STC89C52RC为例) | 03 - LED流水灯
8051单片机实战分析(以STC89C52RC为例) | 04 - 蜂鸣器驱动
8051单片机实战分析(以STC89C52RC为例) | 05 - 静态数码管驱动
8051单片机实战分析(以STC89C52RC为例) | 06 - 动态数码管驱动
8051单片机实战分析(以STC89C52RC为例) | 07 - 独立按键驱动
8051单片机实战分析(以STC89C52RC为例) | 08 - 矩阵按键驱动
8051单片机实战分析(以STC89C52RC为例) | 09 - LED点阵显示数字
但现在我们要开始回到8051单片机内部,通过实战来认识它们的工作原理,你会发现通过它们可以去开发一些更有意思的东西!

这篇博文带领大家认识一下STC89C52RC单片机定时器中断的使用。

如果你不了解什么是中断,建议你先看这篇:
STC89C52RC单片机额外篇 | 01 - 认识中断、中断源以及中断优先级

如果你不了解什么是串口通信,建议你先看这篇:
STC89C52RC单片机额外篇 | 02 - 认识串行通信、波特率以及数据包

1 中断系统结构

以下这张图是从中断引脚到中断入口所经过的通道:

从图中不难看出RXTX引脚经过了SCON、IE、IP这些寄存器,因此我们在写程序时得把这些寄存器功能配置好,CPU才会按照我们的想法只执行!下面分别对这些寄存器进行介绍(稍微了解一下即可,忘记的时候再查)。

1.1 SCON寄存器

SCON(Serial Control Register),中文叫串行口控制寄存器,SCON寄存器是用于控制串行通信的方式选择、接收和发送,指示串口的状态。

首先介绍SCON寄存器位SM0/SM1,它们用于设置工作方式:

其余SCON寄存器位的用途:

SCON寄存器位作用
SM2多机通信控制位。多机通信是工作于方式2和方式3,SM2位主要用于方式2和方式3。接收状态,当串行口工作于方式2或3,以及SM2=1时,只有当接收到第9位数据(RB8)为1时,才把接收到的前8位数据送入SBUF,且置位RI发出中断申请,否则会将接收到的数据放弃。当SM2=0时,就不管第9位数据是0还是1,都会将数据送入SBUF,并发出中断申请。工作于方式0时,SM2必须为0。
REN允许接收位。REN用于控制数据接收的允许和禁止,REN=1时,允许接收,REN=0时,禁止接收。
TB8发送数据位8。在方式2和方式3中,TB8是要发送的——即第9位数据位。在多机通信中同样亦要传输这一位,并且它代表传输的地址还是数据,TB8=0为数据,TB8=1时为地址。
RB8接收数据位8。在方式2和方式3中,RB8存放接收到的第9位数据,用以识别接收到的数据特征。
TI发送中断标志位,它是可寻址标志位。方式0时,发送完第8位数据后,由硬件置位,其它方式下,在发送或停止位之前由硬件置位,因此,TI=1表示帧发送结束,TI可由软件清“0”。
RI接收中断标志位,它是可寻址标志位。接收完第8位数据后,该位由硬件置位,在其他工作方式下,该位由硬件置位,RI=1表示帧接收完成。

要注意的是在串口中断处理时,TI,RI都需要软件清"0",硬件置位后不可能自动清0,此外,在进行缓冲区操作时,需要ES=0,以防止中断出现。ES寄存器位位于接下来介绍的IE寄存器。

1.2 IE寄存器

IE(Interrupt Enable),中文叫中断允许寄存器,它的作用是控制所有中断源的开放或禁止,以及每个中断源是否被允许。

各寄存器位的作用如下:

IE寄存器位作用
EAEA = 0时,所有中断禁止(即不产生中断);EA = 1时,各中断的产生由个别的允许位决定
ES串行口RX/TX中断允许
ET1定时器T1中断允许
EX1外中断INT1中断允许
ET0定时器T0中断允许
EX0外部中断INT0中断允许

1.3 IP寄存器

IP(Interrupt Priority),中文叫中断优先级寄存器,它是用来设定各个中断源属于两级中断中的哪一级。

各寄存器位的作用如下:

IP寄存器位作用
PS串行口RX/TX中断优先
PT1定时器T1中断优先
PX1外中断INT1中断优先
PT0定时器T0中断优先
PX0外部中断INT0中断优先

除此之外,还有一个波特率有关的PCON寄存器,当然这里只用到它其中的一位。

1.4 PCON寄存器

PCON全称Power Control Register,即功率控制寄存器

系统复位默认为SMOD=0。当SMOD=0时,串口方式1,2,3时,波特率正常;当SMOD=1时,串口方式1,2,3时,波特率加倍。

2 串行口结构

注意这里的SBUF寄存器只是逻辑上同名,但是它们物理上是分开的!另外,还需要用到TH1TL1这种属于定时器T1的寄存器,后面会了解到它们的作用!

下面列出各个工作方式的数据传输:

① 工作方式0:

② 工作方式1:

③ 工作方式2与工作方式3:

3 波特率计算

如果你不了解什么是波特率,建议你先看这篇:
STC89C52RC单片机额外篇 | 02 - 认识串行通信、波特率以及数据包

下面列出基于各个工作方式的波特率计算公式,对于可变波特率的设置需要用到TH1寄存器:

当然,对于波特率,一般有几种是可供选择的,因为我们通常使用标称值:

这里我们好奇为什么晶振频率选择11.0592MHz就能减小计算误差,下面我们以9600波特率,分别代入11.0592MHz与12MHz计算一下:

我们设置定时器T1为工作模式2,SMOD 设为1,分别看看那所要求的TH1为何值。代入公式:

  • 11.0592MHz

            9600=(2÷32)×((11.0592M/12)/(256-TH1)) 
            =》TH1=250 
    
  • 12MHz

            9600=(2÷32)×((12M/12)/(256-TH1)) 
            =》TH1≈249.49 
    

4 代码

① 中断服务函数:

我们知道我们编写的C程序,函数的执行是从main主函数开始执行,现在有了中断,自然就产生一个中断服务函数:

从图中我们可以知道单片机在发生中断的时候,程序的执行过程会从主程序A跳到中断服务程序B,在执行完中断服务程序B后,会返回到之前主程序A被中断打断处继续执行程序。

那么我们如何指定中断服务程序?具体参考以下模板(对于函数名你可以随便写,当然最好贴近有意义的命名)。

外部中断0的中断服务函数:

void Int0()	interrupt 0
{
	... // 中断服务程序中要执行内容
}

定时器0的中断服务函数:

void Timer0()	interrupt 1
{
	... // 中断服务程序中要执行内容
}

外部中断1的中断服务函数:

void Int1()	interrupt 2
{
	... // 中断服务程序中要执行内容
}

定时器1的中断服务函数:

void Timer1()	interrupt 3
{
	... // 中断服务程序中要执行内容
}

串行口的中断服务函数:

void Serial()	interrupt 4
{
	... // 中断服务程序中要执行内容
}

② 下面我们写个程序,将波特率设置为4800,把发送的数据字符"1"显示到串口调试助手SSCOM:

#include "reg52.h"			 //此文件中定义了单片机的一些特殊功能寄存器

typedef unsigned int u16;	  //对数据类型进行声明定义
typedef unsigned char u8;

/*******************************************************************************
* 函数名         :UartInit()
* 函数功能		 :设置串口
* 输入           : 无
* 输出         	 : 无
*******************************************************************************/
void UartInit()
{
	SCON=0x50;			//设置为工作方式1
	TMOD=0x20;			//设置计数器工作方式2
	PCON=0x80;			//波特率加倍
	TH1=0xF3;			//计数器初始值设置,注意波特率是4800
	TL1=0xF3;
	ES=1;				//打开接收中断
	EA=1;				//打开总中断
	TR1=1;				//打开计数器
}

/*******************************************************************************
* 函 数 名       : main
* 函数功能		 : 主函数
* 输    入       : 无
* 输    出    	 : 无
*******************************************************************************/
void main()
{	
	UartInit();  //	串口初始化
	while(1);		
}

/*******************************************************************************
 * 函数名         : Serial() interrupt 4
 * 函数功能		 : 串口通信中断函数
 * 输入           : 无
 * 输出          : 无
*******************************************************************************/
void Serial() interrupt 4
{
	u8 receiveData;

	receiveData=SBUF; //出去接收到的数据
	RI = 0;			  //清除接收中断标志位
	SBUF=receiveData; //将接收到的数据放入到发送寄存器
	while(!TI);		  //等待发送数据完成
	TI=0;			  //清除发送完成标志位
}

简要分析:

  • 代码中设置波特率为4800,根据前面的表格不是应该设置TH1=0xFA;吗?根据前面的介绍,我们不难发现此处用到晶振频率肯定不是11.0592MHz,明显是使用12MHz。
  • 注意这里把定时器T1设置为工作方式2——8 位自动重装模式,因此寄存器TH1TL0的值是一样的。
  • 最后附上串口调试助手的操作:

以上是关于8051单片机实战分析(以STC89C52RC为例) | 12 - 串行口中断的使用的主要内容,如果未能解决你的问题,请参考以下文章

8051单片机实战分析(以STC89C52RC为例) | 11 - 定时器中断的使用

8051单片机实战分析(以STC89C52RC为例) | 03 - LED流水灯

8051单片机实战分析(以STC89C52RC为例) | 02 - LED延时约5s闪烁

8051单片机实战分析(以STC89C52RC为例) | 08 - 矩阵按键驱动

8051单片机实战分析(以STC89C52RC为例) | 06 - 动态数码管驱动

8051单片机实战分析(以STC89C52RC为例) | 01 - 点亮一个LED