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 串口设置校验位、速率、停止位等参数

 

 

 

 

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 }