UART

时间:2023-03-28 14:43:14

一、协议部分:

协议部分转自:http://www.s8052.com/index.htm

串行通信的传送方向通常有三种:

1、为单工,只允许数据向一个方向传送;

2、半双工,允许数据向两个方向中的任一方向传送,但每次只能有一个站发送;

3、全双工,允许同时双向传送数据,实际上,全双工配置是一对单向配置,它要求两端的通信设备具有完整和独立的发送和接收能力。

串行通信有两种基本方式,即异步通信和同步通信。

1、异步通信 :

在异步通信中,数据是一帧一帧传送的,所谓一帧是指包含一个有效信息和一些控制代码的二进制字符串。

UART

在帧格式中,一个字符由四个部分组成;起始位、数据位、奇偶校验位和停止位。首先是一个起始位“0”,然后是5~8位数据,接下来是奇偶数验位,该位在某些系统中可省略,最后是停止位“1”。

UART

起始位“0”信号只占用 一位用来通知接收设备一个待接收的字符开始到来。线路上在不传送字符时应保持为“1”。接收端不断检测线路的状态,若连续为“1”以后又测到一个“0”,就知道发来一个新字符,应马上准备接收。字符的起始位还被用作同步接收端调整移位时钟的相位以保证以后的接收能正确进行。起始位后面紧接着是数据位。它可以是5位、6位、7位或8位,在不同的系统中有不同的定义。奇偶校验只占一位,但也可以规定不用奇偶校验位。也可用这一位来确定这一帧中的字符所代表信息的性质,例如用这一位来区别地址和数据,关于这一位的用法我们在多机通信中讲解。停止位的作用是便于接收端辨识下一帧数据的起始位。若停止位以后不是紧接关传送一个字符,则让线路上保持为“1”。称为空闲位“1”,标志线路处于等待状态。存在空闲位正是异步通信的特征之一。

2、同步通信

同步通信中,在数据开始传送前用同步字符来指示,常约定1~2个字符作同步字符,并由时钟来实现发送端和接收端同步,接收端检测到规定的同步字符后,就连续按顺序接收数据,直到通信告一段落。同步传送时,字符与字符之间没有间隙,也不用起始和停止位,仅数据块开始时用同步字符SYNC来指示。同步传送的优点是可以提高传送速率,但硬件比较复杂。

3、串行发送

串行传送中另一个重要概念是波特率。波特率即数据传送速率,表示每秒钟传送二进制代码的位数,它的单位是位/秒。假设传送速率是120字符/秒,而每个字符格式包含10个代码位,则这时传送的波特率为:10X120位/秒=1200波特,波特率的倒数就是每一位代码的传送时间。

UART

串行传送的工作原理:

假定帧格式为10位。即1位起始位,8位数据位,和1位停止位。数据发生的过程序比较简单,当没有数据传送时,发送端发送“空闲位”即高电平,在发送端有数据需要发送时将待发送的8位数据装入发送缓冲器中,然后在发送移位脉冲的控制下一位一位的发送完一帧数据,若一帧数据发送完后还有数据要发送,则紧接着发送下一帧数据。若没有数据要发送,则发送空闲位1,每一帧发送时先发送一位起始位,紧接着发送8位数据,发送8位数据时,先发送低位,后发送高位,最后发送一位停止位。

4、串行接收

接收端在采样时钟的每一个周期对接收引脚RXD采样一次,若引脚为高电平,则表明传输线处于空闲状态,即发送端未发送数据,因而接收端什么也不作,等到下一个采样脉冲到来时继续对引脚RXD采样 。若某一次采样为低电平 ,则接收端将这个低电平当作起始位,然后使波特率发生器从当前采样脉冲开始每隔16个采样脉冲产生一个移位脉冲。

UART

每一个移位脉冲到来,接收端就将接收移位寄存器右移一位,且将引脚RXD上的状态从左边移入位寄存器 。接收端在接收完约定的8位数后,在下一个脉冲到来时接收停止位,若发现这一位为0,则表明传送产生了误差,可采取不同的措施纠正误差 。如要求发送端重发等,若发现这一位为1,则认为这是一个有效的停止位,到些为止,一个数据的接收过程就结束了。 紧接着接收端为接收下一次数据作准备,这个过程序包括:将移位寄存器中的8位数据送入输入缓冲器中,向CPU申请中断,即通知CPU取走输入缓冲器中的数据,然后输入端又在采样脉冲的控制下不断采样引脚RXD,这就是一次完整的数据接收过程。

5、串行传送的完整工作过程 :

假如发送端有两个数00101011B和10011101B需要发送,系统加电后,传输线上为空闲位

UART

发送端送第一个数据到发送缓冲器中,并启动发送过程

第一个移位脉冲到来,发送第一个数据的起始位

接收端检测到起始位,调整接收端移位时的相位,使期周期从头开始

调整接收端移位时的相位,使期周期从头开始

发送端发送第一位数据

接收端接收第一个数据

发送端发送第二位数据

接收端接收第二位数据

发送端发送第三位数据

接收端接收第三位数据

发送端发送第四位数据

接收端接收第四位数据

发送端发送第五位数据

接收端接收第五位数据

发送端发送第六位数据

接收端接收第六位数据

发送端发送第七位数据

接收端接收第七位数据

发送端发送第八位数据

接收端接收第八位数据

发送端发送停止位

发送端申请中断,要求CPU发送下一个数据

接收端接收停止位,将接收到的数据装放缓冲器中

申请中断,要求CPU取走刚接到的数据。

协议部分转自:http://www.s8052.com/index.htm

#####################割割线###############################################################

二、程序部分(IO模拟串口)

程序部分转自:http://hi.baidu.com/mcu99/item/7f41f5c75846b820a1b50a6c

程序硬件平台:11.0592M晶振,STC单片机(兼容51)

1、发送端

/***************************************************************
* 在单片机上模拟了一个串口,使用P2.1作为发送端
* 把单片机中存放的数据通过P2.1作为串口TXD发送出去
***************************************************************/
#include <reg51.h>
#include <stdio.h>
#include <string.h> typedef unsigned char uchar;
int i;
uchar code info[] = {0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55}; sbit newTXD = P2^;//模拟串口的发送端设为P2.1 void UartInit()
{
SCON = 0x50; // SCON: serail mode 1, 8-bit UART
TMOD |= 0x21; // T0工作在方式1,十六位定时
PCON |= 0x80; // SMOD=1;
TH0 = 0xFE; // 定时器0初始值,延时417us,目的是令模拟串口的波特率为2400bps fosc=11.0592MHz
  TL0 = 0x7F; // 定时器0初始值,延时417us,目的是令模拟串口的波特率为2400bps fosc=11.0592MHz
} void WaitTF0(void)
{
while(!TF0);
TF0=;
TH0=0xFE; // 定时器重装初值 fosc=11.0592MHz
TL0=0x7F; // 定时器重装初值 fosc=11.0592MHz
} void WByte(uchar input)
{
//发送启始位
uchar j=;
TR0=;
newTXD=(bit);
WaitTF0();
//发送8位数据位
while(j--)
{
newTXD=(bit)(input&0x01); //先传低位
WaitTF0();
input=input>>;
} //发送校验位(无) //发送结束位
newTXD=(bit);
WaitTF0();
TR0=;
} void Sendata()
{
for(i=;i<sizeof(info);i++)//外层循环,遍历数组
{
WByte(info[i]);
}
} void main()
{
UartInit();
while()
{
Sendata();
}
}

2、接收端

/***************************************************************
* 模拟接收程序,这个程序的作用从模拟串口接收数据,然后将这些数据发送到实际串口
* 在单片机上模拟了一个串口,使用P3.2作为发送和接收端
* 以P3.2模拟串口接收端,从模拟串口接收数据发至串口
***************************************************************/
#include<reg51.h>
#include<stdio.h>
#include<string.h> typedef unsigned char uchar ;
uchar tmpbuf2[]={};
//用来作为模拟串口接收数据的缓存 struct { uchar recv : ;//tmpbuf2数组下标,用来将模拟串口接收到的数据存放到tmpbuf2中
  uchar send : ;//tmpbuf2数组下标,用来将tmpbuf2中的数据发送到串口
} tmpbuf2_point={,}; sbit newRXD=P3^ ;//模拟串口的接收端设为P3.2 void UartInit()
{
SCON=0x50 ;// SCON: serail mode 1, 8-bit UART
TMOD|=0x21 ;// TMOD: timer 1, mode 2, 8-bit reload,自动装载预置数(自动将TH1送到TL1);T0工作在方式1,十六位定时
PCON|=0x80 ;// SMOD=1;
TH1=0xE8 ;// Baud:2400 fosc=11.0592MHz 2400bps为从串口接收数据的速率
TL1=0xE8 ;// 计数器初始值,fosc=11.0592MHz 因为TH1一直往TL1送,所以这个初值的意义不大
TH0=0xFF ;// 定时器0初始值,延时208us,目的是令模拟串口的波特率为9600bps fosc=11.0592MHz
TL0=0xA0 ;// 定时器0初始值,延时208us,目的是令模拟串口的波特率为9600bps fosc=11.0592MHz IE|=0x81 ;// 中断允许总控制位EA=1;使能外部中断0
TF0= ;
IT0= ;// 设置外部中断0为边沿触发方式
TR1= ;// 启动TIMER1,用于产生波特率
} void WaitTF0(void)
{
while(!TF0);
TF0= ;
TH0=0xFF ;// 定时器重装初值 模拟串口的波特率为9600bps fosc=11.0592MHz
TL0=0xA0 ;// 定时器重装初值 模拟串口的波特率为9600bps fosc=11.0592MHz
} //接收一个字符
uchar RByte()
{
uchar Output= ;
uchar i= ;
TR0= ; //启动Timer0 TH0=0xFF ;// 定时器重装初值 模拟串口的波特率为9600bps fosc=11.0592MHz
TL0=0xA0 ;// 定时器重装初值 模拟串口的波特率为9600bps fosc=11.0592MHz
TF0= ; WaitTF0();//等过起始位
//接收8位数据位
while(i--)
{
Output>>= ;
if(newRXD)Output|=0x80 ;//先收低位
WaitTF0();//位间延时
} TR0= ;//停止Timer0
return Output ;
} //向COM1发送一个字符
void SendChar(uchar byteToSend)
{
SBUF=byteToSend ;
while(!TI);
TI= ;
} void main()
{
UartInit();
while()
{
if(tmpbuf2_point.recv!=tmpbuf2_point.send)//差值表示模拟串口接收数据缓存中还有多少个字节的数据未被处理(发送至串口)
{
SendChar(tmpbuf2[tmpbuf2_point.send++]);
}
}
} //外部中断0,说明模拟串口的起始位到来了
void Simulated_Serial_Start()interrupt
{
EX0= ; //屏蔽外部中断0
tmpbuf2[tmpbuf2_point.recv++]=RByte(); //从模拟串口读取数据,存放到tmpbuf2数组中
IE0= ; //防止外部中断响应2次,防止外部中断函数执行2次
EX0= ; //打开外部中断0
}

以上是两个独立的测试程序,分别是模拟串口发送的测试程序和接收的测试程序
上面两个程序在编写过程中参考了这篇文章《51单片机模拟串口的三种方法》(在后文中简称《51》),但在它的基础上做了一些补充,下面是若干总结的内容:

1、《51》在接收数据的程序中,采用的是循环等待的方法来检测起始位(见《51》的“附:51 IO口模拟串口通讯C源程序(定时器计数法)” 部分),这种方法在较大程序中,可能会错过起始位(比如起始位到来的时候程序正好在干别的,而没有处于判断起始位到来的状态),或者一直在检测起始位,而没有办法完成其他工作。为了避免这个问题,在本接收程序中采用了外部中断的方法,将外部中断0引脚作为模拟串口的接收端,设IT0=1(将外部中断0设为边缘触发)。这样当起始位(低电平)到来时,就会引发外部中断,然后在外部中断处理函数中接收余下的数据。这种方法可以保证没数据的时候程序该干什么干什么,一旦模拟串口接收端有数据,就可以立即接收到。

2、加入了模拟串口接收缓冲区。在较大程序中,单片机要完成的工作很多,在模拟串口接收到了数据之后立即处理的话,有可能处理不过来造成丢失数据,或者影响程序其他部分执行。本程序中加入了64个字节的缓冲区,从模拟串口接收到的数据先存放在缓冲区中。这样就算程序一时没工夫处理这些数据,腾出手来之后也能在缓冲区中找到它们。

3、《51》文中的WByte函数和RByte函数中都先打开计数器后关闭计数器。如果使用本文的外部中断法来接收数据,并且外部中断处理函数里外都调用了WByte或RByte的话,需要将这两个函数中的TR0=1,TR0=0操作的语句除去,并在UartInit()中加入一句TR0=1;即让TR0始终开着就可以。
由于之前没有意识到这个问题,因此在具体应用时出现了奇怪的问题:表现为中断处理函数执行完毕之后,似乎回不到主程序,程序停在了一个不知道的地方。后来经过排查后找到了问题所在,那个程序的中断处理函数中用了RByte,中断处理函数外用到了WByte,而这两个函数的最后都有TR0=0。这样当中断处理函数执行完毕后,TR0实际上是为0的,返回主程序后(中断前的主程序可能正好处于其他的WByte或RByte执行中),原先以来定时器0溢出改变TF0才能执行下去的WByte函数就无法进行下去,从而导致整个程序停下来不动。(在本文的接收测试程序中不存在这个问题,因为中断处理程序中虽调用了RByte,但中断处理程序外却没有调用RByte或WByte)
下面是修改后的RByte、WByte和WaitTF0函数,仅供参考:

/**********************************************
* 定时器0溢出后重装初值
**********************************************/ void WaitTF0(void)
{
TF0= ;
TH0=0xFF ;// 定时器重装初值 fosc=11.0592MHz
TL0=0xA0 ;// 定时器重装初值 fosc=11.0592MHz while(!TF0);
TF0= ;
} /**********************************************
* 从串口B接收一个字符
**********************************************/ uchar RByte()
{
uchar Output= ;
uchar i= ;
// TR0=1; //启动Timer0
/*
TH0 = 0xFF; // 定时器重装初值 模拟串口的波特率为9600bps fosc=11.0592MHz
TL0 = 0xA0; // 定时器重装初值 模拟串口的波特率为9600bps fosc=11.0592MHz
*/ WaitTF0();//等过起始位 //接收8位数据位
while(i--)
{
Output>>= ;
if(newRXD)Output|=0x80 ; //先收低位
WaitTF0();           //位间延时
}
//while(!TF0) if(newRXD) break;  //此句和下一句不能加,如果加上了将导致耗时过长,影响下一个字节的接收
//WaitTF0();   //等过结束位
//TR0=0;   //停止Timer0 return Output ;
}
/**********************************************
* 发送一个字节到串口B
**********************************************/ void WByte(uchar input)
{
//发送启始位
uchar j= ;
//TR0=1;
newTXD=(bit) ;
WaitTF0();
//发送8位数据位
while(j--)
{
newTXD=(bit)(input&0x01);//先传低位
WaitTF0();
input=input>> ;
} //发送校验位(无) //发送结束位
newTXD=(bit) ;
WaitTF0();
//TR0=0;
}

4、在上面的新修改后的RByte()函数中,有被注释掉的如下两句:

//while(!TF0) if(newRXD) break;      //此句和下一句不能加,如果加上了将导致耗时过长,影响下一个字节的接收
//WaitTF0();   //等过结束位

这两句在《51》文中的程序是存在的,但是使用中断接收法后,加上这两句后出现了问题。表现为接收到的下一个字节的数据不完整或直接接收不到,似乎这两句占用了过多的时间。
看这两句的目的似乎是要延时以跳过结束位,但是我感觉这个结束位可以不用管它,反正结束位是个高电平,不会妨碍下一个字节是否到来的判断(下一个字节的起始位是低电平)。那就由它去吧,没有必要为了它而占用CPU的时间。
在本文的程序中,去掉这两句后程序执行正确,如果其他朋友在使用时真的出现问题,可以试着再把它们加上试一下。

程序部分转自:http://hi.baidu.com/mcu99/item/7f41f5c75846b820a1b50a6c