網站首頁 編程語言 正文
1.什么是串口?
在不會使用串口通訊之前,暫且可以把它理解為“一個可通訊的口”;使用篇不深入探討理論及原理。能理解串口如何使用之后,可以查看淺談Android串口通訊SerialPort原理
2.添加依賴
1.)在 module 中的 build.gradle 中的 dependencies 中添加以下依賴:
dependencies {
//串口
implementation 'com.github.licheedev:Android-SerialPort-API:2.0.0'
}
2.)低版本的 gradle?在Project 中的 build.gradle 中的 allprojects 中添加以下 maven倉庫 (不添加任然無法加載SerialPort);
allprojects {
repositories {
maven { url "https://jitpack.io" }//maven倉庫
}
}
高版本的 gradle 已經廢棄了 allprojects 在 settings.gradle 中 repositories 添加以下maven倉庫(不添加任然無法加載SerialPort);
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
jcenter() // Warning: this repository is going to shut down soon
maven { url "https://jitpack.io" }//maven倉庫
}
}
3.編寫串口處理類
1.)串口處理類:SerialHandle ;簡單概括這個類,就是通過串口對象去獲取兩個流(輸入流、輸出流),通過者兩個流來監聽數據或者寫入指令,硬件收到后執行。同時注意配置參數(只要支持串口通訊的硬件,一般說明書上都會有寫)
package com.chj233.serialmode.serialUtil;
import android.serialport.SerialPort;
import android.util.Log;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
/**
* 串口實處理類
*/
public class SerialHandle implements Runnable {
private static final String TAG = "串口處理類";
private String path = "";//串口地址
private SerialPort mSerialPort;//串口對象
private InputStream mInputStream;//串口的輸入流對象
private BufferedInputStream mBuffInputStream;//用于監聽硬件返回的信息
private OutputStream mOutputStream;//串口的輸出流對象 用于發送指令
private SerialInter serialInter;//串口回調接口
private ScheduledFuture readTask;//串口讀取任務
/**
* 添加串口回調
*
* @param serialInter
*/
public void addSerialInter(SerialInter serialInter) {
this.serialInter = serialInter;
}
/**
* 打開串口
*
* @param devicePath 串口地址(根據平板的說明說填寫)
* @param baudrate 波特率(根據對接的硬件填寫 - 硬件說明書上"通訊"中會有標注)
* @param isRead 是否持續監聽串口返回的數據
* @return 是否打開成功
*/
public boolean open(String devicePath, int baudrate, boolean isRead) {
return open(devicePath, baudrate, 7, 1, 2, isRead);
}
/**
* 打開串口
*
* @param devicePath 串口地址(根據平板的說明說填寫)
* @param baudrate 波特率(根據對接的硬件填寫 - 硬件說明書上"通訊"中會有標注)
* @param dataBits 數據位(根據對接的硬件填寫 - 硬件說明書上"通訊"中會有標注)
* @param stopBits 停止位(根據對接的硬件填寫 - 硬件說明書上"通訊"中會有標注)
* @param parity 校驗位(根據對接的硬件填寫 - 硬件說明書上"通訊"中會有標注)
* @param isRead 是否持續監聽串口返回的數據
* @return 是否打開成功
*/
public boolean open(String devicePath, int baudrate, int dataBits, int stopBits, int parity, boolean isRead) {
boolean isSucc = false;
try {
if (mSerialPort != null) close();
File device = new File(devicePath);
mSerialPort = SerialPort // 串口對象
.newBuilder(device, baudrate) // 串口地址地址,波特率
.dataBits(dataBits) // 數據位,默認8;可選值為5~8
.stopBits(stopBits) // 停止位,默認1;1:1位停止位;2:2位停止位
.parity(parity) // 校驗位;0:無校驗位(NONE,默認);1:奇校驗位(ODD);2:偶校驗位(EVEN)
.build(); // 打開串口并返回
mInputStream = mSerialPort.getInputStream();
mBuffInputStream = new BufferedInputStream(mInputStream);
mOutputStream = mSerialPort.getOutputStream();
isSucc = true;
path = devicePath;
if (isRead) readData();//開啟識別
} catch (Throwable tr) {
close();
isSucc = false;
} finally {
return isSucc;
}
}
// 讀取數據
private void readData() {
if (readTask != null) {
readTask.cancel(true);
try {
Thread.sleep(160);
} catch (InterruptedException e) {
e.printStackTrace();
}
//此處睡眠:當取消任務時 線程池已經執行任務,無法取消,所以等待線程池的任務執行完畢
readTask = null;
}
readTask = SerialManage
.getInstance()
.getScheduledExecutor()//獲取線程池
.scheduleAtFixedRate(this, 0, 150, TimeUnit.MILLISECONDS);//執行一個循環任務
}
@Override//每隔 150 毫秒會觸發一次run
public void run() {
if (Thread.currentThread().isInterrupted()) return;
try {
int available = mBuffInputStream.available();
if (available == 0) return;
byte[] received = new byte[1024];
int size = mBuffInputStream.read(received);//讀取以下串口是否有新的數據
if (size > 0 && serialInter != null) serialInter.readData(path, received, size);
} catch (IOException e) {
Log.e(TAG, "串口讀取數據異常:" + e.toString());
}
}
/**
* 關閉串口
*/
public void close(){
try{
if (mInputStream != null) mInputStream.close();
}catch (Exception e){
Log.e(TAG,"串口輸入流對象關閉異常:" +e.toString());
}
try{
if (mOutputStream != null) mOutputStream.close();
}catch (Exception e){
Log.e(TAG,"串口輸出流對象關閉異常:" +e.toString());
}
try{
if (mSerialPort != null) mSerialPort.close();
mSerialPort = null;
}catch (Exception e){
Log.e(TAG,"串口對象關閉異常:" +e.toString());
}
}
/**
* 向串口發送指令
*/
public void send(final String msg) {
byte[] bytes = hexStr2bytes(msg);//字符轉成byte數組
try {
mOutputStream.write(bytes);//通過輸出流寫入數據
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 把十六進制表示的字節數組字符串,轉換成十六進制字節數組
*
* @param
* @return byte[]
*/
private byte[] hexStr2bytes(String hex) {
int len = (hex.length() / 2);
byte[] result = new byte[len];
char[] achar = hex.toUpperCase().toCharArray();
for (int i = 0; i < len; i++) {
int pos = i * 2;
result[i] = (byte) (hexChar2byte(achar[pos]) << 4 | hexChar2byte(achar[pos + 1]));
}
return result;
}
/**
* 把16進制字符[0123456789abcde](含大小寫)轉成字節
* @param c
* @return
*/
private static int hexChar2byte(char c) {
switch (c) {
case '0':
return 0;
case '1':
return 1;
case '2':
return 2;
case '3':
return 3;
case '4':
return 4;
case '5':
return 5;
case '6':
return 6;
case '7':
return 7;
case '8':
return 8;
case '9':
return 9;
case 'a':
case 'A':
return 10;
case 'b':
case 'B':
return 11;
case 'c':
case 'C':
return 12;
case 'd':
case 'D':
return 13;
case 'e':
case 'E':
return 14;
case 'f':
case 'F':
return 15;
default:
return -1;
}
}
2.)串口回調SerialInter;簡單概括一下這個類,就是將SerialHandle類中產生的結果,返回給上一層的業務代碼,解偶合
package com.chj233.serialmode.serialUtil;
/**
* 串口回調
*/
public interface SerialInter {
/**
* 連接結果回調
* @param path 串口地址(當有多個串口需要統一處理時,可以用地址來區分)
* @param isSucc 連接是否成功
*/
void connectMsg(String path,boolean isSucc);
/**
* 讀取到的數據回調
* @param path 串口地址(當有多個串口需要統一處理時,可以用地址來區分)
* @param bytes 讀取到的數據
* @param size 數據長度
*/
void readData(String path,byte[] bytes,int size);
}
?3.)串口統一管理SerialManage;簡單概括一下這個類,用于管理串口的連接以及發送等功能,尤其是發送指令,極短時間內發送多個指令(例如:1毫秒內發送10個指令),多個指令之間會相互干擾。可能執行了第一個指令,可能一個都沒執行。這個類不是必須的,如果有更好的方法可以自己定義。
package com.chj233.serialmode.serialUtil;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
/**
* 串口管理類
*/
public class SerialManage {
private static SerialManage instance;
private ScheduledExecutorService scheduledExecutor;//線程池 同一管理保證只有一個
private SerialHandle serialHandle;//串口連接 發送 讀取處理對象
private Queue<String> queueMsg = new ConcurrentLinkedQueue<String>();//線程安全到隊列
private ScheduledFuture sendStrTask;//循環發送任務
private boolean isConnect = false;//串口是否連接
private SerialManage() {
scheduledExecutor = Executors.newScheduledThreadPool(8);//初始化8個線程
}
public static SerialManage getInstance() {
if (instance == null) {
synchronized (SerialManage.class) {
if (instance == null) {
instance = new SerialManage();
}
}
}
return instance;
}
/**
* 獲取線程池
*
* @return
*/
public ScheduledExecutorService getScheduledExecutor() {
return scheduledExecutor;
}
/**
* 串口初始化
*
* @param serialInter
*/
public void init(SerialInter serialInter) {
if (serialHandle == null) {
serialHandle = new SerialHandle();
startSendTask();
}
serialHandle.addSerialInter(serialInter);
}
/**
* 打開串口
*/
public void open() {
isConnect = serialHandle.open("/dev/ttyS1", 9600, true);//設置地址,波特率,開啟讀取串口數據
}
/**
* 發送指令
*
* @param msg
*/
public void send(String msg) {
/*
此處沒有直接使用 serialHandle.send(msg); 方法去發送指令
因為 某些硬件在極短時間內只能響應一個指令,232通訊一次發送多個指令會有物理干擾,
讓硬件接收到指令不準確;所以 此處將指令添加到隊列中,排隊執行,確保每個指令一定執行.
若不相信可以試試用serialHandle.send(msg)方法循環發送10個不同的指令,看看10個指令
的執行結果。
*/
queueMsg.offer(msg);//向隊列添加指令
}
/**
* 關閉串口
*/
public void colse() {
serialHandle.close();//關閉串口
}
//啟動發送發送任務
private void startSendTask() {
cancelSendTask();//先檢查是否已經啟動了任務 ? 若有則取消
//每隔100毫秒檢查一次 隊列中是否有新的指令需要執行
sendStrTask = scheduledExecutor.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
if (!isConnect) return;//串口未連接 退出
if (serialHandle == null) return;//串口未初始化 退出
String msg = queueMsg.poll();//取出指令
if (msg == null || "".equals(msg)) return;//無效指令 退出
serialHandle.send(msg);//發送指令
}
}, 0, 100, TimeUnit.MILLISECONDS);
}
//取消發送任務
private void cancelSendTask() {
if (sendStrTask == null) return;
sendStrTask.cancel(true);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
sendStrTask = null;
}
}
4.使用串口
package com.chj233.serialmode;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import com.chj233.serialmode.serialUtil.SerialInter;
import com.chj233.serialmode.serialUtil.SerialManage;
public class MainActivity extends AppCompatActivity implements SerialInter {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SerialManage.getInstance().init(this);//串口初始化
SerialManage.getInstance().open();//打開串口
findViewById(R.id.send_but).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
SerialManage.getInstance().send("Z");//發送指令 Z
}
});
}
@Override
public void connectMsg(String path, boolean isSucc) {
String msg = isSucc ? "成功" : "失敗";
Log.e("串口連接回調", "串口 "+ path + " -連接" + msg);
}
@Override//若在串口開啟的方法中 傳入false 此處不會返回數據
public void readData(String path, byte[] bytes, int size) {
// Log.e("串口數據回調","串口 "+ path + " -獲取數據" + bytes);
}
}
5.總結
串口通訊對于Android開發者來說,僅需關注如何連接、操作(發送指令)、讀取數據;無論是232、485還是422,對于開發者來說連接、操作、讀取代碼都是一樣的
原文鏈接:https://blog.csdn.net/qq_42111674/article/details/123653870
相關推薦
- 2022-09-17 python?pandas處理excel表格數據的常用方法總結_python
- 2023-04-08 Swift?HTTP加載請求Loading?Requests教程_Swift
- 2022-11-23 Python?threading模塊中lock與Rlock的使用詳細講解_python
- 2022-07-18 RLS遞歸最小二乘法(Recursive Least Squares)
- 2023-12-11 Mybatis對于多對一和一對多的處理
- 2022-05-15 Python?matplotlib?seaborn繪圖教程詳解_python
- 2022-12-26 flouting?ui定位組件完美替代ant?deisgn使用詳解_React
- 2022-09-14 關于Python文本生成的Beam?Search解碼問題_python
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支