在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 範例程式有兩個版本,這是舊版的執行畫面:

舊版的好處是,他PC 的程式不用掛任何Library,您可以完全自行修改組譯,
組譯完成後,您就可以一支應用程式帶著到處跑了。
另一個為新版的執行畫面:

這一支範例程式是要掛原廠所附的一支動態連結Library 的:SLABHIDDevice.dll
您組譯完之後,執行檔的目錄下也一定要有這一支檔案才能順利執行。
當然啊,原廠這一支DLL 檔案他是整合了他原廠相關所有USB 應用範例程式所需的。
個人就覺得沒有這個必要,USB 的東西已經很難搞得懂了,您還把他們東包西包的,
初學者都沒辦法完全掌握程式流程,只是讓初學者更難上手而已。...
至於如果您自己覺得要這樣子比較好用,我個人也沒意見。
----
但是在USB DEVICE 端的韌體就沒有差別了。在USB HID Class 的Report Descriptor
他總共定義了六個Report ID :如下圖

其中有兩個Input data ,而那一個SelectorID 他是打算用Interrupt Interface 來處理。
另外還有一個雙向的Feature (Report ID = 0x06),剩下的全都為Out Data 。
我想一般會用HID 應該這幾個基本的定義就綽綽有餘了。
好了,既然USB HID 是使用微軟作業系統的標準驅動程式,所以,您在PC 端的上層
應用程式,就得依據微軟作業系統所提供的呼叫函數來用了。
所以針對上述的那一些基本資料傳輸的標準呼叫函數為下表所示:

注意:他有附帶說明:這一些呼叫函數都是指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 端的上層應用程式的呼叫函數:

我把原廠的HID_Blinky.SetReport_Interrupt 給註釋掉...意思就是說:
他原廠原本就是希望透過Endpoint 1 的Interrupt Interface 來輸出資料。
但我們從USB 分析儀上看到的卻是Endpoint 0 的Control Interface來下資料的。

這是完全符合上述的微軟API 的呼叫函數的說明。
所以,我當然也可以直接利用HID_Blinky.SetReport_Control (WriteFile)來
透過Endpoint 0 之Control Interface 來下資料。就是我上圖上層程式所示,
至於,USB 分析儀看到的,就如下圖所示:

----
所以,原廠的範例程式明明跟您說:他是用HID_Blinky.SetReport_Interrupt 函數
來下資料,其實,他卻是用Endpoint 0 的Control Interface 來下資料的。
如果您沒留意的話,您又會被原廠範例程式給誤導了。
所以,您在原始的USB HID Interface Descriptor 中,要明確的定義出您有支援
Endpoint 1 的Interrupt Interface...這樣子,微軟的作業系統才知道利用
WriteFile 呼叫函數來把資料下到Endpoint 1 的Interrupt Interface:
---

我們就把他改成這一種方式,再作一次實驗,我們就可以用一樣原廠的上層範例程式,
就可以把Report ID = 0x01 的資料下到 Endpoint 1 的Interrupt Interface 中了:

沒錯吧!...
---
至於,在這個範例中,他有定義兩個In Data 的Report ID,其中Report ID=0x04 是用
Interrupt Interface 呼叫函數(HidD_GetInputReport),而Report ID=0x05就是用
ReadFile 的 Endpoint 0 的Control Interface 來抓資料的:如下圖所示:

----
講完了,很簡單吧,但是我想市面上應該沒有任何一本中文參考書有交代這麼清楚的吧。
好啦...那一定有人會想問我說:那到底用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 匯流排。您看下面這張圖就知道我的意思了。

(圖上從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 還是真的厲害!)