Android jni 串口开发 SerialPort,在C文件中实现所有操作。

前言

搞了一年半的串口了,给大家分享一下入坑与脱坑的经验,有所不足,请多指教

 开始

1、新建项目

      在Android 3.1更新到3.2以后好像就不能自己选择是否导入C文件

2、建立jni文件夹来存放c文件

      Android jni 串口开发 SerialPort,在C文件中实现所有操作。_第1张图片

3、创建C文件,命名后缀为选择.c

Android jni 串口开发 SerialPort,在C文件中实现所有操作。_第2张图片

 

4、在app目录下建立CMakeLists.txt文件

Android jni 串口开发 SerialPort,在C文件中实现所有操作。_第3张图片

5、填写CMakeLists中的内容

Android jni 串口开发 SerialPort,在C文件中实现所有操作。_第4张图片

6、创建类文件 ,Open  Close  Read()  Write() 这4个方法决定了你在C文件中生成的4个方法,我今天要说的就是开 关 读 发 都是在c文件中执行

Android jni 串口开发 SerialPort,在C文件中实现所有操作。_第5张图片

7、连接你的CMakeLists文件,文件地址就在你项目的app目录下面

Android jni 串口开发 SerialPort,在C文件中实现所有操作。_第6张图片

Android jni 串口开发 SerialPort,在C文件中实现所有操作。_第7张图片

8、点击ok后你会发现你定义的类文件中的4个方法变成了红色,然后你定义的C文件中加上

#include 

然后进入类文件中点击alt+enter 选择创建方法

Android jni 串口开发 SerialPort,在C文件中实现所有操作。_第8张图片

Android jni 串口开发 SerialPort,在C文件中实现所有操作。_第9张图片

8、将4个方法全部创建 然后将代码复制到方法中,方法名是我自己的,你们复制的时候不要讲方法名弄进去了

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 


#undef  TCSAFLUSH
#define TCSAFLUSH  TCSETSF
#ifndef _TERMIOS_H_
#define _TERMIOS_H_
#endif

int fd = 0;

JNIEXPORT jint JNICALL
Java_cn_whzwl_xbs_serialport_1demo_SerialPort_Open(JNIEnv *env, jobject obj, jint Port,
                                                   jint Rate) {


    if (fd <= 0) {

        if (0 == Port) {
            __android_log_print(ANDROID_LOG_INFO, "serial", "open fd /dev/ttySAC0");
            fd = open("/dev/ttySAC0", O_RDWR | O_NDELAY | O_NOCTTY);
        } else if (1 == Port) {
            __android_log_print(ANDROID_LOG_INFO, "serial", "open fd /dev/ttySAC1");
            fd = open("/dev/ttySAC1", O_RDWR | O_NDELAY | O_NOCTTY);
        } else if (2 == Port) {
            __android_log_print(ANDROID_LOG_INFO, "serial", "open fd /dev/ttySAC2");
            fd = open("/dev/ttySAC2", O_RDWR | O_NDELAY | O_NOCTTY);
        } else if (3 == Port) {
            __android_log_print(ANDROID_LOG_INFO, "serial", "open fd /dev/ttySAC3");
            fd = open("/dev/ttySAC3", O_RDWR | O_NDELAY | O_NOCTTY);
        } else if (4 == Port) {
            __android_log_print(ANDROID_LOG_INFO, "serial", "open fd /dev/ttyUSB0");
            fd = open("/dev/ttyUSB0", O_RDWR | O_NDELAY | O_NOCTTY);
        } else if (5 == Port) {
            __android_log_print(ANDROID_LOG_INFO, "serial", "open fd /dev/ttyUSB1");
            fd = open("/dev/ttyUSB1", O_RDWR | O_NDELAY | O_NOCTTY);
        } else {
            __android_log_print(ANDROID_LOG_INFO, "serial", "Parameter Error serial not found");
            fd = 0;
            return -1;
        }

        if (fd > 0) {
            // disable echo on serial ports
            struct termios ios;
            tcgetattr(fd, &ios);
            ios.c_oflag &= ~(INLCR | IGNCR | ICRNL);
            ios.c_oflag &= ~(ONLCR | OCRNL);
            ios.c_iflag &= ~(ICRNL | IXON);
            ios.c_iflag &= ~(INLCR | IGNCR | ICRNL);
            ios.c_iflag &= ~(ONLCR | OCRNL);
            tcflush(fd, TCIFLUSH);

            if (Rate == 2400) {
                cfsetospeed(&ios, B2400);
                cfsetispeed(&ios, B2400);
            }
            if (Rate == 4800) {
                cfsetospeed(&ios, B4800);
                cfsetispeed(&ios, B4800);
            }
            if (Rate == 9600) {
                cfsetospeed(&ios, B9600);
                cfsetispeed(&ios, B9600);
            }
            if (Rate == 19200) {
                cfsetospeed(&ios, B19200);
                cfsetispeed(&ios, B19200);
            }
            if (Rate == 38400) {
                cfsetospeed(&ios, B38400);
                cfsetispeed(&ios, B38400);
            }
            if (Rate == 57600) {
                cfsetospeed(&ios, B57600);
                cfsetispeed(&ios, B57600);
            }
            if (Rate == 115200) {
                cfsetospeed(&ios, B115200);
                cfsetispeed(&ios, B115200);
            }

            ios.c_cflag |= (CLOCAL | CREAD);
            ios.c_cflag &= ~PARENB;
            ios.c_cflag &= ~CSTOPB;
            ios.c_cflag &= ~CSIZE;
            ios.c_cflag |= CS8;
            ios.c_lflag = 0;
            tcsetattr(fd, TCSANOW, &ios);
        }

    }

    return fd;
}

JNIEXPORT jint JNICALL
Java_cn_whzwl_xbs_serialport_1demo_SerialPort_Close(JNIEnv *env, jobject obj) {
    if (fd > 0) {
        fd = 0;
        close(fd);
    }
}

JNIEXPORT jintArray JNICALL
Java_cn_whzwl_xbs_serialport_1demo_SerialPort_Read(JNIEnv *env, jobject obj) {
    unsigned char buffer[512];
    int BufToJava[512];
    int len = 0, i = 0;

    memset(buffer, 0, sizeof(buffer));
    memset(BufToJava, 0, sizeof(BufToJava));

    len = read(fd, buffer, 512);

    if (len <= 0)return NULL;

    for (i = 0; i < len; i++) {
        printf("%x", buffer[i]);
        BufToJava[i] = buffer[i];
    }

    jintArray array = (*env)->NewIntArray(env, len);
    (*env)->SetIntArrayRegion(env, array, 0, len, BufToJava);
    (*env)->ReleaseIntArrayElements(env, array, BufToJava, 0);

    return array;
}

JNIEXPORT jint JNICALL
Java_cn_whzwl_xbs_serialport_1demo_SerialPort_Write(JNIEnv *env, jobject obj, jintArray buf,
                                                    jint buflen) {
    jsize len = buflen;

    if (len <= 0)
        return -1;

    jintArray array = (*env)->NewIntArray(env, len);

    if (array == NULL) {
        array = NULL;
        return -1;
    }

    jint *body = (*env)->GetIntArrayElements(env, buf, 0);

    jint i = 0;
    unsigned char num[len];

    for (; i < len; i++)
        num[i] = body[i];

    write(fd, num, len);

    array = NULL;

    return 0;
}

连接的c文件的方式与c文件中的内容就写好了,接下来跟大家来描述怎么操作

主要操作

定义一个抽象类 

SerialUilt 读 收 开 关 的主要方法
 public static SerialPort serial;

    public static Thread receiveThread = null;

    public SerialUilt() {
        serial = new SerialPort();

    }

    /**
     * 开启串口,并且接收执行线程。
     */
    public  void  Open()
    {
         //3对应的是c文件中写的判断端口(如有其它请自己添加)  
         //9600为波特率 ,根据自己需要的填写
        serial.Open(3,9600);  
        receiveSerialPort();
    }


    /**
     * 接收串口数据的方法
     */
    public void receiveSerialPort() {

        if (receiveThread != null)
            return;
    

        /*创建子线程接收串口数据
         */
        receiveThread = new Thread() {
            @Override
            public void run() {
                while (!isInterrupted()) {
                    try {
                        Thread.sleep(100);
                        String data =  Recv();

                        if (!data.equals("")) {

                            onDataReceived(data);
                        }


                    } catch (InterruptedException e) {
                        e.printStackTrace();
                        break;
                    }
                }

            }
        };
        //启动接收线程
        receiveThread.start();
    }

    /**
     * @param serialData
     * 抽象方法主要用于在调用界面从这个里面获取值
     */
    protected abstract void onDataReceived(String serialData);

    /**
     * @param tx 传入指令参数
     *           发送指令
     */
    public void Send(CharSequence tx) {

        int[] text = new int[tx.length()];
        for (int i = 0; i < tx.length(); i++) {
            text[i] = tx.charAt(i);
        }
        serial.Write(text, tx.length());
        Log.e("serialUilt","发送  "+tx);
    }

    /**
     * @return 获取指令返回值
     */
    private String Recv() {
        int[] RX = serial.Read();   //
        if (RX == null) {

            return "";
        } else {
         Log.e("serialUilt","接收  长度:"+RX.length  +"  数据:"+new String(RX, 0, RX.length));
            return new String(RX, 0, RX.length);
        }
    }

    /**
     * 关闭串口,关闭线程
     */
    public  void Close() {
        if (receiveThread!=null){
            receiveThread.interrupt();
            receiveThread=null;
        }

        serial.Close();

    }

在Activity中使用

   Xml文件





        

            

        



        

            

            

Activity代码

package cn.whzwl.xbs.serialport_demo;

import android.content.Context;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ScrollView;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    private Button open_close, send, empty;
    private TextView content;
    private ScrollView scrollView;
    private EditText send_content;
    SerialUilt serialUilt;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
        setContentView(R.layout.activity_main);
        init();
        listening();
        serialUilt = new SerialUilt() {
            @Override
            protected void onDataReceived(final String serialData) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {

                        stringBuffer.append(serialData);//Android串口开发中 很容易出现被截断接收 比如第一次接收的是4个字节 然后第二次就将剩余的全部返回
                        scrollView.fullScroll(ScrollView.FOCUS_DOWN);//滚动到底部
                        //我这边总体判断长度  如果长度一样 就确定是正确串口消息
                        if (stringBuffer.toString().length() == 26) {

                            //在串口消息中需要订协议来通讯 要判断包头  包尾,我这里包头用!- 代替  包尾用-!代替
                            if (stringBuffer.toString().substring(0, 3).equals("!--") && stringBuffer.toString().substring(stringBuffer.toString().length() - 3, stringBuffer.toString().length()).equals("--!")) {
                                Log.e("serialUilt", "接收到完整数据:" + stringBuffer.toString().length() + "\r\n");
                                content.append("\r\n"+stringBuffer.toString()+"\r\n");
                                stringBuffer.setLength(0);

                            }
                        }
                    }
                });
            }

        };
        serialUilt.Open();
    }


    StringBuffer stringBuffer;

    private void init() {
        open_close = findViewById(R.id.open_close);
        send = findViewById(R.id.send);
        send.setEnabled(false);
        content = findViewById(R.id.content);
        scrollView = findViewById(R.id.scrollView);
        send_content = findViewById(R.id.send_content);
//        cycle=findViewById(R.id.cycle);
        empty = findViewById(R.id.empty);


        stringBuffer = new StringBuffer();


    }

    boolean judge = false;


    private void listening() {
        //串口开启关闭
        open_close.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (judge) {
                    serialUilt.Close();
                    judge = false;
                    open_close.setText("已关闭");

                    send.setEnabled(false);
                } else {
                    serialUilt.Open();
                    judge = true;
                    open_close.setText("已开启");
                    send.setEnabled(true);

                }
            }
        });

        //发送串口
        send.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                serialUilt.Send(send_content.getText().toString());
                content.append("\r\n发出消息:" + send_content.getText().toString() + "\r\n");
            }
        });

        //清空接收
        empty.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                content.setText("");
                stringBuffer.setLength(0);
            }
        });


    }

    /**
     * 点击空白区域隐藏键盘.
     */
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            View v = getCurrentFocus();
            if (isShouldHideInput(v, ev)) {

                InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
                if (imm != null) {
                    imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
                }
            }
            return super.dispatchTouchEvent(ev);
        }
        // 必不可少,否则所有的组件都不会有TouchEvent了
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
        return onTouchEvent(ev);
    }

    public boolean isShouldHideInput(View v, MotionEvent event) {
        if (v != null && (v instanceof EditText)) {
            int[] leftTop = {0, 0};
            //获取输入框当前的location位置
            v.getLocationInWindow(leftTop);
            int left = leftTop[0];
            int top = leftTop[1];
            int bottom = top + v.getHeight();
            int right = left + v.getWidth();
            if (event.getX() > left && event.getX() < right
                    && event.getY() > top && event.getY() < bottom) {
                // 点击的是输入框区域,保留点击EditText的事件
                return false;
            } else {
                return true;
            }
        }
        return false;
    }
}

以上就是整个通讯过程。

测试结果

Android jni 串口开发 SerialPort,在C文件中实现所有操作。_第10张图片

 

 

以上就是所有的内容。有所不足请多指教

demo: https://download.csdn.net/download/bisheng_xiong/11182069

github:https://github.com/ASheng-Bisheng/serialport-demo

你可能感兴趣的:(Android jni 串口开发 SerialPort,在C文件中实现所有操作。)