上一篇:Android 串口通信筆記2 調(diào)試工具分析 工具類實現(xiàn)分析犀盟、項目實現(xiàn)
Android串口開發(fā) 延伸和擴展蛙粘,
1.使用JNI Cmake 自己編譯串口通信 的so庫:Android Studio 3.0 實現(xiàn)方式。
2.CRC校驗 以及擴展設(shè)計:
a.一(串口)對多(硬件通信);
b.多(串口)對多(硬件)的實現(xiàn)赠橙。
1.以串口調(diào)試工具為例婶希,使用其原本的源代碼使用JNI Cmake Android Studio 3.0 實現(xiàn)方式孔轴。
勾選 include C++ support 沒有下載ndk 的要下載。
①.延續(xù)使用jni 的方式
把相關(guān)的 been 和實現(xiàn)方法 都復(fù)制過來如圖唇礁。
創(chuàng)建.h 文件 注:一定要現(xiàn)進(jìn)入到app/main/java/ 目錄下
然后 javah -classpath -jni +完整路徑到類名
在main目錄下創(chuàng)建jni 文件夾勾栗,把生成的.h 文件復(fù)制進(jìn)去 ,新建同名的.c文件盏筐,把實現(xiàn)代碼拷進(jìn)去--注意需要修改 open 和close方法的名字 和.h 文件里改為一致围俘。
這是.h 文件的
修改 cmakelist.txt 中 add_library 的so文件名 和路徑
add_library( # Sets the name of the library.
# 設(shè)置so文件名稱.
serial_port
# Sets the library as a shared library.
SHARED
# 設(shè)置這個so文件為共享.
# Provides a relative path to your source file(s).
# 設(shè)置這個so文件為共享.
src/main/jni/com_silencefun_comtest_serialport_SerialPort.c)
// .......省略注釋部分
target_link_libraries( # Specifies the target library.
# 制定目標(biāo)庫.
serial_port
# Links the target library to the log library
# included in the NDK.
${log-lib} )
注: “serial_port” 這個 so庫名稱要和你要加載的要保持一致
在SerialPortJava類中,
在app的build.gradle中 defaultConfig中配置生成平臺so包
defaultConfig {
applicationId "com.silencefun.comtest"
minSdkVersion 19
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
cppFlags ""
//生成多個版本的so文件
abiFilters 'arm64-v8a','armeabi-v7a','x86','x86_64'
}
}
}
然后 同步琢融,build-make project 執(zhí)行完畢之后切換視圖
如圖已經(jīng)生成了so:
②直接使用cmake方式 不得不說新支持方式的簡單了好多楷拳。
創(chuàng)建完支持C的項目后,
在需要調(diào)用java 的Native 類中聲明方法吏奸,其實還是直接復(fù)制SerialPort把加載的so 文件名字改一下:
直接在自動生成的Native-lib.c中完善實現(xiàn) (其他都不用改欢揖,方便快捷)
把 原來.c文件中的實現(xiàn)方法 直接拷過去然后修改方法名:
注意 要對應(yīng)好路徑
出現(xiàn)問題反思:采
用jni方式在Android 5.1的板子沒有問題 換到4.4結(jié)果就不行 總是 LOGE("tcgetattr() failed");
后來 更新ndk 、cmake奋蔚、 LLDB到最新她混,完全解決問題。
github 地址歡迎 star?
https://github.com/silencefun/ComTest/tree/master/AndroidStuido_3.0_COMTEST
2.擴展設(shè)計:一(串口)對多(硬件通信)泊碑、多(串口)對多(硬件)的實現(xiàn)坤按。
因為 有些智能終端硬件限制有些可能只開放一個通信口來接多個硬件模塊數(shù)據(jù),當(dāng)然前提是 掛在這一個通信口上的 硬件模塊 是在同一波特率馒过,因為打開初始化的時候已經(jīng)設(shè)定好了波特率(嘗試動態(tài)更改臭脓,不太理解底層實現(xiàn)過程代碼,屢敗屢試腹忽,最終放棄)来累。
相似的某些硬件模塊要采集的數(shù)據(jù)可能是多條數(shù)據(jù)(多個命令下返回多條數(shù)據(jù) 最后在封裝)所以設(shè)定一個【命令組】的概念:把硬件模塊對應(yīng)的命令放進(jìn)一個數(shù)組或者list中砚作。
命令組,即 該硬件所需要的數(shù)據(jù)是需要連續(xù)發(fā)送一組 若干個命令嘹锁,根據(jù)接收到的多條數(shù)據(jù)來解析--- 此處只是解析方式不同葫录,可以根據(jù)每次傳遞標(biāo)志位來區(qū)分,待所有所需所有數(shù)值都有领猾,即執(zhí)行一輪次之后 完全解析再更新數(shù)據(jù)米同。
同理多串口情況下就是多個通信串口,每一個口上邊都掛了N(N>=1)個硬件模塊(當(dāng)然這么殘暴的情形是有的:比如波特率不同必須多個串口)摔竿。
在之前一篇筆記中 Android 串口通信筆記2 的SerialHelper面粮,即控制類---每個硬件串口對象的管理控制實例 中添加 相應(yīng)的 成員變量 來區(qū)分 目前 具體是哪一個 硬件模塊 的哪一個命令 響應(yīng)的 值。
所以對應(yīng)的 封裝 讀取到的 信息 也要添加上 當(dāng)前 對應(yīng)的 命令 和硬件 模塊標(biāo)志继低。
Combean 添加字段
private String scmd = "";
private String sflag = "";
對應(yīng)的 硬件模塊 數(shù)據(jù)結(jié)構(gòu)大概可以:
MeterInfo
private String name;//名稱
private String modenname;//型號
private String modertype;//類型
private List<String> cmdlist;//發(fā)送命令
private String cleandatacmd;//清除命令
private String decimal;//小數(shù)位置 (可能解析能用到)
private String portname;//port路徑 類似 /dev/ttys1
private String sFlag;//flag 可取 模塊name
SerialHelper 類要在原有基礎(chǔ)之上 添加 部分字段
....
private String scmd = "";
private String sflag = "";
private List<MeterInfo> meterlist = new ArrayList<>(); //一個串口肯能要和多個 硬件通信
.....
所以初始化串口控制類SerialHelper的實例時候但金,要先set List<MeterInfo> ,
在send 命令線程中郁季,兩層循環(huán):
for (int i = 0; i < meterlist .size(); i++) {
List<String> cmdlist = meterlist .get(i).getGetdatacmdlist();
for (int j = 0; j < cmdlist.size(); j++) {
setHexLoopData(cmdlist.get(j));
setSflag(meterlist .get(i).getsFlag());
setScmd(cmdlist.get(j));
//設(shè)定兩次的時間間隔
setTimespace(meterlist .get(i));
send(getbLoopData());//發(fā)送命令
try {
Thread.sleep(iDelay);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
對應(yīng)的在 發(fā)送線程發(fā)送后 發(fā)送線程sleep 的時候 冷溃,讀的線程一直在跑,讀取到數(shù)據(jù) 封裝Combean 對象:
int size = mInputStream.read(buffer);
if (size > 0) {
ComBean ComRecData = new ComBean(sPort, buffer, size);
ComRecData.setScmd(scmd);
ComRecData.setSflag(sflag);
onDataReceived(ComRecData); //調(diào)用抽象方法傳遞
}
這樣在業(yè)務(wù)處理部分實現(xiàn) onDataReceived 方法時候就能判斷是哪個模塊哪個命令對應(yīng)的值梦裂。
關(guān)于CRC校驗:
/**
* 獲取 crc校驗碼
*
* @param hextext 16進(jìn)制
* @return 低位高位順序
*/
public static String getCrc16(String hextext) {
byte[] arr_buff = SerialFunc.HexToByteArr(hextext);
int len = arr_buff.length;
// 預(yù)置 1 個 16 位的寄存器為十六進(jìn)制FFFF, 稱此寄存器為 CRC寄存器似枕。
int crc = 0xFFFF;
int i, j;
for (i = 0; i < len; i++) {
// 把第一個 8 位二進(jìn)制數(shù)據(jù) 與 16 位的 CRC寄存器的低 8 位相異或, 把結(jié)果放于 CRC寄存器
crc = ((crc & 0xFF00) | (crc & 0x00FF) ^ (arr_buff[i] & 0xFF));
for (j = 0; j < 8; j++) {
// 把 CRC 寄存器的內(nèi)容右移一位( 朝低位)用 0 填補最高位, 并檢查右移后的移出位
if ((crc & 0x0001) > 0) {
// 如果移出位為 1, CRC寄存器與多項式A001進(jìn)行異或
crc = crc >> 1;
crc = crc ^ 0xA001;
} else
// 如果移出位為 0,再次右移一位
crc = crc >> 1;
}
}
String c = Integer.toHexString(crc);
if (c.length() == 4) {
c = c.substring(2, 4) + c.substring(0, 2);
} else if (c.length() == 3) {
c = "0" + c;
c = c.substring(2, 4) + c.substring(0, 2);
} else if (c.length() == 2) {
c = "0" + c.substring(1, 2) + "0" + c.substring(0, 1);
}
return c.toUpperCase();
}
Android 串口通信筆記2 調(diào)試工具分析 工具類實現(xiàn)分析、項目實現(xiàn)
Android 串口通信開發(fā)筆記3:CMake 方式實現(xiàn)和 多對多的實現(xiàn)邏輯
Android 串口開發(fā) 支持N-8-1(數(shù)據(jù)位停止位校驗方式) 設(shè)定