在线时间4854 小时
UID3309825
ST金币0
蝴蝶豆17
注册时间2016-9-4
论坛元老
- 最后登录
- 2020-12-9
|
a0a.1 32b0c
本帖最后由 anywill 于 2016-10-21 11:50 编辑
暂未移植到nucleo
Uart(Universal Asynchronous Receiver/Transmitter)异步串口通讯是各类单片机中最古老又是最常用的通讯方式,在UART通讯过程中,我们需要使用3条信号线,即发送(RX),接收(TX)和地(GND),对于收发双方来说,RX和TX要交叉连接。由于这三条线中并不带时钟信号,所有在使用时必须约定数据的发送速度,即波特率;另外为了让接收方能够准确地识别出1个字符,还还需要在发送时添加起始位(1位)和停止位(1,1.5,2位)。由于UART通讯以字符为传输单位(一般是8个bit,但也可以是其它,如7个,9个),且字符的发送时间是不确定的(异步的),所以我们称它为异步串口通讯。UART串口通讯具有结构简单,实现方便的优点,但也有传输速度低的缺点。
在具体的应用中,UART串口有TTL电平的串口和RS232电平的串口类中,TTL电平是高电平为3.3V,低电平为0;而RS232是负逻辑电平,它定义+5~+12V为低电平,而-12~-5V为高电平,对于不加外部电路的单片机而言,它的UART输出都是TTL电平,相对RS232电平来说,通讯距离较短,而且容易受到干扰,但功耗较低,适合1米以内的短距离数据传输。下图是UART数据通讯过程中的时序图: Uart串口通讯其实在前面的代码中已经用到过,标准C语言的printf函数也被重定义到串口输出上,方便用户的调试,对于mbed来说,它使用Serial对象来完成串口的数据收发,它提供的主要方法有: 类名 | | | | Serial(PinName tx, PinName rx, const char *name=NULL); | | | | void format(int bits=8, Parity parity=SerialBase::None, int stop_bits=1); | 设置Uart传输的格式,包括一个字长的位数、奇偶检验的方法、停止位的位数,默认为一个字长为8位,无奇偶检验,1位停止位 | | | | | void attach(void (*fptr)(void), IrqType type=RxIrq); | | void set_flow_control(Flow type, PinName flow1=NC, PinName flow2=NC); | 设置Uart的流控方法,其目标是提高数据发送的可靠性,流控方法有无流控,RTS流控,CTS流控,RTSCTS流控,后面两个常数为流控的管脚设置 | | | | | int printf(const char* format, ...) | 格式化Uart的输出,参数等同标准C的printf | int scanf(const char* format, ...); | |
在这里需要重点说明的是,并不是所有的管脚都能成为Uart管脚,只要被定义成Uart相关功能的GPIO管脚才行,具体来说,需要参考mbed每个平台实现的Serial_api.c文件,其中的相关代码入如下: #define UART_NUM 4 static const PinMap PinMap_UART_TX[] = { {P0_0, UART_3, 2}, {P0_2, UART_0, 1}, {P0_10, UART_2, 1}, {P0_15, UART_1, 1}, {P0_25, UART_3, 3}, {P2_0 , UART_1, 2}, {P2_8 , UART_2, 2}, {P4_28, UART_3, 3}, {NC , NC , 0} }; static const PinMap PinMap_UART_RX[] = { {P0_1 , UART_3, 2}, {P0_3 , UART_0, 1}, {P0_11, UART_2, 1}, {P0_16, UART_1, 1}, {P0_26, UART_3, 3}, {P2_1 , UART_1, 2}, {P2_9 , UART_2, 2}, {P4_29, UART_3, 3}, {NC , NC , 0} }; static const PinMap PinMap_UART_RTS[] = { {P0_22, UART_1, 1}, {P2_7, UART_1, 2}, {NC, NC, 0} }; static const PinMap PinMap_UART_CTS[] = { {P0_17, UART_1, 1}, {P2_2, UART_1, 2}, {NC, NC, 0} }; 这四个PinMap类型的定义就定义了可以用作Uart各类功能的管脚名称,其中的PinMap类型定义如下: typedefstruct { PinNamepin; intperipheral; intfunction;
} PinMap; 其中的各个成员函数可以理解成管脚好、功能类型即UART、ADC、I2C等以及该功能对应GPIO的复用功能知识,如PinMap_UART_TX[]中的 {P0_0, UART_3, 2}表示P0_0管脚可以用作UART3的TX管脚,它对应的功能序号为2;PinMap_UART_RX[]中的{P0_1 , UART_3, 2}表示P0_1管脚可以用作UART3的RX管脚,它对应的功能序号为2。如果你查找LPC1768的Datasheet,你可以发现下面的说明:
后面的I2C,SPI等的构造函数也是同样的原理。对于xbed LPC1768来说,它一共有4个UART口,分别是UART0,UART1,UART2,UART3,其中的UART0已经和CP2104相连,同时也用作printf的重定向输出,用户无法外接使用。 mbed Uart双向串行通讯应用为了理解UART的通讯方法,我们先来输入下面的代码: Serial pc(USBRX,USBTX); DigitalOut led(LED1); int main() { while (1) { pc.putc(pc.getc()); led=0; wait(0.1); led=1; wait(0.1); }
} 我们编译上载后运行你会发现xbed LPC1768 LED乱闪,这是mbed出错的指示方式,表示用户的程序在运行过程中出现了问题,这是因为我们把不能设定为tx的USBRX管脚设成了tx管脚,我们把USBRX、USBTX换个顺序后程序就运行正常了。 现在我们来审视一下这段代码,它的目的是回显用户的输入,并保持LED的变换,但实际上我们发现该程序在运行过程中有以下问题: l 当用户没有字符输入时,LED灯并不会变化,程序处于等待状态; l 当用户一次输入字符过多时,返回的字符会有丢失。 这些问题的产生是由mbed Serial API实现的原理决定的,在mbed中,getc()这一读取函数会一直等待直到读取到输入,所以当没有字符输入时LED灯不会变化;至于字符丢失是因为LPC1768的UART有16个自己的发送和接收队列,对于超过16个字节的数据必须等待mbed读取完了以后再进行处理,如果读取处理速度不够快的会数据就丢失了,而本代码中0.1秒才读一次,显然是不行的,如果我们把wait(0.1)都去掉,那就没问题了。 当然,我们也可以换种方式来使用UART,前面的代码是用户不断地去读取串口数据,也就是我们所说的轮询方式,这种方式效率是很低的,与其对应的是中断方式,即当串口有数据的时候主动通知程序,下面是改进后的代码,此时你会发现数据丢失的问题也没了: Serial pc(USBTX,USBRX); DigitalOut led(LED1); void echouart() { pc.putc(pc.getc()); } int main() { pc.attach(&echouart,SerialBase::RxIrq); while (1) { led=0; wait(0.1); led=1; wait(0.1); }
} 在Uart串口的双向通讯中,经常涉及到一个用户交互的问题,如当用户执行完一段代码后,经常会说请按任意键或特定键继续,这是,就相当于让程序一直等待,直到程序期待的字符出现,这在mbed中是很容易的,如下面的代码: Serial pc(USBTX,USBRX); DigitalOut led(LED1); char username[100]; int userkey; int main() { pc.printf("Hello World,please enter you name to continue\r\n"); pc.scanf("%s",username); pc.printf("You name is %s \r\n",username); while (pc.readable()) pc.getc(); while (1) { pc.printf("Hello World,please enter return to continue\r\n"); userkey=pc.getc(); if (userkey=='\r') break; else pc.printf("Wrong key,please enter return key to continue \r\n"); } pc.printf("Right key,good bye \r\n");
} 这里需要注意的是,我们额外添加了while (pc.readable()) pc.getc()这部分代码,其目标是为了防止前面的输入有可能没有被scanf读取完全,所以先把它清空再读取。 考虑到串口操作涉及的大量是字符串操作,所以我们在必要时还可以使用C++的string类来简化字符串的处理,在利用string之前,必须要添加string的引用,即#include<string>,注意不是string.h,string的具体用法可以参见C++的手册,下面是一个简单的例子,用户可以尝试一下,如果做复杂的字符串查找替换操作,建议使用string类: #include<string> char username[100]; string str("You name is "); int main() { pc.printf("Hello World,please enter you name to continue\r\n"); pc.scanf("%s",username); str=str.append(username); str=str.append(". \n"); pc.printf(str.data());
}
mbed UART通讯原帖地址 http://mbed.smeshlink.com/cookbook/33-mbed-uart
|
|