1. 基于proteus的51单片机开发实例19-两个单片机的串口通信
1.1. 实验目的
本实例我们来了解51单片机的串行通信RS-232的基本原理、使用方法、编程实现。
1.2. 设计思路
图1 两个单片机的串口通信
用两个51单片机的实现串口通信,其中一个作为串口数据发送端,另一个作为串口数据接收端,为了能够直观地看到串口数据的收发,电路中分别在两个单片机上都接了8个LED,用于指示发送的数据和接收的数据,这样便于比较发送和接收的数据是否一致。
1.3. 基础知识
通过本实例,理解、熟悉串行通信的基础知识、常用术语、实验电路、编程方法。
1.3.1. 串行通信的基本概念
单片机与外设之间的数据交互通常有两种方式:并行通信和串行通信。
所谓并行通信,是指一组数据的各个位同时进行传输的通信方式,如下图所示。
图2 并行通信
所谓串行通信,是指一组数据按照一位一位的顺序传输的通信方式,如下图所示。
图3 串行通信
并行通信速度快,但是数据线多,结构复杂,成本高,一般是用于近距离传输、数据位数不多的情况。
串行通信速度慢,但数据线少,接线简单,适用于远距离传输。
1.3.2. 串行通信的基础术语
串行通信有两种基本方式:异步串行通信和同步串行通信。我们在本例中使用的是RS-232串行通信,RS-232串行通信方式是异步串行通信。所以我们这里重点了解与异步串行通信的基本术语。
1.3.2.1. 异步通信
一种基于“位(bit)”数据的通信方式,不要求通信双发(接收方、发送方)具有同步时钟信号,但是需要收发双方具有相同的数据帧结构和波特率,通信过程中的数据收发不需要是连续的。
在实际应用中,异步串行通信的数据是“一帧一帧”传送的,即一帧数据传送完成后,才会再传送下一帧数据。每帧数据发送完后,可以停顿,在停顿期间,数据线保持高电平。
异步通信的数据格式如下图所示。
图4 异步串行通信数据格式
其中数据可以是5~8位数据,奇偶校验位可以通过设置为有或没有校验位。
我们可以看到,在一帧数据中,必须具备的要素是:起始位、数据、结束位。其中起始位必须是低电平,结束位必须是高电平。
1.3.2.2. 波特率
在异步串行通信中,为了确保通信双方能够正确的发送(接收)到数据,必须做好约定,约定哪些内容呢?一是数据帧的格式,二是每一位数据的传送时间,也就是波特率。
波特率是指单位时间内传送的二进制数据的位数。波特率的单位是bit/s。它是串行通信的非常重要的指标及参数。
例如波特率是9600,则表示每秒钟能够发送9600位数据,如果一个字节按照8位计算,就是1200字节数据(但是异步串口通信有起始位、停止位、奇偶校验位等,所以并不能发送1200字节)。
1.3.2.3. 奇偶校验位
奇偶校验位的目的是为了防止通信过程中受到干扰导致某一位或多位数据出错。
一般情况下,异步串行通信中奇偶校验位可通过设置相应的控制寄存器自动生成、自动校验。
1.3.3. 51单片机串行口的内部结构
下图所示是51单片机串行口的内部结构。
51单片机串行口内部结构
从图中可以看出,51单片机的串行口主要有如下几部分构成
数据缓冲器SBUF
数据缓冲器SBUF有两个,一个负责接收数据,另一个负责发送数据。
输入移位寄存器
在接收控制器的控制下,将数据“一位一位”的放入缓冲器SBUF。
串行控制寄存器
设置并控制串行通信的工作方式
定时器T1
用作串行通信的波特率发生器。也就是说,在使用串口通信时,51单片机的定时器T1不能再用作定时或计数功能,只能作为串口的波特率发生器。
串行通信的控制
51单片机中,进行串口通信时,需要通过对相关寄存器进行配置,才能让串口正常工作。相关的寄存器有4个。分别是SCON,PCON,IE,IP。其中IE寄存器用来控制是否允许中断触发。IP用来设置中断优先级,这两个寄存器我们之前已经了解了,在此不再赘述。(本实例中不使用串口中断)
串口控制寄存器SCON
串口控制寄存器SCON用于设置串行口的工作方式。同时还可以监视串行口工作状态、发送与接收的状态控制等。
SCON各个位的含义如下图所示。
SCON
工作方式
电源控制寄存器PCON
该控制器中与串口有关的只有第7位SMOD位,这一位为波特率选择位,如果SMOD=0,则波特率为设置的波特率,如果SMOD=1,则波特率为设置波特率的2倍。
1.4. 电路设计
图1是本实例的电路图。电路中使用了两个单片机,一个发送串口数据,一个接收串口数据,每个单片机都连了8个LED,用于指示发送的数据和接收的数据,这样能够直观地查看数据正确性。
1.5. 程序设计
本实例有两个单片机,每个单片机都有各自代码程序,程序代码如下:
发送程序
- #include<at89x52.h> //
- //发送的数据
- unsigned char code LedTab[16]={0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0xaa,0xbb,0xcc,0xdd,0xee,0xff,0x00};
- //串口数据发送函数,采用查询方式
- void Send(unsigned char ucData)
- {
- SBUF=ucData;//先把要发送的数据放到发送缓冲器
- while(TI==0);//等待发送完毕,
- TI=0;//发送完后,置0,便于下次发送数据
- }
- //延时函数
- void delay(void)
- {
- unsigned int m,n;
- for(m=0;m<800;m++)
- for(n=0;n<250;n++);
- }
- void main(void)
- {
- unsigned char i;
- TMOD=0x20; //定时器工作方式2
- SCON=0x40; //串口工作方式1
- PCON=0x00; //波特率不加倍
- TH1=0xfd; //定时器赋初值
- TL1=0xfd; //
- TR1=1; //打开定时器,开始工作
- P1=0xff;//先让所有LED灭
- while(1)
- {
- for(i=0;i<16;i++) //循环发送16个数据
- {
- P1=LedTab;//发送前,先让LED指示要发送的数据
- Send(LedTab); //通过串口,查询方式发送数据
- delay(); // 发送完延时一会,便于观察LED指示是否正确
- }
- }
- }<code></code>
接收程序
- #include<at89x51.h>
- //串口接收函数,采用查询方式
- unsigned char Receive(void)
- {
- unsigned char ucData;
- while(RI==0) ; //等待接收中断标志置位,置位后说明有数据过来
- RI=0; //手动清除标志
- ucData=SBUF; //从接收缓冲器取数据
- return ucData;
- }
- void main(void)
- {
- TMOD=0x20; //定时器工作方式2
- SCON=0x50; //串口工作方式1,允许接收
- PCON=0x00; //波特率不加倍
- TH1=0xfd; //波特率9600时的定时器初值设置
- TL1=0xfd; //
- TR1=1; //启动定时器
- REN=1; //允许串口数据接收
- P1=0xff;
- while(1)
- {
- P1=Receive(); //将串口接收的数据通过P1口的8个LED显示
- }
- }
1.6. 实例仿真
程序编译完成后,即可装载到仿真电路中,其中send.hex装到发送数据单片机中,receive.hex装入接收数据单片机中。开始仿真,注意观察发送单片机所连的8个LED发光情况,比较同一时间接收单片机所连的8个LED发光情况,这两个应该一致。
|