概述
串行接口(Serial Interface)简称串口,也称串行通信接口或串行通讯接口(通常指COM接口),是采用串行通信方式的扩展接口,串行是指数据一位一位地顺序传送,特点是通信线路简单,只要一对传输线就可以实现双向通信,从而大大降低了成本,特别适用于远距离通信,但传送速度较慢
关键词:
1、是设备之间接线互相传输数据的一种方式
2、数据传输时是一位一位顺序传输
3、双向通信,全双工通信
4、传送速度相对较慢
标准和协议
串口之间按照电气标准和协议的不同来分包括:RS-232-C串口,RS-422串口,RS-485串口
RS-232-C
RS-232-C是标准串口协议,最常用的一种协议,比如台式电脑的九针串口,最高传输速率为20kb/s,最大传输距离为15m,适用于本地设备之间通信。该协议为点对点协议,即1对1收发数据,不可以一台设备对多台设备发送数据
RS-422
RS-422由于接收器采用高输入阻抗和发送驱动器比RS232具有更强的驱动能力,故支持点对多的双向通信。即一个主设备,其余为从设备,各从设备可以和主设备之间收发数据,但从设备之间不能通信。
RS-422的最大传输距离为1219米,最大传输速率为10Mb/s。平衡双绞线的长度与传输速率成反比
RS-485
RS-485是从RS-422基础上发展而来的,无论四线还是二线连接方式总线上可多接到32个设备。
串口电平
经常用的串口是UART-异步串口,即运行速度不同的设备的串行数据传输,传输数据时,快速度的设备减速和慢速度的设备的速度匹配,再传输数据
UART包含用RS-232电平的串口和TTL电平的串口
RS-232电平
RS-232电平,逻辑1是-15到-3v的电压,逻辑0是3到15v的电压
某些单片机用的是RS-232电平,若要和笔记本之间串口通信,那么所用的串口则需要是RS-232串口,矩形设备接入到笔记本,9针串口接入到单片机,如图
TTL电平
TTL电平信号应用广泛,TTL(Transistor-Transistor Logic)即晶体管-晶体管逻辑的简称,它是计算机处理器控制的设备内部各部分之间通信的标准技术。
TTL电平,逻辑1是+5v的电压,逻辑0是0v的电压。
数字电路中由TTL电子元器件组成电路的电平是个电压范围,规定:
输出高电平>=2.4V,输出低电平<=0.4V;
输入高电平>=2.0V,输入低电平<=0.8V
也有某些单片机用的是TTL电平,那么这些单片机会引出两个引脚用来接线,TX端口作为发送端口,RX端口作为接受端口。若要和笔记本之间串口通信,那么需要用到usb转TTL设备,矩形接口接入到笔记本,TX针脚和RX针脚通过杜邦线连接到单片机的RX针脚和TX针脚,这样实现串口通信。如图
上官1号单片机,集成了转TTL的ch340芯片,所以不需要使用usb转TTL设备进行数据传输,可以直接用Type-c数据线连接电脑和单片机,数据到单片机后,经过转ch340芯片,实现串口通信。而且上官1号单片机有单独引出TX和RX针脚(TX为P3^1,RX为P3^0),可以让该单片机和其他串口通信设备进行通信,即电脑和单片机可以在ch340的作用下通过Type-c数据线将二者的TX和RX连接上进行数据传输,且单片机也可以和其他设备通过杜邦线连接单片机和设备之间的TX和RX针脚再在ch340的作用下进行数据传输
不同的串口电平,对于软件层面无影响,相当于软件里面编程的0和1,接不同的串口,对应出的电压大小不同
串口通信
接线
设备与设备之间串口通信,接线时TX口接到RX口,RX口接到TX口才能相互通信
电脑和上官一号的串口数据传输时,电脑的TX口和RX口连接到Type-c线中的TX口和RX口,Type-c的TX口和RX口连接到上官一号的ch340,ch340接收到电脑的TX口和RX口,再在内部将TX口连接到上官一号的RX口,RX口连接到TX口,如此实现数据传输,相当于ch340模块作为中间人将二者的TXRX口连接上了
收发缓冲器SBUF
51单片机设立有两个互相独立的接收发送缓冲器,可以同时接收和发送数据,并且这两个缓冲器共用一个地址码99H,都叫做SBUF,即这两个缓冲器实际情况是一个寄存器在控制,当计算机发送数据时,这个寄存器作为发送缓冲器,只能向缓冲器写入数据,当外部模块给计算机发送数据时,计算机接收数据,这个寄存器作为接收缓冲器,只能向缓冲器读出数据
发送数据时单片机往SBUF写东西,写完后,会发送给外部模块SBUF=data,接收数据时外部设备的值写入到了SBUF中,单片机从SBUF中读出来存到变量里,char data=SBUF,总之向这个寄存器写入数据时,就是作为发送缓冲器,从这个寄存器读出数据时,就是作为接收缓冲器,两个寄存器互不干扰
外部模块通过TX串行输入数据到单片机的RX,RX接到串行输入移位寄存器将数据存入到SBUF,单片机cpu直接从SBUF里面取得外部发送的数据;发送给外部模块时同理
接收数据的注意:首先要及时RI置0,其次移位寄存器如果成功将数据放入SBUF后,单片机要及时取取走这个数据,间隔时间太长,移位寄存器会覆盖SBUF的数据
具体原理:
及时取走SBUF
单片机接收数据时,如果外部模块一次性发送了很多数据,数据串行发送到移位寄存器,移位寄存器接收这些数据,然后存入到SBUF中,当一帧数据成功放入到SBUF里,移位寄存器就会立刻接收下一帧数据,我们需要在移位寄存器接收到下一帧数据再次放入SBUF之前,让单片机取走本次这帧数据(用变量存起来),如果没来得及取走,SBUF的数据会被下一帧的数据覆盖,所以这帧数据将不会被接收而丢失。要避免这种情况,可以让外部模块一次一次的发送数据,可以等SBUF值完全被取走后,再次发送数据,相当于手动降低发送频率
及时RI置0
接收数据时,若移位寄存已经接收到了数据,但是想要让移位寄存器将数据存入SBUF中需要满足的条件是:此时RI=0而且接收到的停止位为1,只有满足这两种条件才能把数据装入SBUF中,不满足条件则数据会丢弃而不存入SBUF中;接收到数据之后,会立刻判断一次能否装入SBUF,结果有效则进入中断,结果无效则丢弃数据,但是结果无论如何,移位寄存器又会再次接收数据,所以进入中断后要及时的将RI置0,如果不及时置0,那么主机在处理中断程序时,可能移位寄存器已经接收到了数据,之后检测RI,因为没有RI置0,则这个数据就丢失了,等到RI置0时,可能已经丢弃了很多数据了,然后再次检测RI,满足条件了,装数据入SBUF,进入中断,如此反复便会损失很多数据
//测试显示:
如果用变量接受SBUF的字符后,再把这个字符回传给PC,之后不延时,相当于单片机接收完SBUF的字符后就结束了,等待着移位寄存器更新SBUF的值,然后又中断取走SBUF,一次性发送600个字符都能完全接收到,超过600个字符可能有数据损失;如果延时100us,一次性发送13个数据以上就会产生数据损失;如果延时1000us,一次性发送4个数据以上就会产生数据损失;如果延时10ms以上,一次性发送3个数据以上就会产生数据损失;
波特率
对于UART异步串口通信,由于设备与设备之间时钟频率不同,双方需要约定一个通信速度,就是波特率
51单片机需要自己在代码中配置波特率,波特率由定时器1控制产生
计算方式:
举例定时器1是8位自动重载定时器,12T模式,SMOD波特率不加倍,要9600的波特率,求THTL1的值
9600=(1/32)*(11059200/(12*(256-TH1))) 计算出TH1=253=0xFD
串口相关寄存器
PCON寄存器
不可位寻址
最高位SMOD位,当SMOD置1串口123波特率加倍,SMOD置0串口123波特率不加倍,默认置0
第6位SMOD0位,当SMOD0置1时,让SCON寄存器的SM0/FE位运行FE功能,检测帧错误,确保数据传输正确,当检测到帧错误时,FE位由UART置1,软件能够检测到此位,而且FE位必须软件置0;当SMOD0置0时,让SCON寄存器的SM0/FE位运行SM0功能,和SM1共同指定串口工作方式,默认置0
SCON寄存器
可位寻址
复位时全0
最高位SM0/FE位,由PCON寄存器选择此位的作用是SM0还是FE,FE检测帧错误,SM0配合SM1决定串口工作方式
第6位SM1位,与SM0配合,SM0与SM1,00为同步移位串行方式,波特率固定;01为8位UART波特率可变,通过设置定时器1的模式和TH1TL1初值和12T/6T模式来改变波特率;10为9位UART,波特率固定;11为9位UART,波特率可变,设置方式如01
实际应用中工作方式多半选择方式1和方式3,即可调波特率8位或9位UART
第5位SM2位,允许方式2或者方式3多机通信控制位。
第4位REN位,REN位置1允许串口接收数据RX口打开,置0不允许,用于接收其他设备向单片机发送数据
第3位第2位为TB8和RB8,在方式2和3才有效,是串口发送或接收的第9位数据,TB8可以用作数据校验位或多机通讯的地址帧标记位,RB8在非多机通讯时是接收到的停止位,方式0不用RB8
第1位为TI发送中断请求标志位,置1请求中断,响应中断后必须软件置0
第0位为RI接收中断请求标志位,同TI,是在接收到数据后硬件置1,没有接收到数据之前RI仍为0
注意:如果要使用中断服务函数,需要打开总中断开关EA,和UART中断开关ES
TI和RI中断时都会进入中断服务函数,但是系统不知道是TI还是RI的中断,所以要在中断服务函数中对TI和RI中断分开处理
案例
案例1 51与PC互相发送数据
代码1 51向PC发送数据-延时方式
#include "reg52.h"
#include <intrins.h>
sfr AUXR=0x8E;
void UartInit(void) //9600bps@11.0592MHz
{
PCON &= 0x7F; //??????
SCON = 0x50; //8???,?????
AUXR &= 0xBF; //???1???Fosc/12,?12T
AUXR &= 0xFE; //??1?????1???????
TMOD &= 0x0F; //?????1???
TMOD |= 0x20; //?????1?8???????
TL1 = 0xFD; //??????
TH1 = 0xFD; //????????
ET1 = 0; //?????1??
TR1 = 1; //?????1
}
void Delay1000ms() //@11.0592MHz
{
unsigned char i, j, k;
_nop_();
i = 8;
j = 1;
k = 243;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void main(){
char msg='a';
char kongge=' ';
int cnt=0;
UartInit();
while(1){
if(cnt==26){
cnt=0;
msg='a';
}
SBUF=msg;
Delay1000ms();
SBUF=kongge;
Delay1000ms();
msg++;
cnt++;
}
}
注意:
往SBUF存数据了之后,会通过移位寄存器读出这个数据发送出去,需要消耗时间,我们需要保证这个数据发送出去之后再往SBUF里存数据,所以往SBUF存数据之后,需要延时一会,再存数据
对于这个问题的解决可以用到串口中断,发送完一个8位数据就发出中断请求中断位由硬件置1,以!TI作为循环的检测标志,发送数据时TI0,循环为死循环,发送结束产生中断跳出循环,再将TI置0,代表可以发送下一个数据,再往SBUF存数据,以!TI来等待,不断循环
代码 51和PC互不影响互发数据
#include "reg52.h"
#include <intrins.h>
sfr AUXR=0x8E;
sbit led=P3^7;
char getflag;
void UartInit(void) //9600bps@11.0592MHz
{ //½µµÍϵͳ¶ÔÍâ½çµÄµç´Å·øÉä
AUXR=0x01;
//SCONÅäÖÃ
SM0=0;
SM1=1;
REN=1;
//PCONÅäÖÃ
PCON=0x10;//²¨ÌØÂʲ»¼Ó±¶×î¸ßλSMOD=0,½øÐд®¿ÚģʽѡÔñµÚ6λSMOD0=0;
//²¨ÌØÂÊÅäÖÃ
//1¡¢¶¨Ê±Æ÷1ģʽΪ8λ×Ô¶¯ÖØÔØ
TMOD &=0x0F;
TMOD |=0x20;
//9600²¨ÌØÂʵijõÖµ
TH1=0xFD;
TL1=0xFD;
TR1=1;
EA=1;
ES=1;
}
void Delay1000ms() //@11.0592MHz
{
unsigned char i, j, k;
_nop_();
i = 8;
j = 1;
k = 243;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void huanhang(char*p){
while(*p!='\0'){
SBUF=*p;
while(!TI);
TI=0;
p++;
}
}
void send(char p,char h){
int cnt=26;
while(cnt--){
SBUF=p;
while(!TI);
TI=0;
SBUF=h;
while(!TI);
TI=0;
p++;
}
}
void main(){
char msg='a';
char* huan="\r\n";
char h=' ';
UartInit();
while(1){
send(msg,h);
Delay1000ms();
huanhang(huan);
}
}
void light() interrupt 4
{
getflag=SBUF;
RI=0;
if(getflag=='o')led=0;
if(getflag=='c')led=1;
}
注意ASCII码的坑:对于发送的char型数据,在计算机内部存在的形式是二进制数字,表示的大小和ASCII码的数字大小相同,如果你发送的是字符,实际单片机接收到的是字符通过ASCII码表转译过来的16进制hex数字,接收时如果选择文本模式,系统会把hex数字转回字符,这样就双方数据匹配了;如果发送的是8位的数字,接收的时候就要选择hex模式
案例2 手机通过蓝牙与51互相通信
蓝牙模块
蓝牙模块的使用很简单,不需要做额外配置。模块上电,手机与蓝牙模块连接成功后,就可以正常通信了,如果想修改蓝牙模块的相关信息,才需要发AT指令来配置。以后的WIFI模块连接后如果要正常使用,让WIFI入网,就需要发一些AT指令来配置
HC-08蓝牙模块,可以让手机软件通过蓝牙连接到模块,连接成功后,手机发送信息到模块,模块接受到信息后,通过串口发送到其他模块,实现信息传递,由于该蓝牙模块所用的串口电平为TTL电平,如果其他模块电平不匹配需要用转TTL设备来转换
AT指令
AT指令是用于终端设备(主机发指令)向终端适配器(从机干活)发送的指令,PC与蓝牙模块连接后,我们可以让PC通过软件向蓝牙模块发送AT指令,来改变蓝牙模块的各种属性
注意:每个AT命令行中只能包含一条AT指令,不能一次性发多条指令,AT指令要以回车来结尾以免出错,一条AT指令除AT外还能接收1056个字符
当蓝牙模块上电后如果没有连接默认进入的是AT状态,此时指示灯闪烁代表进入AT指令模式,连接后指示灯常亮
各AT指令比如:
代码
#include "reg52.h"
#include <intrins.h>
#include <string.h>
sfr AUXR=0x8E;
sbit led=P3^7;
sbit ledy=P3^6;
char getflag[5];
char ms;
void UartInit(void) //9600bps@11.0592MHz
{ //½µµÍϵͳ¶ÔÍâ½çµÄµç´Å·øÉä
AUXR=0x01;
//SCONÅäÖÃ
SM0=0;
SM1=1;
REN=1;
//PCONÅäÖÃ
PCON=0x10;//²¨ÌØÂʲ»¼Ó±¶×î¸ßλSMOD=0,½øÐд®¿ÚģʽѡÔñµÚ6λSMOD0=0;
//²¨ÌØÂÊÅäÖÃ
//1¡¢¶¨Ê±Æ÷1ģʽΪ8λ×Ô¶¯ÖØÔØ
TMOD &=0x0F;
TMOD |=0x20;
//9600²¨ÌØÂʵijõÖµ
TH1=0xFD;
TL1=0xFD;
TR1=1;
EA=1;
ES=1;
}
void Delay1000ms() //@11.0592MHz
{
unsigned char i, j, k;
_nop_();
i = 8;
j = 1;
k = 243;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void huanhang(char*p){
while(*p!='\0'){
SBUF=*p;
while(!TI);
TI=0;
p++;
}
}
void send(char p,char h){
int cnt=26;
while(cnt--){
SBUF=p;
while(!TI);
TI=0;
SBUF=h;
while(!TI);
TI=0;
p++;
}
}
void main(){
char msg='a';
char* huan="\r\n";
char h=' ';
int cnt=0;
UartInit();
while(1){
send(msg,h);
Delay1000ms();
huanhang(huan);
}
}
void light() interrupt 4
{
if(RI==1){
static int i=0;
int j=0;
getflag[i]=SBUF;
RI=0;
i++;
if(i==4){
for(j=0;j<4;j++){
SBUF=getflag[j];
while(!TI);
TI=0;
}
if(!strcmp(getflag,"open")){
led=0;
ledy=0;
i=0;
memset(getflag,'\0',sizeof(getflag));
}
}
else if(i==5){
ledy=0;
i=0;
j=4;
SBUF=getflag[j];
while(!TI);
TI=0;
if(!strcmp(getflag,"close")){
led=0;
ledy=1;
i=0;
}
memset(getflag,'\0',sizeof(getflag));
}
else{
led=1;
ledy=1;
}
}
if(TI==1){
}
}