单片机开发——串口通信

  • Post author:
  • Post category:其他


基本概念

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单片机示例代码。

配置过程

  1. 选择UART工作模式,分别是方式0、方式1、方式2、方式3
  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



摘要


#include <stdio.h>


int printf (


const char *

fmtstr

/* format string */


<[>,

arguments

… <]>);   /* additional arguments */



描述



printf



函数格式化一系列字符串和数值创建一个字符串使用



putchar



函数写入输出流。



fmtstr



参数是一个格式字符串,可以由字符、转义序列和格式说明符组成。


普通字符和转义序列按他们被解释的顺序复制到流。格式说明符总是以百分号


(‘%’)


并需要在调用的



printf



函数中包含附加的



arguments





格式化的字符串从左向右读取。遇到的第一个格式说明符引用



fmtstr



后的第一个



argument



,使用格式说明符转换和输出。第二个格式说明符引用



fmtstr



后的第二个



argument



,以此类推。如果



arguments



比格式说明符多,多余的



arguments



被忽略。如果没有足够的



arguments



来匹配格式说明符或者参数类型不能与



fmtstr



说明匹配,结果是不可预知的。


格式说明符有以下常见格式


:

% <[>flags<]> <[>width<]> <[>.precision<]> <[>{b|B|l|L}<]> type


格式说明符字段可以是单个字符或一个指定特殊格式选项的数字。



type



字段是一个字符,指定参数解释为字符、字符串、数值或指针,详细如下表。



Type



参数类型



输入格式


d


int


有符号十进制数


u


unsigned int


无符号十进制数


o


unsigned int


无符号八进制数


x


unsigned int


无符号十六进制数,使用


“0123456789abcedf”


X


unsigned int


无符号十六进制数,使用


“0123456789ABCDEF”


f


float


浮点数格式化为



<[>-<]>

dddd.dddd


e


float


浮点数格式化为



<[>-<]>

d.dddd

e<[>-<]>

dd


E


float


浮点数格式化为



<[>-<]>

d.dddd

E<[>-<]>

dd


g


float


浮点数使用


e





f


格式,为指定数值选择更紧凑精确的


G


float


浮点数使用


E





f


格式,为指定数值选择更紧凑精确的


c


char


一个字符


s


*


由空字符


(‘\0’)


结尾的字符串


p


*


通用指针格式



t:aaaa







t



是内存类型



aaaa



是十六进制地址



注意


  • 可选的字符l或L可以在类型字符前分别指定d, i, u, o, x和X的long类型。

  • 可选的字符b或B可以在类型字符前分别指定d, i, u, o, x和X的char类型。


字符后跟随一个百分号将不会被认为是格式说明符而是被当作普通字符。例如,


“%%”


向输出流写入一个百分号。



flags



字段是单个字符用来对齐输出,打印一个


+/-


号、空白、小数点、八进制和十进制前缀,详细如下表。



Flag



描述




将指定字段宽度输出左对齐


+


如果输出是一个有符号类型,使用


+








符号做输出的前缀。


blank (‘ ‘)


如果是一个有符号正值,使用空白做输出的前缀。否则,没有空白前缀。


#


当使用


o, x





X


字段类型时,使用


0, 0x





0X


作为非零输出的前缀


当使用


e, E, f, g





G


字段类型,


#flag


强制使输出值包含小数点。


# flag


是在其他情况下被忽略。



width



字段是一个非负数值,指定被打印字符的最小数量。如果输出字符的数量少于


width


,默认在左边添加空白,或者使用


-flag


在右边添加空白来填补最小宽度。如果



width



使用


‘0’


作为前缀,使用


0


替代空白。


Width


字段不会截断输出。如果输出的长度超过指定宽度,所有字符都输出。



width



字段可以是个星号


(‘*’)


,这种情况下,参数列表中的


int



argument



提供



width



值。在星号前面指定一个


‘b’


来说明参数是一个无符号


char





precision



字段是一个非负数值,指定打印字符的数量、有效数字的位数或小数位的位数。



precision



字段可以引起输出截断或凑整,浮点数的情况在下表中说明。



Type



Precision




字段意义


d,u,o,x,X



precision



字段指定输出值中包含的最小位数。如果参数中的位数超过


precision


字段指定的位数,数字不会被截断。如果参数中的位数比


precision


字段少,输出值在左边补


0




f



precision



字段指定小数点右边的位数。最后一位数字四舍五入。


e,E



precision



字段指定小数点右边的位数。最后一位数字四舍五入。


g,G



precision



字段指定输出值的最大有效位数。


s



precision



字段指定输出值中字符的最大数量。超出的字符不输出。


c,p



precision



字段在这些字段类型中无效。



precision



字段可以是一个星号


(‘*’)


,这种情况下,参数列表中的


int



argument



提供这个值


。在星号前面指定


‘b’


说明参数是一个无符号


char





注意


  • 必须保证参数类型与格式说明符匹配。可以使用类型转换来保证将合适的类型传递给printf。

  • 这个函数基于

    _getkey



    putchar

    函数的操作特殊的执行。这些函数在标准库中提供,使用微控制器的串行端口读写字符。可以自定义函数使用其他I/O设备。

  • 由于8051内存有限,可以传递到函数的字节总数受到限制。在SMALL或COMPACT模式中最多15字节可以被传递。LARGE模式中最多40字节可以被传递。



返回值



printf



函数返回实际写入输出流的字符数量。



参考


gets


,


printf517


,


puts


,


scanf


,


scanf517


,


sprintf


,


sprintf517


,


sscanf


,


sscanf517


,


vprintf


,


vsprintf



示例


#include <stdio.h>


void tst_printf (void) {


char a = 1;


int b  = 12365;


long c = 0x7FFFFFFF;


unsigned char x = ‘A’;


unsigned int y  = 54321;


unsigned long z = 0x4A6F6E00;


float f = 10.0;


float g = 22.95;


char buf [] = “Test String”;


char *p = buf;


printf (“char %bd int %d long %ld\n”,a,b,c);


printf (“Uchar %bu Uint %u Ulong %lu\n”,x,y,z);


printf (“xchar %bx xint %x xlong %lx\n”,x,y,z);


printf (“String %s is at address %p\n”,buf,p);


printf (“%f != %g\n”, f, g);


printf (“%*f != %*g\n”, (int)8, f, (int)8, g);


}



版权声明:本文为mengxiaoguang原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。