找到一篇好文分享一下这个博主写个很用心,赞一个。
最近在做android串口的开发,找到一个开源的串口类android-serialport-api。其主页在这里http://code.google.com/p/android-serialport-api/ ,这里可以下到APK及对源码。
但是下载源码之后发现源码不能直接使用,而且源码结构较为复杂。关于串口的操作不外乎几步:
1.打开串口(及配置串口);
2.读串口;
3.写串口;
4.关闭串口。
android-serialport-api的代码使用了继承等复杂的行为,不容易使初学者很快掌握关于串口的上述4步,所以我特别自己写了一个demo,只有一个activity,其中包含了打开串口,写串口,读串口的操作,对于关闭串口,大家一开就会不明白怎么写了。
这篇文章主要参考http://blog.csdn.net/tangcheng_ok/article/details/7021470
还有http://blog.csdn.net/jerome_home/article/details/8452305
下面言归正传:
第一:
说道android 串口,就不得不提JNI技术,它使得java中可以调用c语言写成的库。为可在android中使用串口,android-serialport-api的作者自己写了一个c语言的动态链接库serial_port.so(自动命名成libserial_port.so),并把它放在了libs/aemeabi 里,其c源文件在JNI中,大家在下载了android-serialport-api的源代码后,将这两个文件夹copy到自己新建的工程中即可。
第二:
然后将调用c语言写成的动态链接库的java类放入到src文件夹下的android.serialport包下,这里一定要将包名命名成这个,因为对JNI有一定了解的人就会知道,在写c语言链接库时候,函数的命名是和调用它的类所在的包名相关的,一旦包名与链接库中函数的命名不相符,就不能调用链接库的函数。这里可以打开jni中的.c文件(他就是动态链接库的源文件),可以看到源码:
-
/*
-
* Copyright 2009 Cedric Priscal
-
*
-
* Licensed under the Apache License, Version 2.0 (the "License");
-
* you may not use this file except in compliance with the License.
-
* You may obtain a copy of the License at
-
*
-
* http://www.apache.org/licenses/LICENSE-2.0
-
*
-
* Unless required by applicable law or agreed to in writing, software
-
* distributed under the License is distributed on an "AS IS" BASIS,
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-
* See the License for the specific language governing permissions and
-
* limitations under the License.
-
*/
-
-
#include <termios.h>
-
#include <unistd.h>
-
#include <sys/types.h>
-
#include <sys/stat.h>
-
#include <fcntl.h>
-
#include <string.h>
-
#include <jni.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)
-
-
static speed_t getBaudrate(jint baudrate)
-
{
-
switch(baudrate) {
-
case
0:
return B0;
-
case
50:
return B50;
-
case
75:
return B75;
-
case
110:
return B110;
-
case
134:
return B134;
-
case
150:
return B150;
-
case
200:
return B200;
-
case
300:
return B300;
-
case
600:
return B600;
-
case
1200:
return B1200;
-
case
1800:
return B1800;
-
case
2400:
return B2400;
-
case
4800:
return B4800;
-
case
9600:
return B9600;
-
case
19200:
return B19200;
-
case
38400:
return B38400;
-
case
57600:
return B57600;
-
case
115200:
return B115200;
-
case
230400:
return B230400;
-
case
460800:
return B460800;
-
case
500000:
return B500000;
-
case
576000:
return B576000;
-
case
921600:
return B921600;
-
case
1000000:
return B1000000;
-
case
1152000:
return B1152000;
-
case
1500000:
return B1500000;
-
case
2000000:
return B2000000;
-
case
2500000:
return B2500000;
-
case
3000000:
return B3000000;
-
case
3500000:
return B3500000;
-
case
4000000:
return B4000000;
-
default:
return
-1;
-
}
-
}
-
-
/*
-
* Class: cedric_serial_SerialPort
-
* Method: open
-
* Signature: (Ljava/lang/String;)V
-
*/
-
JNIEXPORT jobject JNICALL Java_android_serialport_SerialPort_open
-
(JNIEnv *env, jobject thiz, jstring path, jint baudrate)
-
{
-
int fd;
-
speed_t speed;
-
jobject mFileDescriptor;
-
-
/* Check arguments */
-
{
-
speed = getBaudrate(baudrate);
-
if (speed ==
-1) {
-
/* TODO: throw an exception */
-
LOGE(
"Invalid baudrate");
-
return
NULL;
-
}
-
}
-
-
/* Opening device */
-
{
-
jboolean iscopy;
-
const
char *path_utf = (*env)->GetStringUTFChars(env, path, &iscopy);
-
LOGD(
"Opening serial port %s", path_utf);
-
fd = open(path_utf, O_RDWR | O_DIRECT | O_SYNC);
-
LOGD(
"open() fd = %d", fd);
-
(*env)->ReleaseStringUTFChars(env, path, path_utf);
-
if (fd ==
-1)
-
{
-
/* 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;
-
}
-
}
-
-
/* 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);
-
}
可以看到,函数的命名规则直接和包名有关。
第三:
android.serialport包下,有两个类,分别是SerialPort.java 和SerialPortFinder.java。
其中,SerialPort.java,这个类主要用来加载SO文件,通过JNI的方式打开关闭串口。
-
/*
-
* Copyright 2009 Cedric Priscal
-
*
-
* Licensed under the Apache License, Version 2.0 (the "License");
-
* you may not use this file except in compliance with the License.
-
* You may obtain a copy of the License at
-
*
-
* http://www.apache.org/licenses/LICENSE-2.0
-
*
-
* Unless required by applicable law or agreed to in writing, software
-
* distributed under the License is distributed on an "AS IS" BASIS,
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-
* See the License for the specific language governing permissions and
-
* limitations under the License.
-
*/
-
-
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";
-
-
/*
-
* Do not remove or rename the field mFd: it is used by native method close();
-
*/
-
private FileDescriptor mFd;
-
private FileInputStream mFileInputStream;
-
private FileOutputStream mFileOutputStream;
-
-
public SerialPort(File device, int baudrate) 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 777 " + device.getAbsolutePath() +
"\n"
-
+
"exit\n";
-
/*String cmd = "chmod 777 /dev/s3c_serial0" + "\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);
-
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);
-
public native void close();
-
static {
-
System.loadLibrary(
"serial_port");
-
}
-
}
可以看到System.loadLibrary("serial_port");一句,这一句就是用来加载动态链接库。我们的串口操作就是要给予这个类来实现。
含有一个类SerialPortFinder.java,这个类是用来找到系统中可以用的串口的,如果你知道的android设备有什么串口,就不必使用这个类来查找串口了,一次简化我们的demo。
第四:加入我们自己的Activity类
为了方便我记在android.serialport包下加入了我自己的MyserialActivity.java,大家从上面的图中也可以看见。
代码如下:
-
package android.serialport;
-
-
-
import java.io.File;
-
import java.io.FileInputStream;
-
import java.io.FileOutputStream;
-
import java.io.IOException;
-
-
import android.app.Activity;
-
-
import android.os.Bundle;
-
-
-
-
//import android.serialport.sample.R;
-
import android.serialport.R;
-
-
import android.view.View;
-
import android.widget.Button;
-
import android.widget.EditText;
-
import android.widget.Toast;
-
-
public
class MyserialActivity extends Activity {
-
/** Called when the activity is first created. */
-
-
-
EditText mReception;
-
FileOutputStream mOutputStream;
-
FileInputStream mInputStream;
-
SerialPort sp;
-
-
@Override
-
-
public void onCreate(Bundle savedInstanceState) {
-
super.onCreate(savedInstanceState);
-
setContentView(R.layout.main);
-
-
-
final Button buttonSetup = (Button)findViewById(R.id.ButtonSetup);
-
buttonSetup.setOnClickListener(
new View.OnClickListener() {
-
public void onClick(View v) {
-
mReception = (EditText) findViewById(R.id.EditTextRec);
-
-
try {
-
sp=
new SerialPort(
new File(
"/dev/ttyS2"),
9600);
-
}
catch (SecurityException e) {
-
// TODO Auto-generated catch block
-
e.printStackTrace();
-
}
catch (IOException e) {
-
// TODO Auto-generated catch block
-
e.printStackTrace();
-
}
-
-
-
mOutputStream=(FileOutputStream) sp.getOutputStream();
-
mInputStream=(FileInputStream) sp.getInputStream();
-
-
Toast.makeText(getApplicationContext(),
"open",
-
Toast.LENGTH_SHORT).show();
-
-
}
-
});
-
-
-
-
final Button buttonsend= (Button)findViewById(R.id.ButtonSent1);
-
buttonsend.setOnClickListener(
new View.OnClickListener() {
-
public void onClick(View v) {
-
-
try {
-
mOutputStream.write(
new String(
"send").getBytes());
-
mOutputStream.write(
'\n');
-
}
catch (IOException e) {
-
e.printStackTrace();
-
}
-
-
-
Toast.makeText(getApplicationContext(),
"send",
-
Toast.LENGTH_SHORT).show();
-
-
}
-
});
-
-
-
final Button buttonrec= (Button)findViewById(R.id.ButtonRec);
-
buttonrec.setOnClickListener(
new View.OnClickListener() {
-
public void onClick(View v) {
-
int size;
-
-
try {
-
byte[] buffer =
new
byte[
64];
-
if (mInputStream ==
null)
return;
-
size = mInputStream.read(buffer);
-
if (size >
0) {
-
onDataReceived(buffer, size);
-
-
}
-
}
catch (IOException e) {
-
e.printStackTrace();
-
return;
-
}
-
-
}
-
});
-
}
-
void onDataReceived(final byte[] buffer, final int size) {
-
runOnUiThread(
new Runnable() {
-
public void run() {
-
if (mReception !=
null) {
-
mReception.append(
new String(buffer,
0, size));
-
}
-
}
-
});
-
}
-
-
-
}
可以看见,功能比较简单,只有三个按钮,分别用来打开串口(buttonsetup),写串口(buttonsend),读串口(buttonrec),一个文本框用来显示串口接收到的信息。功能已经简化到了最简。
下面先说说在模拟器中使用串口的方法:
应先使用-serial选项打开你的模拟器,如图(修改你模拟器的名字)
然后进入adb shell
cd /dev
chmod 777 ttyS2
运行后结果:
相比大家都懂得,我们的串口就是ttyS2,使用chmod命令来获取对它的操作,否则之后你的应用可能没有串口的操作权限。
然后运行程序:
其中Console就是打开串口(原谅我偷懒,忘改名字了)。
你可以把你的电脑的COM1连接到另一台电脑的串口上,并在那台电脑上打开你的串口助手之类的软件,配置好串口(参数不难从源代码里看出来)。按下模拟器中的send键,就能在那台电脑的串口助手中看到:
同样,从那台电脑向这台电脑发送数据也可以显示
至此,这个小demo就完毕了。
我的源码在这里: http://download.csdn.net/detail/akunainiannian/5202173