本次程序的实现是基于上次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的CustomHID的各描述符介绍
下一篇:STM32 keyboard USB键盘功能的实现
推荐阅读最新更新时间:2024-03-16 15:26
设计资源 培训 开发板 精华推荐
- 微灵医疗李骁健:脑机接口技术正在开启意识与AI融合的新纪元
- USB Type-C® 和 USB Power Delivery:专为扩展功率范围和电池供电型系统而设计
- 景昱医疗耿东:脑机接口DBS治疗技术已实现国产替代
- 首都医科大学王长明:针对癫痫的数字疗法已进入使用阶段
- 非常见问题解答第223期:如何在没有软启动方程的情况下测量和确定软启动时序?
- 兆易创新GD25/55全系列车规级SPI NOR Flash荣获ISO 26262 ASIL D功能安全认证证书
- 新型IsoVu™ 隔离电流探头:为电流测量带来全新维度
- 英飞凌推出简化电机控制开发的ModusToolbox™电机套件
- 意法半导体IO-Link执行器电路板为工业监控和设备厂商带来一站式参考设计
- Melexis采用无磁芯技术缩小电流感测装置尺寸