Android開發(fā)板串口(SerialPort)通信

前言:

最近在總是看見有人在群里面問一些串口通信相關(guān)的問題,特別是對于我們這些做APP出生的程序員來說,初次接觸串口通信的確是會遇到各種Bug,各種摸不著頭腦。串口通信偏向嵌入式一點(diǎn),是Android設(shè)備通過串口與其他設(shè)備進(jìn)行通信的一種方式,本文介紹的Android純串口的通信,并不是手機(jī)上的USB串口通信。

簡介:

首先簡述一下此項目應(yīng)用,它是一個簡單的物聯(lián)網(wǎng)智能盒子,主要工作:是通過Android開發(fā)板上的串口進(jìn)行數(shù)據(jù)讀寫操作。一塊Android開發(fā)板外接一個單片機(jī),Android上面跑有MQTT服務(wù)與后臺服務(wù)器通信,Android機(jī)收到MQTT發(fā)布的不同信息后,將信息轉(zhuǎn)換成不同的串口指令發(fā)給單片機(jī),Android板收到對應(yīng)的MQTT消息就做相應(yīng)的界面處理并將MQTT消息轉(zhuǎn)換成不同的串口指令發(fā)給單片機(jī),然后單片機(jī)直接控制硬件設(shè)備工作。

一、什么是串口?

串行端口 (SerialPort)簡稱:串口,主要用于數(shù)據(jù)被逐位按順序傳送的通訊方式稱為串口通訊(簡單來講就是按順序一位一位地傳輸數(shù)據(jù))。

常見的串口有25針和9針(遵循RS-232標(biāo)準(zhǔn))

二、串口通信原理

串口通信(Serial Communications)的概念非常簡單,串口按位(bit)發(fā)送和接收字節(jié)。

串口用于ASCII碼字符的傳輸。通信使用3根線完成,分別是地線(GND)、發(fā)送(TX)、接收(RX)。由于串口通信是異步的,端口能夠在一根線上發(fā)送數(shù)據(jù)同時在另一根線上接收數(shù)據(jù)。其他線用于握手,但不是必須的。串口通信最重要的參數(shù)是波特率、數(shù)據(jù)位、停止位和奇偶校驗(yàn)。對于兩個進(jìn)行通信的端口,這些參數(shù)必須匹配。

三、Android應(yīng)用串口通信的實(shí)現(xiàn)

Android SDK并沒有在Framework層實(shí)現(xiàn)封裝關(guān)于串口通信的類庫。但是,Android是基于Linux kernel 2.6上的,所以我們可以像在Linux系統(tǒng)上一樣來使用串口。因?yàn)镕ramework層中并沒有封裝關(guān)于串口通信的類庫,所以我們需要通過Android NDK來實(shí)現(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代碼實(shí)現(xiàn)類;

此Demo支持多串口通信

Java層的代碼,Google已經(jīng)給封裝好了,主要的都在SerialPort.java

第一步:拷貝創(chuàng)建 native方法的java類 SerialPort.java 到自己的項目中;

第二步:生成.h頭文件; 生成方法有多種,這里使用 cd \app\src\main\java 進(jìn)入到j(luò)ava目錄下;

方式1: javah -classpath . -jni? +全限定類名;

方式2:javah -d ../jni? +全限定類名;(此方式會直接創(chuàng)建一個jni目錄,然后把生成的.h文件存放在目錄里面)

第三步:實(shí)現(xiàn)SerialPort.java類中的本地方法,這里直接拷貝Google寫好的C實(shí)現(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)容,因?yàn)樯暇W(wǎng)有很講這方面知識講解。如有不了解的請自行百度、google。

注意:

不同的開發(fā)板操作的串口路徑是不同的,比如我的開發(fā)板上的串口路徑是“/dev/ttyS5”;還有就是數(shù)據(jù)傳輸?shù)牟ㄌ芈室O(shè)置成一致,不然收到的數(shù)據(jù)會亂碼。??

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,786評論 6 534
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,656評論 3 419
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,697評論 0 379
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,098評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,855評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,254評論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,322評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,473評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,014評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,833評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,016評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,568評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,273評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,680評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,946評論 1 288
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,730評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,006評論 2 374

推薦閱讀更多精彩內(nèi)容

  • _ 聲明: 對原文格式以及內(nèi)容做了細(xì)微的修改和美化, 主要為了方便閱讀和理解 _ 一. 基礎(chǔ) Java Nativ...
    元亨利貞o閱讀 5,989評論 0 34
  • 1 進(jìn)程啟動過程 Android應(yīng)用程序框架層創(chuàng)建的應(yīng)用程序進(jìn)程具有兩個特點(diǎn),一是進(jìn)程的入口函數(shù)是Activit...
    Kevin_Junbaozi閱讀 3,874評論 0 23
  • 在不久前,接觸到了物聯(lián)網(wǎng)的開發(fā),當(dāng)時一臉懵逼了,后來問了度娘和有幸遇到大神的指導(dǎo),最終實(shí)現(xiàn)了功能。 首先先弄懂跟硬...
    Orz013閱讀 4,501評論 0 2
  • 一. Java基礎(chǔ)部分.................................................
    wy_sure閱讀 3,828評論 0 11
  • 先說我的應(yīng)用場景,一塊帶串口的android板子跟一個外設(shè)通過串口直接通信,控制這個外設(shè)的啟停。如圖1(公司規(guī)定不...
    Q1an閱讀 7,296評論 0 11