串行口通信
一、并行与串行通信
1、并行通信
将数据每一字节的各位用多条数据线同时进行传送。每个字节8位,由8条数据线同时传送一位,同一时间总共传送1个字节。除了8条数据线外,还有一条信号线和若干控制线。
由于发送设备和接收设备之间需要的接线较多,采用长途信号传送使得成本激增,所以并行通信一般用于短途通信。
2、串行通信
将数据的一个字节分成8位,一位一位的在一条传输线上传送,数据线只需要一条即可,此外还需一条公共信号地线和若干控制信号线。由于只需要一条数据线,在长途通信中成本不会很高,并且可以利用电话网等现有的设备。
3、并行通信和串行通信优缺点
1)并行通信
优点:控制简单,相对传输速度快
缺点:长距离传输时成本高,一个字节的8位同时发送和接收存在困难,较串行通信抗干扰能力弱
2)串行通信
优点:传输线少,长距离传输成本低,可利用现有设备进行传输,较并行通信抗干扰能力强
缺点:控制比较复杂,较并行通信控制更复杂
4、异步串行通信、同步串行通信
1)异步串行通信
通信的接收方、发送方使用各自的时钟控制数据的接收和发送。(为使双方收发协调,要求发送和接收设备的时钟尽可能一致)
2)异步串行通信的俩个指标——字符帧格式、波特率
异步串行通信中数据通常以字符或者字节为单位组成字符帧传送。字符帧在发送设备逐帧发送,接收设备逐帧接收。发送端和接收端由各自的时钟控制,俩个时钟互不影响、彼此独立。
字符与字符之间的间隔是任意的。任一字符内部的每一位之间的间隔是固定的。
波特率之后着重讲解
3)异步串行通信的常用总线类型
①RS-232
通常 RS-232 接口以9个引脚(DB-9) 或是25个引脚 (DB-25) 的型态出现
S232接口电平值较高,其任何一条信号线的电压均为负逻辑关系。即:逻辑“1”为-3V ~ -15V;逻辑“0”为+3V ~ +15V
RS232与TTL电平不兼容,故需要使用电平转换器才能让RS232与TTL电路连接。如MAX232把TTL电平从0V和5V转换到3V~15V或-3V ~ -15V
RS232传输距离较短,若距离过长,会导致信号失真、抗干扰能力较差。
②RS-485
在要求通信距离为几十米到上千米时,广泛采用RS-485串行总线。RS-485采用平衡发送和差分接收,因此具有抑制共模干扰的能力。加上总线收发器具有高灵敏度,能检测低至200mV的电压,故传输信号能在千米以外得到恢复。
RS-485采用半双工工作方式,所以收、发双方不能同时进行收和发,同一时间只有一方在发送、另一方在接收。
USB转RS485:
③USB(通用串行总线)
4)异步串行通信的特点
不要求收发双方时钟严格一致,实现容易,设备开销小,但每个字符需要附加2~3位用于起始位、校验位、停止位,各帧之间还有间隔,因此传输效率不高。
单片机与单片机、单片机与计算机之间的通信,都为异步串行通信方式
5)同步串行通信
要建立发送方时钟对接收方时钟的直接控制,使双方达到完全同步。此时,传输数据的位之间的距离均为“位间距”的整数倍,同时传送的字符间不留间隙即保持字符同步关系。
同步分为外同步和自同步。
外同步:
自同步:
6)串行通信的奇偶校验
发送数据时,在数据位后面尾随的一位是奇偶校验位。
奇校验时,数据中的1的个数+奇偶校验位中1的个数=奇数,若不为奇数,说明数据传输中出现错误。
偶校验时,数据中的1的个数+奇偶校验位中1的个数=偶数,若不为偶数,说明数据传输中出现错误
还想进一步了解的朋友可以去参考《通信原理》的【差错控制编码】——>【奇偶监督码】和【二维奇偶监督码】
二、MAX232实现RS232电平与TTL电平转换(板子嵌用)
中文参考手册:
单片机原理图:
图一可知:T1in与T2in为TTL电平输入,T1out、T2out为RS232输出。R1in与R2in为RS232电平输入,R1out与R2out为TTL电平输出。
所以MAX232可以将TTL电平转为RS232电平,也可以将RS232电平转为TTL电平
图二可知,单片机芯片P30接单片机R1out,为MAX232芯片的TTL电平输出引脚,单片机P31接T1in,为MAX芯片的TTL电平输入引脚。
信号流程:
单片机通过P31引脚发送信号,到MAX232的T1in引脚,经过转为,变为RS232电平,传输到其他设备,完成信号传送。
其他设备传送的信号,送入MAX232,进过转换,从R1out输出TTL电平,进入单片机P30引脚,单片机接收到其他设备的信号。
所以单片机P30是信号接收引脚,P31是信号发送引脚。
打开8051芯片,发现P30的确是接收引脚,P31的确是发送引脚。
再看MAX232的单片机原理图(上图二):
注:J1是焊接在板子上的接口,我的板子是公口,外插母口进行信号传送
J1的2引脚接R1in,为RS232电平的输入引脚,所以外接的母口的对应2引脚为其他设备的信号输出端。(指另一端设备备接口转换器的输出端)
J1的3引脚接T1out,为RS232电平的输出,所以J1的3引脚接外接母口的对应3引脚,接其他设备的输人端。(指另一端设备备接口转换器的输入端)
总结:
.
发送:单片机P31引脚——>MAX232的T1in引脚——>经转换在MAX232的T1out引脚输出——>J1公口的3引脚——>外插母口的3引脚——>其他设备的接口转换器输入端(若接口也为MAX232,则接R1in或R2in)
接收:其他设备的接口转换器输出端(若接口也为MAX232,则为T1out或T2out)——>外接母口引脚2——>本单片机J1的2引脚——>MAX232的R1in——>经转换后MAX232的R1out输出——>单片机P30引脚
三、波特率与定时器初值
之前总结异步串行通信的俩个指标——字符帧格式与波特率时,未对波特率进行总结,现总结波特率以及与定时器初值的关系
1、波特率
单片机或计算机在串口通信时的速率用波特率表示,它表示每秒传输二进制代码的位数,即1波特=X位/秒,单位是bps(位/秒)。如当异步串行传输包含1个起始位、1个停止位、8位数据位,每秒传输200个字符,则此时的波特率为10×200=2000bps。
《通信原理》中将传信率(又称比特率)Rb,表示传输的信息速率,单位是比特/秒,b/s或bps。传码率RB表示码元传输速率,单位为波特(Baud),简记B,有兴趣的朋友可以去了解一下。
2、波特率计算
在串行通信中,收发双方的波特率需要约定使用同一波特率。通过编程可对单片机串行口设定工作方式,从而指定波特率。
四种工作方式的波特率:
①方式0:fosc/12
②方式1:(2SMOD/32)×(T1溢出率)
③方式2:(2SMOD/64)×fosc
④方式3:(2SMOD/32)×(T1溢出率)
可以看出只有方式1的波特率是固定的
上述式子中,fosc为系统时钟频率,一般为12MHZ或者11.0592MHZ(我的板子是11.0592MHZ)。SMOD是电源管理寄存器PCON的最高位(接下来总结)
T1溢出率即定时器T1溢出的频率,即1s溢出多少次,如之前总结《(四)定时器中断》时,设置为每50ms溢出一次(和寄存器装载的初值有关系),则1s溢出20次,溢出率为20HZ。
方式0的波特率为系统晶振的12分频,之前总结的+1计数器的时钟来源之一就是系统时钟的12分频。
一般情况下,都是用方式1,其他几种方式几乎不会用。
3、溢出率、波特率、定时器综合分析
由上面的工作方式0、1、2、3的波特率计算公式可以得出工作方式1、3与计数器T1的溢出率有关系,由于实际应用中,常见的是工作方式1,现以工作方式1为例来分析。
单片机通信时,一般需要很高的波特率,如常见的9600波特率,可以先选定通信的波特率,然后再求T1溢出率,而1/(T1溢出率)为T1定时器每中断溢出的时间Xms。通过以下公式:
TH1=(65536-X)/256
TL1=(65536-X)%256
可以得出定时器的装载初值。这样,定时器中断的初值、溢出率、波特率都已解决。
51单片机只能通过这种方式规定波特率,不像Arduino、openMV这些单片机,可以在程序中直接指定波特率。
4、定时器工作方式选择——工作方式2
通信的波特率很大——>T1溢出率很大——>定时器1的中断间隔很小——>定时器装载初值很大。因为中断间隔很小,若采用之前的定时器1工作方式1,每次中断都需要软件重新装载初值,执行装载初值语句会浪费一定的时间并且不太稳定,所以定时器的工作方式1在这里不合适。采用定时器的工作方式2,使用8位初值自动装载计数器/定时器,进入中断服务函数后,无需进行任何操作,使定时器溢出速率变得绝对稳定。
使用工作方式2的8位自动重装载计数器/定时器1时,初值的计算公式也发生了变化,以波特率9600为例:(要区分定时器工作方式2和串口工作方式1)
①SMOD=0、12MHZ晶振
串口工作方式1的波特率:9600bps
SMOD=0(波特率不加倍)
T1溢出率=9600×32=307200HZ
中断间隔:1/307200=3255ms≈3.26us(即每3.26us中断一次)
12MHZ晶振的一个机器周期1us(即每计一次数1us)
每计3个数为3us
所以每计三个数中断一次,初值:256-3=253
②SMOD=1、12MHZ晶振
串口工作方式1的波特率:9600bps
SMOD=1(波特率加倍)
T1溢出率=9600×16=153600HZ
中断间隔:1/153600≈6.5us(即每6.5us中断一次)
12MHZ晶振的一个机器周期1us(即每计一次数1us)
每计5个数为5us
所以每计三个数中断一次,初值:256-5=251
12MHZ晶振时有很大误差,毕竟9600波特率是针对11.0592MHZ晶振的
③SMOD=0、11.0592MHZ晶振
串口工作方式1的波特率:9600bps
SMOD=0(波特率不加倍)
T1溢出率=9600×32=307200HZ
中断间隔:1/307200=3255ms≈3.26us(即每3.26us中断一次)
11.0592MHZ晶振的一个机器周期1.085us(即每计一次数1us)
3.26÷1.085=3
所以每计三个数中断一次,初值:256-3=253
④SMOD=1、12MHZ晶振
串口工作方式1的波特率:9600bps
SMOD=1(波特率加倍)
T1溢出率=9600×16=153600HZ
中断间隔:1/153600≈6.5us(即每6.5us中断一次)
11.0592MHZ晶振的一个机器周期1.085us(即每计一次数1us)
6.5÷1.085=6
所以每计三个数中断一次,初值:256-6=250
以上四个计算为了回顾之前博客总结的定时器内容,步骤都分开处理,使得中间公式产生约等于,导致最后结果有一点点误差,若省去中间公式,直接一个合公式导出初值,发现11.0592MHZ晶振时,SMOD=0,定时器初值为253,SMOD=1,定时器初值为250,没有误差。
常见波特率:
串口通信的波特率不能太大,否则会超过定时器的最短溢出时间,比如115200波特率就不能使用11.0592MHZ的晶振的串口通信。也正是为了使波特率为标准通信速率时计数器/定时器初值都为整数,才选用11.0592MHZ的晶振。
四、电源管理寄存器PCON与串行口控制寄存器SCON
1、电源管理寄存器PCON(Power Control)
电源管理寄存器用来管理单片机的电源部分,包括上电复位检测、掉电模式、空闲模式等,单片机复位时PCON全部清0。
电源管理寄存器:
位序号 | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
---|---|---|---|---|---|---|---|---|
位符号 | SMOD | (SMOD0) | (LVDF) | (POF) | GF1 | GF0 | PD | IDL |
D7——SMOD与波特率有关。SMOD=0:串口方式1、2、3波特率正常;SMOD=1,:串口方式1、2、3波特率加倍。(只需记D7位)
D6——(SMOD0):与SCON寄存器的SM0有关,为帧错误检测有效控制位,当SMOD0=1,SCON寄存器中的SM0/FE位用于FE(帧错误检测)功能;SMOD0=0,SCON寄存器中的SM0/FE位用于SM0功能,和SM1一起指定串行口的工作方式。单片机复位时SMOD0=0;
D5——(LVDF):无效
D4——(POF):上电复位标志,单片机停电后,上电复位标志为1,可由软件清零。
察看STC89C52技术手册(了解即可):D3——GF1、D2——GF0:俩个通用工作标志位,用户可以自由使用。
D1——PD:掉电模式设定位。PD=0,单片机正常工作;PD=1:单片机进入掉电(Power Down)模式,可由外部中断低电平触发或由下降沿触发或者硬件复位模式唤醒,进入掉电模式后,外部晶振停振,CPU、定时器、串行口全部停止工作,只有外部中断继续工作
D0——IDL:空闲模式设定位,IDL=0:单片机处于正常工作状态;IDL=1:单片机进入空闲(Idle)模式,除CPU不工作外,其余仍继续工作,在空闲模式下可由任一个中断或者硬件复位唤醒。
打开52的头文件:
定义了PCON寄存器
但是PCON寄存器的每一位没有通过sbit进行位定义,所以程序中只能对寄存器操作,而不能操作单独某一位。
若波特率不加倍,SMOD=0,程序:
PCON=0; //00000000
或者不用写上述语句,单片机上电后,PCON每一位都清零,默认SMOD=0。
若波特率加倍,SMOD=1,程序:
PCON=0x80; //10000000
2、串行口控制寄存器SCON(Serial Control)
51单片机的串行口是可编程全双工的通信接口,具有UART(通用异步收发器)的全部功能
51单片机的串行口组成:俩个串行数据缓冲寄存器SBUF(一个发送缓冲寄存器,一个接收缓冲寄存器)、发送控制器、接收控制器、输入移位寄存器及若干控制门电路组成
俩个SUBF数据缓冲寄存器共用一个地址,当执行写指令时,访问发送缓冲寄存器;当执行读指令时访问接收缓冲寄存器。从接收缓冲寄存器中读数据时,前一个字节还没有完全读出,就可以接收下一个字节,若第一个字节还没读出,下一个字节已经接收完毕,则会丢失第一个字节。
位序号 | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
---|---|---|---|---|---|---|---|---|
位符号 | SM0 | SM1 | SM2 | REN | TB8 | RB8 | TI | RI |
D7——SM0:工作方式选择位
D6——SM1:工作方式选择位
SM0、SM1设置串行口工作方式:
SM0 | SM1 | 方式 | 功能说明 |
---|---|---|---|
0 | 0 | 0 | 同步移位寄存器方式(通常用于扩展I/O口) |
0 | 1 | 1 | 10位异步收发(8位数据),波特率可变(由定时器1的溢出率控制) |
0 | 1 | 2 | 11位异步收发(9位数据),波特率固定 |
1 | 1 | 3 | 11位异步收发(9位数据),波特率可变(由定时器1的溢出率控制) |
D5——SM2多机通信控制位,主要用于方式2、3,暂时不用
D4——REN——运行串行接收位,REN=1:运行串行口接收数据;REN=0:禁止串行口接收数据
D3——TB8:方式2、3中发送数据的第9位(暂时不用)
D2——RB8:方式2、3中接收数据的第9位(暂时不用),在方式1时,若SM2=0则接收数据时,停止位进RB8。
D1——TI:发送中断标志位,在串行工作方式0时,当发送第8位结束时,或在其他方式,串行发送停止位开始时,由内部硬件使TI置1,向CPU发出中断申请。在中断服务程序中,必须用软件将其清0,取消此中断申请。
D0——RI:接收中断标志位,当串行工作方式0时接收第8位数据结束,或在其他方式,串行接收停止位开始时,由内部硬件使RI值1,向CPU发出中断申请,在中断服务程序中,必须用软件将其清0,取消此中断申请。
打开52头文件:
可以看到SCON和SBUF寄存器都已经定义
SCON的每一位都已经定义,可以对每一位单独操作。
五、串行口方式1+面向编程
1、收、发时序图
串行口方式1一帧数据共10位,1位起始位、8位数据位、1位停止位(起始位为0,终止位为1)
方式1数据发送时序图:
当数据写入SBUF后,单片机开始发送数据,起始位(0)、8位数据位、停止位(1)等10位数据依次发送,当发送停止位开始时,由内部硬件使TI置1,向CPU发出中断申请。在中断服务程序中,可以软件将其清0,取消此中断申请,也可以不清零,停止数据输出。
方式1数据接收时序图:
首先需要串口接收允许,即REN=1(软件置1),当检测到RXD的电平发生负跳变,表示起始位已经开始,将其移入输入移位寄存器,并开始接收一帧的其他位(8位数据位、1位停止位),当接收到停止位时,将刚刚接收到的9位数据的前8位(即8位数据位)送入接收SBUF中,第9位(即停止位)进RB8(前提是SCON寄存器的SM2=0,之前总结过),并且RI=1,向CPU请求中断。
2、实现流程
①确定定时器T1的工作方式(TMOD寄存器)
T1计数器,所以低4位为0,高四位中,GATE=0,表示定时器开、关只收TR1控制;C/T=0,表示定时器;M1M0=10,表示工作方式2,8位自动重装载寄存器
所以程序:
TMOD=0x20;
②计算T1计数器的初值,转载TH1、TL1;
前面第三部分总结过9600波特率时初值的方法,其他波特率计算方法相似。得出11.0592MHZ晶振波特率为9600,SMOD=0时初值为253,SMOD=1时初值为250。
253——>11111101B——>0xfd
250——>11111010B——>0xfa
因为是八位寄存器,所以TH1和TL1只用到一个(不清楚用到哪个寄存器),TH1与TL1赋同样值就好了
TH1=0xfd; //9600波特率 11.0592MHZ
TL1=0xfd;
这里不妨算一下串口通信的最大波特率,即当+1计数器每+1就中断,根据第三部分的计算方法:
当SMOD=0时
11059200/(X×32×12)=1,解之得:X=28800bps
当SMOD=1时
11059200/(X×16×12)=1,解之得:X=57600bps
通过上述方法,可以得出可用的波特率(51全部可用波特率【全】)如下程序:
/* //11.0592MHZ晶振下
//C51通用波特率配置定时器初值和PCON寄存器
TH1=0xff; //SMOD=0 28800bps
TL1=0xff; //SMOD=1 57600bps
TH1=0xfe; //SMOD=0 14400bps
TL1=0xfe; //SMOD=1 28800bps
TH1=0xfd; //SMOD=0 9600bps
TL1=0xfd;//SMOD=1 19200bps
TH1=0xfa;//SMOD=0 4800bps
TL1=0xfa;//SMOD=1 9600bps
TH1=0xf4;//SMOD=0 2400bps
TL1=0xf4;//SMOD=1 4800bps
TH1=0xe8;//SMOD=0 1200bps
TL1=0xe8;//SMOD=1 2400bps
*/
③启动T1计数器(TCON的TR1软件置1)
当然启动T1之前需要打开全局中断允许位,即EA
=1;打开串口中断允许位,即ES=1;
EA=1//打开全局中断允许
ES=1;//打开串口中断允许位
TR1=1;//打开定时器1
④确定串行口工作方式(SCON寄存器)
一般为工作方式1,即SM0=0;SM1=1;
⑤中断设置(发生中断后,需要处理什么?)
六、经典例题
例1:
在上位机(上位机为电脑端,下位机为单片机)上用串口调试助手发送一个字符X,单片机收到后返回上位机“I get X”,串口波特率为9600bps
分析:
①确定T1计数器工作方式
TMOD=0x20;//工作方式2
②确定T1的初值
TH1=0xfd;//SMOD=0
TL1=0xfd;
③T1中断器配置
EA=1;//打开全局中断允许
ES=1;//打开定时器1中断允许
TR1=1;//启动定时器
④确定串口工作方式
SM0=0;
SM1=1;//工作方式1
REN=1;//允许串行口接收数据
完整程序:
#include <reg52.h>
#define uchar unsigned char
#define uint unsigned int
uchar flag,a,i;
uchar code table[]="I get ";
void main()
{
TMOD=0x20;//工作方式2
TH1=0xfd;//SMOD=0
TL1=0xfd;
EA=1;//打开全局中断允许
ES=1;//打开串口中断允许位
TR1=1;//启动定时器
SM0=0;
SM1=1;//工作方式1
REN=1;//允许串行口接收数据
while(1)
{
if(flag==1)//表示接收缓冲区有数据
{
ES=0;//关闭串口中断,防止下次中断打断这次传送
for(i=0;i<6;i++)//table数组里的字符串末尾还有一个空格
{
SBUF=table[i];//单片机发送消息
while(!TI);//等待传送完成
TI=0;//一位一位的传送,分6次传送,每次传送完成T1都需软件置0
}
SBUF=a;
while(!TI);//等待传送完成
TI=0;
ES=1;
flag=0;
}
}
}//注意是全双工,即同时收发,只要数据写入SBUF,单片机就开始传送数据
void serial() interrupt 4//串行口中断,无论接收到数据还是发生数据,都调用
{
RI=0;//接收数据完毕后发生中断,此时RI硬件置1,需在服务函数中软件置0
a=SBUF;
flag=1;//标志接收缓冲寄存器接收到一帧的数据
}
打开单片机的供电电路,发现单片机的引脚TXD与RXD即P31与P30接了USB接口,所以直接使用下载/供电的USB接口即可传送信号。当然也可以使用板子上的RS232接口,不过需要一个RS232转USB接电脑的USB接口。
下载完程序后,打开串口助手
输入任何字符: