Android開發板串列埠(SerialPort)通訊
前言:
最近在總是看見有人在群裡面問一些串列埠通訊相關的問題,特別是對於我們這些做APP出生的程式設計師來說,初次接觸串列埠通訊的確是會遇到各種Bug,各種摸不著頭腦。串列埠通訊偏向嵌入式一點,是Android裝置通過串列埠與其他裝置進行通訊的一種方式,本文介紹的Android純串列埠的通訊,並不是手機上的USB串列埠通訊。
簡介:
首先簡述一下此專案應用,它是一個簡單的物聯網智慧盒子,主要工作:是通過Android開發板上的串列埠進行資料讀寫操作。一塊Android開發板外接一個微控制器,微控制器上面跑有MQTT服務與後臺伺服器通訊,微控制器收到MQTT釋出的不同資訊後,將資訊轉換成不同的串列埠指令發給Android板,Android板收到對應的串列埠指令就做相應的介面處理併發送響應指令給微控制器,然後微控制器直接控制硬體裝置工作。一、什麼是串列埠?
串列埠 (SerialPort)簡稱:串列埠,主要用於資料被逐位按順序傳送的通訊方式稱為串列埠通訊(簡單來講就是按順序一位一位地傳輸資料)。
常見的串列埠有25針和9針(遵循RS-232標準)
二、串列埠通訊原理
串列埠通訊(Serial Communications)的概念非常簡單,串列埠按位(bit)傳送和接收位元組。
串列埠用於ASCII碼字元的傳輸。通訊使用3根線完成,分別是地線(GND)、傳送(TX)、接收(RX)。由於串列埠通訊是非同步的,埠能夠在一根線上傳送資料同時在另一根線上接收資料。其他線用於握手,但不是必須的。串列埠通訊最重要的引數是波特率、資料位、停止位和奇偶校驗。對於兩個進行通訊的埠,這些引數必須匹配。
三、Android應用串列埠通訊的實現
Android SDK並沒有在Framework層實現封裝關於串列埠通訊的類庫。但是,Android是基於Linux kernel 2.6上的,所以我們可以像在Linux系統上一樣來使用串列埠。因為Framework層中並沒有封裝關於串列埠通訊的類庫,所以我們需要通過Android NDK來實現開啟、讀寫串列埠,然後提供介面供JAVA本地呼叫。
這是12年的程式碼,還是Eclipse工程,本文主要介紹如何在Android Studio中使用。這裡先得配置好NDK環境
1:專案配置
首先看一下專案結構:建立了一個jni和jniLibs兩個包,jni包用來放生成的標頭檔案(.
Java層的程式碼,Google已經給封裝好了,主要的都在 SerialPort.java
第一步:拷貝建立 native方法的java類 SerialPort.java 到自己的專案中;
第二步:生成.h標頭檔案; 生成方法有多種,這裡使用 cd \app\src\main\java 進入到java目錄下,然後 javah -classpath . -jni +全限定類名
第三步:實現SerialPort.java類中的本地方法,這裡直接拷貝Google寫好的C實現,將c程式碼中的函式名與生成的標頭檔案中的函式名保持一致
配置build.gradle
使用
串列埠作操都封裝到SerialPortUtil類中
/**
* Created by Roy88 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);
//獲取開啟的串列埠中的輸入輸出流,以便於串列埠資料的收發
mInputStream=serialPort.getInputStream();
mOutputStream=serialPort.getOutputStream();
flag=true;
//接收串列埠資料
receiveSerialPort();
}catch(IOException e) {
e.printStackTrace();
}
}
/**
* 接收串列埠資料
*/
public static voidreceiveSerialPort(){
newThread(newRunnable() {
@Override
public voidrun() {
//迴圈接收串列埠資料
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,"接收串列埠資料:"+ str);
if(String.valueOf(str.charAt(0)).equals("{") && str.substring(str.length() -1).equals("}")){
acceptAndNotify(str);
}
}
}catch(Exception e) {
e.printStackTrace();
}
}
}
}).start();
}
/**
* 區分收到的指令資料並分類分發
*
*@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("轉化錯誤... ");
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;
}
}
/**
* 傳送串列埠資料
*@paramdata要傳送的資料
*/
public static voidsendSerialPort(String data){
Log.i(TAG,"傳送串列埠資料:"+ data);
try{
byte[] sendData = data.getBytes();
mOutputStream.write(sendData);
mOutputStream.flush();
Log.i(TAG,"傳送串列埠資料成功!");
}catch(IOException e) {
e.printStackTrace();
Log.i(TAG,"傳送串列埠資料失敗!");
}
}
/**
*關閉串列埠
*關閉串列埠中的輸入輸出流
*然後將flag的值設為flag,終止接收資料執行緒
*/
public static voidcloseSerialPort(){
Log.i(TAG,"關閉串列埠");
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();
}
}
}
本文沒有介紹關於jni、NDK的內容,因為上網有很講這方面知識講解。如有不瞭解的請自行百度、google。
注意:
不同的開發板操作的串列埠路徑是不同的,比如我的開發板上的串列埠路徑是“/dev/ttyS5”;還有就是資料傳輸的波特率要設定成一致,不然收到的資料會亂碼。