美文网首页
CH34X串口通信

CH34X串口通信

作者: 浪克oo | 来源:发表于2018-12-27 15:13 被阅读0次

最近在开发android工控机同硬件设备通信的时候,用到了ch340U转串,所以把关于这个串口的一些知识分享给大家。

简介:

CH34x 系列芯片是 USB 总线的转接芯片,主要包含 CH340、CH341、CH345,通过 USB 总线提供异 步串口、打印口、并口、MIDI 以及常用的 2 线和 4 线等接口。

CH34x 串口提供的 Android 接口需要基于 Android 3.1 及以上版本系统,使用 CH34x 串口 Android 驱动条件:

1、需要基于 Android 3.1 及以上版本系统

2、Android 设备具有 USB Host 或 OTG 接口

接口说明:

EnumerateDevice:枚举 CH34x 设备

函数原型 :public UsbDevice EnumerateDevice()

返回枚举到的 CH34x 的设备,若无设备则返回 null

OpenDevice:打开 CH34x 设备

函数原型 :public void OpenDevice(UsbDevice mDevice)

mDevice :需要打开的 CH34x 设备

ResumeUsbList:枚举并打开 CH34x 设备,这个函数包含了 EnumerateDevice,OpenDevice 操作

函数原型 :public int ResumeUsbList()

返回 0 则成功,否则失败

UartInit:设置初始化 CH34x 芯片

函数原型 :public boolean UartInit()

若初始化失败,则返回 false,成功返回 true

SetConfig:设置 UART 接口的波特率、数据位、停止位、奇偶校验位以及流控

函数原型 :public boolean SetConfig(int baudRate, byte dataBit, byte stopBit, byte parity, byte flowControl)

baudRate :波特率:300,600,1200、2400、4800、9600、19200、38400、57600、115200、 230400、460800、921600,默认:9600

dataBits :5 个数据位、6 个数据位、7 个数据位、8 个数据位,默认:8 个数据位

stopBits :0:1 个停止位,1:2 个停止位,默认:1 个停止位

parity :0:none,1:add,2:even,3:mark 和 4:space,默认:none

flowControl :0:none,1:cts/rts,默认:none

若设置失败,则返回 false,成功返回 true

WriteData:发送数据

函数原型 :public int WriteData(byte[] buf, int length)

buf :发送缓冲区 length :发送的字节数

返回值为写成功的字节数

ReadData:读取数据

函数原型 :public int ReadData(char[] data, int length)

data :接收缓冲区,数据类型为

char length :读取的字节数 返回实际读取的字节数

函数原型 :public int ReadData(byte[] data, int length)

data :接收缓冲区

length :读取的字节数 返回实际读取的字节数

CloseDevice:关闭串口。

函数原型 :public void CloseDevice()

isConnected:判断设备是否已经连接到 Android 系统

函数原型 :public boolean isConnected() 返回为 false 时表示设备未连接到系统,true 表示设备已连接

除了上述提供的接口 API,用户还可以根据自己的设备来设置读写超时时间:

函数原型:public boolean SetTimeOut(int WriteTimeOut, int ReadTimeOut)

WriteTimeOut:设置写超时时间,默认为 10000ms
ReadTimeOut :设置读超时时间,默认为 10000ms

使用:

Screenshot_2018-12-27-12-01-01.png

添加usb权限 :

    <uses-feature android:name="android.hardware.usb.host"/>

现将jar包拷贝到lib文件夹下https://pan.baidu.com/s/1SG-9MWgZ9OfLLxTTXv8RPg

然后就可以调用所提供的的方法了,为了方便理解我另外写了点简单的代码,多的不说直接上代码,注释很详细:


//打开串口

    public void open() {

        try {

            device = new CH34xUARTDriver((UsbManager) context.getSystemService(Context.USB_SERVICE), context, ACTION_USB_PERMISSION);

            if (!device.UsbFeatureSupported())// 判断系统是否支持USB HOST

            { return; }

            //判断是否授权  如果没有等待几秒让用户授权

            if (device.ResumeUsbPermission() == -2) {

                timer.schedule( authorizeTimeTask, 1000, 1000);

            } else {

                //如果已授权 直接开启连接

                if (setCon()) {

                    ConThread = new Thread(Conrunnable);

                    ConThread.start();

                }

            }

        } catch (Exception e) {

            sentConMsg("初始化USB串口异常");

        }

    }

    //连接串口

    private boolean setCon() {

        if (device == null) {

            return false;

        }

        try {

            //检查是否授权

            if (device.ResumeUsbPermission() == -2) {

                sentConMsg("授权失败");

            }

            //得到设备名称

            UsbName = device.EnumerateDevice().getDeviceName();

            //打开设备

            retval = device.ResumeUsbList();

            if (retval == -1) {

                sentConMsg("打开串口失败1");

                device.CloseDevice();

                isStart = false;

            } else if (retval == 0) {

                if (!device.UartInit()) {

                    sentConMsg("打开串口失败2");

                    isStart = false;

                    return false;

                }

                //配置串口波特率

                is = device.SetConfig(baudRate, dataBit, stopBit, parity, flowControl);

                if (is) {

                    sentConMsg("打开串口成功");

                    isStart = true;

                    return true;

                } else {

                    sentConMsg("打开串口失败3");

                }

            }

        } catch (Exception e) {

            sentConMsg("打开串口异常4");

        }

        return false;

    }

本想不停地请求打开设备直到用户授权,但是不行,因为不停地请求会报异常,所以我是设置了个计时器(待优化),在规定的时间内授权后,再次请求打开串口就行了。


//几秒后 用户已授权 然后开始连接串口

    final Handler timeConnectHandler = new Handler() {

        @Override

        public void handleMessage(Message msg) {

            switch (msg.what) {

                case 1:

                    if (recLen < 0) {

                        timer.cancel();

                        if (setCon()) {

                            ConThread = new Thread(Conrunnable);

                            ConThread.start();

                        }

                    }

            }

        }

    };

    TimerTask  authorizeTimeTask = new TimerTask() {

        @Override

        public void run() {

            recLen--;

            Message message = Message.obtain();

            message.what = 1;

            timeConnectHandler.sendMessage(message);

        }

    };

因为我们需要在不同的界面知道现在串口的连接情况所以写了一个接口,专门用来传输连接状态:


    /**

    * 连接状态事件

    */

    private List<ConnectStateListener> lstConnect;

    public void addConnectState(ConnectStateListener connectStateListener) {

        if (lstConnect == null) {

            lstConnect = new ArrayList<>();

        }

        lstConnect.add(connectStateListener);

    }

    public void removeConnectState(ConnectStateListener connectStateListener) {

        if (lstConnect == null) {

            lstConnect = new ArrayList<>();

        }

        lstConnect.remove(connectStateListener);

    }

    private Handler ConnetHandler = new Handler() {

        public void handleMessage(Message msg) {

            if (lstConnect == null)

                return;

            for (ConnectStateListener item : lstConnect) {

                item.ConnectState((String) msg.obj);

            }

        }

    };

    //发送状态

    private void sentConMsg(String msg) {

        Message message = Message.obtain();

        message.obj = msg;

        ConnetHandler.sendMessage(message);

    }

做到现在应该是可以读取到数据了的,开启线程读取数据:


    private Runnable Conrunnable = new Runnable() {

        @Override

        public void run() {

            try {

                byte[] buffer;

                while (isRead) {

                    try {

                        if (!isStart) {

                            break;

                        }

                        buffer = new byte[520];

                        int length = device.ReadData(buffer, 520);

                        if (length > 0) {

                        sentConMsg("串口连接成功");

                            byte[] temp = new byte[length];

                            System.arraycopy(buffer, 0, temp, 0, length);

                            sentData(temp );

                        }

                        if (length == 0) {

                            if (_Timer == null) {

                                _Timer = new Timer();

                                _Timer.schedule(new DataTimer(), 0, 1000);

                            }

                            continue;

                        } else {

                            _Datatime = 0;

                        }

                    } catch (Exception e) {

                        sentConMsg("读取数据异常1");

                        continue;

                    }

                }

                close();

                return;

            } catch (Exception e) {

                sentConMsg("连接异常");

            }

        }

    };

当数据在几秒内持续为空是,视为连接已断开。读取的数据与界面的交互同样使用接口:


/**

    * 数据传输事件

    */

    private List<GetDataListener> lsDatas;

    public void addGetData(GetDataListener dataListener) {

        if (lsDatas == null) {

            lsDatas = new ArrayList<>();

        }

        lsDatas.add(dataListener);

    }

    public void removeGetData(GetDataListener dataListener) {

        if (lsDatas == null) {

            lsDatas = new ArrayList<>();

        }

        lsDatas.remove(dataListener);

    }

    private Handler DataHandler = new Handler() {

        public void handleMessage(Message msg) {

            if (lsDatas == null)

                return;

            for (GetDataListener item : lsDatas) {

                item.getData((byte[]) msg.obj);

            }

        }

    };

    //发送数据

    private void sentData(byte[] bytes) {

        Message message = Message.obtain();

        message.obj = bytes;

        DataHandler.sendMessage(message);

    }

关闭串口,就是回收一下资源,停止读取数据就行


public void close() {

        if (_Timer != null) {

            _Timer.cancel();

            _Timer = null;

            _Datatime = 0;

        }

        UsbName = "";

        isRead = false;

        if (ConThread != null) {

            ConThread.interrupt();

            ConThread = null;

        }

        _Timer = null;

        _Datatime = 0;

        isStart = false;

    }

发送数据给硬件或者是什么主要就是要判一下空


    //发送数据

    public void send(byte[] data) {

        if (device != null && data != null) {

            int retval = device.WriteData(data, data.length);

            if (retval > 0) {

                //发送成功

            } else {

                //发送失败

            }

        }

    }

打开以及关闭的方法需要这样调用:


switch (v.getId()) {

            case R.id.OpenButton:

                Ch34Helper.getInstance(this).open();

                break;

            case R.id.WriteButton:

                byte[] to_send = toByteArray(writeText.getText().toString());

                Ch34Helper.getInstance(this).send(to_send);

                break;

            case R.id.CloseButton:

                Ch34Helper.getInstance(this).close();

                break;

        }

在界面上的调用就很简单了,数据接收需要实现两个接口,然后把数据显示到界面:


//初始化连接以及数据传输事件

    private void initData() {

        Ch34Helper.getInstance(this).addConnectState(this);

        Ch34Helper.getInstance(this).addGetData(this);

    }

//设置连接状态显示

    @Override

    public void ConnectState(String s) {

        if (s != null && s.length() > 0)

            connectText.setText(s);

    }

    //设置接收数据显示

    @Override

    public void getData(byte[] bytes) {

        if (bytes != null && bytes.length > 0)

            readText.setText(bytes2HexString(bytes));

    }

还有就是最后要记得在界面销毁的时候回收资源


  @Override

    protected void onDestroy() {

        super.onDestroy();

        Ch34Helper.getInstance(this).removeConnectState(this);

        Ch34Helper.getInstance(this).removeGetData(this);

        Ch34Helper.getInstance(this).close();

    }

读取类完整代码:


public class Ch34Helper {

    private static Context context;

    private static CH34xUARTDriver device = null;

    private Thread ConThread;

    private boolean isStart = false;

    private static final String ACTION_USB_PERMISSION = "cn.wch.wchusbdriver.USB_PERMISSION";

    private int baudRate = 115200;

    private byte stopBit = 1;

    private byte dataBit = 8;

    private byte parity = 0;

    private byte flowControl = 0;

    private int retval;

    private boolean is;

    private String UsbName;

    private boolean isRead = true;

    private int recLen = 8;

    private Timer timer = new Timer();

    private Timer _Timer = null;

    private int _Datatime = 0;

    private static Ch34Helper ch34Helper;

    public static Ch34Helper getInstance(Context con) {

        if (null == ch34Helper) {

            ch34Helper = new Ch34Helper();

            context = con;

        }

        return ch34Helper;

    }

    private class DataTimer extends TimerTask {

        @Override

        public void run() {

            //超过3秒没有数据 视为连接断开

            _Datatime++;

            if (_Datatime > 3) {

                sentConMsg("串口已断开");

            }

        }

    }

    //打开串口

    public void open() {

        try {

            device = new CH34xUARTDriver((UsbManager) context.getSystemService(Context.USB_SERVICE), context, ACTION_USB_PERMISSION);

            if (!device.UsbFeatureSupported())// 判断系统是否支持USB HOST

            {

                return;

            }

            //判断是否授权  如果没有等待几秒让用户授权

            if (device.ResumeUsbPermission() == -2) {

                timer.schedule(authorizeTimeTask, 1000, 1000);

            } else {

                //如果已授权 直接开启连接

                if (setCon()) {

                    ConThread = new Thread(Conrunnable);

                    ConThread.start();

                }

            }

        } catch (Exception e) {

            sentConMsg("初始化USB串口异常");

        }

    }

    //连接串口

    private boolean setCon() {

        if (device == null) {

            return false;

        }

        try {

            //检查是否授权

            if (device.ResumeUsbPermission() == -2) {

                sentConMsg("授权失败");

            }

            //得到设备名称

            UsbName = device.EnumerateDevice().getDeviceName();

            //打开设备

            retval = device.ResumeUsbList();

            if (retval == -1) {

                sentConMsg("打开串口失败1");

                device.CloseDevice();

                isStart = false;

            } else if (retval == 0) {

                if (!device.UartInit()) {

                    sentConMsg("打开串口失败2");

                    isStart = false;

                    return false;

                }

                //配置串口波特率

                is = device.SetConfig(baudRate, dataBit, stopBit, parity, flowControl);

                if (is) {

                    sentConMsg("打开串口成功");

                    isStart = true;

                    return true;

                } else {

                    sentConMsg("打开串口失败3");

                }

            }

        } catch (Exception e) {

            sentConMsg("打开串口异常4");

        }

        return false;

    }

    //几秒后 用户已授权 然后开始连接串口

    final Handler timeConnectHandler = new Handler() {

        @Override

        public void handleMessage(Message msg) {

            switch (msg.what) {

                case 1:

                    if (recLen < 0) {

                        timer.cancel();

                        if (setCon()) {

                            ConThread = new Thread(Conrunnable);

                            ConThread.start();

                        }

                    }

            }

        }

    };

    TimerTask authorizeTimeTask = new TimerTask() {

        @Override

        public void run() {

            recLen--;

            Message message = Message.obtain();

            message.what = 1;

            timeConnectHandler.sendMessage(message);

        }

    };

    //得到搜索到的设备

    public UsbDevice getDevice() {

        if (device == null) {

            device = new CH34xUARTDriver((UsbManager) context.getSystemService(Context.USB_SERVICE), context, ACTION_USB_PERMISSION);

            return device.EnumerateDevice();

        }

        return device.EnumerateDevice();

    }

    private Runnable Conrunnable = new Runnable() {

        @Override

        public void run() {

            try {

                byte[] buffer;

                while (isRead) {

                    try {

                        if (!isStart) {

                            break;

                        }

                        buffer = new byte[520];

                        int length = device.ReadData(buffer, 520);

                        if (length > 0) {

                      sentConMsg("串口连接成功");

                            byte[] temp = new byte[length];

                            System.arraycopy(buffer, 0, temp, 0, length);

                            sentData(temp );

                        }

                        if (length == 0) {

                            if (_Timer == null) {

                                _Timer = new Timer();

                                _Timer.schedule(new DataTimer(), 0, 1000);

                            }

                            continue;

                        } else {

                            _Datatime = 0;

                        }

                    } catch (Exception e) {

                        sentConMsg("读取数据异常1");

                        continue;

                    }

                }

                close();

                return;

            } catch (Exception e) {

                sentConMsg("连接异常");

            }

        }

    };

    public void close() {

        if (_Timer != null) {

            _Timer.cancel();

            _Timer = null;

            _Datatime = 0;

        }

        UsbName = "";

        isRead = false;

        if (ConThread != null) {

            ConThread.interrupt();

            ConThread = null;

        }

        _Timer = null;

        _Datatime = 0;

        isStart = false;

    }

    //发送数据

    public void send(byte[] data) {

        if (device != null && data != null) {

            int retval = device.WriteData(data, data.length);

            if (retval > 0) {

                //发送成功

            } else {

                //发送失败

            }

        }

    }

    /**

    * 数据传输事件

    */

    private List<GetDataListener> lsDatas;

    public void addGetData(GetDataListener dataListener) {

        if (lsDatas == null) {

            lsDatas = new ArrayList<>();

        }

        lsDatas.add(dataListener);

    }

    public void removeGetData(GetDataListener dataListener) {

        if (lsDatas == null) {

            lsDatas = new ArrayList<>();

        }

        lsDatas.remove(dataListener);

    }

    private Handler DataHandler = new Handler() {

        public void handleMessage(Message msg) {

            if (lsDatas == null)

                return;

            for (GetDataListener item : lsDatas) {

                item.getData((byte[]) msg.obj);

            }

        }

    };

    //发送数据

    private void sentData(byte[] bytes) {

        Message message = Message.obtain();

        message.obj = bytes;

        DataHandler.sendMessage(message);

    }

    /**

    * 连接状态事件

    */

    private List<ConnectStateListener> lstConnect;

    public void addConnectState(ConnectStateListener connectStateListener) {

        if (lstConnect == null) {

            lstConnect = new ArrayList<>();

        }

        lstConnect.add(connectStateListener);

    }

    public void removeConnectState(ConnectStateListener connectStateListener) {

        if (lstConnect == null) {

            lstConnect = new ArrayList<>();

        }

        lstConnect.remove(connectStateListener);

    }

    private Handler ConnetHandler = new Handler() {

        public void handleMessage(Message msg) {

            if (lstConnect == null)

                return;

            for (ConnectStateListener item : lstConnect) {

                item.ConnectState((String) msg.obj);

            }

        }

    };

    //发送状态

    private void sentConMsg(String msg) {

        Message message = Message.obtain();

        message.obj = msg;

        ConnetHandler.sendMessage(message);

    }

}

activity代码:


public class MainActivity extends AppCompatActivity implements View.OnClickListener, ConnectStateListener, GetDataListener {

    private EditText readText, writeText;

    private TextView connectText;

    private Button writeButton, openButton, CloseButton;

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        initView();

        initData();

    }

//初始化连接以及数据传输事件

    private void initData() {

        Ch34Helper.getInstance(this).addConnectState(this);

        Ch34Helper.getInstance(this).addGetData(this);

    }

//设置连接状态显示

    @Override

    public void ConnectState(String s) {

        if (s != null && s.length() > 0)

            connectText.setText(s);

    }

    //设置接收数据显示

    @Override

    public void getData(byte[] bytes) {

        if (bytes != null && bytes.length > 0)

            readText.setText(bytes2HexString(bytes));

    }

    private void initView() {

        connectText = (TextView) findViewById(R.id.ConectValues);

        readText = (EditText) findViewById(R.id.ReadValues);

        writeText = (EditText) findViewById(R.id.WriteValues);

        writeButton = (Button) findViewById(R.id.WriteButton);

        openButton = (Button) findViewById(R.id.OpenButton);

        CloseButton = (Button) findViewById(R.id.CloseButton);

        CloseButton.setOnClickListener(this);

        writeButton.setOnClickListener(this);

        openButton.setOnClickListener(this);

    }

    @Override

    public void onClick(View v) {

        switch (v.getId()) {

            case R.id.OpenButton:

                Ch34Helper.getInstance(this).open();

                break;

            case R.id.WriteButton:

                byte[] to_send = toByteArray(writeText.getText().toString());

                Ch34Helper.getInstance(this).send(to_send);

                break;

            case R.id.CloseButton:

                Ch34Helper.getInstance(this).close();

                break;

        }

    }

    @Override

    protected void onDestroy() {

        super.onDestroy();

        Ch34Helper.getInstance(this).removeConnectState(this);

        Ch34Helper.getInstance(this).removeGetData(this);

        Ch34Helper.getInstance(this).close();

    }

    /**

    * 将String转化为byte[]数组

    *

    * @param arg 需要转换的String对象

    * @return 转换后的byte[]数组

    */

    private byte[] toByteArray(String arg) {

        if (arg != null) {

            /* 1.先去除String中的' ',然后将String转换为char数组 */

            char[] NewArray = new char[1000];

            char[] array = arg.toCharArray();

            int length = 0;

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

                if (array[i] != ' ') {

                    NewArray[length] = array[i];

                    length++;

                }

            }

            /* 将char数组中的值转成一个实际的十进制数组 */

            int EvenLength = (length % 2 == 0) ? length : length + 1;

            if (EvenLength != 0) {

                int[] data = new int[EvenLength];

                data[EvenLength - 1] = 0;

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

                    if (NewArray[i] >= '0' && NewArray[i] <= '9') {

                        data[i] = NewArray[i] - '0';

                    } else if (NewArray[i] >= 'a' && NewArray[i] <= 'f') {

                        data[i] = NewArray[i] - 'a' + 10;

                    } else if (NewArray[i] >= 'A' && NewArray[i] <= 'F') {

                        data[i] = NewArray[i] - 'A' + 10;

                    }

                }

                /* 将 每个char的值每两个组成一个16进制数据 */

                byte[] byteArray = new byte[EvenLength / 2];

                for (int i = 0; i < EvenLength / 2; i++) {

                    byteArray[i] = (byte) (data[i * 2] * 16 + data[i * 2 + 1]);

                }

                return byteArray;

            }

        }

        return new byte[]{};

    }

    /* *

    * @param bytes 字节数组

    * @return 16进制大写字符串

    */

    public static String bytes2HexString(byte[] bytes) {

        if (bytes == null) return null;

        int len = bytes.length;

        if (len <= 0) return null;

        char[] ret = new char[len << 1];

        for (int i = 0, j = 0; i < len; i++) {

            ret[j++] = hexDigits[bytes[i] >>> 4 & 0x0f];

            ret[j++] = hexDigits[bytes[i] & 0x0f];

        }

        return new String(ret);

    }

    private static final char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};

}

布局就不贴了,应该难不倒你们。

总的来说各种各样的串口读取方式其实相差不大,主要就是自己试一把,那么就没多大问题了,过两天再更新一个4孔插口串口的读取方式,希望本篇能帮助到你们。

相关文章

  • CH34X串口通信

    最近在开发android工控机同硬件设备通信的时候,用到了ch340U转串,所以把关于这个串口的一些知识分享给大家...

  • Android平台的串口通信技术

    概念 串口通信 概念;串口通信(Serial Communications)按位(bit)发送和接收字节。串口可以...

  • 干货小知识:一文教你Java程序与串口通信的实现及通信!

    串口通信的原理 串口通信指串口按位(bit)发送和接收字节。尽管比按字节(byte)的并行通信慢,但是串口可以在使...

  • Python串口通信

    前言 从需求说起 串口通信控制二维码阅读器 串口通信控制门闸机 pyserial python 串口通信第三方包 ...

  • 9月21日

    今天讲的全新内容 串口通信 什么是串口和并口 主要讲解是串口通信寄存器的配置 并且初步实现了通信功能 下午复习上午...

  • 2018-01-08课后总结

    今天学习了串口通信。串口通信分为串行通信和并行通信,综合串行和并行的优缺点,在工业分布中串行通信的应用更为广泛。了...

  • java实现上位机与下位机串口通信

    串口通信是在工程应用中很常见。在上位机与下位机通讯过程中常通过有线的串口进行通信,在低速传输模式下串口通信得到广泛...

  • STM32学习:USART串口通信

    一、STM32F1的USART介绍 (一)串口通信简介 串口通信(Serial Communication),是指...

  • Android USB转串口通信

    一、引用 1、Git上最火的USB转串口通信2、Android之USB转串口通信3、安卓开发中的USB转串口通讯 ...

  • python的串口连接

    python真的是强大。还能处理串口的数据。 今天主要解释下串口如何进行通信的。 首先进行串口通信用的是 impo...

网友评论

      本文标题:CH34X串口通信

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