1.簡介

USB協議棧是基於東軟微電子ES32系列的USB控制器開發的USB範例,基於減輕開發者在了解USB控制器的使用及縮短應用範例的開發時程,協議棧以結構化的架構提供一系列的USB應用範例,經由將USB控制器的存取抽象化的架構下,開發者只需針對相關的應用層開發應用功能,可有效降低開發者對了解USB控制器存取的複雜度並縮減開發或移植的時程,在中斷,標準請求及裝置類(Class)請求的處理流程中,亦可經由Callback功能的延伸擴充相關功能的處理流程

2.協議棧架構

USB協議棧的基本架構如下

usb stack structure
Figure 1: 協議棧架構

架構由底層往上區分為核心層,裝置/主機標準枚舉層,裝置/主機類(Class)枚舉層及應用層,各結構層基本功能說明如下:

  • 核心(Core)層:提供USB中斷處理函數及與PHY層配置相關的函數,此核心層的功能均直接透過USB處理器對PHY層做存取

  • 標準枚舉(Enumeration)層:基於USB定義的標準請求規範,處理USB標準請求的基本處理流程,部分請求可經由Callback函數擴充處理流程,在枚舉過程中,除了記錄端點0的狀態外,亦同時記錄其他端點及USB BUS的枚舉狀態

  • 類枚舉(Class enumeration)層:除了USB的標準請求之外,USB還針對不同的應用定義了不同的類(Class)規範,每一個USB類(Class)規範也定義了對應的類(Class)請求及類(Class)相關的Descriptor,類(Class)枚舉層提供類(Class)請求基本的處理流程,部分請求可經由Callback函數擴充處理流程,目前版本提供HID,CDC,MSC的類(Class)請求處理流程

  • 應用層(Application):經由枚舉層提供的端點狀態所延伸的相關應用程序,這也是開發者最需要關注開發的程序,由於不同的USB應用對於所對應的端點狀態及應用流程均不相同,因此無法以通用的端點狀態及應用流程概括之,開發者必須針對開發的USB應用所對應的應用端點狀態自行開發相對應的應用程序

  • Callback:提供開發者對USB中斷,標準請求及裝置類(Class)請求處理流程中擴充額外的處理流程,其中標準請求的GET_DESCRIPTOR為開發者目前必須提供的Callback流程

3.檔案說明

3.1.核心層

  • usb_core.h:
    核心層的表頭檔,定義USB規範的常數,操作模式的參數,標準請求的結構,標準Descriptor的結構及宣告usb_core.c提供的函數

  • usb_core.c:

    • USB Interrupt Service Routine(USB_IRQHandler):
      基本的USB中斷處理程序,開發者可經由Callback程序擴充中斷處理程序

    • 操作模式配置(usb_core_mode_set):
      配置USB控制器的操作模式,目前版本僅提供ForceDeviceMode,ForceHostMode

    • Soft Connect(usb_core_dev_connect):
      在裝置模式下使用Soft Connect連接USB

    • Soft Disconnect(usb_core_dev_disconnect):
      在裝置模式下使用Soft Disconnect斷接USB

3.2.1.標準枚舉層

  • usb_enum.h:
    標準枚舉層的表頭檔,定義USB事件,Tx端點,Rx端點中斷事件的位元常數,及宣告枚舉過程需要參考的指標變數

  • usb_dev_enum.h:
    裝置枚舉層的表頭檔,定義控制傳輸模式的預設封包大小,枚舉的USB狀態,裝置的結構欄位,標準請求及裝置類(Class)請求的Callback結構欄位及宣告usb_dev_enum.c提供的函數

  • usb_dev_enum.c:

    • USB標準請求處理程序(pUSBDStandardRequest[]):
      所有USB標準請求基本處理流程,其中GET_DESCRIPTOR,SET_ADDRESS,SET_COFIGURATION,SET_INTERFACE可經由Callback程序擴充處理流程

    • 裝置枚舉狀態(usbd_dev_enum):
      經由相關的USB中斷處理紀錄目前的裝置枚舉狀態,提供給應用層開發相對應的應用流程,枚舉狀態定義於eUSBDState(usb_dev_enum.h)類型

3.3.裝置類(Class)枚舉層

3.3.1 HID Class
  • usbhid.h:
    HID類(Calss)的表頭檔,定義HID協議的常數,HID類(Class)請求的常數,HID類(Class)Descriptor的結構

  • usb_dev_hid.h:
    HID類(Calss)枚舉的表頭檔,宣告usb_dev_hid.c提供的類(Class)請求處理函數

  • usb_dev_hid.c:

    • HID裝置類(Class)請求處理程序(pHIDClassRequest[]):
      HID裝置類(Class)請求基本處理流程,其中GET_REPORT,GET_IDLE,GET_PROTOCOL,SET_REPORT,SET_IDLE,SET_PROTOCOL可經由Callback程序擴充請求處理流程

3.3.2 CDC Class
  • usbcdc.h:
    CDC類(Calss)的表頭檔,定義CDC協議的常數,CDC類(Class)請求的常數,CDC類(Class)Descriptor的結構

  • usb_dev_cdc.h:
    CDC類(Calss)枚舉的表頭檔,宣告usb_dev_cdc.c提供的類(Class)請求處理函數

  • usb_dev_cdc.c:

    • CDC裝置類(Class)請求處理程序(pCDCClassRequest[]):
      CDC裝置類(Class)請求基本處理流程,其中SET_LINE_CODING,GET_LINE_CODING,SET_CONTROL_LINE_STATE可經由Callback程序擴充請求處理流程

3.3.3 MSC Class
  • usbmsc.h:
    MSC類(Calss)的表頭檔,定義MSC的常數,MSC類(Class)請求的常數,MSC類(Class)Descriptor的結構

  • usb_dev_msc.h:
    MSC類(Calss)枚舉的表頭檔,宣告usb_dev_msc.c提供的類(Class)請求處理函數

  • usb_dev_msc.c:

    • MSC裝置類(Class)請求處理程序(pMSCClassRequest[]):
      MSC裝置類(Class)請求基本處理流程,其中GET_MAX_LUN,STORAGE_RESET可經由Callback程序擴充請求處理流程

3.4.應用層

應用層包含三個基本檔案app_dev_xxxx,cb_dev_xxxx,app_xxxx_desc開發者可視開發需求直接修改應用層檔案,或是依不同專案將檔案複製到不同資料夾,其中xxxx提供開發者依專案賦予個別的專案名稱,以下針對個別檔案說明

3.4.1 app_dev_xxxx檔案

  • app_dev_xxxx.h:
    app_dev_xxxx.c的表頭檔,宣告app_dev_xxxx.c提供的函數

  • app_dev_xxxx.c:

    • 應用裝置初始化(xxxx_dev_init):

      1. 端點0初始化(以最大64Bytes初始化端點0,開發者無須修改此函數),Enable端點0中斷

      2. 開發者將以_USB_FS_DEVICE(usb_core.h)型態定義的USB裝置的cb_app_dev_request欄位指向以_CB_APP_DEV_REQUEST(usb_dev_enum.h)型態定義的標準請求及裝置類(Class)請求的Callback函數

      3. 開發者將cb_app_usb_handler指向自訂的USB中斷Callback函數,如無自訂Callback中斷程序,則保持預設值(NULL)

    • 應用裝置使用的端點配置(xxxx_dev_ep_config):
      配置使用端點的類型,最大封包,記憶體位置,開啟端點中斷

    • 應用裝置枚舉狀態(xxxx_dev_enum):
      經由裝置枚舉層的usbd_dev_enum紀錄目前的裝置枚舉狀態

    • 應用裝置應用程序(xxxx_dev_transaction):
      經由應用相關的端點狀態開發相對應的應用處理程序,這個程序為開發者最必須專注開發的程序,在USB應用過程中,開發者需針對應用的端點狀態及相關的應用處理流程開發相對應的程序

3.4.2 cb_dev_xxxx檔案

  • cb_dev_xxxx.h:
    cb_dev_xxxx.c的表頭檔,宣告cb_dev_xxxx.c提供的Callback函數

  • cb_dev_xxxx.c:
    Callback程序,由於中斷,標準請求及裝置類(Class)請求的處理程序僅依據USB規範提供最基本的標準處理流程,在實際應用中根據系統應用需求可能需擴充額外的處理程序,由於不同應用的額外處理程序均不相同,因此在標準的處理流程下另提供了Callback函數以提供開發者適度的擴充處理程序,Callback程序本身根據應用分成幾大項

    • USB中斷處理(cb_app_usb_handler):
      額外的中斷處理流程,基本的USB中斷流程僅將中斷旗標記錄下來,清除中斷旗標,再回到主程序由usbd_dev_enum(usb_dev_enum.h)決定枚舉狀態,如果開發者希望在USB中斷擴充額外的中斷處理流程,可自行開發Callback處理程序,並將cb_app_usb_handler(usb_enum.h)指向自行開發的中斷處理程序,如不需額外的中斷處理流程,須將cb_app_usb_handler(usb_enum.h)指向NULL以避免函數指向錯誤,cb_app_usb_handler的預設值為NULL

    • 標準請求流程(cb_app_dev_enum):
      額外的標準請求流程,基本的標準請求處理流程僅依據USB規範的標準流程處理,在目前的協議棧中,提供GET_DESCRIPTOR、SET_ADDRESS、SET_CONFIGURATION、SET_INTERFACE請求可由開發者自行開發額外的處理程序,並將_USB_FS_DEVICE(usb_dev_enum.h)中以_CB_APP_DEV_REQUEST(usb_dev_enum.h)型態定義的cb_app_dev_request的cb_app_dev_enum函數指標指向自行開發的請求處理程序,由於應用程序必需至少提供GET_DESCRIPTOR的Callback函數,因此cb_app_dev_enum不能為NULL

      • GET_DESCRIPTOR:
        在標準請求的額外處理程序中,GET_DESCRIPTOR是目前開發者必需提供的請求處理程序,由於Descriptor Data是USB裝置與主機溝通的基本資訊,而且每一個應用裝置的Descriptor Data都不會相同,為保留最大的彈性,所有GET_DESCRIPTOR請求所需要回傳的數據位址與數據長度都留給應用程序提供,開發者須將_USB_FS_DEVICE(usb_dev_enum.h)中定義的pCtrlBurstData指標指向要回傳的Descriptor Data的位址,將_USB_FS_DEVICEㄒ中定義的CtrlBurstRemain變數填入要回傳的Descriptor Data的長度

    • 類(Class)請求流程(cb_app_dev_class):
      額外的裝置類(Class)請求處理流程,基本的裝置類(Class)請求處理流程僅依據USB類(Class)規範的標準流程處理,在目前的協議棧中,依據不同的裝置類(Class)請求提供開發者自行開發額外的處理程序,並將_USB_FS_DEVICE(usb_dev_enum.h)中以_CB_APP_DEV_REQUEST(usb_dev_enum.h)型態定義的cb_app_dev_request的cb_app_dev_class函數指標指向自行開發的裝置類(Class)請求處理程序,額外的裝置類(Class)請求處理程序將於個別的應用開發例程中說明,如不需額外的裝置類(Class)請求處理程序,須將cb_app_dev_class(usb_enum.h)指向NULL以避免函數指向錯誤,cb_app_dev_class預設值為NULL

      • HID裝置(cb_dev_request_hid):
        HID的裝置類(Class)請求處理流程,提供GET_REPORT,GET_IDLE,GET_PROTOCOL,SET_REPORT,SET_IDLE,SET_PROTOCOL請求可由開發者自行開發額外的處理程序

      • CDC裝置(cb_dev_request_cdc):
        CDC的裝置類(Class)請求處理流程,提供SET_LINE_CODING,GET_LINE_CODING,SET_CONTROL_LINE_STATE請求可由開發者自行開發額外的處理程序

      • MSC裝置(cb_dev_request_msc):
        MSC的裝置類(Class)請求處理流程,提供GET_MAX_LUN,STORAGE_RESET請求可由開發者自行開發額外的處理程序

3.4.3 app_xxxx_desc檔案

  • app_xxxx_desc.h:
    app_xxxx_desc.c的表頭檔,定義除了字串0以外的標準字串Descriptor結構,定義USB應用裝置的Configuration Descriptor結構,宣告app_xxxx_desc.c供GET_DESCRIPTOR存取的Descriptor Data

  • app_xxxx_desc.c:
    定義USB應用裝置的Descriptor Data,一個基本的USB裝置通常包含

    • 一組Device Descriptor

    • 一組Configuration Descriptor

    • 一組Interface Descriptor,包含應用所需的Endpoint Descriptor

    • 一組String 0 Descriptor

    • 視應用而定的String Descriptor

4.應用例程開發 - Device

4.1.宣告USB裝置

在開始開發應用例程之前,開發者必須先以_USB_FS_DEVICE(usb_dev_enum.h)的型態定義一個USB裝置,_USB_FS_DEVICE的類型定義在usb_dev_enum.h,類型的欄位說明如下:

  • DevAddr:
    主機賦予的USB裝置地址,預設為0,在裝置枚舉過程中,主機會透過SET_ADDRESS配置一個地址給USB裝置,一個USB裝置只會有一個地址,在協議棧的標準請求流程中,當收到SET_ADDRESS的請求時,會將主機賦予的USB裝置地址存放在這裡

  • ConfigValue:
    主機配置的配置值,預設為0,主機會依據應用裝置返回的Descriptor Data透過SET_CONFIGURATION賦予配置值,個數值是由裝置定義的Configuration Descriptor中的bConfigurationValue決定,在一般應用下,一個裝置只會定義一個配置值,但在多重配置的應用下,一個裝置可以有好幾個不同的Configuration Descriptor,每一個Configuration Descriptor有各自的配置值,在協議棧的標準請求流程中,當收到SET_CONFIGURATION的請求時,會將主機配置的配置值存放在這裡

  • pCtrlBurstData:
    用於指定Control In/Out的數據緩衝區指標,在處理Control In/Out時會直接從這裡所指向的數據緩衝區存取數據

  • CtrlBurstRemain:
    存放Control In/Out要傳送的數據長度,在處理Control In/Out時會從這裡所存放的數值決定還有多少數據需要傳送

  • cb_app_dev_request:
    一個以_CB_APP_DEV_REQUEST(usb_dev_enum.h)的類型定義的Callback函數指標,指向處理額外的標準請求(cb_app_dev_enum)及裝置類(Class)請求(cb_app_dev_class)的處理程序的Callback函數。

在整個枚舉的過程中,這個裝置都會以指標的形式作為函數呼叫的參數之一。另外一個以指標的形式作為函數呼叫的參數為以_USB_SETUP_REQUEST(usb_enum.h)的型態定義的SetupReq指標參數,_USB_SETUP_REQUEST結構包含USB標準請求求所定義的8個Bytes的數據。

4.2.初始化

主程序在做過系統相關的配置之後(系統時鐘,中斷,IO…​)即可開始USB裝置的初始化流程

4.2.1.裝置初始化

應用程序需先呼叫xxxx_dev_init(app_dev_xxxx.c)對以_USB_FS_DEVICE(usb_dev_enum.h)的型態定義的USB裝置初始化,初始化流程包含對端點0的配置(預設是以最大64Bytes配置,應用程序可以不必修改)以及將以_USB_FS_DEVICE(usb_dev_enum.h)的型態定義的USB裝置的Callback函數指標cb_app_dev_request指向標準請求及類(Class)請求的Callback函數(如無Callback,則預設為NULL),應用程序如果需要處理額外的USB中斷流程,也可在此將cb_app_usb_handler(usb_enum.h)指向中斷流程的Callback函數(如無Callback,則預設為NULL)。

除了端點0之外,其他端點的配置不建議在這裡執行,這是因為在枚舉的過程中主機會下RESET命令,當USB控制器收到RESET命令時會重置所有端點的配置狀態,因此在本範例中,端點的配置是在RESET命令之後執行的,開發者也可以依據實際應用調整端點的配置。

4.2.2.配置運行模式

呼叫usb_core_mode_set(usb_core.c)將模式配置為ForceDevice(eUSBModeForceDevice)Mode,並Enable Soft Connect

4.3.開始枚舉流程

初始化完成之後便進入枚舉流程,應用程序呼叫xxxx_dev_enum(app_dev_xxxx.c)以決定枚舉狀態,xxxx_dev_enum(app_dev_xxxx.c)透過呼叫usbd_dev_enum(usbd_dev_enum.c)級USB中斷決定USB枚舉狀態,所有關於標準請求及裝置類(Class)請求的流程都會經由usbd_dev_enum(usbd_dev_enum.c)及usbd_dev_xxx.c(xxx為裝置類(Class)名稱)完成,當開發者需要在請求過程中擴充額外的處理流程時,將透過參數中以_USB_FS_DEVICE(usb_dev_enum.h)定義的USB裝置指標的cb_app_dev_request函數完成,除了端點0的請求流程外,枚舉狀態還包含裝置對應的端點狀態(IN/Out),開發者須依據實際應用的端點狀態開發相關的應用流程,這部分的流程將透過開發xxxx_dev_transaction(app_dev_xxxx.c)及其延伸的處理程序完成

4.4.應用流程圖

usb stack flow
Figure 2: 協議棧開發流程

5.應用例程 - HID裝置

5.1.宣告HID裝置

  • 本範例說明開發HID裝置的基本流程,由於HID裝置定義的應用範圍極廣,本範例以標準的滑鼠為開發範例,且不涉及端點的應用程序開發及相關的週邊設定,僅依據協議棧的架構開發應用範例,使主機可以正確的枚舉成HID裝置,開發者可依據本範例開發實際的HID應用裝置

  • 在主程序(main.c)中以_USB_FS_DEVICE(usb_dev_enum.h)的型態定義一個HID裝置HID_FS_Dev

_USB_FS_DEVICE HID_FS_Dev;

_USB_FS_DEVICE的相關欄位已於之前章節說明,在現行的應用中,開發者須關注的欄位是pCtrlBurstData,CtrlBurstRemain及cb_app_dev_request

5.2.裝置初始化

  • 呼叫hidenum_dev_init(app_dev_hidenum.c)對HIDBLDR_FS_Dev初始化:
    範例的HID裝置將會枚舉成一般的滑鼠,並依據HID規範配置一組INT IN端點及一組INT OUT端點(INT OUT端點為非必要),本範例著重在提供一個基本的滑鼠範例,因此端點的配置可能與實際的應用不盡相符,開發者可視實際應用需求修改配置
    HID裝置的端點配置及Callback函數將於下一章節說明

  • 呼叫usb_core_mode_set(usb_core.c)將運行模式設定為FORCE_DEVICE Mode,Enable Soft Connect

5.3.裝置枚舉

  • 呼叫hidenum_dev_enum(app_dev_hidenum.c)決定枚舉的USB狀態:
    由於標準請求及HID類(Class)請求的流程已於標準枚舉層及HID類(Class)枚舉層完成,因此應用程式只須關注對應的端點狀態即可,以基本的HID裝置來說,通常只配置一組INT IN端點及一組INT OUT端點(有些裝置不需INT OUT端點),因此在枚舉過程中,應用程序只須關注IN端點及OUT端點的狀態開發相關的應用程序

  • 依實際應用的端點狀態呼叫hidenum_dev_transaction(app_dev_hidenum.c)執行實際的應用程序:
    如上所述,應用程序只須關注IN端點及OUT端點的狀態,因此在枚舉過程中判斷應用的IN端點及OUT端點狀態,再呼叫hidenum_dev_transaction(app_dev_hidenum.c)完成對應的應用程序

5.4.開發HID裝置的應用程序

  • app_dev_hidenum.c:

    • hidenum_dev_init:
      設定端點0
      將以_USB_FS_DEVICE(usb_dev_enum.h)定義的HID_FS_Dev中的Callback欄位cb_app_dev_request指向cb_dev_hidenum.c定義的Callback函數 cb_app_dev_hidenum
      由於HID裝置不需要USB中斷Callback,因此保留USB中斷Callback的預設值(NULL)

    • hidenum_dev_ep_config:
      設定HID應用的端點,在範例中定義了INT IN端點(端點1)及INT OUT端點(端點1),因此針對相對應的端點做配置

    • hidenum_dev_enum:
      呼叫usbd_dev_enum(usb_dev_enum.c)以決定USB狀態,在USB主機發出RESET時呼叫hidenum_dev_ep_config設定INT IN端點(端點1)及INT OUT端點(端點1)

    • hidenum_dev_transaction:
      依HID應用的實際流程開發應用程序,包含相關週邊IP的程序,這部分保留給開發者依據實際應用開發

  • cb_dev_hidenum.c:

    • 以_CB_APP_DEV_REQUEST(usb_dev_enum.h)的型態定義一個cb_app_dev_hidenum,並將cb_app_dev_enum函數指標指向cb_app_dev_enum,cb_app_dev_class函數指標指向cb_app_dev_hid,開發者如不需開發額外的HID類(Class)請求Callback,可將cb_app_dev_class指向NULL

    • cb_app_dev_enum:
      在GET_DESCRIPTOR的Callback處理程序中,將須回傳Descriptor的數據指標及數據長度填入HID_FS_Dev的pCtrlBurstData及CtrlBurstRemain欄位中 其餘標準請求處理程序保留給開發者依實際應用開發

    • cb_app_dev_hid:
      HID類(Class)請求處理程序保留給開發者依實際應用開發處理程序

  • app_hidenum_desc.c: 定義HID裝置需要回傳的Descriptor Data及Report Descriptor Data,通常一個基本的HID裝置的Descriptor包含:

    • 一組Device Descriptor

    • 一組Configuration Descriptor

    • 一組Interface Descriptor,包含一組HID Descriptor,一組INT In Endpoint Descriptor及一組INT Out Endpoint Descriptor(非必要,視應用而定)

    • 一組Report Descriptor

    • 一組String 0 Descriptor

    • 以及視應用而定的幾組String Descriptor

6.應用例程 - CDC裝置

6.1.宣告CDC裝置

  • 本範例說明開發CDC裝置的基本流程,由於CDC裝置定義的應用範圍極廣,本範例以虛擬的COM裝置為開發範例,且不涉及端點的應用程序開發及相關的週邊設定,僅依據協議棧的架構開發應用範例,使主機可以正確的枚舉成虛擬COM裝置,開發者可依據本範例開發實際的CDC應用裝置

  • 在主程序(main.c)中以_USB_FS_DEVICE(usb_dev_enum.h)的型態定義一個CDC裝置CDC_FS_Dev

_USB_FS_DEVICE CDC_FS_Dev;

_USB_FS_DEVICE的相關欄位已於之前章節說明,在現行的應用中,開發者須關注的欄位是pCtrlBurstData,CtrlBurstRemain及cb_app_dev_request

6.2.裝置初始化

  • 呼叫cdcenum_dev_init(app_dev_cdcenum.c)對CDC_FS_Dev初始化:
    範例的CDC裝置將會枚舉成一個虛擬的COM裝置,並依據CDC規範配置一組INT IN端點,一組BULK IN端點及一組BULK OUT端點,本範例著重在提供一個基本的虛擬COM裝置範例,因此端點的配置可能與實際的應用不盡相符,開發者可視實際應用需求修改配置
    CDC裝置的端點配置及Callback函數將於下一章節說明

  • 呼叫usb_core_mode_set(usb_core.c)將運行模式設定為FORCE_DEVICE Mode,Enable Soft Connect

6.3.裝置枚舉

  • 呼叫cdcenum_dev_enum(app_dev_cdcenum.c)決定枚舉的USB狀態:
    由於標準請求及CDC類(Class)請求的流程已於標準枚舉層及CDC類(Class)枚舉層完成,因此應用程式只須關注對應的端點狀態即可,以基本的CDC裝置來說,通常配置一組INT IN端點,一組BULK IN端點及一組BULK OUT端點,因此在枚舉過程中,應用程序只須關注IN端點及OUT端點的狀態開發相關的應用程序

  • 依實際應用的端點狀態呼叫cdcenum_dev_transaction(app_dev_cdcenum.c)執行實際的應用程序:
    如上所述,應用程序只須關注IN端點及OUT端點的狀態,因此在枚舉過程中判斷應用的IN端點及OUT端點狀態,再呼叫cdcenum_dev_transaction(app_dev_cdcenum.c)完成對應的應用程序

6.4.開發CDC裝置的應用程序

  • app_dev_cdcenum.c:

    • cdcenum_dev_init:
      設定端點0
      將以_USB_FS_DEVICE(usb_dev_enum.h)定義的CDC_FS_Dev中的Callback欄位cb_app_dev_request指向cb_dev_cdcenum.c定義的Callback函數 cb_app_dev_cdcenum
      由於CDC裝置不需要USB中斷Callback,因此保留USB中斷Callback的預設值(NULL)

    • cdcenum_dev_ep_config:
      設定CDC應用的端點,在範例中定義了一組INT IN端點(端點1),一組BULK IN端點(端點2)及一組BULK OUT端點(端點2),因此針對相對應的端點做配置

    • cdcenum_dev_enum:
      呼叫usbd_dev_enum(usb_dev_enum.c)以決定USB狀態,在USB主機發出RESET時呼叫cdcenum_dev_ep_config設定INT IN端點(端點1),BULK IN端點(端點2)及BULK OUT端點(端點2)

    • cdcenum_dev_transaction:
      依CDC應用的實際流程開發應用程序,包含相關週邊IP的程序,這部分保留給開發者依據實際應用開發

  • cb_dev_cdcenum.c:

    • 以_CB_APP_DEV_REQUEST(usb_dev_enum.h)的型態定義一個cb_app_dev_cdcenum,並將cb_app_dev_enum函數指標指向cb_app_dev_enum,cb_app_dev_class函數指標指向cb_app_dev_cdc,開發者如不需開發額外的CDC類(Class)請求Callback,可將cb_app_dev_class指向NULL

    • cb_app_dev_enum:
      在GET_DESCRIPTOR的Callback處理程序中,將須回傳Descriptor的數據指標及數據長度填入CDC_FS_Dev的pCtrlBurstData及CtrlBurstRemain欄位中 其餘標準請求處理程序保留給開發者依實際應用開發

    • cb_app_dev_cdc:
      CDC類(Class)請求處理程序保留給開發者依實際應用開發處理程序

  • app_cdcenum_desc.c:
    定義CDC裝置需要回傳的Descriptor Data,通常一個基本的CDC裝置的Descriptor包含:

    • 一組Device Descriptor

    • 一組Configuration Descriptor

    • 一組Control Interface Descriptor,包含一組INT In Endpoint Descriptor

    • 一組Data Interface Descriptor,包含一組BULK In Endpoint Descriptor及** 一組BULK Out Endpoint Descriptor

    • 一組String 0 Descriptor

    • 以及視應用而定的幾組String Descriptor

7.應用例程 - 複合(Composite)裝置

7.1.宣告複合裝置

  • 本範例說明開發複合裝置的基本流程,複合裝置係指一個以不同的Interface Descriptor枚舉兩個或以上的不同功能的裝置,本範例依據協議棧的架構開發具備CDC與HID功能的應用範例,在不涉及端點的應用程序開發及相關的週邊設定下,使主機可以正確的枚舉成虛擬COM裝置及HID裝置,開發者可依據本範例開發實際的複合裝置

  • 在主程序(main.c)中以_USB_FS_DEVICE(usb_dev_enum.h)的型態定義一個複合裝置COMPOSITE_FS_Dev

_USB_FS_DEVICE COMPOSITE_FS_Dev;

_USB_FS_DEVICE的相關欄位已於之前章節說明,在現行的應用中,開發者須關注的欄位是pCtrlBurstData,CtrlBurstRemain及cb_app_dev_request

7.2.裝置初始化

  • 呼叫composite_dev_init(app_dev_composite.c)對COMPOSITE_FS_Dev初始化:
    範例的複合裝置將會枚舉成一個虛擬的COM裝置與HID自定義裝置,並依據CDC規範配置一組INT IN端點,一組BULK IN端點及一組BULK OUT端點,依據HID規範配置一組INT IN端點及一組INT OUT端點,本範例著重在提供一個基本的複合裝置範例,因此端點的配置可能與實際的應用不盡相符,開發者可視實際應用需求修改配置
    複合裝置的端點配置及Callback函數將於下一章節說明

  • 呼叫usb_core_mode_set(usb_core.c)將運行模式設定為FORCE_DEVICE Mode,Enable Soft Connect

7.3.裝置枚舉

  • 呼叫composite_dev_enum(app_dev_composite.c)決定枚舉的USB狀態:
    由於複合裝置具備兩個或以上的功能,因此除了標準請求由標準枚舉層完成基本處理流程外,在枚舉不同功能的裝置類(Class)請求時,必須同時判斷Interface以決定是那一個裝置的類(Class)請求,CDC類(Class)請求的流程將於CDC類(Class)枚舉層完成,HID類(Class)請求的流程將於HID類(Class)枚舉層完成,同樣的,應用程式在關注對應的端點狀態時也必須同時經由Interface決定是那個裝置的端點,在枚舉過程中,應用程序須關注不同裝置的端點狀態開發相關的應用程序

  • 依實際應用的端點狀態呼叫composite_dev_transaction(app_dev_composite.c)執行實際的應用程序:
    如上所述,應用程序須關注不同裝置的端點狀態,因此在枚舉過程中判斷不同裝置應用的IN端點及OUT端點狀態,再呼叫composite_dev_transaction(app_dev_composite.c)完成對應的應用程序

7.4.開發複合裝置的應用程序

  • app_dev_composite.c:

    • composite_dev_init:
      設定端點0
      將以_USB_FS_DEVICE(usb_dev_enum.h)定義的COMPOSITE_FS_Dev中的Callback欄位cb_app_dev_request指向cb_dev_cdcenum.c定義的Callback函數 cb_app_dev_composite
      由於CDC裝置不需要USB中斷Callback,因此保留USB中斷Callback的預設值(NULL)

    • composite_dev_ep_config:
      設定複合應用的端點,在範例中的CDC裝置定義了一組INT IN端點(端點1),一組BULK IN端點(端點2)及一組BULK OUT端點(端點2),HID裝置定義了一組INT IN端點(端點3)及一組INT OUT端點(端點3),因此針對相對應的端點做配置

    • composite_dev_enum:
      呼叫usbd_dev_enum(usb_dev_enum.c)以決定USB狀態,在USB主機發出RESET時呼叫composite_dev_ep_config設定CDC裝置的INT IN端點(端點1),BULK IN端點(端點2)及BULK OUT端點(端點2),HID裝置的INT IN端點(端點3)及INT OUT端點(端點3)

    • composite_dev_transaction:
      依複合應用的實際流程開發應用程序,包含相關週邊IP的程序,這部分保留給開發者依據實際應用開發

  • cb_dev_composite.c:
    以_CB_APP_DEV_REQUEST(usb_dev_enum.h)的型態定義一個cb_app_dev_composite,並將cb_app_dev_enum函數指標指向cb_app_dev_enum,cb_app_dev_class函數指標指向cb_app_dev_composite, 開發者如不需開發額外的CDC與HID類(Class)請求Callback,可將cb_app_dev_class指向NULL

    • cb_app_dev_enum:
      在GET_DESCRIPTOR的Callback處理程序中,將須回傳Descriptor的數據指標及數據長度填入CDC_FS_Dev的pCtrlBurstData及CtrlBurstRemain欄位中 其餘標準請求處理程序保留給開發者依實際應用開發

    • cb_app_dev_composite:
      cb_app_dev_composite包含CDC與HID裝置的類(Class)請求Callback函數,處理程序保留給開發者依實際應用開發處理程序

  • app_composite_desc.c:
    定義複合裝置需要回傳的Descriptor Data,本範例定義的複合裝置的Descriptor包含:

    • 一組Device Descriptor

    • 一組Configuration Descriptor

    • 一組IAD Descriptor(將CDC所需的Interface組合成單一裝置)

    • 一組Control Interface Descriptor(CDC),包含一組INT In Endpoint Descriptor

    • 一組Data Interface Descriptor(CDC),包含一組BULK In Endpoint Descriptor及一組BULK Out Endpoint Descriptor

    • 一組Interface Descriptor(HID),包含一組HID Descriptor,一組INT In Endpoint Descriptor及一組INT Out Endpoint Descriptor(非必要,視應用而定)

    • 一組Report Descriptor

    • 一組String 0 Descriptor

    • 以及視應用而定的幾組String Descriptor

8.應用例程 - HID IAP裝置

8.1.宣告HID裝置

  • HID IAP裝置是基於USB HID類(Class)規範所延伸的的一種在線更新的應用,裝置本身會枚舉成HID的自定義裝置,也就是所有的協議都是自定義的,配合主機以HID的規範加上自定義的協議達到在線更新的功能,本範例依據之前章節說明的開發流程開發實際的HID IAP應用裝置

  • 在主程序(main.c)中以_USB_FS_DEVICE(usb_dev_enum.h)的型態定義一個HID裝置HIDBLDR_FS_Dev

_USB_FS_DEVICE HIDBLDR_FS_Dev;

_USB_FS_DEVICE的相關欄位已於之前章節說明,在現行的應用中,開發者須關注的欄位是pCtrlBurstData,CtrlBurstRemain及cb_app_dev_request

8.2.裝置初始化

  • 呼叫hidbldr_dev_init(app_dev_hidbldr.c)對HIDBLDR_FS_Dev初始化:
    本範例的HID裝置將會枚舉成自定義裝置,並依據HID規範及在線更新協議配置一組INT IN端點及一組INT OUT端點
    HID裝置的端點配置及Callback函數將於下一章節說明

  • 呼叫usb_core_mode_set(usb_core.c)將運行模式設定為FORCE_DEVICE Mode,Enable Soft Connect

8.3.裝置枚舉

  • 呼叫hidbldr_dev_enum(app_dev_hidbldr.c)決定枚舉的USB狀態:
    由於標準請求及HID類(Class)請求的流程已於標準枚舉層及HID類(Class)枚舉層完成,因此應用程式只須關注對應的端點狀態即可,以本範例的HID裝置來說,需配置一組INT IN端點及一組INT OUT端點,因此在枚舉過程中,應用程式只須關注相關端點的狀態開發相關的應用程序

  • 依實際應用的端點狀態呼叫hidbldr_dev_transaction(app_dev_hidbldr.c)執行實際的應用程序:
    由於本範例的應用主要解讀來自主機的協議以達到在新更新的功能,在實際的應用中,主機透過配置的INT OUT端點(端點1)傳送在線更新協議,因此應用裝置只須在枚舉過程中判斷INT OUT端點(端點1)的狀態,再呼叫hidbldr_dev_transaction(app_dev_hidbldr.c)完成對應的應用程序,INT IN端點(端點1)主要作為更新過程中回傳給主機的數據傳輸端點

8.4.開發HID裝置的應用程序

  • app_dev_hidbldr.c:

    • hidbldr_dev_init:
      設定端點0
      將以_USB_FS_DEVICE(usb_dev_enum.h)定義的HIDBLDR_FS_Dev中的Callback欄位cb_app_dev_request指向cb_dev_hidbldr.c定義的Callback函數cb_app_dev_hidbldr
      由於HID IAP裝置不需要USB中斷Callback,因此保留USB中斷Callback的預設值(NULL)

    • hidbldr_dev_ep_config:
      設定HID IAP應用的端點,在範例中定義了INT IN端點(端點1),因此針對相對應的端點做配置

    • hidbldr_dev_enum:
      呼叫usbd_dev_enum(usb_dev_enum.c)以決定USB狀態,在USB主機發出RESET時呼叫hidbldr_dev_ep_config設定INT IN端點(端點1)

    • hidbldr_dev_transaction:
      當主機經由INT OUT端點(端點1)發出指令時,主程序呼叫此函數解析指令,並依據指令的內容執行對應的在線更新流程,由於指令的協議較為複雜,且必須對Flash進行擦除及讀寫的動作,因此另外開發了eslab_parser.c及iap_update.c來協助執行在線更新的動作

  • cb_dev_hidbldr.c:

    • 以_CB_APP_DEV_REQUEST(usb_dev_enum.h)的型態定義一個cb_app_dev_hidbldr,並將cb_app_dev_enum函數指標指向cb_app_dev_enum,由於不需開發額外的HID類(Class)請求Callback,因此將cb_app_dev_class函數指標指向NULL

    • cb_app_dev_enum:
      在GET_DESCRIPTOR的Callback處理程序中,將回傳Descriptor的數據指標及數據長度填入HIDBLDR_FS_Dev的pCtrlBurstData及CtrlBurstRemain欄位中

    • cb_app_dev_hid:
      HID類(Class)請求,本應用無須執行額外的HID類(Class)請求處理流程

  • app_hidbldr_desc.c: 定義HID IAP裝置需要回傳的Descriptor Data及Report Descriptor Data,本範例的HID IAP裝置的Descriptor包含:

    • 一組Device Descriptor

    • 一組Configuration Descriptor

    • 一組Interface Descriptor,包含一組HID Descriptor,一組INT In Endpoint Descriptor及一組INT Out Endpoint Descriptor

    • 一組Report Descriptor

    • 一組String 0 Descriptor

    • 一組String 1 Descriptor (開發商字串)

    • 一組String 2 Descriptor (裝置顯示字串)

  • eslab_parser.h: eslab_parser.c的表頭檔,定義HID在線更新協議的指令常數,宣告eslab_parser.c提供的函數

  • eslab_parser.c: 解析在線更新的指令,依據協議執行對IAP Flash的存取,關於本範例使用的在線更新協議將於附錄A說明

  • iap_update.h: iap_update.c的表頭檔,定義在線更新協議的Flash範圍,宣告iap_update.c提供的函數
    由於本範例本身亦存在於Flash,因此在線更新的範圍無法涵蓋完整的Flash,iap_update.h定義了可被更新的Flash範圍,應用程式可視實際應用情況調整可更新的Flash範圍

  • iap_update.c: 對於IAP Flash存取的函數庫,包含Erase,Program,Read等函數

  • HID IAP之操作須配合主機端之應用程式以HID協議與裝置通訊,主機端應用程式開發及相關流程說明不在本文件說明範圍,本範例適用於ES-USB-Lab應用程式,請參考ES-USB-Lab相關說明

9.應用例程 - MSC IAP裝置

9.1.宣告MSC裝置

  • MSC IAP裝置是基於USB MSC類(Class)規範所延伸的的一種在線更新的應用,裝置本身會枚舉成虛擬的儲存裝置,在主機端將要更新的bin檔案拖曳到此虛擬的儲存裝置即可達到在線更新的功能,本範例依據之前章節說明的開發流程開發實際的MSC IAP應用裝置

  • 在主程序(main.c)中以_USB_FS_DEVICE(usb_dev_enum.h)的型態定義一個HID裝置MSCBLDR_FS_Dev

_USB_FS_DEVICE MSCBLDR_FS_Dev;

_USB_FS_DEVICE的相關欄位已於之前章節說明,在現行的應用中,開發者須關注的欄位是pCtrlBurstData,CtrlBurstRemain及cb_app_dev_request

9.2.裝置初始化

  • 呼叫mscbldr_dev_init(app_dev_mscbldr.c)對MSCBLDR_FS_Dev初始化:
    本範例的MSC裝置將會枚舉成虛擬儲存裝置,並依據MSC規範配置一組BULK IN端點及一組BULK OUT端點
    MSC裝置的端點配置及Callback函數將於下一章節說明

  • 呼叫usb_core_mode_set(usb_core.c)將運行模式設定為FORCE_DEVICE Mode,Enable Soft Connect

9.3.裝置枚舉

  • 呼叫mscbldr_dev_enum(app_dev_mscbldr.c)決定枚舉的USB狀態:
    由於標準請求及MSC類(Class)請求的流程已於標準枚舉層及MSC類(Class)枚舉層完成,因此應用程式只須關注對應的端點狀態即可,以本範例的MSC裝置來說,需配置一組BULK IN端點及一組BULK OUT端點,因此在枚舉過程中,應用程式只須關注相關端點的狀態開發相關的應用程序

  • 依實際應用的端點狀態呼叫mscbldr_dev_transaction(app_dev_mscbldr.c)執行實際的應用程序:
    如上所述,由於應用裝置主要解讀來自主機的BULK OUT SCSI指令以達到在線更新的功能,因此應用裝置只須在枚舉過程中判斷BULK OUT端點的狀態,再呼叫mscbldr_dev_transaction(app_dev_mscbldr.c)完成對應的應用程序,BULK IN端點主要作為更新過程中回傳給主機的命令執行狀態

9.4.開發MSC裝置的應用程序

  • app_dev_mscbldr.c:

    • mscbldr_dev_init:
      設定端點0
      將以_USB_FS_DEVICE(usb_dev_enum.h)定義的MSCBLDR_FS_Dev中的Callback欄位cb_app_dev_request指向cb_dev_mscbldr.c定義的Callback函數cb_app_dev_mscbldr
      由於MSC IAP裝置不需要USB中斷Callback,因此保留USB中斷Callback的預設值(NULL)

    • mscbldr_dev_ep_config:
      設定MSC IAP應用的端點,在範例中依據MSC規範定義了BULK IN端點(端點1)及BULK OUT端點(端點1),因此針對相對應的端點做配置

    • mscbldr_dev_enum:
      呼叫usbd_dev_enum(usb_dev_enum.c)以決定USB狀態,在USB主機發出RESET時呼叫mscbldr_dev_ep_config設定BULK IN端點(端點1)及BULK OUT端點(端點1)

    • mscbldr_dev_transaction:
      當主機經由BULK OUT端點(端點1)發出SCSI指令時,主程序呼叫此函數解析SCSI指令,並依據SCSI指令的內容執行對應的流程,由於應用裝置的SCSI指令涉及FAT檔案系統,且必須對Flash進行擦除及讀寫的動作,因此另外開發了msc_fsdev.c及iap_update.c來協助執行在線更新的動作

  • cb_dev_mscbldr.c:

    • 以_CB_APP_DEV_REQUEST(usb_dev_enum.h)的型態定義一個cb_app_dev_mscbldr,並將cb_app_dev_enum函數指標指向cb_app_dev_enum,由於不需開發額外的MSC類(Class)請求Callback,因此將cb_app_dev_class函數指標指向NULL

    • cb_app_dev_enum:
      在GET_DESCRIPTOR的Callback處理程序中,將回傳Descriptor的數據指標及數據長度填入MSCBLDR_FS_Dev的pCtrlBurstData及CtrlBurstRemain欄位中

    • cb_app_dev_msc:
      MSC類(Class)請求,本應用無須執行額外的MSC類(Class)請求處理流程

  • app_mscbldr_desc.c: 定義MSC IAP裝置需要回傳的Descriptor Data,本範例的MSC IAP裝置的Descriptor包含:

    • 一組Device Descriptor

    • 一組Configuration Descriptor

    • 一組Interface Descriptor,包含一組BULK In Endpoint Descriptor及一組BULK Out Endpoint Descriptor

    • 一組String 0 Descriptor

    • 一組String 1 Descriptor (開發商字串)

    • 一組String 2 Descriptor (裝置顯示字串)

  • msc_fsdev.c: 處理裝置定義的FAT16虛擬磁碟檔案系統,解析主機對虛擬儲存裝置存取的LBA,依據BPB,FAT,Root及Data LBA回傳相對應的數據及對Flash的存取,由於回報給主機的是虛擬的磁碟,因此本裝置僅對特定的LBA作相對應的處理,並未完整的開發FAT檔案系統的存取

  • iap_update.c: 對於IAP Flash存取的函數庫,包含Erase,Program,Read等函數

  • 關於MSC IAP之操作將於附錄B說明

附錄A.HID IAP通訊協議

iap flow
Figure 3: IAP流程

附錄A.1.基本格式

所有指令都以單一ASCII字符形式發送,命令以'?'或'!'開始,'?'表示請求指令,'!'表示回應指令,'?'或'!'緊接著的一個ASCII碼為指令碼,一組指令中的的多個參數碼或回應碼用空格間隔,參數碼和返回碼均用ASCII字串表示,數據是以十六進制編碼格式表示,所有指令回應都是以<CR><LF>結束,多餘的<CR>和<LF>將被忽略

附錄A.2.請求命令格式

"?指令碼 參數碼0 參數碼1 …​ 參數碼n<CR><LF>""HEX數據+累加和"(數據只適用於寫指令)

附錄A.3.回應命令格式

"!回應字串<CR><LF>""HEX數據+累加和"(數據只適用於讀指令)

附錄A.4.數據格式

數據採用HEX編碼格式,每次傳送的長度都不應超過64個字元,接收器應當將該校驗和與接收字元的校驗和進行比對,如果校驗和一致,接收器執行指令並於指令執行正確後回應"OK<CR><LF>",如果校驗和不一致,接收器回應"CHECK_EROR<CR><LF>"

附錄A.5.指令集

附錄A.5.1.寫指令

  • 指令格式

請求碼 '?'

指令碼

W

輸入參數

參數0(起始寫入地址(ASCII)) 參數1(數據總長度(ASCII)) <CR><LF>

回應代碼

回應字串<CR><LF>

說明

寫指令

附錄A.5.2.發送數據指令

  • 指令格式

請求碼 '?'

指令碼

D

輸入參數

參數0(地址(ASCII)) 參數1(長度(ASCII)) <CR><LF>
數據(nbyte HEX) 累加和(1byte HEX)
(如果參數1(長度(ASCII)為0xff,表示後續傳送的數據為(起始寫入地址)~(起始寫入地址+數據總長度)的累加和)

回應代碼

回應字串<CR><LF>

說明

發送數據指令

附錄A.6.指令回應代碼

  • 回應代碼列表

回應碼 '!'

回應字串

說明

OK

執行成功

CMD_INVALID

無效的指令

PARAM_ERROR

參數錯誤

COUNT_ERROR

字元數錯誤

ADDR_ERROR

地址錯誤

BUSY

硬體忙碌

CHECK_ERROR

數據校驗錯誤

ERASE_FAILED

擦除錯誤

PROG_FAILED

編寫錯誤

附錄B.MSC IAP裝置訊息說明

MSC IAP範例創建了虛擬的儲存裝置(mass storage device),使用者將要更新的韌體bin檔案複製到此虛擬的儲存裝置後,IAP裝置會將此檔案燒錄到MCU的Flash中,完成韌體更新的動作
使用方法及顯示訊息如下:

  • 將MSC IAP裝置以USB連接線連接到PC上

  • 連接到PC之後,會在Windows系統上出現"MSC BLDR"的磁碟

  • 打開"MSC BLDR"磁碟,顯示"READY"檔名表示IAP裝置已就緒,可以接收bin檔案

  • 將要更新的bin檔案複製到磁碟內,裝置即開始做更新,更新完成後會自動Remap到應用程式執行,IAP虛擬磁碟即自動消失,如更新失敗,磁碟持續存在,並在磁碟內顯示"FAILED"檔名

  • 按下RESET按鍵或重新插拔USB連接線將重啟IAP裝置,並在虛擬磁碟內顯示"READY"檔名等候接收要更新的bin檔案

  • MSC IAP裝置只支援二進制的bin檔案更新

修訂歷史

版本 修改日期 更改概要

V0.1

2021-05-18

初版
基本架構說明
HID範例開發說明

V0.2

2021-05-31

補齊表頭檔的說明
修正文檔內容
CDC範例開發說明
複合裝置範例開發說明

V0.3

2021-06-04

HID IAP範例開發說明
HID在線更新協議

V0.4

2021-06-08

修改HID IAP範例
(符合ES-USB-Lab協議)
修改HID IAP操作說明
依據最新的MD庫重新建置方案

V0.5

2021-06-10

MSC IAP範例開發說明
MSC IAP操作說明

V0.6

2021-08-11

修正HID IAP協議錯誤描述