美文网首页程序员
Android UART 串口通信

Android UART 串口通信

作者: braincs | 来源:发表于2019-03-10 11:12 被阅读0次

    1. 需求

    最近有项目需要实现windows机器和Android开发版进行UART串口通信,经过3天查找尝试,特记录一下最终方案,希望之后的同行少走弯路,最后在git上回开源我最终的方案希望大家支持。

    2. 环境

    Android 3.0.1
    Gradle 4.1
    ARM开发版 : RK3399
    PC机器:Win10
    开发机器:MAC 10.13.3

    3. 解决方法

    1. Android Things

      Android Things 谷歌于2018年5月发布1.0正式版,让开发者可以使用Android开发工具开发嵌入式设备。需要比较高的Android API支持 ( >24 )。很多树莓派3用这个做开发,普遍应用于物联网领域。

      因为是官方的原因,感觉应该靠谱些,且集成方便,Git上参考了https://github.com/androidthings/sample-uartloopback这个项目。但是,每个卵用。简直白折腾。首先需要API 27以上的设备,现在手上的开发版很少有Android 8.0以上版本的。(为此,还刷了固件,做了尝试)最大的坑是:这个项目最后基本安装不到国内的Android 系统上,原因:需要Google Service。强依赖<uses-library android:name="com.google.android.things"/>。考虑日后上线产品的维护,果断放弃。

    2. android-serialport-api

      android-serialport-api,是两个Eclipse项目,导入进去之后,设置好设备和波特律之后直接就可以使用,也看到别人分享的项目经验,这个可用。具体讲解可以参考这篇文章https://blog.csdn.net/qiwenmingshiwo/article/details/49557889,两个项目分别是编译底层JNI项目android-sercd,另一个侧重根据JNI的API实现Java端功能。如果只实现功能就看android-serialport-api

      缺点:项目比较老,需要JNI编译

      经过尝试导入库中已编译好的so,运行可以跑通UART通信。但遇到了一下几个‘坑’ 导入JNI的时候注意事项就不说了,包名这些的都不能改。

      • Run 使用已有的so的项目时候会报错 dlopen failed: "has text relocations"

      • 遇到无法获取port,请重新配置Serial port类似的报错

      解决方法:

      1. 关于dlopen failed: "has text relocations" 的简单解决办法,降级targetSdkVersion 23一下。Android 6.0 机制的问题。根本解决版本使用高版本的NDK重新编译so来解决,后文中我重新编译了一把。这里是gradle文件修改的关键地方。

        compileSdkVersion 22
        defaultConfig {
             applicationId "com.attrsc.braincs.androidserialport"
             minSdkVersion 21
             targetSdkVersion 22
             versionCode 1
             versionName "1.0"
             testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        
             ndk {
                 abiFilters "armeabi-v7a"
             }
         }
        
      2. 关于无法获取port的报错

        一开始我怀疑是我的so没有加载成功,后来断点发现无法从sharePreference中获取到设置的 Device和baudrates。修改SerialPortPreferencesActivity如下:

         public class SerialPortPreferences extends PreferenceActivity {
        
             private Application mApplication;
             private SerialPortFinder mSerialPortFinder;
        
             @Override
             protected void onCreate(Bundle savedInstanceState) {
                 super.onCreate(savedInstanceState);
        
                 mApplication = (Application) getApplication();
                 mSerialPortFinder = mApplication.mSerialPortFinder;
        
                 addPreferencesFromResource(R.xml.serial_port_preferences);
                 final SharedPreferences sp = getSharedPreferences("android_serialport_api.sample_preferences", MODE_PRIVATE);
        
                 // Devices
                 final ListPreference devices = (ListPreference)findPreference("DEVICE");
                 String[] entries = mSerialPortFinder.getAllDevices();
                 String[] entryValues = mSerialPortFinder.getAllDevicesPath();
                 devices.setEntries(entries);
                 devices.setEntryValues(entryValues);
                 devices.setSummary(devices.getValue());
                 devices.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
                       public boolean onPreferenceChange(Preference preference, Object newValue) {
                         preference.setSummary((String)newValue);
                         sp.edit().putString("DEVICE",(String) newValue).apply();//此处添加
                         return true;
                     }
                 });
        
                 // Baud rates
                 final ListPreference baudrates = (ListPreference)findPreference("BAUDRATE");
                 baudrates.setSummary(baudrates.getValue());
                 baudrates.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
                     public boolean onPreferenceChange(Preference preference, Object newValue) {
                         preference.setSummary((String)newValue);
                         sp.edit().putString("BAUDRATE",(String) newValue).apply();//此处添加
                         return true;
                     }
                 });
            }
         }
        
         ​```  
        
        

    4. 终极解决方案

    先上图

    android_menu.png android_talk.png pc-comm.png pc-receive.png

    由于android-serialport-api项目中的so使用较old的ndk编译,所以在对于Android 6.0 以上版本兼容的时候会报错dlopen failed: "has text relocations"。且使用的mk进行编译,特升级为用cmake编译。

    升级android-serialport-api

    • ndk 17.0.4xxx jni编译

    • cmake 编译链

    • EClipse项目-> Android Studio项目

    项目结构:

    .
    ├── AndroidSerialLibrary.iml
    ├── androidserial
    │   ├── CMakeLists.txt
    │   ├── androidserial.iml
    │   ├── build
    │   ├── build.gradle
    │   ├── libs
    │   ├── proguard-rules.pro
    │   └── src
    ├── app
    │   ├── app.iml
    │   ├── build
    │   ├── build.gradle
    │   ├── libs
    │   ├── proguard-rules.pro
    │   └── src
    ├── build
    │   └── android-profile
    ├── build.gradle
    ├── gradle
    │   └── wrapper
    ├── gradle.properties
    ├── gradlew
    ├── gradlew.bat
    ├── local.properties
    └── settings.gradle
    

    app对应原项目中的各个Activity, androidserial 是module 对应编译之前的so,还有API的封装。可以直接引用androidserial,调用方法参考app目录下的activity。

    注意关于权限!

    当接入开发板后如果发现 Error You do not have read/write permission to the serial port 需要root 权限,在开发者模式中开启root 权限 adb和应用

    使用一下命令开启Android对串口的读写权限

    ❯ adb shell 
     rk3399_firefly_mid:/ $ su
     rk3399_firefly_mid:/ # chmod 777 /dev/ttyS4
     rk3399_firefly_mid:/ # setenforce 0 
     rk3399_firefly_mid:/ # 
    

    setenforce 0: 关闭防火墙,有人说关键是这,但是我的环境不用关闭,只要给权限就可以

    注意关于ttyS1 - 6 ttyS1 - 6 对应的是 UART 串口1-6 一般都是一一对应的。这个具体要看一下开发板的说明。

    记录的比较糙,还请见谅,如有问题请留言,我看到后肯定回复。项目主要看结构,剩下的都是复制黏贴的事。git地址:https://github.com/braincs/AndroidSerialLibrary

    相关文章

      网友评论

        本文标题:Android UART 串口通信

        本文链接:https://www.haomeiwen.com/subject/iqkupqtx.html