張海峰,徐杭男,楊偉鋒
(杭州電子科技大學(xué) 電子信息學(xué)院,杭州 310018)
一種融合USB-CDC與JNI技術(shù)的數(shù)據(jù)通信方式
張海峰,徐杭男,楊偉鋒
(杭州電子科技大學(xué) 電子信息學(xué)院,杭州 310018)
本文采用STM32F407為主控制器,根據(jù)板載USB接口的特點(diǎn),設(shè)計(jì)基于通信設(shè)備類(CDC)的USB通信傳輸模塊,然后在裁減配置好的Android系統(tǒng)上利用多線程讀取由STM32采集到的原始數(shù)據(jù)并作處理;STM32利用ST公司提供的驅(qū)動程序,實(shí)現(xiàn)以USB設(shè)備為虛擬串口,利用Android源碼編譯Android程序的JNI部分的鏈接庫文件以串行總線(COM)方式達(dá)到訪問底層USB端口的目的。實(shí)驗(yàn)結(jié)果表明,該采集傳輸方式在數(shù)據(jù)穩(wěn)定高速傳輸上性能顯著。
STM32F407;Android;USB-CDC;虛擬串口
在聲學(xué)測量領(lǐng)域,聲信號A/D轉(zhuǎn)化后的數(shù)據(jù)必須保證實(shí)時性穩(wěn)定傳輸,并且不發(fā)生丟包現(xiàn)象。只有傳輸數(shù)據(jù)能快速穩(wěn)定,才能對后續(xù)連續(xù)頻譜化分析影響降低至最小化。Cortex-M4架構(gòu)的ARM處理器集結(jié)了高性能、低功耗、外設(shè)資源豐富且廉價等優(yōu)點(diǎn)[1-2];而Android系統(tǒng)是基于Linux系統(tǒng),源碼公開[3],可依據(jù)喜好裁減;USB的通用串行總線具有高傳輸速率、支持熱插拔等優(yōu)點(diǎn)[4]。綜合以上,本文提出了結(jié)合Android系統(tǒng)板和STM32板載USB模塊的聲信號采集傳輸技術(shù),在Android系統(tǒng)端編寫APP,以多線程的方式和JNI庫函數(shù)實(shí)現(xiàn)數(shù)據(jù)接收,使整機(jī)系統(tǒng)更符合當(dāng)下人機(jī)關(guān)系[5-6]。
整體系統(tǒng)結(jié)構(gòu)框架圖如圖1所示,該系統(tǒng)主要由底層系統(tǒng)采集傳輸和上層系統(tǒng)接收處理構(gòu)成。底層系統(tǒng)采集傳輸模塊通過STM32F407處理器、電源模塊、聲信號放大與A/D采集模塊、電平轉(zhuǎn)換模塊等組成下位機(jī);上層系統(tǒng)接收處理模塊通過訪問USB虛擬串口,實(shí)現(xiàn)數(shù)據(jù)接收和處理。
圖1 系統(tǒng)框圖
8 MHz外部晶振倍頻為168 MHz后作為微處理器主頻,168 MHz再經(jīng)過分頻后得到精確的48 MHz作為USB通信時鐘,專用PLLI2S為I2S接口實(shí)現(xiàn)高品質(zhì)音頻性能提供了保障;為了滿足轉(zhuǎn)換的量程要求,模擬信號經(jīng)過信號調(diào)理模塊,利用OP2177和THS4521構(gòu)成的二級放大器作為輸入衰減器,量程的切換可通過控制光耦通斷來實(shí)現(xiàn);信號穩(wěn)定后由專業(yè)的A/D音頻芯片PCM1861進(jìn)行A/D轉(zhuǎn)換,將采集的原始數(shù)據(jù)以I2S協(xié)議發(fā)送到STM32F407;STM32板載USB模塊以熱插拔的方式接入Android系統(tǒng)中,在Android系統(tǒng)會出現(xiàn)一個虛擬COM串口,通過把Linux底層的read函數(shù)封裝成JNI函數(shù)庫,用JAVA的API接口去讀取傳輸通道的數(shù)據(jù)。虛擬串口由下位機(jī)USB枚舉產(chǎn)生,作為大批量數(shù)據(jù)傳輸通道。
1.1 STM32F407與Exynos4412
STM32F4系列包含了眾多高性能的外設(shè),尤其常用于高速、大容量的分布式數(shù)據(jù)采集系統(tǒng),本文中采用的STM32F407包含2個DMA控制器(總共16個數(shù)據(jù)流),每個數(shù)據(jù)流多達(dá)8個通道、17個定時器、3個I2C接口、3個SPI接口,支持USB2.0高速傳輸[1-2]。除此之外,多達(dá)1 MB的Flash內(nèi)存和196 KB的SRAM滿足大批量數(shù)據(jù)采集緩存的需求。而Exynos4412作為四核處理器,擁有1.6 GHz主頻,GPU Mali-400MP,同時輔以2 GB運(yùn)行內(nèi)存,滿足數(shù)據(jù)高效處理需求,運(yùn)行Android系統(tǒng)不會有卡滯的情況。
1.2 USB數(shù)據(jù)接口類和通信接口類
通用串行總線(USB)是當(dāng)下計(jì)算機(jī)連接外部設(shè)備裝置的一個高速串行通信協(xié)議標(biāo)準(zhǔn)[7],比如OTG方式使USB設(shè)備間能夠獨(dú)立地進(jìn)行數(shù)據(jù)交換。常見的USB設(shè)備類如HID(Human Interface Device)和CDC(Communication Device Class)在安卓系統(tǒng)上自帶了驅(qū)動程序,因此只要配置好Android的Linux內(nèi)核支持上述功能即可使用。因?yàn)镠ID的傳輸速度很有限,在高速模式下傳輸速度也不過64 kbps,因此為了提高傳輸速率,采用USB-CDC協(xié)議來完成Android與STM32之間的USB通信。
USB-CDC是由通信接口類和數(shù)據(jù)接口類組合而成的。只需要為CDC類添加數(shù)據(jù)接口類,并且為數(shù)據(jù)接口添加一對批量傳輸模式(Bulk Transfer)的輸入端點(diǎn)和輸出端點(diǎn)就能完成基本數(shù)據(jù)收發(fā)功能。首先USB設(shè)備被USB Core所識別和描述,然后將信息上傳至tty協(xié)議層,最后通過線路規(guī)程的方式,將tty協(xié)議層和USB協(xié)議層結(jié)合使用。Android中SDK并沒有開發(fā)CDC特定的API,但是通過USB相關(guān)的操作,如設(shè)備枚舉、設(shè)備打開、接口操作、端口操作等完成CDC類的數(shù)據(jù)批量傳輸。圖2所示是Android與STM32的USB設(shè)備通信圖。
圖2 Android與STM32的USB設(shè)備通信圖
聲信號數(shù)據(jù)接收過程主要包含了STM32下 USB傳輸過程、Android的底層JNI庫函數(shù)開發(fā)和上位機(jī)接收過程。
2.1 STM32下USB傳輸過程
STM32板載的USB模塊為了識別COM端口,需要根據(jù)CDC規(guī)范編寫程序。一般傳輸類型有:控制傳輸、塊傳輸、中斷傳輸和同步傳輸。比較4種傳輸方式,適合少量數(shù)據(jù)傳輸,且對傳輸時間和速率都不要求的是控制傳輸;適合大量數(shù)據(jù),但對時間和速率不要求的是塊傳輸;中斷傳輸適合少量或者中量且對傳輸時間有要求的數(shù)據(jù);同步傳輸適合大量數(shù)據(jù)傳輸且速率恒定,對時間有要求。由于STM32F407封裝了USB-CDC的函數(shù)庫,所以只需要調(diào)用應(yīng)用程序接口層中的代碼,配置設(shè)備端口響應(yīng)Android的USB主機(jī)的讀寫請求,獲得USB設(shè)備與USB主機(jī)的連接,初始化USB傳輸模式為同步傳輸類型,然后開始數(shù)據(jù)采集,設(shè)計(jì)數(shù)據(jù)枚舉成功標(biāo)志位,當(dāng)枚舉成功的時候才能發(fā)送數(shù)據(jù)。該過程的數(shù)據(jù)采集發(fā)送流程圖如圖3所示。
圖3 數(shù)據(jù)采集發(fā)送流程圖
2.2 Android的底層JNI庫函數(shù)開發(fā)
Android基于Linux的開源系統(tǒng),要對串口進(jìn)行操作讀寫,就要使用在用戶空間執(zhí)行的API接口,使其能夠在Linux應(yīng)用層對底層串口設(shè)備進(jìn)行讀寫、控制等操作,這就需要JNI的封裝庫。JNI程序雖然遵循C/C++程序的語法,但此程序要作為Android程序的SO庫文件調(diào)用,需要作一些改變。首先如同Linux訪問串口的應(yīng)用程序一樣,進(jìn)行波特率通信格式的初始化,設(shè)置串口數(shù)據(jù)位、停止位和校驗(yàn)位。然后打開USB設(shè)備節(jié)點(diǎn)名稱,可在JAVA層以函數(shù)參數(shù)傳入,清除輸入輸出緩沖區(qū),配置設(shè)備把終端屬性設(shè)置成原始屬性,創(chuàng)建read、write、close、open等本地函數(shù)供JAVA層調(diào)用,因?yàn)樵诒疚难芯恐恍枰咚侔l(fā)送數(shù)據(jù),所以將新建內(nèi)存空間用來存取讀取數(shù)據(jù),再拷貝到JAVA傳入的用來存儲接收數(shù)據(jù)的字節(jié)型數(shù)組中。為了匹配JAVA層需要調(diào)用的本地方法與C/C++層的方法,需要進(jìn)行相應(yīng)的匹配,過程如下:
static const JNINativeMethod methods[] = {
{"Open","(Ljava/lang/String;I)I",(void *)Java_com_example_xhn_datafromanlog_Open },
{"Setopt","(IIICI)I",(void *)Java_com_example_xhn_datafromanlog_Setopt },
{"Close","(I)I",(void *)Java_com_example_xhn_datafromanlog_Close },
{"Write","(I[BI)I",(void *)Java_com_example_xhn_datafromanlog_Write },
{"Read","(I[B)I",(void *)Java_com_example_xhn_datafromanlog_Read },
};
實(shí)現(xiàn)JNI_OnLoad的注冊:
if ((*env)->RegisterNatives(env,cls,methods,sizeof(methods)/sizeof(methods[0]))< 0)
使用C/C++語言依次實(shí)現(xiàn)USB設(shè)備的打開、初始化、讀、關(guān)閉操作,具體如下所示:
① 波特率通信格式轉(zhuǎn)換的本地函數(shù)
Java_com_example_xhn_datafromanlog_Setopt(JNIEnv *env,jobject cls,jint fd,jint nSpeed,jint nBits,jchar nEvent,jint nStop);
② 打開串口的函數(shù)
Java_com_example_xhn_datafromanlog_Open(JNIEnv *env,jobject cls,jstring filename,jint flag);
③ 關(guān)閉串口的函數(shù)
Java_com_example_xhn_datafromanlog_Close(JNIEnv *env,jobject cls,jstring filename,jint flag);
④ 讀取底層數(shù)據(jù)的函數(shù)
Java_com_example_xhn_datafromanlog_Read(JNIEnv *env,jobject cls,jint fd,jbyteArray buf);
最后將JNI文件放入Linux環(huán)境下的Android原生代碼,運(yùn)行命令“arm-linux-gcc-fPIC-shared uartcontrol.c-o libuartcontrol.so-I/usr/lib/jvm/java-1.7.0-openjdk-amd64/include/-nostdlib/work/android-5.0.2/prebuilts/ndk/9/platforms/android-19/arch-arm/usr/lib/libc.so”,可以生成JAVA調(diào)用的uartcontrol.so庫文件。
2.3 上位機(jī)接收過程處理
利用多線程應(yīng)用編程,開啟單獨(dú)一個線程循環(huán)讀取底層數(shù)據(jù),主要利用面向?qū)ο蟮姆绞?,封裝JNI庫里的本地函數(shù)進(jìn)行調(diào)用,關(guān)鍵是在Android Studio中加載libuartcontrol.so函數(shù)庫,利用“System.loadLibrary(“uartcontrol”)”和“nread=UartControl.Read(devfd,buf_read)”完成數(shù)據(jù)收取。最后將讀取的數(shù)據(jù)在另外一個線程新建內(nèi)存存儲,然后調(diào)用Zoom-FFT算法進(jìn)行頻譜、頻率計(jì)權(quán)處理,在界面進(jìn)行繪圖等。
本系統(tǒng)在Android啟動時,設(shè)置用戶權(quán)限模式為系統(tǒng)用戶,首先通過PC機(jī)的上位機(jī)可以查找到STM32的USB設(shè)備被識別為/dev/usb/tty1-2.3,正如同前文所論述的那樣是通過產(chǎn)品ID和廠商ID進(jìn)行過濾匹配USB設(shè)備的,如圖4中圈出所示。
圖4 USB設(shè)備節(jié)點(diǎn)圖
STM32在通過PC機(jī)模擬虛擬串口接收時,可以清楚看到數(shù)據(jù)并沒有發(fā)生丟包的現(xiàn)象,在串口調(diào)試助手看到USB設(shè)備發(fā)送的A/D數(shù)據(jù)。顯示的信息具體如圖5所示。
圖5 PC總線模擬接收圖
一般24位音頻A/D在雙通道8位模式下的采樣個數(shù)為 24K×2×8=384K個數(shù)據(jù),通過整機(jī)系統(tǒng)聯(lián)調(diào),在Android Studio軟件的打印信息中可以看見logcat窗口欄每秒打印的個數(shù)基本在384 000個,驗(yàn)證了數(shù)據(jù)傳輸?shù)母咚俜€(wěn)定性與可靠性,基本與理論值一致。具體每秒實(shí)時收到的字節(jié)數(shù)如圖6所示。在數(shù)據(jù)沒有丟失的情況下,本文在測試時輸入頻率為1000 Hz,幅度為10 V,幅度衰減為39 dB的正弦信號,測試顯示如圖7所示,完美的正弦曲線可以顯示在Android平臺里,并沒有發(fā)生斷層或者缺失,由此驗(yàn)證了這種傳輸方式的可靠性。
圖6 USB-CDC數(shù)據(jù)通信速率曲線
圖7 系統(tǒng)顯示測試
[1] STMicroelectronics.STM32F10XXX USB development Kit,2008.
[2] STMicroelectronics.ARM based 32-bit MCU STM32F101xx and STM32F103xx firmware library User manual,2008.
[3] 羅升陽.Android系統(tǒng)源代碼情景分析[M].北京:電子工業(yè)出版社,2012.
[4] 宋寶華.Linux設(shè)備驅(qū)動開發(fā)詳解[M].2版.北京:人民郵電出版社,2010.
[5] 王立萍,吳黎明.基于嵌入式的USB數(shù)據(jù)采集系統(tǒng)的設(shè)計(jì)開發(fā)[J].電子測量技術(shù),2007,30(9):42-46.
[6] 羅鈞,桂杰出.USB協(xié)議及其接口實(shí)現(xiàn)[J].儀器儀表學(xué)報(bào),2004(4).
[7] 吳明琪,馬潮.嵌入式系統(tǒng)的USB虛擬串口設(shè)計(jì)[J].單片機(jī)與嵌入式系統(tǒng)應(yīng)用,2005(4):62-63,66.
張海峰(副教授),主要研究方向?yàn)槲锫?lián)網(wǎng)應(yīng)用與嵌入式開發(fā);徐杭男、楊偉鋒(碩士研究生),主要研究方向?yàn)殡娮优c通信工程。
Data Communication Method Combining USB-CDC and JNI Technology
Zhang Haifeng,Xu Hangnan,Yang Weifeng
(School of Electronics Information,Hangzhou Dianzi University,Hangzhou 310018,China)
In the paper,the STM32F407 is used as the control core,according to the characteristics of on-board USB interface,a USB communication transmission module based on communication equipment class(CDC) is proposed.Then,the Android system which is well configured reads the original data collected by STM32 using the multi thread.The STM32 uses the USB driver provided by ST to change USB device to virtual serial port,and compiles JNI program which is loaded read USB port by the way of COM in the environment of Android source code.The experiment results show that the method is significant in data stable and high-speed transmission.
STM32F407;Android;USB-CDC;virtual port
TP391
A
?士然
2016-09-20)