STM32 JoystickMouse USB游戏杆鼠标的实现

  • Post author:
  • Post category:其他





本次程序的实现是基于上次CustomHID修稿过来的,工程的架构就不在介绍,这里主要介绍下,如何修改。



首先当然是usb_desc.c的一些描述符了。


设备描述符需要修改下bMaxPacketSize(最大包长度)为0x08个字节,因为该工程通讯的长度为4字节,根据USB洗衣规范,最大包长度只能是8、16、64等,所以这里选择0x08,这里要注意,改为0x08时,在usb_prop.c的DEVICE_PROP Device_Property->MaxPacketSize域也要改成0x08(我们在下面会说到),要跟配置描述符定义的最大包长度相同,否则,电脑上会出现:“此设备未识别”类似的提示。最好还要修改下厂商ID和产品ID两项。因为不修改PID和VID,如果你上次CustomHID或其他USB工程也使用同样的PID和VID,这次的工程,电脑不会重新加载新的驱动,可能会使功能无法实现。如果出现上面的情况,只要打开设备管理器,找到对应的驱动设备,然后右键选择卸载,然后开发板重新上电,电脑的右下方就会跳出:发现新硬件。还是贴出代码吧:

/* USB标准设备描述符*/ const uint8_t Joystick_DeviceDescriptor[JOYSTICK_SIZ_DEVICE_DESC] = { 0x12, /*bLength:长度,设备描述符的长度为18字节*/ USB_DEVICE_DESCRIPTOR_TYPE, /*bDescriptorType:类型,设备描述符的编号是0x01*/ 0x00, /*bcdUSB:所使用的USB版本为2.0*/ 0x02, 0x00, /*bDeviceClass:设备所使用的类代码*/ 0x00, /*bDeviceSubClass:设备所使用的子类代码*/ 0x00, /*bDeviceProtocol:设备所使用的协议*/ 0x08, /*bMaxPacketSize:最大包长度为64字节*/ 0x88, /*idVendor:厂商ID为0x7788*/ 0x66, 0x22, /*idProduct:产品ID为0x1122*/ 0x11, 0x00, /*bcdDevice:设备的版本号为2.00*/ 0x02, 1, /*iManufacturer:厂商字符串的索引*/ 2, /*iProduct:产品字符串的索引*/ 3, /*iSerialNumber:设备的序列号字符串索引*/ 0x01 /*bNumConfiguration:设备有1种配置*/ }; /* JoystickMouse设备描述符 */



接着需要修改配置描述符集。配置描述符可以不需要修改。找到接口的描述符的bNumEndpoints(该接口所使用的端点数)域,修改为0x01,表示使用1个端点,nInterfaceProtocol (该接口使用的协议)域,修改成0x02,表示使用mouse协议。HID描述符也不需要修改。输入端点描述符跟CustomHID工程的一样,可以修改下 wMaxPacketSize(该端点支持的最大包长度)为0x04字节,因为这里只用到四个字节,注意这里的端点支持最大包长度可以不用按照USB的只能是8、16、64等规范。接着删掉CustomHID工程中的的输出端点描述符。代码如下:

/* USB配置描述符集合(配置、接口、端点、类、厂商)(Configuration, Interface, Endpoint, Class, Vendor */ const uint8_t Joystick_ConfigDescriptor[JOYSTICK_SIZ_CONFIG_DESC] = { 0x09, /*bLength:长度,设备字符串的长度为9字节*/ USB_CONFIGURATION_DESCRIPTOR_TYPE, /*bDescriptorType:类型,配置描述符的类型编号为0x2*/ JOYSTICK_SIZ_CONFIG_DESC, /*wTotalLength:配置描述符的总长度为41字节*/ 0x00, 0x01, /*bNumInterfaces:配置所支持的接口数量1个*/ 0x01, /*bConfigurationValue:该配置的值*/ 0x00, /*iConfiguration:该配置的字符串的索引值,该值为0表示没有字符串*/ 0xE0, /* bmAttributes:设备的一些特性,0xc0表示自供电,不支持远程唤醒 D7:保留必须为1,D6:是否自供电,D5:是否支持远程唤醒,D4~D0:保留设置为0*/ 0x32, /*从总线上获得的最大电流为100mA */ // 0x96, /*MaxPower:设备需要从总线上获取多少电流,单位为2mA,0x96表示300mA*/ /************** 接口描述符****************/ /* 09 */ 0x09, /*bLength:长度,接口描述符的长度为9字节 */ USB_INTERFACE_DESCRIPTOR_TYPE,/* bDescriptorType:接口描述符的类型为0x4 */ 0x00, /*bInterfaceNumber:该接口的编号*/ 0x00, /*bAlternateSetting:该接口的备用编号 */ 0x01, /*bNumEndpoints:该接口所使用的端点数*/ 0x03, /*bInterfaceClass该接口所使用的类为HID*/ 0x01, /*bInterfaceSubClass:该接口所用的子类 1=BOOT, 0=no boot */ 0x02, /*nInterfaceProtocol :该接口使用的协议0=none, 1=keyboard, 2=mouse */ 0, /*iInterface: 该接口字符串的索引 */ /*****************HID描述符 ********************/ /* 18 */ 0x09, /*bLength: HID描述符的长度为9字节 */ HID_DESCRIPTOR_TYPE, /* bDescriptorType: HID的描述符类型为0x21 */ 0x10, /*bcdHID: HID协议的版本为1.1 */ 0x01, 0x00, /*bCountryCode: 国家代号 */ 0x01, /*bNumDescriptors: 下级描述符的数量*/ 0x22, /*bDescriptorType:下级描述符的类型*/ JOYSTICK_SIZ_REPORT_DESC,/* wItemLength: 下一集描述符的长度*/ 0x00, /********************输入端点描述符******************/ /* 27 */ 0x07, /* bLength: 端点描述符的长度为7字节*/ USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: 端点描述符的类型为0x21*/ 0x81, /* bEndpointAddress: 该端点(输入)的地址,D7:0(OUT),1(IN),D6~D4:保留,D3~D0:端点号*/ 0x03, /* bmAttributes: 端点的属性为为中断端点. D0~D1表示传输类型:0(控制传输),1(等时传输),2(批量传输),3(中断传输) 非等时传输端点:D2~D7:保留为0 等时传输端点: D2~D3表示同步的类型:0(无同步),1(异步),2(适配),3(同步) D4~D5表示用途:0(数据端点),1(反馈端点),2(暗含反馈的数据端点),3(保留),D6~D7:保留,*/ 0x04, /* wMaxPacketSize: 该端点支持的最大包长度为4字节*/ 0x00, 0x20, /* bInterval: 轮询间隔(32ms) */ /* 34 */ };




还要替换下报告描述符,该描述符定义了四个字节的输入数据域,第一个字节的D0位用来表示鼠标的左键,D1为表示鼠标的D2键,D3用来表示鼠标的中键(有些鼠标有的),其他5为保留,固定值为0。第二数据域表示鼠标的X轴变化量,取值范围为-127~127,正的表示向上移,符的表示向下移,这里需要注意的是,这个值表示鼠标的变化量,而不是鼠标的位置。第三个字节表示鼠标的Y轴的变化量,取值范围为-127~127,正值表示鼠标往上移,负值表示鼠标往下移,同样注意这个值是表示鼠标的变化量而不是鼠标的位置。第四个值表示鼠标的滚轮的变化量,取值范围-127~127,正值表示向上滚,负值表示向下滚,同样注意是变化量,而不是位置。代码如下:

/* HID的报告描述符*/ const uint8_t Joystick_ReportDescriptor[JOYSTICK_SIZ_REPORT_DESC] = { /*short Item D7~D4:bTag;D3~D2:bType;D1~D0:bSize **bTag ---主条目 1000:输入(Input) 1001:输出(Output) 1011:特性(Feature) 1010:集合(Collection) 1100:关集合(End Collection) ** 全局条目 0000:用途页(Usage Page) 0001:逻辑最小值(Logical Minimum) 0010:逻辑最大值(Logical Maximum) 0011:物理最小值(Physical Minimum) ** 0100:物理最大值(Physical Maximum) 0101:单元指数(Unit Exponet) 0110:单元(Unit) 0111:数据域大小(Report Size) ** 1000:报告ID(Report ID) 1001:数据域数量(Report Count) 1010:压栈(Push) 1011:出栈(Pop) 1100~1111:保留(Reserved) ** 局部条目 0000:用途(Usage) 0001:用途最小值(Usage Minimum) 0010:用途最大值(Usage Maximum) 0011:标识符索引(Designator Index) ** 0100:标识符最小值(Designator Minimum) 0101:标识符最大值(Designator Maximum) 0111:字符串索引(String Index) 1000:字符串最小值(String Minimum) ** 1001:字符串最大值(String Maximum) 1010:分隔符(Delimiter) 其他:保留(Reserved) **bType---00:主条目(main) 01:全局条目(globle) 10:局部条目(local) 11:保留(reserved) **bSize---00:0字节 01:1字节 10:2字节 11:4字节*/ //0x05:0000 01 01 这是全局条目,用途页选择为通用桌面 0x05,0x01, /*Usage Page(Generic Desktop)*/ //0x09:0000 10 01 这是个局部条目,用途为鼠标 0x09,0x02, /*Usage(Mouse)*/ //0xa1:0101 00 01 这是个主条目, 逻辑集合 0xA1,0x01, /*Collection(Logical)*/ //0x09:0000 10 01 这是个局部条目,用途为指针 0x09,0x01, /*Usage(Pointer)*/ /* 8 */ //0xa1:1010 00 01 这是个主条目,连接集合 0xA1,0x00, /*Collection(Linked)*/ //0x05:0000 01 01 这是全局条目,用途页选择为按钮 0x05,0x09, /*Usage Page(Buttons)*/ //0x19:0001 10 01 这是个局部条目,用途的最小值为1 0x19,0x01, /*Usage Minimum(1)*/ //0x29:0010 10 01 这是个局部条目,用途的最大值为3 0x29,0x03, /*Usage Maximum(3)*/ /* 16 */ //0x15:0001 01 01 这是全局条目,逻辑的最小值为0 0x15,0x00, /*Logical Minimum(0)*/ //0x25:0010 01 01 这是全局条目,逻辑的最大值为1 0x25,0x01, /*Logical Maximum(1)*/ //0x95:1001 01 01 这是全局条目,数据域的数量为3 0x95,0x03, /*Report Count(3)*/ //0x75:0111 01 01 这是全局条目,数据域的长度为1 0x75,0x01, /*Report Size(1)*/ /* 24 */ //0x81:1000 00 01 这是个主条目,有3*1bit数据域输入,属性为变量 0x81,0x02, /*Input(Variable)*/ //0x95:1001 01 01 这是全局条目,数据域的数量为1个 0x95,0x01, /*Report Count(1)*/ //0x75:0111 01 01 这是全局条目,每个数据域的长度为5bit 0x75,0x05, /*Report Size(5)*/ //0x81:1000 00 01 这是一个主条目,有1*5bit数据域输入,属性常量数组 0x81,0x03, /*Input(Constant,Array)*/ /* 32 */ //0x05:0000 01 01 这是全局条目,用途页为通用桌面 0x05,0x01, /*Usage Page(Generic Desktop)*/ //0x09:0000 10 01 这是个局部条目,用途为X轴 0x09,0x30, /*Usage(X axis)*/ //0x09:0000 10 01 这是个局部条目,用途为Y轴 0x09,0x31, /*Usage(Y axis)*/ //0x09:0000 10 01 这是个局部条目,用途为滚轮 0x09,0x38, /*Usage(Wheel)*/ /* 40 */ //0x15:0001 01 01 这是全局条目,逻辑的最小值为-127 0x15,0x81, /*Logical Minimum(-127)*/ //0x25:0010 01 01 这是全局条目,逻辑的最大值为127 0x25,0x7F, /*Logical Maximum(127)*/ //0x75:0111 01 01 这是全局条目,数据域的长度为8bit 0x75,0x08, /*Report Size(8)*/ //0x95:1001 01 01 这是全局条目,数据域的数量为3个 0x95,0x03, /*Report Count(3)*/ /* 48 */ //0x81:1000 00 01 这是个主条目,有3*8bit数据域输入,相关的变量 0x81,0x06, /*Input(Variable, Relative)*/ //0xc0:1100 00 00 这是个主条目,关集合 0xC0,0xC0, /*End Collection*/  

/* 52 */ };





至于其他一些描述符,自己定义吧,把我的工程代码贴出来:

/* 语言ID描述符 */ const uint8_t Joystick_StringLangID[JOYSTICK_SIZ_STRING_LANGID] = { JOYSTICK_SIZ_STRING_LANGID, /*bLength:本描述符的长度为4字节*/ USB_STRING_DESCRIPTOR_TYPE, /*bDescriptorType:字符串描述符的类型为0x03*/ 0x09, /*bString:语言ID为0x0409,表示美式英语*/ 0x04 }; /* LangID = 0x0409: U.S. English*/ /*厂商字符串描述符*/ const uint8_t Joystick_StringVendor[JOYSTICK_SIZ_STRING_VENDOR] = { JOYSTICK_SIZ_STRING_VENDOR, /*bLength:厂商字符串描述符的长度*/ USB_STRING_DESCRIPTOR_TYPE, /*bDescriptorType:字符串描述符的类型为0x03*/ 'B' , 0, 'y', 0, ':' , 0, 'z' , 0, 'i', 0, 'y', 0,'e', 0,'3', 0, '3', 0, '4', 0 /*自定义*/ }; /*产品的字符串描述符*/ const uint8_t Joystick_StringProduct[JOYSTICK_SIZ_STRING_PRODUCT] = { JOYSTICK_SIZ_STRING_PRODUCT, /* bLength:产品的字符串描述符*/ USB_STRING_DESCRIPTOR_TYPE, /* bDescriptorType:字符串描述符的类型为0x03*/ 'z', 0, 'i', 0, 'y', 0, 'e', 0, '3', 0, '3', 0 ,'4',0/*自定义*/ }; /*产品序列号的字符串描述符*/ uint8_t Joystick_StringSerial[JOYSTICK_SIZ_STRING_SERIAL] = { JOYSTICK_SIZ_STRING_SERIAL, /* bLength:产品序列号*/ USB_STRING_DESCRIPTOR_TYPE, /* bDescriptorType:字符串描述符的类型为0x03*/ '1', 0, '2', 0, '3', 0,'4', 0,'5', 0, '6', 0, '7', 0 /*自定义*/ };



这里还要贴出usb_desc.h中的一些宏定义,这些宏定义在上面的描述符中用到:

#define USB_DEVICE_DESCRIPTOR_TYPE 0x01 //设备描述符类型 #define USB_CONFIGURATION_DESCRIPTOR_TYPE 0x02 //配置描述符类型 #define USB_STRING_DESCRIPTOR_TYPE 0x03 //字符串描述符类型 #define USB_INTERFACE_DESCRIPTOR_TYPE 0x04 //接口描述符类型 #define USB_ENDPOINT_DESCRIPTOR_TYPE 0x05 //端点描述符类型 #define HID_DESCRIPTOR_TYPE 0x21 //HID描述符类型 #define JOYSTICK_SIZ_HID_DESC 0x09 //HID描述符的长度 #define JOYSTICK_OFF_HID_DESC 0x12 //HID描述符在配置描述符集合数组中的偏移 #define JOYSTICK_SIZ_DEVICE_DESC 18 //设备描述符的长度 #define JOYSTICK_SIZ_CONFIG_DESC 34 //配置描述符的长度 #define JOYSTICK_SIZ_REPORT_DESC 52 //报告描述符的的长度 #define JOYSTICK_SIZ_STRING_LANGID 4 //语言ID字符串描述符的长度 #define JOYSTICK_SIZ_STRING_VENDOR 22 //厂商字符串描述符的长度 #define JOYSTICK_SIZ_STRING_PRODUCT 16 //产品字符串描述符的长度 #define JOYSTICK_SIZ_STRING_SERIAL 26 //序列号字符串描述符的长度 #define STANDARD_ENDPOINT_DESC_SIZE 0x09 #define REPORT_COUNT 4 //端点长度



讲完了usb_desc.c的文件,下面要修改的是usb_prop.c文件。首先要修改下这个文件只需要修改下DEVICE_PROP Device_Property里面的内容,把

MaxPacketSize域的大小改为0x08,与设备描述符对应。如下

DEVICE_PROP Device_Property = //注册一些Joystick函数 { Joystick_init, //Joystick的初始化函数 Joystick_Reset, //Joystick的复位函数 Joystick_Status_In, //Joystick状态输入函数 Joystick_Status_Out, //Joystick状态输出函数 Joystick_Data_Setup, //Joystick的处理有数据阶段的特殊类请求函数 Joystick_NoData_Setup, //Joystick的处理没有数据阶段的特殊类请求函数 Joystick_Get_Interface_Setting, //Joystick获取接口及备用接口设置(是否可用) Joystick_GetDeviceDescriptor, //Joystick获取设备描述符 Joystick_GetConfigDescriptor, //Joystick获取配置描述符 Joystick_GetStringDescriptor, //Joystick获取字符串描述符 0, //当前库未使用 0x08 /*MAX PACKET SIZE*/ //最大的包长度为8字节 };


再在结构体中注册过的USB复位函数,我的工程注册的是Joystick_Reset()这个函数。基于CustomHID的代码,这个函数需要修改下端点的配置,把端点以配置成中断传输端点,设置发送地址,设置发送数量,及接收无效。代码如下:

/******************************************************************************* * Function Name : Joystick_Reset. * Description : Joystick Mouse reset routine.复位 * Input : None. * Output : None. * Return : None. *******************************************************************************/ void Joystick_Reset(void) { /* Set CustomHID_DEVICE as not configured */ pInformation->Current_Configuration = 0; //设置当前的配置为0,表示没有配置过 pInformation->Current_Interface = 0;//默认的接口 /* Current Feature initialization */ pInformation->Current_Feature = Joystick_ConfigDescriptor[7];//当前的属性,bmAttributes:设备的一些特性,0xc0表示自供电,不支持远程唤醒 #ifdef STM32F10X_CL /* EP0 is already configured in DFU_Init() by USB_SIL_Init() function */ /* Init EP1 IN snd EP1 OUT as Interrupt endpoint */ OTG_DEV_EP_Init(EP1_IN, OTG_DEV_EP_TYPE_INT, EP1_SIZE); OTG_DEV_EP_Init(EP1_OUT, OTG_DEV_EP_TYPE_INT, EP1_SIZE); #else SetBTABLE(BTABLE_ADDRESS); /* Initialize Endpoint 0 */ SetEPType(ENDP0, EP_CONTROL); //设置端点1为控制端点 SetEPTxStatus(ENDP0, EP_TX_STALL); //设置端点0发送延时 SetEPRxAddr(ENDP0, ENDP0_RXADDR); //设置端点0的接收缓冲区地址 SetEPTxAddr(ENDP0, ENDP0_TXADDR); //设置端点0的发送缓冲区地址 Clear_Status_Out(ENDP0); //清除端点0的状态 SetEPRxCount(ENDP0, Device_Property.MaxPacketSize);//设置端点0的接收的计数 SetEPRxValid(ENDP0); //使能接收状态 /* Initialize Endpoint 1 */ SetEPType(ENDP1, EP_INTERRUPT); //设置端点1为中断控制端点 SetEPTxAddr(ENDP1, ENDP1_TXADDR); //设置端点1的缓冲地址 SetEPTxCount(ENDP1, REPORT_COUNT);//设置端点1的接收计数 SetEPRxStatus(ENDP1, EP_RX_DIS); //设置端点1接收无效 SetEPTxStatus(ENDP1, EP_TX_NAK); bDeviceState = ATTACHED; //设置设备状态为 ATTACHED状态 /* Set this device to response on default address */ SetDeviceAddress(0); //设置设备为默认地址 #endif /* STM32F10X_CL */ bDeviceState = ATTACHED; }



这样的话,端点就配置好了,我们还要编写模拟鼠标的功能程序,这里我使用六个按键分别模拟鼠标的向左移动,向右移动,向上移动,向下移动,鼠标左键,鼠标右键。我们首先在hw_config.c中编写按键引脚的配置函数USB_GPIO_Configuration()如下:

/******************************************************************************* * Function Name : USB_GPIO_Confguration * Description : USB相关引脚配置 * Input : None. * Output : None. * Return : None. *******************************************************************************/ void USB_GPIO_Confguration(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIO_DISCONNECT, ENABLE); /* USB_DISCONNECT used as USB pull-up */ GPIO_InitStructure.GPIO_Pin = USB_DISCONNECT_PIN; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(USB_DISCONNECT, &GPIO_InitStructure); /*KEY引脚配置*/ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_8; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 ; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOC, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOD, &GPIO_InitStructure); }

该函数在BSP.c的BSP_Init()中调用。



接下去在编写按键扫描函数JoyState():

/******************************************************************************* * Function Name : JoyState. * Description : 检测joystick或mouse的方向 * Input : None. * Output : None. * Return value : 返回方向的值 *******************************************************************************/ uint8_t JoyState(void) { /* "right" key is pressed */ if (!GPIO_ReadInputDataBit(JOY_RIGHT_PORT, JOY_RIGHT)) { GPIO_ResetBits(GPIOF,GPIO_Pin_6); return RIGHT; } /* "left" key is pressed */ else if (!GPIO_ReadInputDataBit(JOY_LEFT_PORT, JOY_LEFT)) { GPIO_ResetBits(GPIOF,GPIO_Pin_7); return LEFT; } /* "up" key is pressed */ else if (!GPIO_ReadInputDataBit(JOY_UP_PORT, JOY_UP)) { GPIO_ResetBits(GPIOF,GPIO_Pin_8); return UP; } /* "down" key is pressed */ else if (!GPIO_ReadInputDataBit(JOY_DOWN_PORT, JOY_DOWN)) { GPIO_ResetBits(GPIOF,GPIO_Pin_9); return DOWN; } else if (!GPIO_ReadInputDataBit(JOY_LEFT_BUTTON_PORT, JOY_LEFT_BUTTON)) { return LEFT_BUTTON; } else if (!GPIO_ReadInputDataBit(JOY_RIGHT_BUTTON_PORT, JOY_RIGHT_BUTTON)) { return RIGHT_BUTTON; } /* No key is pressed */ else { GPIO_SetBits(GPIOF,GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9); return 0; } }



之后还要编写发送函数Joystick_Send():

/******************************************************************************* * Function Name : Joystick_Send. * Description : 发送joystick或mouse的信息 * Input : Keys: 检测到被按下的按键值 * Output : None. * Return value : None. *******************************************************************************/ void Joystick_Send(uint8_t Keys) { uint8_t Mouse_Buffer[4] = {0, 0, 0, 0}; int8_t X = 0, Y = 0,MouseButton=0; switch (Keys) { case LEFT: X -= CURSOR_STEP; break; case RIGHT: X += CURSOR_STEP; break; case UP: Y -= CURSOR_STEP; break; case DOWN: Y += CURSOR_STEP; break; case LEFT_BUTTON: MouseButton = MouseButton|0x01; break; case RIGHT_BUTTON: MouseButton = MouseButton|0x02; break; default: break; } /* prepare buffer to send */ Mouse_Buffer[0] = MouseButton; Mouse_Buffer[1] = X; Mouse_Buffer[2] = Y; /* Copy mouse position info in ENDP1 Tx Packet Memory Area*/ USB_SIL_Write(EP1_IN, Mouse_Buffer, 4); if(Mouse_Buffer[0]!= 0) { Mouse_Buffer[0] = 0; /* Copy mouse position info in ENDP1 Tx Packet Memory Area*/ USB_SIL_Write(EP1_IN, Mouse_Buffer, 4); } SetEPTxValid(ENDP1); }

这个函数有个参数Keys,我们通过Joystick_Send(JoyState());就能将检测到的按键情况传递给Joystick_Send()函数了。Joystick_Send()函数接到按键信息后,会判断是哪个按键被按下,如果是模拟鼠标左键的按键,则将

Mouse_Buffer[0]的D0置1;如果是模拟鼠标右键的按键被按下,则就将


Mouse_Buffer[0]的D1位置1;如果是模拟鼠标X轴正方向的按键,则将


Mouse_Buffer[1]的值加上


CURSOR_STEP个单位;如果是模拟鼠标X轴的负方向的按键,则将


Mouse_Buffer[1]的值减去


CURSOR_STEP个单位;


如果是模拟鼠标Y轴正方向的按键,则将


Mouse_Buffer[2]的值加上


CURSOR_STEP个单位;如果是模拟鼠标Y轴的负方向的按键,则将


Mouse_Buffer[2]的值减去


CURSOR_STEP个单位;至于


Mouse_Buffer[3]这个字节,我们这里不做功能,所以一直设置成0。检测完按键信息并填充了发送数组后,调用


USB_SIL_Write()放送数据,如果鼠标左右键有按下去,则还要清除


Mouse_Buffer[0]的值,因为电脑的USB主机端会保留上一次USB从设备发送的信息一直执行动作,如果这样做的话,电脑上会一直响应鼠标右键或左键的功能,这点千万注意。



最后方然要编写main函数的代码了:

/******************************************************** 函数:main() 描述:程序入口地址 参数:无 返回:无 ********************************************************/ int main(void) { BSP_Init(); printf(" |===============================================|\r\n"); printf(" USB JoystickMouse 程序开始 \r\n"); printf("|===============================================|\r\n"); while (bDeviceState != CONFIGURED); while(1) { if (bDeviceState == CONFIGURED) //USB已经配置 { /* 检测按键状态,并发送鼠标位置数据 */ if (JoyState() != 0) //按键按下 { Joystick_Send(JoyState()); //发送鼠标变化量 } } } }

在mian函数里的while(1)中一直执行案件检测,一旦按键按下被检测到,就会立马发送状态给主机。



最后在贴出readme.text,好的编程习惯从早养起,编写readme能够让程序思路更清晰,弥补一些程序里不能直观表达的一些硬件结构和软件思维。

===================================================================================
下载方式
===================================================================================
SWD


JTAG
===================================================================================
程序功能
===================================================================================
模拟鼠标的上下移动和左右键
===================================================================================
硬件连接
===================================================================================
神州III开发板


开发板上WAKEUP按键模拟鼠标向右移动,连接引脚PA0


开发板上TENPER按键模拟鼠标向左移动,连接引脚PC13


开发板上USER1按键模拟鼠标向上移动,连接引脚PA8


开发板上USER2按键模拟鼠标想下移动,连接引脚PD3


开发板的PA4(用线连接)模拟鼠标的左键


开发板的PA5(用线连接)模拟鼠标的右键
===================================================================================
软件设置
===================================================================================
连接串口1,打开串口调试助手,选择好相应的COM口
按如下进行设置:
—————-
波特率 | 115200 |
—————-
数据位 |   8    |
—————-
停止位 |   1    |
—————-
校验位 | None   |
—————-
流控制 | None   |
—————-
===================================================================================
实验现象
===================================================================================
WAKEUP按键按下,鼠标向右移动
TEMPER按键按下,鼠标向左移动
USER1按键按下,鼠标向上移动
USER2按键按下,鼠标向下移动
PA4:鼠标左键现象
PA5:鼠标右键现象