因为公司产品的需求,需要用到树莓派上的串口功能,但树莓派3的串口直接不能用,因为在树莓派3用的CPU中本身有两个串口,一个是硬件串口(PL011 UART),一个是迷你串口(mini-uart),在树莓派2B和B+等老版本上,树莓派将硬件串口分配给了GPIO14和GPIO15,因此可以直接使用这个串口。但在树莓派3上,多了一个然并卵的蓝牙模块(起码我感觉丫就是然并卵的东西,我们有自己的蓝牙产品,完全不用这个多余的货,各位有其他用的另当别论了),官方将硬件串口无私的给了蓝牙,而将一个没有时钟源,必须由内核提供时钟参考源的“迷你串口”分配给了GPIO的串口,这样以来由于内核的频率本身是变化的,就会导致“迷你串口”的速率不稳定,这样就出现了无法正常使用的情况。
网上给的提示是,关闭蓝牙,直接回复硬件串口的GPIO,所以呢,如果想用串口的话,还是放弃蓝牙吧。
还有一点恶心的,我的测试机用的是2016-03-18-raspbian-jessie,而现在我们正式环境用的是2016-09-23-raspbian-jessie-lite,本来在测试机调试好的,到生产环境就SB了,完全不通,所以各位看这个教程的时候,请务必先确认一下自己系统的版本,对于3月之后版本的,等我研究透了再另行发帖,到时候我会附在本帖后面。目前网上没找到关于3月之后版本怎么做串口的,大家静候吧。
废话过后,干货来了!
第一步,我们需要修改配置文件,
sudo vi /boot/config.txt
在最下面添加两行内容
dtoverlay=pi3-miniuart-bt-overlay
force_turbo=1
这里需要注意以下,做这一步的时候先去看看/boot/overlays下面是不是有一个叫pi3-miniuart-bt-overlay.dtb的文件,或者pi3-miniuart-bt.dtb,都行,根据文件名不同,上面配置的那个参数也不一样
如果没有,去下载以下,地址 http://ukonline2000.com/?attachment_id=881
第二步,配置串口
sudo vi /boot/cmdline.txt
把里面的内容改成
dwc_otg.lpm_enable=0 console=serial1,115200 console=tty1 root=/dev/mmcblk0p2 kgdboc=serial1,115200 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait
具体为啥,我还没来得及研究,有明白人可以回复一下,或者等我有空了补上这块内容
第三步,关闭蓝牙
sudo systemctl disable hciuart
第四步,修改串口指向
sudo vi /lib/systemd/system/hciuart.service
将 “ttyAMA0”修改为“ttyS0”就下面图这样子
只改这一样就行了,下面的那个不用管他
如果这时候你看到的是After=def-serial1.device,那说明你是3月之后的版本,这篇教程不适用
第五步,重启树莓派,就OK了
还有一点需要提示的,如果你安装的是精简版(尾缀-lite的镜像),有可能串口没有打开,这个可以先去驱动里确认一下
ls /dev看看有没有ttyAMA0,如果没有,就说明没打开,这时候去配置中开一下就有了
sudo raspi-config
选择Advanced Options -> Serial
选择yes就行了
完事之后重启树莓派,就有ttyAMA0了
我们用的是JAVA开发程序,所以才做串口必须借助一些开发包,当然首选应该是Pi4J,毕竟正在做这块的翻译嘛,但是我并没用他,而是用了RXTX,比较方便。
先说安装步骤,Windows下直接下载包就可以用了,树莓派下需要安装一下。
第一步,下载包
wget http://rxtx.qbang.org/pub/rxtx/rxtx-2.1-7r2.zip
第二步,解压
unzip rxtx-2.1-7r2.zip
第三步,修改参数
首先需要获得一个版本号
uname -r
(我的是4.1.19-v7+)
sudo vi /usr/include/linux/version.h
最后面加一行
#define UTS_RELEASE "4.1.19-v7+"
那个版本号就是咱们刚才查出来的版本号
第四步,修改安装文件参数
在解压好的包中,有个configure,我们要把里面的所有1.2*|1.3*|1.4*|1.5*,都改成1.2*|1.3*|1.4*|1.5*|1.6*|1.7*|1.8*
这一步建议到Windows下面修改,大概有个四五处吧
sudo sh ./configure
make
sudo make install
最后一样记得加 sudo
出现 /usr/bin/install -c RXTXcomm.jar /usr/lib/jvm/jdk-8-oracle-arm32-vfp-hflt/jre/lib/ext/就表示安装成功了!
还有一点要注意的,安装貌似需要JDK8环境,7试验失败,不知道能不能装,建议大家还是升级一下吧
下面,开始写程序
RXTX只支持ttyS开头的设备,而我们要跟串口通讯用的是 ttyAMA0,所以在此之前还要做一下映射
sudo ln -s /dev/ttyAMA0 /dev/ttyS45
45只是我随便起了个名字而已,大家随意吧
树莓派的串口是TTL电平的,所以测试的时候需要注意以下
import
gnu.io.CommPortIdentifier;
import
gnu.io.PortInUseException;
import
gnu.io.SerialPort;
import
gnu.io.SerialPortEvent;
import
gnu.io.SerialPortEventListener;
import
gnu.io.UnsupportedCommOperationException;
import
java.io.IOException;
import
java.io.InputStream;
import
java.io.OutputStream;
import
java.util.Enumeration;
import
java.util.TooManyListenersException;
public
class
Serial {
private
SerialPort
port
=
null
;
private
InputStream
in
=
null
;
private
OutputStream
out
=
null
;
private
Serial() {}
private
Serial(String name) {
//获得本地所有串口列表,这里其实只能获得ttyS开头的串口
Enumeration portList =CommPortIdentifier.getPortIdentifiers();
while
(portList.hasMoreElements()){
//获得串口的标识符
CommPortIdentifier portId = portList.nextElement();
//通过标识符得到串口名字,并判断这个名字是不是我们需要的那个串口
if
(portId.getName().equals(
"/dev/"
+name)){
SerialPort p=
null
;
try
{
//如果确实是我们需要的串口,则打开这个串口
//open(串口占用进程名称,串口等待超时时间)
p = (SerialPort) portId.open(
"TTSTest"
, 2000);
//给串口一个数据到达侦听(触发器)
p.addEventListener(
new
EventListener());
//把数据到达通知打开
p.notifyOnDataAvailable(
true
);
//设置串口的波特率,参数依次是(波特率,数据位,停止位,校验位)
p.setSerialPortParams(9600,SerialPort.
DATABITS_8
, SerialPort.
STOPBITS_1
,SerialPort.
PARITY_NONE
);
//获得输入输出流,方便操作。
out
= p.getOutputStream();
in
= p.getInputStream();
port
=p;
}
catch
(PortInUseException e) {
e.printStackTrace();
}
catch
(TooManyListenersException e) {
e.printStackTrace();
}
catch
(UnsupportedCommOperationException e) {
e.printStackTrace();
}
catch
(IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 打开串口
*
*
@param
port
*/
public
static
final
Serial open(String name) {
Serial serial =
new
Serial(name);
if
(serial.
port
!=
null
){
return
serial;
}
return
null
;
}
/**
* 发送数据。
*
@param
data
*/
public
void
send(
byte
[] data){
try
{
out
.write(data);
out
.flush();
}
catch
(IOException e) {
e.printStackTrace();
}
}
/**
* 关闭串口。
*/
public
void
close(){
//当然,这里可以做一下事件侦听,再给close加个参数,这样在串口异常报错的时候能能捕获到了。
port
.close();
port
=
null
;
out
=
null
;
in
=
null
;
}
private
class
EventListener
implements
SerialPortEventListener{
@Override
public
void
serialEvent(SerialPortEvent event) {
switch
(event.getEventType()) {
//这些属性应该跟串口特性有关,我还没搞清楚,暂时不解释
case
SerialPortEvent.
BI
:
case
SerialPortEvent.
OE
:
case
SerialPortEvent.
FE
:
case
SerialPortEvent.
PE
:
case
SerialPortEvent.
CD
:
case
SerialPortEvent.
CTS
:
case
SerialPortEvent.
DSR
:
case
SerialPortEvent.
RI
:
case
SerialPortEvent.
OUTPUT_BUFFER_EMPTY
:
break
;
case
SerialPortEvent.
DATA_AVAILABLE
:
//获取到串口返回信息
byte
[] data =
new
byte
[512];
int
len=0;
try
{
while
((len=
in
.read(data))>=0){
//这里处理你获得的数据即可。
}
}
catch
(IOException e) {
//读信息报错后直接关闭串口
close();
e.printStackTrace();
}
//如果len读到-1的话,说明串口出问题了,这里直接关闭就行了
//以下的部分自由发挥吧
close();
break
;
default
:
break
;
}
}
}
}