前言 理解 STM32F103 上 USB 模塊的端點資源,靈活在應用中的配置。 問題 某客戶使用 STM32F103 的 USB 模塊做設備時和上位機 PC 連接時碰到一個問題:PC 端驅動已經固定好,是對下位機 USB 設備上的地址編號為 0x0A 和 0x0B 的兩個端點通信,從 0x0A 端點讀取數據,向 0x0B 端點寫數據。而 STM32F103 的 USB 模塊只有 8 個雙向端點,能否支持這樣的尋址。 1. 問題調研 我們先來看看 STM32F103 上的 USB 端點資源。從 STM32F103 參考手冊(RM0008)可知,一共有 8 個雙向端點,對應 8個寄存器來控制其屬性和表征其狀態。如下圖,可知每一對端點必須配置成相同 的端點地址,這個地址位域是 4 位,取值從 0x0 到 0x0F 范圍。 
和以下摘錄的 USB 規范符合: 
客戶使用的是 STSW-STM32121(STM32F10x, STM32L1xx and STM32F3xx 全速 USB 設備庫), 那么應該修改哪些代碼呢? 2. 問題分析 首先,USB 設備通過端點描述符向主機 PC 報告它所使用的端點有哪些:每個端點的地址(即 USB 規 范里,以及參考手冊的寄存器中規定的那 4 位地址域)、傳輸方向、傳輸類型、最大包長等。以 STSW-STM32121 庫中的 Mass_Storage 例程為例,需要把<usb_desc.c>中的端點描述符做如下修改: 0x0A 地址的端點作為 IN端點(PC 從它讀取數據),0x0B 地址的端點作為 OUT 端點(PC 向它寫數 據)。 const uint8_t MASS_ConfigDescriptor[MASS_SIZ_CONFIG_DESC] = { 。。。。。。 /* 18 */ 0x07, /*Endpoint descriptor length = 7*/ 0x05, /*Endpoint descriptor type */ 0x8A, //0x81, /*Endpoint address (IN, address 1) */ 0x02, /*Bulk endpoint type */ 0x40, /*Maximum packet size (64 bytes) */ 0x00, 0x00, /*Polling interval in milliseconds */ /* 25 */ 0x07, /*Endpoint descriptor length = 7 */ 0x05, /*Endpoint descriptor type */ 0x0B, //0x02, /*Endpoint address (OUT, address 2) */ 0x02, /*Bulk endpoint type */ 0x40, /*Maximum packet size (64 bytes) */ 0x00, 0x00 /*Polling interval in milliseconds*/ /*32*/ }; 接下來就是考慮使用 STM32F103 USB 模塊提供的 8 個雙向端點的哪個端點了。我們剛才從參考手冊 關于寄存器描述的截圖中看到,每一對端點具有相同的地址。在庫函數里,對端點寄存器的地址位域 的操作在這里:

這個函數的名稱是“設置(USB)設備地址”,但是其中除了最后一句是在設置 USB 設備的地址,前面 的 for 循環是在設置該設備內的端點地址。
從以上綠色標注的代碼段可以看到,庫代碼固定給 1 號端點”0x01”這個地址,2 號端點”0x02”這個地址, 以此類推。這里的”1 號”、”2 號”指的是端點的編號,對應的就是之前提到的 8 個寄存器的編號,即下圖 中的 n=0~7。n 在這里就是端點的編號。 
那么在這個應用中,需要用到地址為 0x0A 和 0x0B 的兩個端點,但是端點編號最多只能到 7,因此需 要修改庫代碼中關于端點地址設置的地方如下:這里,我們使用編號為 1和 2 的兩個端點。 為啥不用編號 0?因為編號 0 默認給雙向 0 端點,即用于控制傳輸的 0 端點。 為啥用 2 個編號?因為這里需要 2 個不同的端點地址,必須用 2 個編號。一個編號對應的 2 個 端點必須共享同樣的端點地址。 】 
既然這里指定了使用編號 1 和編號 2 的端點,那么需要在<usb_conf.h>中設置這兩個端點的硬件收發緩沖區地址 
當然如果你很任性,一定要使用編號為 6和 7 的端點,也可以,那么代碼就如下修改: void SetDeviceAddress(uint8_t Val) { /* set address in every used endpoint */ _SetEPAddress((uint8_t)0, (uint8_t)0); _SetEPAddress((uint8_t)6, (uint8_t)0x0A); _SetEPAddress((uint8_t)7, (uint8_t)0x0B); _SetDADDR(Val | DADDR_EF); /* set device address and enable function */ } 相應地,需要在<usb_conf.h>中指明編號為 6 和 7 的這兩個端點的硬件收發緩沖區地址。那么如法炮 制做如下修改,就可以了嗎?就可以了嗎?? /* EP0 */ /* rx/tx buffer base address */ #define ENDP0_RXADDR (0x18) #define ENDP0_TXADDR (0x58) /* 6 號端點,IN 端點,發送緩沖區如下 */ #define ENDP6_TXADDR (0x98) /* 7 號端點,OUT 端點,接收緩沖區如下 */ #define ENDP7_RXADDR (0xD8) 答案是否定的!以下的代碼才 OK。欲知詳情,請參考下《STM32F103 上 USB 模塊的包緩沖區詳解》或相關應用文字。 /* EP0 */
/* rx/tx buffer base address */ #define ENDP0_RXADDR (0x40) #define ENDP0_TXADDR (0x80) /* 6 號端點,IN 端點,發送緩沖區如下 */ #define ENDP6_TXADDR (0xC0) /* 7 號端點,OUT 端點,接收緩沖區如下 */ #define ENDP7_RXADDR (0x100)
【本文摘于ST MCU官網,供分享交流】 |