【51单片机系列】51单片机的中断系统使用总结二

发布时间:2024年01月12日


四、串口通信相关介绍

80C51系列单片机的串口结构如下图,A是发送或接收的串行数据;有两个独立的缓冲器SBUF,上面的SBUF是发送缓冲器,下面的SBUF是接收缓冲器;由TH1和TL1可以计算出T1溢出率,SMOD确定波特率是否倍增,通过设置控制寄存器实现串口中断。

51单片机的串口结构图

4.1、与串口通信相关的寄存器

串口通信时除了串行口的控制寄存器SCON和控制波特率的位SMOD外,还需要定时器1确定溢出率。为了开启串口中断还需要IE.4的ES位打开串口中断允许。

  • SCON-串口控制寄存器

SCON是一个特殊功能寄存器,用来设定串行口的工作方式、接收/发送控制以及设置状态标志,各位结构如下:

76543210
字节地址:98HSM0SM1SM2RENTB8RB8TIRI
SCON工作方式选择位多机通信控制位允许串行接收位方式2或方式3中是发送数据的第9位方式2或方式3中是接收数据的第9位发送中断标志位接收中断标志位

SM0SM1是串口通信工作方式选择位,有4种工作方式:

SM0SM1工作方式说明波特率
000移位寄存器 f O S C / 12 f_{OSC}/12 fOSC?/12
01110位异步收发器,含8位数据可变
10211位异步收发器,含9位数据 f O S C / 64 f_{OSC}/64 fOSC?/64 f O S C / 32 f_{OSC}/32 fOSC?/32
11311位异步收发器,含9位数据可变

SM2是多机通信控制位,主要用于方式2和方式3。
当接收机的SM2=1时利用收到的RB8来控制是否激活RI,RB8=0时不激活RI,收到的信息丢弃;RB8=1时收到的数据进入SBUF,并激活RI,进而在中断服务中将数据从SBUF读走。
当SM2=0时,不论收到的RB8为0和1,均可以使收到的数据进入SBUF,并激活RI。
工作方式0时,SM2必须是0;工作方式1时,如果SM2=1,只有接收到有效停止位时,RI置1。

REN是允许串行接收位,软件置REN=1,则启动串行口接收数据;软件置REN=0,禁止接收数据。

TB8,方式2和方式3中,是发送数据的第9位,可以用软件规定作用,可以用作数据的奇偶校验位,或者在多机通信中,作为地址帧和数据帧的标志位。方式0和方式1中,该位未使用。

RB8,方式2和方式3中,是接收到数据的第9位,作为奇偶校验位或地址帧/数据帧的标志位。方式1中,如果SM2=0,RB8是接收到的停止位。

TI是发送中断标志位。方式0时,当串行发送第8位数据结束时,或在其它方式,串行发送停止位的开始时,由内部硬件使TI置1,向CPU发送中断申请。在中断处理程序中,用软件将其清0,取消此中断申请。

RI是接收中断标志位。方式0时,当串行接收第8位数据结束时,或在其它方式,串行接收停止位的中间时,由内部硬件使RI置1,向CPU发送中断申请。在中断处理程序中,用软件将其清0,取消此中断申请。

  • PCON.7-与波特率倍增有关的位

PCON中有一个位SMOD与串行口工作有关,PCON结构如下:

76543210
字节地址:97HSMOD

SMOD是波特率倍增位。在串行口方式1、方式2、方式3时,波特率与SMOD有关。当SMOD=1时,波特率提高一倍。复位时SMD=0。

4.2、波特率计算

串口通信时,收发双方对发送或接收数据的速率要有约定。串口编程方式有四种工作方式,其中方式0和方式2的波特率是固定的,方式1和方式2的波特率是固定的,方式1和方式3的波特率是可变的,由定时器T1的溢出率来决定。

串口通信的四种工作方式对应三种波特率。由于输入的时钟来源不同,所以,各种方式的波特率计算公式也不相同。

方式0的波特率= f O S C / 12 f_{OSC} / 12 fOSC?/12
方式2的波特率= ( 2 S M O D / 64 ) ? f O S C (2^{SMOD}/64) \cdot f_{OSC} (2SMOD/64)?fOSC?
方式1的波特率= ( 2 S M O D / 32 ) ? ( T 1 溢出率 ) (2^{SMOD} / 32) \cdot (T1溢出率) (2SMOD/32)?(T1溢出率)
方式3的波特率= ( 2 S M O D / 32 ) ? ( T 1 溢出率 ) (2^{SMOD} / 32) \cdot (T1溢出率) (2SMOD/32)?(T1溢出率)

T1作为波特率发生器时,最典型的用法是使T1工作在自动重装的8位定时器方式,即定时器的工作方式2,并置TCON的TR1=1,以启动定时器。此时溢出率取决于TH1中的计数值。

T1溢出率计算公式:

T 1 溢出率 = f O S C / { 12 × [ 256 ? T H 1 ] } T1溢出率 = f_{OSC} / \{ 12 \times [256 - TH1 ]\} T1溢出率=fOSC?/{12×[256?TH1]}

单片机应用中,常用的晶振频率是12MHz和11.0592MHz,选用的波特率也相对固定。可以使用软件计算初值。

51单片机波特率初值计算软件:
链接:https://pan.baidu.com/s/1x5R4GzN5RAJmXt0-Qner3A?pwd=8051
提取码:8051
打开软件,设置定时器方式、晶振频率、波特率、SMOD,然后点确定,在上方的黑色框中会显示定时器初值。
在这里插入图片描述

4.3、如何实现串口通信

串口通信使用51单片机的RXD(P3.0引脚)和TXD(P3.1引脚),当RXD接收数据或TXD发送数据时,向CPU申请中断,CPU响应串口中断进入中断处理函数中接收或发送SBUF中的数据。

中断响应的三个条件:①中断源有中断请求;②此中断源的中断允许位为1;③CPU开中断。

第一个条件:中断源有中断请求。对于串口来说,在发送数据结束或接收数据结束时,硬件会置TI或RI为1,向CPU申请中断。要发送或接收数据,需要确定波特率,即确定定时器1的工作方式TMOD、计数初值THx和TLx;要使定时器1工作,需要启动计数器TR1;同时还要确定数据发送或接收的控制方式SCON。
第二个条件:此中断源的中断允许位为1,设置串口中断允许位ES=1。
第三个条件:CPU开中断,设置CPU总中断允许位EA=1。

4.3.1、串口通信工作方式0使用

串口通信在工作方式0时为同步移位寄存器方式,这种方式用于单片机外接移位寄存器,用来扩展I/O口。

方式0以8位数据为1帧,没有起始位和停止位,先发送或接收最低位。波特率固定,为 f O S C / 12 f_{OSC}/12 fOSC?/12

(1) 方式0输出

方式0输出的工作原理:当单片机执行将写入发送缓冲器SBUF的指令时,产生一个正脉冲,串口开始把SBUF中的8位数据以 f O S C / 12 f_{OSC}/12 fOSC?/12的固定波特率从RXD引脚串行输出,低位在先,TXD引脚输出同步移位脉冲,当8位数据发送完,中断标志位TI置1。

串口通信方式0时序

(2)方式0输出使用示例

方式0输出的典型应用是串口外接串行输入/并行输出的同步移位寄存器74LS164,实现并行输出端口的扩展。

Proteus中设计串口工作在方式0,通过74LS164芯片的输出控制8只外接LED发光二极管亮灭的接口电路。串口被设置在方式0输出时,串行数据由RXD端(P3.0)送出,移位脉冲由TXD(P3.1)端送出。在移位脉冲的作用下,串口发送缓冲器的数据逐位地从RXD端串行地移入74LS164芯片中。

proteus设计原理图如下:

方式0输出扩展并行输出使用示例
74LS164芯片的8脚为同步脉冲输入端;9脚为控制端,当9脚为低电平时,允许串行数据由RXD端向74LS164芯片的串行数据输入端输入,此时74LS164芯片的8位并行输出端关闭,当9脚为高电平时,串行数据输入端关闭,允许74LS164芯片中的8位数据并行输出。

当串口将8位串行数据发送完毕后,申请中断,在中断处理函数中,单片机向串口输出下一个8位数据。

首先需要设置串口通信在方式0的中断响应条件,即串口通信初始化,代码如下:

// 串口中断初始化函数
void UsartInit()
{
	// 1. 确定串口控制方式,工作在方式0,RI不受RB8控制,禁止接收数据
	SCON=0x00;
	// 2. 打开串并口中断允许位
	ES=1;
	// 3. 打开CPU总中断允许位
	EA=1;
}

串口发送完一帧数据后,申请中断,在中断处理函数中,向串口输出下一帧数据,即串口中断处理函数代码如下:

// 串口中断处理函数
void Usart() interrupt 4
{
	// 判断TI是否置1,一个字节串行发送完毕
	if(TI)
	{
		CTRL=1;  // 允许74LS164并行输出,流水点亮二极管
		SBUF=sendByte;  // 向SBUF写入数据,启动串行发送
		delay(500);
		
		CTRL=0;  // 允许串行写入,关闭并行输出
		sendByte=_crol_(sendByte, 1);
		SBUF=sendByte;  // 再次发送点亮数据
			
	}
	// 数据处理结束,置TI和RI为0
	TI=0;
	RI=0;
}

在主函数中,首先调用串口初始化函数,然后需要设置74LS164的控制引脚为低电平,允许串行数据输入,最后再向SBUF中发送数据,启动串行发送。代码如下:

void main()
{
	UsartInit();
	CTRL=0;  // 串行口向74LS164发送数据
	// CPU向SBUF中写入数据,启动串行发送
	SBUF=sendByte;
	while(1);
}

全局变量sendByte声明:u8 sendByte=0x01;

仿真结果:

串口通信方式0仿真结果

(3) 方式0输入

方式0输入时,REN作为串行口允许接收控制位,REN=0时禁止接收,REN=1时允许接收。

当向SCON寄存器写入控制字(设置方式0、REN=1,RI=0)时,产生一个正脉冲,串行口开始接收数据。

RXD引脚是数据输入端,TXD为移位脉冲信号输出端,接收器以 f O S C / 12 f_{OSC}/12 fOSC?/12的固定波特率采样RXD引脚的数据信息,当接收完8位数据时,中断标志RI置1,表示一帧数据接收完毕,可以进行下一帧数据的接收。

方式0输入时序图如下:

串口通信方式0输入时序图

(4) 方式0输入使用示例

如下原理图,单片机的串口外接一片8位并行输入、串行输出的同步移位寄存器74LS165,从而实现了扩展一个8位并行输入口。将接在74LS165的8各开关S0 ~ S7的状态通过串行口的方式0输入方式读入到单片机内。

74LS165芯片的1脚SH/LD为控制端,SH/LD=0时,允许74LS165芯片并行输入数据,关闭串行输出端;当SH/LD=1时,关闭并行输入端,可向单片机串行发送数据。

当P3.2引脚连接的开关k合上时,通过外部中断0控制开关S0 ~ S7的状态数字量并行是否允许读入,使用一个连接在P1.0引脚的LED灯显示是否可以读入的状态,LED灯亮可以读入,LED不亮则不允许读入。采用中断方式对S0 ~ S7状态进行读取,由单片机的P2口控制对应的开关按下的二极管发光点亮。

Proteus设计原理图如下:

串口通信工作方式0输入使用示例

软件设计,首先设置中断响应条件,这里需要CPU接收数据,所以应该置REN=1,串口中断初始化函数如下:

// 串口通信方式0输入初始化
void UsartInit()
{
	// 1. 设置工作方式0,允许接收数据
	SCON=0x10;
	// 2. 打开串口中断允许位
	ES=1;
	// 3. 打开CPU总中断允许位
	EA=1;
}

串口中断处理函数如下:

// 串口中断处理函数
void Usart() interrupt 4
{
	if(!LED){
		CTRL_74LS165=0;  // 读入并行数据
		delay(1000);
		CTRL_74LS165=1;  // 向CPU发送串行数据
		
		RI=0;
		receiveByte=SBUF;
		
		GPIO_LED=receiveByte;
	}
}

其中LED在外部中断0处理函数中进行取反操作。

主函数调用串口中断初始化函数UsartInit和外部中断0初始化函数Int0Init后进入while(1);即可。

仿真结果如下,可以看到,当没有按下按键K,直接按S0~S7,连接到P2口的LED不会亮;当按下按键K后,再按S0 ~ S7,对应的LED会亮。

串口通信工作方式0输入使用示例

4.3.2、串口通信工作方式1使用

串口通信的工作方式1为双机通信方式,如下图所示。SM0SM1设置为01时,串口通信设置为方式1的双机串行通信,TXD脚和RXD脚分别用于发送和接收数据。
方式1双机通信的连接电路
方式1收发一帧数据为10位,1位起始位(0)+8位数据位+1位停止位(1)。先发送或接收最低位。

方式1时串口波特率可变,波特率计算方式为: 2 S M O D 32 × T 1 溢出率 \frac{2^{SMOD}}{32} \times T1溢出率 322SMOD?×T1溢出率,其中SMOD是寄存器PCON的最高位的值。

(1) 方式1发送

串口以方式1输出时,数据位由TXD端输出,发送一帧信息。当CPU执行写数据到发送缓冲器SBUF的命令后,就启动发送。

方式1发送时序图
如上图是方式1发送时序图。先发送起始位,由TXD引脚输出,然后接着输出8位数据位,数据位全部发送完毕后,串口发送中断标志位TI置1,向CPU申请中断。

(2) 方式1接收

串口以方式1(SM0SM1=01)接收时(REN=1),数据从RXD引脚输入。当检测到起始位的负跳变,开始接收。
方式1接收时序
接收时,定时控制信号有两种,一种是接收移位时钟,它的频率和传送的波特率相同,另一种是位检测器采样脉冲,它的频率是接收移位时钟的16倍。也就是在1位数据器件,有16个采用脉冲,以波特率的16倍速率采样RXD引脚状态。

当采样到RXD端从1到0负跳变时,就启动接收检测器。接收的值是3次连续采样(第7、8、9个脉冲时采样),取两次相同的值,以确认是否是真正的起始位的开始,这样能够比较好地消除干扰引起的影响,保证可靠无误地开始接收数据。

当确认起始位有效时,开始接收一帧信息。接收每一位数据时,也都进行3次连续采样,接收的值是3次采样中至少两次相同的值,以保证接收到的数据位的准确性。

当一帧数据接收完毕后,必须同时满足以下两个条件,这次接收才真正有效。

  1. 当RI=0时,即上一帧数据接收完成时,RI=1发出的中断请求已被响应,SBUF中的数据已被取走,说明接收SBUF已空。
  2. SM2=0或收到的停止位=1(方式1时,停止位已进入RB8),则将接收到的数据装入SBUF和RB8(装入的是停止位),且中断标志RI置1。

如果不同时满足这两个条件,收到的数据不能装入SBUF,这表示该帧数据将丢失。

(3)方式1使用示例——双机通信

方式1可用来实现双机通信。两个单片机利用串口进行串行通信:串行通信的波特率由按键设定,可选的波特率为1200bps、2400bps、4800bps、9600bps。在工作方式1下进行全双工串行通信。

本示例通过按键得到设定的波特率,载入到T1的计数器中。运行时将数据0xaa从主机传输到从机,并显示到从机的LED上,从而验证串口通信的实现。

在实际的硬件实现中,如果串口通信线路过长,考虑采用MAX232进行电平转换,以延长传输距离。

为减少波特率的计算误差,采用11.0592MHz的晶振。

Proteus中两个单片机之间的串口通信接口设计原理电路如下图所示:

方式1实现双机通信Proteus设计原理图
软件设计,因为是双机通信,有两个单片机,所以需要编写两份代码。其中一个单片机发送消息,另一个单片机接收消息。这里把发送消息的称为主机,接收消息的称为从机。

首先两个单片机的P1.0连接到K1,P1.1连接到K2,P1.2连接到K3,P1.3连接到K4,两份代码的按键扫描函数是一样的,如下:

sbit K1=P1^0;
sbit K2=P1^1;
sbit K3=P1^2;
sbit K4=P1^3;

// 按键扫描函数,返回按下的按键序号,K1~K4序号为0~3,初始运行时如果没有按下按键,默认使用K2指示的波特率
uchar KeyScan()
{
	uchar keyscan_num, temp;
	
	P1=0xff;
	temp=P1;
	if(~(temp & 0xff))  // 当没有按键按下时,temp=0xff,temp&0xff=0xff,取反后为0不会进入条件语句
	{
		if(0==K1)
		{
			keyscan_num=0;
		}
		else if(0==K2)
		{
			keyscan_num=1;
		}
		
		else if(0==K3)
		{
			keyscan_num=2;
		}
		
		else if(0==K4)
		{
			keyscan_num=3;
		}
		else
		{
			keyscan_num=1;
		}
	}
	return keyscan_num;	
}

然后根据按下的按键设置对应的波特率,K1对应1200bps,K2对应2400bps,K3对应4800bps,K4对应9600bps。设置随影波特率的函数如下,在该函数中需要设置串口控制字、定时器T1工作方式、T1计数初值、打开中断允许等。

以1200bps波特率为例,另外的三个波特率只有计数初值不一样,其它都是一样的

// 串口通信初始化,工作方式1,波特率为1200bps
void Init1200bps()
{
	
	// 1. 确定串口工作方式;确定定时器工作方式及计数初值
	SCON=0x50;  // 方式1 8位异步收发,允许接收数据
	PCON=0x00;  // 波特率不加倍
	TI=0;
	TMOD=0x20;  // 方式2,8位自动装载模式
	TH1=0xE8;
	TL1=0xE8;
	// 2. 打开串口中断允许位
	ES=1; 
	// 3. 打开CPU总中断允许位
	EA=1;
	// 4. 启动定时
	TR1=1;
}

各个波特率计数初值如下:

各波特率计数初值
最后是主函数,master.c中的主函数中,循环获取哪个按键按下,根据按下的按键设置对应的波特率,然后发送数据。代码如下:

/*
	实现功能:串口通信,方式1实现双机通信。
		主机代码:扫描按下的按键,根据按键设置波特率,发送数据
	[2024-01-12]
*/

#include <reg52.h>
#include "KeyFuncs.h"
#include "BAUDs.h"

typedef unsigned char u8;
typedef unsigned int u16;

u8 sendByte=0xaa;

void main()
{
	u8 keyPress;  // 获取哪个按键按下,使用对应的波特率
	
	while(1)
	{
		keyPress=KeyScan();
		switch(keyPress)
		{
			case 0: Init1200bps(); break;
			case 1: Init2400bps(); break;
			case 2: Init4800bps(); break;
			case 3: Init9600bps(); break;
		}
		SBUF=sendByte;  // 发送数据
		while(TI==0);  // 等待直到发送完成
		TI=0;  // TI软件清0
	}
}

slaver.c的主函数中,循环扫描按下的按键,根据按键设置对应的波特率,通过串口中断处理函数接收数据,并在LED上显示。代码如下:

/*
	实现功能:串口方式1实现双机通信。
		从机代码:扫描按下的按键,根据按键设置波特率,在串口中断处理程序中接收数据。
	[2024-01-12]
*/
#include <reg52.h>
#include "KeyFuncs.h"
#include "BAUDs.h"

typedef unsigned char u8;
typedef unsigned int u16;

u8 reveiveByte;

void main()
{
	u8 keyPress;  // 获取哪个按键按下,使用对应的波特率
	
	while(1)
	{
		keyPress=KeyScan();
		switch(keyPress)
		{
			case 0: Init1200bps(); break;
			case 1: Init2400bps(); break;
			case 2: Init4800bps(); break;
			case 3: Init9600bps(); break;
		}
		while(RI==0);  // 等待直到接收数据
	}
}

// 接收到数据进入串口中断处理程序
void ReceiveDat() interrupt 4
{
	P2=SBUF;  // 将接收到的数据作用到P2口,P2口连接了8个LED
	RI=0;  // RI软件清0
}

仿真结果:

在这里插入图片描述

4.3.3 串口通信工作方式2使用

串口通信工作于方式2和方式3时,被定义为9位的异步通信接口。每帧数据均为11位,1位起始位+8位数据位(先低位)+1位可编程位+1位停止位。

方式2的波特率计算公式为: 2 S M O D 64 × f O S C \frac{2^{SMOD}}{64} \times f_{OSC} 642SMOD?×fOSC?

(1) 方式2发送

方式2在发送前,先根据通信协议由软件设置TB8(如双击通信时的奇偶校验位或多机通信时的地址/数据的标志位),然后将要发送的数据写入SBUF,即可启动发送过程。

串口能够自动把寄存器SCON中的TB8取出,并装入到第9位数据位的位置,再逐一发送出去。发送完毕,使TI置1。

串口通信方式2和方式3发送时序图

(2) 方式2接收

当串行口的SCON寄存器的SM0SM1=10B,且REN=1时。允许串行口以方式2接收数据。

接收时,数据由RXD端输入,接收11位信息。当位检测逻辑采样到RXD引脚从1到0的负跳变,并判断起始位有效后,便开始接收一帧信息。

在接收完第9位数据后,需要满足以下两个条件,才能将接收到的数据送入接收缓冲器SBUF。

  1. RI=0,意味着接收缓冲器为空。
  2. SM2=0或接收到的第9位数据位RB8=1。

当满足上述两个条件时,接收到的前8位数据送入SBUF,第9位数据送入RB8,且RI置1.如果不满足这两个条件,接收到的信息将被丢弃。

串口通信方式2和方式3接收时序图

4.3.4 串口通信工作方式3使用

SM0SM1=11时,串口被定义在方式3,方式3为波特率可变的9位异步通信方式,除了波特率外,方式3和方式2相同。

方式3的波特率计算: 2 S M O D 32 × T 1 溢出率 \frac{2^{SMOD}}{32} \times T1溢出率 322SMOD?×T1溢出率

文章来源:https://blog.csdn.net/sinat_41752325/article/details/135521705
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。