關於stm32的VCP技術原理

  在現代個人電腦的USB是幾乎所有外設的標準通信端口。然而許多工業應用軟件仍然使用經典的串口(UART)。USB虛擬串口提供了繞過這個問題的一個簡單的解決方案。

        爲了讓USB被視爲一個COM端口,USB設備必須根據通信設備類(CDC)規範來實現兩個接口:

1.抽象控制模型通信,在端點中有1箇中斷:在我們的實現中,這個接口在描述符中聲明,但是相關的端點(端點2)不被使用。

2.抽象控制模型數據,具有1個bulk in端點和一個bulk out 端點,這個接口在實際中由端點1(in)和端點3(out)表示。端點1用於通過USB將從UART接收到的數據發送到PC。端點3用於接收來自PC的數據並通過UART發送。

爲了實現虛擬COM端口,該設備支持以下類特定請求:

● SET_CONTROL_LINE_STATE:

RS-232信號用於告訴設備數據終端設備現在是存在的.該請求總是在Virtual_Com_Port_NoData_Setup() 函數中返回一個USB_SUCCESS(usb_prop.c中 )。

● SET_COMM_FEATURE:

控制特定通信功能的設置。這個請求總是在Virtual_Com_Port_NoData_Setup() 函數中返回一個USB_SUCCESS(usb_prop.c中 )。

● SET_LINE_CODING:

發送設備的配置。它包括波特率、停止位、奇偶校驗和字符位數。所接收的數據存儲在一個特定的數據結構中,稱爲“linecoding”,用於更新UART參數。

● GET_LINE_CODING:

此命令要求獲取設備當前波特率、停止位、奇偶校驗和字符位數。該設備用存儲在“linecoding”結構中的數據做出響應。

 

硬件配置接口

在虛擬COM端口中的硬件配置接口(hw _ config.c)管理以下程序:

●配置的系統和外設(USB和USART)時鐘和中斷

●USART默認值初始化

●用通過SET_LINE_CODING命令收到的參數配置串口

●把收到的數據發送到PC,通過USB串口通訊

●發送收到的數據通過USB串口通訊

 

1、USB標準規範類型

    USB規範大致可分爲以下三類:

1)、標準規範

    標準規範爲最基礎的規範,主要有USB1.0、USB1.1、USB2.0和USB3.0等等。

2)、USB設備類規範

    設備類規範主要是針對於具體的USB設備而推出的類規範,有Mass Storage、Audio Device、Video Device等等設備相關規範。

3)、USB HOST控制器規範

    主要有OHC和UHC等

1、標準規範

     USB是通用串行總線的英文縮寫,是Intel公司開發的總線架構,使得在計算機上添加串行設備非常容易。只須將設備插入計算機的USB端口中,系統會自動識別和配置。根據時代發展,USB接口標準經歷了一代USB、第二代USB 2.0和第三代USB 3.0。

    USB規格第一次是於1995年,由Intel、IBM、Compaq、Microsoft、NEC、Digital、North Telecom等七家公司組成的USBIF(USB Implement Forum)共同提出,USBIF於1996年1月正式提出USB1.0規格,頻寬爲1.5Mbps.不過因爲當時支持USB的周邊裝置少的可憐,所以主機板商不太把USB Port直接設計在主機板上。

    USB2.0技術規範是有由Compaq、Hewlett Packard、Intel、Lucent、Microsoft、NEC、Philips共同制定、發佈的,規範把外設數據傳輸速度提高到了480Mbps,是USB 1.1設備的40倍!2000年制定的USB 2.0標準是真正的USB 2.0,被稱爲USB 2.0的高速(High-speed)版本,理論傳輸速度爲480 Mbps.

    USB 3.0是最新的USB規範,該規範由英特爾等公司發起,USB3.0的最大傳輸帶寬高達5.0Gbps(640MB/s),USB3.0 引入全雙工數據傳輸。5根線路中2根用來發送數據,另2根用來接收數據,還有1根是地線。也就是說,USB 3.0可以同步全速地進行讀寫操作。

USB版本

最大傳輸速率

速率稱號

最大輸出電流

推出時間

USB1.0

1.5Mbps(192KB/s)

低速(Low-Speed)

5V/500mA

1996年1月

USB1.1

12Mbps(1.5MB/s)

全速(Full-Speed)

5V/500mA

1998年9月

USB2.0

480Mbps(60MB/s)

高速(High-Speed)

5V/500mA

2000年4月

USB3.0

5Gbps(500MB/s)

超高速(Super-Speed)

5V/900mA

2008年11月

USB 3.1

10Gbps(1280MB/s)

超高速+(Super-speed+)

20V/5A

2013年12月

這是從時間維度上來看的,但是每一代USB接口針對不同的設備又細分出來具體的型號,如USB Type A/B/C/Mini/Micro。下面是USB2.0和USB3.0標準下的各類接口示意圖:

 

一、USB系統的結構

    USB系統是由三個邏輯層組成:功能層、USB設備層和USB總線接口層。並且每一層都是由主機和USB設備不同的功能模塊組成,如下圖所示:

1、功能層(接口)

    功能層是由客戶軟件和設備方的功能單元組成,其能夠實現USB設備傳輸的特定功能。通過功能層可直觀地理解USB傳輸的數據內容。其中,客戶軟件通過USB系統軟件來與USB設備進行通信。功能單元對於客戶軟件,可視爲接口的集合。

 

2、USB設備層 (端點)

    USB設備層是由USB系統軟件和USB設備的USB邏輯設備組成,其實現主機和USB設備之間傳輸的具體配置。USB邏輯設備對於USB系統軟件,可視爲端點的集合。

 

3、USB總線接口層

    USB總線接口層是由主機的USB主控制器和設備的USB總線接口組成。其實現主機和USB設備實際的數據傳輸。

 

4、主機部分

    USB主機部分由客戶軟件、USB系統軟件和USB總線接口組成。

4.1 客戶軟件

    客戶軟件負責和USB設備的功能單元進行通信,以實現特定的功能。客戶軟件不能直接與USB設備相連接,必須通過USB系統軟件和USB總線接口才能實現連接。客戶軟件包括USB設備驅動程序和界面應用程序兩部分。

 

4.2 USB系統軟件

    USB系統軟件負責和USB邏輯設備進行配置通信,並管理客戶軟件啓動的數據。一般包括USB總線驅動程序、USB主控制驅動程序和非USB主機軟件三個部分,這部分會由系統提供。

 

4.3 USB總線接

    USB總線接口包括主控制器和跟集線器兩部分。其中,主控制器是負責完成主機和USB設備間的數據實際傳輸。根集線器是爲USB系統連接起點。

 

5、設備部分

    USB設備部分由三個功能模塊組成,分別是USB總線接口、USB邏輯設備和功能單元。

    功能單元看作是一個接口的集合;USB 邏輯設備被USB系統軟件看作一個端點的集合;USB總線接口是USB設備中的串行接口引擎(SIE)。

 

    當客戶程序通過USB管道發送或接收數據時,它首先調用Win32 APl,調用最終將使功能驅動程序收到一個IRP。而驅動程序的工作就是把客戶的請求引導到有正確端點的管道上。它把請求提交到總線驅動程序,總線驅動程序再把請求分解成多個事務,然後這些事務被送往總線。總線上的信息流以每毫秒一幀數據的形式流動。總線驅動程序必須安排好多個事務以使它們能被裝入同一幀中。

 

二、USB的拓撲結構

    USB是一種主從結構的系統。主機叫Host,從機叫做Device(也叫設備)。通常所說的主機具有一個或者多個USB主控制器(host  controller)和根集線器(root hub)。主控制器主要負責數據處理,而跟集線器則提供一個連接主控制器與設備之間的接口和通路。

    通常情況下,PC機上有多個主控器和多個USB口,一個主控器下有一根集線器,一根集線器通常具有一個或者幾個USB接口。通常集線器可以通過USB集線器來擴展USB接口,只是帶寬是不能夠拓寬的,因爲帶寬是共享一個USB主控器的。

    瞭解上面的信息後,再來看USB的拓撲結構如下圖:

USB的拓撲結構看起來是一個金字塔形的結構,具體構成如下:

    塔頂是USB的主控器和根集線器,下面接USB集線器,USB集線器將一個USB口擴展爲多個USB口,多個USB口同樣可以通過USB集線器擴展出更多的接口。不過USB口並不能通過USB集線器無止境的擴展,他是有限制的,例如在USB2.0規定它最多擴展6次。

    理論上一個USB主控器最多可接127個設備,這是因爲協議規定每個USB設備具有一個7bit地址(取值範圍爲0~127,地址用於給主機識別是哪個設備,其中0地址值得注意,是給剛接入未初始化的設備使用的)。

 

三、USB的設備架構

    設備架構認爲設備是由一些配置、接口和端點構成的。其中配置和接口是USB功能的抽象,實際的數據傳輸由端點來完成。其對應關係如下圖所示:

1.設備

    設備代表USB設備,它由一個或多個配置組成。設備描述符用於說明設備的總體信息,並指出其所包含配置的個數。

 

2.配置 

    在使用USB設備前,必須爲其選擇一個合適的配置。如USB設備的低功耗模式和高功耗模式分別對應一個配置。配置描述符用於說明USB設備中各個配置的特性。

 

3.接口 

    一個配置可以包含一個或多個接口。接口是一個端點的集合。接口描述符用於說明USB設備中各個接口的特性。 

 

4.端點 

    端點是USB設備中的實際物理單元,USB數據傳輸就是在主機和USB設備各個端點之間進行的。

    

5、管道

    管道,是主機軟件(數據緩衝區)和USB設備的各個端點之間的數據傳輸鏈接,它是兩者之間通信流的抽象。然而,實際的數據傳輸是由USB總線接口層來完成的。管道和USB設備中的端點一一對應,並且各個管道的數據傳輸是相互獨立的。

    管道有兩種類型:流管道和消息管道。其中最爲重要的消息管道是“缺省控制管道”,這個管道在設備開始上電後就存在了,它用於提供設備的配置與狀態等信息。主機與設備之間的聯絡就是通過消息管道實現的。

 

四、USB枚舉與通信的具體過程

1、USB接頭

    下圖是一個USB接頭的結構圖:

    由圖可以看出,標準的USB鏈接線使用4芯電纜:5V電源線(Vbus)\差分數據線負(D-)、差分數據線正(D+)和地線(GND)。

    在hub端,數據線D+和D-都有一個阻值在14.25k到24.8k的下拉電阻Rpd,而在設備端,D+(全速,高速)和D-(低速)上有一個1.5k的上拉電阻Rpu。當設備插入到hub端口時,有上拉電阻的一根數據線被拉高到幅值的90%的電壓(大致是3V)。hub檢測到它的一根數據線是高電平,就認爲是有設備插入,並能根據是D+還是D-被拉高來判斷到底是什麼設備(全速/低速)插入端口。

 

2、USB通信過程

    主機和USB設備可以相互傳輸數據,其具體過程如下(以主機箱設備傳輸爲例):

step1:客戶軟件首先將要傳輸的數據放入緩衝區,同時向USB總線驅動程序發出IRPS,請求數據傳輸。(客戶軟件)step2:USB總線驅動接收到程序接收請求,並對數據進行處理,轉化爲具有USB格式的事務處理。(USB系統軟件)

step3:USB主控制器驅動程序將這些事務處理建立成事務列表,同時要求不能超過USB帶寬。(USB系統軟件)

step4:USB主控制器讀取到事務列表並將事務轉化爲信息包,發送到USB總線上。(USB總線接口)

step5:USB設備收到這些信息後,SIE(USB總線接口是USB設備中的串行接口引擎)將其解包後放入指定端點的接收緩衝區內,由芯片固件對其進行處理。

 

用框圖表示如下:

3、USB枚舉通信具體過程

     枚舉就是從設備讀取一些信息,知道設備是什麼樣的設備,如何進行通信,這樣主機就可以根據這些信息來加載合適的驅動程序。調試USB設備,很重要的一點就是USB的枚舉過程,只要枚舉成功了,那麼就已經成功大半了。

 

枚舉通信過程具體如下:

step1:檢測電壓變化,報告主機

    首先,USB設備上電後,一直監測USB設備接口電平變化HUB檢測到有電壓變化,將利用自己的中斷端點將信息反饋給主控制器有設備連接。

 

Step2:主機瞭解連接設備

    主機在知道有設備接入後會發送一個Get_Port_Status請求(request)給hub以瞭解此次狀態改變的確切含義。

 

Step3:Hub檢測所插入的設備是高速還是低速

    hub通過檢測USB總線空閒(Idle)時差分線的高低電壓來判斷所連接設備的速度類型,當host發來Get_Port_Status請求時,hub就可以將此設備的速度類型信息回覆給host。USB 2.0規範要求速度檢測要先於復位(Reset)操作。

 

Step4:hub復位設備

    主機一旦得知新設備已連上以後,它至少等待100ms以使得插入操作的完成以及設備電源穩定工作。然後主機控制器就向hub發出一個 Set_Port_Feature請求讓hub復位其管理的端口(剛纔設備插上的端口)。hub通過驅動數據線到復位狀態(D+和D-全爲低電平 ),並持續至少10ms。當然,hub不會把這樣的復位信號發送給其他已有設備連接的端口,所以其他連在該hub上的設備自然看不到復位信號,不受影響。

 

Step5: Host檢測所連接的全速設備是否是支持高速模式

    因爲根據USB 2.0協議,高速(High Speed)設備在初始時是默認全速(Full Speed )狀態運行,所以對於一個支持USB 2.0的高速hub,當它發現它的端口連接的是一個全速設備時,會進行高速檢測,看看目前這個設備是否還支持高速傳輸,如果是,那就切到高速信號模式,否則就一直在全速狀態下工作。

    同樣的,從設備的角度來看,如果是一個高速設備,在剛連接bub或上電時只能用全速信號模式運行(根據USB 2.0協議,高速設備必須向下兼容USB 1.1的全速模式)。隨後hub會進行高速檢測,之後這個設備纔會切換到高速模式下工作。假如所連接的hub不支持USB 2.0,即不是高速hub,不能進行高速檢測,設備將一直以全速工作。

 

Step6:Hub建立設備和主機之間的信息通道

    主機不停地向hub發送Get_Port_Status請求,以查詢設備是否復位成功。Hub返回的報告信息中有專門的一位用來標誌設備的復位狀態。

       當hub撤銷了復位信號,設備就處於默認/空閒狀態(Default state),準備接收主機發來的請求。設備和主機之間的通信通過控制傳輸,默認地址0,端點號0進行。此時,設備能從總線上得到的最大電流是100mA。(所有的USB設備在總線復位後其地址都爲0,這樣主機就可以跟那些剛剛插入的設備通過地址0通信。)

 

Step7:主機發送Get_Descriptor請求獲取默認管道的最大包長度

     默認管道(Default Pipe)在設備一端來看就是端點0。主機此時發送的請求是默認地址0,端點0,雖然所有未分配地址的設備都是通過地址0來獲取主機發來的請求,但由於枚舉過程不是多個設備並行處理,而是一次枚舉一個設備的方式進行,所以不會發生多個設備同時響應主機發來的請求。

      設備描述符的第8字節代表設備端點0的最大包大小。雖然說設備所返回的設備描述符(Device Descriptor)長度只有18字節,但系統也不在乎,此時,描述符的長度信息對它來說是最重要的,其他的瞄一眼就過了。當完成第一次的控制傳輸後,也就是完成控制傳輸的狀態階段,系統會要求hub對設備進行再一次的復位操作(USB規範裏面可沒這要求)。再次復位的目的是使設備進入一個確定的狀態。

 

Step8:主機給設備分配一個地址

     主機控制器通過Set_Address請求向設備分配一個唯一的地址。在完成這次傳輸之後,設備進入地址狀態(Address state),之後就啓用新地址繼續與主機通信。這個地址對於設備來說是終生制的,設備在,地址在;設備消失(被拔出,復位,系統重啓),地址被收回。同一個設備當再次被枚舉後得到的地址不一定是上次那個了。

 

Step9:主機獲取設備的信息

    主機發送 Get_Descriptor請求到新地址讀取設備描述符,這次主機發送Get_Descriptor請求可算是誠心,它會認真解析設備描述符的內容。設備描述符內信息包括端點0的最大包長度,設備所支持的配置(Configuration)個數,設備類型,VID(Vendor ID,由USB-IF分配), PID(Product ID,由廠商自己定製)等信息。

    之後主機發送Get_Descriptor請求,讀取配置描述符(Configuration Descriptor),字符串等,逐一瞭解設備更詳細的信息。事實上,對於配置描述符的標準請求中,有時wLength一項會大於實際配置描述符的長度(9字節),比如255。這樣的效果便是:主機發送了一個Get_Descriptor_Configuration 的請求,設備會把接口描述符,端點描述符等後續描述符一併回給主機,主機則根據描述符頭部的標誌判斷送上來的具體是何種描述符。

      接下來,主機就會獲取配置描述符。配置描述符總共爲9字節。主機在獲取到配置描述符後,根據裏面的配置集合總長度,再獲取配置集合。配置集合包括配置描述符,接口描述符,端點描符等等。

     如果有字符串描述符的話,還要獲取字符串描述符。另外HID設備還有HID描述符等。

 

Step10: 主機給設備掛載驅動(複合設備除外)

    主機通過解析描述符後對設備有了足夠的瞭解,會選擇一個最合適的驅動給設備。  然後tell the world(announce_device)說明設備已經找到了,最後調用設備模型提供的接口device_add將設備添加到 usb 總線的設備列表裏,然後 usb總線會遍歷驅動列表裏的每個驅動,調用自己的 match(usb_device_match) 函數看它們和你的設備或接口是否匹配,匹配的話調用device_bind_driver函數,現在就將控制權交到設備驅動了。   

     對於複合設備,通常應該是不同的接口(Interface)配置給不同的驅動,因此,需要等到當設備被配置並把接口使能後纔可以把驅動掛載上去。

 

Step11:設備驅動選擇一個配置

    驅動(注意,這裏是驅動,之後的事情都是有驅動來接管負責與設備的通信)根據前面設備回覆的信息,發送Set_Configuration請求來正式確定選擇設備的哪個配置(Configuration)作爲工作配置(對於大多數設備來說,一般只有一個配置被定義)。至此,設備處於配置狀態(Configured),當然,設備也應該使能它的各個接口(Interface)。

    對於複合設備,主機會在這個時候根據設備接口信息,給它們掛載驅動。

一、端點概念。

    端點(Endpoint),是主機與設備之間通訊數據的接收或來源。主機與設備之間通信時最終會總用於設備上的各個端點,它是主機與設備間通信流的一個邏輯終端。一系列相互獨立的端點在一起構成了USB邏輯設備,在系統結構中,位於下方紅色方框內:

二、端點的分類

    每個USB設備都有一個唯一的設備地址,設備地址是設備連接上主機時由主機分配的,主機主要依靠這個設備地址對USB設備進行訪問。但是在設備內部地址會被分的更細,設備會分出一些端點來,每個端點在設備都會有唯一的端點號,這個端點號是設計設備時給定的。如端點0,端點1等。一個設備最多可以包含16個端點,每個端點的地址爲0-15。(網上也有說幾十個的,有待考究)    

    其中每個端點地址對應一個方向。例如端點3-IN,端點3-OUT,這兩個含義完全不同。但是需要注意其中的一個特殊端點--端點0,每個USB設備必須要有一個端點0,其作用爲對設備枚舉和對設備進行一些基本的控制功能,端點0也被稱爲控制端點。並且它與其他的端點還有一個不同之處在於端點0的數據傳輸方向是雙向的,即端點0既可以給主機發送數據,也可以接收主機發送過來的數據,而其它端點均爲單向。

    雖然有16個端點,但通常我們只用到3個,如下:

     1)、EP0:做傳輸配置和控制信息;

     2)、EP1:做數據輸入IN_EP;

     3)、EP2:做數據輸出OUT_EP。

 

注意:除了端點0,其餘的端點在設備配置之前不能與主機通信,只有向主機報告這些端點的特性並被確認後才能被激活。

 

三、端點的特性

    一個端點的特性決定了它與客戶軟件進行傳送的類型。一個端點具有以下一些特性:

   ·端點的總線訪問頻率要求

   ·端點的總線延遲要求

   ·端點的帶寬要求

   ·端點的端點號

   ·對錯誤處理的要求

   ·端點能接收或發送的包的最大長度

   ·端點的傳送類型

   ·端點與主機的數據傳送方向

 

四、端點描述符

    USB設備中端點描述符描述了端點信息,端點描述符格式如下:

typedef struct _USB_ENDPOINT_DESCRIPTOR_

{

    BYTE        bLength,

    BYTE        bDescriptorType,

    BYTE        bEndpointAddress,

    BYTE        bmAttributes,

    WORD      wMaxPacketSize,

    BYTE        bInterval

}USB_ENDPOINT_DESCRIPTOR;

各變量具體釋義如下:

bLength : 描述符大小.固定爲0x07.

bDescriptorType : 接口描述符類型.固定爲0x05.

bEndpointType : USB設備的端點地址.Bit7,方向,對於控制端點可以忽略,1/0:IN/OUT.Bit6-4,保留.BIt3-0:端點號.

bmAttributes : 端點屬性.Bit7-2,保留.BIt1-0:00控制,01同步,02批量,03中斷.

wMaxPacketSize : 本端點接收或發送的最大信息包大小.

bInterval : 輪訓數據傳送端點的時間間隔.對於批量傳送和控制傳送的端點忽略.對於同步傳送的端點,必須爲1,對於中斷傳送的端點,範圍爲1-255。

 

五、端點與管道

1、管道的概念

    管道是主機軟件(數據緩存區),和USB設備各各端點之間的數據傳輸連接,他是兩者之間通信流的抽象(實際上數據傳輸是USB總線接口完成)。管道與USB設備中的端點逐個對應,並且各個管道的數據傳輸是相互獨立的。

 

2、管道的格式分類

    管帶的通信格式分爲兩種,一種爲流,另一種爲消息,這兩種通信格式不同且互斥。

1)、“流”指不具有USB定義格式的數據流,流通道中的數據是流的形式,也就是該數據內容不具有USB要求的結構。數據從流通道一端流進的順序與它們從流通道另一端流出時的順序是一樣的(先進先出),並且流通道中的通信流總是單向的。

2)、“消息”指具有某種USB定義格式的數據流。消息通道與端點的關係同流通道與端點的關係是不同的。首先,主機向USB設備發出一個請求;接着,就是數據的傳送;最後,是一個狀態階段(這部分即一次命令請求的過程)。爲了能夠容納請求/數據/狀態的變化,消息通道要求數據有一個格式,此格式保證了命令能夠被可靠地傳送和確認。消息通道允許雙方向的信息流。

 

六、端點的傳輸類型

    一個具體的端點只能屬於四個傳輸模式下中的一種。數據傳輸類型分爲四種分別是:控制傳輸、批量傳輸、同步傳輸和中斷傳輸。一般情況下,通常把工作在什麼模式下的端點就叫什麼端點,例如:控制端點、批量端點、同步端點和中斷端點。

    端點0,是設備的默認控制端點,在設備上電後就存在並可以使用,在Set Config之前所有的傳輸都是通過端點0傳輸的。

USB控制傳輸分爲以下四種:

  • 批量傳輸:批量傳輸一般用於批量的和非實時的數據傳輸,通俗的來說就是用於數據量大但對時間要求又不高的場合的一種傳輸方式,類似用於USB打印機和USB掃描儀等等。
  • 中斷傳輸:中斷傳輸一般用於小批量的和非連續的數據傳輸,通俗的來說就是用於數據量小的數據不連續的但實時性高的場合的一種傳輸方式,類似用於USB鼠標和USB鍵盤等等。
  • 等時傳輸:等時傳輸也有“同步傳輸”的叫法,一般用於要求數據連續、實時且數據量大的場合,其對傳輸延時十分敏感,類似用於USB攝像設備,USB語音設備等等。
  • 控制傳輸:控制傳輸是一種特殊的傳輸方式,且傳輸過程相對以上三種而言更復雜一些,但也十分重要。當USB設備初次連接主機時,用控制傳輸傳送控制命令等對設備進行配置。同時設備接入主機時,需要通過控制傳輸去獲取USB設備的描述符以及對設備進行識別,在設備的枚舉過程中都是使用控制傳輸進行數據交換。

 

一、控制傳輸的結構

    一次完整控制傳輸可以分爲三個階段:初始設置階段--->數據階段(不必須)--->狀態信息階段。下面的

 

1、初始設置階段

    初始設置階段用於固定建立SETUP事務,標誌一次控制傳輸的開始。初始設置階段爲一個SETUP事務,同樣分爲三個階段如下:

令牌包階段:

    主機會發送一個SETUP令牌包,如下:

相當於告訴設備,我要跟你進行通訊請你做好準備。

 

數據包階段:

    發送DATA0數據包(注意SETUP只能使用DATA0包,8字節),讓設備接收。例如發送獲取設備描述符命令包:

相當於告訴設備,請將設備描述符的內容發給我。

 

握手包階段:

    設備自動應答。

 

結合上面的過程可以用下圖表示初始設置階段。

 

2、數據階段

    初始設置階段中命令如果要求讀/寫數據,數據階段就會在這一階段來具體交換數據(如果沒有數據交換要求則可省去該步驟,具體有SETUP事務標準請求命令決定)。在此需要做一些說明:傳輸控制在前言有說到控制傳輸的用途是獲取設備信息與對設備進行配置。所以這些數據操作分爲以下三類:

1)、控制讀傳輸;

2)、控制寫傳輸;

3)、無數據控制傳輸。

 

    一次控制傳輸必定爲上面三種中的其中一種,所以數據階段中的數據事務也是根據該規則來決定數據事務的。

    此處還應該注意的是數據階段是由一到多個IN/OUT事務組成。這是由於有時候存在一個事務傳不完的數據,所以可能存在多個連續IN/OUT事務的情況。這也就決定了,在同一次數據傳輸階段中事務類型必定相同(IN/OUT事務)。

 

2.1、傳輸格式

    綜上所述,所以從傳輸控制的不同類型來講述數據階段的格式會更好理解。

1)、控制讀傳輸

    控制讀傳輸時數據階段在整個傳輸的格式如下圖藍框部分:

 

數據方向爲:設備 —> 主機 (讀取USB描述符)

    這裏每個數據包是DATA0和DATA1交替出現的。需要注意的是當最後一個包剛好爲允許的最大數據包大小時需要再傳一個0長度的數據包,表示傳輸的結束。

 

控制讀傳輸的數據過程IN事務的三個階段如下:

令牌包階段:

    主機會發送一個IN令牌包,觸發設備產生IN包中斷,如下:

數據包階段:

    設備回覆主機請求,迴應數據。例如回覆設備描述符命令請求:

握手包階段:

    主機自動應答。

2)、控制寫傳輸

    控制寫傳輸時數據階段在整個傳輸的格式如下下圖藍框部分:

數據方向爲:主機 —> 設備(配置USB設備)

 

傳輸過程和規則基本與讀取相似,不多做贅述。

 

 

3)、無數據控制

    控制傳輸不一定要傳輸很多數據,有些控制可能只是告訴設備要做一件事,這個命令包含在建立階段的建立事務的8字節數據中即可,設備只需回覆主機收到命令與否即可,所以就跳過數據階段直接進入到狀態階段。無數據控制的格式如下圖:

所以注意在無數據控制傳輸時,是無數據階段的!

 

 

3、狀態信息階段

    狀態信息階段是要返回數據傳輸的成功與否,具體也需要看控制傳輸的類型。需要注意的是,狀態信息的數據傳輸方向與數據階段方向相反。例如,數據階段爲IN事務則狀態信息階段爲OUT事務。

 

3.1、控制讀傳輸

    在控制讀傳輸時,該階段則爲OUT事務,其中的數據包固定爲DATA1數據包。返回數據成功與否以有以下情況:

    1)、讀數據成功                      主機發送OUT令牌包(ping令牌包,高速情況下),主機發送0長度數據包,設備ACK。

    2)、數據傳輸出錯                   主機發送OUT令牌包(ping令牌包,高速情況下),主機發送0長度數據包,設備STALL。

    3)、設備忙(比如正在寫數據)   主機發送OUT令牌包(ping令牌包,高速情況下),主機發送0長度數據包,設備NAK。 

 

控制讀傳輸的狀態信息階段OUT事務的三個階段如下(以ACK爲例):

令牌包階段:

    主機會發送一個OUT令牌包,如下:

數據包階段:

    主機發送0字節數據包,作爲狀態正常信息迴應:

握手包階段:

    設備自動應答。

3.2、控制寫傳輸

    在控制讀傳輸時,該階段則爲IN事務,其中的數據包固定爲DATA1數據包。返回數據成功與否以有以下情況:

    1)、寫數據成功                      主機發送IN令牌包,主機發送0長度數據包,設備回覆ACK。

    2)、數據傳輸出錯                   主機發送IN令牌包,設備回覆STALL。

    3)、設備忙(比如正在寫數據)   主機發送IN令牌包,設備回覆NAK。 

 

控制讀傳輸的狀態信息階段IN事務過程與讀類似。

 

3.3、無數據控制傳輸

    該階段則爲IN事務,其規則與控制寫傳輸相似。

 

至此,一次控制傳輸完成,整個過程結束。

 

STM32F103 的 MCU 自帶 USB 從控制器,符合 USB 規範的通信連接;PC 主機和微控制器
之間的數據傳輸是通過共享一專用的數據緩衝區來完成的,該數據緩衝區能被 USB 外設直接訪
問。這塊專用數據緩衝區的大小由所使用的端點數目和每個端點最大的數據分組大小所決定,
每個端點最大可使用 512 字節緩衝區(專用的 512 字節,和 CAN 共用),最多可用於 16 個單
向或 8 個雙向端點。USB 模塊同 PC 主機通信,根據 USB 規範實現令牌分組的檢測,數據發送
/接收的處理,和握手分組的處理。整個傳輸的格式由硬件完成,其中包括 CRC 的生成和校驗。
每個端點都有一個緩衝區描述塊,描述該端點使用的緩衝區地址、大小和需要傳輸的字節
數。當 USB 模塊識別出一個有效的功能/端點的令牌分組時,(如果需要傳輸數據並且端點已配
置)隨之發生相關的數據傳輸。USB 模塊通過一個內部的 16 位寄存器實現端口與專用緩衝區的
數據交換。在所有的數據傳輸完成後,如果需要,則根據傳輸的方向,發送或接收適當的握手
分組。在數據傳輸結束時,USB 模塊將觸發與端點相關的中斷,通過讀狀態寄存器和/或者利用
不同的中斷來處理。
USB 的中斷映射單元:將可能產生中斷的 USB 事件映射到三個不同的 NVIC 請求線上:
1、USB 低優先級中斷(通道 20):可由所有 USB 事件觸發(正確傳輸,USB 復位等)。固件
在處理中斷前應當首先確定中斷源。
2、USB 高優先級中斷(通道 19):僅能由同步和雙緩衝批量傳輸的正確傳輸事件觸發,目
的是保證最大的傳輸速率。
3、USB 喚醒中斷(通道 42):由 USB 掛起模式的喚醒事件觸發

ST 提供的 USB 驅動庫,可以在: http://www.stmcu.org/document/detail/index/id-213156 這裏
下載到(STSW-STM32121)

ST 不但提供源碼,還提供了說明文件:CD00158241.pdf(UM0424),專門講解 USB 庫怎
麼使用。有了這些資料對我們瞭解 STM32F103 的 USB 會有不少幫助,尤其在不懂的時候,看
看 ST 的例程,會有意想不到的收穫。本實驗的 USB 部分就是移植 ST 的 Virtual_COM_Port 例
程相關部分而來,完成一個 USB 虛擬串口的功能。
硬件設計

53.3 軟件設計
代碼移植自 ST 官方例程:

有了這個官方例程做指引,我們就知道具體需要哪些文件,從而實現本例程。
首先,在工程文件夾下面,新建 USB 文件夾,
並拷貝官方 USB 驅動庫相關代碼到該文件夾下,即拷貝STM32_USB-FS-Device_Lib_V4.0.0Libraries 文件夾下的 STM32_USB-FS-Device_Driver 文件夾到該文件夾下面。然後,在 USB 文件夾下,新建 CONFIG 文件夾存放 Virtual COM 實現相關代碼,即:STM32_USB-FS-Device_Lib_V4.0.0ProjectsVirtual_COM_Portsrc 文件夾下的部分代碼:
hw_config.c、usb_desc.c、usb_endp.c、usb_istr.c、usb_prop.c 和 usb_pwr.c 等 6 個.c 文件,同時
拷貝:STM32_USB-FS-Device_Lib_V4.0.0ProjectsVirtual_COM_Portinc 文件夾下面的:
hw_config.h、platform_config.h、usb_conf.h、usb_desc.h、usb_istr.h、usb_prop.h 和 usb_pwr.h 等
7 個頭文件到 CONFIG 文件夾下,最後 CONFIG 文件夾下的文件如圖

之後,根據 ST 官方 Virtual_COM_Port 例程,在我們本章例程的基礎上新建分組添加相關
代碼,具體細節,這裏就不詳細介紹了,添加好之後,如圖  所示: 

移植時,我們重點要修改的就是 CONFIG 文件夾下面的代碼,USB_CORE 文件夾下的代
碼一般不用修改。現在,我們先來簡單介紹一下 USB_CORE 文件夾下的幾個.c 文件。

usb_regs.c 文件,該文件主要負責 USB 控制寄存器的底層操作,裏面有各種 USB 寄存器
的底層操作函數。
usb_init.c 文件,該文件裏面只有一個函數:USB_Init,用於 USB 控制器的初始化,不過對
USB 控制器的初始化,是 USB_Init 調用用其他文件的函數實現的,USB_Init 只不過是把他們
連接一下罷了,這樣使得代碼比較規範。
usb_int.c 文件,該文件裏面只有兩個函數 CTR_LP 和 CTR_HP,CTR_LP 負責 USB 低優先
級中斷的處理。而 CTR_HP 負責 USB 高優先級中斷的處理。
usb_mem.c 文件,該文件用於處理 PMA 數據,PMA 全稱爲 Packet memory area,是 STM32
內部用於 USB/CAN 的專用數據緩衝區,該文件內也只有 2 個函數即: PMAToUserBufferCopy
和 UserToPMABufferCopy,分別用於將 USB 端點的數據傳送給主機和主機的數據傳送到 USB
端點。
usb_croe.c 文件,該文件用於處理 USB2.0 協議。
usb_sil.c 文件,該文件爲 USB 端點提供簡化的讀寫訪問函數。
以上幾個文件具有很強的獨立性,除特殊情況,不需要用戶修改,直接調用內部的函數即
可。接着我們介紹 CONFIG 文件夾裏面的幾個.c 文件。
hw_config.c 文件,該文件用於硬件的配置,比如初始化 USB 時鐘、USB 中斷、低功耗模
式處理等。
usb_desc.c 文件,該文件用於 Virtual Com 描述符的處理。
usb_endp.c 文件,該文件用於非控制傳輸,處理正確傳輸中斷回調函數。
usb_pwr.c 文件,該文件用於 USB 控制器的電源管理;
usb_istr.c 文件,該文件用於處理 USB 中斷。
usb_prop.c 文件,該文件用於處理所有 Virtual Com 的相關事件,包括 Virtual Com 的初始
化、復位等等操作。
另外官方例程用到 stm32_it.c 來處理 USB 相關中斷,包括兩個中斷服務函數,第一個是:
USB_LP_CAN1_RX0_IRQHandler 函數,我們在該函數裏面調用 USB_Istr 函數,用於處理 USB
發生的各種中斷。另外一個就是 USBWakeUp_IRQHandler 函數,我們在該函數就做了一件事:
清除中斷標誌。爲了方便,我們直接將 USB 中斷相關代碼,全部放到 hw_config.c 裏面,所以,
本例程直接用不到 stm32_it.c。
USB 相關代碼,就給大家介紹到這裏,詳細的介紹,請大家參考:CD00158241.pdf 這個文
檔。
注意,以上代碼,有些是經過修改了的,並非完全照搬官方例程。接着我們在工程文件裏
面新建 USB_CORE 和 USB_CONFIG 分組,分別加入 USB\ STM32_USB-FS-Device_Driver\src
下面的代碼和 USB\CONFIG 下面的代碼,然後把 USB\STM32_USB-FS-Device_Driver\inc 和
USB\CONFIG 文件夾加入頭文件包含路徑。
最後修改 main.c 裏面代碼如下:
int main(void)
{
u16 t; u16 len;
u16 times=0; u8 usbstatus=0;
delay_init(); //延時函數初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //設置 NVIC 中斷分組 2
uart_init(115200); //串口初始化爲 115200
LED_Init(); //初始化與 LED 連接的硬件接口

LCD_Init(); //初始化 LCD
POINT_COLOR=RED; //設置字體爲紅色
LCD_ShowString(30,50,200,16,16,"WarShip STM32");
LCD_ShowString(30,70,200,16,16,"USB Virtual USART TEST");
LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK");
LCD_ShowString(30,110,200,16,16,"2015/1/28");
LCD_ShowString(30,130,200,16,16,"USB Connecting...");//提示 USB 開始連接
delay_ms(1800);
USB_Port_Set(0); //USB 先斷開
delay_ms(700);
USB_Port_Set(1); //USB 再次連接
Set_USBClock();
USB_Interrupts_Config();
USB_Init();
while(1)
{
if(usbstatus!=bDeviceState)//USB 連接狀態發生了改變.
{
usbstatus=bDeviceState;//記錄新的狀態
if(usbstatus==CONFIGURED)
{
POINT_COLOR=BLUE;
LCD_ShowString(30,130,200,16,16,"USB Connected "); //連接成功
LED1=0;//DS1 亮
}else
{
POINT_COLOR=RED;
LCD_ShowString(30,130,200,16,16,"USB disConnected "); //提示斷開
LED1=1;//DS1 滅
}
}
if(USB_USART_RX_STA&0x8000)
{
len=USB_USART_RX_STA&0x3FFF;//得到此次接收到的數據長度
usb_printf("\r\n 您發送的消息爲:%d\r\n\r\n",len);
for(t=0;t<len;t++)
{
USB_USART_SendData(USB_USART_RX_BUF[t]);//按字節發送給 USB
}
usb_printf("\r\n\r\n");//插入換行
USB_USART_RX_STA=0;
}else
{

times++;
if(times%5000==0)
{
usb_printf("\r\n 戰艦 STM32 開發板 USB 虛擬串口實驗\r\n");
usb_printf("正點原子@ALIENTEK\r\n\r\n");
}
if(times%200==0)usb_printf("請輸入數據,以回車鍵結束\r\n");
if(times%30==0)LED0=!LED0;//閃爍 LED,提示系統正在運行.
delay_ms(10);
}
}
}
在此部分代碼用於實現我們在硬件設計部分提到的功能,USB 的配置通過三個函數完成:
USB_Interrupts_Config()、Set_USBClock()和 USB_Init(),第一個函數用於設置 USB 喚醒中斷和
USB 低優先級數據處理中斷,Set_USBClock 函數用於 配置 USB 時鐘,也就是從 72M 的主頻
得到 48M 的 USB 時鐘(1.5 分頻)。最後 USB_Init()函數用於初始化 USB,最主要的就是調用
了 Virtual_Com_Port_init 函數,開啓了 USB 部分的電源等。這裏需要特別說明的是,USB 配置
並沒有對 PA11 和 PA12 這兩個 IO 口進行設置,是因爲,一旦開啓了 USB 電源(USB_CNTR
的 PDWN 位清零)PA11 和 PA12 將不再作爲其他功能使用,僅供 USB 使用,所以在開啓了 USB
電源之後不論你怎麼配置這兩個 IO 口,都是無效的。要在此獲取這兩個 IO 口的配置權,則需
要關閉 USB 電源,也就是置位 USB_CNTR 的 PDWN 位,我們通過 USB_Port_Set 函數來禁止/
允許 USB 連接,在復位的時候,先禁止,再允許,這樣每次我們按復位電腦都可以識別到 USB
鼠標,而不需要我們每次都拔 USB 線。USB_Port_Set 函數在 hw_config.c 裏面實現,代碼請參
考本例程源碼。
USB 虛擬串口的數據發送,我們通過函數:USB_USART_SendData 來實現,該函數在
hw_config.c 裏面實現,該函數代碼如下:
//發送一個字節數據到 USB 虛擬串口
void USB_USART_SendData(u8 data)
{
uu_txfifo.buffer[uu_txfifo.writeptr]=data;
uu_txfifo.writeptr++;
if(uu_txfifo.writeptr==USB_USART_TXFIFO_SIZE)//超過 buf 大小了,歸零.
{
uu_txfifo.writeptr=0;
}
}
該函數實現發送 1 個字節到虛擬串口,這裏,我們用到了一個 uu_txfifo 的結構體,該結構
體是我們在 hw_config 裏面定義的一個 USB 虛擬串口發送數據 FIFO 結構體,定義如下:
//定義一個 USB USART FIFO 結構體
typedef struct
{
u8 buffer[USB_USART_TXFIFO_SIZE]; //buffer
vu16 writeptr; //寫指針

vu16 readptr; //讀指針
}_usb_usart_fifo;
extern _usb_usart_fifo uu_txfifo; //USB 串口發送 FIFO
該結構體用於處理 USB 串口要發送的數據,所有要通過 USB 串口發送的數據,都將先存
放在該結構體的 buffer 數組(FIFO 緩存區)裏面,USB_USART_TXFIFO_SIZE 定義了該數組
的大小,通過 writeptr 和 readptr 來控制 FIFO 的寫入和讀出,該結構體 buffer 數據的寫入,是
通過 USB_USART_SendData 函數實現,而 buffer 數據的讀出(然後發送到 USB)則是通過端
點 1 回調函數:EP1_IN_Callback 函數實現,該函數在 usb_endp.c 裏面實現,代碼如下:
void EP1_IN_Callback (void)
{
u16 USB_Tx_ptr;
u16 USB_Tx_length;
if(uu_txfifo.readptr==uu_txfifo.writeptr) return; //無任何數據要發送,直接退出
if(uu_txfifo.readptr<uu_txfifo.writeptr) //沒有超過數組,讀指針<寫指針
{
USB_Tx_length=uu_txfifo.writeptr-uu_txfifo.readptr; //得到要發送的數據長度
}else //超過數組了 讀指針>寫指針
{
USB_Tx_length=USB_USART_TXFIFO_SIZE-uu_txfifo.readptr;//發送的數據長度
}
if(USB_Tx_length>VIRTUAL_COM_PORT_DATA_SIZE) //超過 64 字節?
{
USB_Tx_length=VIRTUAL_COM_PORT_DATA_SIZE; //此次發送數據量
}
USB_Tx_ptr=uu_txfifo.readptr; //發送起始地址
uu_txfifo.readptr+=USB_Tx_length; //讀指針偏移
if(uu_txfifo.readptr>=USB_USART_TXFIFO_SIZE) //讀指針歸零
{
uu_txfifo.readptr=0;
}
UserToPMABufferCopy(&uu_txfifo.buffer[USB_Tx_ptr], ENDP1_TXADDR, USB_Tx_
length);
SetEPTxCount(ENDP1, USB_Tx_length);
SetEPTxValid(ENDP1);
}
這個函數由 USB 中斷處理相關函數調用,將要通過 USB 發送給電腦的數據拷貝到端點 1
的發送區,然後通過 USB 發送給電腦,從而實現串口數據的發送。因爲 USB 每次傳輸數據長
度不超過 VIRTUAL_COM_PORT_DATA_SIZE,所以 USB 發送數據長度:USB_Tx_length 的最
大值只能是 VIRTUAL_COM_PORT_DATA_SIZE。
以上,就是 USB 虛擬串口的數據發送過程,而 USB 虛擬串口數據的接收,則是通過端點
3 來實現的,端點 3 的回調函數爲 EP3_OUT_Callback,該函數也是在 usb_endp.c 裏面定義,代
碼如下:
void EP3_OUT_Callback(void)

{
u16 USB_Rx_Cnt;
USB_Rx_Cnt = USB_SIL_Read(EP3_OUT, USB_Rx_Buffer);
//得到 USB 接收到的數據及其長度
USB_To_USART_Send_Data(USB_Rx_Buffer, USB_Rx_Cnt);
//處理數據(其實就是保存數據)
SetEPRxValid(ENDP3); //時能端點 3 的數據接收
}
該函數也是被 USB 中斷處理調用,該函數通過調用 USB_To_USART_Send_Data 函數,實
現 USB 接收數據的保存,USB_To_USART_Send_Data 函數在 hw_config.c 裏面實現,代碼如下:
//用類似串口 1 接收數據的方法,來處理 USB 虛擬串口接收到的數據.
u8 USB_USART_RX_BUF[USB_USART_REC_LEN];
//接收緩衝,最大 USART_REC_LEN 個字節.
//接收狀態
//bit15, 接收完成標誌
//bit14, 接收到 0x0d
//bit13~0, 接收到的有效字節數目
u16 USB_USART_RX_STA=0; //接收狀態標記
//處理從 USB 虛擬串口接收到的數據
//databuffer:數據緩存區
//Nb_bytes:接收到的字節數.
void USB_To_USART_Send_Data(u8* data_buffer, u8 Nb_bytes)
{
u8 i;
u8 res;
for(i=0;i<Nb_bytes;i++)
{
res=data_buffer[i];
if((USB_USART_RX_STA&0x8000)==0) //接收未完成
{
if(USB_USART_RX_STA&0x4000) //接收到了 0x0d
{
if(res!=0x0a)USB_USART_RX_STA=0;//接收錯誤,重新開始
else USB_USART_RX_STA|=0x8000; //接收完成了
}else //還沒收到 0X0D
{
if(res==0x0d)USB_USART_RX_STA|=0x4000;
else
{
USB_USART_RX_BUF[USB_USART_RX_STA&0X3FFF]=res;
USB_USART_RX_STA++;
if(USB_USART_RX_STA>(USB_USART_REC_LEN-1))
USB_USART_RX_STA=0;//接收數據錯誤,重新開始接收

}
}
}
}
}
該函數接收數據的方法,同第九章串口通信實驗的串口中斷接收數據方法完全一樣,在這
裏就不詳細介紹了,請參考第九章相關內容即可。USB_To_USART_Send_Data 函數類似於串口
通信實驗的串口中斷服務函數(USART1_IRQHandler),完成 USB 虛擬串口的數據接收。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章