飞道的博客

安卓之串口通信以及封装

263人阅读  评论(0)

 

引言:

最近项目用到了串口通信功能,刚好写一篇文章来记录一下.

为什么学习Android串口通信:

  • 现在,对于很多公司而言,Android主板与各种传感器和智能设备之间通信是很常见的事情了,那么安卓开发中,串口通信就是必须学习的事情了!
  • Google出品,必属精品。Android串口通信的源代码是Google公司在2011年开源的Google官方源代码

集成串口通信:

一 、新建库项目导入so库

 

                                     

二 、 配置Gradle文件:基本没啥可配置的

前置工作就这么完成了....

Google串口代码分析之SerialPort类 


  
  1. package android_serialport_api;
  2. import android.util.Log;
  3. import java.io.File;
  4. import java.io.FileDescriptor;
  5. import java.io.FileInputStream;
  6. import java.io.FileOutputStream;
  7. import java.io.IOException;
  8. import java.io.InputStream;
  9. import java.io.OutputStream;
  10. /**
  11. * Google官方代码
  12. * 此类的作用为,JNI的调用,用来加载.so文件的
  13. * 获取串口输入输出流
  14. */
  15. public class SerialPort {
  16. private static final String TAG = "SerialPort";
  17. /*
  18. * Do not remove or rename the field mFd: it is used by native method
  19. * close();
  20. */
  21. private FileDescriptor mFd;
  22. private FileInputStream mFileInputStream;
  23. private FileOutputStream mFileOutputStream;
  24. public SerialPort(File device, int baudrate, int flags)
  25. throws SecurityException, IOException {
  26. /* Check access permission */
  27. if (!device.canRead() || !device.canWrite()) {
  28. try {
  29. /* Missing read/write permission, trying to chmod the file */
  30. Process su;
  31. su = Runtime.getRuntime().exec( "/system/bin/su");
  32. String cmd = "chmod 666 " + device.getAbsolutePath() + "\n"
  33. + "exit\n";
  34. su.getOutputStream().write(cmd.getBytes());
  35. if ((su.waitFor() != 0) || !device.canRead()
  36. || !device.canWrite()) {
  37. throw new SecurityException();
  38. }
  39. } catch (Exception e) {
  40. e.printStackTrace();
  41. throw new SecurityException();
  42. }
  43. }
  44. Log.v(TAG, ".\n********************************\nfile:" + device.getAbsolutePath() +
  45. "\nbaudrate:" + baudrate + "\nflags:" + flags + "\n********************************");
  46. mFd = open(device.getAbsolutePath(), baudrate, flags);
  47. if (mFd == null) {
  48. Log.e(TAG, "native open returns null");
  49. throw new IOException();
  50. }
  51. Log.v(TAG, "open:" + mFd);
  52. mFileInputStream = new FileInputStream(mFd);
  53. mFileOutputStream = new FileOutputStream(mFd);
  54. }
  55. // Getters and setters
  56. public InputStream getInputStream() {
  57. return mFileInputStream;
  58. }
  59. public OutputStream getOutputStream() {
  60. return mFileOutputStream;
  61. }
  62. // JNI
  63. private native static FileDescriptor open(String path, int baudrate,
  64. int flags);
  65. public native void close();
  66. static {
  67. System.out.println( "==============================");
  68. System.loadLibrary( "serial_port");
  69. System.out.println( "********************************");
  70. }
  71. }

一:类作用及介绍

通过打开JNI的调用,打开串口。获取串口通信中的输入输出流,通过操作IO流,达到能够利用串口接收数据和发送数据的目的

二:类方法介绍

A : 开启串口的方法:private native static FileDescriptor open(String path, int baudrate,int flags)

  • path:为串口的物理地址,一般硬件工程师都会告诉你的例如ttyS0、ttyS1等,或者通过SerialPortFinder类去寻找得到可用的串口地址。
  • baudrate:波特率,与外接设备一致
  • flags:设置为0,原因较复杂,有兴趣的可以自己去研究一下

B : IO流

获取了输入输出流以后就可以对流进行操作了。


  
  1. package com.seriallibrary.myserialport;
  2. import android.util.Log;
  3. import android_serialport_api.SerialPort;
  4. import java.io.File;
  5. import java.io.IOException;
  6. import java.io.InputStream;
  7. import java.io.OutputStream;
  8. /**
  9. * @author by AllenJ on 2018/4/20.
  10. * <p>
  11. * 通过串口用于接收或发送数据
  12. */
  13. public class SerialPortUtil {
  14. private SerialPort serialPort = null;
  15. private InputStream inputStream = null;
  16. private OutputStream outputStream = null;
  17. private ReceiveThread mReceiveThread = null;
  18. private boolean isStart = false;
  19. private SerialListen mSerialListen;
  20. /**
  21. * 打开串口,接收数据
  22. * 通过串口,接收单片机发送来的数据
  23. */
  24. public void openSerialPort(String path, int baudrate, int flags) {
  25. try {
  26. serialPort = new SerialPort( new File(path), baudrate, flags);
  27. //调用对象SerialPort方法,获取串口中"读和写"的数据流
  28. inputStream = serialPort.getInputStream();
  29. outputStream = serialPort.getOutputStream();
  30. isStart = true;
  31. } catch (IOException e) {
  32. e.printStackTrace();
  33. }
  34. getSerialPort();
  35. }
  36. public void setSerialListen(SerialListen vSerialListen) {
  37. mSerialListen = vSerialListen;
  38. }
  39. /**
  40. * 关闭串口
  41. * 关闭串口中的输入输出流
  42. */
  43. public void closeSerialPort() {
  44. Log.i( "test", "关闭串口");
  45. try {
  46. if (inputStream != null) {
  47. inputStream.close();
  48. }
  49. if (outputStream != null) {
  50. outputStream.close();
  51. }
  52. isStart = false;
  53. } catch (IOException e) {
  54. e.printStackTrace();
  55. }
  56. }
  57. /**
  58. * 发送数据
  59. * 通过串口,发送数据到单片机
  60. *
  61. * @param data 要发送的数据
  62. */
  63. public void sendSerialPort(String data) {
  64. try {
  65. byte[] sendData = DataUtils.HexToByteArr(data);
  66. outputStream.write(sendData);
  67. outputStream.flush();
  68. } catch (IOException e) {
  69. e.printStackTrace();
  70. }
  71. }
  72. private void getSerialPort() {
  73. if (mReceiveThread == null) {
  74. mReceiveThread = new ReceiveThread();
  75. }
  76. try {
  77. mReceiveThread.start();
  78. } catch (Exception e) {
  79. e.printStackTrace();
  80. }
  81. }
  82. /**
  83. * 接收串口数据的线程
  84. */
  85. private class ReceiveThread extends Thread {
  86. @Override
  87. public void run() {
  88. super.run();
  89. //条件判断,只要条件为true,则一直执行这个线程
  90. while (isStart) {
  91. if (inputStream == null) {
  92. return;
  93. }
  94. byte[] readData = new byte[ 1024];
  95. try {
  96. int size = inputStream.read(readData);
  97. if (size > 0) {
  98. String readString = DataUtils.ByteArrToHex(readData, 0, size);
  99. if (mSerialListen != null) {
  100. mSerialListen.getSerial(readData, readString);
  101. }
  102. }
  103. } catch (IOException e) {
  104. e.printStackTrace();
  105. }
  106. }
  107. }
  108. }
  109. }

 SerialListen


  
  1. package com.seriallibrary.myserialport;
  2. public interface SerialListen {
  3. void getSerial(byte[] bytes,String string);
  4. }

 串口数据转换工具类  DataUtils 


  
  1. package com.seriallibrary.myserialport;
  2. import java.util.ArrayList;
  3. import java.util. List;
  4. /**
  5. * 串口数据转换工具类
  6. * Created by Administrator on 2016/6/2.
  7. */
  8. public class DataUtils {
  9. //-------------------------------------------------------
  10. // 判断奇数或偶数,位运算,最后一位是1则为奇数,为0是偶数
  11. public static int isOdd( int num) {
  12. return num & 1;
  13. }
  14. //-------------------------------------------------------
  15. //Hex字符串转int
  16. public static int HexToInt( String inHex) {
  17. return Integer.parseInt(inHex, 16);
  18. }
  19. public static String IntToHex( int intHex){
  20. return Integer.toHexString(intHex);
  21. }
  22. //-------------------------------------------------------
  23. //Hex字符串转byte
  24. public static byte HexToByte( String inHex) {
  25. return (byte) Integer.parseInt(inHex, 16);
  26. }
  27. //-------------------------------------------------------
  28. //1字节转2个Hex字符
  29. public static String Byte2Hex(Byte inByte) {
  30. return String.format( "%02x", new Object[]{inByte}).toUpperCase();
  31. }
  32. //-------------------------------------------------------
  33. //字节数组转转hex字符串
  34. public static String ByteArrToHex(byte[] inBytArr) {
  35. StringBuilder strBuilder = new StringBuilder();
  36. for (byte valueOf : inBytArr) {
  37. strBuilder.append(Byte2Hex(Byte.valueOf(valueOf)));
  38. strBuilder.append( " ");
  39. }
  40. return strBuilder.toString();
  41. }
  42. //-------------------------------------------------------
  43. //字节数组转转hex字符串,可选长度
  44. public static String ByteArrToHex(byte[] inBytArr, int offset, int byteCount) {
  45. StringBuilder strBuilder = new StringBuilder();
  46. int j = byteCount;
  47. for ( int i = offset; i < j; i++) {
  48. strBuilder.append(Byte2Hex(Byte.valueOf(inBytArr[i])));
  49. }
  50. return strBuilder.toString();
  51. }
  52. //-------------------------------------------------------
  53. //转hex字符串转字节数组
  54. public static byte[] HexToByteArr( String inHex) {
  55. byte[] result;
  56. int hexlen = inHex.length();
  57. if (isOdd(hexlen) == 1) {
  58. hexlen++;
  59. result = new byte[(hexlen / 2)];
  60. inHex = "0" + inHex;
  61. } else {
  62. result = new byte[(hexlen / 2)];
  63. }
  64. int j = 0;
  65. for ( int i = 0; i < hexlen; i += 2) {
  66. result[j] = HexToByte(inHex.substring(i, i + 2));
  67. j++;
  68. }
  69. return result;
  70. }
  71. /**
  72. * 按照指定长度切割字符串
  73. *
  74. * @param inputString 需要切割的源字符串
  75. * @param length 指定的长度
  76. * @return
  77. */
  78. public static List< String> getDivLines( String inputString, int length) {
  79. List< String> divList = new ArrayList<>();
  80. int remainder = (inputString.length()) % length;
  81. // 一共要分割成几段
  82. int number = ( int) Math.floor((inputString.length()) / length);
  83. for ( int index = 0; index < number; index++) {
  84. String childStr = inputString.substring(index * length, (index + 1) * length);
  85. divList.add(childStr);
  86. }
  87. if (remainder > 0) {
  88. String cStr = inputString.substring(number * length, inputString.length());
  89. divList.add(cStr);
  90. }
  91. return divList;
  92. }
  93. /**
  94. * 计算长度,两个字节长度
  95. *
  96. * @param val value
  97. * @return 结果
  98. */
  99. public static String twoByte( String val) {
  100. if (val.length() > 4) {
  101. val = val.substring( 0, 4);
  102. } else {
  103. int l = 4 - val.length();
  104. for ( int i = 0; i < l; i++) {
  105. val = "0" + val;
  106. }
  107. }
  108. return val;
  109. }
  110. /**
  111. * 校验和
  112. *
  113. * @param cmd 指令
  114. * @return 结果
  115. */
  116. public static String sum( String cmd) {
  117. List< String> cmdList = DataUtils.getDivLines(cmd, 2);
  118. int sumInt = 0;
  119. for ( String c : cmdList) {
  120. sumInt += DataUtils.HexToInt(c);
  121. }
  122. String sum = DataUtils.IntToHex(sumInt);
  123. sum = DataUtils.twoByte(sum);
  124. cmd += sum;
  125. return cmd.toUpperCase();
  126. }
  127. }

 主要的类就是这么几个,一个so加载类,一个串口操作工具类,一个数据解析工具类,一个回调接口,主要是用来封装串口的!

调用方式:  主要分为实例化工具类,打开串口,设置接口回调方法,在需要使用串口的时候发送数据,数据是String格式的,在SerialPortUtil里面会自动转化,只需要使用即可

整个代码我已经封装了一个aar包,可以导入直接使用,如果需要免费下载!

 

----------------------------------------------------------------------------------------------------

源代码Here:

----------------------------------------------------------------------------------------------------

 


转载:https://blog.csdn.net/qq_36333309/article/details/111310038
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场