说明:此文章仅是我学习过程中的一些记录,如有侵权,请联系我删除,文章中难免有遗漏错误之处,欢迎指出。
目录
三、编写一个串口通讯程序,收到计算机通过串口发来的信息,并相应的点亮发光二极管(查询法或者中断法)
四、编写一个串口通讯程序,利用中断法,单片机收到什么,发什么回去给计算机
一、计算机串行通信基础
主要是了解一些基本的概念。
串口是单片机的重要知识。
计算机通信是指计算机与外部设备或计算机与计算机之间的信息交换。
1.并行通信
2.串行通信
串行通信有两种,一种是异步通信,比较常用,另一种是同步通信,用得少。
①异步通信
异步通信是指通信的发送与接收设备使用各自的时钟控制数据的发送和接收过程。为使双方的收发协调,要求发送和接收设备的时钟尽可能一致。
异步通信是以字符(构成的帧)为单位进行传输,字符与字符之间的间隙(时间间隔)是任意的,但每个字符中的各位是以固定的时间传送的,即字符之间不一定有“位间隔”的整数倍的关系,但同一字符内的各位之间的距离均为“位间隔”的整数倍。
数据格式是一个重点,我们最好能够记住。
空闲:最左侧的空闲,即上面提到的间隙。
起始位:每一帧要先有起始位,这样接收设备才知道下面要开始传输信号了。起始位用低电平表示。
校验位:校验位在不需要校验时可以不要,此时校验位也作为数据位传输数据。
停止位:用高电平表示,代表传输结束。
②同步通信(了解即可)
注意:本型号单片机用的是异步串行口,因此只需要重点掌握异步串行通信即可,对于并行通信,以及同步通信,了解即可。(不确定其他型号的单片机是否也是异步串行口,不过我感觉应该大多数都是,对于其他型号的单片机,如果要编写串行通讯程序,可以参照其文档中的例程进行编写,这里仅提供一种思路)
③串行通信的错误校验
因为第三种常用于同步通信,所以一般程序中不会用到,还有就是,本文的代码不涉及错误校验,作为初学者,能发送接收即可。
3.传输速率与传输距离
①传输速率
比特率是每秒钟传输二进制代码的位数,单位是:位/秒(bps)。如每秒钟传送240个字符,而每个字符格式包含10位(1个起始位、1个停止位、8个数据位),这时的比特率为:
10位×240个/秒 = 2400 bps
②传输距离与传输速率的关系
串行接口或终端直接传送串行信息位流的最大距离与传输速率及传输线的电气特性有关。当传输线使用每0.3m(约1英尺)有50PF电容的非平衡屏蔽双绞线时,传输距离随传输速率的增加而减小。当比特率超过1000 bps 时,最大传输距离迅速下降,如9600 bps 时最大距离下降到只有76m(约250英尺)。
4.串行通信接口标准
串行通信的数据要通过接口来传输,有几种常用的接口标准。
一种是RS-232C接口,它的电平是RS-232C电平,单片机上用的是TTL电平,所以它们的高低电平表示方式不同,这个在前面也有提到过,因此需要进行转换。
最早用到的是MC1488以及MC1489型号的芯片,比较麻烦,需要用2个芯片来分别转换,郭天祥实验板上用的是MAX232型号的芯片,这种芯片集成了上面两款芯片的功能。
采用RS-232C接口存在的问题:
因为这些问题,所以后面有升级版的RS-422A接口:
以及RS-485接口:
如同上面提到的,相应的,RS-485接口需要用MAX485芯片来进行电平转换。
二、80C51的串行口
1.80C51串行口的结构
注意:用两个缓冲器SBUF,可以分别通过
a = SBUF; 以及 SBUF = a;
来进行接收和发送。
2.80C51串行口的控制寄存器
3.80C51串行口的工作方式
这里只介绍方式1。
其中写入SBUF的脉冲当执行代码例如SBUF = 0x01;时会出现。
4.波特率的计算
三、编写一个串口通讯程序,收到计算机通过串口发来的信息,并相应的点亮发光二极管(查询法或者中断法)
1.初始化
如果使用的是中断法,则需要先进行中断设置。
下面进行具体介绍。
2.第一步是通过编程TMOD寄存器确定定时器T1的工作方式
TMOD寄存器的介绍在定时器篇,这里不详细介绍了。需要注意的是这里要用到的定时器工作方式是工作方式2,而不是之前常用的工作方式1。原因是工作方式1在每次进入中断时都需要重新进行一次初值的装载,需要用软件进行装值,会花费一定的时间,当定时器要求精确时就不用这种方式了,选用方式2,自动重装,但是它有个限制,就是只有8位,不过用在串口这里也不影响。
这里再介绍一下为什么串口通讯会用到定时器,当然这是我自己理解的,我还不太确定,后面我确定之后再进行修改,这里仅作为理解。正如上面提到的,波特率。串口通信是用定时器作为基础,例如这里常用的T1定时器,当定时器T1溢出时,进行一次中断,读取一次数据,所以波特率和定时器T1的初值有关。
令T1工作在方式2,所以代码如下:
TMOD = 0x20;
3.第二步是计算T1的初值,装载TH1、TL1
这一步是设置波特率,可以按你所需的波特率赋不同的初值,这里我们用9600。
从上面的表格可以看到,当单片机所用的晶振是11.0592MHz,并且SMOD = 0时,初值是十六进制的FD。
因此代码如下:
TH1 = 0xfd;
TL1 = 0xfd;
4.然后第三步就是启动定时器T1了
TR1= 1;
5.第四步是通过编程SCON寄存器确定串行口控制
SM0、SM1是工作方式选择位,我们选用方式1。
SM0 = 0;
SM1 = 1;
SM2、TB8主要用于方式2和方式3,因此这里置0即可。
默认为0,不用修改了
REN是允许串行接收位,需要打开。
REN = 1;
RB8在校验的时候才会用到,作为校验位,不用校验位,所以置0即可,不用管。
默认为0,不用修改了
6.查询法
在SCON寄存器还有两个位TI、RI初始化的时候不用修改,原因如下:
RI:接收中断标志位。在方式1时,在串行接收停止位的中间时,由内部硬件使RI置1,向CPU发中断申请,必须在中断程序中,用软件将其清0,取消此中断申请。
意思是,当接收停止时,RI会被硬件置1,所以我们可以据此判断是否是接收停止,并且要注意要对其清0。据此我们编写程序如下:
#include <reg52.h>
void main()
{
TMOD = 0x20;//设置定时器1为工作方式2
TH1 = 0xfd;
TL1 = 0xfd; //波特率是9600
TR1 = 1;
REN = 1;
SM0 = 0;
SM1 = 1;
while(1)
{
if(RI == 1) //查询法,RI==1表示接收停止
{
RI = 0; //清除标志位
P1 = SBUF;//P1等于接收到的内容
}
}
}
因为单片机开发板上的P1口连接着一排发光二极管,所以当计算机通过串口发送数据时,发光二极管会相应点亮。
在查询法中,是根据RI的状态进行判断,并没有用到中断服务程序。
这里有个奇怪的点我也不太明白,就是若P1不是等于SBUF,而是使P1 = 0xfe,即本意是使无论收到什么程序,都点亮第一个发光二极管,但是如果这样的话,并不能达到想要的结果,因为第一个发光二极管会常亮,本来以为是单片机上电后会想计算机发送问是否有新数据需要下载,会通过串口发送数据,即自动发送的,但是复位之后仍然会这样,而如果是P1=SBUF则不会出现这种情况,我认为如果是有发送数据的话,那么等于SBUF使应该也会使P1口控制的灯有一些改变,但是也并没有,可能是连着计算机就会这样。
还有一点就是,if语句也可以改用while,如下:
while(!RI);
注意要分号结束。
7.中断法
可以看到,RI置1后,会提出中断申请,如果有中断服务程序,它会进入中断服务程序,所以我们可以在中断函数中进行处理。
#include <reg52.h>
void main()
{
TMOD = 0x20;//设置定时器1为工作方式2
TH1 = 0xfd;
TL1 = 0xfd;
TR1 = 1;
REN = 1;
SM0 = 0;
SM1 = 1;
EA = 1; //开总中断
ES = 1; //开串口中断
while(1);
}
void ser() interrupt 4 //中断法
{
RI = 0;
P1 = SBUF;
}
用中断法需要注意要开总中断和串口中断。
四、编写一个串口通讯程序,利用中断法,单片机收到什么,发什么回去给计算机
这里要注意一点就是,单片机除了接收RI会进行中断,发送TI也会进入中断,所以在发送时我们要对TI进行处理。并且,接收和发送的中断进入的是同一个中断函数。
TI:发送中断标志位。在方式1,在串行发送停止位的开始时,由内部硬件使TI置1,向CPU发中断申请。在中断服务程序中,必须用软件将其清0,取消此中断申请。
处理的方式是,在发的时候,关闭中断,具体代码如下:
#include <reg52.h>
#define uint unsigned int
#define uchar unsigned char
uchar flag,a;
void main()
{
TMOD = 0x20;
TH1 = 0xfd;
TL1 = 0xfd;
TR1 = 1;
REN = 1;
SM0 = 0;
SM1 = 1;
EA = 1;
ES = 1;
while(1)
{
if(flag == 1)
{
ES = 0; //关中断,因发信息也会进入中断,关中断避免死循环
flag = 0;
SBUF = a;//把a发走,两个SBUF不同
while(!TI); //等到发送结束
TI = 0;
ES = 1; //开中断
}
}
}
void ser() interrupt 4
{
RI = 0;
a = SBUF; //a保存收到的信息
flag = 1; //标志收到串口发送的信息了
}
如果我们先发送某个字符,也可以使
SBUF = 'A';
SBUF = a;
while(!TI);
TI = 0;