Android串口通信

1. 解析SerialPort API 串口通信例子  

         首先分析一下例子中的類結構 :

         spacer.gifwKiom1bdPUzCcOzkAABUaH1taQI037.png

     通過類結構可知,最主要的還是在SerialPortJNI.java 類 ,該類寫了一些Native 方法處理打開與關閉 串口 接收 發送的 

     SerialPort.Java 代碼如下 :


package com.dwin.navy.serialportapi;


import java.io.FileDescriptor;


import android.util.Log;


/**

 * 串口JNI

 * 

 * @author dwin

 * 

 */

public class SerialPortJNI {


    static {

        Log.i("NativeClass", "before load library");

        System.loadLibrary("serialport");

        Log.i("NativeClass", "after load library");

    }


    public FileDescriptor mFd;


    public String mDevNum;

    public int mSpeed;

    public int mDataBits;

    public int mStopBits;

    public int mParity;


    public int RS485ModFp = -1;


    public static int RS485Read = 0;

    public static int RS485Write = 1;


    public native int setSpeed(FileDescriptor fd, int speed);


    public native int setParity(FileDescriptor fd, int dataBits, int stopBits,

            int parity);


    public native FileDescriptor openDev(String devNum);


    public native FileDescriptor open485Dev(String devNum);


    public native int closeDev(FileDescriptor fd);


    public native int close485Dev(FileDescriptor fd);


    public native int readBytes(FileDescriptor fd, byte[] buffer, int length);


    public native boolean writeBytes(FileDescriptor fd, byte[] buffer,

            int length);


    public native int set485mod(int mode);

  紅色區域 先申明一個static 靜態域 加載.so 動態庫文件 .so通過JNI方式生成 保存在libs目錄下 ,由於本例子使用的cpu爲 mips(默認爲arm) 所以 .so庫文件 將存入mips文件夾下

  還有打開串口,關閉串口,485模式下開關方式 ,接收 與發送 byte 


        1> SerialPort API 處理了接收與發送 文本與十六進制 兩種模式下接收與發送數據 

          UI佈局如下 :

        spacer.gifwKiom1bdPWKzRG9PAABeP7AoSao094.png

  

         2>在電腦中插入usb轉串口工具 ,然後在app中打開串口 

    

      SerialPortOpt.java 繼承了 SerialPortJNI.Java 類 調用相應的JNI接口 

 

      SerialPortOpt.java  類大致如下 :


     package com.dwin.navy.serialportapi;


import java.io.FileDescriptor;

import java.io.FileInputStream;

import java.io.FileOutputStream;

import java.io.InputStream;

import java.io.OutputStream;


import android.util.Log;


/**

 * 調用JNI的串口

 * 

 * @author dwin

 * 

 */

public class SerailPortOpt extends SerialPortJNI {


    private static final String TAG = "SerialPort";


    private FileInputStream mFileInputStream;

    private FileOutputStream mFileOutputStream;


    public FileDescriptor openDev(String devNum) {

        super.mFd = super.openDev(devNum);

        if (super.mFd == null) {

            Log.e(TAG, "native open returns null");

            return null;

        }

        mFileInputStream = new FileInputStream(super.mFd);

        mFileOutputStream = new FileOutputStream(super.mFd);

        return super.mFd;

    }


    public FileDescriptor open485Dev(String devNum) {

        super.mFd = super.open485Dev(devNum);

        if (super.mFd == null) {

            Log.e(TAG, "native open returns null");

            return null;

        }

        mFileInputStream = new FileInputStream(super.mFd);

        mFileOutputStream = new FileOutputStream(super.mFd);

        return super.mFd;

    }


    public InputStream getInputStream() {

        return mFileInputStream;

    }


    public OutputStream getOutputStream() {

        return mFileOutputStream;

    }


    public int setSpeed(FileDescriptor optFd, int speed) {

        return super.setSpeed(optFd, speed);

    }


    public int setParity(FileDescriptor optFd, int databits, int stopbits,

            int parity) {

        return super.setParity(optFd, databits, stopbits, parity);

    }


    public int closeDev(FileDescriptor optFd) {

        int retStatus;

        retStatus = super.closeDev(optFd);

        super.mFd = null;

        return retStatus;

    }


    public int close485Dev(FileDescriptor optFd) {

        int retStatus;

        retStatus = super.close485Dev(optFd);

        super.mFd = null;

        return retStatus;

    }


    public int readBytes(FileDescriptor fd, byte[] buffer, int length) {

        return super.readBytes(fd, buffer, length);

    }


    public boolean writeBytes(FileDescriptor fd, byte[] buffer, int length) {

        return super.writeBytes(fd, buffer, length);

    }


    public int readBytes(FileDescriptor fd, byte[] buffer) {

        return super.readBytes(fd, buffer, buffer.length);

    }


    public boolean writeBytes(FileDescriptor fd, byte[] buffer) {

        return super.writeBytes(fd, buffer, buffer.length);

    }


    public int readBytes(byte[] buffer) {

        return super.readBytes(mFd, buffer, buffer.length);

    }


    public boolean writeBytes(byte[] buffer) {

        return super.writeBytes(mFd, buffer, buffer.length);

    }


    public int read485Bytes(FileDescriptor fd, byte[] buffer, int length) {

        return super.readBytes(fd, buffer, length);

    }


    public boolean write485Bytes(FileDescriptor fd, byte[] buffer, int length) {

        boolean ret;

        super.set485mod(RS485Write);

        ret = super.writeBytes(fd, buffer, length);

        super.set485mod(RS485Read);

        return ret;

    }


    public int read485Bytes(FileDescriptor fd, byte[] buffer) {

        return super.readBytes(fd, buffer, buffer.length);

    }


    public boolean write485Bytes(FileDescriptor fd, byte[] buffer) {

        boolean ret;

        super.set485mod(RS485Write);

        ret = super.writeBytes(fd, buffer, buffer.length);

        super.set485mod(RS485Read);

        return ret;

    }


    public int read485Bytes(byte[] buffer) {

        return super.readBytes(mFd, buffer, buffer.length);

    }


    public boolean write485Bytes(byte[] buffer) {

        boolean ret;

        super.set485mod(RS485Write);

        ret = super.writeBytes(mFd, buffer, buffer.length);

        super.set485mod(RS485Read);

        return ret;

    }


}


openDev(String str)方法中調用父類的方法 ,將返回一個文件描述對象FileDescriptor ,然後初始化 FileInputStream,FileOutputStream 輸入輸出IO流對象 ,

還有讀寫byte字節數組的方法 


 3>申明或者實例化一個類用來調用和實現 這些方法 前兩個類只是申明 相應的方法並沒有實現相應的方法體 

  定義一個SerialPort 類

  

SerialPort.Java :


package com.dwin.dwinapi;


import java.io.FileDescriptor;

import java.io.IOException;

import java.io.InputStream;

import java.io.UnsupportedEncodingException;


import com.dwin.navy.serialportapi.SerailPortOpt;


import android.content.Context;

import android.util.Log;


/**

 * 自定義串口對象

 * 

 * @author F

 * 

 */

public class SerialPort {

    Context context;


    /**

     * 自定義串口對象

     */

    private static SerialPort serialPort;


    /**

     * 調用JNI的串口

     */

    private SerailPortOpt serialportopt;


    /**

     * 讀取數據線程

     */


    /**

     * 串口接收到的流

     */

    private InputStream mInputStream;


    /**

     * 用來判斷串口是否已打開

     */

    public boolean isOpen = false;


    /*

     * 接收到的數據

     */

    String data;


    /**

     * 實例化並打開串口對象

     * 

     * @param devNum

     *            串口號 S0,S1,S2,S3,S4

     * @param dataBits

     *            數據位

     * @param speed

     *            波特率

     * @param stopBits

     *            停止位

     * @param parity

     *            校驗位

     */

    public SerialPort(String devNum, int speed, int dataBits, int stopBits,

            int parity) {

        serialportopt = new SerailPortOpt();

        openSerial(devNum, speed, dataBits, stopBits, parity);

    }


    /**

     * 打開串口時傳入參數,可以指定打開某個串口,並設置相應的參數

     * 

     * @param devNum

     *            串口號 COM0,COM1,COM2,COM3,COM4

     * @param dataBits

     *            數據位

     * @param speed

     *            波特率

     * @param stopBits

     *            停止位

     * @param parity

     *            校驗位

     * @return

     */

    private boolean openSerial(String devNum, int speed, int dataBits,

            int stopBits, int parity) {

        serialportopt.mDevNum = devNum;

        serialportopt.mDataBits = dataBits;

        serialportopt.mSpeed = speed;

        serialportopt.mStopBits = stopBits;

        serialportopt.mParity = parity;


        // 打開串口

        FileDescriptor fd = serialportopt.openDev(serialportopt.mDevNum);

        if (fd == null) {

            return false;// 串口打開失敗

        } else {

            // 設置串口參數

            serialportopt.setSpeed(fd, speed);

            serialportopt.setParity(fd, dataBits, stopBits, parity);

            mInputStream = serialportopt.getInputStream();

            isOpen = true;

            return true;

        }

    }


    /**

     * 關閉串口

     */

    public void closeSerial() {

        if (serialportopt.mFd != null) {

            serialportopt.closeDev(serialportopt.mFd);

            isOpen = false;

        }

    }


    /**

     * 發送數據

     * 

     * @param data

     *            數據內容

     */

    public void sendData(String data, String type) {

        try {

            serialportopt.writeBytes(type.equals("HEX") ? HexString2Bytes(data

                    .length() % 2 == 1 ? data += "0" : data.replace(" ", ""))

                    : HexString2Bytes(toHexString(data)));

        } catch (Exception e) {


        }

    }


    /**

     * 接收數據

     * 

     * @param 收發數據類型

     * @return 接收到的字符串

     */

    public String receiveData(String type) {

        byte[] buf = new byte[1024];

        int size;

        if (mInputStream == null) {

            return null;

        }

        size = serialportopt.readBytes(buf);

        if (size > 0) {

            try {

                data = type.equals("HEX") ? bytesToHexString(buf, size)

                        : new String(buf, 0, size, "gb2312").trim().toString();

            } catch (UnsupportedEncodingException e) {

                e.printStackTrace();

            }

            return data;

        } else {

            return null;

        }

    }


    /**

     * 轉化字符串爲十六進制編碼

     * 

     * @param s

     * @return

     */

    private String toHexString(String s) {

        String str = "";

        for (int i = 0; i < s.length(); i++) {

            int ch = (int) s.charAt(i);

            String s4 = Integer.toHexString(ch);

            str = str + s4;

        }

        return str;

    }


    /**

     * 將指定字符串src,以每兩個字符分割轉換爲16進制形式 如:"2B44EFD9" --> byte[]{0x2B, 0x44, 0xEF,

     * 0xD9}

     * 

     * @param src

     *            String

     * @return byte[]

     */

    private static byte[] HexString2Bytes(String src) {

        byte[] ret = new byte[src.length() / 2];

        byte[] tmp = src.getBytes();

        for (int i = 0; i < tmp.length / 2; i++) {

            ret[i] = uniteBytes(tmp[i * 2], tmp[i * 2 + 1]);

        }

        return ret;

    }


    /**

     * 將Hex數組轉換爲Hex字符串

     * 

     * @param src

     * @param size

     * @return

     */

    public static String bytesToHexString(byte[] src, int size) {

        String ret = "";

        if (src == null || size <= 0) {

            return null;

        }

        for (int i = 0; i < size; i++) {

            String hex = Integer.toHexString(src[i] & 0xFF);

            if (hex.length() < 2) {

                hex = "0" + hex;

            }

            hex += " ";

            ret += hex;

        }

        return ret.toUpperCase();

    }


    /**

     * 將兩個ASCII字符合成一個字節; 如:"EF"--> 0xEF

     * 

     * @param src0

     *            byte

     * @param src1

     *            byte

     * @return byte

     */

    private static byte uniteBytes(byte src0, byte src1) {

        byte _b0 = Byte.decode("0x" + new String(new byte[] { src0 }))

                .byteValue();

        _b0 = (byte) (_b0 << 4);

        byte _b1 = Byte.decode("0x" + new String(new byte[] { src1 }))

                .byteValue();

        byte ret = (byte) (_b0 ^ _b1);

        return ret;

    }


}


剩下的就是MainActivty中 ,打開串口 創建兩個線程,ReceiverThread,SendThread 先 ,Handler 異步更新UI

private Handler mHandler = new Handler() {

        public void handleMessage(android.os.Message msg) {

            switch (msg.what) {

            case 1:

                Date date = new Date();

                eTextShowMsg.append("[" + date.getMinutes() + ":"

                        + date.getSeconds() + "] " + (CharSequence) msg.obj);

                break;

            default:

                break;

            }

        };

    };


    /**

     * 接收數據線程

     */

    class ReceiveThread extends Thread {

        public void run() {


            while (serialPort.isOpen) {

                if (isReceive) {

                    String type = togBtnShowDataType.getText().toString()

                            .trim();

                    String data = serialPort.receiveData(type);

                    if (data != null) {

                        Message msg = new Message();

                        msg.what = 1;

                        msg.obj = data;

                        System.out.println(data + "<<<<<<<<==========data");

                        mHandler.sendMessage(msg);

                    }

                }

                try {

                    Thread.sleep(1000);

                } catch (InterruptedException e) {

                    e.printStackTrace();

                }

            }

        }

    }


當串口接收到數據 每一秒更新一次UI,根據需求可轉換爲十六進制與字符

          

   


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章