前面讲解的都是单片机自身的一些功能,并未涉及单片机与其它单片机或者计算机之间通信。那么单片机与其它设备之间又是怎么通信的呢?通常来讲有两种通信方式,即并行通信与串行通信。
两个单片机之间的并口通信,以 8 位为例,直接将单片机 1 的 8 位 I/O 口 P0 与单片机 2 的 P1 相连接,在同步时钟信号的作用进行数据的传输。以单片机 1 向单片机 2 传输数据 0x7B 为例,单片机 1 只需将数据 0x7B 输出到 P0 口上,单片机 2 在同步时钟信号的步调下将 0x7B 直接读取。此时同步时钟信号的周期长短将决定数据传输的速度,周期越短,速度越快。并口通信的最大优点为速度快,一个时钟周期内可以传播一个字节,甚至多个字节,缺点为需要占用大量的 I/O 口资源,对于大多数资源紧张的应用场合来说,这种方法是不可取的,而串行通信有效的解决了资源问题。
STC89C52 系列单片机配置了串行方式通信接口,对应单片机的管脚 P3.0/RxD,串行数据接收引脚、P3.1/TxD,串行数据发送引脚。如上图所示,单片机 1 的串行发送引脚 TxD 与单片机 2 的串行数据接收引脚 RxD 相连接,形成了一条单片机 1 发送、单片机 2 接收的数据通信链路。单片机 2 的 TxD 与单片机 1 的 RxD 连接,形成了一条单片机 2 发送、单片机 1 接收数据的通信链路。单片机 1 的 GND 与单片机 2 的 GND 相连接确保了两个单片机在同一电源基准下工作。接下来讲解单片机串行通信的工作原理。
同样以单片机 1 向单片机 2 发送数据 0x7B 为例,二进制表示为 01111011,单片机依次 01111011 的最低位‘1’到最高位‘0’依次发送出去。发送 1 的时候为将 TxD 拉高’一段时间’,发送 0 的时候为将 TxD 拉低‘一段时间’,即发送数据的时候后,每发送一位都持续‘一段时间’,发送 0x7B 的从低位到高位的顺序为:1->1->0->1->1->1->1->0。“一段时间”的长短决定了单片机的串口传输的速度,时间越短,传输速度越快。那么在同样位传输速率的情况下,并行通信速度为串行通信的 8 倍,显然串行通信以牺牲速度的方式换取了更多的资源,这就是时间换资源的概念。
上面讲解的“一段时间”实际上为单片机串口传输 1 位数据所耗费的时间,在应用中我们常常把 1 秒传输多少位来衡量单片机串口传输速率,这就是波特率的概念。例如通信双方约定波特率为:9600bps,即每秒传输 9600 位(bps:bit per second)。波特率在单片串口通信中为非常重要的参数指标,通信双方只有在约定共同的波特率下才能保证数据的正确传输。
在单片机串口通信中,并没有同步时钟信号来统一数据发送和接收,通过双方约定的波特率来保证数据的通信。以单片机 1 向单片机 2 发送数据为例,假设双方设定的波特率为 9600bps,那么单片机 2 是怎么知道单片机 1 什么时候给它发送数据,又是什么时候结束的呢?
这里我们引入两个重要的概念,起始位和停止位。单片机在没有进行串口通信的情况下,数据发送引脚 TxD 为高电平,当单片机 1 需要发送数据时,首先通过 TXD 发送一位“0”,即把单片机 TxD 从高电平拉低,当单片机 2 检测到数据接收引脚 RxD 的低电平后便开始接收数据了。那么这里单片机 1 发送的第一位“0”称为起止位。紧接着单片机 1 将 8 位数据依次发送出去,当 8 位数据发送完后,单片机发送一位“1”,单片机 2 接收完 8 位数据后,又接收到一位“1”,便知道停止发送数据了,至此便完成了一次数据的传输。这里单片机 1 发送的最后一位“1”称之为停止位。根据上面的步骤,完成一次数据的传输,包括起始位、8 位数据、停止位,总共 10 位数据,如下图所示。这里的 10 位数据称之为一帧数据。
下面讲解一下单片机串口的工作原理。STC89C52系列单片机串口内部模块有两个独立的串行口缓冲寄存器(SBUF),两个寄存器均为8位,一个为发送SBUF,只能往里写数据、另一个为接收SBUF、只能读取数据。当单片机要通过串口往外发送数据时,只需要将待发送的数据写入发送SBUF中,通过TxD引脚将数据发送出去。当一帧数据发送完成后,内部硬件自动置位TI,即TI=1,请求中断处理。前面讲过串口中断是单片机中断源的一种,产生中断后,程序进入串口中断函数。同样,当单片机接收到一帧数据后,内部硬件自动置位RI,即RI=1,请求串口中断处理,进入串口中断函数。读取接收SBUF可获得通过串口接收到的数据。
在单片机内部编程中两个寄存器共用 SBUF 这个名字,那么怎么区分是对发送还是接收 SBUF 进行操作呢?由于这两个寄存器一个只能读、另一个只能写。当程序中往 SBUF 中写入数据时,则代表操作发送 SBUF,当程序中读取 SBUF 的数据时,则代表操作接收 SBUF。
通过上面的介绍,无论是接收到一帧数据,还是发送完一帧数据,程序都会产生串口中断,那么在串口中断程序中是怎么区分来的是接收中断还是发送中断呢?在单片机串口中断程序开始的地方通过查询中断标志位 TI、RI 哪个为 1 来判断串口中断的类型。
和单片机的其它功能的实现一样,通过配置相应的特殊功能寄存器来实现串口通信的功能。与 STC89C52 系列单片机串口通信相关的寄存器如下表所示:
熟练掌握上述特殊功能寄存器的应用便能掌握 STC89C52 系列单片机的串口功能应用。
STC89C52 系列单片机共设有两个控制寄存器:串行口控制寄存器 SCON 和波特率选择特殊功能寄存器 PCON。SCON 用于设置串口的工作方式和某些控制功能,寄存器格式如下表所示:
其中,SM0、SM1 共同组合确定串口的工作方式:
如上所示,单片机共有 4 种工作方式,各工作方式的主要区别为数据的位数以及波特率的不同。方式 0、1 均为 8 位数据模式,方式 2,3 为 9 位数据模式。方式 0,2 为固定波特率模式,方式 1,3 为波特率可变模式。根据应用场合的不同可以选择相应的工作模式。方式 1,3 最为常用。
SM2:为方式 2 或方式 3 多机通信控制位。
REN:允许/禁止串行接收控制位。有软件置位,当 REN=1 时,允许接收 RxD 引脚数据,当 REN=0 时,禁止接收串行数据。
TB8:在方式 2 或方式 3,它为要发送的第 9 位数据,按需要进行置位或清零。例如可作为数据的校验位。
RB8:在方式 2 或方式 3,是接收到的第 9 位数据。
TI:发送中断请求标志位。在发送完数据后,由内部硬件自动置位,即 TI=1,向主机请求中断,响应中断后必须用软件复位,即 TI=0。
RI:接收中断请求标志位。在发送完数据后,由内部硬件自动置位,即 RI=1,向主机请求中断,响应中断后必须用软件复位,即 RI=0。
在中断请求 TI 或 RI 响应完串口中断函数后,必须由软件进行复位,否则将程序将多次进入中断函数。
PCON 为串口波特率选择特殊功能寄存器,格式如下:
SMOD:波特率选择位。当软件置位 SMOD,即 SMOD=1,则使串口工作方式 1,2,3 的波特率加倍,当软件清零 SMOD,即 SMOD=0,波特率不加倍。
中断允许寄存器 IE 的 ES 位为串行口中断允许位,ES=1,允许串行口中断,ES=0,屏蔽串行口中断。
根据上面介绍,我们大致可分析出单片机在进行通信之前要对上述寄存器进行设置。通过设置寄存器 SCON 选择串口工作方式,允许串口接收数据。设置 PCON 寄存器是否波特率加倍,设置中断控制寄存器 IE 允许串口中断。
由表 10-4 所示,STC89C52 系列单片机工作在方式 1,3 时,波特率不仅与 SMOD 值有关,还与定时器 1 的溢出率有关。
方式 1,3 波特率 =2SMOD/32x(定时器 1 的溢出率)
其中,SYSclk 为系统时钟频率,在 RY-51 单片机系统时钟为晶振时钟频率 11.0592MHz。
其中,TH1 为定时器工作在模式 2 的初始值,当定时器工作在模式 2 时为 8 位自动重装模式,当定时器产生溢出时,系统会自动把 TL1 的值自动重载到 TH1 中开始重新定时,因此,在单片机编程过程中只需在初始化时将初始值同时赋值给 TH1,TL1 保证波特率正常的产生。
由上面公式计算可得,当单片机工作在 12T 模式下,定时器 1 初始值:
TH1 =TL1=256- SYSclk x2SMOD/32 /12/波特率
当单片机工作在 6T 模式下,定时器 1 初始值:
TH1 =TL1=256- SYSclk x2SMOD/32 /6/波特率
单片机与单片之间通可以采用串口直接相连的方式进行,而计算机一般并没有配置与单片机接口一致的的串行通信接口,但一般计算机都配备了 USB 接口。在我们前面经常使用到的通过 USB 将程序下载到单片机,实际上是在计算机 USB 与单片机之间增加了一个串口转 USB 模块。在 RY-51 单片机开发板是配置了串口转 USB 芯片 CH340G,硬件原理图如下所示:
如图所示,USB+、USB-分别连接到计算机 USB 接口的数据正负端,USB_RX、USB_TX 分别连接到单片机的 P3.0(RxD)、P3.1(TxD)引脚。在计算机安装好串口转 USB 驱动后,便可实现计算机与单片机之间的通信。我们经常使用串口调试助手来实现单片机与计算机专家的通信,串口调试助手软件主界面如下图所示:
如上图所示,串口调试助手界面主要包含三大块,首先为通信方式选择区,根据通信双方的约定选择合适的参数,数据发送区,将数据输入到这个区域,点击发送可将数据发送到单片机,接收数据显示区,顾名思义计算机接收到单片机发来的数据将显示到该区域。
前面介绍了单片机串口工作原理,这节我们结合上面内容举例说明串口的实际应用。程序实现的功能为:首先由计算机通过串口调试助手往单片机发送一个数据,当单片机接收到数据后翻转 led0 小灯,并将接收到的数据加 1 发送给计算机,发送完数据后翻转 led1。串口应用程序编写顺序如下:
/*----------------------------------------------------
** 串口应用程序
** 串口工作方式1,定时器1工作模式2
** 将接收到的数据加1发送回去,收到数据时翻转led0
** 发送完数据时翻转led1
**
----------------------------------------------------*/
#include< reg52.h >
#define uchar unsigned char
#define uint unsigned int
#define FOSC 11059200 //单片机晶振频率
#define BAUD 9600 //波特率设置为9600
sbit led0 = P1^0; //led0位声明
sbit led1 = P1^1; //led1位声明
void main()
{
SCON = 0x50;//串口配置为工作方式1
PCON = 0; //波特率不加倍
TMOD = 0x20;//设置定时器1为8位自动重装模式
TH1=TL1= 256- FOSC/32/12/BAUD;//定时器1赋初始值
ET1 = 0;//禁止定时器1中断
TR1 = 1;//启动定时器1
ES = 1;//允许串口中断
EA = 1;//开总中断
while(1);
}
//串口中断函数
void Uart_r(void) interrupt 4
{
if(RI)
{
RI = 0; //清零接收中断
SBUF = SBUF+1; //读取接收到的数据加1并发送出去
led0 = ~led0; //翻转led0
}
if(TI)
{
TI = 0; //清零发送中断
led1 = ~led1; //翻转led1
}
}
将上述代码编译后,下载到开发板进行测试,打开串口调试助手软件,设置后下图中红框中参数,点击打开串口,如果打开串口成功,软件左上角红色指示灯灯会点亮。
在数据发送区输入 16 进制数“EF”,点击“手动发送”按钮将数据“EF”发送给单片机,此时观察到串口调试助手数据接受区接收到 “F0”,接收到的值正好为发送的数值加 1,与我们预期的结果相同。同时观察 RY-51 开发 led0,led1 小灯也发生了翻转。
上面的程序只是发送单个字节的程序,下面举例说明字符串的发送方法。功能为当 RY-51 开发板上电后向计算机发送指定的字符串。为了程序的可读性,我们将定时器的初始化放到初始化函数 Uart_int()。代码如下所示:
/*----------------------------------------------------
** 串口应用程序
** 串口工作方式1,定时器1工作模式2
** 将接收到的数据加1发送回去,收到数据时翻转led0
** 发送完数据时翻转led1
** 上电后向单片机发送指定字符串
**
----------------------------------------------------*/
#include< reg52.h >
#define uchar unsigned char
#define uint unsigned int
#define FOSC 11059200 //单片机晶振频率
#define BAUD 9600 //波特率设置为9600
sbit led0 = P1^0; //led0位声明
sbit led1 = P1^1; //led1位声明
sbit dir485 = P3^7;
uchar busy = 0;//发送完一帧数据标志位
void Uart_init(void);//串口初始化函数声明
void Send_String(char *s);//字符串发送函声明
void main()
{
Uart_init(); // 串口初始化
EA = 1; //开总中断
Send_String("RY-51 STC89C52 Uart test!n"); //发送字符串
while(1);
}
//串口中断函数
void Uart_r(void) interrupt 4
{
if(RI)
{
RI = 0; //清零接收中断
dir485=1;
SBUF = SBUF+1; //读取接收到的数据加1并发送出去
// led0 = ~led0; //翻转led0
}
if(TI)
{ dir485=0;
TI = 0; //清零发送中断
led1 = ~led1; //翻转led1
busy = 0; //发送完数据,清除发送完标志位
}
}
//串口初始化函数
void Uart_init(void)
{
SCON = 0x50;//串口配置为工作方式1
PCON = 0; //波特率不加倍
TMOD = 0x20;//设置定时器1为8位自动重装模式
TH1=TL1= 256- FOSC/32/12/BAUD;//定时器1赋初始值
ET1 = 0;//禁止定时器1中断
TR1 = 1;//启动定时器1
ES = 1;//允许串口中断
}
//字符串发送函
void Send_String(char *s)
{
while(*s) //检查字符串是否到了结尾
{
while(busy); //等待上一帧数据发送完毕
busy = 1;
dir485=1;
SBUF = *s++; //发送当前字符,并将字符指针移到下一个字符
}
dir485=0;
}
将程序编译后,下载到 RY-51 开发板,设置后串口调试助手,并不选中“十六进制显示”,给开发板重新加电,收到的指定字符串如下图所示:
本章介绍了51单片机串口通信的基本原理。
全部0条评论
快来发表一下你的评论吧 !