搞了一年半的串口了,给大家分享一下入坑与脱坑的经验,有所不足,请多指教
1、新建项目
在Android 3.1更新到3.2以后好像就不能自己选择是否导入C文件
2、建立jni文件夹来存放c文件
3、创建C文件,命名后缀为选择.c
4、在app目录下建立CMakeLists.txt文件
5、填写CMakeLists中的内容
6、创建类文件 ,Open Close Read() Write() 这4个方法决定了你在C文件中生成的4个方法,我今天要说的就是开 关 读 发 都是在c文件中执行
7、连接你的CMakeLists文件,文件地址就在你项目的app目录下面
8、点击ok后你会发现你定义的类文件中的4个方法变成了红色,然后你定义的C文件中加上
#include
然后进入类文件中点击alt+enter 选择创建方法
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;
}
}
以上就是整个通讯过程。
测试结果
以上就是所有的内容。有所不足请多指教
demo: https://download.csdn.net/download/bisheng_xiong/11182069
github:https://github.com/ASheng-Bisheng/serialport-demo