900字范文,内容丰富有趣,生活中的好帮手!
900字范文 > 【USB】STM32模拟USB鼠标

【USB】STM32模拟USB鼠标

时间:2019-08-10 04:57:24

相关推荐

【USB】STM32模拟USB鼠标

目录

1、前言

2、工程搭建

3、代码修改

4、参考资料

1、前言

本实验使用STM32F103ZET6开发板为例,实现了模拟USB鼠标的功能,并且能够在电脑上控制鼠标完成鼠标具备的功能。

2、工程搭建

使用STM32CubeMX配置工程,非常方便高效,配置如下:

使用外部高速晶振作为时钟源。

勾选使用USB外设,STM32F103ZET6只有USB Device功能,没有HOST功能,而且只有一个USB外设,使用PA11(USB_DM),PA12(USB_DP)管脚。

勾选USB DEVICE功能为HID设备。

时钟配置按默认的自动配置,USB时钟会自动配置到48MHz。

配置MDK工程输出目录,

勾选只输出必要的文件,工程更简洁。最后点击GENERATE CODE,输出工程完毕。

3、代码修改

原始生成的这个工程编译之后代码下载到开发板,将开发板USB口连线接上电脑后,查看设备管理器,可以看到多了一个HID mouse设备。

上面STM32CubeMX生成的原始工程只是纯粹能够在电脑上显示有个鼠标设备而已,没有给出上报鼠标事件完成鼠标移动或点击等功能的demo。我们可以通过USBD_HID_SendReport这个函数来上报鼠标事件,举个例子。

int main(void){/* USER CODE BEGIN 1 */unsigned char buff[4] = {0};/* 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){buff[0] = 0x01;//鼠标左键按下USBD_HID_SendReport(&hUsbDeviceFS, buff, sizeof(buff));HAL_Delay(100);buff[0] = 0x00; //鼠标左键松开USBD_HID_SendReport(&hUsbDeviceFS, buff, sizeof(buff));HAL_Delay(1000);/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */}

在main函数while循环中加入这段代码,可以看到鼠标每隔一秒进行一次左键单击事件。

下面搞复杂一点完善下鼠标的功能,我们加上鼠标事件上报的代码,完成鼠标的操作功能。我的开发板上有4个用户按键,分别编号key0到key3,原理图如下。

图中的KEY_UP为key0,定义4个按键完成的功能为:

key0单击:鼠标滚轮向上滑动。

key1单击:鼠标左键单击一次。

key2单击:鼠标滚轮向下滑动。

key3单击:鼠标右键单击一次。

key0长时间按下:鼠标向上移动。

key1长时间按下:鼠标向左移动。

key2长时间按下:鼠标向下移动。

key3长时间按下:鼠标向右移动。

最终的main.c代码如下:

/* Includes ------------------------------------------------------------------*/#include "main.h"#include "usb_device.h"/* Private includes ----------------------------------------------------------*//* USER CODE BEGIN Includes *//* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*//* USER CODE BEGIN PTD *//* USER CODE END PTD *//* Private define ------------------------------------------------------------*//* USER CODE BEGIN PD *//* USER CODE END PD *//* Private macro -------------------------------------------------------------*//* USER CODE BEGIN PM *//* USER CODE END PM *//* Private variables ---------------------------------------------------------*//* USER CODE BEGIN PV *//* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/void SystemClock_Config(void);static void MX_GPIO_Init(void);/* USER CODE BEGIN PFP *//* USER CODE END PFP *//* Private user code ---------------------------------------------------------*//* USER CODE BEGIN 0 */extern USBD_HandleTypeDef hUsbDeviceFS;extern uint8_t USBD_HID_SendReport (USBD_HandleTypeDef *pdev, uint8_t *report,uint16_t len);typedef struct {char mouse_abs_left : 1;//鼠标左键单击char mouse_abs_right : 1;//鼠标右键单击char mouse_abs_wheel : 1;//鼠标中键单击char reserve : 5;//常量0char mouse_rel_x;//鼠标X轴移动值char mouse_rel_y;//鼠标Y轴移动值char mouse_rel_wheel;//鼠标滚轮移动值}tyMouse_buff;#define KEY0_Press(1 << 0)#define KEY1_Press(1 << 1)#define KEY2_Press(1 << 2)#define KEY3_Press(1 << 3)tyMouse_buff tMouse_buff;void User_Init(void){tMouse_buff.mouse_abs_left = 0;tMouse_buff.mouse_abs_right = 0;tMouse_buff.mouse_abs_wheel = 0;tMouse_buff.reserve = 0;tMouse_buff.mouse_rel_x = 0;tMouse_buff.mouse_rel_y = 0;tMouse_buff.mouse_rel_wheel = 0;}unsigned char Get_Key_State(void){unsigned char keyState = 0;if (HAL_GPIO_ReadPin(key0_GPIO_Port, key0_Pin) == GPIO_PIN_SET){keyState |= KEY0_Press;}if (HAL_GPIO_ReadPin(GPIOE, key1_Pin) == GPIO_PIN_RESET){keyState |= KEY1_Press;}if (HAL_GPIO_ReadPin(GPIOE, key2_Pin) == GPIO_PIN_RESET){keyState |= KEY2_Press;}if (HAL_GPIO_ReadPin(GPIOE, key3_Pin) == GPIO_PIN_RESET){keyState |= KEY3_Press;}return keyState;}void Send_mouse_msg(void){USBD_HID_SendReport(&hUsbDeviceFS, (uint8_t *)&tMouse_buff, sizeof(tMouse_buff));}/* USER CODE END 0 *//*** @brief The application entry point.* @retval int*/int main(void){/* USER CODE BEGIN 1 */unsigned char keyState;unsigned char keyStateLast;int cnt;/* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init */User_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){if ((keyState = Get_Key_State()) != 0){HAL_Delay(10);cnt = 0;while ((keyState = Get_Key_State()) != 0){keyStateLast = keyState;if (cnt != 0){if (keyState & KEY0_Press){tMouse_buff.mouse_rel_y = -1;}else if (keyState & KEY2_Press){tMouse_buff.mouse_rel_y = 1;}else{tMouse_buff.mouse_rel_y = 0;}if (keyState & KEY1_Press){tMouse_buff.mouse_rel_x = -1;}else if (keyState & KEY3_Press){tMouse_buff.mouse_rel_x = 1;}else{tMouse_buff.mouse_rel_x = 0;}}if (cnt == 0){HAL_Delay(200);}else{Send_mouse_msg();HAL_Delay(10);}cnt++;}if (cnt == 1){if (keyStateLast & KEY1_Press){tMouse_buff.mouse_abs_left = -1;}if (keyStateLast & KEY3_Press){tMouse_buff.mouse_abs_right = 1;}if (keyStateLast & KEY0_Press){tMouse_buff.mouse_rel_wheel = 10;}if (keyStateLast & KEY2_Press){tMouse_buff.mouse_rel_wheel = -10;}Send_mouse_msg();HAL_Delay(10);tMouse_buff.mouse_abs_left = 0;tMouse_buff.mouse_abs_right = 0;tMouse_buff.mouse_rel_wheel = 0;Send_mouse_msg();}tMouse_buff.mouse_rel_x = 0;tMouse_buff.mouse_rel_y = 0;}/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */}/*** @brief System Clock Configuration* @retval None*/void SystemClock_Config(void){RCC_OscInitTypeDef RCC_OscInitStruct = {0};RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};/**Initializes the CPU, AHB and APB busses clocks */RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;RCC_OscInitStruct.HSEState = RCC_HSE_ON;RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;RCC_OscInitStruct.HSIState = RCC_HSI_ON;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK){Error_Handler();}/**Initializes the CPU, AHB and APB busses clocks */RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK){Error_Handler();}PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USB;PeriphClkInit.UsbClockSelection = RCC_USBCLKSOURCE_PLL_DIV1_5;if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK){Error_Handler();}}/*** @brief GPIO Initialization Function* @param None* @retval None*/static void MX_GPIO_Init(void){GPIO_InitTypeDef GPIO_InitStruct = {0};/* GPIO Ports Clock Enable */__HAL_RCC_GPIOE_CLK_ENABLE();__HAL_RCC_GPIOA_CLK_ENABLE();/*Configure GPIO pins : key1_Pin key2_Pin key3_Pin */GPIO_InitStruct.Pin = key1_Pin|key2_Pin|key3_Pin;GPIO_InitStruct.Mode = GPIO_MODE_INPUT;GPIO_InitStruct.Pull = GPIO_PULLUP;HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);/*Configure GPIO pin : key0_Pin */GPIO_InitStruct.Pin = key0_Pin;GPIO_InitStruct.Mode = GPIO_MODE_INPUT;GPIO_InitStruct.Pull = GPIO_PULLDOWN;HAL_GPIO_Init(key0_GPIO_Port, &GPIO_InitStruct);}/* USER CODE BEGIN 4 *//* USER CODE END 4 *//*** @brief This function is executed in case of error occurrence.* @retval None*/void Error_Handler(void){/* USER CODE BEGIN Error_Handler_Debug *//* User can add his own implementation to report the HAL error return state *//* USER CODE END Error_Handler_Debug */}#ifdef USE_FULL_ASSERT/*** @brief Reports the name of the source file and the source line number* where the assert_param error has occurred.* @param file: pointer to the source file name* @param line: assert_param error line source number* @retval None*/void assert_failed(uint8_t *file, uint32_t line){ /* USER CODE BEGIN 6 *//* User can add his own implementation to report the file name and line number,tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) *//* USER CODE END 6 */}#endif /* USE_FULL_ASSERT *//************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

鼠标事件每次上报4个字节的信息,用结构体tyMouse_buff来表示了。

怎么知道哪些事件对应哪些位的呢?刚开始我是网上查的,还以为是标准定死的,后来才知道这个是由HID设备的报告描述符决定的,当然市面上的鼠标报告描述符基本都一样的,算是都有个默契了,在usbd_hid.c文件下有个数组staticuint8_tHID_MOUSE_ReportDesc[HID_MOUSE_REPORT_DESC_SIZE]就是报告描述符。这个数组告知PC机要如何去解析HID设备上报的数据。这一串数据可以由HID Descriptor tool这个工具方便地生成(工具下载地址)。整个报告描述符的内容其实就是描述了tyMouse_buff这个结构体。

下面这个报告描述符是USB鼠标报告描述符。它描述了4个字节,第一个字节表示按键,第二个字节表示x轴(即鼠标左右移动,0表示不动,正值表示往右移,负值表示往左移),第三个字节表示y轴(即鼠标上下移动,0表示不动,正值表示往下移动,负值表示往上移动),第四个字节表示鼠标滚轮(正值为往上滚动,负值为往下滚动)。

code char MouseReportDescriptor[52] = {//通用桌面设备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)//使用最小值10x19, 0x01, // USAGE_MINIMUM (Button 1)//使用最大值3。1表示左键,2表示右键,3表示中键0x29, 0x03, // USAGE_MAXIMUM (Button 3)//逻辑最小值00x15, 0x00, // LOGICAL_MINIMUM (0)//逻辑最大值10x25, 0x01, // LOGICAL_MAXIMUM (1)//数量为30x95, 0x03, // REPORT_COUNT (3)//大小为1bit0x75, 0x01, // REPORT_SIZE (1)//输入,变量,数值,绝对值//以上3个bit分别表示鼠标的三个按键情况,最低位(bit-0)为左键//bit-1为右键,bit-2为中键,按下时对应的位值为1,释放时对应的值为00x81, 0x02, // INPUT (Data,Var,Abs)//填充5个bit,补足一个字节0x95, 0x01, // REPORT_COUNT (1)0x75, 0x05, // REPORT_SIZE (5)0x81, 0x03, // INPUT (Cnst,Var,Abs)//用途页为通用桌面0x05, 0x01, // USAGE_PAGE (Generic Desktop)//用途为X0x09, 0x30, // USAGE (X)//用途为Y0x09, 0x31, // USAGE (Y)//用途为滚轮0x09, 0x38, // USAGE (Wheel)//逻辑最小值为-1270x15, 0x81, // LOGICAL_MINIMUM (-127)//逻辑最大值为+1270x25, 0x7f, // LOGICAL_MAXIMUM (127)//大小为8个bits0x75, 0x08, // REPORT_SIZE (8)//数量为3个,即分别代表x,y,滚轮0x95, 0x03, // REPORT_COUNT (3)//输入,变量,值,相对值0x81, 0x06, // INPUT (Data,Var,Rel)//关集合0xc0, // END_COLLECTION0xc0 // END_COLLECTION};

通过对上面的报告分析,我们知道报告返回4个字节,没有报告ID。如果鼠标左键按下,则返回01 00 00 00(十六进制值),如果右键按下,则返回02 00 00 00,如果中键按下,则返回04 00 00 00,如果三个键同时按下,则返回07 00 00 00。如果鼠标往右移动则第二字节返回正值,值越大移动速度越快。其它的类推。

4、参考资料

《利用STM32CubeMX来生成USB_HID_Mouse工程》

《Hid Report Descriptor 报告描述符》

《The USB ID Repository》

《Tutorial about USB HID Report Descriptors》

书籍 《微控制器USB的信号和协议实现》

下载资料:链接:/s/1irh2x1P5fLhwPRdAaJmiTQ 提取码:64jy

HID Descriptor tool.zip

usbHIDdemo-mouse.rar

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。