本帖最后由 lilijin1995 于 2022-12-13 10:45 编辑
序:
其实这样一个设备我们在ch32v103上实现过,但移植到STM32 HAL库上面,还是发现很多问题的,现在就移植过程给大家分享一下在基于STM32 HAL库实现鼠标键盘摇杆的USB复合设备的实现过程,由于楼主水平有限,文档和视频中难免有出错和讲得不好的地方,欢迎各位读者和观众善意地提出意见和建议,谢谢!
实验目的:
我们要实现一个具备摇杆、模拟鼠标和键盘功能的USB HID,通过USB接口插入PC主机,可以在主机测试通过,但仍然会有一下bug,至于什么bug就先不剧透了。
实验材料:
使用我们自己设计的MiniGamepad板子如下图,我们这款板子是为了测试学习方便用的。
软件设计:
目标是实现Joystick、Mouse和Keyboard的组合,即把三个设备组合成一个设备,通过按键SW2按下依次切换成Joystick、Mouse和Keyboard设备,而WS2812灯珠则通过红绿蓝指示切换三种不同的设备。
下面分8个步骤来实现HID的枚举。
__ALIGN_BEGIN static uint8_t CUSTOM_HID_ReportDesc_FS[USBD_CUSTOM_HID_REPORT_DESC_SIZE] __ALIGN_END =
{
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x04, // USAGE (Joystick)
0xa1, 0x01, // COLLECTION (Application)
0xa1, 0x02, // COLLECTION (Logical)
0x09, 0x30, // USAGE (X)
0x09, 0x31, // USAGE (Y)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255)
0x35, 0x00, // PHYSICAL_MINIMUM (0)
0x46, 0xff, 0x00, // PHYSICAL_MAXIMUM (255)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x02, // REPORT_COUNT (2)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x05, 0x09, // USAGE_PAGE (Button)
0x19, 0x01, // USAGE_MINIMUM (Button 1)
0x29, 0x08, // USAGE_MAXIMUM (Button 8)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x95, 0x08, // REPORT_COUNT (8)
0x75, 0x01, // REPORT_SIZE (1)
0x81, 0x02, // INPUT (Data,Var,Abs)
0xc0, // END_COLLECTION
0xc0 // END_COLLECTION
};
/* USER CODE BEGIN PRIVATE_VARIABLES */
__ALIGN_BEGIN static uint8_t Mouse_ReportDesc_FS[USBD_MOUSE_REPORT_DESC_SIZE] __ALIGN_END =
{
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x02, // USAGE (Mouse)
0xa1, 0x01, // COLLECTION (Application)
0x09, 0x01, // USAGE (Pointer)
0xa1, 0x00, // COLLECTION (Physical)
0x05, 0x09, // USAGE_PAGE (Button)
0x19, 0x01, // USAGE_MINIMUM (Button 1)
0x29, 0x03, // USAGE_MAXIMUM (Button 3)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x95, 0x03, // REPORT_COUNT (3)
0x75, 0x01, // REPORT_SIZE (1)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x05, // REPORT_SIZE (5)
0x81, 0x03, // INPUT (Cnst,Var,Abs)
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x30, // USAGE (X)
0x09, 0x31, // USAGE (Y)
0x09, 0x38, // USAGE (Wheel)
0x15, 0x81, // LOGICAL_MINIMUM (-127)
0x25, 0x7f, // LOGICAL_MAXIMUM (127)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x03, // REPORT_COUNT (3)
0x81, 0x06, // INPUT (Data,Var,Rel)
0xc0, // END_COLLECTION
0xc0 // END_COLLECTION
};
__ALIGN_BEGIN static uint8_t KEY_ReportDesc_FS[USBD_KEY_REPORT_DESC_SIZE] __ALIGN_END =
{
/* USER CODE BEGIN 0 */
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x06, // USAGE (Keyboard)
0xa1, 0x01, // COLLECTION (Application)
0x05, 0x07, // USAGE_PAGE (Keyboard)
0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl)
0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x75, 0x01, // REPORT_SIZE (1)
0x95, 0x08, // REPORT_COUNT (8)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x08, // REPORT_SIZE (8)
0x81, 0x03, // INPUT (Cnst,Var,Abs)
0x95, 0x05, // REPORT_COUNT (5)
0x75, 0x01, // REPORT_SIZE (1)
0x05, 0x08, // USAGE_PAGE (LEDs)
0x19, 0x01, // USAGE_MINIMUM (Num Lock)
0x29, 0x05, // USAGE_MAXIMUM (Kana)
0x91, 0x02, // OUTPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x03, // REPORT_SIZE (3)
0x91, 0x03, // OUTPUT (Cnst,Var,Abs)
0x95, 0x06, // REPORT_COUNT (6)
0x75, 0x08, // REPORT_SIZE (8)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0xFF, // LOGICAL_MAXIMUM (255)
0x05, 0x07, // USAGE_PAGE (Keyboard)
0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated))
0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application)
0x81, 0x00, // INPUT (Data,Ary,Abs)
/* USER CODE END 0 */
0xC0 /* END_COLLECTION */
};
typedef struct _USBD_CUSTOM_HID_Itf
{
uint8_t *jReport;
uint8_t *mReport;
uint8_t *kReport;
int8_t (* Init)(void);
int8_t (* DeInit)(void);
int8_t (* OutEvent)(uint8_t event_idx, uint8_t state);
} USBD_CUSTOM_HID_ItfTypeDef;
USBD_CUSTOM_HID_ItfTypeDef USBD_CustomHID_fops_FS =
{
CUSTOM_HID_ReportDesc_FS,
Mouse_ReportDesc_FS,
KEY_ReportDesc_FS,
CUSTOM_HID_Init_FS,
CUSTOM_HID_DeInit_FS,
CUSTOM_HID_OutEvent_FS
};
__ALIGN_BEGIN static uint8_t USBD_CUSTOM_HID_CfgFSDesc[USB_CUSTOM_HID_CONFIG_DESC_SIZ] __ALIGN_END =
{
0x09, /* bLength: Configuration Descriptor size */
USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */
USB_CUSTOM_HID_CONFIG_DESC_SIZ,
/* wTotalLength: Bytes returned */
0x00,
0x03, /*bNumInterfaces: 1 interface*/
0x01, /*bConfigurationValue: Configuration value*/
0x00, /*iConfiguration: Index of string descriptor describing the configuration*/
0x80, /*bmAttributes: bus powered */
0x32, /*MaxPower 100 mA: this current is used for detecting Vbus*/
//----------- Descriptor of Joystick interface ---------------------//
/* 09 */
0x09, /*bLength: Interface Descriptor size*/
USB_DESC_TYPE_INTERFACE,/*bDescriptorType: Interface descriptor type*/
0x00, /*bInterfaceNumber: Number of Interface*/
0x00, /*bAlternateSetting: Alternate setting*/
0x01, /*bNumEndpoints*/
0x03, /*bInterfaceClass: CUSTOM_HID*/
0x00, /*bInterfaceSubClass : 1=BOOT, 0=no boot*/
0x00, /*nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse*/
0, /*iInterface: Index of string descriptor*/
//-----------HID Descriptor of Joystick ---------------------//
/* 18 */
0x09, /*bLength: CUSTOM_HID Descriptor size*/
CUSTOM_HID_DESCRIPTOR_TYPE, /*bDescriptorType: CUSTOM_HID*/
0x11, /*bCUSTOM_HIDUSTOM_HID: CUSTOM_HID Class Spec release number*/
0x01,
0x00, /*bCountryCode: Hardware target country*/
0x01, /*bNumDescriptors: Number of CUSTOM_HID class descriptors to follow*/
0x22, /*bDescriptorType*/
USBD_CUSTOM_HID_REPORT_DESC_SIZE,/*wItemLength: Total length of Report descriptor*/
0x00,
//----------- Descriptor of Joystick endpoints ---------------------//
/* 27 */
0x07, /*bLength: Endpoint Descriptor size*/
USB_DESC_TYPE_ENDPOINT, /*bDescriptorType:*/
CUSTOM_HID_EPIN_ADDR, /*bEndpointAddress: Endpoint Address (IN)*/
0x03, /*bmAttributes: Interrupt endpoint*/
CUSTOM_HID_EPIN_SIZE, /*wMaxPacketSize: 2 Byte max */
0x00,
CUSTOM_HID_FS_BINTERVAL, /*bInterval: Polling Interval */
/* 34 */
//----------- Descriptor of Mouse interface ---------------------//
0x09, /*bLength: Interface Descriptor size*/
USB_DESC_TYPE_INTERFACE,/*bDescriptorType: Interface descriptor type*/
0x01, /*bInterfaceNumber: Number of Interface*/
0x00, /*bAlternateSetting: Alternate setting*/
0x01, /*bNumEndpoints*/
0x03, /*bInterfaceClass: CUSTOM_HID*/
0x01, /*bInterfaceSubClass : 1=BOOT, 0=no boot*/
0x02, /*nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse*/
0, /*iInterface: Index of string descriptor*/
//----------- Descriptor of Mouse CUSTOM_HID ---------------------//
/* 43 */
0x09, /*bLength: CUSTOM_HID Descriptor size*/
CUSTOM_HID_DESCRIPTOR_TYPE, /*bDescriptorType: CUSTOM_HID*/
0x11, /*bCUSTOM_HIDUSTOM_HID: CUSTOM_HID Class Spec release number*/
0x01,
0x00, /*bCountryCode: Hardware target country*/
0x01, /*bNumDescriptors: Number of CUSTOM_HID class descriptors to follow*/
0x22, /*bDescriptorType*/
USBD_MOUSE_REPORT_DESC_SIZE,/*wItemLength: Total length of Report descriptor*/
0x00,
//----------- Descriptor of Mouse endpoints ---------------------//
/* 52 */
0x07, /*bLength: Endpoint Descriptor size*/
USB_DESC_TYPE_ENDPOINT, /*bDescriptorType:*/
MOUSE_HID_EPIN_ADDR, /*bEndpointAddress: Endpoint Address (IN)*/
0x03, /*bmAttributes: Interrupt endpoint*/
MOUSE_HID_EPIN_SIZE, /*wMaxPacketSize: 2 Byte max */
0x00,
CUSTOM_HID_FS_BINTERVAL, /*bInterval: Polling Interval */
/* 59 */
//----------- Descriptor of KeyBoard interface ---------------------//
0x09, /*bLength: Interface Descriptor size*/
USB_DESC_TYPE_INTERFACE,/*bDescriptorType: Interface descriptor type*/
0x02, /*bInterfaceNumber: Number of Interface*/
0x00, /*bAlternateSetting: Alternate setting*/
0x01, /*bNumEndpoints*/
0x03, /*bInterfaceClass: CUSTOM_HID*/
0x00, /*bInterfaceSubClass : 1=BOOT, 0=no boot*/
0x00, /*nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse*/
0, /*iInterface: Index of string descriptor*/
//----------- Descriptor of KeyBoard CUSTOM_HID ---------------------//
/* 68 */
0x09, /*bLength: CUSTOM_HID Descriptor size*/
CUSTOM_HID_DESCRIPTOR_TYPE, /*bDescriptorType: CUSTOM_HID*/
0x11, /*bCUSTOM_HIDUSTOM_HID: CUSTOM_HID Class Spec release number*/
0x01,
0x00, /*bCountryCode: Hardware target country*/
0x01, /*bNumDescriptors: Number of CUSTOM_HID class descriptors to follow*/
0x22, /*bDescriptorType*/
USBD_KEY_REPORT_DESC_SIZE,/*wItemLength: Total length of Report descriptor*/
0x00,
//----------- Descriptor of KeyBoard endpoints ---------------------//
/* 77 */
0x07, /*bLength: Endpoint Descriptor size*/
USB_DESC_TYPE_ENDPOINT, /*bDescriptorType:*/
KEY_HID_EPIN_ADDR, /*bEndpointAddress: Endpoint Address (IN)*/
0x03, /*bmAttributes: Interrupt endpoint*/
KEY_HID_EPIN_SIZE, /*wMaxPacketSize: 2 Byte max */
0x00,
CUSTOM_HID_FS_BINTERVAL, /*bInterval: Polling Interval */
/* 84 */
};
#define USBD_MAX_NUM_INTERFACES 3
static uint8_t USBD_CUSTOM_HID_Setup(USBD_HandleTypeDef *pdev,
USBD_SetupReqTypedef *req)
{
USBD_CUSTOM_HID_HandleTypeDef *hhid = (USBD_CUSTOM_HID_HandleTypeDef *)pdev->pClassData;
uint16_t len = 0U;
uint8_t *pbuf = NULL;
uint16_t status_info = 0U;
uint8_t ret = USBD_OK;
switch (req->bmRequest & USB_REQ_TYPE_MASK)
{
case USB_REQ_TYPE_STANDARD:
switch (req->bRequest)
{
case USB_REQ_GET_DESCRIPTOR:
if (req->wValue >> 8 == CUSTOM_HID_REPORT_DESC)
{
if (req->wIndex == 0)
{
len = MIN(USBD_CUSTOM_HID_REPORT_DESC_SIZE, req->wLength);
pbuf = ((USBD_CUSTOM_HID_ItfTypeDef *)pdev->pUserData)->jReport;
}else if (req->wIndex == 1){//mouse
len = MIN(USBD_MOUSE_REPORT_DESC_SIZE, req->wLength);
pbuf = ((USBD_CUSTOM_HID_ItfTypeDef *)pdev->pUserData)->mReport;
}else if (req->wIndex == 2){//
len = MIN(USBD_KEY_REPORT_DESC_SIZE, req->wLength);
pbuf = ((USBD_CUSTOM_HID_ItfTypeDef *)pdev->pUserData)->kReport;
}
}
else
{
if (req->wValue >> 8 == CUSTOM_HID_DESCRIPTOR_TYPE)
{
pbuf = USBD_CUSTOM_HID_Desc;
len = MIN(USB_CUSTOM_HID_DESC_SIZ, req->wLength);
}
}
USBD_CtlSendData(pdev, pbuf, len);
break;
}
return ret;
}
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x00 , PCD_SNG_BUF, 0x40);
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x80 , PCD_SNG_BUF, 0x80);
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , CUSTOM_HID_EPIN_ADDR , PCD_SNG_BUF, 0xC0);
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , CUSTOM_HID_EPOUT_ADDR , PCD_SNG_BUF, 0x100);
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , MOUSE_HID_EPIN_ADDR , PCD_SNG_BUF, 0x140);
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , KEY_HID_EPIN_ADDR , PCD_SNG_BUF, 0x180);
static uint8_t USBD_CUSTOM_HID_Init(USBD_HandleTypeDef *pdev,uint8_t cfgidx)
{
uint8_t ret = 0U;
USBD_CUSTOM_HID_HandleTypeDef *hhid;
/* Open EP IN */
USBD_LL_OpenEP(pdev, CUSTOM_HID_EPIN_ADDR, USBD_EP_TYPE_INTR,CUSTOM_HID_EPIN_SIZE);
pdev->ep_in[CUSTOM_HID_EPIN_ADDR & 0xFU].is_used = 1U;
/* Open EP OUT */
USBD_LL_OpenEP(pdev, CUSTOM_HID_EPOUT_ADDR, USBD_EP_TYPE_INTR,CUSTOM_HID_EPOUT_SIZE);
pdev->ep_out[CUSTOM_HID_EPOUT_ADDR & 0xFU].is_used = 1U;
/* Open MOUSE EP IN */
USBD_LL_OpenEP(pdev, MOUSE_HID_EPIN_ADDR, USBD_EP_TYPE_INTR,MOUSE_HID_EPIN_SIZE);
pdev->ep_in[MOUSE_HID_EPIN_ADDR & 0xFU].is_used = 1U;
/* Open KEY EP IN */
USBD_LL_OpenEP(pdev, KEY_HID_EPIN_ADDR, USBD_EP_TYPE_INTR,KEY_HID_EPIN_SIZE);
pdev->ep_in[KEY_HID_EPIN_ADDR & 0xFU].is_used = 1U;
pdev->pClassData = USBD_malloc(sizeof(USBD_CUSTOM_HID_HandleTypeDef));
if (pdev->pClassData == NULL)
{
ret = 1U;
}
else
{
hhid = (USBD_CUSTOM_HID_HandleTypeDef *) pdev->pClassData;
hhid->state = CUSTOM_HID_IDLE;
((USBD_CUSTOM_HID_ItfTypeDef *)pdev->pUserData)->Init();
/* Prepare Out endpoint to receive 1st packet */
USBD_LL_PrepareReceive(pdev, CUSTOM_HID_EPOUT_ADDR, hhid->Report_buf,
USBD_CUSTOMHID_OUTREPORT_BUF_SIZE);
}
return ret;
}
static uint8_t USBD_CUSTOM_HID_DeInit(USBD_HandleTypeDef *pdev,
uint8_t cfgidx)
{
/* Close CUSTOM_HID EP IN */
USBD_LL_CloseEP(pdev, CUSTOM_HID_EPIN_ADDR);
pdev->ep_in[CUSTOM_HID_EPIN_ADDR & 0xFU].is_used = 0U;
/* Close CUSTOM_HID EP OUT */
USBD_LL_CloseEP(pdev, CUSTOM_HID_EPOUT_ADDR);
pdev->ep_out[CUSTOM_HID_EPOUT_ADDR & 0xFU].is_used = 0U;
/* Close MOUSE_HID EP IN */
USBD_LL_CloseEP(pdev, MOUSE_HID_EPIN_ADDR);
pdev->ep_in[MOUSE_HID_EPIN_ADDR & 0xFU].is_used = 0U;
/* Close JOYSTICK_HID EP IN */
USBD_LL_CloseEP(pdev, KEY_HID_EPIN_ADDR);
pdev->ep_in[KEY_HID_EPIN_ADDR & 0xFU].is_used = 0U;
/* FRee allocated memory */
if (pdev->pClassData != NULL)
{
((USBD_CUSTOM_HID_ItfTypeDef *)pdev->pUserData)->DeInit();
USBD_free(pdev->pClassData);
pdev->pClassData = NULL;
}
return USBD_OK;
}
至此,插上USB已经可以枚举成三个组合的HID了。但是也仅仅是万丈高楼平地起,只是打了了基础,只是配置好了usb,仅仅是实现了usb功能,我们的应用功能还需要安排上。
MultiTimer timer1;
MultiTimer timer2;
MultiTimer timer3;
void SystemClock_Config(void);
typedef enum
{
DEVICE_JOYSTICK=0,
DEVICE_MOUSE,
DEVICE_KEYBOARD
}DEVICE_TypeDef;
DEVICE_TypeDef myHid=DEVICE_JOYSTICK;
void RportTimer1Callback(MultiTimer* timer, void *userData)
{
if(myHid==DEVICE_JOYSTICK)
{
Joystick_Handle();
}else if(myHid==DEVICE_MOUSE){
Mouse_Handle();
}else if(myHid==DEVICE_KEYBOARD){
Key_Board_Handle();
}
MultiTimerStart(timer, 10, RportTimer1Callback, userData);
}
void WS2812BTimer2Callback(MultiTimer* timer, void *userData)
{
if(myHid==DEVICE_JOYSTICK)
{
WS_WriteAll_RGB(0xFF,0x00,0x00);
}else if(myHid==DEVICE_MOUSE){
WS_WriteAll_RGB(0x00,0xFF,0x00);
}else if(myHid==DEVICE_KEYBOARD){
WS_WriteAll_RGB(0x00,0x00,0xFF);
}
MultiTimerStart(timer, 100, WS2812BTimer2Callback, userData);
}
void DeviceSwitchTimer3Callback(MultiTimer* timer, void *userData)
{
static u16 swtick=0;
if((UPKEY)==0)
{
if(swtick++>300)
{
swtick=0;
if(++myHid>DEVICE_KEYBOARD)
{
myHid=DEVICE_JOYSTICK;
}
}
}else{
swtick=0;
}
MultiTimerStart(timer, 10, DeviceSwitchTimer3Callback, userData);
}
int main(void)
{
HAL_Init();
SystemClock_Config();
MultiTimerInstall(PlatformTicksGetFunc);
MX_GPIO_Init();
MX_DMA_Init();
MX_ADC1_Init();
MX_USB_DEVICE_Init();
MX_USART1_UART_Init();
MX_TIM3_Init();
Gp_ADC_Start_DMA();
MultiTimerStart(&timer1, 5, RportTimer1Callback, NULL);
MultiTimerStart(&timer2, 10, WS2812BTimer2Callback, NULL);
MultiTimerStart(&timer3, 10, DeviceSwitchTimer3Callback, NULL);
while (1)
{
MultiTimerYield();
}
}
下载验证我们把固件程序下载进去,摇动摇杆,按住SW2大于4s,可依次切换成鼠标模式成摇杆、鼠标、键盘模式,2812显示对应的红绿蓝色。在不同设备模式下摇动摇杆或轻触按键可以测试不同设备下的不同功能,而由于我们硬件条件有限,所以使用了分时复用,而如果您是自己设计了其他硬件,完全可以在我们的基础上修改,加加加,实现自己客制化的功能,毕竟现在网上有很多客制化键盘鼠标摇杆方案。具体的实现现象可以观看我们的视频; 视频教程
|
此文章已获得独家原创/原创奖标签,著作权归21ic所有,未经允许禁止转载。
打赏榜单
21ic小管家 打赏了 30.00 元 2022-12-20 理由:签约作者奖励
|