色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

如何添加USB Host Class驅動

恩智浦MCU加油站 ? 來源:恩智浦MCU加油站 ? 作者:郭嘉 ? 2021-10-28 09:23 ? 次閱讀

站長薦語:雖然本文談的主題是添加USB Host Class驅動,但文中所用到的方法具有普遍意義,所有MCU工程師都可以使用這種方法,參照已有功能做其它功能的擴展。

前 言

由于NXP有專業的MCU USB軟件開發團隊,NXP SDK USB協議棧支持了眾多的常用class,功能異常強大,為用戶帶來了很多的便利,加速了客戶的產品研發。

但是由于USB的應用場景實在是過于廣泛,有的class比如CCID host,是SDK USB 協議棧目前暫時沒有支持的。遇到這種情況,我們就需要自己動手來開發新的USB host class。本文以USB host CCID class 為例,探討如何在i.MX RT1020上實現此host class。本文將從以下3個方面闡述如何基于SDK USB協議棧進行新的USB host class的開發:
  1. 實現一個新的USB host class需要解決的問題。

  2. SDK USB host協議棧的基礎知識。

  3. 實現新的USB host class的一些要點展示。

需要解決的問題

實現一個新的USB host class,我們需要解決以下問題:

  • 如何進行枚舉,拿到設備的各種描述符?

  • 如何建立pipe(Bulk, Inerrupt, … …)通信

  • 如何進行USB host class的上層開發?

小編整理了一個USB 應用的模型結構如下圖所示。

從這個結構圖中,我們可以看到,基于USB協議棧,我們要重點實現pipe通信,在pipe通信之上,是具體的USB class的實現。而最終的應用是基于class實現的基礎之上的。

這個圖同時也說明,class的實現以及應用,可以通過pipe與USB stack進行隔離,相互保持獨立。軟件模塊之間保持獨立/低耦合,可以使軟件系統更加易于調試、維護和更新。

而USB控制器,在強大的SDK USB協議棧的加持下,對我們來說完全可以不關心。小編整個開發過程中,完全不需要去了解USB控制器的任何知識就可以完成新的host class的開發。

協議棧的基礎知識

一、從task的角度來看USB host協議棧

下圖以USB HOST CDC為例,展示了USB host協議棧的task結構。

了解task結構對于了解USB host協議棧是如何工作的非常有幫助。

幾個關鍵點(圖中紅色字體所示)就在于:

  1. 應用層事件的回調機制
  2. 設備狀態管理,主要是設備的接入、拔除、枚舉等。
  3. Class狀態管理,這里是設備運行的狀態機管理,包含了關鍵的class的interface和pipe的建立,class的初始化等等。

二、從函數調用棧的角度看USB host協議棧

對于復雜的軟件系統,分析調用棧是個不二捷徑,屢試不爽。

軟件工程學里面有一個概念,叫隔離。隔離是一個非常重要的概念,軟件工程學者認為隔離可以給軟件系統帶來很多的好處,兩個隔離的模塊,一個模塊做了內部改動的同時,不會影響到另一個模塊。對于復雜系統來說,這點尤其重要。

而具體到C語言,這種隔離的具體體現就是函數指針。

比如,我們要打開一個門,函數可以寫成:
void open_door(void)
{
    // action of open door
}

然后調用的時候我們只需要:

open_door();

就可以了。

引入隔離和指針后,我們就看不見函數的實現了,甚至看不到被調用的函數名,而是只需要知道通過指針進行訪問。

對于引用方來說代碼就變成:void (*p_open_door)(void);這個模塊在初始化的時候,初始化這個指針,調用的時候只需要:(*p_open_door)();這樣,我們可以在開始把void open_my_door(void)傳過去,后面又把void open_your_door(void)傳過去。而使用了函數指針的模塊不會有任何影響,這個模塊本身不會因為外部函數的改動而改動,甚至擺脫了linker的控制,因為這個模塊本身甚至不需要重新做link來指向更改后的函數地址。函數指針除了帶來了隔離的好處,另一個好處是靈活性,就像上面的例子,我們甚至可以在運行中動態的來改變函數指針,而被隔離模塊在不知不覺中就實現了多種不同的open door,而自己執行的代碼在binary層面并沒有任何改變,只是通過同一個函數指針調用該指針指向的函數。這個方法在軟件工程學界是得到了公認的一種做法,得到了極高的贊許和評價,對實際的軟件應用產生了十分深遠的影響。在小編見過的不少協議棧軟件中,這個理念用得特別的廣泛。給人的感覺就是函數指針的應用儼然已經成為了專業軟件的一個標配,沒有函數指針的代碼必定不是好代碼,沒有函數指針的代碼,必定是不專業的代碼,不懂函數指針的工程師,必定是很low的工程師。這個方法好是好,但是對于使用者來(非協議棧的開發者)說,會有一個比較麻煩的地方,就是代碼讀著讀著,一看到指針就不知道飛到哪兒去了。靜態代碼閱讀,根本無法了解代碼前世今生,來龍去脈。甚至極端的情況,一段函數指針滿天飛的代碼只能讓人暈頭轉向,感到天昏地暗,垂頭喪氣,昏昏欲睡,挫折不已。但是小編幾乎從來沒有看到過有專業的書籍、大咖或者文章指出這個問題。不知道是不是只是小編自己會覺得這個會是一個問題,難道是小編自己太菜了?嗚嗚嗚… …依稀記得以前上哲學課的時候學到的一些觀點,比如矛盾論竟然可以完美的在這里得到解釋,好與壞,白與黑,精華與糟粕就這樣完美的統一在一起了。

當然,我們的SDK USB協議棧是由專業的軟件團隊開發的,自然也不可避免的使用了這一理念,在帶來各種強大而精彩的功能的同時,也不可避免的引入了其弊端。所以我們是無法用靜態代碼閱讀的方式去快速了解這套軟件的。

小編的解決方法是觀察調用棧,幾個核心的調用棧被列出來后,整個軟件的運行體系就自然而然水落石出,山高月小。

調用棧分析的方法除了可以用在有函數指針的場景下,對于沒有函數指針的復雜軟件分析的場景也同樣適用,可以用海量的代碼中迅速看到函數之間的多層級聯調用關系,這是快速分析復雜軟件的很高效的方法。

這里小編列出了6個核心調用棧給大家參考,根據小編的實際使用體驗,這6個核心調用棧已經足以幫助小編解決新的USB host classk開發中的所有問題了。如果讀者有別的問題,也可以采用類似的方法來了解整個軟件體系的結構,這比直接閱讀代碼要高效太多太多。

核心調用棧1:在何處發起枚舉的控制傳輸?

f841e968-378a-11ec-82a8-dac502259ad0.png

核心調用棧2:在何處解析配置描述符?

f8b7a392-378a-11ec-82a8-dac502259ad0.png

核心調用棧3:Host event是如何回調回來的?

f96dcc6c-378a-11ec-82a8-dac502259ad0.png

核心調用棧4:什么時候打開系統的控制interface/pipe?

f9f11e32-378a-11ec-82a8-dac502259ad0.png

核心調用棧5:什么時候打開class的控制interface/pipe?

fa68696a-378a-11ec-82a8-dac502259ad0.png

核心調用棧6:什么時候打開class的數據interface/pipe?

fae05614-378a-11ec-82a8-dac502259ad0.png

實現的一些要點展示

在本章節中,將探討如何基于現有的USB host CDC class來實現USB host CCID class。本章節會展示一些關鍵點,也基本上是step by step的guide。

為了突出重點,有些不是很重要的細枝末節的地方并沒有講述,讀者如果有興趣可以參考本文對應的代碼工程獲取更多更詳細的信息

一、獲取設備描述符內容

獲取設備描述符的相關函數在USB_HostProcessCallback() in usb_host_devices.c

這里我們只需要加入內存打印語句,就可以把從device獲取到的描述符打印出來。

由于我們重用了USB host CDC的架構,這部分不需要做任何改動就可以直接進行枚舉。

相關的核心代碼如下:
case kStatus_DEV_GetDes8: /* process get 8 bytes descriptor result */
            … … 
            usb_echo("kStatus_DEV_GetDes8
");
            mem_dump_8(deviceInstance->deviceDescriptor, dataLength);
case kStatus_DEV_GetDes: /* process get full device descriptor result */
            … … 
            usb_echo("kStatus_DEV_GetDes
");
            mem_dump_8(deviceInstance->deviceDescriptor, dataLength);
            break;
case kStatus_DEV_GetCfg9: /* process get 9 bytes configuration result */
            … … 
            usb_echo("kStatus_DEV_GetCfg9
");
            mem_dump_8(configureDesc, dataLength);
case kStatus_DEV_GetCfg: /* process get configuration result */
            … … 
            usb_echo("kStatus_DEV_GetCfg
");
            mem_dump_8(deviceInstance->configurationDesc, dataLength);
運行后輸出結果:
Console output:
kStatus_DEV_GetDes8
0x20003fa8: 12 01 00 02 00 00 00 40 
kStatus_DEV_GetCfg9
0x20003fba: 09 02 5d 00 01 01 00 c0 
0x000000c7: 32 -- -- -- -- -- -- -- 
kStatus_DEV_GetCfg
0x20003fd0: 09 02 5d 00 01 01 00 c0 
0x20003fd8: 32 09 04 00 00 03 0b 00 
0x20003fe0: 00 03 36 21 10 01 01 02 
0x20003fe8: 01 00 00 00 fc 0d 00 00 
0x20003ff0: fc 0d 00 00 00 80 25 00 
0x20003ff8: 00 80 25 00 00 00 00 00 
0x20004000: 00 00 00 00 00 00 00 00 
0x20004008: 00 00 38 00 02 00 0f 01 
0x20004010: 00 00 00 00 00 00 00 01 
0x20004018: 07 05 81 02 40 00 00 07 
0x20004020: 05 02 02 40 00 00 07 05 
0x000000f7: 83 03 08 00 08 -- -- --
device not supported.

從這里我們可以看到從設備獲取的設備描述符和配置描述符,但是進一步顯示設備不支持

二、為什么設備不支持?

要得到答案,這個問題還是要研究一下的,這里小編就不繞彎子,直接公布答案了:

在USB_HostCdcEvent(), host_cdc.c,這里面會解析配置描述符的信息,看是不是CDC的class,因為我們接入的是CCID設備,而原始代碼是按照CDC class去解析,自然就會失敗。

解決的方式就是在這個函數里面做CCID class的解析就好了。
usb_status_t USB_HostCdcEvent(usb_device_handle deviceHandle,
                              usb_host_configuration_handle configurationHandle,
                              uint32_t event_code)
{
… … 
    switch (event_code)
    {
        case kUSB_HostEventAttach:
… … 
            for (interface_index = 0; interface_index < configuration->interfaceCount; ++interface_index)
            {
                hostInterface = &configuration->interfaceList[interface_index];
                id            = hostInterface->interfaceDesc->bInterfaceClass;
                if (id == USB_HOST_CCID_CLASS_CODE)                
                {                    
                    usb_echo("***ccid device detected. 
");
                    cdcDataInterfaceHandle = hostInterface;
                    cdcDeviceHandle      = deviceHandle;
                    break;
                }
            }
            
            if ((NULL != cdcDataInterfaceHandle) && (NULL != cdcDeviceHandle))
            {
                status = kStatus_USB_Success;
            }
            else
            {
                status = kStatus_USB_NotSupported;
            }
            break;

當我們在這里正確的識別到CCID class設備,返回kStatus_USB_Success,就不會出現設備不支持了。

此時的log輸出為:
Console output:
kStatus_DEV_GetDes8
0x20003fa8: 12 01 00 02 00 00 00 40 
kStatus_DEV_GetCfg9
0x20003fba: 09 02 5d 00 01 01 00 c0 
0x000000b6: 32 -- -- -- -- -- -- -- 
kStatus_DEV_GetCfg
0x20003fd0: 09 02 5d 00 01 01 00 c0 
0x20003fd8: 32 09 04 00 00 03 0b 00 
0x20003fe0: 00 03 36 21 10 01 01 02 
0x20003fe8: 01 00 00 00 fc 0d 00 00 
0x20003ff0: fc 0d 00 00 00 80 25 00 
0x20003ff8: 00 80 25 00 00 00 00 00 
0x20004000: 00 00 00 00 00 00 00 00 
0x20004008: 00 00 38 00 02 00 0f 01 
0x20004010: 00 00 00 00 00 00 00 01 
0x20004018: 07 05 81 02 40 00 00 07 
0x20004020: 05 02 02 40 00 00 07 05 
0x000000e6: 83 03 08 00 08 -- -- -- 
***ccid device detected.

可以看到,我們目前拿到了CCID的配置描述符,并且根據spec正確的識別到了CCID設備,這樣枚舉就過了。

是不是感覺很輕松?

三、CCID配置描述符解析

這里僅列出CCID配置描述符的結構。

重點是我們要知道,CCID class有一個interface,里面有3個EP,一個Bulk In,一個Bulk Out,一個Interrupt In,我們會根據這個信息在下一步調整class狀態機。

四、class狀態機分析

Class狀態機在USB_HostCdcTask()中實現。

先看看CDC的狀態機:

fbfb3d16-378a-11ec-82a8-dac502259ad0.png

與CDC相比,CCID只有一個interface,并且設備相關上層操作小編想獨立出來在另外的地方做,于是CCID的狀態機如下,灰色部分為跳過的部分。

fc708d8c-378a-11ec-82a8-dac502259ad0.png

五、打開interface和pipe

打開interface和pipe和操作在USB_HostCdcOpenDataInterface(), 位于文件usb_host_cdc.c中。

這里需要適配CCID的操作,去openBulk In, Bulk Out, Interrupt In pipe。注意這3個endpoint在同一個interface下面。
for (ep_index = 0; ep_index < interfaceHandle->epCount; ++ep_index)
    {
        usb_echo("ep_index = %x
", ep_index);
        
        ep_desc = interfaceHandle->epList[ep_index].epDesc;
        if (((ep_desc->bEndpointAddress & USB_DESCRIPTOR_ENDPOINT_ADDRESS_DIRECTION_MASK) ==
             USB_DESCRIPTOR_ENDPOINT_ADDRESS_DIRECTION_IN) &&
            ((ep_desc->bmAttributes & USB_DESCRIPTOR_ENDPOINT_ATTRIBUTE_TYPE_MASK) == USB_ENDPOINT_BULK))
        {
 … …
            status  = USB_HostOpenPipe(cdcInstance->hostHandle, &cdcInstance->inPipe, &pipeInit);
 … …
        }
        else if (((ep_desc->bEndpointAddress & USB_DESCRIPTOR_ENDPOINT_ADDRESS_DIRECTION_MASK) ==
                  USB_DESCRIPTOR_ENDPOINT_ADDRESS_DIRECTION_OUT) &&
                 ((ep_desc->bmAttributes & USB_DESCRIPTOR_ENDPOINT_ATTRIBUTE_TYPE_MASK) == USB_ENDPOINT_BULK))
        {
 … … 
            status = USB_HostOpenPipe(cdcInstance->hostHandle, &cdcInstance->outPipe, &pipeInit);
 … … 
        }
        else if (((ep_desc->bEndpointAddress & USB_DESCRIPTOR_ENDPOINT_ADDRESS_DIRECTION_MASK) ==
                  USB_DESCRIPTOR_ENDPOINT_ADDRESS_DIRECTION_IN) &&
                  ((ep_desc->bmAttributes & USB_DESCRIPTOR_ENDPOINT_ATTRIBUTE_TYPE_MASK) == USB_ENDPOINT_INTERRUPT))
        {
… … 
            status = USB_HostOpenPipe(cdcInstance->hostHandle, &cdcInstance->interruptPipe, &pipeInit);
……
    }

需要注意的是,USB協議棧會自動解析interface和endpoint,這里的數據結構是前面已經解析過的。我們需要在這里去識別Bulk In, Bulk Out, 以及Interrupt In。

相關的log:
Console log:***ccid device detected. 
device cdc attached:
pid=0x9cvid=0x1fc9 address=1
cdc device attached
 s - kUSB_HostCdcRunSetControlInterfaceDone 
--> USB_HostCdcOpenDataInterface 
ep_index = 0bulk in ep_index = 1bulk out ep_index = 2interrupt in 
 s - kUSB_HostCdcRunSetDataInterfaceDone

從log我們可以看到,我們已經成功的檢測到interface下面的3個EP了,Bulk In, Bulk Out,Interrupt In。

六、測試pipe的通信

既然pipe已經打開,下面我們就要測試一下pipe的通信了。

這里我們沿用了USB stack的task的做法,在一個無限loop里面去做處理,所以需要變量記錄狀態。

首先記錄狀態,代碼如下(位于函數USB_HostCdcTask()中):
case kUSB_HostCdcRunSetControlInterfaceDone:
            ... ...
            if (USB_HostCdcSetDataInterface(cdcInstance->classHandle, cdcInstance->dataInterfaceHandle, 0,
                                            USB_HostCdcControlCallback, &g_cdc) != kStatus_USB_Success)
            {
                usb_echo("set data interface error
");
            }
            … …            
            ccid_communication_ready();
            break;

然后我們就可以基于USB stack的API進行pipe通信了,相關代碼如下(位于函數ccid_app_task()中):

    if(flag_test == 0)
    {
        usb_echo("ccid_ready_for_communicatio 
");        USB_HostCdcDataSend(g_cdc.classHandle, "12345", 5, USB_CCID_BULK_OUT_Callback, &g_cdc);
    }
    else if(flag_test == 2)
    {        USB_HostCdcInterruptRecv(g_cdc.classHandle, buf, 8,USB_CCID_HID_Callback, &g_cdc);
    }
    else if(flag_test == 4)
    {        USB_HostCdcDataRecv(g_cdc.classHandle, buf, 8,USB_CCID_BULK_IN_Callback, &g_cdc);
    }

注意這里的收發API都是基于回調機制,收發完成后和app通過回調函數進行同步(通信)。

回調機制是一個非常優秀的機制(這同時也是小編前面吐槽的函數指針,又愛又恨),這樣避免了低效率的狀態輪詢。

完成相關的代碼后,接下來測試pipe,看看log輸出:

Console log:ep_index = 0
bulk in 
ep_index = 1
bulk out 
ep_index = 2
interrupt in 
ccid_ready_for_communicatio 
 s - kUSB_HostCdcRunSetDataInterfaceDone 
USB_CCID_BULK_OUT_Callback
USB_CCID_HID_Callback
USB_CCID_BULK_IN_Callback

這里我們可以看到回調機制已經正確觸發了。

這里可以看到,我們已經正確的觸發了Bulk In,Bulk Out以及Interrupt transfer。

七、關于新的class的開發和上層應用開發

在pipe的通信已經正確的建立后,class的開發和上層應用的開發,并沒有統一的模式。每個工程師很可能都有自己的想法去實現,這部分的實現,自由度可以很大。

對于CCID我們要做的主要工作是集成spec定義的消息,以及spec定義的相關的通信狀態機。這部分本文并不做重點討論,每個class都有自己的特點和定義,需要參考spec和應用場景去具體實現。

小編這里推薦盡量把具體的class處理的這部分相對于USB stack獨立出來,這樣系統的整體設計脈絡更加清晰一些,讓我們更能聚焦在新的USBhost class的開發,也便于軟件的長期開發和維護。

八、本文的相關代碼

本文的相關代碼可以從以下鏈接進行獲取,該代碼下載后可以直接編譯并且運行在i.MXRT1020 EVK上。

https://github.com/jiaguonxpcom/usb_host_ccid

小 結

本文基于i.MX RT1020平臺,向讀者展示了如何基于NXP SDK USB host來實現一個新的class,重點講述了相關pipe的建議。建立pipe通信是實現新的USB host的核心步驟。

希望本文能給需要做相關類似開發的讀者一些參考,避免少走彎路,而愉快的基于SDK USB協議棧完成相關的新任務的開發。
責任編輯:haq


聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • NXP
    NXP
    +關注

    關注

    60

    文章

    1287

    瀏覽量

    185009
  • usb
    usb
    +關注

    關注

    60

    文章

    7976

    瀏覽量

    265517
  • 驅動
    +關注

    關注

    12

    文章

    1848

    瀏覽量

    85468

原文標題:新添USB host class驅動開發

文章出處:【微信號:NXP_SMART_HARDWARE,微信公眾號:恩智浦MCU加油站】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    AT32F437 USB HOST RT-thread u盤讀寫不穩定是怎么回事?

    RT-thread usb host框架的問題,RT-thread 論壇上也有很多反饋usb host 此問題的,都是各顯神通,沒有最終定論。 希望咱雅特力可以給力些,看咱
    發表于 07-26 07:15

    請問ESP32-S3 USB HOST是否支持USB2.0?

    規格書介紹ESP32-S2指定USB HOST支持的USB1.1,ESP32-S3的USB HOST支持
    發表于 07-19 06:53

    STM32F407作USB host驅動聲卡,USB就會出錯的原因?

    同時作同步IN和OUT),USB就會出錯,具體出錯中斷信號為 未完成周期性傳輸中斷或Tx FIFO 空中斷。 因為ST 對USB host 提供的 audio class例程只有str
    發表于 07-04 06:56

    STM32F107VCT6使用USB HOST OTG掛載U盤連續讀寫文件錯誤是怎么回事?如何解決?

    最近研究STM32F107VCT6的USB HOST OTG驅動U盤,目前可以正確識別到U盤和掛載文件系統,但是聯系讀寫U盤內文件時會出現錯誤,導致沒法繼續讀取文件內容
    發表于 04-29 08:28

    stm32f429 USB Host HID鍵盤枚舉成功讀取鍵值失敗的原因?怎么解決?

    求助大神,手上一塊stm32f429的板子,作為USB host連接USB鍵盤,使用cubemx生成工程文件,在主循環內添加鍵值處理程序,現在的問題是通過串口打印調試信息顯示,
    發表于 04-28 07:18

    STM32F407 USB HOST HID部分鼠標鍵盤無法讀取數據的原因?

    ) { UNMASK_HOST_INT_CHH (num); USB_OTG_HC_Halt(pdev, num); CLEAR_HC_INT(hcreg , nak); pdev-&
    發表于 04-22 08:22

    ST-usb-host-hid庫如何解決usb掃碼槍這類的usb-hid-keyboard設備?

    用cube生成的usb-host-hid調試掃碼槍的時候只能支持小部分的usb掃碼槍,有好幾種掃碼槍的現象是:枚舉成功了,但是在獲取設備信息:USBH_HID_GetHIDDescriptor
    發表于 04-17 06:49

    stm32l4如何下載到usb host的固件庫?

    各位大神,請教下stm32l4如何下載到usb host的固件庫,還想請教下stm32有沒有開源的rndis和ecm代碼
    發表于 04-11 07:19

    請問USB CDC host怎么與多串口的從機通信?

    USB CDC host怎么與多串口的從機通信? 有一個從設備的USB虛擬了3個串口,作為主設備,如何分別識別出每個串口,分別于每個串口通信? 我的主設備使用的是官方單獨的USB標準庫
    發表于 04-11 06:11

    使用STM32F407 USB HOST遇到的疑問求解

    我用的是STM32F407 使用STM32FCUBE產生的工程 起重 USB_OTG_HS 選擇的是 Host Only SOF 和 VBUS 都沒勾選 USB_OTG_FS 選擇
    發表于 04-03 06:11

    需要用f4xx做usb vcp host端,請問有沒有辦法讓host端不輪詢讀呢?

    請教一下 我們需要用f4xx做usb vcp host端,用cubeide生成了usb host端的 virtual com port代碼工程,能夠正常識別插入的vcp設備,但是發現
    發表于 03-26 08:25

    請問stm32H743II usb HOST如何識別雙遙桿游戲手柄?

    stm32H743II usb HOST 如何識別 雙遙桿游戲手柄?北通usb游戲手柄插到PC上顯示是XBOX 360手柄,手柄上傳為14個字節數據,分別為0-7兩個遙桿的XY,8-9為手柄油門
    發表于 03-15 07:52

    使用STM32u575配置USB host讀取U盤功能,是使用適應M3的USB驅動還是必須使用M33的USB驅動

    使用STM32u575配置USB host 讀取U盤功能,可以使用適應M3的USB驅動嗎?還是必須使用M33的USB
    發表于 03-13 07:54

    如何實現STM32 USB host對另外的一個USB虛擬串口設備的通訊?

    如何實現STM32 USB host 對另外的一個USB 虛擬串口設備的通訊
    發表于 03-07 06:10

    什么是USB HOSTUSB Slave和USB OTG?它們之間有什么區別?

    什么是USB HOSTUSB Slave和USB OTG?它們之間有什么區別? USB(通用串行總線)是一種用于連接設備的通信接口標準,它
    的頭像 發表于 02-02 15:32 ?1.4w次閱讀
    主站蜘蛛池模板: 抽插H浊水H嫩B父皇| 国产日韩久久久精品影院首页| 国产国产乱老熟视频网站| 欧美乱子YELLOWVIDEO| 2022精品福利在线小视频| 久久99re66热这里只有精品| 亚洲精品高清在线观看| 国产精品亚洲国产三区| 天天狠狠色综合图片区| 国产成人a一在线观看| 视频成人永久免费看| 高h超辣bl文| 爽爽窝窝午夜精品一区二区| 动漫美女的阴| 学生小泬无遮挡女HD| 国自产拍 高清精品| 野花韩国在线观看| 久久婷婷五月综合色丁香花| 中文字幕无码一区二区免费| 免费被靠视频动漫| 激情床戏揉胸吃胸视频| 亚洲欧美日韩另类精品一区二区三区 | 天美传媒MV高清免费看| 啊好大好厉害好爽真骚| 日韩欧美高清一区| 国产精品系列在线观看| 亚洲色无码播放| 老师给美女同学开嫩苞| gv肉片视频免费观看| 日韩欧美精品有码在线播放免费| 岛国电影网址| 新新电影理论中文字幕| 99久久免费国产精精品| 欧美同志高清vivoeso| 公和我做好爽添厨房中文字幕| 四虎永久在线精品国产免费| 国产亚洲精品久久久闺蜜| 幼儿交1300部一区二区| 欧洲精品一区二区不卡观看| 国产精品久久久精品a级小说| 亚洲视频国产在线精品|