??最近在做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