Saturday, December 27, 2014

2014年終雜感

只是記下一些感想。

  • 今年就是爽。不用接大客戶的案子,也不用接小客戶的案子,只要好好搞定USB問題就可以。
  • 雖然沒甚麼進步,還是感覺有點強?
    • 台灣工程師還是不長進啊。明明問的問題文件內都有了,還是一直問。會這樣幹的還不只是ODM工程師,連品牌廠的工程師也是這樣,甚至連自己家的工程師也是這副德行。
    • 如果是第一次遇到一個問題就算了,同一個問題可以每個案子都問一次,真的很無言,學習能力是零嗎?
    • 不然就是直接問[為什麼HOST可以認到USB MSC裝置,隨便插個USB接頭就不行]。老大,你已經是經理了,撥出時間K一下spec會很難嗎?不然google一下也是一堆中文介紹。
    • 工作態度問題。台灣工程師的工作態度大部分有點鄉愿,像是:
      • 這東西沒文件是要怎麼搞?
      • 這個人跟我比較好,就不要去煩他了。
      • 問題解決就好。
      • 哎呀,這問題不用副本老闆了。
      • 搞不清楚[求]跟[要求]的差別。
    • 我認為正確的態度應該是:
      • 沒文件你要叫對方生文件,對方沒文件就是對方的問題。客戶會因為你沒給文件就放過你嗎?
      • 如果你可以自己搞定這個問題,當然不用煩對方。但是如果你也搞不定,有甚麼資格不去要求別人幫忙?
      • 想個辦法不要讓問題再次發生不是更好嗎?不然同一件事情一直發生大家會很高興嗎?
      • 副本老闆的原因是讓老闆知道有這件事情,萬一你請假的時候老闆可以指派另一個人幫忙。正常的老闆是不會管工程師的處理方式的,只要不要太誇張的話。
      • [求]是指讓對方去做原本不在他工作範圍內的事情。[要求]是指讓對方去做原本就在他工作範圍內的事情。台灣工程師常常會把[要求]當成[求],不但自己很委屈,對方的氣焰又會漸漸高漲。拿文件這件事情當例子,給文件天經地義,根本不用[求],如果沒文件,我是可以[要求]對方給出文件的。反之,把[求]當作[要求]的工程師也是有,這種工程師就很可憐,因為一直被凹,然後在老闆的眼中又沒業績。
    • 工作態度問題只能慢慢教,或是等本人自己領悟,我一開始也是很鄉愿,後來老闆的提點跟自己領悟之後就漸漸知道事情要怎麼處理了。

Tuesday, December 09, 2014

MCU以及SoC

這篇文章主要是由這邊 http://chamberplus.blogspot.tw/2014/12/blog-post.html 有感而發。

嵌入式系統包含太多了,我只能說chamberplus的重心一直都在MCU上面,他並不需要好的軟體人才,反而是需要有經驗的8051程式設計師。

話說回來,MCU跟SoC的差別在哪裡呢?

就我的認知,MCU基本上就是指8051。8051是由很簡單的CPU跟簡單的IO針腳所組成的,因為他是8-bit的CPU,所以能定址的記憶體也小。這個限制在今天似乎變成了一個缺陷:當整個系統需求越來越多時,如何讓程式碼可以維持在同樣大小,記憶體不用增加?

為什麼記憶體增加不可行?第一,會用MCU的廠商最大的考量就是成本,而增加記憶體對成本的傷害很大。另外就是8-bit CPU再怎麼增加記憶體就是只能用到256KB(有旁門左道可以增加到1或2MB,但是還是不夠用)。

你當然可以找很厲害的人來縮小程式碼,但是你要付多少錢給這個人?舉個簡單的FAT檔案系統好了,你要找到會FAT,又能夠用組語寫出小於100KB的程式的人,你覺得他值多少?而如果把條件放寬,會FAT,又能夠用C寫出小於2MB的程式的人,他又值多少?前者根本就是稀有財,而後者找個比較厲害的國立大學資工所畢業學生可能還可以做。說到底還是成本問題。

SoC基本上就是把CPU跟一堆周邊通通包進一顆晶片內。所以你要說單晶片,SoC也是啊?可是我看網路上,提到單晶片,大多是指MCU。SoC封裝的晶片大多比較高階,ARM11或是Cortex都有,重點是他們都是32-bit CPU,而且通常還有記憶體控制器。這表示可用的記憶體理論上可以到達4GB。其實不用到4GB,光是128MB就非常夠用了。這表示你可以有OS跑在這顆SoC上,而且你可以找到很多人幫你寫C程式,價錢又不會很貴。這時候老闆關心的是功能甚麼時候可以好,而不是程式碼有多小。

SoC看起來很好,那為什麼MCU還是風行?當然是價錢啦。一顆MCU可能台幣10元,可是一顆SoC可能要價美金10元。價錢決定市場,所以MCU大多是用在功能單一,價錢很敏感的產品上。而SoC則往往用在功能複雜,價錢較不敏感的產品上。

但是,這個趨勢有被打破的頃向,MCU越來越往SoC的方向走了,而且價錢還是一樣...。這當然是ARM積極介入的影響。畢竟有32-bit CPU的MCU可以用,誰會想用8-bit 8051 MCU。不過我個人認為這兩種東西還是不會匯合,理由還是成本問題。所以MCU的市場專注焦點還是[程式可以多小],而SoC的市場專注焦點還是[功能有多少]。

libusb

話說每個USB device在插上USB Host的時候,都需要一個對應的驅動程式才能正常運作。不要懷疑,連隨身碟都需要驅動程式的,只是每個作業系統都已經內建,所以一插上隨身碟就會自動載入,導致大多數的人都覺得,USB還需要驅動程式嗎?

現在假設你想要實作一個你自己的USB通訊協定,你搞定了USB device,那USB Host端的驅動程式呢?自己寫一個嗎?有這麼簡單嗎?如果你有google過Windows驅動程式要怎樣寫,相信你不會想自己寫一個的。

既然自己寫太麻煩,那就用現成的吧。libusb就是這類現成的驅動程式。libusb是開放原始碼,而且跨平台,還是泛用的USB驅動程式。因為它是泛用型的驅動程式,所以應用程式得透過它的API去跟你的USB device溝通,這邊就不講了。libusb的官方網站都有寫。

講到這邊,好像很簡單喔?如果你這麼想,那就錯了。雖然你不用寫驅動程式,但是你還是得跟作業系統講要在你的USB device插入時,載入libusb啊。光是這一點就卡死一堆人了,這邊我也不打算講要怎麼做,因為libusb的官方網站都有寫...。

就算解決驅動程式安裝的問題,還是有更多問題會產生,所以我不建議用libusb直接當作最終產品的解決方案,否則真的是人生就此陷入黑暗。為什麼我會這樣講,這是因為客戶永遠不會把規格寫出來,今天跟你說要支援Windows,明天問你能不能支援Linux,改天再問你MacOS行不行,永遠做不完啊。自己寫驅動程式也會面臨一樣的問題,但是你可以說沒人力,一旦用了libusb,不知情人士只會問[不是只要裝起來就可以動了?],事情如果有這麼簡單,你來?

所以啊,libusb拿來做一些小案子,或是學術性研究是很好用的,拿到市場上嘛,自求多福吧。

我遇過最盧的客戶,是跟他講可以用libusb確認他寫的USB device是否能夠正常動作。結果他連官網都不去看,硬是要人家教他怎麼把libusb程式碼編成執行檔,這樣他就可以直接確認,有沒有這麼懶啊?

Saturday, October 11, 2014

USB Video Class 簡介

UVC大概是我看過比較複雜的協定。基本上它複雜不是協定本身,而是裡面一堆[用語]非常的不人性化,導致看的人超痛苦。

基本上UVC跟CDC-ACM類似,它一定要支援兩個Interface,一個用來控制用,一個用來傳資料。控制用的Interface叫做Video Control Interface,另一個叫Video Streaming Interface。這兩個Interface得用IAD把他們接合在一起。

Video Control Interface,顧名思義,就是拿來控制用的,它裡面可以包含幾種元件(我亂翻的):
  • Input Terminal:基本上是描述這個裝置的輸入端,以及它支援的功能。
  • Output Terminal:基本上是描述這個裝置的輸出端,以及它支援的功能。
  • Camera Terminal:基本上是描述這個裝置的相機功能,像是Focus或Zoom等等的功能。
  • Select Unit:簡而言之就是MUX。
  • Processing Unit:基本上是描述這個裝置的影像處理功能,像是Hue或Brightness等等的功能。
  • Extension Unit:描述這個裝置支援的延伸性功能,可有可無。

請注意,每個元件都需要一個獨一無二的ID,並請他們要構成一個Topology(Topology真的不知道怎麼翻)。下圖是某個範例:


這個範例說明某個裝置的Topology,它有兩個輸入,可以用Select Unit選擇其中一個輸入,輸入的資料可以透過Processing Unit做一些處理,然後輸出到Output Terminal。

至於如何把你的裝置的Topology告訴HOST?當然是透過Descriptor啦。每個元件都有對應的Descriptor,基本上只要照規格的範例抄就可以了。

Video Streaming Interface主要用來控制資料的傳輸以及資料的格式。

先來講資料傳輸。UVC支援兩種資料傳輸模式,第一個是Isochronous模式,第二個是bulk模式。這邊不講bulk模式,因為市面上99.9999%的UVC裝置都是用Isochronous模式。

Isochronous模式有個特點,就是必須使用Alternate Setting,如果你忘了Alternate Setting的話,記得再去瞄一下USB2.0規格。基本上只要宣告Alternate Setting 0跟1就可以了。其中Alternate Setting 0是不包含任何Endpoint宣告的,而Alternate Setting 1則會包含一個Isochronous Endpoint宣告。當HOST想要接收影像資料時,會用Set Interface去選擇Alternate Setting 1,而這時DEVICE就得開始輸出資料。相同地,Alternate Setting 0就是不要傳資料。

資料的格式部分,因為UVC目的是支援大部分的裝置,所以裝置端必須提供它支援的格式。資料格式分兩種,第一種是Video Format,其下又分Video Frame。這很好理解,基本上你得先讓HOST選擇影像格式,才能再選擇它的解析度、bitrate跟frame rate。Video Format對應的就是影像格式,而Video Frame則是對應到解析度、bitrate跟frame rate。這樣做的好處是每個裝置可以支援多種影像格式,像是RGB或 MJPEG。

題外話,UVC要支援MJPEG或H264的原因很簡單,因為傳統的RGB格式真的佔用太多USB頻寬了,加上現在沒有HD賣不動,用RGB格式根本沒辦法達到720p30。1280*720*30*3 = 82,944,000 bytes,直接爆掉USB頻寬,所以你會看到有些Webcam號稱支援720p或1080p RGB格式,但是它的frame rate是可憐的1或5,這有意義嗎?

每個Video Format有對應的規格要K,這邊就不介紹了,基本上UVC規格都有給範例,照抄就是了。至於要怎麼跟HOST講這些資訊,當然還是Descriptor啦。他們對應到的分別是Class-specific VS Format Descriptor跟Class-specific VS Frame Descriptor。

整個UVC descriptor的結構如下:


 這個結構很重要,基本上所有USB class規格都會提供類似的結構圖,了解整個結構是快速理解整個通訊協定的關鍵。

接下來是Class Request的部分。UVC的Class Request有點多,而且它牽涉到你的裝置所宣告的內容。每個在Video Control Interface宣告的元件都會有對應的Class Request。在看這些Class Request之前,得先搞清楚它的格式,不然會看不懂。下面是Set Request的格式:



這個比較簡單,Entity ID就是每個元件的ID。

下面是Get Request的格式:

 有沒有看到每個元件可以支援這麼多bRequest?其實沒有很難,會這樣子的原因是,還是UVC是個通用型的協定的關係。所以它基本上不知道每個控制項可以支援的範圍,它只能訂這類泛用的bRequest,然後讓每個裝置提供不同的數值。舉例來說,HOST可以透過GET_DEF取得Hue控制項的預設值、透過GET_MAX跟GET_MIN取得Hue控制項的支援範圍,可以透過GET_CUR取得Hue控制項的現在值。

下面是Hue Control(Processing Unit)的Class Request: 

Class Request還分Video Control Interface跟Video Streaming Interface。Video Control Interface基本上沒有甚麼大困難,Video Streaming Interface就有點複雜了。

Video Streaming Interface只需要支援兩種Class Request,一個是Video Probe,另外一個是Video Commit。他們是用來讓HOST跟DEVICE協調出一組共通的影像傳輸格式。如前所述,Video Streaming Interface描述這個裝置所支援的影像格式,HOST得透過Video Probe跟DEVICE協調出一組雙方都可以接受的參數,然後透過Video Commit指定這種參數。下面是示意圖:

根據我的經驗,HOST幾乎都會發出符合Descriptor宣告的參數給DEVICE端,所以如果HOST遲遲不發COMMIT_CONTROL,那就表示DEVICE回PROBE_CONTROL(GET_CUR)的內容有誤。我遇過的那次是structure的endian問題造成的。

USB PTP/MTP 簡介

MTP其實就是PTP的延伸,所以這兩個是一樣的東西。

MTP之前幾年幾乎沒有人在用。有啦,Pictbridge是架在PTP之上,所以PTP還有存在的價值,但是近幾年來Pictbridge也漸漸地消失在市場中了....理由我猜是相容性問題吧。


MTP的用途跟MSC類似,不同的地方wiki解釋的很清楚,我就不講了。


PTP規格是ISO標準(ISO15740),提到ISO,第一個就要想到[錢]。是的,下載它是要錢地。但是呢,MTP規格下載是不用錢的...。所以現在大家都用MTP規格在玩。


請注意一點,MTP只是個資料交換的通訊協定,所以要透過哪種載具(transport)去傳輸這些資料,是每個載具要定義的,如果要透過USB傳輸MTP資料,請參考[Still Image Capture Device Definition]這份規格書。跟MSC很像,這份文件只定義MTP資料要透過那些Endpoint傳輸,以及容器的格式。資料內容的話,請參照[Media Transfer Protocol]這份規格書。


先談[Still Image Capture Device Definition]。


這份文件很簡單,MTP主要透過Bulk-In,Bulk-In,跟 Interrupt-In來傳輸資料。一般情況下,Bulk-In跟Bulk-In就夠了,Interrupt-In主要是提供一些即時資訊給HOST知道,沒提供也不會怎樣。


每筆MTP資料之前都要加上一個header,這邊叫container,格式如下:


基本上HOST一定是發Command Block,Device回Response Block,Data Block則是看哪一方要發資料,這沒甚麼好說的。


Class Request則是有四個,Cancel、Get Extended Event Data、Device Reset、跟Get Device Status。

  • Cancel有點特別,它通常用來取消某次的傳輸。為什麼說他很特別?因為這是MTP特有的問題。在MTP規格內,如果要傳一個大檔案,因為要等很久,使用者有可能會在中途取消。問題來了,MTP規格沒有定義取消方式,所以只能在Transport Layer做這個處理。
  • Get Extended Event Data:我沒用過...。
  • Device Reset:沒有甚麼特殊。
  • Get Device Status:我看過唯一有用的是在Cancel之後,HOST會用這個Request來問狀態。
這四個Class Request的使用時機其實沒甚麼標準,跟HOST作業系統要怎麼運用有關,所以準備好USB Analyzer,然後準備好相容性測試吧。

接下來是[Media Transfer Protocol]。


MTP資料格式全部都是little endian,比較特殊的有字串跟Array:



  • 字串:它是所謂的Pascal String,但是用的是unicode 16 little endian編碼。第一個byte表示後面有幾個[字元],這個字串還必須包含'\0'這個字元。跟strlen()不同,要小心。
  • Array:第一個integer表示後面還有幾個element。
  • 日期:它是個字串,格式是"YYYYMMDDThhmmss.s"。既然它是個字串,所以它也遵循字串的編碼規定。".s"可有可沒有。


基本上MTP的流程是Command -> Data -> Response。在MTP規格裡,Command稱為[Operation]。而每個物件(或稱為檔案)都必須有個獨一無二的ID,規格稱為[Handle],整個通訊協定裡,雙方基本上是在處理這些Handle。請注意,物件並不是只有檔案,還包含目錄,這就是為什麼要叫物件而非檔案。

透過這些資訊,大概的物件交換協定也可以猜得出來。我這邊直接用規格來說明:
  • HOSTGetObjectHandles命令取得DEVICE端的物件列表。
  • HOST取得列表後,可以用GetObjectInfo命令取得每個物件的資訊。
  • HOST可以用GetObject命令取得該物件的資料內容。
  • 如果要取得某個物件的縮圖,可以用GetThumb命令。
  • 要刪除某個物件,可以使用DeleteObject命令。
  • GetObjectPropSupportedGetObjectPropValueGetObjectPropDesc則是MTP特有命令,它可以支援更多關於物件的屬性。因為GetObjectInfo的屬性是固定的,所以只能用額外的命令去新增屬性。
當然,當USB MTP裝置一插上HOST時,HOST可以用GetDeviceInfo取得這個裝置的資訊,並用GetDevicePropDescGetDevicePropValue取得這個裝置支援的屬性。
等等,上面沒提到如何寫入物件到裝置啊?這有點複雜,因為物件一向都是裝置提供對應的IDHOST根本沒法存取一個不存在的物件啊?所以這個動作要分兩個命令,第一個是SendObjectInfoDEVICE說要新增一個物件以及它的屬性,然後DEVICE會把這個物件的ID放在Response內,之後HOST再用SendObject把這個物件的資料傳給DEVICE。這樣做很引發一些問題,例如不能重傳或從某個地方開始傳。傳到一半要取消也是很麻煩。

實作MTP基本上不難,但是有幾點很麻煩:
  • 相容性問題。這個基本上無解,因為MTP是微軟主推的規格,其他作業系統可能不太想支援太多。就我所知,至少MAC OS對它的支援就不是太好。有一些行為跟Windows差很多。
  • 多檔案處理:因為所有物件都是用Handle ID來表示了,所以DEVICE必須處理Handle ID所對應的實體檔案轉換。所以一個映射表免不了,當檔案多到99999時候,這個映射表會有多大?存取速度呢?
  • WHCK。這個真的很煩。

RS232 over USB 簡介

RS232 over USB就是所謂的USB UART。現在這個東西會流行的原因,主要是現在很多電腦根本就沒有COM port,所以只好用USB來橋接並模擬UART。下圖是個很簡單的示意圖,會有這種需求的大多是工程師吧...。


USB對於這個東西的規範是落在Communication Device Class內,縮寫就是CDC。以下都用CDC描述。因為CDC包含太多類別了,所以CDC規格書裡面有定義這些類別的對應規格:


RS232 over USB是屬於Abstract  Control Model這個類別裡,所以請參考USBPSTN這個規格書吧,以下都會用CDC-ACM來代表RS232 over USB。

翻開PSTN規格書,它裡面定義了三種Model:
  • Direct Line Control Model 
  • Abstract Control Model 
  • Telephony Control Model 
本文只介紹Abstract Control Model,因為它就是我們要的。注意一下,因為UART並不需要支援AT command,所以Class Protocol Code可以是0:

接著就是如何寫CDC-ACM的descriptor了。請注意,CDC規格定義一個CDC裝置必須支援兩個Interface,一個Interface描述這個function所支援的功能,而另外一個則是描述資料傳輸的通道。然而,如果一個裝置宣告它支援兩個Interface,一般就表示它支援兩個function。這很明顯跟USB 2.0規範相衝突,所以IAD就上場啦。IAD全名是Interface Association Descriptor,它的功用就是拿來宣告某幾個interface其實是屬於同一個function,這樣就解決這個問題。IAD的規格現在已經包含在USB2.0之內,usb.org可以下載的到。

CDC規格有規定第一個interface要額外支援的descriptor:
  • Header Functional Descriptor:沒甚麼特殊,照抄即可。
  • Union Functional Descriptor :沒甚麼特殊,照抄即可。
  • Country Selection Functional Descriptor:雖然說是必須的,但是好像不用提供也可以?
  • Interrupt Endpoint Descriptor (Optional) :雖然說是非必須,但是最好要提供?
PSTN規格還規定第一個Interface要額外支援的Descriptor:
  • Call Management Functional Descriptor:比較值得注意的是bmCapability這個欄位,其實都是0就可以了...。
  • Abstract Control Management Functional Descriptor:一樣,比較值得注意的是bmCapability這個欄位,只要支援D1這個bit就可以,其他的就看要不要支援了。

而第二個Interface則是很簡單,它單純只是宣告資料傳輸的途徑:
  • Standard Interface Descriptor
  • Bulk-In Endpoint Descriptor
  • Bulk-Out Endpoint Descriptor 
接下來是CDC-ACM必須支援的Class Requests:


如果只是要實作CDC-ACM,其實不用管SendEncapsulatedCommmand跟GetEncapsulatedResponse,因為不用支援AT command,所以Host根本就不會發這兩個Class Request。SetLineCoding、GetLineCoding、跟SetControlLineStatus則是要支援,因為我們在Abstract Control Management Functional Descriptor內的bmCapability欄位說有支援。

提供正確的descriptor給Host之後,Host就會認到你的CDC-ACM裝置,如果你用任何一個Terminal工具像是TeraTerm去開啟它的話,Host會透過SetLineCoding跟SetControlLineStatus去設定這個CDC-ACM的屬性,而你的裝置就應該可以開始傳輸資料了。運氣好的話,就可以在TeraTerm內看到一些字了。

市面上有蠻多便宜的CDC-ACM裝置,他們的主要缺點是,只要印多一點的字,就會開始印亂碼,我猜這主要是因為這些裝置通常內建記憶體很小,當從實體UART來的資料超多時,他們就會來不及把這些資料傳給Host,然後就開始掉字,如果很不幸,掉的是一些控制字元,就會出現亂碼了。便宜沒好貨啊。

Wednesday, October 01, 2014

USB Mass Storage Class 簡介

USB Mass Storage Class的規格很薄,只有22頁,這邊我是指[Bulk-Only Transport]。因為它是目前最廣泛支援的規格,所以只談這部分,其他的規範,用到再去看即可。

顧名思義,這個規格規範如何只用一個bulk-in跟一個bulk-out endpoint去達成資料傳輸的目的。它首先定義兩個它專屬的 class request:

  • Bulk-Only Mass Storage Reset:就是Transport Layer的reset。至於要做到怎樣,規格寫得很清楚。
  • Get Max LUN:這個就有點模糊了。其實LUN就是Logical Unit Number,也就是這個裝置所支援的[槽(Slot/Drive)]。如果這個裝置支援兩個槽,那Max LUN就是1。

接下來是整個資料的流程(規格書):


HOST先透過bulk-out endpoint送CBW(Command Block Wrapper),如果Device有回ACK,接下來HOST可以收送資料,然後Device送個CSW(Command Status Wrapper),一個命令完成。

下面是CBW的資料格式(規格書):




下面是CSW的資料格式(規格書):


接下來就沒啦,只剩下一些錯誤處理的方式描述。有這麼簡單嘛?當然沒有啦,請注意[Bulk-Only Transport]這個名詞,Transport就是表示這份規格只是一份載具的規格,至於這個載具要搭載甚麼樣的資料,不好意思喔,請看另外的文件。

一般而言,目前市面上隨身碟支援的大多是SCSI command,所以請用google搜尋[scsi command]。這些文件會描述USB Mass Storage Class應該傳輸的資料內容。我手邊的有[SCSI Block Commands - 2 (SBC-2)]、[SCSI Block Commands - 3 (SBC-3)],跟[SCSI Primary Commands - 4 (SPC-4)],但是都是Draft version,據說正式版已經要收錢才可以取得了...。

話說回來,這些文件又臭又長,不過也不必全部都實作啦,只要做一些大家都會用到的命令就可以。雖然如此,大部分的人都是用買來的實作,所以你也不太有機會碰到。但是呢,微軟超機車的WHCK可不會這樣放過你,它會一個一個根據你支援的命令,發出一些很機車的參數,然後看你的裝置可不可以通過它的測試。我曾經被玩過一次,那是在WHCK還叫做WLK的時代,真的是痛不欲生...因為有些通不過的地方,雙方文件都寫得很模糊,根本就是只能用猜的,那真的是只能賭運氣看看怎樣改會過。所以如果你買的實作無法通過WHCK,請乖乖K規格書,然後期待問題不會太多吧。

還有一點要提的是,SCSI commands其實是很底層的命令,所以基本上它都是會存取到實體層的資訊。如果你的USB Mass Storage Class要支援SD卡,那你還得跟SD Host controller打交道才行,如果是NAND Flash的話,那就是要跟NAND controller打交道。

花了一點時間喵了一下,SCSI Primary Commands,應該是定義所有基本的命令,而INQUERY命令內的[PERIPHERAL DEVICE TYPE]欄位則是告訴HOST要套用哪一份文件:



通常NAND跟SD卡都是用SBC-2。SBC-3好像是新版,它相容於SBC-2。

Tuesday, August 26, 2014

USB 簡介[8] - USB Class

USB Class是指USB上層的應用。因為USB其實只是一個Transport layer,就像是TCP/UDP一樣,所以它還必須定義上層應用的通訊協定。最有名的當然就是Mass Storage Class,也就是大家都會用的隨身碟所採用的USB Class。

下面是架構圖:



當然,你也可以實做你自己的上層應用,但是問題是HOST端的driver要誰來提供?現在主流的HOST作業系統就有Windows、MAC OS、跟Linux,要針對這些作業系統提供對應的driver,事實上是件很大的工程。

因為有driver的問題,所以目前大部分的作業系統其實都已經內建一些USB Class的driver,Mass Storage Class不用說,一定支援。也因為這樣,基本上你只需要管device端的事情就可以。但是呢,事情往往不是這樣子的,所以還是會遇到一些跟HOST端有關的問題,這時候就得開始debug了,這就是所謂的相容性測試...。


Friday, August 15, 2014

USB 簡介[7] - Zero Length Packet

Zero Length Packet這個概念很有趣,主要是Device或Host可以發出長度為0的資料封包。為什麼會需要這種東西?

USB的transaction傳輸最大不能超過該endpoint的maximum packet size,那如果這次傳輸資料小於maximum packet size呢?那這筆資料就叫『short packet 』。如果是剛剛好等於maximum packet size呢?那就有點問題了:到底要不要繼續發出In-Token或Out-Token呢?所以USB定了一個Zero Length Packet這種東西,來告訴HOST這筆『transfer』已經結束了。

這種狀況特別容易發生在不知道要收多少資料的狀況下,舉個例子,假設bulk-in endpoint的maximum packet size是512 bytes。而HOST應用程式不知道要收多少資料的狀況下,它可能會跟HOST說他要一次收8MB的資料,反正應用程式記憶體不用錢。假設這次剛好Device只會傳2MB的資料,那HOST就很尷尬了:在不知道到底Device會不會繼續傳的前提下,只能繼續發In-Token,如果這時Device死不傳資料,雙方就準備等到天荒地老吧。

為了避免這種狀況出現,只好搞出Zero Length Packet這種東西。他其實就是一種『short packet 』,但是資料長度是『0』。HOST收到Zero Length Packet或short packet之後,就可以把這次的傳輸結束,然後跟應用程式報告。

至於什麼狀況下應該發Zero Length Packet?好像沒標準,都是看雙方的實做,也就是相容性...。為了保險,你可以每次都發,但是這會浪費一點頻寬。

下圖是有『short packet 』的狀況,大部份的傳輸都是這種:

下圖是沒有『short packet 』的狀況,但是因為剛好整個傳輸等於應用程式要的,所以還是可以收到:

下圖是沒有『short packet 』的狀況,因為收不到預期的資料量,所以USB Host Driver根本就不會結束,所以應用程式就一直等在那邊 :

下圖是有『short packet 』的狀況,就算沒收到預期的資料,還是可以結束這次的傳輸 :



Tuesday, July 22, 2014

USB 簡介[6]

終於到了最後也是最重要的一章,鼎鼎有名的Chapter 9,USB Device Framework。

為什麼這一章這麼有名?因為前面的鋪陳完全就是在這一章爆發啊。重點是,USB標準測試程式,USBCV(USB Command Verifier),跟微軟的WHCK(Windows Hardware Certification Kit),就是一直在找Chapter 9的碴啊。

第一點,USB裝置可分為好幾種狀態,而每個狀態可以吃的電流是有關連的,基本上只有Configured狀態才可以吃超過100mA的電流。而比較值得注意的是,任何狀態都可以進Suspend狀態,這是因為Suspend狀態是透過Suspend訊號來偵測,很簡單。但是哪個狀態進Suspend,出Suspend之後還得是那個狀態,這個就不簡單了。在Suspend狀態中,裝置最多只能吃500uA的電,要怎麼吃那少的電,還要能夠回到上個狀態,這個就不簡單了。

第二點,當有USB裝置插上去的時候,HOST怎麼知道這個裝置到底是什麼東西?那就是透過USB Device Descriptor啦。HOST只要發個"Get Descriptor"這個標準要求給這個裝置,裝置必須回正確的Device Descriptor,這個Device Descriptor內容很簡單,就是描述這個裝置支援的USB版本、實作的Class、Control Endpoint的最大資料量、Vendor ID跟Product ID等等。

先說明『標準要求』,USB有規範一些『標準要求』(Standard Request),目的是讓每個裝置能夠回覆足夠多的資訊給HOST,這樣HOST才能繼續下個動作。有幾個一定要知道:

  • Set Address:HOST透過它來設定USB裝置的位址。每個裝置插上去之後,預設的位址是0,這樣HOST才能透過位址0來讀取Device Descriptor,然後再把這個裝置的位址設到不同的位址去。
  • Get Descriptor:基本上HOST透過這個要求取得USB裝置的所有資訊。
  • Set Configuration:就是選擇USB裝置提供的設定,完成Set Configuration裝置才算進入Configured狀態。
其實最重要的就是Get Descriptor。提到這個要求得先提USB Descriptor。USB Descriptor有分幾種,下面只列常用的:
  • Device Descriptor
  • Configuration Descriptor 1
    • Interface Descriptor
      • Endpoint Descriptor
  • String Descriptor
請注意,他們有些是有階層概念的,Configuration Descriptor主要是描述這個裝置所提供的組態。大多數的裝置都只會提供一種組態,有的會提供兩個以上,主要是電力需求的不同。Configuration Descriptor最重要的資訊就是這個組態最大吃多少電流,以及這個組態支援幾個Interface。要選擇那個組態是HOST決定的,而對應的就是Set Configuration這個要求。

Interface Descriptor主要描述這個裝置有哪些功能(function),一個Interface代表一個功能,所以當你看到一個Configuration Descriptor裡面有包含兩個Interface時,基本上就是只這個裝置支援多種功能,也就是Composite Device。但是,還是有但是,USB後來又有出一種叫做IAD(Interface Association Descriptor)這種東西,它可以把兩個Interface組成一種功能,所以世事無絕對...。

Interface Descriptor主要描述它所支援的功能,以及它支援的Endpoint數目跟種類。它裡面還有一個叫做Alternate Setting的欄位,這個欄位主要是描述這個Interface支援哪幾種頻寬設定。還記得Interrupt跟Isochronous傳輸是有頻寬保證的嗎?這種保證是需要HOST來提供。那HOST要怎知道它能不能保證Device提出的頻寬需求?就是透過Interface Descriptor啦。Alternate Setting可以讓這個Interface提供多種Interrupt或Isochronous傳輸的組合。舉個例子,Interface Descriptor可以提供三種Alternate Setting:


  • 0:需要每個SOF能夠傳輸64 byte的Interrupt Endpoint
  • 1:需要每個SOF能夠傳輸512 byte的Interrupt Endpoint
  • 2:需要每個SOF能夠傳輸1024 byte的Interrupt Endpoint
然後HOST可以根據目前的剩餘頻寬,選擇適合的Alternate Setting。為什麼要搞這麼複雜?因為其他的裝置可能也需要HOST保證一些頻寬,所以得這樣搞。不過我很少遇到這種狀況就是了,尤其是如果都沒Interrupt或Isochronous Endpoint的話,其實只要提供Alternate Setting 0就好了,因為Bulk傳輸並不需要保證頻寬。

而HOST要怎麼選擇Alternate Setting?當然是透過Set Interface這個需求。如果一個Interface只提供一種Alternate Setting,沒有Set Interface其實也可以。

Endpoint則是說明這個Endpoint的類型以及最大傳輸量。

String Descriptor很煩,它主要是提供一些對於Device、Configuration、Interface的描述,也就是人可以看得懂的字串。它一定得提供Unicode字串,而且還有語系之分,但是因為實在太煩了,所以大家都只用英文語系....也造成Unicode字串根本沒有用...。一般就是提供Manufacturer、Product跟SerialNumber這三個字串而已。WHCK機車的地方就在於,它會要你插上兩個同樣Vendor ID跟Product ID的裝置,然後這兩個裝置所提供的SerialNumber還得不一樣。

總結一下,當USB裝置插上HOST時,HOST會SetAddress、Get Descriptor,抓完所有Descriptor之後,Set Configuration選擇適當的組態,然後再Set Interface選擇適當的頻寬需求,結束。接下來就可以透過支援的Endpoint傳輸資料了。說真的,這些東西都不難,只是規格絕對不會跟你講HOST應該會怎麼做,而這也是最機車的地方:每種HOST的實作方式都不同,所以流程絕對不同,光是插上去的流程,就足夠引起一堆相容性問題了。

解這種相容性問題最最重要的就是一台USB Analyzer....,它可以幫你省掉很多猜測的時間。

最後,你以為這樣就完了嗎?當然沒有,如果Chapter 9有這麼簡單,我也不會到現在還在翻它...。

第一,除了Standard Request之外,它還定義了Class Request跟Vendor Request。Class Request之後會提到,每種USB Class都會規定它專屬的Class Request。Vendor Request則是這個裝置特有的要求,說真的很少裝置會提供...就算提供,HOST也不一定會讓程式發出這類要求。

第二,還記得USB裝置必須支援Test Mode來量測眼圖嗎?要怎麼進Test Mode?當然就是透過Set Feature這個要求啦。








USB 簡介[5]

接下來是protocol layer。

第一,USB是little endian,不管是Byte或是Bit順序。

第二,免不了是一堆封包的格式規範,這個看看就好。比較重要的有:

  • SOF(Start of Frame):SOF很重要,對HOST而言。如果你是作Device端的,看看就好。SOF基本上只有HOST可以發,如果是HS的話,是每125us發一次,每個裝置都要去聽SOF,因為後面就會開始資料傳輸了。FS的話是1ms發一次。
  • HandShake封包:基本上就是收到資料回ACK,沒有資料回NAK(不是資料錯誤喔)。有問題回STALL,下一次的資料來不及準備,回NYET(但是這一次的已經收下)。ERR我沒見過,因為是HUB專用。
  • HOST要讀資料之前,一定會發 In-Token,然後Device吐資料。注意喔,HOST在收到資料後,是不能回NAK的。這是因為HOST沒空間可以存資料幹嘛發In-Token?
  • 同理,HOST要寫資料之前,一定會發Out-Token,然後吐資料給Device。
  • Control Endpoint的資料傳輸比較特別,如果是Control In的話,順序是Setup packet --> Data In --> 0 byte Data Out --> Device Ack。如果是Control Out的話,順序是Setup packet --> Data Out --> In-token --> Device send 0 byte data --> HOST Ack。至於為什麼要定這麼奇怪的協定,我也不清楚...。一般工程師很容易犯的錯誤就是在Control Out的時候,最後沒發0 byte的資料,然後HOST就等在那邊,然後還會怪HOST沒做對...。
  • 其他傳輸類型都是 In/Out Token --> Data --> ACK。Isochronous傳輸沒有ACK。
第三,如果資料有錯誤,HOST會嘗試重傳三次。當然,這個對Isochronous傳輸不適用。基本上錯誤處理大多是硬體方面的處理,軟體能派上用場的機會不高。我遇過最好玩的一個錯誤是,Device沒等HOST的In-Token來就把資料送到BUS上了,結果就是babble錯誤。這個也很容易發現,用USB Analyzer抓log,它就會幫你把錯誤標示出來。千萬不要用什麼軟體USB Analyzer,這些軟體通常只能用來紀錄上層通訊協定的資料,對底層是無能為力的,而且它們還不會幫你檢查錯誤。現在一台USB Analyzer很便宜,網路上看到一台"Mercury T2 Protocol Suite"才特價台幣七八萬塊,這台十年前可是要價台幣一百萬呢。它已經這麼便宜了,沒必要再省了...。

Monday, July 21, 2014

USB 簡介[4]

本章主要介紹USB電器訊號。因為我不是唸硬體的,所以有錯請包涵。

電器訊號好像對軟體工程師不用看喔?這個想法有點天真,因為出問題別人一定要你先釐清問題倒底是硬體還是軟體出錯,所以多看看是沒錯的。

第一點,HOST是怎麼知道一個USB裝置插上來了呢?絕對不是看VBUS訊號。其實它是偵測D+或D-,如果D+有被PULL-UP,就表示有個Full Speed 裝置插入,如果是D-被PULL-UP,那就是Low Speed。

那High Speed呢?High Speed裝置其實也是先進Full Speed,然後HOST會再透過一些協定(chirp)跟這個FS裝置溝通,如果成功,雙方就會進HS模式。如果失敗,那就維持在FS。很詭異是吧?其實這就是為了向下相容的緣故,因為只有兩根線,沒辦法生出第三種辨識方式了。

由上可知,其實HOST是沒辦法直接移除一個USB裝置的,因為PULL-UP是由裝置端控制。所以當HOST根本就沒偵測到硬體時,這一定是USB裝置的問題。另外一點是,如果你的裝置老是進FS,記得換條好一點、短一點的USB線,或是換個USB插槽試試看,有些線或插槽的訊號真的很糟...。

第二點就是eye-pattern,中文叫眼圖,因為他真的長得很像一隻眼睛。作USB裝置一定得通過眼圖測試。這點就不是軟體工程師要量了,但是軟體工程師還是得讓USB裝置進入一個叫測試模式的狀態,不然硬體工程師沒辦法量測。一般的USB裝置一定支援測試模式,如果沒有,那就換一家chip...。

第三點,Reset、Suspend、跟Resume是硬體訊號。基本上如何偵測跟應對,規格書寫的一清二楚。比較值得注意的是Suspend,在Suspend模式下,一般狀況下,裝置是不能吃HOST超過500uA的電。這個規定很嚴格,基本上就是叫你把整個裝置都斷電的意思。如果你的裝置沒辦法整個斷電,就不要從HOST吃電,改吃電池即可。沒有電池?不好意思,那就不要去過認證...。

第四點,0/1的資料還要透過NRZI編碼,然後再透過J/K訊號傳輸。這是因為USB界面並沒有clock線路,所以只能把clock編進資料裡。而如果資料是一堆0或1的話,會讓D+/D-一直維持在同一個狀態,這樣雙方根本無法維持一定的clock,所以NRZI基本上就是讓一堆0或1會有所變化。不過不用擔心,這個都是直接硬體就做掉了,錯了一定不是軟體的錯。

第五點,電源管理。一個USB裝置只能吃HOST最多500mA的電。吃超過,對不起,無法過認證。至於HOST會怎樣,則是看HOST的實作,有的HOST比較好心,還是會讓這個裝置抽電,有的則是直接斷電。我認為最標準的作法是斷電,因為這樣可以保護HOST不會過載。

Saturday, July 19, 2014

USB 簡介[3]

前面提到USB支援四種Endpoint傳輸:

  1. Control
  2. Bulk
  3. Interrupt
  4. Isochronous

這邊簡單說明,太詳細實在沒意思。

Control傳輸:一定是透過Endpoint 0,而且一個裝置只能有一個,而且一定要支援。它一次最大能傳的資料大小為64 bytes,並且是雙向傳輸。基本上一個USB裝置一插上電腦,電腦就會透過Endpoint 0來問一些資料。根據USB2.0規格,Host必須保留10%的頻寬給Control傳輸。

Bulk傳輸:它一次最大能傳的資料大小為512 bytes(HS)。它並沒有保證頻寬這件事情,通常都是HOST覺得BUS有頻寬了才會選擇傳輸Bulk資料。相對的,如果頻寬很空閒,每秒可傳輸的資料量就很大。

Interrupt傳輸:它一次最大能傳的資料大小為1024 bytes(HS),而且是定期傳輸,裝置端需要聲明這個期間是多久,以HS而言,是以micro-second為單位。它需要HOST保證頻寬,如果HOST無法滿足這個裝置的頻寬需求,HOST可以拒絕啟動這個裝置。

Isochronous傳輸:跟Interrupt傳輸非常類似,但是它沒有錯誤重傳的機制。以上三種都有錯誤重傳的機制,而這個機制建立在每次傳輸都會有ACK封包的前提下。Isochronous傳輸並不需要ACK封包,所以自然無法偵測錯誤。

Interrupt跟Isochronous傳輸還支援一種叫做"High Bandwidth"的傳輸,也就是在HS模式下,資料量最大可以三倍。作法很簡單,HOST每次要開始Interrupt或Isochronous傳輸時,直接發三次傳輸要求即可。不過我很少會看到有這樣子做的裝置,也有可能是我孤陋寡聞。

Isochronous傳輸可以用在即時的影音資料傳輸,因為這些影音資料錯了就錯了,沒關係。但是,凡是都有個但是,如果你的影音資料是一些有編碼過的,這個前提就有問題了。據我實驗的結果,如果你拿Isochronous傳輸MJPEG資料,並透過Windows內建的MJPEG decoder來解MJPEG,如果很不幸的資料掉了,那微軟的MJPEG decoder就有可能直接當掉,不解了。如果使用FFDSHOW反而沒這個問題。如果你今天是作產品的,敢賭這個嗎?

從上面的四種傳輸特性,你會發現沒有一種可以用來傳輸非常即時的大量重要資料?Interrupt傳輸可以保證頻寬,但是很抱歉,頻寬已經固定了,這次傳不完,等下次吧。Bulk傳輸沒有頻寬保證,所以根本不知道什麼時候可以傳完。Control傳輸資料量太小,而且也無法保證傳輸時間。Isochronous傳輸看似可以,但是資料不保證完整。

為什麼會有這種限制?主要是因為USB所有的傳輸都必須由HOST發起,而USB也沒有類似Interrupt這個腳位來讓HOST知道Device有急事要處理,所以一切都得等HOST的反應,而HOST也只能按照規範來作,所以結果就是這樣了。

在USB2.0規格裡,第五章裡面有計算每種傳輸的最大實際頻寬,有興趣可以自己算一下,其實USB的overhead還算蠻大的,尤其是每次的傳輸資料量小的時候,主要是檔頭跟CRC佔了一部分。這個沒辦法,每種傳輸介面一定都會有overhead,所以USB2.0號稱480Mbps,可是實際上可以達到240Mbps實際資料傳輸就很了不起了。這是因為不會每次都是大量的資料傳輸,一定會穿插一些小資料的傳輸。

這邊又有另外一個觀念,前面所提的USB傳輸是指Transaction,也就是不能傳超過該傳輸種類的資料大小。如果要傳超過最大資料量呢?那就是叫做Transfer。其實說穿了transfer就是數個transaction集合起來的。舉個例子,如果一個裝置支援一個最大資料量512 bytes的bulk-in endpoint,那的確在BUS上,你只能每次傳512 bytes。如果你要傳2048 bytes,你得分4次512 bytes,而這整個2048 bytes傳輸,就叫做transfer。這個觀念很重要,因為這牽涉到你的傳輸速度。如果你的裝置支援USB transfer的API呼叫,那就表示它可以用硬體幫你切成transaction傳輸,這樣會快很多。如果你用軟體做的話,速度會差很多。

有一些工程師不知道Transaction跟Transfer的差別,明明可以用transfer方式傳,結果還傻傻的用transaction,那結果就是效能低落,然後還怪別人或自己的硬體效能差。




USB 簡介[2]

USB就速度來分類的話,目前有四種:
  1. Low speed(LS): 最慢,只有1.5Mbps。
  2. Full speed(FS): 有點慢,12Mbps。
  3. High speed(HS): 快,480Mbps。
  4. Super speed(SS): 超快,5Gbps。這系列文章不會談到Super speed,因為我也沒做過。規格書是有唸過,但是不知道人家實際上是怎麼設計的。
HS必須下相容於FS。所以如果你的裝置是HS,很抱歉,你還得支援FS。一般而言,我們會把HS稱為USB2.0,這個基本上有點問題,因為USB2.0規格書還包含了FS跟LS。

在規格書裡,function這個名詞記一下,它對應到device端的功能部分,因為USB規定,一個裝置可以擁有超過一個的function,所以function有時候不等於device。至於compound device跟composite device的差別:compound device是一個實體裝置,但是它其實是一個hub跟多個function組成的,compound device的一個例子是一個支援hub的鍵盤。而composite device則是一個支援多個function的實體裝置,實際上的例子是現在的Android手機幾乎都同時支援MTP跟MSC功能,這種就是composite device。

compound device現實生活中比較難見,我猜原因是因為價錢因素吧。Hub功能比device難作多了,所以compound device會比一般的裝置貴,但是很難說服消費者用比較貴的價錢去買,因為hub的價錢已經被打爛了...。

接下來就是幾乎每個初學者都很頭痛的pipe。一開始看到規格書上的pipe,第一個想法一定是『它在講什麼』。要知道pipe是什麼,你得先從它整個架構上來看。而這也就是USB2.0規格第五章的核心:架構要先懂。

要了解一個架構,基本上不是bottom-up就是top-down方法。我是比較習慣用bottom-up的方式去了解,USB2.0規格則是用top-down方法。這裡我用bottom-up方法講。

USB2.0最底層的實體傳輸只有兩根線,D+跟D-,其他兩根是傳輸電力的,不用管。D+跟D-事實上是用來構成differential signal(台灣翻成差動訊號,後面都用這個名詞)。

差動訊號基本上就是用兩根訊號的準位差來表示一個準位。為什麼要用差動訊號呢?以前的訊號基本上都是用GND來當基準點,但是這會有缺點:萬一兩邊的GND準位不同,同一個電壓會對兩方有不同的意義。舉個例子,如果兩方一個GND 1.5V,一個0V,那萬一一方傳一個1.7V的訊號,一方會辨識成Low(0.2V),一方可能會變High(1.7V)。 差動訊號沒有這個困擾,而且抗干擾的能力變強,因為D+跟D-同時被干擾,但是相減就把干擾去掉了。另一個差動訊號的好處是,同樣的壓差,準位的差距可以變大,訊號的內容就可以變多,因為差距由(0V~5V)變成(5V ~ -5V)。因為差動訊號有這些優點,所以後來有很多的周邊都改為差動訊號了。

在這裡,你只要知道D+跟D-可以構成0跟1即可。實際上的0跟1準位判斷,USB2.0規格的第七章講得一清二楚。知道0跟1之後,往上層就是用這些0/1去構成一個個USB封包。這些封包的內容基本上就是說明這個封包要傳給哪個裝置,或者是由哪個裝置傳出來的。為什麼要這樣?這是因為USB是BUS架構,而實體傳輸又是只靠兩根線在傳輸,如果沒有封包的定址能力,雙方根本無法知道實體傳輸上的資料是要給誰啊。而USB並不滿足於裝置定址能力,它還規定每個裝置可以擁有特定的傳輸通道,也就是Endpoint,或者稱為Pipe(以下都用Endpoint稱呼)。Endpoint基本上是個虛擬的資料傳輸通道,它在USB Bus上其實也就是一個個封包,或者是一堆0/1的組合,但是人總是無法直接解譯0/1,所以越往上層越是抽象。所以下次人家跟你講Endpoint或者是Pipe時,不要再一臉狐疑了,這只會讓人家知道你不懂USB。

USB2.0規格對Endpoint型別有四種規範:

  1. Control
  2. Bulk
  3. Interrupt
  4. Isochronous

這四種基本上比較會用到的就是Control跟Bulk。Control Endpoint是雙向傳輸,其他三種都是單向傳輸,也就是說,如果你要雙向傳輸Bulk資料,你的裝置得支援兩個Bulk Endpoint才行。

在這邊又得提一個觀念,USB的資料傳輸方向一律是以HOST為視角,所以IN就是表示HOST要收資料、Device要寫資料。而OUT則是相反,HOST要寫資料、Device要收資料。

所以你會常常看到 Bulk-In、Bulk-Out、Interrupt-In、Interrupt-Out、Isochronous-In、Isochronous-Out這類的名詞。請搞清楚他們的方向性。要支援那些Endpoint是裝置端的責任跟權力。

一旦知道Endpoint,最上層的應用程式只要對這些Endpoint做讀寫就好,事情就變得比較簡單一點。而這也就是USB2.0的圖5-9所要表達的意思:每一層都把它自己的工作做好,事情會變得比較簡單。



Sunday, July 13, 2014

USB 簡介[1]

USB,全名是Universal Serial Bus。顧名思義,它是Serial介面,而且是BUS架構。Universal當初應該是希望能夠通吃所有裝置吧,就結果來看它也達成這個目標。

先來研究為什麼會設計成這樣(我的理解):

  1. Serial介面:這個應該是硬體設計思維。Parallel介面在高速的clock之下很難維持資料的同步,所以用serial傳輸會省掉一些麻煩。
  2. BUS架構:因為USB支援Plug and Play,所以設計成BUS架構很合理,擴充容易。


看起來沒有很厲害啊?是的,但是它堪用。一個東西能不能成功,往往不是一個因素或是技術很厲害造成的。USB過去跟現在之所以流行,我認為其實是很多因素造成的:

  1. 它不需要授權金
  2. 規格公開
  3. Windows支援
  4. balabala

這幾個理由就夠人家試試看了。至於價格便宜,那是後話了(台灣IC設計所賜)。

USB架構

USB是Master-Slave架構。USB Host一定是Master。通常USB Host就是指PC,但是現在行動裝置流行,也有可能是ㄧ些支援USB Host的行動裝置。而Slave就是指USB裝置,最有名的代表就是隨身碟(我討厭大陸用語[大拇哥],Mass Storage還比較有科技感)。

這邊先講一些基本知識,免得雞同鴨講。其實這些知識都是[計算機概論]或[計算機結構]課程的一部分,沒讀過或是懶得讀的也可以用google查到。

Master-Slave架構的特點如下:

  1. Master才能發起一個Request。
  2. Slave只能回應Master發出的Request。

至於BUS的特點:

  1. 大家共用線路。最大的優點是省線路,在很早以前能省線路就是省錢的年代,這個很重要。
  2. 沒了。

BUS架構很簡單對不對,但是衍生的問題很多。

第一,共用線路代表同一時間只能有一個裝置可以用到線路。所以如何讓BUS的使用率到達最高,是個很難的問題。你可以想像如果有個慢速裝置在BUS上傳一筆很大的資料,這樣的BUS使用率會有多糟。

第二,如果有裝置不遵守規矩怎麼辦?所以BUS會制訂它專屬的通訊協定,讓這件事情比較容易偵測。但是如果一個裝置真得要亂搞,也真的沒辦法。如果你知道Ethernet的CSMA/CD(Carrier Sense Multiple Access/Collision Detection)的話,你大概會知道可以在偵測到網路碰撞時,讓等待時間最小化,如此你的網路卡表現就可以比別人好。這樣是不合規範的,可是還真的沒辦法防止。

USB針對BUS架構的缺點,提出了它的解決方式,這也是大多是BUS架構的解決方式,就是Master-Slave架構。因為Master-Slave架構可以有效解決BUS使用率的問題,並且有限度的防止惡意裝置亂搞。用一句話就可以說明:你們這些USB裝置通通聽USB HOST命令,一個口令一個動作。

這邊不談OTG(USB On-The-Go),它是個變種。

USB 簡介 - 前言

其實USB簡介很早就應該寫了,因為光是這幾年就不知道為其他人上過幾次相關課程了。只不過那時都是用投影片,所以也沒辦法很深入。

算一算USB都已經快要問世20年了(今年剛好20,1994年開始設計),可是就我的觀察,還是很多做USB相關的工程師不是很懂。沒辦法,USB實在太複雜了,我自己也不是全懂。

台灣我看就ChamberPlus有在分享他的經驗,不過他的經驗主要是在實作跟USB1.1方面,而且透過8051基本上有一些SOC控制部分會有差異。我這邊大概只能分享一些SOC如何控制USB controller的部分,以及USB2.0。

為什麼USB會這麼難呢?我的感覺是因為有些事情是硬體做掉,有些是軟體做掉,而工程師大部分都不是軟硬通吃的,再加上有一些東西真的是要做了,或是有人教,才會知道。這造成很多軟體工程師都是一知半解在做USB產品,想當然爾問題就很多了。

Saturday, July 12, 2014

SPI 簡介

SPI,全名是Serial Peripheral Interface。他是master-slave架構。顧名思義,他也是用來跟各種裝置溝通。

標準的SPI需要四根線,分別是SCLKMOSIMISO,以及CS
SCLKclock,一定是由master拉。
MOSImaster-output-slave-inputMaster寫資料到Slave
MISOmaster-input-slave-outputSlave寫資料到Master
CS則是chip-select。當有兩個以上的SPI裝置的時候,CS有拉的SPI裝置才可以回應。

I2C很類似,不過還是有點不同:
1.      SPI沒有定義protocol,請參考每個SPI裝置的datasheet
2.      SPI多了兩根線。不過如果可以的話,MOSIMISO擇一就可。因為通常SPI裝置只會支援獨或寫。而這種三根線的SPI,有人叫3-wire。不過還是要跟對方確認清楚,因為3-wire泛指[只用三根線來通訊]的東西。萬一對方講的不是SPI,那就糗了。
3.      SPI可以跑比較快。為什麼可以跑比較快,我不是很清楚
4.      SPI基本上不算BUS架構(雖然同一個master可以接多個裝置),所以不會有裝置衝突的問題,但是缺點就是,如果要支援多個SPI裝置同時跑,就需要多個SPI master

在跟SPI裝置溝通時,要注意的事項基本上跟I2C差不多:
1.      Polarity:到底是clockrising edge還是falling edge要抓取或寫出資料。
2.      Clock skew:波型不要跟datasheet差太多即可。有些客戶很龜毛,一定要符合datasheet上的要求,如果你是用8051SPI的話,請自求多福吧。如果是硬體做掉,那就直接看硬體的支援程度了,一翻兩瞪眼。
3.      Debug也是用示波器就可以了。幾MHZ的波型基本的示波器都可以抓到。

在這邊順便提一下,台灣軟體工程師分兩種:一種是完全不碰硬體的,一種則是會碰(廢話)。如果你是前者,建議你不要碰I2CSPI。因為這兩種基本上一定要看波型,而這一定需要硬體工程師的幫忙(不然示波器要從哪裡來)。硬體工程師至少要幫你設定好環境(或是教你怎麼操作示波器),至於看波型,就看你跟硬體工程師交情了。


我基本上不太喜歡求人,所以最多就是去跟硬體工程師借示波器,了不起就是問一下怎麼操作。波型還是得自己看。我曾經看過有些軟體工程師一直凹硬體工程師幫他把波型存起來給他看,或是直接說是硬體的問題,叫硬體工程師debug的。我知道這是公司文化使然,但是這個真的很不好。如果你想請人幫忙,至少先做個功課吧。

Tuesday, July 08, 2014

I2C 簡介

I2C,念法應該是[ I-squared-C],不過不必糾結於念法,老美也有亂念的,像是[I-Two-C],聽得懂就好。

I2C只需要兩根線就可以。一根是SCLSerial Clock?),另外一根是SDASerial Data?)。SCL顧名思義,就是提供clock的線,SDA則是要跟clock對齊的資料。

I2Cmaster-slaveBUS架構,master是大多數只有一個,slave則是可以有很多個。而只有master可以拉SCL

因為只有一根線可以傳資料,所以必須定義SDA訊號的內容。I2C超簡單,只有幾種:
1.      Start condition:傳輸開始
2.      End condition:傳輸結束
3.      8-bit address:因為是BUS架構,必須指定這次傳輸要傳給哪個裝置。很好玩的是,I2C並沒有動態指定address的協定,所以每個裝置的address都是先講好的。所以記得檢查你的I2C bus上有沒有address衝除的裝置。每個裝置的datasheet都會說明它自己的address。有一個bit是拿來表示這次的傳輸是讀還是寫,所以其實只有7-bit可以用,所以address最大是127
4.      8-bit or 16bit data
5.      ACK:表示資料收到了

在這邊又衍生出另外一個問題:SDA到底要先傳most significant bit還是最後傳?google了一下發現是先傳。

I2C協定有許多變種,所以就不列波形圖(其實是懶),每個裝置的datasheet都會列出它需要的波形圖,唯一可以相信的就是這個。其實I2C對於波形的準確性不是很在意,意思到了就會動,而這也代表著它的傳輸速度不會太快,了不起100Kbps吧。請注意,100Kbps是指clock rate,也就是100Khz,實際上有效資料有到50Kbps就可以偷笑了。

我以前沒想過的是,用8051就可以拉出I2C需要的波型。後來想想也對,反正先拉SCL再拉SDA即可,反正容許誤差很大,這樣也會動。但是速度一定是慢,而且用8051寫這個可是一件苦差事。

還有一種是直接透過硬體控制I2C,只要填填register就好。這種比較簡單,而且速度比較快。

I2C debug很簡單,直接用示波器量訊號,一翻兩瞪眼,是誰的問題一目了然。

debug時,另外一個問題是(怎麼這麼多問題),到底誰會在哪個時候拉SDA?這個就是大哉問了。大部分都是master,裝置應該是只會在回資料跟ACK才會需要拉吧。這點一定要注意,不然到時候就會錯怪忠良了。


Monday, July 07, 2014

ARM-based SOC vs 8051-base USB device solution

目前我遇到的USB device控制方案有兩種:一種是8051-based,另外一種是ARM-based。這兩種控制方案基本上是兩種不同的硬體思維所產生的。

8051-based USB devices

會採用這種方式的公司通常是要生產[固定功能]的USB裝置,也就是這家公司出的USB裝置幾乎只有一種功能(了不起再多一種)。要改成其他的,不好意思,等下一版IC吧。

因為USB功能大多固定,所以許多功能直接用硬體做掉會比較有效率。相較之下,CPU只是拿來做一些客製化或流程控制用,因此用8051綽綽有餘了。當然,重點是[授權金很便宜,甚至不用錢],這永遠是台灣IC設計的最愛

我之前的公司就是做這一類的,8051負擔的工作很簡單:

1.          初始化USB controller
2.          提供 USB descriptors USB Host
3.          處理一些 standard/class/vendor requests

有沒有發現,USB資料傳輸根本不需要8051介入,狠一點的公司可能可以連8051都不要了。

優點

1.          便宜(功能固定)
2.          效能好(還是因為功能固定)

缺點

1.          支援的記憶體大小有限。雖然大多數的8051開發環境宣稱支援C,但是為記憶體大小的關係,大多數還是得用組合語言寫程式。更慘的是,用C編出來的組合語言有時還會有問題。而且幾乎不可能在8051上執行OS,這代表所以事情都得自己來。
2.          支援的周邊有限,擴充能力有限。

ARM-based USB devices

會採用這種方式通常是希望同一顆IC可以支援的USB功能越多越好。這種想法所導致的結果就是USB device controller的功能會非常有彈性,而CPU控制所有事情。8051當然沒辦法負擔這種差事-它跑太慢了,而且支援的周邊有限。

ARM架構就不用多說,用google可以查出一狗票資料。

優點

1.          有彈性,不論是硬體或軟體
2.          支援很多周邊

缺點

1.          比較貴,不過現在ARM已經有便宜版
2.          效能一般會比純硬體的裝置差

結論

就軟體工程師的角度來看,當然是後者比較好玩,因為軟體複雜度比較高,可以調整的東西比較多。

codeblock