在PC 端的應用程式的一些注意事項,然後可能又會再暫停一陣子。
這一回寫USB HID 真的是標準的自學計畫,一來真的在系統應用設計上真的有這個需求,
才不得不又拿起USB MCU 來把玩一下,否則,有時還真的蠻難說:非得為了搞懂USB
才去搞USB,這樣子您還是會因為找不到真正的應用產品。上週跟客人在台北合江街
喝咖啡時,客人一直感慨說:現在賣IC 真的越來越辛苦,以前都還會跟客人要求MOQ 的。
但這幾年來,在國內生意真的越來越難做,不要說MOQ 啊,只要一個月可以先幾百顆或
幾千顆的...就如同恩同再造似的,業務跑得很勤勞的。誰怪這幾年下來台灣電子行業
蓬勃發展所帶來價格與市場崩盤,記得兩三年前一棵SOT223 包裝的 1117 穩壓IC 還有
至少台幣兩三塊的行情,現在連0.9 元台幣都有人在喊。...您就跟賣IC 通路業務說:
有需要這麼殺嗎?!...他還煞有其事的回答您說:"那請問客人大人您,那您有更好的
產品可以做嗎?!" 。想一想,他講得也沒錯,人家是天天在外面跑市場的,有市場
或產品訊息他們最懂了。現在要搞電子產品,又要有量,又要有穩定訂單的,您說:
沒有蘋果的加持,請問您要做什麼?以前我們還可以做MP3 隨身聽,或搞個什麼GPS
手持導航的...甚至什麼數位相機啊、什麼多媒體播放器等等...結果,全被現在的
智慧型手機,平版電腦給全打趴在地上了。這就不用再去質疑什麼了,您只要翻開
目前上市櫃公司裡,只要沒跟智慧型手機沾上邊的,業績都一路掉。就連電子行業
裡的指標股--- 大M,最近也被外資法人一直唱衰,您就不用再看別的個股了。
如果現在才想到要搞電子,有時想想:還真的生不逢時啊。
-----------------------------------
好吧,不管如何,我們還是把這一系列的章節講完吧。
關於USB HID 的SiLabs原廠範例程式是:"BlinkyExample"...至於您不是用這一家的,
不好意思,我沒辦法雨露均沾的全面說明,現在賣IC 要別人幫忙搞系統應用的,
講難聽一點,沒有一定的"好處",誰要理您啊?!這個"好處"的解讀就很多種了。
譬如:您公司從來沒聽過搞過什麼MCU 或是相關產品,甚至連USB 產品的邊都沒沾上,
突然就冒出一個USB MCU 要別人搞?...別人還會懷疑說:您會不會玩一下就不玩了?
那我們為什麼還要幫您抬轎呢?搞了老半天,結果原來您們也只是想Try 市場而已。
所以啦,如果您真的要挑一棵USB MCU 來玩的話,這一個問題要想清楚,不要別人
隨便不知從哪來搞出一片EV 版子,然後您就如獲至寶亂搞一通,不要到時候,
人家給您斷貨或是給您開天窗,您再來那邊 @ # % &...誰理您啦!
這一支BlinkyExample 的PC 範例程式有兩個版本,這是舊版的執行畫面:
![](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXtHQyrYb7GaHz6NO8OcdOSV2p2_Vn-mhXLjYUjyAZw6r7mPtoUNa4_u5y3Dpfk2m1fBi45i1a9QLfaubIJwH9a1PN4Gur-R-bxt3b1Amp1rd5uDye5wEn6cUZNEWNQy1Q_OLszPp8qmn7/s1600/XlOzpoCvOEKbdYGkCJZS2Q.jpg)
舊版的好處是,他PC 的程式不用掛任何Library,您可以完全自行修改組譯,
組譯完成後,您就可以一支應用程式帶著到處跑了。
另一個為新版的執行畫面:
![](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjuSMna7a-jc9sMGDilVpMvH0ds23qyiYNs6PqRqfUKgQFt5-4qETzUk5i13zts8H1Deni_lRqN3-iwKBx0FIol6xtccfQtWsc9uieahz5vEffNvd-lztkXaDnNbiEWDXtMGyC5A-YZ9pLA/s1600/UxQGOBPIXmsVqZHEFaFXOw.jpg)
這一支範例程式是要掛原廠所附的一支動態連結Library 的:SLABHIDDevice.dll
您組譯完之後,執行檔的目錄下也一定要有這一支檔案才能順利執行。
當然啊,原廠這一支DLL 檔案他是整合了他原廠相關所有USB 應用範例程式所需的。
個人就覺得沒有這個必要,USB 的東西已經很難搞得懂了,您還把他們東包西包的,
初學者都沒辦法完全掌握程式流程,只是讓初學者更難上手而已。...
至於如果您自己覺得要這樣子比較好用,我個人也沒意見。
----
但是在USB DEVICE 端的韌體就沒有差別了。在USB HID Class 的Report Descriptor
他總共定義了六個Report ID :如下圖
![](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEioXf7UjW3uEtTsl4m4V_LZ7mN4OwjDx3BY8C2oxzHKsRXYBA0WR1aCn4_R0hpldj30ePklM3dKHuZyBYYZCBMHjwbBosis9TJ3nztE34gTP2Ws8Xl7jBQnps4GaV5cMW1S0rMMJB0xzkIT/s1600/95rq4zR0OBJafMaFohr8Pg.jpg)
其中有兩個Input data ,而那一個SelectorID 他是打算用Interrupt Interface 來處理。
另外還有一個雙向的Feature (Report ID = 0x06),剩下的全都為Out Data 。
我想一般會用HID 應該這幾個基本的定義就綽綽有餘了。
好了,既然USB HID 是使用微軟作業系統的標準驅動程式,所以,您在PC 端的上層
應用程式,就得依據微軟作業系統所提供的呼叫函數來用了。
所以針對上述的那一些基本資料傳輸的標準呼叫函數為下表所示:
![](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiaHv5c1cXhSRupcyfRB3OIYEoRiTWTLGSi9g0lwDJRmS8-OoYyqoFHnm9CgZ45cRg0Uxr2G_UFHwhmpJhy3BsepFxtKd0tD66ZZ6fJJklUFQontjU0Fwn4WOMY5aavF3iDdnQCh7sgiwOF/s1600/eRIRq.paVyu40TKs9z.Bng.jpg)
注意:他有附帶說明:這一些呼叫函數都是指M$ Windows XP 作業系統版本後才可以用的。
所以,您就知道:為何許多搞USB的人在適用Windows XP 之後,就不太想再更新作業系統
了,
其中,其實那個Get/Set Feature 也是透過標準的Endpoint 0 的Control Interface 來下
的啦。比較值得留意的是:那一個WriteFile 的說明是比較複雜的,因為所謂WriteFile
指的就是PC USB Out Data,那到底是走Endpoint 0 的Control Interface呢?還是
Endpoint x 的Interrupt Interface呢?...他的說明是:如果有Endpoint x(通常是1)
Interrupt Interface 定義的話,他就會用Interrupt Interface 來傳資料,否則,
他會用Endpoint 0 的Control Interface 來傳資料...這一點對於USB Device 端的
韌體程式是不一樣的啦。...很不幸的是:這一支原廠的範例韌體程式,他在兩邊
Endpoint 0 與Endpoint 1 都有寫一樣功能的程式,所以,如果您沒有留意的話,
您根本不知道PC 端應用程式是透過哪一個Interface 傳給您USB Device 韌體的。
因為:這一支標準範例程式都會正確接收資料的。
---
這一點我要補充說明一下USB HID Class 的規範:他有說明,Interrupt In Data
Interface 是一定要具備的;至於Interrupt Out Data Interface 是可有可無,
沒有硬性規定的。完全看系統應用端自行定義。
好了,我們就來看PC 端的上層應用程式的呼叫函數:
![](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhSIeMgYXHYI4FHJyfARDqYFIei4Xsl2AZmQEFrSQwFlHmYS5Gl9sFCRj7cV2k3awVAGlGcOdtWHz8DBss426u1SxFjTCLpD5z_R-NsqkQaN-K5sWC9mHn-BcSQrlAuMOm3Dwwk6xZn-OQe/s1600/itUagsCirFDhK8Ba_y6WYg.jpg)
我把原廠的HID_Blinky.SetReport_Interrupt 給註釋掉...意思就是說:
他原廠原本就是希望透過Endpoint 1 的Interrupt Interface 來輸出資料。
但我們從USB 分析儀上看到的卻是Endpoint 0 的Control Interface來下資料的。
![](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidSngEbU0ubm4pgHq7wwkSI8Sbvl3deyRzzPkrlV2nOxSKV7QhZFHa1BgQZmtJdEk54zi4YOOWXgjlwvOBI6lYsXfsp7ADZseCfOmZB1L33YQHhq_CgVs4pX2rOD_nr-kOlFzD_PPJiOY0/s1600/zEBwX7SnBuBy6nHLdrxHqw.jpg)
這是完全符合上述的微軟API 的呼叫函數的說明。
所以,我當然也可以直接利用HID_Blinky.SetReport_Control (WriteFile)來
透過Endpoint 0 之Control Interface 來下資料。就是我上圖上層程式所示,
至於,USB 分析儀看到的,就如下圖所示:
![](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhXEQXSvZ0y6MKgJdsLf4uAg-4paAfUPmRimV_GnbhlIJ-7ZvOi7_lVuuU2AKBE8DyrLb6lV9CtzAYcxGvfT5QKxVDb1zAxfn6pv3noHm77VBT5KxUIPe7ATJBD7VqjBUbZeQ70Tqji2fXE/s1600/AqPjvrCYZqldU4Hv_HS2QQ.jpg)
----
所以,原廠的範例程式明明跟您說:他是用HID_Blinky.SetReport_Interrupt 函數
來下資料,其實,他卻是用Endpoint 0 的Control Interface 來下資料的。
如果您沒留意的話,您又會被原廠範例程式給誤導了。
所以,您在原始的USB HID Interface Descriptor 中,要明確的定義出您有支援
Endpoint 1 的Interrupt Interface...這樣子,微軟的作業系統才知道利用
WriteFile 呼叫函數來把資料下到Endpoint 1 的Interrupt Interface:
---
![](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQOQom16fWlkoO4ov8HrfNlzhj1kyHerkOv-qW5lRMcwWVDADs32l1ZSgUOhk0dKyugJE2eWgd1ewtxgs69f2F9-__8cJB8f5POXJFOgAQ535D_APdK8Q9BVOKst0WzV58EYQnRewv1J7c/s1600/tCFEW7GSNYp1tCoqEUQGoA.jpg)
我們就把他改成這一種方式,再作一次實驗,我們就可以用一樣原廠的上層範例程式,
就可以把Report ID = 0x01 的資料下到 Endpoint 1 的Interrupt Interface 中了:
![](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDYiP7U3mA5iPeKnOCljHg9CYowmB24pGRnWpAXm-9PSM-rGN1E2lUigawDOLWT9bDKqeWneTMb3xx1aH3a_vXiv6VZKkZYqSLtvRQbC5IZ-ii66dIAK5Ka946zX4ZgAS6edOg5KgrsWj7/s1600/CGezD.ysDf3glJDixEWg0Q.jpg)
沒錯吧!...
---
至於,在這個範例中,他有定義兩個In Data 的Report ID,其中Report ID=0x04 是用
Interrupt Interface 呼叫函數(HidD_GetInputReport),而Report ID=0x05就是用
ReadFile 的 Endpoint 0 的Control Interface 來抓資料的:如下圖所示:
![](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjRqKkG7MUKpw_ThxBs0IQBv1_JtyW40luTsYL2aarhco5VxEfdMVMnFoM4fqJk4GQCNPRc7oHWVWGZjyQknzBDWAgaOBbXTzSOvQ_1lQW81flJ5F4t1Cfd7uGUi2ZZ145KSk2FZRNzpJd_/s1600/fbRPO5HtguOtCbiAWrESgA.jpg)
----
講完了,很簡單吧,但是我想市面上應該沒有任何一本中文參考書有交代這麼清楚的吧。
好啦...那一定有人會想問我說:那到底用Endpoint 0 的Control Interface 來In/Out
資料比較好呢?還是用Endpoint x 的Interrupt Interface來In/Out 資料比較好呢?
您看那個Control Interface 每一次In/Out 資料都是一大串的Setup/In(Out)/Null 的。
好像比較沒效能,其實,剛好相反...因為,這一種Control Interface來In/out 資料,
只要上層下一次命令,他就只作一次。而那一種Interrupt Interface 他是會一直掛在
USB 匯流排上一直等資料...(其實,我們用一大堆NAK 擋掉的),積少成多,
USB 匯流排的傳輸頻寬是固定的,您老是用一大堆NAK 來佔用頻寬,當然您的USB傳輸速率
老被您嫌慢...萬一您的一個USB 端口用掛了一大堆這一種HID 支援Interrupt Interface
的...想必也是一定一大堆NAK 在USB 匯流排上的...這也難怪人家 USB HID Class 命令
定義中,也不想強制定義您的Out Data 也要用Interrupt Interface了,以免一大堆
HID 裝置中的NAK 佔去太多USB 匯流排。您看下面這張圖就知道我的意思了。
![](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhMHo2qGASZhtKxQlxCcaCcrQL26f3mMCSz0fuSG9u6bq6ibRFb3ThuJtLDLj20mOSck2C6kBVIP6_Y2x42V0hB825YH9mE8s9RrhgK1e3PZLzM4LJv1XKVB7myqSBauuJ8kzhpVq_Et6GV/s1600/hLSK7pOksSlhej2gGRdS9A.jpg)
(圖上從Packet# 4570 一直到#4951 及#7002 都有塞進一大堆Endpoint 1 的NAK)
還有一點是Interrupt In Interface 的缺點:就是當M$ 的底層Driver 把您要的資料抓
進來之後,如果您上層的AP 應用程式沒有及時抓走的話,底層的驅動程式再從USB Device
端再抓一次資料時,會把上一次的資料給覆蓋掉的啦。所以,某種程度來說:您會以為
好像有掉資料似的。---- 有興趣者可以去查M$ 的MSDC 相關函數說明。
---
所以,沒有任何一種規格都是十全十美的,也沒有任何一種規格或產品都會適合您的
系統應用需求,我們之所以會討論這麼多關於系統問題,就是希望說:您越能瞭解
許多規格,您就越會懂得如何在系統應用上彈性應用您的解決方案...這一點就是我們
在系統應用上的最大價值了。謝謝!
(完)
-------------
1. 改寫原廠的USB應用程式
2. 改寫原廠的USB應用程式(續一)
3.改寫原廠的USB應用程式(續二)
4.USB DIY-- 自學計畫(一)
5.USB DIY-- 自學計畫(二)
6.USB DIY-- 自學計畫(三)
7.USB DIY-- 自學計畫(四)
=D>
回覆刪除感謝整理如此詳細。
不客氣,就引用以下一個影片的最後一句話:
刪除Life is for sharing...
(蘋果的iPhone 還是真的厲害!)