你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

ARM®mbed OS入门开发 mbed UART通讯

[复制链接]
anywill 提问时间:2016-10-16 21:37 /
本帖最后由 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
Serial(PinName tx, PinName rx, const char *name=NULL);
构造函数,把tx,rx设成Uart的输出输入管脚
void baud(int baudrate);
设置Uart的波特率,默认为9600
void format(int bits=8, Parity parity=SerialBase::None, int stop_bits=1);
设置Uart传输的格式,包括一个字长的位数、奇偶检验的方法、停止位的位数,默认为一个字长为8位,无奇偶检验,1位停止位
int readable();
返回Uart是否有数据到达
int writeable();
返回Uart是否还有空间进行数据发送
void attach(void (*fptr)(void), IrqType type=RxIrq);
设置Uart中断是需要执行的用户自定义函数
void set_flow_control(Flow type, PinName flow1=NC, PinName flow2=NC);
设置Uart的流控方法,其目标是提高数据发送的可靠性,流控方法有无流控,RTS流控,CTS流控,RTSCTS流控,后面两个常数为流控的管脚设置
int getc()
从Uart读取一个字符
int putc(int c);
向Uart发送一个字符
int printf(const char* format, ...)
格式化Uart的输出,参数等同标准C的printf
int scanf(const char* format, ...);
格式化Uart的输入,参数等同标准C的scanf
在这里需要重点说明的是,并不是所有的管脚都能成为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
收藏 2 评论6 发布时间:2016-10-16 21:37

举报

6个回答
anywill 回答时间:2016-10-16 21:53:57
热烈庆祝自己升上高级会员
zero99 回答时间:2016-10-17 08:43:33
这么快
anywill 回答时间:2016-10-17 09:39:58

小白要勤奋
高二毛 回答时间:2016-10-17 11:30:06
很详细啊,感谢分享。
xyx365 回答时间:2016-10-17 17:31:21
很不错,感谢分享。
assssdz 回答时间:2016-10-17 21:04:35
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32Cube扩展软件包
意法半导体边缘AI套件
ST - 理想汽车豪华SUV案例
ST意法半导体智能家居案例
STM32 ARM Cortex 32位微控制器
关注我们
st-img 微信公众号
st-img 手机版