• Android 串口设置校验位、速率、停止位等参数


    Android 串口通讯设置校验位、速率、停止位等参数

     

    最近业余时间有个android项目需要和硬件进行通信,通讯方式都是通过串口,android串口通讯google有个开源的demo 和很多人一样我也是通过下载这个demo进行开发和研究的。

    google android串口通讯开源demo地址:https://code.google.com/archive/p/android-serialport-api/

    串口通讯中我们可能会要设置校验位、速率、停止位等参数,但一般情况下还是不用设置的,只需要设置波特率就行。google提供的demo中就只提供一个波特率的设置其他的参数一并没有提供。

    在使用google 源码的时候一定要注意 jni 当中.h和.c文件中的方法命名的规则,是java关键字+包名+类名+方法名。一开始我没注意,程序报错走了好多弯路。所以画图具体解释下。

     

     

     

     

    android串口通讯说到底其实还是linux串口通讯。我们jni中的c代码其实就是操作linux中提供的串口文件。一开始并不知道这个原理,只是后来在网上找如何设置校验位、速率等问题时才明白的。

    比如打开串口 fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY); 其实就是打开linux设备中串口文件。串口文件都是以ttys开头 如ttys0 等。这个fd很重要,应该就是代表当前串口文件对象。只要这里打开成功了,C代码其他地方对串口的操作以及配置都是基于这个fd来进行的。

     

    在adb 中可以用 cd dev来查看是否有串口文件。如果看到了 ttys0等这样的文件说明目前设备上有串口否则没有找到串口设备。

    adb 中通过命令向串口发送数据:echo -e "AT ">/dev/ttyS0

     

    顺便说下 如何在模拟器中使用电脑上的串口 这个只能使用android自带的模拟器。 一直想如果第三方模拟器能加载电脑的串口设备就好了。

    emulator @模拟器名称 -qemu -serial COM1   // 这个命令会自动启动安卓模拟器

    chmod 777 ttyS2  //提示权限 串口设备文件 ttys2

     

    废话不多说,下面附上完整的代码。

    注:代码是google的源代码+网上找的一些参考资料=现在的一个整合代码。

    注意C和h代码中的数据类型,尽量以jint、jstring等来定义。(jint相当于java的int...)因为要被java调用,所以做好这样定义

    .h代码(用于jni方法声明 即要在java中调用的方法声明)注意包名+类名+方法名

    SerialPort.h

     

     1 /* DO NOT EDIT THIS FILE - it is machine generated */
     2 #include <jni.h>
     3 /* Header for class org_winplus_serial_utils_SerialPort */
     4 
     5 #ifndef _Included_android_serialport_SerialPort
     6 #define _Included_android_serialport_SerialPort
     7 #ifdef __cplusplus
     8 extern "C" {
     9 #endif
    10     /*
    11      * Class:     org_winplus_serial_utils_SerialPort
    12      * Method:    open
    13      * Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor;
    14      */
    15     JNIEXPORT jobject JNICALL Java_android_serialport_SerialPort_open
    16     (JNIEnv *env, jclass thiz, jstring path, jint baudrate,
    17      jint databits, jint stopbits, jchar parity);
    18     /*
    19      * Class:     org_winplus_serial_utils_SerialPort
    20      * Method:    close
    21      * Signature: ()V
    22      */
    23     JNIEXPORT void JNICALL Java_android_serialport_SerialPort_close
    24     (JNIEnv *, jobject);
    25 
    26 #ifdef __cplusplus
    27 }
    28 #endif
    29 #endif

     

    .C代码(用于jni方法的具体实现代码本例中串口操作的所有代码)注意包名+类名+方法名

    注意C代码中 写一个方法,一定要写到调用地方的前面。或者前面写声明;即 A要调用B方法,B方法代码必须要在A方法前面。由于平时写java或者c#这些代码是不需要这样的写法。一时切换到C代码可能容易忘记。

     

    SerialPort.C

     

      1 #include <termios.h>
      2 #include <unistd.h>
      3 #include <sys/types.h>
      4 #include <sys/stat.h>
      5 #include <fcntl.h>
      6 #include <string.h>
      7 #include <jni.h>
      8 
      9 #include "SerialPort.h"
     10 
     11 #include "android/log.h"
     12 static const char *TAG = "serial_port";
     13 #define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO,  TAG, fmt, ##args)
     14 #define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args)
     15 #define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args)
     16 int fd;
     17 static speed_t getBaudrate(jint baudrate)
     18 {
     19     switch(baudrate)
     20     {
     21     case 0:
     22         return B0;
     23     case 50:
     24         return B50;
     25     case 75:
     26         return B75;
     27     case 110:
     28         return B110;
     29     case 134:
     30         return B134;
     31     case 150:
     32         return B150;
     33     case 200:
     34         return B200;
     35     case 300:
     36         return B300;
     37     case 600:
     38         return B600;
     39     case 1200:
     40         return B1200;
     41     case 1800:
     42         return B1800;
     43     case 2400:
     44         return B2400;
     45     case 4800:
     46         return B4800;
     47     case 9600:
     48         return B9600;
     49     case 19200:
     50         return B19200;
     51     case 38400:
     52         return B38400;
     53     case 57600:
     54         return B57600;
     55     case 115200:
     56         return B115200;
     57     case 230400:
     58         return B230400;
     59     case 460800:
     60         return B460800;
     61     case 500000:
     62         return B500000;
     63     case 576000:
     64         return B576000;
     65     case 921600:
     66         return B921600;
     67     case 1000000:
     68         return B1000000;
     69     case 1152000:
     70         return B1152000;
     71     case 1500000:
     72         return B1500000;
     73     case 2000000:
     74         return B2000000;
     75     case 2500000:
     76         return B2500000;
     77     case 3000000:
     78         return B3000000;
     79     case 3500000:
     80         return B3500000;
     81     case 4000000:
     82         return B4000000;
     83     default:
     84         return -1;
     85     }
     86 }
     87 
     88 /**
     89 
     90 * 设置串口数据,校验位,速率,停止位
     91 
     92 * @param nBits 类型 int数据位 取值 位7或8
     93 
     94 * @param nEvent 类型 char 校验类型 取值N ,E, O,,S
     95 
     96 * @param mStop 类型 int 停止位 取值1 或者 2
     97 
     98 */
     99 
    100 int set_opt(jint nBits, jchar nEvent, jint nStop)
    101 {
    102 
    103     LOGE("set_opt:nBits=%d,nEvent=%c,nSpeed=%d,nStop=%d", nBits, nEvent, nStop);
    104 
    105 
    106     struct termios newtio;
    107 
    108     if(tcgetattr(fd, & newtio) != 0)
    109     {
    110 
    111         LOGE("setup serial failure");
    112 
    113         return -1;
    114 
    115     }
    116 
    117     bzero( & newtio, sizeof(newtio));
    118 
    119     //c_cflag标志可以定义CLOCAL和CREAD,这将确保该程序不被其他端口控制和信号干扰,同时串口驱动将读取进入的数据。CLOCAL和CREAD通常总是被是能的
    120 
    121     newtio.c_cflag |= CLOCAL | CREAD;
    122 
    123 
    124     switch(nBits) //设置数据位数
    125     {
    126 
    127     case 7:
    128 
    129         newtio.c_cflag &= ~CSIZE;
    130 
    131         newtio.c_cflag |= CS7;
    132 
    133         break;
    134 
    135     case 8:
    136 
    137         newtio.c_cflag &= ~CSIZE;
    138 
    139         newtio.c_cflag |= CS8;
    140 
    141         break;
    142 
    143     default:
    144 
    145 
    146         break;
    147 
    148     }
    149 
    150     switch(nEvent) //设置校验位
    151     {
    152 
    153     case 'O':
    154 
    155         newtio.c_cflag |= PARENB; //enable parity checking
    156 
    157         newtio.c_cflag |= PARODD; //奇校验位
    158 
    159         newtio.c_iflag |= (INPCK | ISTRIP);
    160 
    161 
    162 
    163         break;
    164 
    165     case 'E':
    166 
    167         newtio.c_cflag |= PARENB; //
    168 
    169         newtio.c_cflag &= ~PARODD; //偶校验位
    170 
    171         newtio.c_iflag |= (INPCK | ISTRIP);
    172 
    173 
    174 
    175         break;
    176 
    177     case 'N':
    178 
    179         newtio.c_cflag &= ~PARENB; //清除校验位
    180 
    181 
    182 
    183         break;
    184 
    185 
    186     default:
    187 
    188 
    189         break;
    190 
    191     }
    192     switch(nStop) //设置停止位
    193     {
    194 
    195     case 1:
    196 
    197         newtio.c_cflag &= ~CSTOPB;
    198 
    199         break;
    200 
    201     case 2:
    202 
    203         newtio.c_cflag |= CSTOPB;
    204 
    205         break;
    206 
    207     default:
    208 
    209         // LOGW("nStop:%d,invalid param", nStop);
    210 
    211         break;
    212 
    213     }
    214 
    215     newtio.c_cc[VTIME] = 0;//设置等待时间
    216 
    217     newtio.c_cc[VMIN] = 0;//设置最小接收字符
    218 
    219     tcflush(fd, TCIFLUSH);
    220 
    221     if(tcsetattr(fd, TCSANOW, & newtio) != 0)
    222     {
    223 
    224         LOGE("options set error");
    225 
    226         return -1;
    227 
    228     }
    229 
    230 
    231     LOGE("options set success");
    232     return 1;
    233 
    234 }
    235 
    236 
    237 /*
    238  * Class:     android_serialport_SerialPort
    239  * Method:    open
    240  * Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor;
    241  */
    242 JNIEXPORT jobject JNICALL Java_android_serialport_SerialPort_open
    243 (JNIEnv *env, jclass thiz, jstring path, jint baudrate,
    244  jint databits, jint stopbits, jchar parity)
    245 {
    246 
    247     speed_t speed;
    248     jobject mFileDescriptor;
    249 
    250     /*波特率 */
    251     {
    252         speed = getBaudrate(baudrate);
    253         if (speed == -1)
    254         {
    255             /* TODO: throw an exception */
    256             LOGE("Invalid baudrate");
    257             return NULL;
    258         }
    259     }
    260 
    261     /* Opening device */
    262     {
    263         jint flags = 0;
    264         jboolean iscopy;
    265         const char *path_utf = (*env)->GetStringUTFChars(env, path, &iscopy);
    266         LOGD("Opening serial port %s with flags 0x%x", path_utf, O_RDWR | flags);
    267         fd = open(path_utf, O_RDWR | O_NONBLOCK);
    268         //fd=fd;
    269         LOGD("open() fd = %d", fd);
    270         (*env)->ReleaseStringUTFChars(env, path, path_utf);
    271         if (fd == -1)
    272         {
    273             /* Throw an exception */
    274             LOGE("Cannot open port");
    275             /* TODO: throw an exception */
    276             return NULL;
    277         }
    278     }
    279 
    280     /* Configure device */
    281     {
    282         struct termios cfg;
    283         LOGD("Configuring serial port");
    284         if (tcgetattr(fd, &cfg))
    285         {
    286             LOGE("tcgetattr() failed");
    287             close(fd);
    288             /* TODO: throw an exception */
    289             return NULL;
    290         }
    291 
    292         cfmakeraw(&cfg);
    293         //设置波特率
    294         cfsetispeed(&cfg, speed);
    295         cfsetospeed(&cfg, speed);
    296 
    297         if (tcsetattr(fd, TCSANOW, &cfg))
    298         {
    299             LOGE("tcsetattr() failed");
    300             close(fd);
    301             /* TODO: throw an exception */
    302             return NULL;
    303         }
    304 
    305         //配置校验位 停止位等等
    306         set_opt(databits, parity, stopbits);
    307     }
    308 
    309     /* Create a corresponding file descriptor */
    310     {
    311         jclass cFileDescriptor = (*env)->FindClass(env, "java/io/FileDescriptor");
    312         jmethodID iFileDescriptor = (*env)->GetMethodID(env, cFileDescriptor, "<init>", "()V");
    313         jfieldID descriptorID = (*env)->GetFieldID(env, cFileDescriptor, "descriptor", "I");
    314         mFileDescriptor = (*env)->NewObject(env, cFileDescriptor, iFileDescriptor);
    315         (*env)->SetIntField(env, mFileDescriptor, descriptorID, (jint)fd);
    316     }
    317 
    318     return mFileDescriptor;
    319 
    320 }
    321 
    322 /*
    323  * Class:     cedric_serial_SerialPort
    324  * Method:    close
    325  * Signature: ()V
    326  */
    327 JNIEXPORT void JNICALL Java_android_serialport_SerialPort_close
    328 (JNIEnv *env, jobject thiz)
    329 {
    330     jclass SerialPortClass = (*env)->GetObjectClass(env, thiz);
    331     jclass FileDescriptorClass = (*env)->FindClass(env, "java/io/FileDescriptor");
    332 
    333     jfieldID mFdID = (*env)->GetFieldID(env, SerialPortClass, "mFd", "Ljava/io/FileDescriptor;");
    334     jfieldID descriptorID = (*env)->GetFieldID(env, FileDescriptorClass, "descriptor", "I");
    335 
    336     jobject mFd = (*env)->GetObjectField(env, thiz, mFdID);
    337     jint descriptor = (*env)->GetIntField(env, mFd, descriptorID);
    338 
    339     LOGD("close(fd = %d)", descriptor);
    340     close(descriptor);
    341 }

     

    java代码 调用jni提供的方法进行串口操作

     1 package android.serialport;
     2 
     3 import java.io.File;
     4 import java.io.FileDescriptor;
     5 import java.io.FileInputStream;
     6 import java.io.FileOutputStream;
     7 import java.io.IOException;
     8 import java.io.InputStream;
     9 import java.io.OutputStream;
    10 
    11 import android.util.Log;
    12 
    13 public class SerialPort {
    14     private static final String TAG = "SerialPort";
    15    
    16     private FileDescriptor mFd;
    17     private FileInputStream mFileInputStream;
    18     private FileOutputStream mFileOutputStream;
    19     /***
    20      * 构造方法
    21      * @param device 串口文件
    22      * @param baudrate 波特率
    23      * @param dataBits 数据位
    24      * @param stopBits 停止位
    25      * @param parity   校验位
    26      * @throws SecurityException
    27      * @throws IOException
    28      */
    29     public SerialPort(File device, int baudrate, int dataBits,int stopBits,char parity)
    30             throws SecurityException, IOException {
    31 
    32         /* Check access permission */
    33         if (!device.canRead() || !device.canWrite()) {
    34             try {
    35                 /* Missing read/write permission, trying to chmod the file */
    36                 Process su;
    37                 su = Runtime.getRuntime().exec("/system/bin/su");
    38                 String cmd = "chmod 666 " + device.getAbsolutePath() + "
    "
    39                         + "exit
    ";
    40                 su.getOutputStream().write(cmd.getBytes());
    41                 if ((su.waitFor() != 0) || !device.canRead()
    42                         || !device.canWrite()) {
    43                     throw new SecurityException();
    44                 }
    45             } catch (Exception e) {
    46                 e.printStackTrace();
    47                 throw new SecurityException();
    48             }
    49         }
    50 
    51         mFd = open(device.getAbsolutePath(), baudrate, dataBits,stopBits,parity);
    52         if (mFd == null) {
    53             Log.e(TAG, "native open returns null");
    54             throw new IOException();
    55         }
    56         mFileInputStream = new FileInputStream(mFd);
    57         mFileOutputStream = new FileOutputStream(mFd);
    58     }
    59 
    60     // Getters and setters
    61     public InputStream getInputStream() {
    62         return mFileInputStream;
    63     }
    64 
    65     public OutputStream getOutputStream() {
    66         return mFileOutputStream;
    67     }
    68 
    69 
    70     // 调用JNI中 打开方法的声明 
    71     private native static FileDescriptor open(String path, int baudrate,
    72             int dataBits,int stopBits,char parity);
    73 
    74     public native void close();
    75 
    76     static {
    77         System.loadLibrary("serial_port");
    78     }
    79 }

     

     

     

     

     

     

     

  • 相关阅读:
    MySQL之PXC集群
    MySQL大表查询未走索引异常分析
    Redis场景应用之UA池
    MySQL日志剖析
    Redis场景应用之排行榜
    MySQL B+树索引解析
    Redisson分布式锁剖析
    关键字替代符号C++
    并查集按秩合并
    POJ3048
  • 原文地址:https://www.cnblogs.com/rui1236/p/5988074.html
Copyright © 2020-2023  润新知