UART
一、协议部分:
协议部分转自:http://www.s8052.com/index.htm
串行通信的传送方向通常有三种:
1、为单工,只允许数据向一个方向传送;
2、半双工,允许数据向两个方向中的任一方向传送,但每次只能有一个站发送;
3、全双工,允许同时双向传送数据,实际上,全双工配置是一对单向配置,它要求两端的通信设备具有完整和独立的发送和接收能力。
串行通信有两种基本方式,即异步通信和同步通信。
1、异步通信 :
在异步通信中,数据是一帧一帧传送的,所谓一帧是指包含一个有效信息和一些控制代码的二进制字符串。
在帧格式中,一个字符由四个部分组成;起始位、数据位、奇偶校验位和停止位。首先是一个起始位“0”,然后是5~8位数据,接下来是奇偶数验位,该位在某些系统中可省略,最后是停止位“1”。
起始位“0”信号只占用 一位用来通知接收设备一个待接收的字符开始到来。线路上在不传送字符时应保持为“1”。接收端不断检测线路的状态,若连续为“1”以后又测到一个“0”,就知道发来一个新字符,应马上准备接收。字符的起始位还被用作同步接收端调整移位时钟的相位以保证以后的接收能正确进行。起始位后面紧接着是数据位。它可以是5位、6位、7位或8位,在不同的系统中有不同的定义。奇偶校验只占一位,但也可以规定不用奇偶校验位。也可用这一位来确定这一帧中的字符所代表信息的性质,例如用这一位来区别地址和数据,关于这一位的用法我们在多机通信中讲解。停止位的作用是便于接收端辨识下一帧数据的起始位。若停止位以后不是紧接关传送一个字符,则让线路上保持为“1”。称为空闲位“1”,标志线路处于等待状态。存在空闲位正是异步通信的特征之一。
2、同步通信
同步通信中,在数据开始传送前用同步字符来指示,常约定1~2个字符作同步字符,并由时钟来实现发送端和接收端同步,接收端检测到规定的同步字符后,就连续按顺序接收数据,直到通信告一段落。同步传送时,字符与字符之间没有间隙,也不用起始和停止位,仅数据块开始时用同步字符SYNC来指示。同步传送的优点是可以提高传送速率,但硬件比较复杂。
3、串行发送
串行传送中另一个重要概念是波特率。波特率即数据传送速率,表示每秒钟传送二进制代码的位数,它的单位是位/秒。假设传送速率是120字符/秒,而每个字符格式包含10个代码位,则这时传送的波特率为:10X120位/秒=1200波特,波特率的倒数就是每一位代码的传送时间。
串行传送的工作原理:
假定帧格式为10位。即1位起始位,8位数据位,和1位停止位。数据发生的过程序比较简单,当没有数据传送时,发送端发送“空闲位”即高电平,在发送端有数据需要发送时将待发送的8位数据装入发送缓冲器中,然后在发送移位脉冲的控制下一位一位的发送完一帧数据,若一帧数据发送完后还有数据要发送,则紧接着发送下一帧数据。若没有数据要发送,则发送空闲位1,每一帧发送时先发送一位起始位,紧接着发送8位数据,发送8位数据时,先发送低位,后发送高位,最后发送一位停止位。
4、串行接收
接收端在采样时钟的每一个周期对接收引脚RXD采样一次,若引脚为高电平,则表明传输线处于空闲状态,即发送端未发送数据,因而接收端什么也不作,等到下一个采样脉冲到来时继续对引脚RXD采样 。若某一次采样为低电平 ,则接收端将这个低电平当作起始位,然后使波特率发生器从当前采样脉冲开始每隔16个采样脉冲产生一个移位脉冲。
每一个移位脉冲到来,接收端就将接收移位寄存器右移一位,且将引脚RXD上的状态从左边移入位寄存器 。接收端在接收完约定的8位数后,在下一个脉冲到来时接收停止位,若发现这一位为0,则表明传送产生了误差,可采取不同的措施纠正误差 。如要求发送端重发等,若发现这一位为1,则认为这是一个有效的停止位,到些为止,一个数据的接收过程就结束了。 紧接着接收端为接收下一次数据作准备,这个过程序包括:将移位寄存器中的8位数据送入输入缓冲器中,向CPU申请中断,即通知CPU取走输入缓冲器中的数据,然后输入端又在采样脉冲的控制下不断采样引脚RXD,这就是一次完整的数据接收过程。
5、串行传送的完整工作过程 :
假如发送端有两个数00101011B和10011101B需要发送,系统加电后,传输线上为空闲位
发送端送第一个数据到发送缓冲器中,并启动发送过程
第一个移位脉冲到来,发送第一个数据的起始位
接收端检测到起始位,调整接收端移位时的相位,使期周期从头开始
调整接收端移位时的相位,使期周期从头开始
发送端发送第一位数据
接收端接收第一个数据
发送端发送第二位数据
接收端接收第二位数据
发送端发送第三位数据
接收端接收第三位数据
发送端发送第四位数据
接收端接收第四位数据
发送端发送第五位数据
接收端接收第五位数据
发送端发送第六位数据
接收端接收第六位数据
发送端发送第七位数据
接收端接收第七位数据
发送端发送第八位数据
接收端接收第八位数据
发送端发送停止位
发送端申请中断,要求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
UART的更多相关文章
- [Intel Edison开发板] 05、Edison开发基于MRAA实现IO控制,特别是UART通信
一.前言 下面是本系列文章的前几篇: [Intel Edison开发板] 01.Edison开发板性能简述 [Intel Edison开发板] 02.Edison开发板入门 [Intel Edison ...
- z-stack协议uart分析(DMA)
1.从ZMain里面的main函数开始分析 2.进入int main( void ); HalDriverInit(); //硬件相关初始化,有DMA初始化和UART初始化 3.进入HalDriv ...
- Win10 IoT C#开发 4 - UART 串口通信
Windows 10 IoT Core 是微软针对物联网市场的一个重要产品,既可以开发设备UI与用户交互式操作,又可以控制GPIO等接口,使得原来嵌入式繁琐的开发变得简单.通过Remote Debug ...
- (三) UART 串口通讯
UART : university asynchronous receiver and transmitter UART // 通用异步接收器和发送器 为什么要有串口:因为许多嵌入式设备没有显示屏 ...
- I2S/PCM/IOM-2、I2C/SPI/UART/GPIO/slimbus
概述 I2S,PCM,IOM-2都是数字音频接口,传数据的. I2C,SPI,UART,GPIO是控制接口,传控制信令的. I2S I2S(Inter-IC Sound Bus)是飞利浦公司为数字音频 ...
- 什么是UART中的FIFO
FIFO是先进先出缓冲区的意思,即串口接收到的数据可以先进入FIFO,不必马上进入中断服务程序接收,这样可以节省CPU时间.对于发送数据也一样可以把要发送的数据一起写入FIFO,串口控制器按照写入的顺 ...
- Uart、SPI和I2C的区别
串口通信:UART.SPI.I2C区别[引用] 1.UART就是两线,一根发送一根接收,可以全双工通信,线数也比较少.数据是异步传输的,对双方的时序要求比较严格,通信速度也不是很快.在多机通信上面 ...
- Raspberry Pi UART with PySerial
参考:http://programmingadvent.blogspot.hk/2012/12/raspberry-pi-uart-with-pyserial.html Raspberry Pi UA ...
- Raspberry Pi Resources-Using the UART
参考:RPi Serial Connection 本文来自:http://www.raspberry-projects.com/pi/programming-in-c/uart-serial-port ...
- Linux学习 : 裸板调试 之 配置UART
1.UART原理说明 发送数据时,CPU将并行数据写入UART,UART按照一定的格式在一根电线上串行发出:接收数据时,UART检测另一根电线上的信号,串行收集然后放在缓冲区中,CPU即可读取UART ...
随机推荐
- 再读C++线程池
最近仔细看了一下https://github.com/henkel/threadpool代码,总体感觉非常精巧,使用了 boost库的bind function完成了线程池与业务端的完全解耦:所有的任 ...
- javascript页面加载完执行事件
<script type="text/javascript" language="JavaScript"> //: 判断网页是否加载完成 docum ...
- 【JavaScript】之【Object】
见代码: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF- ...
- Hadoop中如何查看文件的block size
因为对控制文件的分片数目,进而控制文件的map个数非常感兴趣,因此有查找如下资料的需求.下面来看看我总结的吧:) 当向HDFS上写文件时,可以通过设置dfs.blocksize配置项来设置文件的blo ...
- 【转】C#线程同步示例
using System; using System.Threading; // 银行帐户类 class Account { int balance; ...
- The constructor User.Student(String, String, String) is not visible
项目:蒙文词语检索 日期:2016-05-01 提示:The constructor User.Student(String, String, String) is not visible 出处:Db ...
- selected 刷新页面后selected选中的值保持不表(thinkphp 从控制器assign 传值到js)
昨晚解决select 刷新页面以后选择的值保持不变,要想让seleted不变,有两种思路, 1,在提交表单的时候,将所选择的option的属性设为checked . 2.将option的value或者 ...
- SqlServer性能优化 提高并发性能(八)
并发访问: 当多个线程访问同一个资源,会产生并发性问题 并发控制与处理: 乐观并发控制:一种方式是"后来的更新者获胜" 这意味着先来的用户提交的值会在没有察觉的情况下丢失. 为 ...
- Java中长度为0的数组与null的区别
有如下两个变量定义,这两种定义有什么区别呢? 1. int[] zero = new int[0]; 2. int[] nil = null; zero是一个长度为0的数组,我们称之为“空数组”,空数 ...
- 个人Web工具箱&资源整理(1)
很久就想把使用的工具及收藏的资源整理一番:一是为了传达博客社区的理念:资源共享,而是方便自己及团队快速获取. 学习资源: 首推两个入门级在线参考网站. 1 w3c school. 2 Runoob.c ...