我只是USB新手,我只是想叙述一下我如何 将JoystickMouse例程 改为 USB虚拟键盘的过程,以及一些仍未解决的疑惑。
我昨天补了1天的USB知识,对USB的枚举,描述符间的关系,控制传输有个大致清晰了解,但是并不深刻。(为此,我决定随后开始从PDIUSB12芯片开始学习起走)
首先,我们先打开JoystickMouse的例程,先下载进单片机看看,以确保单片机和USB之间的通信是正常(即硬件无问题),如果是正常的,驱动程序安装后,鼠标会跑
先看主函数.
int main(void)
{
#ifdef DEBUG
debug();
#endif
Set_System();
USB_Interrupts_Config();
Set_USBClock();
USB_Init();
while (1)
{
Delay(10000);
if ((JoyState() != 0) & (bDeviceState == CONFIGURED))
{
Joystick_Send(JoyState()); //这里便是发送信息至PC机的函数,这个信息可以代表很多设备,主要是看描述符的内容。
}
}
}
要想让Joystick_Send发送控制键盘的信息,我们需要更改描述符.
打开usb_desc.c usb_desc.h此处是用于编写USB描述符的.c 以及 .h文件
Joystick_DeviceDescriptor 是设备描述符,需要修改一下VID(厂商ID) PID(产品ID)
即更改
0x83, 0x04, /*idVendor (0x0483)*/
0x10, 0x57, /*idProduct = 0x5710*/
随意更改下数字即可,我是更改为
0x34, 0x12,/*idVendor (0x1234)*/
0x21, 0x43,/*idProduct = 0x4321*/
其实该处影响并不大,如果ID号不变的话,就会加载之前JoystickMouse的驱动程序(如果成功运行JoystickMouse例程),但是该驱动程序并不满足我们的需求,也就是说和虚拟键盘的描述符之间存在矛盾 .(加载的驱动程序不是我们想要的嘛,肯定就会有问题,换一个ID的话,会重新检测并加载新的驱动程序)
接着改Joystick_ConfigDescriptor,这是配置描述符(注意---配置描述符,接口描述符,HID描述符,端点描述符均包含在其中)
该处参考电脑圈圈的程序,更改为
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 */
};
这时候编译试试,会发现
error: #146too many initializer values的问题,很显然给配置描述符数组设定的大小太小了
打开usb_desc.h
将数组大小改为
#define JOYSTICK_SIZ_CONFIG_DESC 41 //是41个字节嘛~
然后改usb_desc.c里面的Joystick_ReportDescriptor(报告描述符)
我们仍然参考电脑圈圈的程序
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 */
好,尝试编译编译。
OK,编译成功,
但是千万不要认为就这样结束了!!!!!!!!!!
Joystick_ReportDescriptor[JOYSTICK_SIZ_REPORT_DESC]的数组大小还没改呢!
这个必须得改,不改的话,PC机识别不了人体输入设备。
我曾在这里花了很久的时间。。。。。。。这个大小必须改为实际大小
数数行数 1行2个,最后1行1个。
1共61个字节。
返回usb_desc.h中,
#define JOYSTICK_SIZ_REPORT_DESC 61
OK。
描述符就改完了。
现在描述符是用于处理键盘的了。
我们试试编译下载程序,打开设备管理器看看,发现是这样的
不用着急,还有些东西也需要改。
我们先打开usb_conf.h
由于我们程序里面会用到EndPoint1的收和发,所以我们需要定义一下EP1的tx buffer地址 和 rx_buffer地址
/* EP0 */
/* rx/tx buffer base address */
#define ENDP0_RXADDR (0x18)
#define ENDP0_TXADDR (0x58)
/* EP1 */
/* tx buffer base address */
#define ENDP1_TXADDR (0x100)
#define ENDP1_RXADDR (0xD8)
该处的地址估计可以为任意值,但是不能重复,而且需要一定间隔,具体间隔多少我也不是很清楚,希望有大侠给我说下间隔如何确定(不知道是不是Joystick_Reset.c中的SetEPTxCount(ENDP1, 8); 设置端点长度来确定)
就先不管这个吧~ 反正我们把地址给设置好了~
编译~ 没有问题~~
好,我们打开usb_prop.c
该文件适用于Joystick的初始化和复位等。
既然是初始化和复位 Init和Reset肯定比较重要~
首先我们找到Joystick_init
我们注释掉// Get_SerialNum();
即
void Joystick_init(void)
{
/* Update the serial number string descriptor with the data from the unique
ID*/
// Get_SerialNum();
pInformation->Current_Configuration = 0;
/* Connect the device */
PowerOn();
/* USB interrupts initialization */
_SetISTR(0); /* clear pending interrupts */
wInterrupt_Mask = IMR_MSK;
_SetCNTR(wInterrupt_Mask); /* set interrupts mask */
bDeviceState = UNCONNECTED;
}
编译下载看看,下载成功后,关机再打开,我们会发现设备管理器里面
PC机可以将其视为键盘了。说实话,我也不知道为什么,为啥把Get_SerialNum();注释掉,就可以成功找到键盘,取消注释的话,会发现仍然识别不了为键盘。知道的大侠能解释下吗?
就不管那个问题了,
我们接着改。
现在还没完了。
找到Joystick_Reset(void)
更改两个东西
SetEPTxCount(ENDP1, 8); //该处将端点1的长度设为8
// SetEPRxStatus(ENDP1, EP_RX_DIS); 此处注释掉
做到这里,我们离成功已经很近了。
此处重新找到Joystick_Send, 该函数在hw_config.c中
最开始我们就知道,这个函数是用于发送数据给USB的。
现在该数据已是键盘数据了。
我们需要更改下Joystick_Send内部的一些结构。
由于我们报告描述符是移植的电脑圈圈的程序,所以我们需要根据报告描述符来编写Joystick_Send
我们知道端点1的长度为8字节,
所以我们一次给USB发送的字节可以达到8字节。
根据报告描述符中定义的,第一个字节8个位,分别代表功能键的点击情况(功能键即为ctrl alt)等,第二个字节不适用,保留
第3到第8个字节 每个字节代表一个按键。
于是我们知道发送一次数据,最多可以按键6个(5个普通键,1个功能键)
#define KEY_SEL 0x01
#define KEY_RIGHT 0x02
#define KEY_LEFT 0x04
#define KEY_DOWN 0x10
#define KEY_UP 0x08
#define KEY_2 0x20
#define KEY_3 0x40
/*******************************************************************************
* Function Name : Joystick_Send.
* Description : prepares buffer to be sent containing Joystick event infos.
* Input : Keys: keys received from terminal.
* Output : None.
* Return value : None.
*******************************************************************************/
void Joystick_Send(u8 Keys)
{
u8 Buffer[8] = {0, 0, 0, 0, 0, 0, 0, 0};
u8 i;
i=2;
//对各个按键进行处理。注意,由于这里的摇杆5个按键
//不可能同时按下,所以返回的普通键数量不会超过6个。
//如果你的键盘同时按下的普通键能够超过6个的话,就需要做
//点特殊处理了,将后面6字节全部设置为0xFF,表示按键无法识别。
if(Keys&KEY_UP)
{
Buffer=0x52; //Keyboard UpArrow
i++;
}
if(Keys&KEY_DOWN)
{
Buffer=0x51; //Keyboard DownArrow
i++;
}
if(Keys&KEY_LEFT)
{
Buffer=0x50; //Keyboard LeftArrow
i++;
}
if(Keys&KEY_RIGHT)
{
Buffer=0x4F; //Keyboard RightArrow
i++;
}
if(Keys&KEY_2)
{
Buffer=0x39; //Keyboard Caps Lock
i++;
}
if(Keys&KEY_3)
{
Buffer=0x53; //Keypad Num Lock and Clear
i++;
}
if(Keys&KEY_SEL)
{
Buffer=0x28; //Keyboard Return (ENTER)
}
/*copy mouse position info in ENDP1 Tx Packet Memory Area*/
UserToPMABufferCopy(Buffer, GetEPTxAddr(ENDP1), 8); //发8个字节
/* enable endpoint for transmission */
SetEPTxValid(ENDP1);
}
将该内容整体替代Joystick_Send函数
里面有些#define定义,记得将这些定义也搬到main文件里面,这样可读性更强。
好了,到这里,我们的程序也就差不多了,
回到主函数
我们将while里面的东西全去掉
int main(void)
{
#ifdef DEBUG
debug();
#endif
Set_System();
USB_Interrupts_Config();
Set_USBClock();
USB_Init();
while (1)
{
;
}
}
我们测试下按键,
把while里面的写成
while(1)
{
Delay(10000);
Joystick_Send(KEY_RIGHT);
}
会发现光标一直忘右跑。
如果有这一现象,就代表之前都做对了。
也就实现了虚拟按键
值得注意的是,
Joystick_Send(KEY_RIGHT);
仅代表按键按下,并不代表松开
Joystick_Send(0)才代表松开,
Joystick_Send(0)发送出去为8个字节均是0,这样按键才松开。
大家或许发现我没有写接收端点,恩啊,我只需要发送功能,接收功能就没有写了~ O(∩_∩)O~~
大家可以根据电脑圈圈的来修改。
我来发一个我更改的例子吧~
以及电脑圈圈的例子,我也是仿写的
我也不知道我有没有理解正确~
另外还有为啥注释掉Get_SerialNum();才能成功枚举到主机,也就是上述红 {MOD}的字段。我是百思不得其解。
最后还有个问题
USB初始化成功后,我如此写
Delay(10000);
Joystick_Send(KEY_RIGHT);
while(1)
光标并没有任何动作,这是为什么呀?
只有改为在循环内部,才会有作用。。。
我最开始怀疑是延时不够,但是我设了很大的延时,仍然没能起作用。(一定运行到了Joystick_Send了的)。
还没到看懂这些的地步。
一周热门 更多>