2020年1月18日 星期六

USB DIY --- USB Host 端的軟體開發(三) 與韌體之間的關係

既然要講 USB DIY 中關於PC端的軟體開發,就得同時要看USB Device 端的韌體

問題,因為許多USB 的軟體中的通訊協定是跟著Device 端的韌體支持來決定的。

之前也有許多網友也提出一些經驗,就算是標準USB class 的裝置,碰上微軟的

系統之後,也會有許多令人意想不到的結果,所以啦~搞USB 真的沒辦法把這兩者

之間分得那麼清楚,以前我們搞韌體的就得不斷的跟寫PC軟體的工程師不斷的溝通,

甚至還得把USB 分析儀搬來搬來搬去的讓彼此聽得懂或看得懂彼此的想法與做法。

所以這一文章我就來舉個很簡單的例子吧。

首先要先說明的是我的硬體部分:


其實以這一種應用來說:並沒有強調USB controller 的效能要多好?非得要 32 bits MCU

才行。或許往後有機會我也可以拿一顆 STM32 來玩玩。

我用的這一棵 USB Controller 應該是我看過最小最精簡的USB MCU 了。

因為他只有 3x3 mm 這麼小,而且可以直接吃USB 5V 的電源,還可以轉出一組 3.3V

給你用。沒比較就沒感覺吧:以下就是很暢銷的 STM32 的開發工具:ST-link 的工具:


上圖左邊的是原廠標準品,而右邊則是滿街地攤貨的東西,這個東西我記得只要一百元

左右就可以買得到了。(哇~點進以上連結之後,我才發現也有人只賣7~80 元而已!!)

而打開內容一看:


就是一顆 STM32 的MCU 外加一顆晶體振盪器。還沒有喔:他的背面還要再加一顆

USB 5V 轉  3.3 V 穩壓IC。


所以呢?這個東西的價值在哪?那就不用我說了,STM32F103 光賣這個市場就不得了。

反正硬體的東西就是秤斤論兩的,也不用我們多說討論的。這種開發工具不就是

USB 轉 Serial 的東西,不是UART 、SPI、I2C 或是 JTAG 、SWD..等等,基本上他的

IO 都不需要太多的。那你會擔心程式碼或空間不夠用?這個你就不用擔心了。

因為基本上這些開發工具都具有控制韌體智能升級的功能,隨時可以更新下載其中

的韌體,反正這種東西只要能動,你也不會一天到晚更新韌體的。

不過,倒也點出一個很重要的產品技術觀點:就是得支援 韌體更新問題。

所以這就牽涉到軟體與韌體之間的通訊協定與基本的系統架構,這篇文章我所採用的

就是原廠所提供的USB 韌體更新程式範例來做的。從這一點我們來看看這樣子的

東西,到底要搞到甚麼程度?

我們先來看那個 ST-LINK 的東西,研究一下別人東西或許也可以供我們參考一下嘛!

這個 ST-Link 掛上PC 之後,是跑到 "通用序列匯流排裝置"去,看來他不是走 USB HID

介面的。


看一下他的基本 USB 裝置宣告。


果然是全部都是屬於自行定義的介面,這代表著他一定得自行開發USB 驅動程式,

也需要有驅動程式才能動作。


他準備了三組 Endpoint。一組給 Output ,兩組給 Input 。




而且都是採用 Bulk Transfer 介面,這可能考慮到他的傳輸資料量需求比較大一點。

當然啊~現在 32 bits MCU 的 Flash 容量一路攀升,那個 256 KBytes 或 512KBytes 都

不算甚麼大驚小怪的容量了。另外人家是大公司,資源比較多,相對來說:這方面的

軟體開發人員也不用擔心的~只是不是每家公司都可以這樣子玩USB 系統的,

像我們就只能用 USB HID 這種現成的東西來玩玩而已。
----
所以啦~從以上的簡單範例說明:搞USB 裝置的第一個重要參考依據就是得先明確的

USB 裝置的宣告要採用怎樣的介面與描述(Descriptor) 。

我們參考了原廠在USB Bootloader 這方面的範例程式,他給了一版很簡單的 USB HID 的

韌體更新範例。他的USB 裝置宣告在 USB HID 的 Report Descriptor 為:


只有兩個: 一個 input report,另一個為 Output report。是啊~這樣子的東西算範例嘛!

只要先證明我會動就好了,其實很多人用了這一種韌體之後。(重點是原廠沒有給

PC 軟體的參考原始碼)。你怎麼要整進自己的系統中呢?這樣子的宣告夠用嗎?

好啦~我們就參考一下他原廠的 USB 介面的開發工具:
--
然後他的USB 裝置宣告呢?


哇靠~你自己都搞了一個 855 Bytes 的 Report Descriptor 的宣告。"莊孝維"...

搞開發工具最怕甚麼?就是未來所不能預測的需求在哪?在宣告上當然保留越多,

未來擴充的機會就越多啊。那原廠就給一個簡單到不要說 128Bytes 的宣告,

結果他給的範例宣告不但很短,還短到沒有達到 64 Bytes 。

一個USB Packet 的基本都沒有。然後更神奇的是,他的範例程式還跟你說:

"我們這隻範例程式不支援超過 64 bytes (EP0_PACKET_SIZE) 的傳輸"。


所以啊~我常說:搞USB 裝置光從範例程式架起來跑是不夠的,你也不要傻傻的

拿這個東西跟你的主管、老闆或客戶說:你看這個我架起來跑了。多棒啊!

那你就自討苦吃了。因為你都還考慮到未來韌體與軟體之間未來的種種困難之處。

這個就是我為什麼一直很難給個原始碼給大家讀或參考的。

因為這兩邊問題加起來,真的會搞死人的。

(附註說明一下:他原廠範例程式沒有採用 Report ID的宣告,但他自己本身的開發工具

是有的,我個人的建議:還是採用 Report ID 的作法,對於你或軟體工程師來說:

未來的擴充性與保留彈性比較大,但此用 Report ID 之後,軟體的傳輸長度會與宣告的

資料長度會不一樣的,會多加一 (+1) 出來,這一點一定要先說明一下,這個就是搞

軟體的人要注意的地方:如下圖所示。原廠的韌體不支持這個,所以資料傳輸長度

不同,如果發生錯誤的話,就直接死在PC 作業系統底層...你根本看不到的!)



---
接下來我們就繼續的往下走...

一般你從別人的範例程式改成屬於自己的程式之後,這個微軟的作業系統很聰明的

如果你可能先用範例程式先掛到PC 端,完成正常的 Enumeration 之後,下次你略作

修改USB 裝置宣告之後,再掛一次(插拔)PC 端的話,微軟的作業系統就會給你

出考題了,真的有夠聰明的...(靠~就是純心要玩死你的啦。),舉例如下。

譬如:原廠這隻範例程式沒有支援 USB 字串宣告,但一般搞產品~老闆或公司都會

要求要加入這個東西,譬如公司或產品名稱、甚至產品序號。所以你USB 的裝置宣告

描述(Device Descriptor) 就得加入此一宣告,然後你再做一次插拔:


你看到微軟在幹嘛?在你的USB 裝置宣告加入支持字串描述之後,他就出了一道題

給你:問你的字串描述中 Descriptor index 0xEE 是甚麼?如果你的韌體答不出來,要了

三次拿不到答案,直接給你踢掉...怎麼辦?改韌體~直接回 Stall 。

為什麼?再看一次:當你把原來一樣的USB裝置中,用同樣的 VID/PID 修改了

裝置宣告描述之後,PC 端就會隨便考你的:底下是另一個例子:


USB Device 之描述(Descriptor) 中 Descriptor 第六項是甚麼?

規格書1.1 裡只有定義五種。(Device、Configuration、String、Interface 及 Endpoint),

第六種為 USB 2.0  的東西。稱為 Device Qualifier 。是用來切換USB 速度判斷用,

但一般MCU 平台大部分還都是支援 Full Speed 就夠用了,這是因為你在你的

Device Descriptions 中宣稱為 USB 2.0 版,所以微軟就會來問你,但如果你沒有想到

這麼複雜的話,那你就如上圖中的方式:直接回 Stall (Error) 代表不支援啦。

也不要搞死自己。
---
接下來,為了我們未來系統需求,我真的需要多一點的 HID Report 介面,

所以我的 USB HID 中的 Report Descriptor 的長度一定要再增加的。

結果修改之後,"同理可證:微軟會再玩你一次的 ..."。(這到底是甚麼含意呢?)


我們修改了原來範例程式裡的 Report Descriptor 之後,長度增加為 D8 (216 Bytes)。

明明宣告描述中都跟你微軟的作業系統說了,結果呢?


作業系統竟然用 0x118 (280Bytes) 長度問我關於 Report Descriptor 的內容為何?

這時候當然不能再回 Stall 了,就據實以 216 Bytes 回答即可。這根本就是透過一連串的

考驗,來證明你搞USB 的功力到底行不行啊?

在這些過程中,你會發現:明明一開始我用原廠的範例韌體程式可以很順利地掛到

電腦的裝置內,怎麼稍微動一下,就不行了呢?出現這個東西:


好像對,又好像不對,軟體那邊就是看不到了。哇~真的想哭,又不知要找誰哭?

當初不是明明信誓旦旦地跟主管、老闆或客人說可以的嗎?現在怎麼辦?

"個人造業,個人擔吧!"

另外,當你修改了USB 基本韌體之後,有時候也不要以為你已經搞定裝置描述宣告了,

其實有時候你也可能忽略掉小細節,譬如在修改USB 裝置韌體時,沒有很好或適當的

處理韌體,你會發現雖然你已經順利地掛上PC 端的裝置了,其實從USB 的通訊協定

裡,你可能又不小心踩了一個地雷:


上圖這個情形很容易發生在一般 USB Bus Power 的裝置裡,因為上電後,沒有很好的

處理系統初始化的問題所產生的。雖然微軟還是修正之後幫了你。但是這個東西從

USB 的 Bus 分析儀看起來就是不舒服。就看每個人對於系統穩定性或他的強健性要求

了喔?只是個人經驗,提出供大家參考一下吧。
---

好了,接下來就得往系統開發走了,尤其是這一種系統之間資料的傳輸交換,

一個就是PC 端,一個就是USB 裝置端。USB 不比 一般傳統UART傳輸,除了

你系統本身個人的通訊協定定義外,你也得遵循PC 端作業系統的要求。

當然這一點你真的很難體會得到:因為微軟不一定要跟你說明白的。

底下就是一個簡單例子,尤其是在像 USB HID 裝置裡。

譬如我們可能會用 USB Control Token 下命令給我們的 USB  裝置,但也不知道

系統有沒有完成這項命令?或是還要等多久?這是一般很簡單的常識。

故之所以會採用USB 的HID 就是希望藉由HID中的Interrupt In Token 來輕鬆完成

這項任務,結果呢?


這張圖真的要考驗一下你搞USB 裝置系統的功力了。就是我們用USB control Token

下命令,然後用Interrupt In Token  (Endpoint 1)來檢視裝置的處理狀況。

奇怪了?為什麼裝置會一下子回我兩個結果呢?這個問題如果讓寫軟體的看到了:

一定會把搞裝置韌體的工程師給罵死了。然後軟體工程師認為沒他的事了,

就Happy 的下班回家去了,然後韌體工程師會搞到三更半夜還不知道問題出在哪?

我告訴你:這個問題其實是出在PC 端的軟體問題啦。

韌體工程師真的冤枉地喊一聲:人真的不是我殺的啦。悲啊~

從圖片中真的可以看到是USB 裝置韌體回的時機不對啊。其實這是因為PC 端的軟體

沒有真正從PC 端的底層Buffer 取走資料,讓資料卡在PC 端作業系統的驅動程式裡。

那你說:微軟的作業系統有錯嗎?也沒錯。因為上層AP 沒來取資料,他當然也不能

跟USB 裝置再要資料啊。所以這個問題我們修改了PC 端的APP 軟體程式之後的結果:


這個才是漂亮的一來一往的系統資料交換的標準作業流程。

只是這一切只有攤在USB 分析儀裡才比較容易判斷出來的。

如果你一開時從上面那張圖也看不出來,那也分析儀給你的,你也不一定可以馬上

裁奪問題是出在哪裡?

---
結論:這篇文章中,我用了一個非常簡單鮮明的例子來說明USB 裝置系統,在韌體

與軟體開發過程中所容易碰到的問題,而這些問題往往一開始明明還很好、很正常,

怎麼一不小心又發散的產生了呢?

其實這個基礎的開發,我也早在約一年多前已經做過一次了。

USB DIY--自學計畫_USB新平台(三)


只不過,我想重新再整理一下,結果又跑出不同的結果,也常常讓我意想不到的。

不過幸好的是:這些問題都能夠在短短的一兩天內,在"正常的工作"時間內

給解決處理掉了。

搞系統不難,往往最大挑戰就是碰到問題的處理能力與速度,想想自己在上班時,

會搞得身心俱疲的,絕對不是做不到甚麼產品,而是在產品開發過程中,

碰到問題遲遲一直找不到問題出在哪?又有著時程上的壓力...

如果你剛好也有在搞USB 系統裝置,又有時候碰到文章內所描述的類似問題的話,

不妨就多多少少參考一下吧。或許,有一天你也可以很 Happy 的早點下班回家去吧。
---
加油吧!







5 則留言:

  1. 這種QFN轉板最好是4方型的,前陣子用這種轉板黏上DDR PSRAM,跑起怪怪的,估計是頻率太高線路徑長短不一.當時心想要是有方型的說不定能跑成功.

    回覆刪除
    回覆
    1. 嗯~這個了解。

      以前我們搞IC 設計時的Chip 載板也都是儘量是四方形,為的就是讓PCB 走線距離相當,

      尤其是memory bus 之類的。不過哪些都是在要求比較高頻運作得環境下,

      但如果以系統的角度來看,這個東西不是甚麼非常高頻的東西,而且只是為了讓大家

      可以方便地從QFN 轉版到DIP 環境而已。而之所以會採用QFN 也是可以理解的。

      畢竟現在IC 至成大躍進,像這一種MCU 隨便塞了一大堆東西(USB/ADC/TIMER/SPI/I2C/UART...)

      結果還是Core Limit ...還是小小的一顆晶圓而已。也就只能封裝程3x3 mm 這一種小包裝,

      I/O 腳也就只能那麼多了。

      所以啦~像許多IC 設計到IC 製造封裝,還是有許多產品本身在市場銷售的定位問題。

      也就不用強調說:為什麼人家MCU 為什麼不塞多一點 SRAM 或 Flash 或是其他IP 進去?

      搞許多技術指標的東西從技術發展或指標本身的角度來說,都沒有不對的。

      只是再好的產品技術,也得要得懂得市場行銷的精打細算啊。

      刪除
  2. 中斷型EP 需要指定 interval,但是windows就不見得會照時間來做,我們就遇過interval=5,HOST 還是用4ms來詢問,通常HOST可以慢點來問,(當然準時比較好),可是windows比預期快的時候,取決於device是否跟得上這個速度,差一點點或許還可以,但就是有這麼一絲絲機會可能錯過這個時間造成錯誤,開發的人一多,難免出現有人用週期的方式傳資料,很多工程師用Delay解決事情,好像delay治百病一樣
    這世界不見得每個人都知道,USB是一問一答的,USB是有主從架構的,但USB產品就是做出來了,是吧

    回覆刪除
    回覆
    1. 講坦白的話啦,搞PC 軟體與USB 裝置之間的通訊協定與資料交換,

      我最受不了的就是靠Delay 這個東西了。一白遮三醜嗎?甚麼道理我真的搞不懂啊。

      明明就可以靠 Handshake 及定義Protocol 處理的,幹嘛自作主張瞎搞啊?

      只要是明確的 Handshake 及定義Protocol,我相信人家微軟也沒辦法破壞你的作法。

      我本來要找一個例子來解說:明明就是 Handshake 及定義Protocol不清的問題,

      但PC 端用Delay 可以解,但我最後還是不用,因為問題真的不是這種情況啊。

      一問一答的東西,頂多就只有Timeout 機制,那差幾個 mSec 的問題,根本不是甚麼問題吧。

      我以前講過一個名言:不穩的東西,跑得多快有甚麼用啊?穩一點,順順的慢慢跑,

      保你天天準時下班吃晚餐啦。

      刪除
  3. 先前在你文章曾提到有時後資料會loss其實是覆蓋,當看到data0 data1這樣輪流傳其實設計這個協議的意向是傳到2個buffer,但device的IP設計者無意的將buffer只用一個時就及容易出現覆蓋,當此時傳送速率必然低落,選型時不得不查.
    當然啦~~~也有可能是故意只用1個buffer,那就是偷工減料!
    這種單一buffer的結構注定FS 只能傳個幾百Kbyte/s.除非你的處理器死盯著這buffer,這將造成處理器效能低下.

    回覆刪除