正常Android设备的串口一般是用作debug调试使用,随着Android设备使用越来越广,比如智能pos、智能扫码机都会用到Android主板和单片机进行通信,如果Android主板和单片机通信数据量大可以使用USB,若是通信数据量小可以使用串口进行通信,因为串口通信简单并且稳定性高,最近做的一个项目就是Android使用串口和加密芯片进行通信,Android主板使用的是rk3288和rk3368. 在rk3288源码中集成了一个串口demo,目录在

rk3288/frameworks/base/tests/SerialChat

网上存在一个demo是通过 java->jni->Serial驱动,虽然也可以正常读写,这样做缺点是
1、如果突然串口不能正常通信可能会导致app崩溃,
2、这个串口只能被当前的app使用,其他app 不能使用。
3、违背正常Android设计初衷,Android标准流程是
java->service->jni->hal->serial驱动。
所以我在项目中使用了Android源码中的demo

public class SerialChat extends Activity implements Runnable, TextView.OnEditorActionListener {    private static final String TAG = "SerialChat";    private TextView mLog;    private EditText mEditText;    private ByteBuffer mInputBuffer;    private ByteBuffer mOutputBuffer;    private SerialManager mSerialManager;    private SerialPort mSerialPort;    private boolean mPermissionRequestPending;    private static final int MESSAGE_LOG = 1;    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        mSerialManager = (SerialManager)getSystemService(Context.SERIAL_SERVICE);        setContentView(R.layout.serial_chat);        mLog = (TextView)findViewById(R.id.log);        mEditText = (EditText)findViewById(R.id.message);        mEditText.setOnEditorActionListener(this);        if (false) {            mInputBuffer = ByteBuffer.allocateDirect(1024);            mOutputBuffer = ByteBuffer.allocateDirect(1024);        } else {            mInputBuffer = ByteBuffer.allocate(1024);            mOutputBuffer = ByteBuffer.allocate(1024);        }    }    @Override    public void onResume() {        super.onResume();        String[] ports = mSerialManager.getSerialPorts();for(int 1 = 0;i < ports.length;i++ ){Log.e("PPTV", "ports is ====" +  ports[i] );}        if (ports != null && ports.length > 0) {            try {                mSerialPort = mSerialManager.openSerialPort(ports[1], 115200);                if (mSerialPort != null) {                    new Thread(this).start();                }            } catch (IOException e) {            }        }    }    @Override    public void onPause() {        super.onPause();        }    @Override    public void onDestroy() {        if (mSerialPort != null) {            try {                mSerialPort.close();            } catch (IOException e) {            }            mSerialPort = null;        }        super.onDestroy();    }    public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {        if (/* actionId == EditorInfo.IME_ACTION_DONE && */ mSerialPort != null) {            try {                String text = v.getText().toString();                Log.d(TAG, "write: " + text);                byte[] bytes = text.getBytes();                mOutputBuffer.clear();                mOutputBuffer.put(bytes);                mSerialPort.write(mOutputBuffer, bytes.length);            } catch (IOException e) {                Log.e(TAG, "write failed", e);            }            v.setText("");            return true;        }        Log.d(TAG, "onEditorAction " + actionId + " event: " + event);        return false;    }    public void run() {        Log.d(TAG, "run");        int ret = 0;        byte[] buffer = new byte[1024];        while (ret >= 0) {            try {                Log.d(TAG, "calling read");                mInputBuffer.clear();                ret = mSerialPort.read(mInputBuffer);                Log.d(TAG, "read returned " + ret);                mInputBuffer.get(buffer, 0, ret);            } catch (IOException e) {                Log.e(TAG, "read failed", e);                break;            }            if (ret > 0) {                Message m = Message.obtain(mHandler, MESSAGE_LOG);                String text = new String(buffer, 0, ret);                Log.d(TAG, "chat: " + text);                m.obj = text;                mHandler.sendMessage(m);            }        }        Log.d(TAG, "thread out");    }   Handler mHandler = new Handler() {        @Override        public void handleMessage(Message msg) {            switch (msg.what) {                case MESSAGE_LOG:                    mLog.setText(mLog.getText() + (String)msg.obj);                    break;             }        }    };}

首先是获取串口服务

 mSerialManager = (SerialManager)getSystemService(Context.SERIAL_SERVICE);

如果要想通过getSystemService获取serial服务,首先要在
core/java/android/content/Context.java:
添加

public static final String SERIAL_SERVICE = "serial";

在ContextImpl.java 中注册服务

    registerService(SERIAL_SERVICE, new ServiceFetcher() {                public Object createService(ContextImpl ctx) {                    IBinder b = ServiceManager.getService(SERIAL_SERVICE);                    return new SerialManager(ctx, ISerialManager.Stub.asInterface(b));                }});

每次开机会自动启动。可以通过命令查看当前服务是否启动

adb shell service list

Android串口Serial服务解析_第1张图片
可以看到服务已经启动。
目前启动的服务Java层面的服务,只是对我们C语言串口服务做了一层封装,真正工作的是我们native serial(SerialService).
所以registerService 函数中会获取SerialService的代理端,

 IBinder b = ServiceManager.getService(SERIAL_SERVICE);                    return new SerialManager(ctx, ISerialManager.Stub.asInterface(b));

SerialService 注册在SystemServer.java 中

                try {                    Slog.i(TAG, "Serial Service");                    // Serial port support                    serial = new SerialService(context);                    ServiceManager.addService(Context.SERIAL_SERVICE, serial);                } catch (Throwable e) {                    Slog.e(TAG, "Failure starting SerialService", e);                }            }

SerialService.java 源码

public class SerialService extends ISerialManager.Stub {    private final Context mContext;    private final String[] mSerialPorts;    public SerialService(Context context) {        mContext = context;        mSerialPorts = context.getResources().getStringArray(                com.android.internal.R.array.config_serialPorts);    }    public String[] getSerialPorts() {        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SERIAL_PORT, null);        ArrayList ports = new ArrayList();        for (int i = 0; i < mSerialPorts.length; i++) {            String path = mSerialPorts[i];            if (new File(path).exists()) {                ports.add(path);            }        }        String[] result = new String[ports.size()];        ports.toArray(result);        return result;    }    public ParcelFileDescriptor openSerialPort(String path) {        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SERIAL_PORT, null);        for (int i = 0; i < mSerialPorts.length; i++) {            if (mSerialPorts[i].equals(path)) {                return native_open(path);            }        }        throw new IllegalArgumentException("Invalid serial port " + path);    }    private native ParcelFileDescriptor native_open(String path);}
 mContext = context;        mSerialPorts = context.getResources().getStringArray(                com.android.internal.R.array.config_serialPorts);

会获取目前存在可以使用串口列表。这个根据自己需要添加。
我的就使用一个串口3

      "/dev/ttyS3"    

回到SerialChat.java

String[] ports = mSerialManager.getSerialPorts(); 

结果就是/dev/ttyS3

mSerialPort = mSerialManager.openSerialPort(ports[1], 115200);

打开串口3

public SerialPort openSerialPort(String name, int speed) throws IOException {        try {            ParcelFileDescriptor pfd = mService.openSerialPort(name);//打开串口            if (pfd != null) {                SerialPort port = new SerialPort(name);                port.open(pfd, speed);//设置波特率及其他属性                return port;            } else {                throw new IOException("Could not open serial port " + name);            }        } catch (RemoteException e) {            Log.e(TAG, "exception in UsbManager.openDevice", e);        }        return null;    }

结果会调用SerialService.openSerialPort(name)

 public ParcelFileDescriptor openSerialPort(String path) {        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SERIAL_PORT, null);        for (int i = 0; i < mSerialPorts.length; i++) {            if (mSerialPorts[i].equals(path)) {                return native_open(path);            }        }        throw new IllegalArgumentException("Invalid serial port " + path);    }

最终打开调用native_open 通过jni方式打开串口3。

static jobject android_server_SerialService_open(JNIEnv *env, jobject thiz, jstring path){    const char *pathStr = env->GetStringUTFChars(path, NULL);    int fd = open(pathStr, O_RDWR | O_NOCTTY);    if (fd < 0) {        ALOGE("could not open %s", pathStr);        env->ReleaseStringUTFChars(path, pathStr);        return NULL;    }    env->ReleaseStringUTFChars(path, pathStr);    jobject fileDescriptor = jniCreateFileDescriptor(env, fd);    if (fileDescriptor == NULL) {        return NULL;    }    return env->NewObject(gParcelFileDescriptorOffsets.mClass,        gParcelFileDescriptorOffsets.mConstructor, fileDescriptor);}

阻塞的方式打开串口

设置串口波特率已经属性

android_hardware_SerialPort.cpp

static voidandroid_hardware_SerialPort_open(JNIEnv *env, jobject thiz, jobject fileDescriptor, jint speed){    switch (speed) {        case 50:            speed = B50;            break;        case 75:            speed = B75;            break;        case 110:            speed = B110;            break;        case 134:            speed = B134;            break;        case 150:            speed = B150;            break;        case 200:            speed = B200;            break;        case 300:            speed = B300;            break;        case 600:            speed = B600;            break;        case 1200:            speed = B1200;            break;        case 1800:            speed = B1800;            break;        case 2400:            speed = B2400;            break;        case 4800:            speed = B4800;            break;        case 9600:            speed = B9600;            break;        case 19200:            speed = B19200;            break;        case 38400:            speed = B38400;            break;        case 57600:            speed = B57600;            break;        case 115200:            speed = B115200;            break;        case 230400:            speed = B230400;            break;        case 460800:            speed = B460800;            break;        case 500000:            speed = B500000;            break;        case 576000:            speed = B576000;            break;        case 921600:            speed = B921600;            break;        case 1000000:            speed = B1000000;            break;        case 1152000:            speed = B1152000;            break;        case 1500000:            speed = B1500000;            break;        case 2000000:            speed = B2000000;            break;        case 2500000:            speed = B2500000;            break;        case 3000000:            speed = B3000000;            break;        case 3500000:            speed = B3500000;            break;        case 4000000:            speed = B4000000;            break;        default:            jniThrowException(env, "java/lang/IllegalArgumentException",                              "Unsupported serial port speed");            return;    }    int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);    // duplicate the file descriptor, since ParcelFileDescriptor will eventually close its copy    fd = dup(fd);    if (fd < 0) {        jniThrowException(env, "java/io/IOException", "Could not open serial port");        return;    }    env->SetIntField(thiz, field_context, fd);    struct termios tio;    if (tcgetattr(fd, &tio))        memset(&tio, 0, sizeof(tio));    tio.c_cflag =  speed | CS8 | CLOCAL | CREAD;    // Disable output processing, including messing with end-of-line characters.    tio.c_oflag &= ~OPOST;    tio.c_iflag = IGNPAR;// 忽略奇偶校验错误    tio.c_lflag = 0; /* turn of CANON, ECHO*, etc */    /* no timeout but request at least one character per read */    tio.c_cc[VTIME] = 0;//如果读取不到就一直等待    tio.c_cc[VMIN] = 1;//读取一个byte就返回    tcsetattr(fd, TCSANOW, &tio);    tcflush(fd, TCIOFLUSH);}

到这里open 串口完成
串口写很简单。就不解析了。重点解析read ,由于我们是block 方式打开的串口,所以如果没有数据流返回,串口会一直阻塞

 public void run() {        Log.d(TAG, "run");        int ret = 0;        byte[] buffer = new byte[1024];        while (ret >= 0) {            try {                Log.d(TAG, "calling read");                mInputBuffer.clear();                ret = mSerialPort.read(mInputBuffer);                Log.d(TAG, "read returned " + ret);                mInputBuffer.get(buffer, 0, ret);            } catch (IOException e) {                Log.e(TAG, "read failed", e);                break;            }            if (ret > 0) {                Message m = Message.obtain(mHandler, MESSAGE_LOG);                String text = new String(buffer, 0, ret);                Log.d(TAG, "chat: " + text);                m.obj = text;                mHandler.sendMessage(m);            }        }        Log.d(TAG, "thread out");    }   Handler mHandler = new Handler() {        @Override        public void handleMessage(Message msg) {            switch (msg.what) {                case MESSAGE_LOG:                    mLog.setText(mLog.getText() + (String)msg.obj);                    break;             }        }    };}

开启一个thread方式read 串口数据,
无数据会一直阻塞在ret = mSerialPort.read(mInputBuffer);
另外注意 的是读写buffer 要是用

  private ByteBuffer mInputBuffer;    private ByteBuffer mOutputBuffer;

收到数据可以保存到普通的 byte[]中。

public int read(ByteBuffer buffer) throws IOException {        if (buffer.isDirect()) {            return native_read_direct(buffer, buffer.remaining());        } else if (buffer.hasArray()) {            return native_read_array(buffer.array(), buffer.remaining());        } else {            throw new IllegalArgumentException("buffer is not direct and has no array");        }    }

调用底层的c语言的read函数

static jintandroid_hardware_SerialPort_read_direct(JNIEnv *env, jobject thiz, jobject buffer, jint length){    int fd = env->GetIntField(thiz, field_context);    jbyte* buf = (jbyte *)env->GetDirectBufferAddress(buffer);    if (!buf) {        jniThrowException(env, "java/lang/IllegalArgumentException", "ByteBuffer not direct");        return -1;    }    int ret = read(fd, buf, length);    if (ret < 0)        jniThrowException(env, "java/io/IOException", NULL);    return ret;}

更多相关文章

  1. 【Android學習專題】網絡通信篇:Socket TCP(简易聊天室)
  2. android aidl 进程通信 调用错误
  3. Android 利用TCP通信 实现环境数据显示及控制指令下发
  4. Android USB通信
  5. RN系列:Android原生与RN如何交互通信
  6. Android跨进程通信IPC系列
  7. flutter 与 android 混合开发 以及 Android与flutter之间的通信
  8. Android跨进程通信IPC之9——Binder之Framework层C++篇2
  9. Android跨进程通信IPC之9——Binder之Framework层C++篇1

随机推荐

  1. MongoDB全自动容灾集群节点角色解析
  2. 中国网络“野蛮生长”的20年
  3. 一种etcd容灾备份的设计与实现
  4. VS经常提示函数不安全(报错、报警告),该怎
  5. Logstash 之 重要插件
  6. Python是什么?Python有哪些框架?
  7. 自动修改centos服务器IP地址
  8. YUM变量缺失导致的问题小记
  9. 如何去使用Python爬虫来爬取B站的弹幕数
  10. Rancher面板权限配置