日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

[单片机芯片]CH32V203的USB1 HID库调试经验分享

發布時間:2023/12/20 编程问答 51 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [单片机芯片]CH32V203的USB1 HID库调试经验分享 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

背景:? ?? ?

今天要水的帖子是關于使用CH32V203替代舊STM32F103實現USB霍爾搖桿的項目。關于調試經驗啥的,主要分享CH32V203的USB使用。這個項目最開始是使用邁來芯的方案,但是價格30RMB/PCS,也是芯片漲價時候漲上去的,后來替換了同廠不同方案,這個方案芯片對磁場靈敏度很高,高得離譜,迫不得已更換了磁鐵,后來總算滿足了,2021年時候發現了國產替代方案,矽睿科技的QMC磁力計方案,本來想推給老板更換的,然而老板覺得不成熟,時至今日,老板實在用不起國外的方案了,機會來了,我對WCH的CH32V RISC-V系列MCU比較熟悉,今天我們就開始國產化替代過程吧!


關于硬件
? ?? ?由于需要兼容STM32的USB,所以這里使用了PA11(DM)和PA12(DP),對應CH32V203的USB1,我看USB Device 的實現跟STM32 的HAL庫居然有點一致了,之前一致是在中斷回調函數中枚舉的。其實我更喜歡第二種方式,可能效率會更高。不過USB2好像還是原來的方式。



軟件設計

我是在懶得新建工程,移植USB Library了,干脆直接在官方工程上改,我用的是EXAM\USB\USBD\CompositeKM,USBD目錄的工程用的USB1口對應PA11(DM)和PA12(DP),大家不要搞錯了,我就搞錯過,還寄了芯片給WCH的FAE調試,后來發現我自己用錯IO了。

1.時鐘初始化配置,
初始化需要配置RCC_USBCLKConfig,以及RCC_APB1PeriphClockCmd,而在例程中是使用Set_USBConfig調用實現的,如下代碼:

void Set_USBConfig( ){RCC_USBCLKConfig(RCC_USBCLKSource_PLLCLK_Div3);RCC_APB1PeriphClockCmd(RCC_APB1Periph_USB, ENABLE);? ?? ?? ?? ?? ?? ?? ?? ???}

大家都知道,USB 設備一般都是使用48MHz的時鐘,而例程的PLLCLK是144Mhz,所以這里是3分頻,另外需要使能一下APB1的外設時鐘。

2.USB端點配置、描述符配置標準請求的實現等。

端點地址、大小,描述符,各類請求的實現是通過以下函數實現:

void USB_Init(void){pInformation = &Device_Info;pInformation->ControlState = 2;pProperty = &Device_Property;pUser_Standard_Requests = &User_Standard_Requests;pProperty->Init();}

如果你定位到pInformation,pProperty,以及pUser_Standard_Requests的定義,可以發現,
pInformation的定義其實是DEVICE_INFO這樣子的,這其實是一些USB請求事務信息。

typedef struct _DEVICE_INFO{uint8_t USBbmRequestType;? ?? ? /* bmRequestType */uint8_t USBbRequest;? ?? ?? ?? ?/* bRequest */uint16_t_uint8_t USBwValues;? ?? ?? ?/* wValue */uint16_t_uint8_t USBwIndexs;? ?? ?? ?/* wIndex */uint16_t_uint8_t USBwLengths;? ?? ???/* wLength */uint8_t ControlState;? ?? ?? ???/* of type CONTROL_STATE */uint8_t Current_Feature;uint8_t Current_Configuration;? ?/* Selected configuration */uint8_t Current_Interface;? ?? ? /* Selected interface of current configuration */uint8_t Current_AlternateSetting;/* Selected Alternate Setting of currentinterface*/ENDPOINT_INFO Ctrl_Info;}DEVICE_INFO;

而pProperty的定義更復雜以下,DEVICE_PROP定義如下,如果說DEVICE_INFO是數據結構的話,我覺得DEVICE_PROP更像是實現,因為里面都是一些函數指針,和一些必要的指針和變量,囊括了USB 設備的枚舉過程:端點的配置,各種請求類,相關描述符的獲取。

typedef struct _DEVICE_PROP{void (*Init)(void);? ?? ???/* Initialize the device */void (*Reset)(void);? ?? ? /* Reset routine of this device */void (*Process_Status_IN)(void);void (*Process_Status_OUT)(void);RESULT (*Class_Data_Setup)(uint8_t RequestNo);RESULT (*Class_NoData_Setup)(uint8_t RequestNo);RESULT??(*Class_Get_Interface_Setting)(uint8_t Interface, uint8_t AlternateSetting);uint8_t* (*GetDeviceDescriptor)(uint16_t Length);uint8_t* (*GetConfigDescriptor)(uint16_t Length);uint8_t* (*GetStringDescriptor)(uint16_t Length);void* RxEP_buffer;uint8_t MaxPacketSize;}DEVICE_PROP;


在這里我們先來改描述符的獲取吧,

??uint8_t* (*GetDeviceDescriptor)(uint16_t Length);uint8_t* (*GetConfigDescriptor)(uint16_t Length);uint8_t* (*GetStringDescriptor)(uint16_t Length);

這三個指針函數是獲取設備描述符,配置描述符集合,字符串描述符。

設備描述符:以下是我雷蛇鼠標的:?


配置描述符集合包含了配置描述符,接口描述符,HID描述符(如果是HID),端點描述符:


上圖只是接口0的,共有三個接口,其實都是同理可得。
HID每個接口幾乎都有自己的報表描述符。

在代碼中如何實現的呢?
Device_Property中的USBD_Data_Setup里面有個USBD_GetReportDescriptor請求,這是獲取報表描述符的。

ONE_DESCRIPTOR Report_Descriptor[3] ={{(uint8_t*)USBD_JoystickRepDesc, USBD_SIZE_REPORT_DESC_JS},{(uint8_t*)USBD_MouseRepDesc, USBD_SIZE_REPORT_DESC_MS},{(uint8_t*)USBD_VendorRepDesc, USBD_SIZE_REPORT_DESC_VD},};

如果設備描述符、配置描述符、接口描述符、HID描述符、端點描述符、報表描述符沒有問題的話,是不是就一定可以枚舉成功了呢,是不是就可以高枕無憂了呢,然而事實并非如此。還需要注意一下端點地址和大小的配置。
端點地址和緩存大小其實也是在Device_Property中的USBD_Reset復位中的,

void USBD_Reset(void){pInformation->Current_Configuration = 0;pInformation->Current_Feature = USBD_ConfigDescriptor[7];pInformation->Current_Interface = 0;SetBTABLE(BTABLE_ADDRESS);SetEPType(ENDP0, EP_CONTROL);SetEPTxStatus(ENDP0, EP_TX_STALL);SetEPRxAddr(ENDP0, ENDP0_RXADDR);SetEPTxAddr(ENDP0, ENDP0_TXADDR);Clear_Status_Out(ENDP0);SetEPRxCount(ENDP0, Device_Property.MaxPacketSize);SetEPRxValid(ENDP0);_ClearDTOG_RX(ENDP0);_ClearDTOG_TX(ENDP0);SetEPType(ENDP1, EP_INTERRUPT);SetEPRxAddr(ENDP1, ENDP1_RXADDR);SetEPTxAddr(ENDP1, ENDP1_TXADDR);SetEPTxStatus(ENDP1, EP_TX_NAK);Clear_Status_Out(ENDP1);SetEPRxCount(ENDP1, Device_Property.MaxPacketSize);SetEPRxValid(ENDP1);_ClearDTOG_TX(ENDP1);_ClearDTOG_RX(ENDP1);SetEPType(ENDP2, EP_INTERRUPT);SetEPTxAddr(ENDP2, ENDP2_TXADDR);SetEPTxStatus(ENDP2, EP_TX_NAK);_ClearDTOG_TX(ENDP2);_ClearDTOG_RX(ENDP2);SetEPType(ENDP3, EP_INTERRUPT);SetEPTxAddr(ENDP3, ENDP3_TXADDR);SetEPTxStatus(ENDP3, EP_TX_NAK);_ClearDTOG_TX(ENDP3);_ClearDTOG_RX(ENDP3);SetDeviceAddress(0);bDeviceState = ATTACHED;}

地址是偏移地址,需要根據端點描述符中的最大Size來配置并偏移。

如果你的設備成功枚舉,這時候HID還不能動的,需要實現端點數據的上報,如下:

uint8_t USBD_ENDPx_DataUp( uint8_t endp, uint8_t *pbuf, uint16_t len ){if( endp == ENDP1 ){if (USBD_Endp1_Busy){return USB_ERROR;}USB_SIL_Write( EP1_IN, pbuf, len );USBD_Endp1_Busy = 1;SetEPTxStatus( ENDP1, EP_TX_VALID );}else if( endp == ENDP2 ){if (USBD_Endp2_Busy){return USB_ERROR;}USB_SIL_Write( EP2_IN, pbuf, len );USBD_Endp2_Busy = 1;SetEPTxStatus( ENDP2, EP_TX_VALID );}else if( endp == ENDP3 ){if (USBD_Endp3_Busy){return USB_ERROR;}USB_SIL_Write( EP3_IN, pbuf, len );USBD_Endp3_Busy = 1;SetEPTxStatus( ENDP3, EP_TX_VALID );}else{return USB_ERROR;}return USB_SUCCESS;}

依葫蘆畫瓢,根據端點不同改一下即可實現不同的端點數據上報。
最后,希望大家樣機調試成功!!!
---------------------
作者:lilijin1995
鏈接:https://bbs.21ic.com/icview-3271062-1-1.html
來源:21ic.com
此文章已獲得原創/原創獎標簽,著作權歸21ic所有,任何人未經允許禁止轉載。

?

總結

以上是生活随笔為你收集整理的[单片机芯片]CH32V203的USB1 HID库调试经验分享的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。