android4.0下serial port给应用操作完成特殊定制

来源:互联网 发布:淘宝店申请步骤 编辑:程序博客网 时间:2024/05/21 06:51

android4.0下serial port给应用操作完成特殊定制

        我们在开发中,串口也就是serialport或者叫uart用的是相当频繁的,很普通的接口了,今天为什么在这提出来呢?笔者前年完成了一款android4.0平台的车载平板产品,客户外接了一个DTV,我们在android这边通过GPIO模拟IR来控制DTV盒子的。客户前面也做了特殊的一些应用,可以通过wifi跟服务器连。服务器通过wifi网络发送控制命令给车载平板机器,但是客户反馈在wifi 在heavy WiFi trafic / interferences时,丢包率很高,达到50%以上,造成很多控制命令丢失。

/*****************************************************************************************************/
声明:本博内容均由http://blog.csdn.net/edsam49原创,转载请注明出处,谢谢!
/*****************************************************************************************************/

        其实通过网络传输,在网络堵塞的时候,丢包率高这是很正常的问题,怎么不能通过控制好发送命令再确认ack,如果约定时间内没收到就继续重发呢?不知道老外怎么想的。由于给他做的控制DTV的GPIO模拟IR接口很好用,很稳定,因此客户想通过串口去控制DTV,同时有些控制信息就走DVB网络,应该没那么堵塞,再通过DTV盒子通过串口回传给android车载平台机器,这样也确实是可行。由于目前产品早已经成形,客户也不想把通过串口的数据让我们来解析,所以我们就只要提供一条操作串口的库给上层就可以了,这样相对对我们来说也简单了,只要打通串口,上层能收发串口数据就可以了。

        从android4.1起,android系统提供了操作串口的service,但是笔者以前是基于android4.0的,怎么办呢?参考android4.1以后的系统操作控制方式,主要思路还是提供一个操作串口的文件句柄给应用上层使用。但是笔者觉得不大好的,android提供的serial service没有提供关闭串口的接口,这样如果频繁关闭打开的话会存在很多句柄没关闭的情况。当然,如果只在service里面打开的话也没问题,如果在上层打开的话可能就会存在问题。下面笔者就带你一起游历这个过程吧!

       第一步,肯定是要搞定驱动。串口驱动目前来说,每个平台基本都是做好了,直接做好配置打开就可以了,笔者使用的全志平台,配置如下:

;----------------------------------------------------------------------------------;uart configuration;uart_type ---  2 (2 wire), 4 (4 wire), 8 (8 wire, full function);----------------------------------------------------------------------------------[uart_para0]uart_used                = 1uart_port                = 0uart_type                = 2uart_tx                  =port:PB22<2><1><default><default>uart_rx                  =port:PB23<2><1><default><default>

     第二步,确认串口设备文件的权限,需要有system的权限,所以笔者把它配置成666了。笔者使用的是打印串口做的测试,因此是ttyS0设备文件。

 

     第三步,写一个JNI文件,提供一个库给应用调用了,因为我们不可能给他做一个系统service来使用了,以最小的代价来搞定,这样的话使用NDK也好,可以直接用一个apk就可以完成了。在这个JNI文件里,笔者提供了两个接口,一个open,一个close,刚好形成最小闭环循环。代码也不难,涉及一点JNI基本知识,如下:

extern "C" JNIEXPORT jobject JNICALL Java_com_jeavox_ttys_utils_SerialPort_open  (JNIEnv *env, jobject thiz, jstring path, jint baudrate, jint flags){        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(path, NULL);                LOGD("Opening serial port %s with flags 0x%x", path_utf, O_RDWR | flags);                fd = open(path_utf, O_RDWR | flags);                env->ReleaseStringUTFChars(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("java/io/FileDescriptor");                jmethodID iFileDescriptor = env->GetMethodID( cFileDescriptor, "<init>", "()V");                jfieldID descriptorID = env->GetFieldID( cFileDescriptor, "descriptor", "I");                mFileDescriptor = env->NewObject( cFileDescriptor, iFileDescriptor);                env->SetIntField( mFileDescriptor, descriptorID, (jint)fd);        }        return mFileDescriptor;}
extern "C" JNIEXPORT void JNICALL Java_com_jeavox_ttys_utils_SerialPort_close  (JNIEnv *env, jobject thiz){        jclass SerialPortClass = env->GetObjectClass(thiz);        jclass FileDescriptorClass = env->FindClass("java/io/FileDescriptor");        jfieldID mFdID = env->GetFieldID( SerialPortClass, "mFd", "Ljava/io/FileDescriptor;");        jfieldID descriptorID = env->GetFieldID( FileDescriptorClass, "descriptor", "I");        jobject mFd = env->GetObjectField( thiz, mFdID);        jint descriptor = env->GetIntField( mFd, descriptorID);        close(descriptor);}

     第四步,到应用层去接应JNI库,这里很重要的是使用了FileInputStream跟FileOutputStream。在FileOutputStream使用上有一点要注意,就是目前FileOutputStream提供的三个read接口都是阻塞的,会一直在那等数据,但是另外还提供了一个available接口,这个接口是用来提示有多少数据现在可以读取,这样我们去读的时候,就有目的性了,挺好的。

private FileDescriptor mFd;private FileInputStream mFileInputStream;private FileOutputStream mFileOutputStream;public SerialPort(File device, int baudrate, int flags) throws SecurityException, IOException {mFd = open(device.getAbsolutePath(), baudrate, flags);if (mFd == null) {Log.e(TAG, "native open returns null");throw new IOException();}mFileInputStream = new FileInputStream(mFd);mFileOutputStream = new FileOutputStream(mFd);}// Getters and setterspublic InputStream getInputStream() {return mFileInputStream;}
    也是在一个thread里面去读数据的,

private class ReadThread extends Thread {@Overridepublic void run() {super.run();while (!isInterrupted()) {int size;try {byte[] buffer = new byte[64];if (mInputStream == null)return;if(mInputStream.available() > 0 ){    size = mInputStream.read(buffer);    } else{    continue;    }if (size > 0) {onDataReceived(buffer, size);}} catch (IOException e) {e.printStackTrace();return;}}}}

     第五步,当然是起应用去call相应接口了。

        mSerialPort = new SerialPort(new File("/dev/ttyS0"), 115200, 0);
             这边调用,也就是设置好设备文件已经波特率,其他flag先不传了。其他应用代码未贴,基本的界面都不难搞的。

     第六步,就看看应用的效果吧!我从串口输入了一些字符,在应用上读出来显示出来。

 

      整个过程还是不算难搞的,整个过程参考了网上一些大侠的代码,参考了android4.4的代码实现,同时得到了做应用的同事协助完成整个调试。在此看透这个问题,android4.1以后的serialservice只不过是把它写成一个系统service的形式供应用使用。总之,算交差了,通道建好了,客户自己爱怎么折腾就怎么折腾吧!

 

2 0
原创粉丝点击