怯肇乾,史繼峰
(南昌職業(yè)大學(xué) 信息技術(shù)學(xué)院,南昌 330500)
現(xiàn)有手勢(shì)識(shí)別導(dǎo)航,最常見(jiàn)的是紅外收發(fā)/分析識(shí)別,雖然經(jīng)典,但距離短,通常最遠(yuǎn)只有150 mm,若作為論壇、會(huì)展、會(huì)議等演示場(chǎng)合應(yīng)用,使用局限,不現(xiàn)實(shí)際;更有傳統(tǒng)的激光筆導(dǎo)航,雖然簡(jiǎn)便,但需要正對(duì)接收操作,客戶體驗(yàn)不佳;還有重大國(guó)際/國(guó)家類論壇、會(huì)展中運(yùn)用的攝像識(shí)別,盡管操控便捷而且技術(shù)高大上,但價(jià)格不匪,難以拓展應(yīng)用到廣泛的課堂教學(xué)等尋常場(chǎng)景。
教學(xué)、論壇、會(huì)展、會(huì)議市場(chǎng)需求空間巨大,尋找和建立更為可靠、準(zhǔn)確、高效、廉價(jià)的檢測(cè)傳感器和手勢(shì)識(shí)別算法模型意義重大。經(jīng)反復(fù)查閱和對(duì)比,最終聚焦了激光飛秒測(cè)距ToF(Time of Flight)傳感器和“人工智能AI(Artificial Intelligence)深度學(xué)習(xí)自適應(yīng)”算法建模,于是有了這個(gè)省廳科技項(xiàng)目——激光飛秒手勢(shì)導(dǎo)航簡(jiǎn)易微系統(tǒng)實(shí)現(xiàn)。
采用高性價(jià)比的激光飛秒測(cè)距ToF傳感器,得到原始距離數(shù)據(jù)。客戶體驗(yàn)考慮,使用3個(gè)傳感器呈上、左、右布局[避免1個(gè)傳感器不合常規(guī)的特定手勢(shì)定義與強(qiáng)制學(xué)習(xí)],以此3路距離數(shù)據(jù)組成采集數(shù)據(jù)輸入。以最簡(jiǎn)潔的“人工智能AI(Artificial Intelligence)深度學(xué)習(xí)自適應(yīng)”算法模型——“后向反饋神經(jīng)元網(wǎng)絡(luò)BP_NN(Back Propagation Neural Networks)”為核心,輔以“智能模糊控制”和“專家經(jīng)驗(yàn)控制”,構(gòu)成“運(yùn)算處理分析”中心,最終得到并輸出“手勢(shì)數(shù)據(jù)信息”[1-3,18]。整個(gè)“ToF手勢(shì)識(shí)別算法”模型如圖1所示。BP_NN運(yùn)算處理分析框架,主要由適配器和調(diào)整器構(gòu)成;適配器,前向傳輸(Feed Forward),逐層波浪式的傳遞輸出值[左右、上下、前后];調(diào)整器,逆向反饋(Back Propagation),反向逐層調(diào)整權(quán)重和閾值,修正誤差至最小。“智能模糊控制”和“專家經(jīng)驗(yàn)控制”,進(jìn)行前級(jí)“預(yù)處理”和后級(jí)“結(jié)果識(shí)別”;前級(jí)“預(yù)處理”,通過(guò)“濾波”,得到50~600 mm內(nèi)的傳感器“視場(chǎng)出入及其運(yùn)動(dòng)的距離和時(shí)刻數(shù)據(jù)”,并對(duì)快速移動(dòng)情形完成數(shù)據(jù)插入,便利BP_NN更加有效運(yùn)算;后級(jí)“結(jié)果識(shí)別”處理,即后處理,對(duì)BP_NN運(yùn)算得到的左右、上下、前后數(shù)據(jù)進(jìn)一步處理分析,得到左右、上下、點(diǎn)擊等手勢(shì)直接數(shù)據(jù)。借助專家經(jīng)驗(yàn),可以把實(shí)際操控中多數(shù)習(xí)慣的“斜上再斜下”的動(dòng)作解釋為前后運(yùn)動(dòng)的“點(diǎn)擊”。
圖1 激光飛秒手勢(shì)識(shí)別算法模型框圖
BP_NN框架,選用3-4-3層次,如圖2所示,3個(gè)輸入層,4個(gè)隱藏層,3個(gè)輸出層,便于常用的Cortex-Mx/RISC-V類型的微控制器MCU (Microcontroller Unit)在軟件層次上進(jìn)行快速高效的實(shí)現(xiàn)[MCU,優(yōu)勢(shì)在控制,數(shù)字信號(hào)處理是弱勢(shì)]。BP_NN運(yùn)算,經(jīng)典成熟,不再贅述相關(guān)過(guò)程的各個(gè)離散處理公式,其關(guān)鍵是誤差計(jì)算及其對(duì)各層權(quán)值和閥值的修正,盡管已經(jīng)采用最簡(jiǎn)層次框架,放在MCU上實(shí)現(xiàn)仍然運(yùn)算繁鎖、耗時(shí)、耗資源,便于MCU硬軟件進(jìn)一步最簡(jiǎn)化及其成本最少考慮起見(jiàn),借助Mablib、Maple等的NN工具函數(shù)和仿真功能,針對(duì)實(shí)際測(cè)量數(shù)據(jù),訓(xùn)練各層的權(quán)值和閥值,形成表格,縮短訓(xùn)練和學(xué)習(xí)時(shí)間,即采用Mablib實(shí)現(xiàn)適配器,MCU僅做調(diào)整器實(shí)現(xiàn)。環(huán)境光的變化是手勢(shì)準(zhǔn)確度的最大影響因素,從系統(tǒng)實(shí)用和精簡(jiǎn)方面考慮,訓(xùn)練4種常用光環(huán)境:夜晚、白熾燈、日光燈、太陽(yáng)光,MCU系統(tǒng)在硬件上采用以光敏電阻-模數(shù)轉(zhuǎn)換ADC(Analog-to-Digital Converter)電路進(jìn)行選擇識(shí)別。
圖2 后向反饋神經(jīng)元網(wǎng)絡(luò)層次構(gòu)成示意圖
MCU在BP_NN中執(zhí)行前向輸出的運(yùn)算公式如下,這里激勵(lì)函數(shù)采用修正線性單元ReLU(Rectified Linear Unit),其中x/h/y分別對(duì)應(yīng)輸入/隱藏/輸出各層神經(jīng)元節(jié)點(diǎn),w/θ表示相應(yīng)前級(jí)的權(quán)重和閥值。
系統(tǒng)由三部分組成:信號(hào)采集識(shí)別發(fā)送終端(以下簡(jiǎn)稱采發(fā)終端)、手勢(shì)數(shù)據(jù)收集轉(zhuǎn)發(fā)終端(以下簡(jiǎn)稱收轉(zhuǎn)終端)和視窗展示服務(wù)軟件(以下簡(jiǎn)稱視窗展示軟件)。短距離無(wú)線通信采用抗干擾強(qiáng)、傳輸距離大、穿墻能力強(qiáng)、功耗低(發(fā)射最大功耗35 mA)的遠(yuǎn)距無(wú)線LoRa通信(Long Range Radio)形式。由于系統(tǒng)主要工作在Windows、UOS等通用操作系統(tǒng)下,并且服務(wù)以優(yōu)秀的微軟office等辦公軟件,轉(zhuǎn)換接口采用精簡(jiǎn)易用的WinUSB形式。終端微控制核心MCU,選用高性價(jià)比的32位的GD32F103TB(Cortex-M3內(nèi)核)或GD32VF103TB(RISC-V內(nèi)核),以C語(yǔ)言編程實(shí)現(xiàn)BP_NN適配器及其TOF數(shù)據(jù)采集與LoRa、USB傳輸通信。視窗展示軟件,以C++語(yǔ)言編程實(shí)現(xiàn)WinUSB驅(qū)動(dòng),以C++或Python語(yǔ)言編程實(shí)現(xiàn)手勢(shì)隨動(dòng)的各個(gè)視窗切換,伴隨視窗上隱含的微透明動(dòng)態(tài)指示,既可完成基本的PPT幻燈選擇播放,也可完成包括office(word、excel、ppt等)、acrobat(pdf電子文檔)在內(nèi)常用辦公軟件的遠(yuǎn)程切換翻頁(yè)操作,更可完成涵蓋辦公軟件在內(nèi)包括eclipse、notepad等集成開(kāi)發(fā)環(huán)境的遠(yuǎn)程研發(fā)演示,即3個(gè)版本:幻燈播放版、辦公播控版和開(kāi)發(fā)播控版[4-5,17]。完整的系統(tǒng)構(gòu)成及其核心算法模型與語(yǔ)言編程實(shí)現(xiàn),如圖3所示,詳細(xì)的微產(chǎn)品系統(tǒng)如圖4演示PPT截圖所示,圖4還示意了產(chǎn)品系統(tǒng)化的生產(chǎn)測(cè)試與配置的演示軟件。
圖3 微系統(tǒng)構(gòu)成及其核心技術(shù)手段示意圖
圖4 微產(chǎn)品系統(tǒng)應(yīng)用及其測(cè)試與配置示意圖
3.1.1 電子電路設(shè)計(jì)
核心功能實(shí)現(xiàn),以MCU片上接口——內(nèi)部集成電路IIC(Inter-Integrated Circuit)掛接ToF傳感器、異同步串行收發(fā)器USART(Universal Synchronous/Asynchronous Receiver/Transmitter)掛接LoRa無(wú)線模塊和ADC片上外設(shè)連接光敏電阻,同時(shí)以其片內(nèi)實(shí)時(shí)時(shí)鐘RTC(Real_Time Clock)完成ToF測(cè)距時(shí)刻記錄。便攜低功耗設(shè)計(jì)考慮,采用1 000~2 500 mA扁平鋰電池供電,以高性價(jià)比的ETA7640芯片作為充供電管理,同時(shí)設(shè)置按鈕喚醒重啟電路。ToF激光發(fā)射測(cè)距和LoRa數(shù)據(jù)發(fā)送,最大功耗達(dá)100 mA,供電轉(zhuǎn)換電路選用性能優(yōu)良的大功率LM1117_3V3,無(wú)檢測(cè)傳感時(shí)必須預(yù)以關(guān)斷,因而設(shè)計(jì)10 min內(nèi)無(wú)動(dòng)作時(shí)通過(guò)軟件關(guān)閉系統(tǒng),再次使用時(shí)以上述按鈕喚醒重啟。由于采用QFN36最小MCU封裝,此類MCU無(wú)VB電池待機(jī)/休眠管理腳,則控制關(guān)斷時(shí)直接切斷LM117供電電路。電源開(kāi)關(guān)選用P溝通MOS場(chǎng)效應(yīng)管PDN304P。ToF傳感器2.8 V供電時(shí)性能最佳,選用XC6206P28MR為其集群獨(dú)立供電,3.3 V與2.8 V之間的IIC總線采用N溝通MOS場(chǎng)效應(yīng)管2N7002過(guò)渡。模塊化電路設(shè)計(jì),MCU與ToF傳感器及其喚醒按鈕構(gòu)成小頂板,大底板攜帶鋰電池及其充供電路,并背附LoRa無(wú)線通信模塊。一大一小兩板在兩側(cè)通過(guò)“信號(hào)排針”連通[5,11,17]。終端平面大小不超過(guò)鋰電池大小35 mm×50 mm,包括鋰電池在內(nèi)整體厚度控制在8 mm內(nèi),“胸針”或“別針”形式,供胸前佩帶,充分體現(xiàn)“便攜”。選用透明熱縮管外封,既時(shí)尚大方又可免去昂貴的模具設(shè)制費(fèi)用。識(shí)別模塊設(shè)計(jì)最大持續(xù)工作時(shí)間不低于8h@1 000 mA,總體重量控制在250 gf內(nèi)。電路原理及其整體外觀造型如圖5所示。
圖5 采發(fā)終端電路原理及其整體造型示意圖
3.1.2 軟件體系構(gòu)造及其編制
穩(wěn)定高效設(shè)計(jì)及其開(kāi)發(fā)考慮,基于MCU寄存器和多中斷處理機(jī)制,構(gòu)造嵌入式軟件應(yīng)用體系,這里采用項(xiàng)目主持人的發(fā)明專利——ARM/RISC-V系列微控制處理器軟件架構(gòu)工具,快速得到包括啟動(dòng)代碼、系統(tǒng)時(shí)鐘變換配置、片內(nèi)接口/外設(shè)驅(qū)動(dòng)程序、中斷分配及其處理程序、多任務(wù)調(diào)度程序等的基本框架代碼,直接在此基礎(chǔ)上展開(kāi)填空式的功能代碼設(shè)計(jì)。
3.1.2.1 TOF測(cè)距及其BP_NN手勢(shì)識(shí)別
TOF測(cè)距,采用意法半的VL53L0X模塊,需要兩級(jí)驅(qū)動(dòng),一級(jí)驅(qū)動(dòng)IIC傳輸完成數(shù)據(jù)讀寫(xiě),二級(jí)驅(qū)動(dòng),移植相應(yīng)的應(yīng)用程序接口API(Application Programming Interface),配合模塊內(nèi)嵌算法處理得到最終距離數(shù)據(jù)。采用兩個(gè)中斷處理完成核心操作:IIC中斷和距離獲取事件中斷,優(yōu)先級(jí)最高,僅次任務(wù)調(diào)度的時(shí)鐘節(jié)拍中斷。兩級(jí)中斷的使用,把單次測(cè)距時(shí)間提升了18 ms內(nèi)(廠家提供的最小值為22 ms)。
中斷處理IIC操作,效率最優(yōu),但時(shí)序節(jié)點(diǎn)把握要求苛刻,特別是單字節(jié)的讀操作,很難駕馭,常常因此造成系統(tǒng)停滯,多采用通用輸入輸出GPIO(General-Purpose Input/Output)模擬實(shí)現(xiàn),軟件架構(gòu)工具得到的IIC驅(qū)動(dòng)程序,很好解決了這個(gè)問(wèn)題[19-21]。核心的底層IIC驅(qū)動(dòng)程序流程如圖6所示。
圖6 IIC中斷收發(fā)驅(qū)動(dòng)程序流程圖
關(guān)鍵的單字節(jié)讀取IIC操控時(shí)序如圖7所示,先寫(xiě)指定寄存器地址,再?gòu)闹凶x出數(shù)據(jù)。
圖7 恰當(dāng)?shù)钠瑑?nèi)IIC驅(qū)動(dòng)程序流程圖
距離獲取事件中斷,設(shè)置任務(wù)啟動(dòng)標(biāo)識(shí),處理程序在濾波插值、BP_NN適配輸出、手勢(shì)識(shí)別3個(gè)順序任務(wù)中實(shí)現(xiàn),首先是濾波插值,依據(jù)專家經(jīng)驗(yàn),只取連續(xù)(最大時(shí)間間隔100 ms)有效手勢(shì)距離(110~550 mm內(nèi))構(gòu)成原始數(shù)據(jù)隊(duì)列(長(zhǎng)度30內(nèi)),且每個(gè)傳感器應(yīng)用至少3個(gè)數(shù)據(jù),數(shù)量不足按照進(jìn)出計(jì)量場(chǎng)的數(shù)據(jù)變化規(guī)律插入,然后是BP_NN適配和手勢(shì)識(shí)別[8-9,19-21],核心的處理函數(shù)代碼如下:
char vldDtLgth = 0, vldDtFlg = 0, lstGst =‘z’; // 有效數(shù)據(jù)數(shù)量、標(biāo)識(shí)、上次手勢(shì)值
VL53L0X_RangingPoint vldDt[30] = {0}; // 有效數(shù)據(jù)存儲(chǔ)
unsigned int lstTs = 0; // 最近測(cè)量活動(dòng)時(shí)間記錄
void tof_bpnn_process(char maxNum) { // ToF數(shù)據(jù)分析處理[傳感器最大數(shù)量]
uint8_t i, m; unsigned short v; unsigned int ts;
VL53L0X_RangingMeasurementData_t msData[maxNum];
for(i=0; i if(((alarm_flag>>i)&1) 1) { alarm_flag &= ~(1 << i); VL53L0X_GetRangingMeasurementData(&vl53l0x_dev[i], &msData[i]); // 獲取測(cè)量距離 msData[i].TimeStamp = RTC_GetCounter(); ts = msData[i].TimeStamp; v = msData[i].RangeMilliMeter; if((v>110)&&(v<550)) { if((vldDtLgth 0)&&(vldDtFlg 0)) { vldDt[vldDtLgth].dvcNum = i; vldDt[vldDtLgth].TimeStamp = msData[i].TimeStamp; vldDt[vldDtLgth].RangeMilliMeter = msData[i].RangeMilliMeter; vldDtLgth += 1; lstTs = ts; vldDtFlg = 1; if((optFlg>>1)&1) printf("**%d-%d: %4dmm,%8d;
",vldDtLgth, i, v, ts); } else { if(((ts-lstTs)<10)&&(vldDtLgth<30)&&(vldDtFlg 1)) { vldDt[vldDtLgth].dvcNum = i; vldDt[vldDtLgth].TimeStamp = msData[i].TimeStamp; vldDt[vldDtLgth].RangeMilliMeter = msData[i].RangeMilliMeter; vldDtLgth += 1; lstTs = ts; if((optFlg>>1)&1) printf("**%d-%d: %4dmm,%8d;
",vldDtLgth, i, v, ts); } else vldDtFlg = 2; } } VL53L0X_ClearInterruptMask(&vl53l0x_dev[i],0); // 清除VL53L0X中斷標(biāo)志位 } } if((vldDtFlg 1)&&((RTC_GetCounter()-lstTs)>53)) vldDtFlg = 2; if(vldDtFlg 2) { // 手勢(shì)分析識(shí)別 VL53L0X_DtSetInterpolation (); // 插值 VL53L0X_BpNnAdepter(); // BP_NN適配輸出 VL53L0X_gestureRecognition(); // 手勢(shì)識(shí)別 if(gestureFlg) { // Lora外傳 printf(" gesture:%d
", m); gesture[5] = m | 0x30; USART1_SendData(gesture, 6); } } } 3.1.2.2 LoRa配置及其有效數(shù)據(jù)無(wú)線發(fā)送 配置包括LoRa地址、通道、升級(jí)通信速率,并進(jìn)入易用的透明傳輸方式,需要反復(fù)寫(xiě)入驗(yàn)證特別耗時(shí),僅做一次即可,無(wú)需每次啟動(dòng)時(shí)都執(zhí)行,設(shè)計(jì)快速啟動(dòng)運(yùn)行,運(yùn)行中需要修改時(shí)通過(guò)UART串口或USB接口通信實(shí)現(xiàn),盡管耗時(shí),卻一勞永逸,相關(guān)操控函數(shù)代碼如下: void Lora2G4_smpInit(void) { // Lora2G4模塊快速常規(guī)初始化 PIO_DataOutput(0, 5, 1); // 設(shè)置進(jìn)入連續(xù)透?jìng)鞴ぷ髂J?/p> PIO_DataOutput(0, 6, 0); PIO_DataOutput(0, 7, 0); while(PIO_DataInput(0, 4) 0); // 等待模塊有效 } void Lora2G4_vInit(void) { // Lora2G4模塊配置初始化 unsigned char i, b[15] = {0}; unsigned char a[6] = { 0xC0, // 工作模式: 參數(shù)掉電保存 {0x30, 0x31}, 0x28, 0x03, 0x04 }; // 地址, 波特率, 通道號(hào),透?jìng)?功耗 inFlshByteRead(0, AddrChnl, 3); // Lora地址通道號(hào) a[1] = AddrChnl[0]; a[2] = AddrChnl[1]; // Lora地址通道號(hào) a[4] = AddrChnl[2]; PIO_DataOutput(0, 5, 1); // 設(shè)置模塊進(jìn)入配置模式 PIO_DataOutput(0, 6, 1); PIO_DataOutput(0, 7, 1); while(PIO_DataInput(0, 4) 0); // 等待模塊有效 do { // 配置參數(shù)及其驗(yàn)證 Delay(300); USART1_SendData(a, 6); // 發(fā)送配置參數(shù) Delay(300); b[0] = b[1] = b[2] = 0xC1; // 驗(yàn)證配置 USART1_SendData(b, 3); Delay(100); i = USART1_QueueRcvData(b); }while((b[0]!=a[0]) || (b[1]!=a[1]) || (b[2]!=a[2]) || (b[3]!=a[3]) || (b[4]!=a[4]) || (b[5]!=a[5]) || (i!=6) ); PIO_DataOutput(0, 6, 0); // 設(shè)置進(jìn)入連續(xù)透?jìng)髂J?/p> PIO_DataOutput(0, 7, 0); USART1_CR1 &= ~6; // USART波特率調(diào)整: 禁止收發(fā) USART1_BRR = 0x00000138; // 波特率115200 USART1_CR1 |= 6; // 收發(fā)使能 while(PIO_DataInput(0, 4) 0); // 等待模塊有效 } void USART0_Process(void) // USART0數(shù)據(jù)收發(fā)處理 { unsigned int status = USART0_SR; if(status&15) return; // 異常處理: 校驗(yàn)錯(cuò), 幀錯(cuò), 噪聲, 溢出 else if((status>>5)&1) // 數(shù)據(jù)接收(環(huán)形隊(duì)列存放, 隊(duì)列滿則拋掉) { status = Usart0RcvPrt + 1; if(status>Usart0QueueLth) status = 0; if(status!=Usart0AppPrt) { status = USART0_DR; switch(Usart0RcvFlg) { case 0: if(status '*') Usart0RcvFlg += 1; break; case 1: if(status ' ') Usart0RcvFlg += 1; else Usart0RcvFlg = 0; break; case 2: case 3: case 4: Usart0RcvFlg += 1; break; default: break; } Usart0RcvQueue[Usart0RcvPrt++] = status; if(Usart0RcvPrt>=Usart0QueueLth) Usart0RcvPrt = 0; } else USART0_DR; } } 3.2.1 電子電路設(shè)計(jì) 收轉(zhuǎn)終端,USART串口無(wú)線LoRa接收,通用串行總線USB(Universal Serial Bus )“二傳”并供電,采用MCU片內(nèi)外設(shè)——USB2.0全速模塊[5-8][10-12],電路原理及其正反面mini造型如圖8所示。 圖8 收轉(zhuǎn)終端原理及其正反面mini造型圖 3.2.2 軟件體系構(gòu)造及其編制 仍然采用項(xiàng)目主持人的發(fā)明專利——ARM/RISC-V系列微控制處理器軟件架構(gòu)工具,快速得到基本框架代碼(包括USB驅(qū)動(dòng)程序),在此基礎(chǔ)上展開(kāi)填空式的功能代碼設(shè)計(jì)。采用USART中斷和USB中斷進(jìn)行LoRa無(wú)線透?jìng)餍畔⒔邮蘸蚒SB收發(fā)通信,中斷里接收,相應(yīng)任務(wù)中分析處理[17,22]。 LoRa手勢(shì)信息接收,在中斷處理程序中設(shè)置環(huán)形隊(duì)列高效接收識(shí)別,相應(yīng)任務(wù)中轉(zhuǎn)包調(diào)用USB發(fā)送任務(wù)上傳,關(guān)鍵程序代碼如下: int USART1_QueueRcvData(unsigned char *Data) // 環(huán)形隊(duì)列中斷數(shù)據(jù)接收 { int i = 0; USART1_CR1 |= 1 << 2; // 使能啟動(dòng)接收 if(Usart1AppPrt { do { i++; *Data++ = Usart1RcvQueue[Usart1AppPrt++]; } while(Usart1AppPrt!=Usart1RcvPrt); } else if(Usart1AppPrt>Usart1RcvPrt) // 隊(duì)列反向增長(zhǎng)數(shù)據(jù)接收 { do { *Data++ = Usart1RcvQueue[Usart1AppPrt++]; i++; if(Usart1AppPrt Usart1QueueLth) Usart1AppPrt = 0; } while(Usart1AppPrt!=Usart1RcvPrt); } return i; // 返回?cái)?shù)據(jù)接收量 } void USART1_Process(void) // USART1數(shù)據(jù)收發(fā)處理 { unsigned int status = USART1_SR; if(status&15) return; // 異常處理: 校驗(yàn)錯(cuò), 幀錯(cuò), 噪聲 if((status>>5)&1) // 數(shù)據(jù)接收(環(huán)形隊(duì)列存放,滿則拋掉) { status = Usart1RcvPrt + 1; if(status>Usart1QueueLth) status = 0; if(status!=Usart1AppPrt) { status = USART1_DR; switch(Usart1RcvFlg) { case 0: if(status '*') Usart1RcvFlg += 1; break; case 1: if(status '&') Usart1RcvFlg += 1; else Usart1RcvFlg = 0; break; case 2: if(status '^') Usart1RcvFlg += 1; else Usart1RcvFlg = 0; break; case 3: if(status ' ') Usart1RcvFlg += 1; else Usart1RcvFlg = 0; break; case 4: if(status '@') Usart1RcvFlg += 1; else Usart1RcvFlg = 0; break; case 5: Usart1RcvFlg += 1; break; default: break; } Usart1RcvQueue[Usart1RcvPrt++] = status; if(Usart1RcvPrt>=Usart1QueueLth) Usart1RcvPrt = 0; } else USART1_DR; } } while(1) { m = DrvUSB_EpRead(2, tmp, 6); // USB接收數(shù)據(jù)處理 if(m 0) { DrvUSB_EpWrite(1, tmp, 5); // 通過(guò)USB上傳 if((tmp[0] '*')&&(tmp[1] ' ')) { // Lora地址通道號(hào)變更 AddrChnl[0] = tmp[2] & 0x0F; AddrChnl[1] = tmp[3] & 0x0F; AddrChnl[2] = tmp[4] & 0x0F; inFlshPageErase(); // 閃存記錄修正 inFlshByteWrite(0, AddrChnl, 3); USART1_CR1 &= ~6; // USART波特率調(diào)整 USART1_BRR = 0x00000E98; // 波特率9600 USART1_CR1 |= 6; Lora2G4_vInit(); // Lora重新初始化 } } if(Usart1RcvFlg 6) { // Lora收到有效數(shù)據(jù) m = USART1_QueueRcvData(tmp); Usart1RcvFlg = 0; if(m 6) DrvUSB_EpWrite(1, tmp, 6); // 通過(guò)USB上傳 } } USB收發(fā)傳輸,不以傳統(tǒng)的人機(jī)接口設(shè)備HID(Human Interface Device)/配合主機(jī)USART轉(zhuǎn)USB做“借尸還魂”,直接采用高效的WinUSB格式,增加特定的操作系統(tǒng)描述、兼容ID特征描述和設(shè)備接口GUID描述符,并對(duì)收發(fā)任務(wù)函數(shù)做超時(shí)處理以避免程序阻塞,主要程序代碼如下: const unsigned char OsDscrptStr[] = // 操作系統(tǒng)描述字符串 { USBStrDscrpt_Lth(8), 3, USBStrDscrpt_Uncd('M'), USBStrDscrpt_Uncd('S'), USBStrDscrpt_Uncd('F'), USBStrDscrpt_Uncd('T'), USBStrDscrpt_Uncd('1'), USBStrDscrpt_Uncd('0'), USBStrDscrpt_Uncd('0'), USBStrDscrpt_Uncd(1) }; const CptbIdDscrpt cptbIdDscrpt = // 兼容ID特征描述符 {0x28, 0x100, 4, 1, // dwLength, bcdVersion, wIndex, wCount {0, 0, 0, 0, 0, 0, 0}, // Reserved[7] 0, 1, // bFirstInterfaceNumber, RESERVED {'W', 'I', 'N', 'U', 'S', 'B', 0, 0}, // compactiableID[8] {0, 0, 0, 0, 0, 0, 0, 0}, // subCompactiableID[8] {0, 0, 0, 0, 0, 0} // Reserved[6] }; const ItfcGuidDscrpt itfcGuidDscrpt = // 設(shè)備接口GUID描述符 { 0x8E, 0x100, 5, 1, // dwTotalSize, bcdVersion, wIndex, wCount 0x84, 1, 0x28, // dwSize, dwPropertyDataType, wPropertyNameLength USBStrDscrpt_Uncd('DeviceInterfaceGUID'), // bProperytName 0x4E, // dwPropertyDataLength USBStrDscrpt_Uncd('{12345678-1234-1344-1234-12345678ABCD}0') // bPropertyData }; // 單緩端點(diǎn)數(shù)據(jù)發(fā)送(查詢激發(fā)中斷發(fā)出), 參數(shù): 端點(diǎn)號(hào)1-7, 預(yù)發(fā)數(shù)據(jù), 數(shù)量, 返回[0-正確/-1超時(shí)] char DrvUSB_EpWrite(char EpNum, unsigned char* data, unsigned int counts) { unsigned int i; short sz; if(EpNum 1) sz = CfgDscrpts.EpIn1.wMaxPcktSz; while(counts) { i = 0; do { i++; if(i>maxTmOut) return 0xFF; // 超時(shí)退出 } while((EpDtSts>>EpNum)&1); // 時(shí)機(jī)查詢: 等待USBD空閑 if(counts>sz) // 超過(guò)最大包尺寸的發(fā)送 { USBD_BasicWrite(EpNum, 0, data, sz); data += sz; counts -= sz; } else // 最大包尺寸內(nèi)的發(fā)送 { USBD_BasicWrite(EpNum, 0, data, counts); counts = 0; } EpDtSts |= 1 << EpNum; // 標(biāo)識(shí)需要數(shù)據(jù)發(fā)送 } return 0; } // 端點(diǎn)數(shù)據(jù)接收, 參數(shù): 端點(diǎn)號(hào)1-7, 預(yù)發(fā)數(shù)據(jù), 數(shù)量, 返回[0-正確/-1超時(shí)] char DrvUSB_EpRead(char EpNum, unsigned char* data, unsigned int counts) { unsigned int i; RcvPt = data; while(counts) { i = 0; do { i++; if(i>maxTmOut) return 0xFF; // 超時(shí)退出 } while(!((EpDtSts>>EpNum)&1)); // 等待USB數(shù)據(jù)到來(lái) counts -= RcvCnt; EpDtSts &= ~(1 << EpNum); // 標(biāo)識(shí)已經(jīng)取得USBD收到數(shù)據(jù) } return 0; } 3.3.1 WinUSB驅(qū)動(dòng)及其數(shù)據(jù)接收實(shí)現(xiàn) 針對(duì)常規(guī)Windows、UOS操作系統(tǒng)應(yīng)用,直接調(diào)用Win7以上內(nèi)嵌的WinUSB.dll動(dòng)態(tài)庫(kù)實(shí)現(xiàn),主要是操控句柄的開(kāi)關(guān)和接收監(jiān)聽(tīng),采用兩個(gè)定時(shí)器分別開(kāi)關(guān)線程實(shí)現(xiàn)收轉(zhuǎn)USB終端的即插即用和動(dòng)態(tài)接收監(jiān)聽(tīng)。只要底層嵌入式應(yīng)用軟件中添加特定描述支持,Windows就可以自動(dòng)識(shí)其為WinUSB設(shè)備,上層可視化測(cè)試/應(yīng)用程序就可以通過(guò)WinUSB.dll與其進(jìn)行USB數(shù)據(jù)傳輸通信。這樣, USB設(shè)備的數(shù)據(jù)采集、監(jiān)視控制、配置參數(shù)、軟件刷新等編程操控,就同傳統(tǒng)的“RS-232C通信”一樣了,無(wú)需USB轉(zhuǎn)串口作硬軟件轉(zhuǎn)接,而且速度快、實(shí)時(shí)性強(qiáng),直截了當(dāng)[6][10-15]。相關(guān)關(guān)鍵代碼如下: HANDLE hDeviceHandle = INVALID_HANDLE_VALUE; // 設(shè)備文件句柄 WINUSB_INTERFACE_HANDLE hWinUSBHandle // WinUSB操作句柄 = INVALID_HANDLE_VALUE; OVERLAPPED overlapped; // 異步收發(fā)緩存結(jié)構(gòu)變量 BOOL flgWinUSB = false; // WinUSB設(shè)備存在與否[true存在] char thrdStt = 0; // 線程建立標(biāo)識(shí)狀態(tài)[0-無(wú)/1-建] void __fastcall TfmShow::SystemInit(void) { // 系統(tǒng)初始化 static const GUID stm32F1xxDvcItfc = { 0x12345678, 0x1234, 0x1344, { 0x12, 0x34, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC } }; GUID guidDeviceInterface = stm32F1xxDvcItfc; BOOL bResult = TRUE; AnsiString str; bResult = GetDeviceHandle(guidDeviceInterface, &hDeviceHandle); if (!bResult) { inOutTmr->Enabled = true; sttFlg->Font->Color = clGray; return; } bResult = GetWinUSBHandle(hDeviceHandle, &hWinUSBHandle); if (!bResult) { inOutTmr->Enabled = true; sttFlg->Font->Color = clGray; return; } flgWinUSB = true; // 設(shè)備已經(jīng)正常尋址到 inOutTmr->Enabled = false; sttFlg->Font->Color = clRed; if(thrdStt 0) { // 啟動(dòng)并運(yùn)行線程 rcvThrdDt = new rcvThrd(true, imgDrct, sttFlg, spcTmr, inOutTmr); thrdStt = 1; } rcvThrdDt->FreeOnTerminate = true; rcvThrdDt->Resume(); UINT timeout = 100; // 最大傳輸時(shí)間[ms] bResult = WinUsb_SetPipePolicy(hWinUSBHandle, 0x81, PIPE_TRANSFER_TIMEOUT, sizeof(timeout), &timeout); if(!bResult) { str = "設(shè)置超時(shí)錯(cuò)誤:"; str += GetLastError(); ShowMessage(str); return; } ZeroMemory(&overlapped, sizeof(overlapped)); overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); } void __fastcall rcvThrd::RcvDt() { if(hWinUSBHandle INVALID_HANDLE_VALUE) return; ULONG cbRead = 0; UCHAR szBffr[20] = {'0'}; AnsiString str; BOOL bResult = WinUsb_ReadPipe(hWinUSBHandle, 0x81, szBffr, 15, &cbRead, &overlapped); if(bResult) { const char* t = szBffr; str = t; windowChange(str); } else if(ERROR_IO_PENDING GetLastError()) { bResult = WinUsb_GetOverlappedResult(hWinUSBHandle, &overlapped, &cbRead, true); if(NULL != cbRead) { int m = cbRead; cbRead = 0; if(bResult) { const char* d = szBffr; str = d; char i = 0; windowChange(str); } } } else if(22 GetLastError()) inOutTmr->Enabled = true; // 設(shè)備撥出 } 3.3.2 視窗動(dòng)態(tài)展現(xiàn)及其控制軟件編制 開(kāi)發(fā)之初,設(shè)想用通用流行的Python語(yǔ)言開(kāi)發(fā),擬用PyUSB、Kivy/Tkinter、python-pptx/word/excel/acrobat,PyKeyboard/PyMouse等庫(kù),深入之后發(fā)現(xiàn)這些庫(kù)多是用C++開(kāi)發(fā)打包的,多是針對(duì)WinAPI的包,而且不新、不全面,作為解釋語(yǔ)言也沒(méi)有編譯語(yǔ)言C++運(yùn)行效率高,與其特制一些把C++庫(kù)彌補(bǔ)Python應(yīng)用,倒不如與底層合二為一,直接嵌入其監(jiān)聽(tīng)線物程中實(shí)現(xiàn),更為經(jīng)典直接,于是回到了C++開(kāi)發(fā)實(shí)現(xiàn)。這里選用Enbarcadero的RadStudio開(kāi)發(fā)環(huán)境,主要是兩類窗口:引導(dǎo)窗口和展示窗口。引導(dǎo)窗口,小視野透明暗標(biāo)動(dòng)態(tài)圖文指示,控制展示窗口切換和翻頁(yè)。展示窗口,展示常規(guī)辦公軟件和特定開(kāi)發(fā)軟件的窗口,主要通過(guò)調(diào)用Windows視窗變換和鍵盤(pán)模擬操控API函數(shù)實(shí)現(xiàn)[6][10-15]。核心程序函數(shù)代碼如下: BOOL SetTopWindow(HWND hWnd) { // 設(shè)置窗口到最頂層[參數(shù):窗口句柄] HWND hForeWnd = GetForegroundWindow(); DWORD dwForeID = GetWindowThreadProcessId(hForeWnd, NULL); DWORD dwCurID = GetCurrentThreadId(); AttachThreadInput(dwCurID, dwForeID, TRUE); ShowWindow(hWnd, SW_MAXIMIZE); SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); SetWindowPos(hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); SetForegroundWindow(hWnd); AttachThreadInput(dwCurID, dwForeID, FALSE); return TRUE; } 窗口隨動(dòng)變換展示的程序流程圖如圖9所示。 圖9 窗口隨動(dòng)變換展示的程序流程圖 生產(chǎn)出廠測(cè)試,輸出指示原始的手勢(shì)分析結(jié)果和通信通道的暢通性,配置LoRa無(wú)線通信地址、通道及其是否輸出原始測(cè)距數(shù)據(jù)。設(shè)計(jì)有專用的可視化USB軟件,完成手勢(shì)分析原始數(shù)據(jù)的即時(shí)查看和LoRa無(wú)線通信配置。同時(shí)底層采發(fā)終端還支持使用USB串口助手查看手勢(shì)分析原始數(shù)據(jù)、原始測(cè)距數(shù)據(jù)詳細(xì)查看和LoRa無(wú)線通信配置[6-8]。專用可視化USB軟件和USB串口助手操控界面,如圖10~11所示。 圖10 專用可視化USB軟件實(shí)現(xiàn)的測(cè)試與配置示意圖 圖11 USB串口助手實(shí)現(xiàn)的測(cè)試與配置示意圖 要做到這些,終端軟件中需要添加相應(yīng)支持,采發(fā)終端中相關(guān)的典型實(shí)現(xiàn)函數(shù)代碼如下: void Lora2G4_chgCfg(void) { // Lora地址通道變更/系統(tǒng)打印信息[配合USART0接收中斷] unsigned char tmp[10] = {0}, m; if(Usart0RcvFlg 5) { // 收到有效數(shù)據(jù) m = USART0_QueueRcvData(tmp); Usart0RcvFlg = 0; if(m 5) { if(tmp[2]<0x41) { // Lora地址通道變更 AddrChnl[0] = tmp[2] & 15; AddrChnl[1] = tmp[3] & 15; AddrChnl[2] = tmp[4] & 15; inFlshPageErase(); // 閃存記錄修正 inFlshByteWrite(0, AddrChnl, 3); USART1_CR1 &= ~6; // USART波特率調(diào)整 USART1_BRR = 0x00000E98; // 波特率9600 USART1_CR1 |= 6; Lora2G4_vInit(); // Lora重新初始化 } else { // 系統(tǒng)輸出指示信息變更 if(((optFlg>>1)&1) 0) optFlg |= 1 <<1; // 詳細(xì) else optFlg &= ~(1 << 1); } } } } 激光手指飛行導(dǎo)航簡(jiǎn)易微系統(tǒng),試銷以后,以極高的性價(jià)比,滿足了各類學(xué)校、企業(yè)、酒店、機(jī)關(guān)、場(chǎng)所的尋常應(yīng)用需要,內(nèi)外循環(huán)行銷,市場(chǎng)前景頗佳,緊接著就圍繞微型化和低功耗設(shè)計(jì)又推出了二代產(chǎn)品。另外,頂板即主板,還作為獨(dú)立的ToF手勢(shì)或雷達(dá)測(cè)距測(cè)向模塊,單獨(dú)出售,填補(bǔ)遠(yuǎn)距離手勢(shì)導(dǎo)航和低成本雷達(dá)測(cè)距測(cè)向領(lǐng)域的空白。 激光手指飛行導(dǎo)航簡(jiǎn)易微系統(tǒng),麻雀雖小,五臟俱全,以神經(jīng)元深度學(xué)習(xí)自適應(yīng)人工智能手勢(shì)識(shí)別核心算法數(shù)學(xué)處理模型為中心,既涵蓋了數(shù)電/模電/射電在內(nèi)的低功耗微型電子電路設(shè)計(jì),也包括了嵌入式C語(yǔ)言應(yīng)用軟件設(shè)計(jì)、C++驅(qū)動(dòng)程序設(shè)計(jì)和C++/Python動(dòng)態(tài)視窗切換技術(shù),通用流行的局部長(zhǎng)距離無(wú)線通信和各類局部總線接口技術(shù)一應(yīng)俱全,既是可探索開(kāi)發(fā)項(xiàng)目的過(guò)程研發(fā),也產(chǎn)生了可測(cè)試生產(chǎn)行銷的產(chǎn)品系統(tǒng)。在此基礎(chǔ)上,加大深度學(xué)習(xí)自適應(yīng)人工智能算法處理,實(shí)現(xiàn)鼠標(biāo)功能的“遠(yuǎn)距離移動(dòng)飛鼠”,將有望成為可能,“學(xué)研”應(yīng)用顛覆突破,意義重大,拭目以待,為期不遠(yuǎn)。3.2 接收轉(zhuǎn)換傳輸電子終端構(gòu)造
3.3 WinUSB接收及其視窗展示控制實(shí)現(xiàn)
4 產(chǎn)品系統(tǒng)化測(cè)試及其配置考慮
5 產(chǎn)品系統(tǒng)的應(yīng)用統(tǒng)計(jì)分析
6 結(jié)束語(yǔ)