Android 串口设置校验位、速率、停止位等参数
Android 串口通讯设置校验位、速率、停止位等参数
最近业余时间有个android项目需要和硬件进行通信,通讯方式都是通过串口,android串口通讯google有个开源的demo 和很多人一样我也是通过下载这个demo进行开发和研究的。
google android串口通讯开源demo地址:https://code.google.com/archive/p/android-serialport-api/
串口通讯中我们可能会要设置校验位、速率、停止位等参数,但一般情况下还是不用设置的,只需要设置波特率就行。google提供的demo中就只提供一个波特率的设置其他的参数一并没有提供。
在使用google 源码的时候一定要注意 jni 当中.h和.c文件中的方法命名的规则,是java关键字+包名+类名+方法名。一开始我没注意,程序报错走了好多弯路。所以画图具体解释下。

android串口通讯说到底其实还是linux串口通讯。我们jni中的c代码其实就是操作linux中提供的串口文件。一开始并不知道这个原理,只是后来在网上找如何设置校验位、速率等问题时才明白的。
比如打开串口 fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY); 其实就是打开linux设备中串口文件。串口文件都是以ttys开头 如ttys0 等。这个fd很重要,应该就是代表当前串口文件对象。只要这里打开成功了,C代码其他地方对串口的操作以及配置都是基于这个fd来进行的。
在adb 中可以用 cd dev来查看是否有串口文件。如果看到了 ttys0等这样的文件说明目前设备上有串口否则没有找到串口设备。
adb 中通过命令向串口发送数据:echo -e "AT\r\n">/dev/ttyS0
顺便说下 如何在模拟器中使用电脑上的串口 这个只能使用android自带的模拟器。 一直想如果第三方模拟器能加载电脑的串口设备就好了。
emulator @模拟器名称 -qemu -serial COM1 // 这个命令会自动启动安卓模拟器
chmod 777 ttyS2 //提示权限 串口设备文件 ttys2
废话不多说,下面附上完整的代码。
注:代码是google的源代码+网上找的一些参考资料=现在的一个整合代码。
注意C和h代码中的数据类型,尽量以jint、jstring等来定义。(jint相当于java的int...)因为要被java调用,所以做好这样定义
.h代码(用于jni方法声明 即要在java中调用的方法声明)注意包名+类名+方法名
SerialPort.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class org_winplus_serial_utils_SerialPort */ #ifndef _Included_android_serialport_SerialPort
#define _Included_android_serialport_SerialPort
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: org_winplus_serial_utils_SerialPort
* Method: open
* Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor;
*/
JNIEXPORT jobject JNICALL Java_android_serialport_SerialPort_open
(JNIEnv *env, jclass thiz, jstring path, jint baudrate,
jint databits, jint stopbits, jchar parity);
/*
* Class: org_winplus_serial_utils_SerialPort
* Method: close
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_android_serialport_SerialPort_close
(JNIEnv *, jobject); #ifdef __cplusplus
}
#endif
#endif
.C代码(用于jni方法的具体实现代码本例中串口操作的所有代码)注意包名+类名+方法名
注意C代码中 写一个方法,一定要写到调用地方的前面。或者前面写声明;即 A要调用B方法,B方法代码必须要在A方法前面。由于平时写java或者c#这些代码是不需要这样的写法。一时切换到C代码可能容易忘记。
SerialPort.C
#include <termios.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <jni.h> #include "SerialPort.h" #include "android/log.h"
static const char *TAG = "serial_port";
#define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO, TAG, fmt, ##args)
#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args)
#define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args)
int fd;
static speed_t getBaudrate(jint baudrate)
{
switch(baudrate)
{
case :
return B0;
case :
return B50;
case :
return B75;
case :
return B110;
case :
return B134;
case :
return B150;
case :
return B200;
case :
return B300;
case :
return B600;
case :
return B1200;
case :
return B1800;
case :
return B2400;
case :
return B4800;
case :
return B9600;
case :
return B19200;
case :
return B38400;
case :
return B57600;
case :
return B115200;
case :
return B230400;
case :
return B460800;
case :
return B500000;
case :
return B576000;
case :
return B921600;
case :
return B1000000;
case :
return B1152000;
case :
return B1500000;
case :
return B2000000;
case :
return B2500000;
case :
return B3000000;
case :
return B3500000;
case :
return B4000000;
default:
return -;
}
} /** * 设置串口数据,校验位,速率,停止位 * @param nBits 类型 int数据位 取值 位7或8 * @param nEvent 类型 char 校验类型 取值N ,E, O,,S * @param mStop 类型 int 停止位 取值1 或者 2 */ int set_opt(jint nBits, jchar nEvent, jint nStop)
{ LOGE("set_opt:nBits=%d,nEvent=%c,nSpeed=%d,nStop=%d", nBits, nEvent, nStop); struct termios newtio; if(tcgetattr(fd, & newtio) != )
{ LOGE("setup serial failure"); return -; } bzero( & newtio, sizeof(newtio)); //c_cflag标志可以定义CLOCAL和CREAD,这将确保该程序不被其他端口控制和信号干扰,同时串口驱动将读取进入的数据。CLOCAL和CREAD通常总是被是能的 newtio.c_cflag |= CLOCAL | CREAD; switch(nBits) //设置数据位数
{ case : newtio.c_cflag &= ~CSIZE; newtio.c_cflag |= CS7; break; case : newtio.c_cflag &= ~CSIZE; newtio.c_cflag |= CS8; break; default: break; } switch(nEvent) //设置校验位
{ case 'O': newtio.c_cflag |= PARENB; //enable parity checking newtio.c_cflag |= PARODD; //奇校验位 newtio.c_iflag |= (INPCK | ISTRIP); break; case 'E': newtio.c_cflag |= PARENB; // newtio.c_cflag &= ~PARODD; //偶校验位 newtio.c_iflag |= (INPCK | ISTRIP); break; case 'N': newtio.c_cflag &= ~PARENB; //清除校验位 break; default: break; }
switch(nStop) //设置停止位
{ case : newtio.c_cflag &= ~CSTOPB; break; case : newtio.c_cflag |= CSTOPB; break; default: // LOGW("nStop:%d,invalid param", nStop); break; } newtio.c_cc[VTIME] = ;//设置等待时间 newtio.c_cc[VMIN] = ;//设置最小接收字符 tcflush(fd, TCIFLUSH); if(tcsetattr(fd, TCSANOW, & newtio) != )
{ LOGE("options set error"); return -; } LOGE("options set success");
return ; } /*
* Class: android_serialport_SerialPort
* Method: open
* Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor;
*/
JNIEXPORT jobject JNICALL Java_android_serialport_SerialPort_open
(JNIEnv *env, jclass thiz, jstring path, jint baudrate,
jint databits, jint stopbits, jchar parity)
{ speed_t speed;
jobject mFileDescriptor; /*波特率 */
{
speed = getBaudrate(baudrate);
if (speed == -)
{
/* TODO: throw an exception */
LOGE("Invalid baudrate");
return NULL;
}
} /* Opening device */
{
jint flags = ;
jboolean iscopy;
const char *path_utf = (*env)->GetStringUTFChars(env, path, &iscopy);
LOGD("Opening serial port %s with flags 0x%x", path_utf, O_RDWR | flags);
fd = open(path_utf, O_RDWR | O_NONBLOCK);
//fd=fd;
LOGD("open() fd = %d", fd);
(*env)->ReleaseStringUTFChars(env, path, path_utf);
if (fd == -)
{
/* Throw an exception */
LOGE("Cannot open port");
/* TODO: throw an exception */
return NULL;
}
} /* Configure device */
{
struct termios cfg;
LOGD("Configuring serial port");
if (tcgetattr(fd, &cfg))
{
LOGE("tcgetattr() failed");
close(fd);
/* TODO: throw an exception */
return NULL;
} cfmakeraw(&cfg);
//设置波特率
cfsetispeed(&cfg, speed);
cfsetospeed(&cfg, speed); if (tcsetattr(fd, TCSANOW, &cfg))
{
LOGE("tcsetattr() failed");
close(fd);
/* TODO: throw an exception */
return NULL;
} //配置校验位 停止位等等
set_opt(databits, parity, stopbits);
} /* Create a corresponding file descriptor */
{
jclass cFileDescriptor = (*env)->FindClass(env, "java/io/FileDescriptor");
jmethodID iFileDescriptor = (*env)->GetMethodID(env, cFileDescriptor, "<init>", "()V");
jfieldID descriptorID = (*env)->GetFieldID(env, cFileDescriptor, "descriptor", "I");
mFileDescriptor = (*env)->NewObject(env, cFileDescriptor, iFileDescriptor);
(*env)->SetIntField(env, mFileDescriptor, descriptorID, (jint)fd);
} return mFileDescriptor; } /*
* Class: cedric_serial_SerialPort
* Method: close
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_android_serialport_SerialPort_close
(JNIEnv *env, jobject thiz)
{
jclass SerialPortClass = (*env)->GetObjectClass(env, thiz);
jclass FileDescriptorClass = (*env)->FindClass(env, "java/io/FileDescriptor"); jfieldID mFdID = (*env)->GetFieldID(env, SerialPortClass, "mFd", "Ljava/io/FileDescriptor;");
jfieldID descriptorID = (*env)->GetFieldID(env, FileDescriptorClass, "descriptor", "I"); jobject mFd = (*env)->GetObjectField(env, thiz, mFdID);
jint descriptor = (*env)->GetIntField(env, mFd, descriptorID); LOGD("close(fd = %d)", descriptor);
close(descriptor);
}
java代码 调用jni提供的方法进行串口操作
package android.serialport; import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream; import android.util.Log; public class SerialPort {
private static final String TAG = "SerialPort"; private FileDescriptor mFd;
private FileInputStream mFileInputStream;
private FileOutputStream mFileOutputStream;
/***
* 构造方法
* @param device 串口文件
* @param baudrate 波特率
* @param dataBits 数据位
* @param stopBits 停止位
* @param parity 校验位
* @throws SecurityException
* @throws IOException
*/
public SerialPort(File device, int baudrate, int dataBits,int stopBits,char parity)
throws SecurityException, IOException { /* Check access permission */
if (!device.canRead() || !device.canWrite()) {
try {
/* Missing read/write permission, trying to chmod the file */
Process su;
su = Runtime.getRuntime().exec("/system/bin/su");
String cmd = "chmod 666 " + device.getAbsolutePath() + "\n"
+ "exit\n";
su.getOutputStream().write(cmd.getBytes());
if ((su.waitFor() != 0) || !device.canRead()
|| !device.canWrite()) {
throw new SecurityException();
}
} catch (Exception e) {
e.printStackTrace();
throw new SecurityException();
}
} mFd = open(device.getAbsolutePath(), baudrate, dataBits,stopBits,parity);
if (mFd == null) {
Log.e(TAG, "native open returns null");
throw new IOException();
}
mFileInputStream = new FileInputStream(mFd);
mFileOutputStream = new FileOutputStream(mFd);
} // Getters and setters
public InputStream getInputStream() {
return mFileInputStream;
} public OutputStream getOutputStream() {
return mFileOutputStream;
} // 调用JNI中 打开方法的声明
private native static FileDescriptor open(String path, int baudrate,
int dataBits,int stopBits,char parity); public native void close(); static {
System.loadLibrary("serial_port");
}
}
Android 串口设置校验位、速率、停止位等参数的更多相关文章
- Linux下打开串口设置
给出打开串口函数 int open_tty(char tty[]) { int fd; char tty_path[32]={0}; sprintf(tty_path,"/dev/%s&qu ...
- Linux串口设置及编程(转)
用户常见的数据通信的基本方式可分为并行通信和串行通信. 并行通信是指利用多条数据传输线将一个资料的各位同时传送.特点是传输速度快,适用于短距离通信,但要求传输速度较高的应用场合. 串行通信是指利用一条 ...
- poll函数和串口设置
2015.1.24 今天星期六,多云,早晨8:17起床的,今天是来南京起床最迟的一天,因为昨晚睡得有点迟,今天又不用上课,整个人有点放松.收拾好来到教室,教室门没有开,胡明也到了,其他人还在宿舍睡觉, ...
- Android串口通信
前段时间因为工作需要研究了一下android的串口通信,网上有很多讲串口通信的文章,我在做的时候也参考了很多文章,现在就将我学习过程中的一些心得分享给大家,希望可以帮助大家在学习的时候少走一些弯路,有 ...
- 嵌入式linux串口设置(一)
在linux中,所有的设备文件一般都位于“/dev”下,串口1和串口2对应的设备名依次为“/dev/ttyS0”, “/dev/ttyS1”,而且USB转串口的设备名通常为“/dev/ttyUSB0” ...
- Android editview 设置只能输入数字、字母和汉字
Android editview 设置只能输入数字.字母和汉字 当处理密码.昵称等特殊情况的时候,往往需要对输入内容加以限制.对于单纯的一种输入法限制可以对键盘和editview熟悉进行控制,而对于多 ...
- Android闹钟设置的解决方案
Android设置闹钟并不像IOS那样这么简单,做过Android设置闹钟的开发者都知道里面的坑有多深.下面记录一下,我解决Android闹钟设置的解决方案. 主要问题 API19开始AlarmMan ...
- Android串口开发
参考资料: https://www.jianshu.com/p/9249ed03e745 GitHUb地址: https://github.com/AIlll/AndroidSerialPort An ...
- win7下android开发环境搭建(win7 64位)
win7下android开发环境搭建(win7 64位) 一.安装 JDK 下载JDK最新版本,下载地址如下: http://www.oracle.com/technetwork/java/jav ...
随机推荐
- document.all 用法
一. document.all是页面内所有元素的一个集合.例如: document.all(0)表示页面内第一个元素 二. document.all可以判断浏览器是否是IE if ...
- vector作为参数的三种传参方式
c++中常用的vector容器作为参数时,有三种传参方式,分别如下(为说明问题,用二维vector): function1(std::vector<std::vector<int> ...
- [刷题]算法竞赛入门经典 3-10/UVa1587 3-11/UVa1588
书上具体所有题目:http://pan.baidu.com/s/1hssH0KO 题目:算法竞赛入门经典 3-10/UVa1587:Box 代码: //UVa1587 - Box #include&l ...
- NancyFx 2.0的开源框架的使用-Caching
新建一个空的Web项目,命名CachingDemo 然后添加三个Nuget安装包 Nancy Nancy.Hosting.Aspnet Nancy.ViewsEngines.Razor 然后往项目里面 ...
- 进制转换,杭电0j-2031
进制转换,杭电0j-2031原题地址:http://acm.hdu.edu.cn/showproblem.php?pid=2031 [Problem Description] 输入一个十进制数N,将它 ...
- 主java程序猿知识体系结构
zuoxiaolong博客园<浅谈程序猿书箱的选择,你会如何选择自己的爱书呢>一文,链接如下:http://www.cnblogs.com/zuoxiaolong/p/life19.htm ...
- 让xcode8支持7.0的设备
升级到xcode8之后发现不能支持7.0设备 1 . 下载文件将文件覆盖到 /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS. ...
- angularjs里重要的route
写一段代码来解释吧! <!DOCTYPE html><html ng-app="mainApp"><head lang="en"& ...
- Some 3D Graphics (rgl) for Classification with Splines and Logistic Regression (from The Elements of Statistical Learning)(转)
This semester I'm teaching from Hastie, Tibshirani, and Friedman's book, The Elements of Statistical ...
- 深度解析PHP数组函数array_slice
看到array_slice()这个函数让我想起了VFP中的range这个范围取值的子句 这个函数一共有四个参数: 被取值的数组(必需) 取值的起始位置(必需) 取值的终止位置,如果不填写默认到数组最后 ...