STM32完成USB_Keyboard的实验总结
实验平台: 正点原子战舰开发板(STM32F103ZET6) + win7(PC),下位机程序是通过战舰开发板上“实验49触摸USB鼠标”更改.
实验目的: 使用STM32F103自带的USB模块编程实现下位机与PC的USB通讯,使用开发板上自带的触目屏作为键盘输入,在PC的记事本中显示对应按键.
实验心得: 实验中碰到问题最多的地方就是设备的枚举不成功.若设备的枚举成功,后面的处理就会简单很多.通讯成功后就可以使用USBlyzer软件抓取数据包,解析通讯数据.
1. USB_Keyboard的枚举过程
以下代码是使用串口助手获取的数据,记录了USB_Keyboard枚举过程的部分数据,主要是通过Endpoint0的Setup完成枚举,代码中有部分注释,详细的枚举过程请参考USB2.0协议或参考《圈圈教你玩USB》.
设备复位
设备复位
SetUp阶段......
Host 输入信息
USBbmRequestType = 0x80 Get_Configuaration
USBbRequest = 0x06 Get_Descriptor
USBwValue = 0x01 Device Descriptor
USBwIndex = 0x00
USBwLength = 0x40 Max Bytes
数据输出
0x12 0x01 0x00 0x02 0x00 0x00 0x00 0x40 0x83
0x04 0x10 0x57 0x00 0x02 0x01 0x02 0x03 0x01
const u8 Keyboard_DeviceDescriptor[KEYBOARD_SIZ_DEVICE_DESC] =
{
0x12, /*bLength */
USB_DEVICE_DESCRIPTOR_TYPE, /*bDescriptorType*/
0x00, /*bcdUSB */
0x02,
0x00, /*bDeviceClass*/
0x00, /*bDeviceSubClass*/
0x00, /*bDeviceProtocol*/
0x40, /*bMaxPacketSize40*/
0x83, /*idVendor (0x0483)*/
0x04,
0x10, /*idProduct = 0x5710*/
0x57,
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*/
}
; /* Keyboard_DeviceDescriptor */
设备复位
SetUp阶段......
Host 输入信息
USBbmRequestType = 0x00 Set_Address
USBbRequest = 0x05
USBwValue = 0x0004 Address = 0x04
USBwIndex = 0x00
USBwLength = 0x00
SetUp阶段......
Host 输入信息
USBbmRequestType = 0x80 Get_Configuaration
USBbRequest = 0x06 Get_Descriptor
USBwValue = 0x01 Device Descriptor
USBwIndex = 0x00
USBwLength = 0x12
数据输出
0x12 0x01 0x00 0x02 0x00 0x00 0x00 0x40 0x83
0x04 0x10 0x57 0x00 0x02 0x01 0x02 0x03 0x01
SetUp阶段......
Host 输入信息
USBbmRequestType = 0x80 Get_Configuaration
USBbRequest = 0x06 Get_Descriptor
USBwValue = 0x02 Configuaration + Interface + String + Endpoint Descriptor
USBwIndex = 0x00
USBwLength = 0xff
数据输出
0x09 0x02 0x29 0x00 0x01 0x01 0x00 0xe0 0x32
0x09 0x04 0x00 0x00 0x02 0x03 0x01 0x01 0x00
0x09 0x21 0x00 0x01 0x00 0x01 0x22 0x3d 0x00
0x07 0x05 0x81 0x03 0x08 0x00 0x20 0x07 0x05
0x01 0x03 0x08 0x00 0x20
const u8 Keyboard_ConfigDescriptor[KEYBOARD_SIZ_CONFIG_DESC] =
{
0x09, /* bLength: Configuation Descriptor size */
USB_CONFIGURATION_DESCRIPTOR_TYPE, /* bDescriptorType: Configuration */
KEYBOARD_SIZ_CONFIG_DESC,
/* wTotalLength: Bytes returned */
0x00,
0x01, /*bNumInterfaces: 1 interface*/
0x01, /*bConfigurationValue: Configuration value*/
0x00, /*iConfiguration: Index of string descriptor describing
the configuration*/
0xE0, /*bmAttributes: bus powered */
0x32, /*MaxPower 100 mA: this current is used for detecting Vbus*/
/************** Descriptor of Keyboard 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, /*nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse*/
0, /*iInterface: Index of string descriptor*/
/******************** Descriptor of Keyboard 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*/
KEYBOARD_SIZ_REPORT_DESC,/*wItemLength: Total length of Report descriptor*/
0x00,
/******************** Descriptor of Keyboard 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)*/
/******************** Descriptor of Keyboard Mouse endpoint ********************/
/* 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 */
};
SetUp阶段......
Host 输入信息
USBbmRequestType = 0x80
USBbRequest = 0x6
USBwValue = 0x0300 03:String_descripotr 00:Index
USBwIndex = 0x00
USBwLength = 0xff
数据输出
0x04 0x03 0x09 0x04
const u8 Keyboard_StringLangID[KEYBOARD_SIZ_STRING_LANGID] =
{
KEYBOARD_SIZ_STRING_LANGID,
USB_STRING_DESCRIPTOR_TYPE,
0x09,
0x04
}
; /* LangID = 0x0409: U.S. English */
SetUp阶段......
Host 输入信息
USBbmRequestType = 0x80
USBbRequest = 0x06
USBwValue = 0x0302 03:String_descripotr 02:Index
USBwIndex = 0x0409
USBwLength = 0xff
数据输出
0x20 0x03 0x48 0x00 0x41 0x00 0x49 0x00 0x59
0x00 0x41 0x00 0x4e 0x00 0x47 0x00 0x20 0x00
0xe6 0x89 0xa7 0x63 0x55 0x00 0x53 0x00 0x42
0x00 0x2e 0x95 0xd8 0x76
//使用UNICODE编码
const u8 Keyboard_StringProduct[KEYBOARD_SIZ_STRING_PRODUCT] =
{
KEYBOARD_SIZ_STRING_PRODUCT, /* bLength */
USB_STRING_DESCRIPTOR_TYPE, /* bDescriptorType */
'H', 0, 'A', 0, 'I', 0, 'Y', 0, 'A', 0, 'N', 0, 'G', 0,
' ', 0,
0XE6,0X89,//触
0XA7,0X63,//控
'U', 0,
'S', 0,
'B', 0,
0X2e,0X95,//键
0Xd8,0X76,//盘
};
SetUp阶段......
Host 输入信息
USBbmRequestType = 0x80
USBbRequest = 0x06
USBwValue = 0x0303 03:String_descripotr 03:Index
USBwIndex = 0x0409
USBwLength = 0xff
数据输出
0x1a 0x03 0x30 0x00 0xff 0x00 0xda 0x00 0x05
0x00 0x42 0x00 0x47 0x00 0x31 0x00 0x36 0x00
0x39 0x00 0x65 0x00
u8 Keyboard_StringSerial[KEYBOARD_SIZ_STRING_SERIAL] =
{
KEYBOARD_SIZ_STRING_SERIAL, /* bLength */
USB_STRING_DESCRIPTOR_TYPE, /* bDescriptorType */
'S', 0, 'T', 0, 'M', 0, '3', 0, '2', 0, '1', 0, '0', 0
}; 0x08 0x00 0x43 0x00
SetUp阶段......
Host 输入信息
USBbmRequestType = 0x80
USBbRequest = 0x06
USBwValue = 0x06
USBwIndex = 0x00
USBwLength = 0xa
SetUp阶段......
Host 输入信息
USBbmRequestType = 0x80
USBbRequest = 0x06
USBwValue = 0x01
USBwIndex = 0x00
USBwLength = 0x12
数据输出
0x12 0x01 0x00 0x02 0x00 0x00 0x00 0x40 0x83
0x04 0x10 0x57 0x00 0x02 0x01 0x02 0x03 0x01
SetUp阶段......
Host 输入信息
USBbmRequestType = 0x80
USBbRequest = 0x06
USBwValue = 0x02
USBwIndex = 0x00
USBwLength = 0x09
数据输出
0x09 0x02 0x29 0x00 0x01 0x01 0x00 0xe0 0x32
SetUp阶段......
Host 输入信息
USBbmRequestType = 0x80
USBbRequest = 0x06
USBwValue = 0x02
USBwIndex = 0x00
USBwLength = 0x29
数据输出
0x09 0x02 0x29 0x00 0x01 0x01 0x00 0xe0
0x32 0x09 0x04 0x00 0x00 0x02 0x03 0x01
0x01 0x00 0x09 0x21 0x00 0x01 0x00 0x01
0x22 0x3d 0x00 0x07 0x05 0x81 0x03 0x08
0x00 0x20 0x07 0x05 0x01 0x03 0x08 0x00
0x20
SetUp阶段......
Host 输入信息
USBbmRequestType = 0x00
USBbRequest = 0x09 set_configuration
USBwValue = 0x0001 select configuration
USBwIndex = 0x00 set current configuration = 0x01
USBwLength = 0x00
SetUp阶段......
Host 输入信息
USBbmRequestType = 0x21 HID
USBbRequest = 0x0a set_idle
USBwValue = 0x00
USBwIndex = 0x00
USBwLength = 0x00
SetUp阶段......
Host 输入信息
USBbmRequestType = 0x80
USBbRequest = 0x06
USBwValue = 0x06
USBwIndex = 0x00
USBwLength = 0x0a
SetUp阶段......
Host 输入信息
USBbmRequestType = 0x81 Interface_request
USBbRequest = 0x06
USBwValue = 0x22 Report_descriptor
USBwIndex = 0x00
USBwLength = 0x7d
数据输出
0x05 0x01 0x09 0x06 0xa1 0x01 0x05 0x07
0x19 0xe0 0x29 0xe7 0x15 0x00 0x25 0x01
0x95 0x08 0x75 0x01 0x81 0x02 0x95 0x01
0x75 0x08 0x81 0x03 0x95 0x06 0x75 0x08
0x25 0xff 0x19 0x00 0x29 0x65 0x81 0x00
0x25 0x01 0x95 0x02 0x75 0x01 0x05 0x08
0x19 0x01 0x29 0x02 0x91 0x02 0x95 0x01
0x75 0x06 0x91 0x03 0xc0
const u8 Keyboard_ReportDescriptor[KEYBOARD_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
}
; /* Keyboard_ReportDescriptor */
SetUp阶段......
Host 输入信息
USBbmRequestType = 0x80
USBbRequest = 0x06
USBwValue = 0x0301
USBwIndex = 0x0409
USBwLength = 0xff
数据输出
0x10 0x03 0x77 0x6d 0x0b 0x0d 0x35 0x75
0x50 0x5b 0xe5 0x5d 0x5c 0x4f 0xa4 0x5b
SetUp阶段......
Host 输入信息
USBbmRequestType = 0x80
USBbRequest = 0x06
USBwValue = 0x0303
USBwIndex = 0x0409
USBwLength = 0xff
数据输出
0x1a 0x03 0x30 0x00 0xff 0x00 0xda 0x00
0x05 0x00 0x42 0x00 0x47 0x00 0x31 0x00
0x36 0x00 0x39 0x00 0x65 0x00 0x08 0x00
0x43 0x00
SetUp阶段......
Host 输入信息
USBbmRequestType = 0x21
USBbRequest = 0x0a
USBwValue = 0x00
USBwIndex = 0x00
USBwLength = 0x00
SetUp阶段......
Host 输入信息
USBbmRequestType = 0x80
USBbRequest = 0x06
USBwValue = 0x06
USBwIndex = 0x00
USBwLength = 0x0a
SetUp阶段......
Host 输入信息
USBbmRequestType = 0x21
USBbRequest = 0x0a
USBwValue = 0x00
USBwIndex = 0x00
USBwLength = 0x00
SetUp阶段......
Host 输入信息
USBbmRequestType = 0x80
USBbRequest = 0x06
USBwValue = 0x0301
USBwIndex = 0x0409
USBwLength = 0xff
数据输出
0x10 0x03 0x77 0x6d 0x0b 0x0d 0x35 0x75
0x50 0x5b 0xe5 0x5d 0x5c 0x4f 0xa4 0x5b
SetUp阶段......
Host 输入信息
USBbmRequestType = 0x80
USBbRequest = 0x06
USBwValue = 0x0303
USBwIndex = 0x0409
USBwLength = 0xff
数据输出
0x1a 0x03 0x30 0x00 0xff 0x00 0xda 0x00
0x05 0x00 0x42 0x00 0x47 0x00 0x31 0x00
0x36 0x00 0x39 0x00 0x65 0x00 0x08 0x00
0x43 0x00
SetUp阶段......
Host 输入信息
USBbmRequestType = 0x21
USBbRequest = 0x0a
USBwValue = 0x00
USBwIndex = 0x00
USBwLength = 0x00
在usb_core.c文件中Endpoint0处理函数中增加代码,将USB主机与USB设备通讯的数据都通过串口打印出来,通过打印的数据来分析USB设备的枚举过程对理解程序很有帮助,能够更好的了解设备的枚举过程.建议多看一下usb_core.c文件,里面有详细的处理过程,对以后编写程序有帮助.
实验过程中,按照网上查找的资料更改usb_des.c文件中的报告描述符,反复试了几个版本都无法完成设备的枚举,后来在论坛中找到了一份描述符才能够完成设备的枚举,报告描述符在上面的代码中,有需要的请参考.
2. 模拟键盘发送数据
设备成功枚举后,就可以修改程序完成键盘数据的发送了.
本实验因使用2.8‘触摸屏,尺寸较小,因此每次只发送一个按键值,实现简单功能.
首先对Endpoint1进行设置.在usb_proc.c文件的Keyboard_Reset函数中增加Endpoint1接收地址等设置.如下,已标红色.
-
void Keyboard_Reset(void) -
{ -
/* Set Keyboard_DEVICE as not configured */ -
pInformation->Current_Configuration = 0; -
pInformation->Current_Interface = 0;/*the default Interface*/ -
-
/* Current Feature initialization */ -
pInformation->Current_Feature = Keyboard_ConfigDescriptor[7]; -
-
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 1 */ -
SetEPType(ENDP1, EP_INTERRUPT); -
SetEPTxAddr(ENDP1, ENDP1_TXADDR); -
SetEPTxCount(ENDP1, 8); -
//SetEPRxStatus(ENDP1, EP_RX_DIS); -
SetEPTxStatus(ENDP1, EP_TX_NAK); -
-
SetEPRxAddr(ENDP1, ENDP1_RXADDR); -
SetEPRxCount(ENDP1, 1); -
SetEPRxStatus(ENDP1, EP_RX_VALID); -
-
bDeviceState = ATTACHED; -
-
/* Set this device to response on default address */ -
SetDeviceAddress(0); -
}
编写键盘按键值发送函数.
-
const u8 Keyboard_Value[] = {0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x04, 0x27, //1, 2, 3, 4, 5, 6, 7, 8, 9, a, 0, -
0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, //b, c, d, e, f, g, h, i, j, k, l, -
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, //m, n, o, p, q, r, s, t -
}; -
void Keyboard_Send(u8 keys) -
{ -
u8 Keyboard_Buffer[8] = {0, 0, 0, 0, 0, 0, 0, 0}; -
-
u8 i; -
-
i = 2; -
if(keys) -
{ -
Keyboard_Buffer[i] = Keyboard_Value[keys - 1]; -
i++; -
} -
if(keys != 0) flag = 1; -
else flag = 0; -
/*copy mouse position info in ENDP1 Tx Packet Memory Area*/ -
UserToPMABufferCopy(Keyboard_Buffer, GetEPTxAddr(ENDP1), 8); -
/* enable endpoint for transmission */ -
SetEPTxValid(ENDP1); -
}
最初时程序编译下载后,当有键按下时会一直发送同一按键值.上网查找资料后发现在没有接收到新的数据时PC会一直认为接收到上次数据,所以在发送完一个按键后需要发送 一包数据“00“(8个0x00)到PC,这样就不会误操作了.手边正好有一个Dell的USB键盘连接到电脑上使用USBlyzer软件抓取Dell键盘按键数据,当发送完一个按键后后面就给跟着发送一包 "00"(仅测试了单个按键发送的情况),所以在按键发送程序中增加标志位flag,当发送键值不为0时,flag置1,这样可以在发送完按键值后再发送一包数据"00".
按键发送处理如下:
-
if(key) -
{ -
Keyboard_Send(key); -
} -
else -
{ -
if(flag_sendok == 1) //判断是否发送完成 -
{ -
if(flag == 1) //发送了一包不为0x00的数据 -
{ -
Keyboard_Send(0); //不为0x00数据后发送0x00数据 -
flag = 0; //清除标志位 -
} -
flag_sendok = 0; -
} -
}
按键值首先会写入USB Endpoint1的发送缓冲区中等待发送,当发送完成后会产生相应中断.如果在发送前缓冲区中的数据改变,则数据会被覆盖,所以必须等待上次数据发送完成后再向缓冲区内写入数据,故程序中使用flag_sendok标志位来标志数据是否发送成功.当发送成功后会在中断函数中置1.
-
if ((wEPVal & EP_CTR_TX) != 0) -
{ -
/* clear int flag */ -
_ClearEP_CTR_TX(EPindex); -
-
/* call IN service function */ -
(*pEpInt_IN[EPindex-1])(); -
flag_sendok = 1; -
} /* if((wEPVal & EP_CTR_TX) != 0) */
以上,便可实现USB_Keyboard简单功能.仅为最近学习USB的一点心得,和大家分享一下.
第一次发博客,而且关于USB的知识水平有限,有不对的地方欢迎大家指正!谢谢!
本文是STM32完成USB_Keyboard的实验总结。实验使用正点原子战舰开发板与PC通讯,以触摸屏作键盘输入。实验中设备枚举易出问题,成功枚举后可修改程序发送键盘数据。还介绍了枚举过程分析及模拟键盘发送数据的具体实现方法。
430

被折叠的 条评论
为什么被折叠?



