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

查看: 3235|回复: 6

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

[复制链接]

61

主题

1071

回帖

17

蝴蝶豆

论坛元老

最后登录
2020-12-9
发表于 2016-10-16 21:37:07 | 显示全部楼层 |阅读模式
本帖最后由 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
<
回复

使用道具 举报

61

主题

1071

回帖

17

蝴蝶豆

论坛元老

最后登录
2020-12-9
 楼主| 发表于 2016-10-16 21:53:57 | 显示全部楼层
热烈庆祝自己升上高级会员
回复 支持 反对

使用道具 举报

1182

主题

3785

回帖

1

蝴蝶豆

论坛元老

最后登录
2020-3-17
发表于 2016-10-17 08:43:33 | 显示全部楼层
这么快
回复 支持 反对

使用道具 举报

61

主题

1071

回帖

17

蝴蝶豆

论坛元老

最后登录
2020-12-9
 楼主| 发表于 2016-10-17 09:39:58 | 显示全部楼层

小白要勤奋
回复 支持 反对

使用道具 举报

4

主题

574

回帖

3

蝴蝶豆

高级会员

最后登录
2020-12-9
发表于 2016-10-17 11:30:06 | 显示全部楼层
很详细啊,感谢分享。
回复 支持 反对

使用道具 举报

0

主题

159

回帖

0

蝴蝶豆

高级会员

最后登录
2017-3-9
发表于 2016-10-17 17:31:21 | 显示全部楼层
很不错,感谢分享。
回复 支持 反对

使用道具 举报

13

主题

695

回帖

0

蝴蝶豆

金牌会员

最后登录
2020-6-7
发表于 2016-10-17 21:04:35 | 显示全部楼层
回复 支持 反对

使用道具 举报

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版