完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
STM32配置组合设备(HID+CDC)
pass:其他组合设备也可以根据自己的想法搭建 通过:本实验基于stm32f107+CubeMx+Keil实现 本文对HID和CDC组合设备生成讲解,关于USB设备专用等请大家参考本人之前的博客 1. CDC基础工程,HID基础工程生成 首先使用stm32 cubemx配置生成CDC和HID工程,注意HID的时候生成的是HID的代码而不是自定义HID的代码。 CDC基础工程步骤 HID基础工程生成步骤 HID工程配置完成后,修改主函数将数据发送出来 /** * @brief The application entry point. * @retval int */ int main(void) { /* USER CODE BEGIN 1 */ uint8_t send_buf[]={0x00,0x00,0x00,0x00}; /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_USB_DEVICE_Init(); /* USER CODE BEGIN 2 */ /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { USBD_HID_SendReport(&hU***DeviceFS,send_buf,sizeof(send_buf)); HAL_Delay(100); /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ } 需要HID只配置了输入数据(也只能自己发数据),并没有配置输出内容(因此并通过HID接收数据) CDC工程生成之后,修改主函数, /** * @brief The application entry point. * @retval int */ int main(void) { /* USER CODE BEGIN 1 */ uint8_t buf[]=“abcd”; /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_USB_DEVICE_Init(); /* USER CODE BEGIN 2 */ /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { CDC_Transmit_FS(buf,sizeof(buf)); HAL_Delay(100); /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ } 到此HID和CDC的基础工程已经完成了,接下来就是使用这两个基础工程来搭建HID+CDC复合设备了! 2.USB了解实际 复合设备的写信大家还可以参考阅读这篇文章博客STM32 USB复合设备编写,然后需要大家认真去理解一下USB的程序了,需要大家自己去看下代码,对整个框架有个熟悉,大家也可以借助调试调试,看代码如何运行的。 2.1 USB初始化 /** * Init USB device Library, add supported class and start the library * @retval None */ void MX_USB_DEVICE_Init(void) { /* USER CODE BEGIN USB_DEVICE_Init_PreTreatment */ /* USER CODE END USB_DEVICE_Init_PreTreatment */ /* Init Device Library, add supported class and start the library. */ if (USBD_Init(&hU***DeviceFS, &FS_Desc, DEVICE_FS) != USBD_OK) { Error_Handler(); } if (USBD_RegisterClass(&hU***DeviceFS, &USBD_COMPOSITE) != USBD_OK) { Error_Handler(); } // if (USBD_CDC_RegisterInterface(&hU***DeviceFS, &USBD_CDC_Interface_fops_FS) != USBD_OK) // { // Error_Handler(); // } if (USBD_Start(&hU***DeviceFS) != USBD_OK) { Error_Handler(); } /* USER CODE BEGIN USB_DEVICE_Init_PostTreatment */ /* USER CODE END USB_DEVICE_Init_PostTreatment */ } MX_USB_DEVICE_Init初始化USB,主要是注册相关功能 USBD_RegisterClass 支持注册类,将潜在支持对应功能 USBD_COMPOSITE 具体函数为 USBD_ClassTypeDef USBD_COMPOSITE = { USBD_Composite_Init, USBD_Composite_DeInit, USBD_Composite_Setup, NULL, /*EP0_TxSent*/ USBD_Composite_EP0_RxReady, //add USBD_Composite_DataIn, USBD_Composite_DataOut, NULL, NULL, NULL, NULL, USBD_Composite_GetFSCfgDesc, NULL, USBD_Composite_GetDeviceQualifierDescriptor, }; 主要用于实现u***复合设备的初始化,设置,数据输入输出处理等待 USBD_ClassTypeDef是一个函数轨迹结构体 ```c typedef struct _Device_cb { uint8_t (*Init)(struct _USBD_HandleTypeDef *pdev, uint8_t cfgidx); uint8_t (*DeInit)(struct _USBD_HandleTypeDef *pdev, uint8_t cfgidx); /* Control Endpoints*/ uint8_t (*Setup)(struct _USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); uint8_t (*EP0_TxSent)(struct _USBD_HandleTypeDef *pdev); uint8_t (*EP0_RxReady)(struct _USBD_HandleTypeDef *pdev); /* Class Specific Endpoints*/ uint8_t (*DataIn)(struct _USBD_HandleTypeDef *pdev, uint8_t epnum); uint8_t (*DataOut)(struct _USBD_HandleTypeDef *pdev, uint8_t epnum); uint8_t (*SOF)(struct _USBD_HandleTypeDef *pdev); uint8_t (*IsoINIncomplete)(struct _USBD_HandleTypeDef *pdev, uint8_t epnum); uint8_t (*IsoOUTIncomplete)(struct _USBD_HandleTypeDef *pdev, uint8_t epnum); uint8_t *(*GetHSConfigDescriptor)(uint16_t *length); uint8_t *(*GetFSConfigDescriptor)(uint16_t *length); uint8_t *(*GetOtherSpeedConfigDescriptor)(uint16_t *length); uint8_t *(*GetDeviceQualifierDescriptor)(uint16_t *length); #if (USBD_SUPPORT_USER_STRING_DESC == 1U) uint8_t *(*GetUsrStrDescriptor)(struct _USBD_HandleTypeDef *pdev, uint8_t index, uint16_t *length); #endif } USBD_ClassTypeDef; ``` USBD_CDC_RegisterInterface注册接口的,外口的发送接收功能 USBD_CDC_ItfTypeDef USBD_CDC_Interface_fops_FS = { CDC_Init_FS, CDC_DeInit_FS, CDC_Control_FS, CDC_Receive_FS }; 这里的注释是我们在外面的函数中将因为指向过去了,也就是这一步我们在里面的步骤中已经实现了,所以这里不再需要 2.2USB中断 我们配置了USB之后,CubeMX会自动中断,所有的USB通讯都通过中断完成(故障肯定是这样,发送是不是还没研究) 中断函数如下: /** * @brief This function handles USB OTG FS global interrupt. */ void OTG_FS_IRQHandler(void) { /* USER CODE BEGIN OTG_FS_IRQn 0 */ /* USER CODE END OTG_FS_IRQn 0 */ HAL_PCD_IRQHandler(&hpcd_USB_OTG_FS); /* USER CODE BEGIN OTG_FS_IRQn 1 */ /* USER CODE END OTG_FS_IRQn 1 */ } 进入HAL_PCD_IRQHandler函数 我使用debug发现,主要处理在以下部分 //截取其中一部分 while (ep_intr != 0U) { if ((ep_intr & 0x1U) != 0U) { epint = USB_ReadDevOutEPInterrupt(hpcd-》Instance, (uint8_t)epnum); if ((epint & USB_OTG_DOEPINT_XFRC) == USB_OTG_DOEPINT_XFRC) { CLEAR_OUT_EP_INTR(epnum, USB_OTG_DOEPINT_XFRC); (void)PCD_EP_OutXfrComplete_int(hpcd, epnum); } if ((epint & USB_OTG_DOEPINT_STUP) == USB_OTG_DOEPINT_STUP) { CLEAR_OUT_EP_INTR(epnum, USB_OTG_DOEPINT_STUP); /* Class B setup phase done for previous decoded setup */ (void)PCD_EP_OutSetupPacket_int(hpcd, epnum); } if ((epint & USB_OTG_DOEPINT_OTEPDIS) == USB_OTG_DOEPINT_OTEPDIS) { CLEAR_OUT_EP_INTR(epnum, USB_OTG_DOEPINT_OTEPDIS); } /* Clear Status Phase Received interrupt */ if ((epint & USB_OTG_DOEPINT_OTEPSPR) == USB_OTG_DOEPINT_OTEPSPR) { CLEAR_OUT_EP_INTR(epnum, USB_OTG_DOEPINT_OTEPSPR); } /* Clear OUT NAK interrupt */ if ((epint & USB_OTG_DOEPINT_NAK) == USB_OTG_DOEPINT_NAK) { CLEAR_OUT_EP_INTR(epnum, USB_OTG_DOEPINT_NAK); } } epnum++; ep_intr 》》= 1U; } } 主要的处理在(void)PCD_EP_OutXfrComplete_int(hpcd, epnum); 和 (void)PCD_EP_OutSetupPacket_int(hpcd, epnum);内。 (void)PCD_EP_OutSetupPacket_int(hpcd, epnum);函数分析 进入此函数,会调用HAL_PCD_SetupStageCallback(hpcd); 之后调用USBD_LL_SetupStage((USBD_HandleTypeDef*)hpcd-》pData, (uint8_t *)hpcd-》Setup);,核心部分在这里面 /** * @brief USBD_SetupStage * Handle the setup stage * @param pdev: device instance * @retval status */ USBD_StatusTypeDef USBD_LL_SetupStage(USBD_HandleTypeDef *pdev, uint8_t *psetup) { USBD_ParseSetupRequest(&pdev-》request, psetup); pdev-》ep0_state = USBD_EP0_SETUP; pdev-》ep0_data_len = pdev-》request.wLength; switch (pdev-》request.bmRequest & 0x1FU) { case USB_REQ_RECIPIENT_DEVICE: USBD_StdDevReq(pdev, &pdev-》request); break; case USB_REQ_RECIPIENT_INTERFACE: USBD_StdItfReq(pdev, &pdev-》request); break; case USB_REQ_RECIPIENT_ENDPOINT: USBD_StdEPReq(pdev, &pdev-》request); break; default: USBD_LL_StallEP(pdev, (pdev-》request.bmRequest & 0x80U)); break; } return USBD_OK; } 主要在内调用不同的函数,实现设备枚举等内容,每个人点进去就可以发现,他的调用过程都是使用视图切换对应的函数,而视图的内容在u***初始化的时候已经设置好了。 (void)PCD_EP_OutXfrComplete_int(hpcd, epnum);函数分析 进入此函数会调用HAL_PCD_DataOutStageCallback(hpcd, (uint8_t)epnum);, 进入之后由调用USBD_LL_DataOutStage((USBD_HandleTypeDef*)hpcd-》pData, epnum, hpcd-》OUT_ep[epnum].xfer_buff);,核心在这里面 /** * @brief USBD_DataOutStage * Handle data OUT stage * @param pdev: device instance * @param epnum: endpoint index * @retval status */ USBD_StatusTypeDef USBD_LL_DataOutStage(USBD_HandleTypeDef *pdev, uint8_t epnum, uint8_t *pdata) { USBD_EndpointTypeDef *pep; if (epnum == 0U) { pep = &pdev-》ep_out[0]; if (pdev-》ep0_state == USBD_EP0_DATA_OUT) { if (pep-》rem_length 》 pep-》maxpacket) { pep-》rem_length -= pep-》maxpacket; USBD_CtlContinueRx(pdev, pdata, (uint16_t)MIN(pep-》rem_length, pep-》maxpacket)); } else { if ((pdev-》pClass-》EP0_RxReady != NULL) && (pdev-》dev_state == USBD_STATE_CONFIGURED)) { pdev-》pClass-》EP0_RxReady(pdev); } USBD_CtlSendStatus(pdev); } } else { if (pdev-》ep0_state == USBD_EP0_STATUS_OUT) { /* * STATUS PHASE completed, update ep0_state to idle */ pdev-》ep0_state = USBD_EP0_IDLE; USBD_LL_StallEP(pdev, 0U); } } } else if ((pdev-》pClass-》DataOut != NULL) && (pdev-》dev_state == USBD_STATE_CONFIGURED)) { pdev-》pClass-》DataOut(pdev, epnum); } else { /* should never be in this condition */ return USBD_FAIL; } return USBD_OK; } 在此函数内完成数据输出DataOut,EP0RxReady,填充设置等等_。 |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1885 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1663 浏览 1 评论
1149 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
763 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1720 浏览 2 评论
1965浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
790浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
616浏览 3评论
631浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
594浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2025-1-14 14:37 , Processed in 0.804924 second(s), Total 75, Slave 59 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (威廉希尔官方网站 图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号