ETRD博客

树莓派3硬件串口的使用及编程

引言

通常学习一块新的硬件平台,大家第一个工程便是流水灯,实际上这属于IO口的基本控制,这与上篇文章VS使用wiringPi库控制树莓派的GPIO是异曲同工的,接下来便应该是开始使用其通信接口,而通信接口里面最简单的又属串口(uart),虽然简单但目前仍然有很多模块是以串口作为通信接口的,如一些蓝牙模块、低速wifi模块、GPS模块、GPRS模块等。树莓派3代与之前的树莓派相比除了CPU性能上的提升外,还有一个显著的特色便是板载集成了WIFI及蓝牙,正是由于集成的这个蓝牙模块,又使得树莓派3代的串口使用相比于之前版本有了些麻烦,本文的目的便是解决这些麻烦并编写验证了一个简单的串口代码。

树莓派3代串口的麻烦

树莓派从大的方向来说一共出了3代,每一代的CPU外设基本相同,但内核不同,外设里面一共包含两个串口,一个称之为硬件串口(/dev/ttyAMA0),一个称之为mini串口(/dev/ttyS0)。硬件串口由硬件实现,有单独的波特率时钟源,性能高、可靠,mini串口性能低,功能也简单,并且没有波特率专用的时钟源而是由CPU内核时钟提供,因此mini串口有个致命的弱点是:波特率受到内核时钟的影响。内核若在智能调整功耗降低主频时,相应的这个mini串口的波特率便受到牵连了,虽然你可以固定内核的时钟频率,但这显然不符合低碳、节能的口号。在所有的树莓派板卡中都通过排针将一个串口引出来了,目前除了树莓派3代以外 ,引出的串口默认是CPU的那个硬件串口。而在树莓派3代中,由于板载蓝牙模块,因此这个硬件串口被默认分配给与蓝牙模块通信了,而把那个mini串口默认分配给了排针引出的GPIO Tx Rx,下图是树莓派3的接口图

QQ截图20170129145844

其中红框中就是引出的串口IO,如果我们需要通过UART外接模块,默认情况下必须得使用性能很低的mini串口了,而且随着内核主频的变化,还会造成波特率的变化导致通信的失败,几乎很难使用。所以我们希望恢复硬件串口与GPIO 14/15的映射关系,使得我们能够通过GPIO使用高性能的硬件串口来连接我们的串口设备。

将树莓派3的硬件串口与mini串口默认映射对换

树莓派可以配置文件来修改设备树,我的理解是可以通过配置文件来修改管脚的映射关系,这在许多Cortex-M3内核的单片机中也很常见,可以将同一个串口映射到不同的管脚上,以方便PCB的布线。

为了在树莓派3中通过GPIO使用高性能的硬件串口,我们必须将分配给蓝牙使用的硬件串口与分配给IO排针的mini串口进行对换,这必然会使得蓝牙模块的功能受到影响,但还好,蓝牙并不是必须的。

在树莓派系统中

1
/boot/overlays/

目录下,提供了一个pi3-miniuart-bt-overlay.dtb的文件,这个文件的作用可以通过该目录下的README文件查看

QQ截图20170129153256

QQ截图20170129153541

README文件中说明了这个文件的功能是将树莓派3的蓝牙切换到mini串口(ttyS0),并且恢复硬件串口(ttyAMA0)到GPIO 14&15脚中。并且给出了载入的方法。

首先在树莓派命令终端中通过命令查看树莓派3当前的串口映射关系

1
ls -l /dev

QQ截图20170129151839

红色框中体现的应该是一种映射关系,此处暂时没有做深究,简单理解 serial0 就是GPIO映射的串口,此时GPIO映射的串口是默认的/dev/ttyS0这个mini串口。

使用下面这条指令编辑 /boot/config.txt 文件

1
sudo nano /boot/config.txt

在该文件中增加一行代码

1
dtoverlay=pi3-miniuart-bt

然后保存文件,重启树莓派使之生效。

再通过 ls -l /dev 命令查看修改后的映射关系

QQ截图20170129154121

对比修改前的关系,可以看出serial0和serial1 与 ttyAMA0和ttyS0的映射关系对换完成了,也就是ttyAMA0映射到了引出的GPIO Tx Rx上。

禁用串口的控制台功能

前面的步骤已经交换了硬件串口与mini串口的映射关系,但现在想使用树莓派外接串口模块进行通信还不行,因为树莓派IO引出的串口默认是用来做控制台使用的,它的初衷是为了在没有网络接口时,通过串口对树莓派进行相关的配置。因此需要禁用这个默认功能,使得串口为我们自由使用。

在树莓派命令窗口中分别通过如下两个命令停止和禁用串口的控制台功能

1
2
sudo systemctl stop serial-getty@ttyAMA0.service
sudo systemctl disable serial-getty@ttyAMA0.service

由于我们前面已经交换了串口的映射关系,因此这里注意是ttyAMA0。

然后通过下列指令编辑cmdline.txt文件

1
sudo nano /boot/cmdline.txt

然后看到里面类似如下的内容

1
dwc_otg.lpm_enable=0 console=serial0,115200 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait

把console=serial0,115200删掉 console=serial0,115200 ,剩下的内容类似如下

1
dwc_otg.lpm_enable=0 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait

然后重新启动树莓派,使修改生效

编写一个简单的串口程序

wiringpi实际上不只提供了基本的IO口的读写函数,还提供了串口操作库函数,引用”wiringserial.h”即可使用,仍然使用前面的工程,参考wiringpi的串口例程,写了如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <errno.h>
#include <string.h>
#include <wiringpi.h>
#include <wiringserial.h>

int main(int argc, char *argv[])
{

int fd;
if ((fd = serialOpen("/dev/ttyAMA0", 115200)) < 0)
{
fprintf(stderr, "Unable to open serial device: %s\n", strerror(errno));
return 1 ;
}
serialPuts(fd, "uart send test, just by launcher");

for (;;)
{
if (serialDataAvail(fd) > 0)
{
putchar(serialGetchar(fd));
}

}
return 0;
}

使用USB转TTL串口模块,USB端接电脑,TTL端接树莓派的IO口,实物连接图如下:

QQ截图20170129165518

1
serialPuts(fd, "uart send test, just by launcher");

该语句是实现树莓派发送一串字符串给Windows电脑端的串口调试助手

1
2
3
4
if (serialDataAvail(fd) > 0)
{
putchar(serialGetchar(fd));
}

这几行代码是实现将Windows电脑端串口调试助手发过来的信息进行接收,并显示在调试输出信息上。

实测结果:

QQ截图20170129142637

这是Windows串口调试助手上接收的信息,以及等待发送的信息。

QQ截图20170129162448

这是调试时,树莓派串口接收到信息时的输出

小结

树莓派3代不同于以往的树莓派旧版,CPU的高性能硬件串口默认分配给了蓝牙使用,GPIO 14&15默认分配的是性能较差的mini串口,在无需使用板载蓝牙的情况下,可以通过配置文件修改树莓派的设备树,使得高性能硬件串口重新恢复映射到IO排针接口中。同时要想通过GPIO 14&15外接串口通信模块还需要关掉串口的默认控制台功能。本文在参考已有文献资料的情况下试图阐述清楚了树莓派中两个串口的关系,并最后给出了一个简单的示例代码实现了串口的编程使用。

限于水平,难免有理解或者认识的错误之处,有任何问题,请让我知道。

参考文献

http://spellfoundry.com/2016/05/29/configuring-gpio-serial-port-raspbian-jessie-including-pi-3/

https://www.raspberrypi.com.tw/10842/raspberry-pi-3-uart-overlay-workaround/

http://wiringpi.com/reference/serial-library/

https://github.com/WiringPi/WiringPi/blob/master/examples/serialRead.c