基本概念
UART (Universal Asynchronous Receiver/Transmitter) 是一种通用异步收发传输器,其使用串行的方式在双机之间进行数据交换,实现全双工通信。数据引脚仅包含用于接收数据的RXD和用于发送数据的TXD, 数据在数据线上按位一位一位的串行传输,要正确解析这些数据,必须遵循UART协议,作为了解,这里仅简要讲述几个关键的概念:
波特率
波特率决定了数据传输速率,其表示每秒传送数据的位数,值越大,数据通信的速率越高,数据传输得越快。常见的波特率有4800、9600、14400、19200、38400 、115200等等,若波特率为115200, 则表示每秒钟可以传输115200位(注意:是bit,不是byte) 数据。
空闲位
数据线上没有数据传输时, 数据线处于空闲状态。空闲状态的电平逻辑为”l” 。
起始位
起始位表示一帧数据传输的开始, 起始位的电平逻辑是”O”。
数据位
紧接起始位后,即为实际通信传输的数据,数据的位数可以是5、6、7、8 等,数据传输时,从最低位开始依次传输。
奇偶校验位
奇偶校验位用于接收方对数据进行校验,及时发现由于通信故障等问题造成的错误数据。奇偶校验位是可选的,可以不使用奇偶校验位。奇偶校验有奇校验和偶校验两种形式,该位的逻辑电平与校验方法和所有数据位中逻辑”1″的个数相关。
奇校验:通过设置该位的值(“1″或”O”),使该位和数据位中逻辑”1″的总个数为奇数。例如,数据位为8位,值为:1001 1001,1的个数为4个(偶数),则奇校验时,为了使1 的个数为奇数,就要设置奇偶校验位的值为1, 使1的总个数为5个(奇数)。
偶校验:通过设置该位的值(“1″或”O”),使该位和数据位中逻辑”1″的总个数为偶数。例如,数据位为8 位,值为:1001 1001,1的个数为4个(偶数),则偶校验时,为了使1的个数为偶数,就要设置奇偶校验位的值为0,使1的个数保持不变,为4( 偶数)。
通信双方使用的校验方法应该一致,接收方通过判断”1″的个数是否为奇数(奇校验)或偶数(偶校验)来判定数据在通信过程中是否出错。
停止位
停止位表示一帧数据的结束,其电平逻辑为”1″,其宽度可以是1位、1.5 位、2位。即其持续的时间为位数乘以传输一位的时间(由波特率决定),例如,波特率为115200,则传输一位的时间为1/115200 秒, 约为8.68us。若停止位的宽度为1.5位,则表示停止位持续的时间为:1.5 × 8.68us≈13us 。
常见的帧格式为:1位起始位,8 位数据位,无校验,1位停止位。由于起始位的宽度恒为1位,不会变化,而数据位,校验位和停止位都是可变的,因此,往往在描述串口通信协议时,都只是描述其波特率、数据位,校验位和停止位,不再单独说明起始位。帧格式下所示:
注意,通信双方必须使用完全相同的协议,包括波特率、起始位、数据位、停止位等。如果协议不一致,则通信数据会错乱,不能正常通信。在通信中,若出现乱码的情况,应该首先检查通信双方所使用的协议是否一致。
硬件连接
硬件连接主要有两种情况,一对一连接,一对多连接(多机通信)。
电平转换
上图UART_TX与UART_RX端为3.3V,RXD1与TXD1端为5V。TXD1->UART_RX利用分压原理得到3.3V,UART_TX->RXD1利用S9013三极管开通与关闭实现3.3V转5V电平。
编程实现
由于各类单片机的UART外设功能存在差异,因此寄存器内容也存在较大差异,此处不做详细列举,给出一个通用配置过程与8051单片机示例代码。
配置过程
- 选择UART工作模式,分别是方式0、方式1、方式2、方式3
- 设置波特
- 使能中断(通常使用接收中断)
示例代码
void UartInit()
{
PCON |= 0x80; //
SM0 = 0;
SM1 = 1;
SCON |= 0x40; //使用方式1,8位波特率可变
TMOD |= 0x20;
TH1 = TL1 = 0xF3; //波特率19200
TR1 = 1;
REN = 1; //使能UART
ES = 1; //使能中断
EA = 1;
}
unsigned char Cmd[4]; //被接受数据的存储位置
unsigned char RecLen; //已接收的数据长度
#define DefRecLen 4 //允许接收的最大长度
unsigned char TimeOut; //接收超时
bit RecOver;
void RecDataHandle(unsigned char recdata)
{
if(RecLen < DefRecLen)
{
Cmd[RecLen] = recdata;
RecLen ++;
TimeOut = 100;
RecOver = 0;
}
}
void Uart1_Interrupt() interrupt 4
{
unsigned char RecData;
if(RI)
{
RI = 0;
RecData = SBUF;
RecDataHandle(RecData);
}
if(TI)
{
// TI = 0;
}
}
格式化输出
格式化输出,需调用标准C语言库函数printf(),需包含头文件“stdio.h”。以KEIL集成开发环境为例,printf()函数在编译时会自动调用putchar()函数,如果应用程序中未声明putchar()函数,则自动调用KEIL系统下的putchar.c中的putchar()函数。由于单片机的种类繁多,系统的putchar()函数无法适应全部硬件,因此,KEIL在\KEIL\C51\LIB路径下提供了PUTCHAR.C源文件,可对其修改来适应硬件环境。
建议:将\KEIL\C51\LIB路径下的PUTCHAR.C文件复制到应用程序目录下再添加到工程文件,避免修改系统PUTCHAR.C文件。
示例
#include <stdio.h>
void UserPrintf()
{
printf(“This is a test example!”);
}
printf
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
% <[>flags<]> <[>width<]> <[>.precision<]> <[>{b|B|l|L}<]> type
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|