前言:
最近在總是看見有人在群里面問一些串口通信相關(guān)的問題墨技,特別是對于我們這些做APP出生的程序員來說谷朝,初次接觸串口通信的確是會遇到各種Bug,各種摸不著頭腦寄摆。串口通信偏向嵌入式一點喇闸,是Android設(shè)備通過串口與其他設(shè)備進行通信的一種方式逝她,本文介紹的Android純串口的通信浇坐,并不是手機上的USB串口通信。
簡介:
首先簡述一下此項目應(yīng)用黔宛,它是一個簡單的物聯(lián)網(wǎng)智能盒子近刘,主要工作:是通過Android開發(fā)板上的串口進行數(shù)據(jù)讀寫操作。一塊Android開發(fā)板外接一個單片機臀晃,Android上面跑有MQTT服務(wù)與后臺服務(wù)器通信觉渴,Android機收到MQTT發(fā)布的不同信息后,將信息轉(zhuǎn)換成不同的串口指令發(fā)給單片機徽惋,Android板收到對應(yīng)的MQTT消息就做相應(yīng)的界面處理并將MQTT消息轉(zhuǎn)換成不同的串口指令發(fā)給單片機案淋,然后單片機直接控制硬件設(shè)備工作。
一险绘、什么是串口踢京?
串行端口 (SerialPort)簡稱:串口誉碴,主要用于數(shù)據(jù)被逐位按順序傳送的通訊方式稱為串口通訊(簡單來講就是按順序一位一位地傳輸數(shù)據(jù))。
常見的串口有25針和9針(遵循RS-232標準)
二瓣距、串口通信原理
串口通信(Serial Communications)的概念非常簡單黔帕,串口按位(bit)發(fā)送和接收字節(jié)。
串口用于ASCII碼字符的傳輸蹈丸。通信使用3根線完成成黄,分別是地線(GND)、發(fā)送(TX)逻杖、接收(RX)奋岁。由于串口通信是異步的,端口能夠在一根線上發(fā)送數(shù)據(jù)同時在另一根線上接收數(shù)據(jù)荸百。其他線用于握手闻伶,但不是必須的。串口通信最重要的參數(shù)是波特率够话、數(shù)據(jù)位虾攻、停止位和奇偶校驗。對于兩個進行通信的端口更鲁,這些參數(shù)必須匹配。
三奇钞、Android應(yīng)用串口通信的實現(xiàn)
Android SDK并沒有在Framework層實現(xiàn)封裝關(guān)于串口通信的類庫澡为。但是,Android是基于Linux kernel 2.6上的景埃,所以我們可以像在Linux系統(tǒng)上一樣來使用串口媒至。因為Framework層中并沒有封裝關(guān)于串口通信的類庫,所以我們需要通過Android NDK來實現(xiàn)打開谷徙、讀寫串口拒啰,然后提供接口供JAVA本地調(diào)用。
這里可以參照Google已經(jīng)給出了源碼完慧。
這是12年的代碼谋旦,還是Eclipse工程,本文主要介紹如何在Android Studio中使用屈尼。這里先得配置好NDK環(huán)境
1:項目配置
首先看一下項目結(jié)構(gòu):創(chuàng)建了一個jni目錄册着,jni目錄用來放生成的頭文件(.h文件)及Java本地方法的C代碼實現(xiàn)類;
Java層的代碼脾歧,Google已經(jīng)給封裝好了甲捏,主要的都在SerialPort.java
第一步:拷貝創(chuàng)建 native方法的java類 SerialPort.java 到自己的項目中;
第二步:生成.h頭文件; 生成方法有多種鞭执,這里使用 cd \app\src\main\java 進入到j(luò)ava目錄下司顿;
方式1: javah -classpath . -jni? +全限定類名芒粹;
方式2:javah -d ../jni? +全限定類名;(此方式會直接創(chuàng)建一個jni目錄大溜,然后把生成的.h文件存放在目錄里面)
第三步:實現(xiàn)SerialPort.java類中的本地方法化漆,這里直接拷貝Google寫好的C實現(xiàn),將c代碼中的函數(shù)名與生成的頭文件中的函數(shù)名保持一致
配置build.gradle
需要封裝好的aar或jar包猎提,加扣扣
使用
串口作操都封裝到SerialPortUtil類中
```
/**
* Created by 631934797 on 2017/6/18.
*
*串口工具類
*/
public classSerialPortUtil {
private static finalStringTAG= SerialPortUtil.class.getSimpleName();
public staticSerialPortserialPort=null;
public staticInputStreammInputStream=null;
public staticOutputStreammOutputStream=null;
public static booleanflag=false;
private staticBufferedReaderbr;
/**
* 打開串口
*/
public static voidopenSrialPort(String port, intbaudrate){
Log.i(TAG,"打開串口");
try{
serialPort=newSerialPort(newFile("/dev/"+ port),baudrate,0);
//獲取打開的串口中的輸入輸出流获三,以便于串口數(shù)據(jù)的收發(fā)
mInputStream=serialPort.getInputStream();
mOutputStream=serialPort.getOutputStream();
flag=true;
//接收串口數(shù)據(jù)
receiveSerialPort();
}catch(IOException e) {
e.printStackTrace();
}
}
/**
* 接收串口數(shù)據(jù)
*/
public static voidreceiveSerialPort(){
newThread(newRunnable() {
@Override
public voidrun() {
//循環(huán)接收串口數(shù)據(jù)
while(flag) {
try{
if(mInputStream==null)return;
br=newBufferedReader(newInputStreamReader(mInputStream));
String str;
while((str =br.readLine()) !=null)
{
if(TextUtils.isEmpty(str))continue;
Log.i(TAG,"接收串口數(shù)據(jù):"+ str);
if(String.valueOf(str.charAt(0)).equals("{") && str.substring(str.length() -1).equals("}")){
acceptAndNotify(str);
}
}
}catch(Exception e) {
e.printStackTrace();
}
}
}
}).start();
}
/**
* 區(qū)分收到的指令數(shù)據(jù)并分類分發(fā)
*
*@paramjsonBack收到的JSON指令
*/
private static voidacceptAndNotify(String jsonBack) {
if(jsonBack ==null||"".equals(jsonBack.trim()))
throw newIllegalArgumentException("JsonBack is illegal, please check args ... ");
JsonParser jsonParser =newJsonParser();
JsonObject json = (JsonObject) jsonParser.parse(jsonBack);
if(json ==null)
throw newJsonParseException("Json Parse error, please check args ... ");
String protocolResult = json.getAsJsonPrimitive("protocol").getAsString();
if(protocolResult ==null||"".equals(protocolResult))
throw newNumberFormatException("轉(zhuǎn)化錯誤... ");
switch(protocolResult) {
case"coin_in":
//? ? ? ? ? ? ? ? EventBus.getDefault().post(new SerialPortEvent(GsonUtil.parse(jsonBack, CoinAndRemoteCoinBean.class)));
break;
case"remote_coin_in":
//? ? ? ? ? ? ? ? EventBus.getDefault().post(new SerialPortEvent(GsonUtil.parse(jsonBack, CoinAndRemoteCoinBean.class)));
break;
case"key_event":
//? ? ? ? ? ? ? ? EventBus.getDefault().post(new SerialPortEvent(GsonUtil.parse(jsonBack, OrientationAndKeyBean.class)));
break;
}
}
/**
* 發(fā)送串口數(shù)據(jù)
*@paramdata要發(fā)送的數(shù)據(jù)
*/
public static voidsendSerialPort(String data){
Log.i(TAG,"發(fā)送串口數(shù)據(jù):"+ data);
try{
byte[] sendData = data.getBytes();
mOutputStream.write(sendData);
mOutputStream.flush();
Log.i(TAG,"發(fā)送串口數(shù)據(jù)成功!");
}catch(IOException e) {
e.printStackTrace();
Log.i(TAG,"發(fā)送串口數(shù)據(jù)失斚撬铡疙教!");
}
}
/**
*關(guān)閉串口
*關(guān)閉串口中的輸入輸出流
*然后將flag的值設(shè)為flag,終止接收數(shù)據(jù)線程
*/
public static voidcloseSerialPort(){
Log.i(TAG,"關(guān)閉串口");
try{
if(serialPort!=null) {
serialPort.close();
}
if(mInputStream!=null) {
mInputStream.close();
}
if(mOutputStream!=null){
mOutputStream.close();
}
if(br!=null){
br.close();
}
flag=false;
}catch(IOException e) {
e.printStackTrace();
}
}
}
```
本文沒有介紹關(guān)于jni伞租、NDK的內(nèi)容贞谓,因為上網(wǎng)有很講這方面知識講解。如有不了解的請自行百度葵诈、google裸弦。