2017年7月13日 星期四

USB DIY--自學計畫 (N+1)

我查了一下我最後一篇寫了USB DIY 是在 2012 年所寫的。那是五年前...

人生有多少五年?尤其是在工作職場上去堅持一件事。

當然啊~當你年紀越大,能選擇的機會就越少了。所以人家才說人怕入錯行啊。

而對我來說:我早就說過了,只要我還能做,還能寫,我應該還是多多少少會摸一點

技術DIY 的東西吧。沒辦法,這應該就是一種天生工程師的毛病。

而這一次剛好有機會要在短短一兩個月內重新整理一套USB Customer HID 裝置的東西。

所以也就順便可以再整理一篇 關於USB Customer HID 的東西。

為什麼?會稱為USB Customer HID ?因為在新一代 ARM 32bit Cortex-M3 上,人家的

原廠所提供的範例程式函數庫,就稱為USB Customer HID。意思就是:它基本上還是

走標準USB HID Class,但卻不用任何標準上層應用程式。譬如舉個例子說:

如果你的HID 裝置宣告成滑鼠之後,當你插上PC 電腦之後,作業系統不但接手驅動程式

之外,還會同時啟動他自己本身所對應滑鼠的應用程式,所以你就可以發現你的滑鼠

已經不受你控制了。說實在的~這一種USB HID  裝置就不是我們一般DIY 人士或是特殊應用

所需的東西。這一類USB 裝置產品大概就是一般要拼命降低成本去搶滑鼠鍵盤生意的

SOC IC 公司會做的事,應該也會很少會去用 32 bit ARM 吧。除非是反過來,是在ARM 的

系統平台裡,要支援滑鼠,不過這是反過來用,自己本身是USB HOST,那是另一個故事了。

其實要在USB 裝置裡,將 標準HID 滑鼠,轉換成一般 Customer HID 很簡單,就是將USB

Device Descriptor 中的 HID Descriptor 中改掉兩行就可以了:

{ // interface_descriptor hid_interface_descriptor
// Interface 1 (Mouse):
INTERFACE_DESCRIPTOR_SIZE,            // Length = 9 bytes
USB_INTERFACE_DESCRIPTOR_TYPE,        // Descriptor Type
0x01,                                 // Interface Number (Interface 1)
0x00,                                 // Alternate Setting
0x01,                                 // Number of Endpoint (1 Endpoint)
0x03,                                 // Interface Class (HID class device)
0x00,                                 // Interface Sub Class (No Boot)
0x00,                                 // Interface Protocol (Vendor)
// 0x01,                                 // Interface Sub Class (Boot Device)
// 0x02,                                 // Interface Protocol (Mouse)
0x00,                                 // Index of string descriptor describing this interface
},

另外就是 Usage 宣告也要改一下:

code const U8 HID_Report1_Descriptor[]=

{
  0x06,0x00, 0xff,      // USAGE PAGE(Vendor Defined Page 1)
  0x09,0x01,        // USAGE(Vendor Usage 1)
//  0x05, 0x01, //Usage Page (Generic Desktop Control)
//  0x09, 0x02, //Usage (Mouse)
  0xa1, 0x01, //Collection (Application)

...
...

但是從此之後,

作業系統只會幫你掛上驅動程式,但就不會有任何應用程式可以供你使用了。

這一部分你就得自己想辦法自行解決了。而我發現這才是大家搞USB 裝置的最大挑戰:

除了要會改USB 裝置的韌體之外,還要自己搞定 PC 端APP 應用程式。這個東西

現在搞不好,寫個DOS Prompt 程式可能會比寫一個 Windows GUI 程式還難囉。

所以到了這裡大家就沒力了。在這一篇文章內,我就簡單的帶大家重新走一遍,

好讓一些比較沒經驗的人,可以事先評估看看,或是可以考慮一下自己的系統需求。

---
說真的~現在單晶片微處理器(微控制器)的效能越來越好。能做的事也越來越多。

所以單一功能的USB 裝置可能會小看它了,所以在USB 裝置上就有可能碰上所謂的

USB Composite Device。而所謂的 USB Composite Device指的就是至少支援兩組

Interface Descriptor 的東西。簡單來說:兩種 Interface 就是代表有兩種功能的裝置。

譬如鍵鼠合一的東西就是了。這個東西對USB 裝置韌體來說,不難。但萬一你是要搞成

USB Customer HID,比較痛苦的是:PC 的應用了。說實在的~USB 發展至今已經超過

二十年了。(好像剛好二十年喔?!)。不要說USB 裝置技術啦。就連PC 端作業系統也都

很成熟了,我想現在應該沒有多少人還會去寫專屬的USB Device Driver 了。

所以我們大家這些喜歡DIY 或是人家老闆可能只有把這份工作丟給你一個人時,

你就得要自己衡量一下整個工作分配了。

至於要不要非得兩個Interface 宣告不可? 這個問題還是得回到我們一般系統應用來檢視。

兩個 Interface 當然要支援兩個 Endpoint  來完成資料傳輸,但非得要用兩以上的Endpoint 嗎?

其實在一般標準的 USB HID 中,還有另外一個宣告,就是有所謂的  Report ID 。

我們是可以利用不同的 Report ID 來區別不同的資料傳輸內容。就像在HID 規格裡也沒有

強制你非得還要一個 Interrupt Out Pipe 一樣,因為你也可以透過 Endpoint 0 中的

Control (Token) pipe 去完成同樣的功能。這一部分最大的問題還是在於PC 端如何處理

這個問題?目前網路搜尋可以找到的USB customer HID 範例,幾乎都是支援一組

Interface 的標準範例程式。那怎麼完成兩個 interface (Endpoint Pipe --- 兩個Interrupt IN)

的資料讀取?我找了一下:只有找到這一篇:Intel Platform Controller Hub EG20T

不過呢?我試了一下,也不行。

另外一篇則是 MicroSoft 官方的文件說明。答案也不是這篇說的。

所以這件事告訴我們一件事:那就是看別人的範例程式都很快,很好...但問題往往就是

當碰到問題時,答案在哪裡?尤其這一種核心問題還卡在微軟的作業系統裡的。

因為對我們這些又要搞定USB 裝置韌體,還要能去寫個MFC 程式的...都已經不容易了,

還要我往微軟的作業系統底層驅動程式鑽?有沒有搞錯啊?

不過呢?在此我還要稍微交代一下:我們寫這些HID 應用軟體時,常用到的函數庫就是


我們發現能用透過這組函數庫所能動用的函數也不多,而且還沒辦法抓到許多USB 裝置

的許多資訊。尤其是USB Device 支援多少個 interface ?如果要能用的,大概就是還要往下

動用到:那些 IOCTL_GET_PIPE_CONFIGURATION I/O 等類似的東西,但這些都不是我們這一

種人可以隨隨便便的去架設的東西。(因為你可能要安裝個 微軟DDK開發工具才行)。

所以還是得回到系統應用觀念?我到底真的需要多少個 Interface ?我真的需要一個 USB

Composite Device 嗎?答案還是得看你自己的系統需求吧。
---
針對單一Interface 的USB HID東西,網路上有很多範例,這一部分我就不再多做交代,

我這邊還是以特殊的USB Composite Device 來說明一下吧。

我們直接用結果說明比較快:



這是一個支援兩個 interface 的 USB Composite Device 的範例程式。不好意思,這是幫別人

完成的一個工作,所以有些地方就不得不馬賽克一下。

其實,當我們USB HID裝置一插入PC 端的USB 插槽時,當作業系統完成 Device Enumeration

之後,就是當你從裝置管理員看到正確的USB 裝置時,作業系統的USB HID 底層的驅動程式

就開始抓取你USB 韌體所提供的資料了。只是我說的:因為沒有上層的應用程式,你就看不

到任何傳輸資料內容而已。所以當我們用軟體打開底層的驅動程式之後。就是經由

CreateFile --->再來 ReadFile 就可以開始讀取 USB HID 資料了。

就是我們上圖所看到的同時兩組從不同的 Endpoint 所讀取的資料。

簡單來說,這個答案就是其實微軟的作業系統應該是用兩隻不同的驅動程式來處理不同的

Interface。所以上述那兩個連結網頁所說的也不是不對,只是這個結果有點令人訝異而已。

就是會有兩個 Handle 來提供給兩個不同的  Read File 用。

----
接著我們就用 Control Token (Endpoint 0) 來下 Set Report 給USB  Composite Device。

然後當USB裝置收到這個命令時,我們USB 裝置韌體就改變輸出資料型態來讓我們

的應用程式可以辦別:我們也順便改變資料輸出顏色,好讓我們觀察答案:


這是一種在系統驗證上變通的方法。這也是不得已的作法。

但如果我們利用 USB Analyzer  來看的話:我們就很容易看出答案了:


上圖就是USB Device 在不斷的透過 Endpoint 1 (Interrupt IN) 及 Endpoint 3 (Interrupt IN) 的

拼命的往PC 端送出資料過程中,然後我們可以再按下那個 "Set Report "按鈕,就可以經由

 Endpoint 0 (Control Token) 下達一個 HID class 的 Set Report命令給我們USB 裝置了。

當我們韌體收到這組命令之後,我們隨後就可以在下一 個Endpoint 3 (Interrupt IN) 改變

輸出資料了。這也證明我們在整個 USB Composite Device (Customer HID )與PC 端之間

利用微軟的作業系統及其標準驅動程式所完成的工作流程。也無需要動用到 DDK 的開發

工具平台。 大家可以比對一下跟以前一篇USB DIY文章比對一下結果差異在哪裡?

USB DIY-- 自學計畫(十一)

--------
結論:

這一篇關於針對USB Composite Device 所做的實驗,其實可能在實際應用上,並不太可能

會出現你的實際應用中,因為還是有比較簡單的作法,就是回到單純的 USB Customer HID

,然後再用簡單的  Report ID 的方法來簡化工作。不過,大家還是可以參考一下這一種作法。

當然這一種應用有一種特殊應用性:就是可能用同一組USB HID 裝置,但可以分別用兩隻

APP 應用軟體來跑。這應該也是可以做得到的~只是要做甚麼?可要發揮一下大家的想像力。


其次部分....其實還是最重要的一部分

大家別看說看別人搞這些 USB DIY ,看似簡單,其實也不簡單的。最主要的問題就是:

雖然網路上有一大堆參考文件可以搜尋參考,甚至複製引用,但往往就是當你碰到問題

卡住時,還真的很難下手 Debug ... 像我自己也常常就莫名其妙的就卡在某一個簡單的

指令或是一個小環節上。一下子要回到USB Device 端的韌體程式修改,一下子就又得去

K 一下 MFC 的相關訊息...(我現在用的是Microsoft Visual Studio 2010 版的)。甚至有時候,

還是得搬出 USB Analyzer 幫忙釐清一下問題點。甚至有時都有點懷疑網路上這麼一大堆

參考範例程式,有的寫得簡化許多,有的卻是一大堆有的沒有的判斷考慮...對我們這些

也不是一天到晚都是在微軟作業系統裡打滾的人來說,要看懂微軟的相關技術內容,還真

的有點是天書一般。我終於也了解到:為什麼明明大家都已經進化到 32 Bit ARM 平台了,

對於USB 的東西還是常常一知半解?就連我買了幾本對岸相關書籍:



還是沒看到多少相關文章與內容。真的~有時要交代這些內容時,真的也不容易

一一俱全。 只能說:還是得多做練習。畢竟這個東西,以後只會越來越多,也不會

變少的。

大家就加減參考一下吧。

-------------
1. 改寫原廠的USB應用程式
2. 改寫原廠的USB應用程式(續一
3.改寫原廠的USB應用程式(續二)
4.USB DIY-- 自學計畫(一)
5.USB DIY-- 自學計畫(二)
6.USB DIY-- 自學計畫(三)
7.USB DIY-- 自學計畫(四)


沒有留言:

張貼留言