好像HID用途里面有个通用桌面用途,有专门的关机指令,但是不会通用桌面用途的报告表抒写,所以就通过虚拟按键以此实现关机。
WIN7下关机快捷键:WIN+←+Enter
因此我们只需要模拟以上操作即可。
拷贝原子哥触控usb实验中的USB文件夹,新建一个工程。
添加文件后,编写程序如下
#include <stm32f10x_lib.h>
#include "sys.h"
#include "delay.h"
#include "usb_lib.h"
#include "hw_config.h"
#include "usb_pwr.h"
int main(void)
{
Stm32_Clock_Init(9);
delay_init(72);
//USB配置
USB_Interrupts_Config();
Set_USBClock();
USB_Init();
while(1);
}
下入单片机后,重启单片机,查看设备管理器,可以发现鼠标设备
接下来可以开始更改程序了:
原子哥的描述符是用于鼠标的,因此需要修改usb_desc.c中的描述符内容,修改为键盘。
这里直接使用已经修改好了的,用于键盘的即可
const u8 Joystick_DeviceDescriptor[JOYSTICK_SIZ_DEVICE_DESC] =
{
0x12, /*bLength */
USB_DEVICE_DESCRIPTOR_TYPE, /*bDescriptorType*/
0x00, /*bcdUSB */
0x02,
0x00, /*bDeviceClass*/
0x00, /*bDeviceSubClass*/
0x00, /*bDeviceProtocol*/
0x40, /*bMaxPacketSize40*/
0x34, /*idVendor (0x1234)*/
0x12,
0x21, /*idProduct = 0x4321*/
0x44,
0x00, /*bcdDevice rel. 2.00*/
0x02,
1, /*Index of string descriptor describing
manufacturer */
2, /*Index of string descriptor describing
product*/
3, /*Index of string descriptor describing the
device serial number */
0x01 /*bNumConfigurations*/
}
; /* Joystick_DeviceDescriptor */
const u8 Joystick_ConfigDescriptor[JOYSTICK_SIZ_CONFIG_DESC] =
{
//以下为配置描述符
0x09, /* bLength: Configuation Descriptor size */
USB_CONFIGURATION_DESCRIPTOR_TYPE, /* bDescriptorType: Configuration */
JOYSTICK_SIZ_CONFIG_DESC,
/* wTotalLength: Bytes returned */
0x00,
0x01, /*bNumInterfaces: 1 interface*/
0x01, /*bConfigurationValue: Configuration value*/
0x00, /*iConfiguration: Index of string descriptor describing
the configuration*/
0xC0, /*bmAttributes: self powered */
0x32, /*MaxPower 100 mA: this current is used for detecting Vbus*/
//以下为接口描述符
/************** Descriptor of Joystick Mouse interface ****************/
/* 09 */
0x09, /*bLength: Interface Descriptor size*/
USB_INTERFACE_DESCRIPTOR_TYPE,/*bDescriptorType: Interface descriptor type*/
0x00, /*bInterfaceNumber: Number of Interface*/
0x00, /*bAlternateSetting: Alternate setting*/
0x02, /*bNumEndpoints*/
0x03, /*bInterfaceClass: HID*/
0x01, /*bInterfaceSubClass : 1=BOOT, 0=no boot*/
0x01, /*bInterfaceProtocol : 0=none, 1=keyboard, 2=mouse*/
0, /*iInterface: Index of string descriptor*/
//以下为HID描述符
/******************** Descriptor of Joystick Mouse HID ********************/
/* 18 */
0x09, /*bLength: HID Descriptor size*/
HID_DESCRIPTOR_TYPE, /*bDescriptorType: HID*/
0x00, /*bcdHID: HID Class Spec release number*/
0x01,
0x00, /*bCountryCode: Hardware target country*/
0x01, /*bNumDescriptors: Number of HID class descriptors to follow*/
0x22, /*bDescriptorType*/
JOYSTICK_SIZ_REPORT_DESC,/*wItemLength: Total length of Report descriptor*/
0x00,
//以下为输入端点1描述符
/******************** Descriptor of Joystick Mouse endpoint ********************/
/* 27 */
0x07, /*bLength: Endpoint Descriptor size*/
USB_ENDPOINT_DESCRIPTOR_TYPE, /*bDescriptorType:*/
0x81, /*bEndpointAddress: Endpoint Address (IN)*/
0x03, /*bmAttributes: Interrupt endpoint*/
0x08, /*wMaxPacketSize: 8 Byte max */
0x00,
0x20, /*bInterval: Polling Interval (32 ms)*/
//以下为输出端但1描述符
/* 34 */
0x07, /*bLength: Endpoint Descriptor size*/
USB_ENDPOINT_DESCRIPTOR_TYPE, /*bDescriptorType:*/
0x01, /*bEndpointAddress: Endpoint Address (OUT)*/
0x03, /*bmAttributes: Interrupt endpoint*/
0x08, /*wMaxPacketSize: 8 Byte max */
0x00,
0x20, /*bInterval: Polling Interval (32 ms)*/
/* 41 */
}; /* MOUSE_ConfigDescriptor */
const u8 Joystick_ReportDescriptor[JOYSTICK_SIZ_REPORT_DESC] =
{
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x06, // USAGE (Keyboard)
0xa1, 0x01, // COLLECTION (Application)
0x05, 0x07, // USAGE_PAGE (Keyboard/Keypad)
0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl)
0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x95, 0x08, // REPORT_COUNT (8)
0x75, 0x01, // REPORT_SIZE (1)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x08, // REPORT_SIZE (8)
0x81, 0x03, // INPUT (Cnst,Var,Abs)
0x95, 0x06, // REPORT_COUNT (6)
0x75, 0x08, // REPORT_SIZE (8)
0x25, 0xFF, // LOGICAL_MAXIMUM (255)
0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated))
0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application)
0x81, 0x00, // INPUT (Data,Ary,Abs)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x95, 0x02, // REPORT_COUNT (2)
0x75, 0x01, // REPORT_SIZE (1)
0x05, 0x08, // USAGE_PAGE (LEDs)
0x19, 0x01, // USAGE_MINIMUM (Num Lock)
0x29, 0x02, // USAGE_MAXIMUM (Caps Lock)
0x91, 0x02, // OUTPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x06, // REPORT_SIZE (6)
0x91, 0x03, // OUTPUT (Cnst,Var,Abs)
0xc0 // END_COLLECTION
}; /* Joystick_ReportDescriptor */
对于报告描述符(Joystick_ReportDescriptor)以下的内容不需作任何修改。因为后面的东西和用途无关,只是一些诶字符串内容而已。
由于更改的时候,配置描述符和报告描述符的字节数有变化,所以需要在usb_desc.h中对上述两种描述符的长度作修改。
#define JOYSTICK_SIZ_CONFIG_DESC 41
#define JOYSTICK_SIZ_REPORT_DESC 61
此时主函数中只需初始化时钟和USB配置,关闭单片机,再开机即可发现
已经识别为键盘了。
书写描述符中有对端点1输入输出进行描述,端点1的输入原子哥的代码已经配置好了,现在需要配置端点1的输出。
首先对端点1设置地址
在usb_conf.h中
#define ENDP1_TXADDR (0x100)
#define ENDP1_RXADDR (0x80) //设置端点1输出地址
接着在usb_prop.c文件中的void Joystick_Reset(void)下设置端点1的输入输出属性
更改为以下
void Joystick_Reset(void)
{
/* Set Joystick_DEVICE as not configured */
pInformation->Current_Configuration = 0;
pInformation->Current_Interface = 0;/*the default Interface*/
SetBTABLE(BTABLE_ADDRESS);
/* Initialize Endpoint 0 */
SetEPType(ENDP0, EP_CONTROL);
SetEPTxStatus(ENDP0, EP_TX_STALL);
SetEPRxAddr(ENDP0, ENDP0_RXADDR);
SetEPTxAddr(ENDP0, ENDP0_TXADDR);
Clear_Status_Out(ENDP0);
SetEPRxCount(ENDP0, Device_Property.MaxPacketSize);
SetEPRxValid(ENDP0);
/* Initialize Endpoint In 1 */
SetEPType(ENDP1, EP_INTERRUPT); //初始化为中断端点类型
SetEPTxAddr(ENDP1, ENDP1_TXADDR); //设置发送数据的地址
SetEPTxCount(ENDP1, 8); //设置发送的长度
// SetEPRxStatus(ENDP1, EP_RX_DIS);
SetEPTxStatus(ENDP1, EP_TX_NAK); //设置端点处于忙状态
/* Initialize Endpoint Out 1 */
SetEPRxAddr(ENDP1, ENDP1_RXADDR); //设置接收数据的地址
SetEPRxCount(ENDP1, 1); //设置接收长度
SetEPRxStatus(ENDP1, EP_RX_VALID); //设置端点有效,可以接收数据
/* Set this device to response on default address */
SetDeviceAddress(0);
}
接着需要写端点1的输出回调函数
该函数是当端点1接收到PC机输出的数据,便会调用该函数,该函数通常用于获得电脑发送出来的数据,由于我们这里并不需要电脑发送过来的数据(我们的目的只是发送虚拟按键给电脑)
因此该处函数只需如此处理即可
void EP1_OUT_Callback(void)
{
SetEPRxValid(ENDP1); //设置端点有效,以接收下一次数据 没有该句,便只能接收一次。
}
函数在usb_istr.c中编写,编写前需要看看usb_conf.h中,有个#define EP1_OUT_Callback NOP_Process的定义,我们需要这个回调函数,所以将该定义注释掉。
到此,我们成功的完成了端点1的输出。
剩下的只需要编写输入的回调函数。
输入很简单,同样在usb_conf.h中 注释掉#define EP1_IN_Callback NOP_Process
EP1_IN_Callback函数会在数据发送后调用。
在usb_istr.c中编写
u8 EP1BUSY=0;
void EP1_IN_Callback(void)
{
EP1BUSY=0; //发送成功后,清除端点1繁忙标志
}
这里用到了EP1BUSY的标志变量,是为了避免端点1繁忙时 发送数据,进入该回调前,数据一直在发送缓冲区,并没有发送成功,进入中断,就代表发送缓冲区已空,可以进行下一次发送。
现在需要更改Joystick_Send函数该函数的功能是打包数据,将数据发送给PC机,该数据包里面就有虚拟按键的数据。
这个函数在hw_config.h中。
我们做简单一点就可以了
void Joystick_Send(u8 buf0,u8 buf1)
{
u8 Mouse_Buffer[8] = {0, 0, 0, 0, 0, 0, 0, 0};
/* prepare buffer to send */
Mouse_Buffer[0]=buf0; //根据报告描述符,第0个字节为功能键,其中win键也在其中
Mouse_Buffer[2]=buf1; //根据报告描述符,第1字节为保留,后面的字节才为普通按键
/*copy mouse position info in ENDP1 Tx Packet Memory Area*/
UserToPMABufferCopy(Mouse_Buffer, GetEPTxAddr(ENDP1), 8);
/* enable endpoint for transmission */
SetEPTxValid(ENDP1);
}
到此,我们已经完成了虚拟鼠标到虚拟键盘的修改。
一下我贴出我的实现源码,关于具体数值对应的虚拟按键为什么,请参考HID用图表。
#include <stm32f10x_lib.h>
#include "sys.h"
#include "delay.h"
#include "usb_lib.h"
#include "hw_config.h"
#include "usb_pwr.h"
extern u8 EP1BUSY;
u8 Key[3] = {0x00, 0x50, 0x28};
u8 Win[3] = {0x80, 0, 0};
void Press(void);
int main(void)
{
Stm32_Clock_Init(9);//系统时钟设置
delay_init(72); //延时初始化
//USB配置
USB_Interrupts_Config();
Set_USBClock();
USB_Init();
while(1)
{
if(bDeviceState==CONFIGURED)
{
if (EP1BUSY==0)
{
EP1BUSY=1;
  ress();
}
}
}
}
void Press(void)
{
static u32 i=0;
static u8 j=0;
if(i%2)
{
i++;
Joystick_Send(Win[j],Key[j]);
j++;
if(j>=3)
j=0;
}
else if(i%2 == 0)
{
i++;
Joystick_Send(0,0);
}
}
在这里,插入USB,就可以实现关机了。
|