STC8H開發(九): STC8H8K64U模擬USB HID外設


目錄

關於USB

通用串行總線(Universal Serial Bus, 簡稱USB), 是當前使用最廣泛的外設接口. 因為供電簡單, 支持熱插拔, 擴展端口簡單, 傳輸方式多樣化, 兼容性好, 支持的外設類型豐富, 基本成為PC標配.
STC MCU從STC15開始支持USB ISP, 但是僅僅用於下載和調試, 可在代碼中配置的USB外設功能直到STC8H8K64U才出現. USB標准規范包括USB1.0, USB1.1, USB2.0, USB3.0 以及2019年9月發布的 USB4TM, STC8H8K64U 支持的是 USB2.0 標准.

USB的物理連接結構

USB系統由一個USB Host, 一個或多個USB Hub, 一個或多個USB Node 組成. USB的物理連接是層次性的星型結構, 在系統中, 有且僅有一個USB主機, USB Hub實際上就是一個具有特殊功能的USB設備. 所有的USB設備都連接在USB Hub上, 同時Hub有責任為每個連接其上的USB Node 提供 +5V 和 500mA 的電源.

USB總線采用的是樹形結構、主從工作模式. USB主機根據各個設備的屬性, 周期性訪問各個設備. 而USB設備則是被動響應USB主機的訪問請求, 這樣避免了USB設備主動發送數據導致的總線沖突. 當然, 這樣就導致沒有USB主機的情況下 USB 設備之間無法通訊. USB OTG(USB On-The-Go)技術用於解決此問題, 拓展了USB在嵌入式設備, 如手機、PDA等方面的應用.

USB系統的分層結構

在系統設計和邏輯連接關系上USB總線系統具有明確的分層結構. USB設備與主機的驅動/應用程序通信, 是通過特定的USB端點來實現的.

整個USB系統可以分為USB總線接口層, USB設備層, 功能層

  1. USB總線接口層
    USB總線接口層主要用於實現USB主機和USB設備之間的數據傳輸, 實際的數據流是在此層運行的. USB規范中 , USB總線接口NRZI編碼(反向歸零編碼)來傳輸數據. 編碼的過程是自動進行的, 由USB系統硬件完成.
  2. USB設備層
    USB設備層主要用於管理USB設備, 包括分配USB地址、讀取設備描述報文等. 在這一層中, USB主機可以獲取USB設備的各種屬性或能力. 這部分的功能, USB主機通過驅動軟件來完成, 而USB設備的固件層也需要編寫對應的代碼進行支持.
  3. 功能層
    功能層主要負責數據傳輸, 由USB設備的功能單元和對應的USB主機程序實現. 按照雙方通信的類型, 可分為
    • 控制傳輸(Control Transfers). 主要用於傳輸少了的對時間和速率沒有要求的數據, 一般用於USB主機讀取或設置USB設備的配置信息, 或者其他的簡單操作.
    • 中斷傳輸(Interrupt Transfers). 主要用於傳輸少了對傳輸時間具有周期性要求的數據, 在鼠標、鍵盤等HID設備中經常使用.
    • 批量傳輸(Bulk Transfers). 用於傳輸大量的、對傳輸時間和速率沒有嚴格要求的數據.
    • 實時傳輸(Isochronous Transfers). 用於傳輸大量的、且傳輸時間具有周期性、速率恆定的數據

在USB HID設備的傳輸中主要使用的是控制傳輸.

USB的命令(USB Device Request)

USB規范定義了設備請求(USB Device Request), 以更好地完成USB主機對總線上所有USB設備的統一控制. 此設備請求由USB主機發往USB設備. USB命令包括USB標准命令, 類命令, 廠商命令. 這些命令的格式都是相同的, 如下表所示.

偏移 字段 長度 數值 描述
0 bmRequesType 1 位圖 D7: 數據的傳輸方向: 0=主機->設備, 1=設備->主機
D6-D5: 命令的類型, 0=標准命令 1=類命令 2=廠商命令 3=保留
D4-D0: 接收對象 0=設備 1=接口 2=端點 3=其他  4…31=保留
1 bRequest 1 命令的序號
2 wValue 2 根據不同的命令, 含義也不同
4 wIndex 2 索引或偏移 根據不同的命令, 含義不同, 主要用於傳送索引或者偏移
6 wLength 2 如果有數據階段, 此字段為數據的字節數

標准命令是每種USB設備都要支持的, 類命令則與USB設備所述類有關, 比如USB HID設備有HID類特有的命令, 如Get_Report、SetReport等.

USB標准命令

USB 1.1的規范中, 規定了11種USB標准命令, 用來完成各種目的. 根據不同的命令, 相應的字段含義也有所不同. 下表列出了11個USB標准命令的功能.

命令 請求號 功能描述
Get_Status 0x00 讀取USB設備、接口或者端口的狀態
Clear_Feature 0x01 清除或禁止USB設備、接口或端點的某些特征
Set_Feature 0x03 設置或使能USB設備、接口或端點的某些特征
Set_Address 0x05 分配USB設備地址
Get_Descriptor 0x06 讀取描述報文
Set_Descriptor 0x07 更新已有的描述報文或添加新的描述報文
Get_Configuration 0x08 讀取USB設備的當前配置值
Set_Configuration 0x09 為USB設備選擇一個合適的配置值
Get_Interface 0x0A 獲得設備接口當前工作的選擇設置值
Set_Interface 0x0B 激活USB設備的某個接口
Synch_Frame 0x0C 設置並報告端點的同步幀號

在USB規范中, 對於這些標准的USB命令, 所有的USB設備必須都支持, 並能夠對命令進行響應. 如果不需要對命令進行操作, 也必須准備一個空的響應. 除了這些標准的USB命令, 對於不同的類, 也有類相關的USB命令. 比如人機接口類設備有Set_Report、Get_Report等命令, 集線器類設備有GetHubStatus、GetBusState等命令. 在開發相應類設備的時候, 也需要熟悉這些類本身特有的USB命令.

下表是Get_Descriptor命令的結構

偏移 字段 內容
0 bmRequesType 值為10000000B, 設備到主機
1 bRequest GET_DESCRIPTOR, 0x06
2 wValue 描述報文的類型和描述報文的索引值
4 wIndex 0或語言標識(LANGID)
6 wLength 描述報文的長度
  • Get_Descriptor命令用來獲取USB設備的各種描述報文, 包括設備描述報文、配置描述報文、接口描述報文、端點描述報文和字符串描述報文.
  • 需要獲取的描述報文類型, 由wValue字段給出. wValue由兩個字節組成, 高字節表示描述報文的類型, 低字節表示描述報文的索引值.
  • wIndex字段除去獲取字符串描述報文之外, 其他情況下設置為0. 獲得字符串描述報文的過程分為兩步: 第一次發送命令后獲得語言標識;第二次發送命令時, 將語言標識賦給wIndex字段, 需要獲取的字符串描述報文的索引值賦給wValue字段, 即可獲得所需要的字符串.
  • Get_Descriptor命令中的字段wLength, 表示描述報文的字節長度, 由USB主機指定. 當指定wLength比實際的描述報文長度小時, USB設備嚴格按照主機指定的字節長度返回描述報文信息;當wLength比實際的描述報文長度大時, USB設備值返回描述報文長度的信息. 比如在訪問配置描述報文時, USB主機不清楚配置信息的總長, 可以將Get_Descriptor命令中的wLength設置為4, 得到配置描述報文中wTotalLength. 然后, 重新發送Get_Descriptor命令, 此時將wLength設置為wTotalLength的值, 從而獲得整個配置描述報文信息.

USB HID的類命令

HID設備除了支持標准的USB命令外, 還支持6個HID特定的類命令, 如下表所示.

命令 請求號 功能描述
Get_Report 0x01 USB主機接收HID設備發來的報告
Get_Idle 0x02 用於讀取HID設備當前空閑速率
Get_Protocol 0x03 用於讀取HID設備的協議值
Set_Report 0x09 USB主機向HID設備發送報告
Set_Idle 0x0A 用於設置HID設備的空閑速率
Set_Protocol 0x0B 用於設置HID設備的協議值

HID的類命令, 其數據結構與USB的標准命令類似, 而且也是采用控制傳輸發送的. HID示例中主要用到了 Get_Report 和 Set_Report 兩個命令

Get_Report命令

用於獲取HID設備發送來的報告, 它主要在HID設備初始化和讀取HID報告時使用. 此命令是所有HID設備都必須支持的, 其結構如下所示.

偏移 字段 內容
0 bmRequesType 值為10100001B, 設備到主機
1 bRequest GET_REPORT, 0x01
2 wValue 報告類型及報告ID
4 wIndex 用於指明支持此命令的接口號碼
6 wLength 報告長度

其中, wValue用來指明報告的類型. 它由兩個字節組成, 低字節表示報告ID. 高字節值為1時, 表示Input報告;值為2時, 表示Output報告;值為3時, 表示Feature報告.

Set_Report命令

用於USB主機向HID設備發送報告數據, 它與Get_Report命令類似, 只是數據傳輸的方向不同. Set_Report命令並不是所有HID設備都必須支持的, 其結構如下所示.

偏移 字段 內容
0 bmRequesType 值為00100001B, 主機到設備
1 bRequest SET_REPORT, 0x09
2 wValue 報告類型及報告ID
4 wIndex 用於指明支持此命令的接口號碼
6 wLength 報告長度

USB 的描述報文

USB 設備響應命令的內容就是描述報文, 為了方便USB主機對USB設備進行管理, USB-IF對USB設備的功能采用了分層結構, 包括設備層、配置層、接口層和端點層. 這四層的作用分別為

  1. 設備層. 說明USB設備的主要類型特征(如設備類別、接口、端點等屬性), 保障設備枚舉過程的正常進行
  2. 配置層. 選擇不同的失敗配置滿足USB主機對設備功能的選擇, 可選擇復式的設備接口功能, 如圖1展示的選擇鼠標、鍵盤和游戲桿的復合功能
  3. 接口層. 將具體功能分類, 不同的功能對用不同的操作方式
  4. 端點層. 針對特定的設備功能, 選擇不同的端點, 提供不同的數據管道, 與USB主機進行數據通訊

為了描述USB設備的這些特征, USB規范定義了相應結構的描述報文, 包括設備描述報文、配置描述報文、接口描述報文、端點描述報文和字符串描述報文等. 下表給出了USB1.1下各種USB描述報文的類型值.

類型 描述報文 描述報文值
標准描述報文 設備描述報文(Device Descriptor) 0x01
配置描述報文(Configuration Descriptor) 0x02
字符串描述報文(String Descriptor) 0x03
接口描述報文(Interface Descriptor) 0x04
端點描述報文(Endpoint Descriptor) 0x05
類描述報文 集線器類描述報文(hub descriptor) 0x29
人機接口類描述報文(HID) 0x21
廠商自定義 0xFF

其他版本的USB規范, 還定義了其他類型的描述報文, 比如USB 2.0中的設備限定描述報文(Device_Qualifier), USB 3.2中的二進制設備對象存儲描述報文(Binary Device Object Store, 簡稱BOS)等. 這些內容在博客中不會涉及, 可在USB-IF的官網下載相關的USB規范文檔了解.

USB主機會發送USB命令給USB設備, GET_DESCRIPTOR 是常用的獲取描述報文命令. USB設備根據命令所要求的, 給出對應的描述報文. 本篇主要講述描述報文的結構.

不管哪種USB設備, 都必須提供標准描述報文, 用於告知主機設備本身的屬性. 以下介紹的內容, 可以使用Bus Hound或USB邏輯分析儀等工具, 去抓取USB設備的識別和通信包.

設備描述報文

USB的設備描述報文用於表示USB設備的一般信息, 如制造商ID、產品序列號等. 一個USB設備有且只有一個設備描述報文, 它是USB主機所讀取的第一個描述報文, 其結構如下所示.

偏移 大小 描述
0 bLength 1 數字 描述報文字節數長度(0x12)
1 bDescriptor 1 常量 描述報文的類型(0x01)
2 bcdUSB 2 BCD碼 USB設備支持的協議版本號
4 bDeviceClass 1 設備類代碼
5 bDeviceSubClass 1 子類 子類代碼, 根據bDeviceClass來定
6 bDevicePortocol 1 協議 協議碼
7 bMaxPacketSize0 1 數字 端點0的最大包長度
8 idVendor 2 ID 廠商ID(由USB-IF賦值)
10 idProduct 2 ID 產品ID(由廠商賦值)
12 bcdDevice 2 BCD碼 設備發行號(BCD碼)
14 iManufacturer 1 索引 廠商信息的字符串描述報文索引值
15 iProduct 1 索引 產品信息的字符串描述報文索引值
16 iSerialNumber 1 索引 設備序列號信息的字符串描述報文索引值
17 bNumConfigurations 1 數字 配置描述報文數目

設備描述報文結構中的

  • bMaxPacketSize0, 它用來告知USB主機設備所支持的最大數據長度.
  • bDeviceClass表示設備所述的類別, 如果此值為0, 則表示每一個配置中的每個接口來指明它所屬的類別(即在接口描述報文中給出設備類), 並且各接口獨立工作. 如果此值為0xFF, 則由供應商自定義該設備類. 介於兩者之間的值0x1-0xFE, 表示USB規范中定義的某個設備類, 比如0x03表示HID設備類. 它和bDeviceSubClass、bDeviceProtocol共同規定了設備的類別和采用的協議, 更具體的分類定義, 可以參考USB-IF官網.
  • iManufacturer、iProduct和iSerialNumber, 使用字符串描述報文的索引值來進行描述, 索引值為0表示沒有字符串描述報文對其進行描述. 通過此索引值和USB命令Get_Descriptor, 可以得到對應的字符串描述報文.

配置描述報文

USB規范中, USB設備可以有一個或者多個配置描述報文, 每個配置描述報文提供了設備特定的配置. 在設備描述報文中的bNumConfigurations提供了配置描述報文的個數, 任何時刻只有一種配置處於工作狀態.

配置描述報文中提供了在該配置下設備的接口數目, 一個設備的不同配置描述報文可能包含不同數目和特性的設備接口. 圖1中的設備配置1, 就包含了鼠標功能接口和鍵盤功能接口兩種接口. 此外, 配置描述報文中還會描述設備的供電方式(自供電/總線供電)、最大耗電量等信息. 其結構如下表所示.

偏移 大小 描述
0 bLength 1 數字 描述報文的字節數長度(0x09)
1 bDescriptorType 1 常量 配置描述報文的類型(0x02)
2 wTotalLength 2 BCD碼 配置信息的總長(包括配置、接口、端點和設備類及廠商定義的描述報文)
4 BnumInterfaces 1 該設備所支持的接口數目
5 bConfigurationValue 1 子類 配置值
6 iConfiguration 1 協議 描述該配置的字符串描述報文索引值
7 bmAttributes 1 數字 配置特性: D7:保留 D6:自供電 D5:遠程喚醒 D4…0:保留
8 MaxPower 1 數字 該配置下所需最大總線電流(2mA為單位)

接口描述報文

USB設備的接口是端點的集合, 負責完成該設備的特定功能, 比如數據的輸入和輸出. 接口描述報文用來表示在USB設備中, 各個接口的特性, 包括接口的端點個數、所述的設備類和子類等.

擁有多個接口的USB設備, 如果設備描述報文中描述的bDeviceClass不為0, 則表示接口之間是互斥關系, 否則接口相互獨立, 每個接口有自己的類號、子類號和協議號. 類號、子類號和協議號的定義, 與設備描述報文中的定義是一致的. 接口描述報文的結構如下表所示.

偏移 大小 描述
0 bLength 1 數字 描述報文的字節數長度(0x09)
1 bDescriptorType 1 常量 接口描述報文的類型(0x04)
2 bInterfaceNumber 1 數字 接口號(從0開始)
3 bAlternateSetting 1 數字 可選設置的索引值
4 bNumEndpoint 1 數字 此接口的端點數量(不計默認端點0)
5 bInterfaceClass 1 接口所屬的類值
6 bInterfaceSubClass 1 子類 接口所屬子類的值
7 bInterfaceProtocol 1 協議 協議碼, 根據上面的兩個值而定
8 iInterface 1 索引 表示此接口的字符串描述報文的索引值

端點描述報文

USB規范中, 端點描述報文用於指出USB設備端點的特性, 包括其所支持的傳輸類型、傳輸方向等. 端點0沒有端點描述報文, 其他端點必須包含端點描述報文.

端點是設備與主機之間進行數據傳輸的邏輯接口, 除配置使用的端點0為雙向外, 其他均為單向. 端點描述報文描述了數據的傳輸類型、傳輸方向、數據包大小, 以及端點地址, 其結構如下表所示.

偏移 大小 描述
0 bLength 1 數字 描述報文的字節數長度(0x07)
1 bDescriptorType 1 常量 端點描述報文的類型(0x05)
2 bEndPointAddress 1 端點 描述了端點的地址、方向 Bit3…0: 端點號 Bit6…4: 保留, 為0 Bit7: 傳輸方向, 如果是控制端點則忽略 0: 輸出端點(主機到設備) 1: 輸入端點(設備到主機)
3 bmAttributes 1 位圖 端點傳輸類型 Bit1…0: 傳送類型 00B=控制傳送 01B=實時傳送 10B=批量傳送 11B=中斷傳送
4 wMaxPacketSize 2 數字 接收/發送的最大數據包長度
6 bInterval 1 數字 周期數據傳輸端點的時間間隙

字符串描述報文

字符串描述報文是可選的, 它描述了制造商、設備名稱或序列號等信息. 它使用的是Unicode編碼, 並支持多語言. USB主機要求獲得字符串描述報文時, 需要用一個16位的語言標識出語言類別. 比如, 常用的語言標識1033表示美國英語, 而2052表示中文. 其他的語言標識, 可以在微軟的網站上找到 .

USB主機請求得到某個字符串描述報文時分為兩步, 首先向USB設備發送USB命令 Get_Descriptor, 命令的wIndex字段設置為0, 設備將返回描述語言標識的字符串描述報文, 然后, USB主機根據需要的語言, 向USB設備發送命令 Get_Descriptor, 在命令對應的字段中設置字符串的索引值和語言標識, 得到需要的字符串描述報文. 字符串描述報文有兩種格式

第一種用來指明所用的語言標識, 如下表所示.

偏移 大小 描述
0 bLength 1 N+2 描述報文的字節數長度(N+2字節)
1 bDescriptorType 1 常量 字符串描述報文的類型(0x03)
2 wLANGID[0] 2 數字 語言標識(LANGID), 碼0
N wLANGID[x] 2 數字 語言標識(LANGID), 碼x

第二種為Unicode字符串描述報文, 包含了非NULL結尾的Unicode字符串, 如下表所示.

偏移 大小 描述
0 bLength 1 N+2 描述報文的字節數長度(N+2字節)
1 bDescriptorType 1 常量 字符串描述報文的類型(0x03)
2 bString N 數字 Unicode編碼的字符串

在實際編寫代碼中, 接口描述報文、端點描述報文一般是和配置描述報文在同一數組中的


HID設備的描述報文

示例的USB設備是HID類設備. HID(Human Interface Devices)為人機接口設備, 日常使用的鍵盤、鼠標等, 都屬於HID設備.

USB規范中, HID類設備的規范為Device Definiton for Human Interface Devices, 每個HID設備都必須符合該規范中對描述報文、傳輸類型等的定義. 另外, USB-IF還提供了HID的用例規范, 名為HID Usage Tables, 定義了HID設備和USB主機之間通信的HID數據. 所有的 HID 傳輸都是使用默認控制管道或是一個中斷管道, HID 設備必須有一個中斷輸入端點來傳送數據到主機, 中斷輸出端點則不是必需的.

HID設備的標准描述報文同樣遵循上一篇所介紹的內容, 也即擁有各種標准描述報文. 對於HID設備, 其設備描述報文的類代碼、子類代碼和協議代碼都需要設置為0;接口描述報文的類代碼設為0x03, 子類代碼為0或者0x01, 協議碼為0、0x01或0x02.

HID設備支持3種類描述報文: HID描述報文、報告描述報文和物理描述報文. 一個USB設備只能包含一個HID描述報文, 可以支持多個報告描述報文, 而物理描述報文是可選的. 在示例中, 只用到了HID描述報文和報告描述報文.

HID描述報文

HID描述報文主要用來識別HID設備通信時所使用的額外信息(如HID版本號、報告描述報文長度等), 如下表所示, 給出了HID描述報文的定義結構.

偏移 大小 描述
0 bLength 1 數字 描述報文的長度
1 bDescriptorType 1 常量 描述報文的類型, 0x21
2 bcdHID 2 BCD碼 HID規范版本號(BCD碼)
4 bCountryCode 1 數字 國家代碼
5 bNumDescriptor 1 數字 支持的其他類描述報文的數量
6 bDescriptorType 1 常量 類別描述報文的類型
7 wDescriptorLength 2 數字 報表描述報文的總長度
9 bDescriptorType 1 常量 識別描述報文類型的常數, 多個描述報文時使用
10 wDescriptorLength 2 數字 描述報文總長度, 多個描述報文時使用

其中

  • bCountryCode 表示的是國家代碼, 一般將其設置為0就可以了. 0表示的是HID設備不是本土化的, 其他的值可以查看HID規范.
  • bDescriptorType 識別HID描述報文附屬的描述報文的類型, 報表描述報文的類型為0x22, 物理描述報文的類型為0x23. 每一個 HID設備都必須至少支持一個報表描述報文. 一個接口可以支持多個報表描述報文, 以及一個或多個實體描述報文.
  • HID描述報文的偏移量為9和10的bDescriptorType和wDescriptorLength可以重復存在多個

在USB主機發送標准命令 Get_ Configuration, 獲取配置描述報文的時候, 將按照配置描述報文、接口描述報文、HID描述報文、端點描述報文的順序, 返回數據. 也即主機此時可用獲取到HID描述報文, 可以據其信息再去獲取相關的描述報文(如報告描述報文).

報告描述報文

HID設備的報告描述報文是一種數據報表, 主要用來定義HID設備和USB主機之間的數據交換格式. 與標准描述報文不同, 它沒有固定的長度, 根據設備不同的用途, 需要准備不同的結構和數據描述.

報告描述報文由多個不同的項目(item)組成, 它有兩種編碼: 短項目(short item)和長項目(long item). 長項目保留給未來使用, 本節不對它進行介紹. HID短項目的字節格式如下所示.

23       16|15      8|7    4|3     2|1     0|
| -data  - | -data --| bTag | bType | bSize |
  • bSize用來指出項目所需要的數據字節(即data部分的字節長度), 其值可為0(bSize=0)、1(bSize=1)、2(bSize=2)和4(bSize=3). 注意沒有3個字節長的數據, 大部分項目只需要1個字節數據.

  • bType表示項目的類型, 用來表示是主(Main)項目、全局(Global)還是局部(Local)項目. 主項目用來定義報告中數據的種類和格式;局部項目表示所定義只能適用於其下的第一個主項目, 不能擴展到其他主項目;全局項目則適用於其下方所有主項目, 除非被另一個相同標簽的全局項目替代. bType=0時, 表示項目為主項目;bType=1時, 項目為全局項目;bType=2時, 項目為局部項目.

  • bTag為標簽, 用來標記各項目的作用.

  • data部分是可選的, 其長度由bSize決定. 比如集合結束(End Collection)項目, 其值一般為0xC0, 后面不帶有任何數據.

主項目中的Input、Output和Feature用來定義報告中的數據通訊字段, 每個項目標簽之后是用來描述特性的32位數, 其中前9位有不同的含義, 后23位被保留. 當然, HID項目中的data部分, 不一定是32位數, 其長度仍舊由bSize值決定. 這三個項目的特性描述, 如下表所示.

偏移 描述
0 0=數據, 表示項目內容可改;1=常數, 項目內容不可改
1 0=數組, 表示項目描述每個控制的狀態;1=變量, 項目只報告作用中的控制
2 0=絕對, 表示數值以固定值為基准;1=相對, 當前數值以上一數值為基准
3 0=沒有折行, 數據不作折返處理;1=有折行, 遇到最大最小界限時折返
4 0=線性, 測量與報告數據為線性關系;1=非線性, 測量與報告數據非線性關系
5 0=優選狀態, 無交互時, 回到特定狀態;1=非優選狀態
6 0=沒有空位置;1=空狀態
7 0=非揮發;1=揮發, 設備可以自己改變數值. 此位對Input無效
8 0=位字段, 表示每一位或每一字節內的群組位可代表一份數據;1=緩沖字節, 表示信息包含一個或者多個字節, 緩沖字節的報告大小必須為8
9-23 保留

表2中的Usage Page和Usage是最復雜的項目, 為此USB-IF專門為它們准備了參考文檔. Usage Page是全局項目, 用來定義數據的用法或功能;Usage是局部項目, 用來描述項目或集合的用途.

Usage Page一般和Usage, 或者和Usage Minimum、Usage Maximum共同決定設備的用途. 如示例1所示, 這是一個常見的Usage Page和Usage的定義項目.

USB示例

示例說明

在demo中提供了兩個HID示例, 一個是響應輸入返回ADC采樣結果, 另一個是模擬鍵盤輸出按鍵狀態.

代碼的工作流程都是相似的, 初始化USB, 初始化通道(Endpoint), 在USB的中斷響應中, 判斷類型和通道分別處理. 在初始化階段根據不同的命令返回對應的描述報文, 在輸入輸出階段響應對應的數據.

USB配置步驟

准備以下的描述報文

__CODE uint8_t  DEVICEDESC[18]; // 設備描述
__CODE uint8_t  CONFIGDESC[41]; // 配置描述
__CODE uint8_t  HIDREPORTDESC[27]; // HID報告
__CODE uint8_t  LANGIDDESC[4];     // 語言描述
__CODE uint8_t  MANUFACTDESC[8];   // 制造商描述
__CODE uint8_t  PRODUCTDESC[30];   // 產品描述

設置GIPIO

GPIO_P3_SetMode(GPIO_Pin_0|GPIO_Pin_1, GPIO_Mode_Input_HIP);

初始化USB

void USB_Init()
{
    SYS_EnableOscillator48M();               // USB 48MHz晶振
    USB_SetClockSource(USB_ClockSource_6M);  
    USB_SetEnabled(HAL_State_ON);            // 開啟USB
    USB_SetDpDmPullUp(HAL_State_ON);         // 設置D-/D+上拉
    EXTI_USB_SetIntPriority(EXTI_IntPriority_Highest); // 設置USB中斷優先級

    USB_WriteReg(FADDR, 0x00);
    USB_WriteReg(POWER, 0x08);
    USB_WriteReg(INTRIN1E, 0x3f);
    USB_WriteReg(INTROUT1E, 0x3f);
    USB_WriteReg(INTRUSBE, 0x00);
    USB_WriteReg(POWER, 0x01);
    Ep0Stage.bStage = USB_CtrlState_Idle;

    EXTI_USB_SetIntState(HAL_State_ON);     // 開啟全局中斷
}

USB中斷處理

INTERRUPT(USB_Routine, EXTI_VectUSB)
{
    uint8_t intrusb;
    uint8_t intrin;
    uint8_t introut;
    uint8_t csr;
    uint8_t cnt;
    uint16_t len;
    intrusb = USB_ReadReg(INTRUSB);
    intrin = USB_ReadReg(INTRIN1);
    introut = USB_ReadReg(INTROUT1);

    /**
     * 中斷類型:重置
     */
    if (intrusb & RSTIF)
    {
        USB_SelectEndPoint(1);
        USB_WriteReg(INCSR1, INCLRDT);
        USB_SelectEndPoint(1);
        USB_WriteReg(OUTCSR1, OUTCLRDT);
        Ep0Stage.bStage = USB_CtrlState_Idle;
    }

    /**
     * 中斷類型: Endpoint-0
     */
    if (intrin & EP0IF)
    {
        USB_SelectEndPoint(0);
        csr = USB_ReadReg(CSR0);
        if (csr & STSTL) // Sent Stall
        {
            USB_WriteReg(CSR0, csr & ~STSTL);
            Ep0Stage.bStage = USB_CtrlState_Idle;
        }

        if (csr & SUEND) // Setup End
        {
            USB_WriteReg(CSR0, csr | SSUEND);
        }

        // 這里根據不同的 stage, 分別處理
        switch (Ep0Stage.bStage)
        {
            case USB_CtrlState_Idle:
                //...略
                break;

            case USB_CtrlState_DataIn:
                //...略
                break;

            case USB_CtrlState_DataOut:
                //...略
                break;
        }
    }

    /**
     * 中斷類型: Endpoint-1 In
     */
    if (intrin & EP1INIF)
    {
        USB_SelectEndPoint(1);
        csr = USB_ReadReg(INCSR1);
        if (csr & INSTSTL)
        {
            USB_WriteReg(INCSR1, INCLRDT);
        }
        if (csr & INUNDRUN)
        {
            USB_WriteReg(INCSR1, 0);
        }
    }

    /**
     * 中斷類型: Endpoint-1 Out
     */
    if (introut & EP1OUTIF)
    {
        USB_SelectEndPoint(1);
        csr = USB_ReadReg(OUTCSR1);
        if (csr & OUTSTSTL)
        {
            USB_WriteReg(OUTCSR1, OUTCLRDT);
        }
        if (csr & OUTOPRDY)
        {
            USB_ReadFIFO(FIFO1, HidInput);
            USB_WriteReg(OUTCSR1, 0);
            /** Write response */
            if((HidInput[0] == 0xaa) && (HidInput[1] == 0x55) && (HidInput[2] == 0x01))
            {
                USB_SelectEndPoint(1);
                USB_WriteFIFO(FIFO1, HidOutput, 64);
                USB_WriteReg(INCSR1, INIPRDY);
            }
        }
    }
}

示例代碼

接線說明

與PC的連線

轉接板   STC8H8K64U
3.3V    -> VCC
D-      -> P3.0
D+      -> P3.1
GND     -> GND

轉接板僅將5V轉為3.3V, 可以參考前面的文章說明


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM