STM32 JoystickMouse USB游戏杆鼠标的实现

发布者:跳跃龙珠最新更新时间:2016-12-20 来源: eefocus关键字:STM32  JoystickMouse  USB  游戏杆鼠标 手机看文章 扫描二维码
随时随地手机看文章

本次程序的实现是基于上次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:鼠标右键现象


关键字:STM32  JoystickMouse  USB  游戏杆鼠标 引用地址:STM32 JoystickMouse USB游戏杆鼠标的实现

上一篇:STM32的CustomHID的各描述符介绍
下一篇:STM32 keyboard USB键盘功能的实现

推荐阅读最新更新时间:2024-03-16 15:26

STM32学习笔记之内存结构
本文以STM32F103ZE为原型,来剖析其内存结构,从而了解其内存物理地址,分配结构以扩展应用。 STM32F103ZE这款芯片内置了32KB的SRAM,512KB的Flash,其映射地址如下图所示,该图来源于其数据手册 。其中Flash的起始地址为0x0800 0000,SRAM的起始地址为0x2000 0000,外设的起始地址为0x4000 0000,地址为0x4002 2000是控制Flash的寄存器地址。 参考链接: 【1】 每个程序员都应该了解的内存知识 【2】 STM32片上Flash内存映射、页面大小、突破口映射
[单片机]
<font color='red'>STM32</font>学习笔记之内存结构
USB的便携式ARINC429总线通信设备技术
USB的便携式ARINC429总线通信设备技术 ARINC429是航空电子系统之间最常用的通信总线之一。要在计算机上实现与机载设备的ARINC429总线数据通信,必须实现429总线与计算机总线之间的数据传输。 在航空电子综合化系统中,快速、有效的数据传输对整个航空电子系统的性能有很大影响,因此数据总线被称为现代航空电子系统的“骨架”。 本文设计了基于USB总线的便携式ARINC429总线通信设备,并通过实际运行测试,对该设备的可靠性和稳定性进行了验证。 1 系统总体设计 1.1 系统功能分析 该系统主要分为3大功能单元:中央控制单元、429数据收发单元、429电平转换单元。系统的功能结构框图如图1所示。中央控制单元与
[模拟电子]
<font color='red'>USB</font>的便携式ARINC429总线通信设备技术
stm32之TIM-高级定时器应用实例一(详细)
硬件:stm32f103c8t6 开发工具:Keil uVision4 下载调试工具:ARM仿真器 如果第一次接触定时器,可以先看基本定时器。本篇内容较多,如果想直接动手操作,可以跳到后面的实验代码。 stm32标准库对定时器外设建立了4个初始化结构体,定时器分为基本定时器、通用定时器、高级定时器,针对不用的定时器要使用不同初始化结构体。下面是4个初始化结构体的适用分类: TIM_TimeBaseInitTypeDef (基本定时器、通用定时器、高级定时器) TIM_OCInitTypeDef (通用定时器、高级定时器) TIM_ICInitTypeDef (通用定时器、高级定时器
[单片机]
<font color='red'>stm32</font>之TIM-高级定时器应用实例一(详细)
基于stm32的 ucGUI 12864下的移植
ucGUI是纯C写的的,移植需要定义点阵数,颜色数,和画点函数 以下是 ucGUI 12864下的移植 基于ST7920控制的12864液晶用于字符显示很方便的,但网友说用它显示图形并不合适,原因就是它绘图时先要关闭显示,绘完后又要打开,速度会较慢。我没有用过别的液晶,手中只有这一款,摆弄了几天,掌握了一点东西,写出来共享。 首先,我们知道,图形都是由像素点组成的,绘图的基础其实就是画点。只要我们能点亮液晶的任意一个像素点,那么绘图就不是什么难事了。万丈高楼平地起嘛,先要做的,当然是要打好基础。 ST7920提供了用于绘图的GDRAM(graph display RAM)。共 64×32,64是 个字节的空间(由扩充指令设定
[单片机]
基于<font color='red'>stm32</font>的 ucGUI 12864下的移植
STM32G0生态系统将扩展USB-C标准接口
通过微控制器对USB Type-C™和Power Delivery(PD) 3.0 认证协议的支持和 STM32Cube生态系统新增的工具和功能,意法半导体正在逐步缩短STM32G0 *用户的学习曲线。 STM32G0微控制器是世界上首个支持USB Type-C规格的通用微控制器。意法半导体创新的USB Type-C Power Delivery (UCPD)接口IP模块整合通过认证的USB Type-C连接器管理和PD协议处理与微控制器功能,包括Arm®Cortex®-M0 +内核、高达512 KB的闪存以及外围设备,例如,12位2.5 MSPS ADC、2通道DAC、快速比较器和高精度定时器。 片上集成多达两个U
[嵌入式]
STM32G0生态系统将扩展<font color='red'>USB</font>-C标准接口
郭明錤:苹果双USB-C充电器即将量产,出货有望达200-300万款
IT之家 4 月 9 日消息,9to5Mac 今天曝光了一款苹果可能即将推出的双口 35W USB-C 充电器。支持文件只在苹果网站上出现了很短的时间,但其中明确提到了未发布的充电器,但这似乎只是其中之一。   IT之家曾报道,天风国际证券分析师 @郭明錤 此前表示,苹果可能会在今年某个时候推出下一代氮化镓充电器,最高支持 30W 快充,并且采用全新的外形。   在今日的 35W 充电器被曝光后,郭明錤声称该项目“已接近量产”,预计今年出货量可达 200-300 万款。虽然它的发布时间目前还没有明确消息,但苹果肯定会在年底前推出。   苹果目前还没有推出双 USB-C 接口的充电器,因此这可能是苹果正在开发的产品。35W
[手机便携]
郭明錤:苹果双<font color='red'>USB</font>-C充电器即将量产,出货有望达200-300万款
STM32 GPIO的8种工作模式及相关配置寄存器
GPIO八种工作模式 四种输入: GPIO_Mode_IPU(上拉输入) GPIO_Mode_IPD(下拉输入) 原理: 经过上拉开关和下拉开关的连接,再经过触发器转化为0,1的数字信号,存储到数据寄存器中,然后我们就可以通过配置寄存器CRL,CRH控制这两个开关。 用法: 若GPIO引脚配置为上拉输入模式,在默认状态下(GPIO引脚无输入),取得的GPIO引脚数据为1,既高电平. 而下拉输入模式则是相反的,在默认状态下其引脚数据为0,低电平. GPIO_Mode_IN_FLOATING(浮空输入) 原理: 不接上拉和下拉开关,直接经由触发器输入. 用法: 若配置成这个模式可以用电表测量其引脚电压是1点几伏(不确定的值).由于输
[单片机]
基于P89C61x2和ISP1581的USB接口电路的设计与实现
本文主要针对传统仪器的并行接口设计了一种基于单片机的接口电路。 主要芯片介绍 本设计采用控制芯片P89C61x2和接口芯片ISP1581实现USB接口电路的设计。 P89C61x2包含1024B RAM、64KB Flash存储器、32个I/O口、3个16位定位/计数器、6个中断源-4个中断优先级-嵌套的中断结构、1个增强型UART、片内振荡器和时钟电路。此外,器件的静态设计使其具有非常宽的频率范围,可选择1MHz~12MHz的晶体振荡器。具有两个软件可选的节电模式-空闲模式和掉电模式。 USB接口芯片ISP1581是一种价格低、功能强的USB接口器件,符合USB2.0规范,并为基于微控制器或微处理器的系统提供了高速USB通信
[应用]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved