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\r\n">/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() + "\n"
39                         + "exit\n";
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 }

 

 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM