美文网首页
Android串口编程

Android串口编程

作者: Mr_Fly | 来源:发表于2021-03-08 15:58 被阅读0次

    前言

    最近在做手机跟外设交互,因为之前没有涉猎过这方面,做起来真的是头大。幸好有万能的百度和无所不能的google,以及程序员的小帮手github,多方查询资料,咨询同事,以及万能的群友帮助,终于顺利实现了第一款串口编程的App。不得不说现在的手机越来越强大,都可以通过USB接口,直接读取其它外设的数据了。写这篇博客一是为了记录一下这次开发的经验,二是给后来的同学提供一些经验。

    基本常识

    串口通信:指串口按位(bit)发送和接收字节。尽管比按字节(byte)的并行通信慢,但是串口可以使用一根线发送数据的同时接收数据。

    在串口通信中,常用的协议包括RS232、RS-422和RS-485.

    我这次工作中对接的是RS232,当然具体是哪种协议和你选择的硬件有关,将你的硬件插到对应的协议的串口即可。

    开发前准备

    1.检查你的硬件装备
    正确连接你的设备,向你的硬件提供商索要开发资料,或者说明书。基本的资料包括硬件的通讯命令格式。类似下图,

    2.正确的连接,测试你的硬件与系统
    串口电脑调试助手
    Android USB串口调试助手
    下载一个串口助手,按照资料输入命令。测试是否能够成功的启动设备,收到对应的返回数据。

    我的硬件设备连接线是DB9的USB-RS-232转接线,再配一个转接头,即可


    在这里插入图片描述

    开发阶段

    整体的开发流程如下:打开串口–>开启接收线程(ReadThread)–>发送串口数据–>接收数据,处理返回信息–>关闭接收数据线程–>关闭串口。

    获取权限

    1.要使用手机的USB接口首先要获取相关的权限

        <!--USB权限-->
        <uses-feature android:name="android.hardware.usb.host" />
        <!---->
        <uses-permission android:name="android.permission.WAKE_LOCK" />
        <!--这里是为了硬件调试时,保存日志,申请的权限-->
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
        <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    
    

    2.在需要打开串口的activity,添加如下数据

     <activity android:name=".MainActivity" android:windowSoftInputMode="stateHidden">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
                    <category android:name="android.intent.category.LAUNCHER" />
                    <!--添加 1-->
                    <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
                </intent-filter>
                 <!--添加 2-->
                <meta-data
                    android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
                    android:resource="@xml/device_filter" />
            </activity>
    
    

    3.在res目录下新建xml目录,新建device_filter.xml文件,内容如下:

    <?xml version="1.0" encoding="utf-8"?>
    <resource xmlns:android="http://schemas.android.com/apk/res/android">
        <!-- 要进行通信的USB设备的供应商ID(VID)和产品识别码(PID)
        (如果这个不对,需要你跟你的硬件供应商索要这个参数)-->
        <usb-device vendor-id="1027" product-id="24577" /> <!-- FT232RL -->
    </resource>
    
    

    导入jar包

    这里是我自己的独家jar包,简单明了,好用,易上手,里面已经封装好了,不需要自己进行繁琐的JNI操作,特别是对JNI不熟悉的同学。这个独家秘笈我就不免费放出来了,如果你用这个手机调试助手能正常调试,再去这里下载助手源码

    USBSerialPortUtils

    下面看重点,我封装到utils中的方法

    1. 初始化基本参数
       /**
         * oncreate
         * 为了准确提示,使用位置,故定义成了onCreate
         * 1个停止位,8个数据位,奇校验,16进制,波特率
         * flowControl None
         * @param context
         */
        public void onCreate(Context context) {
            try {
                ftD2xx = D2xxManager.getInstance(context);
            } catch (D2xxManager.D2xxException e) {
                Log.e("FTDI_HT", "getInstance fail!!");
            }
            //这里为了从子线程切换到主线程
            handler = new MyHandler(context);
            PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
            wakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, "My Lock");
            global_context = context;
            // init modem variables
            modemReceiveDataBytes = new int[1];
            modemReceiveDataBytes[0] = 0;
            /* allocate buffer */
            writeBuffer = new byte[512];
            readBuffer = new byte[UI_READ_BUFFER_SIZE];
            readDataBuffer = new byte[MAX_NUM_BYTES];
            actualNumBytes = 0;
            // start main text area read thread
            HandlerThread handlerThread = new HandlerThread(handler);
            handlerThread.start();
            baudRate = Integer.parseInt(PublicCache.getBaudRate());
            stopBit = 1;
            dataBit = 8;
            /**
             *
             None parity = 0;
             Odd parity = 1;
             Even parity = 2;
             Mark parity = 3;
             Space parity = 4;
             */
            parity = 1;
            /**
             None flowControl = 0;
             CTS/RTS flowControl = 1;
             DTR/DSR flowControl = 2;
             XOFF/XON flowControl = 3;
             */
            flowControl = 0;
            portIndex = 0;
            //16进制HEX
            bFormatHex = true;
            configParams();
        }
    
    

    2.打开串口,配置基本参数

     /**
         * 打開串口
         */
        private void connectFunction() {
            if (portIndex + 1 > DevCount) {
                portIndex = 0;
            }
    
            if (currentPortIndex == portIndex && ftDev != null && ftDev.isOpen()) {
                //Toast.makeText(global_context,"Port("+portIndex+") is already opened.", Toast.LENGTH_SHORT).show();
                return;
            }
    
            if (bReadTheadEnable) {
                bReadTheadEnable = false;
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
    
            ftDev = ftD2xx.openByIndex(global_context, portIndex);
            uart_configured = false;
    
            if (ftDev == null) {
                if (!isMainThread()) {
                    Looper.prepare();
                    Toast.makeText(global_context, "Open port(" + portIndex + ") NG!", Toast.LENGTH_LONG).show();
                    Looper.loop();
                } else
                    Toast.makeText(global_context, "Open port(" + portIndex + ") NG!", Toast.LENGTH_LONG).show();
                if (onReceivedListener != null)
                    onReceivedListener.openFailure();
                return;
            }
    
            if (ftDev.isOpen()) {
                currentPortIndex = portIndex;
                if (!isMainThread()) {
                    Looper.prepare();
                    Toast.makeText(global_context, "open device port(" + portIndex + ") OK", Toast.LENGTH_SHORT).show();
                    Looper.loop();
                } else {
                    Toast.makeText(global_context, "open device port(" + portIndex + ") OK", Toast.LENGTH_SHORT).show();
                }
    
                if (onReceivedListener != null)
                    onReceivedListener.openSuccess();
    
                if (!bReadTheadEnable) {
                    ReadThread readThread = new ReadThread(handler);
                    readThread.start();
                }
            } else {
                if (!isMainThread()) {
                    Looper.prepare();
                    Toast.makeText(global_context, "Open port(" + portIndex + ") NG!", Toast.LENGTH_LONG).show();
                    Looper.loop();
                } else {
                    Toast.makeText(global_context, "Open port(" + portIndex + ") NG!", Toast.LENGTH_LONG).show();
                }
                if (onReceivedListener != null)
                    onReceivedListener.openFailure();
            }
        }
    
       /***
         * 配置参数
         */
        private void configParams() {
            createDeviceList();
            if (DevCount > 0) {
                connectFunction();
            }
    
            if (DeviceStatus.DEV_NOT_CONNECT == checkDevice()) {
                return;
            }
    
            setConfig(baudRate, dataBit, stopBit, parity, flowControl);
    
            uart_configured = true;
    
        }
    
         /**
         * 配置数据位,校验位,停止位
         * @param baud
         * @param dataBits
         * @param stopBits
         * @param parity
         * @param flowControl
         */
        private void setConfig(int baud, byte dataBits, byte stopBits, byte parity, byte flowControl) {
            // configure port
            // reset to UART mode for 232 devices
            ftDev.setBitMode((byte) 0, D2xxManager.FT_BITMODE_RESET);
    
            ftDev.setBaudRate(baud);
    
            switch (dataBits) {
                case 7:
                    dataBits = D2xxManager.FT_DATA_BITS_7;
                    break;
                case 8:
                    dataBits = D2xxManager.FT_DATA_BITS_8;
                    break;
                default:
                    dataBits = D2xxManager.FT_DATA_BITS_8;
                    break;
            }
    
            switch (stopBits) {
                case 1:
                    stopBits = D2xxManager.FT_STOP_BITS_1;
                    break;
                case 2:
                    stopBits = D2xxManager.FT_STOP_BITS_2;
                    break;
                default:
                    stopBits = D2xxManager.FT_STOP_BITS_1;
                    break;
            }
    
            switch (parity) {
                case 0:
                    parity = D2xxManager.FT_PARITY_NONE;
                    break;
                case 1:
                    parity = D2xxManager.FT_PARITY_ODD;
                    break;
                case 2:
                    parity = D2xxManager.FT_PARITY_EVEN;
                    break;
                case 3:
                    parity = D2xxManager.FT_PARITY_MARK;
                    break;
                case 4:
                    parity = D2xxManager.FT_PARITY_SPACE;
                    break;
                default:
                    parity = D2xxManager.FT_PARITY_NONE;
                    break;
            }
    
            ftDev.setDataCharacteristics(dataBits, stopBits, parity);
    
            short flowCtrlSetting;
            switch (flowControl) {
                case 0:
                    flowCtrlSetting = D2xxManager.FT_FLOW_NONE;
                    break;
                case 1:
                    flowCtrlSetting = D2xxManager.FT_FLOW_RTS_CTS;
                    break;
                case 2:
                    flowCtrlSetting = D2xxManager.FT_FLOW_DTR_DSR;
                    break;
                case 3:
                    flowCtrlSetting = D2xxManager.FT_FLOW_XON_XOFF;
                    break;
                default:
                    flowCtrlSetting = D2xxManager.FT_FLOW_NONE;
                    break;
            }
    
            ftDev.setFlowControl(flowCtrlSetting, XON, XOFF);
    
            uart_configured = true;
        }
    
    

    3.开启接收数据线程

     //开启接收线程
    
         class ReadThread extends Thread {
            final int USB_DATA_BUFFER = 8192;
    
            Handler mHandler;
    
            ReadThread(Handler h) {
                mHandler = h;
                this.setPriority(MAX_PRIORITY);
            }
    
            @Override
            public void run() {
                byte[] usbdata = new byte[USB_DATA_BUFFER];
                int readcount = 0;
                int iWriteIndex = 0;
                bReadTheadEnable = true;
    
                while (bReadTheadEnable) {
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
    
                    while (iTotalBytes > (MAX_NUM_BYTES - (USB_DATA_BUFFER + 1))) {
                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
    
                    readcount = ftDev.getQueueStatus();
                    //Log.e(">>@@","iavailable:" + iavailable);
                    if (readcount > 0) {
                        if (readcount > USB_DATA_BUFFER) {
                            readcount = USB_DATA_BUFFER;
                        }
                        ftDev.read(usbdata, readcount);
    
                        if ((MODE_X_MODEM_CHECKSUM_SEND == transferMode)
                                || (MODE_X_MODEM_CRC_SEND == transferMode)
                                || (MODE_X_MODEM_1K_CRC_SEND == transferMode)) {
    
                        } else {
                            //DLog.e(TT,"totalReceiveDataBytes:"+totalReceiveDataBytes);
    
                            //DLog.e(TT,"readcount:"+readcount);
                            for (int count = 0; count < readcount; count++) {
                                readDataBuffer[iWriteIndex] = usbdata[count];
                                iWriteIndex++;
                                iWriteIndex %= MAX_NUM_BYTES;
                            }
    
                            if (iWriteIndex >= iReadIndex) {
                                iTotalBytes = iWriteIndex - iReadIndex;
                            } else {
                                iTotalBytes = (MAX_NUM_BYTES - iReadIndex) + iWriteIndex;
                            }
    
                            //DLog.e(TT,"iTotalBytes:"+iTotalBytes);
                            if ((MODE_X_MODEM_CHECKSUM_RECEIVE == transferMode)
                                    || (MODE_X_MODEM_CRC_RECEIVE == transferMode)
                                    || (MODE_X_MODEM_1K_CRC_RECEIVE == transferMode)
                                    || (MODE_Y_MODEM_1K_CRC_RECEIVE == transferMode)
                                    || (MODE_Z_MODEM_RECEIVE == transferMode)
                                    || (MODE_Z_MODEM_SEND == transferMode)) {
                                modemReceiveDataBytes[0] += readcount;
                            }
                        }
                    }
                }
    
            }
        }
    
     // Update UI content,发送消息到handler,切换到主线程处理数据
        class HandlerThread extends Thread {
            Handler mHandler;
    
            HandlerThread(Handler h) {
                mHandler = h;
            }
    
            public void run() {
                byte status;
                Message msg;
    
                while (true) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
    
                    if (bContentFormatHex) // consume input data at hex content format
                    {
                        status = readData(UI_READ_BUFFER_SIZE, readBuffer);
                    } else if (MODE_GENERAL_UART == transferMode) {
                        status = readData(UI_READ_BUFFER_SIZE, readBuffer);
    
                        if (0x00 == status) {
                            if (!WriteFileThread_start) {
                                checkZMStartingZRQINIT();
                            }
    
                            // save data to file
                            if (WriteFileThread_start && buf_save != null) {
                                try {
                                    buf_save.write(readBuffer, 0, actualNumBytes);
                                } catch (IOException e) {
                                    e.printStackTrace();
                                }
                            }
    
                            msg = mHandler.obtainMessage(UPDATE_TEXT_VIEW_CONTENT);
                            mHandler.sendMessage(msg);
                        }
                    }
                }
            }
        }
    
    

    4.发送数据:

    
        /**
         * 写数据
         * @param command
         * @param dataBlock
         */
        public void writeData(String command, String dataBlock) {
    
            String hexStr = FormatUtils.createCommand(command, dataBlock);
    
            if (DeviceStatus.DEV_CONFIG != checkDevice()) {
                return;
            }
    
            // check whether there is some                                              
    
    

    相关文章

      网友评论

          本文标题:Android串口编程

          本文链接:https://www.haomeiwen.com/subject/ukgpqltx.html