0
点赞
收藏
分享

微信扫一扫

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 中断系统结构

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


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

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

1.1 SCON寄存器

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


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

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


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

其余​​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),中文叫中断允许寄存器,它的作用是控制所有中断源的开放或禁止,以及每个中断源是否被允许。

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

各寄存器位的作用如下:

IE寄存器位

作用

​EA​

​EA = 0​​​时,所有中断禁止(即不产生中断);​​EA = 1​​时,各中断的产生由个别的允许位决定

​ES​

串行口​​RX/TX​​中断允许

​ET1​

定时器​​T1​​中断允许

​EX1​

外中断​​INT1​​中断允许

​ET0​

定时器​​T0​​中断允许

​EX0​

外部中断​​INT0​​中断允许

1.3 IP寄存器

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

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

各寄存器位的作用如下:

IP寄存器位

作用

​PS​

串行口​​RX/TX​​中断优先

​PT1​

定时器​​T1​​中断优先

​PX1​

外中断​​INT1​​中断优先

​PT0​

定时器​​T0​​中断优先

​PX0​

外部中断​​INT0​​中断优先

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

1.4 PCON寄存器

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


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

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

2 串行口结构


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

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

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

① 工作方式0:


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

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

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

② 工作方式1:


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

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

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


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

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

3 波特率计算

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

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


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

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


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

这里我们好奇为什么晶振频率选择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主函数开始执行,现在有了中断,自然就产生一个中断服务函数:


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

从图中我们可以知道单片机在发生中断的时候,程序的执行过程会从主程序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 位自动重装模式,因此寄存器​​TH1​​​与​​TL0​​的值是一样的。
  • 最后附上串口调试助手的操作:



举报

相关推荐

0 条评论