前面说了USB鼠标,这次趁热打铁,说一下USB键盘。依然只说如何修改,不说背后的原理。原因你懂的,涉及的知识点太多了。 会不会写成USB三部曲? 不知道 猜猜我下一步再写个啥? 6 ~- K9 k. z( F9 T* z! y( ^( g
7 `3 B. \, J" L. ^, S% @, f 生成工程
* U( m& r r2 j首先,STM32CubeMX的配置部分不说了,和USB鼠标部分的一样。唯一需要注意的一点是,VID和PID这两个值要改一下,否则主机(也就是电脑)会以为你还是鼠标。
, `5 T- ^' Y/ `" n* p8 S9 a * I) I7 b1 I/ b% A) o
, g" e; V: C* g2 X1 H1 [& `3 s7 q
修改usbd_hid.c文件
+ u2 d) O& S0 P! v2 V- a其次,生成工程后打开,修改usbd_hid.c文件。配置集合(USBD_HID_CfgFSDesc)要做一些改动,首先是长度:
( X8 ?- [ \3 n* `7 q6 [8 ~, `. |5 u 这是个宏定义,之前是34,现在变成41. 然后是端点数,之前是1,现在改成2. 接着是接口协议,之前是2(鼠标),现在改成1(键盘)。 再接着是报告描述符长度: 之前是:HID_MOUSE_REPORT_DESC_SIZE,长度是74,现在改成: HID_KEYBOARD_REPORT_DESC_SIZE,长度63. 9 L% c2 u I' F% `0 S+ N
还有就是端点每次发送的数据包长度: 之前是4,不够用了,现在改成16.
I/ k+ M7 P) W# H% T
最后配置集合中增加一部分端点描述符,因为USB键盘对主机来说,不光有输入,还有输出。所以,增加的这部分端点描述符,用来描述输出。 - ' T( O$ t! @, m$ F( ~
- /******************** Descriptor of Mouse Output endpoint ********************/
! a T1 X7 }1 C- F% o+ z4 |6 v0 L* k - 0x07, /*bLength: Endpoint Descriptor size*/+ u6 Z" O9 v! {6 l0 Z
- USB_DESC_TYPE_ENDPOINT, /*bDescriptorType:*/
& M# R- l$ i8 S6 h; R$ g6 U3 j - 0x01,
. d3 k* B" A- p" V5 |5 _ - 0x03, /*bmAttributes: Interrupt endpoint*/
X1 y9 w( y1 n j* y0 s- B - 0x10,2 n. R4 g2 a" z Q2 n9 k8 u
- 0x00,
0 c/ F# i" y5 j! Z" e) v( L - HID_FS_BINTERVAL, /*bInterval: Polling Interval */
复制代码
5 o4 |4 `- ` B8 f+ e6 h8 n1 ~" I% h; R& o5 n
L* b# ?7 O9 ~! T" G7 X
修改HID描述符 : R. x( r, E% x9 g2 \
修改HID描述符中的报告描述符长度: 上面提到了,之前是鼠标描述符,长度74,现在改成键盘描述符,长度63.
; |( ]0 A6 t. x4 | Y: ?
8 E z+ C& f/ A! d
/ j- s1 e+ y1 N8 G g
生成键盘的报告描述符
1 A' B ~! F! E1 I+ H( V把USB鼠标的报告描述符删掉,换成USB键盘的报告描述符。
0 A# P, p& w8 J 不会写USB键盘的报告描述符怎么办? USB官方提供了一个USB报告描述符自动配置的工具,打开!里面有各种例程,我们直接复制一个USB键盘的报告描述符即可。 生成.h文件如下: - 6 Q% S' `9 e) u, e9 E' S+ N5 ~
- __ALIGN_BEGIN static uint8_t HID_KEYBOARD_ReportDesc[HID_KEYBOARD_REPORT_DESC_SIZE] __ALIGN_END =
9 p5 A- R: x7 ]# `3 C - {
0 u9 [! ]1 t) S! }9 I - 0x05, 0x01, // USAGE_PAGE (Generic Desktop)* L2 Y2 Y" f2 a6 V7 `
- 0x09, 0x06, // USAGE (Keyboard)/ v8 l" j, r6 s9 V5 U% r
- 0xa1, 0x01, // COLLECTION (Application)/ ], j3 I) D; d' }' H& \3 T% F! x
- 0x05, 0x07, // USAGE_PAGE (Keyboard)6 l; p7 ` t: l: y. ~) L; X( P9 o% ?
- 0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl)
0 G7 Y3 b' V, S6 y# k8 T; I - 0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI)
8 {6 Q5 L3 `5 J1 r/ Q) L7 _ - 0x15, 0x00, // LOGICAL_MINIMUM (0)
2 C' R# d$ k+ W! h; z1 j) k# \; z - 0x25, 0x01, // LOGICAL_MAXIMUM (1) C3 l$ l2 u/ C! D- K; Y3 a
- 0x75, 0x01, // REPORT_SIZE (1)
- v: S9 L' K! B5 Z* L1 K - 0x95, 0x08, // REPORT_COUNT (8)
8 N. V. B2 }- n4 `4 I - 0x81, 0x02, // INPUT (Data,Var,Abs)
$ R% \8 Z# v! N, _" q8 R& A - 0x95, 0x01, // REPORT_COUNT (1)
, ?. z+ R7 f; B9 U5 f - 0x75, 0x08, // REPORT_SIZE (8); D9 ?9 r1 ^3 n- J$ Z& K
- 0x81, 0x03, // INPUT (Cnst,Var,Abs)
3 B: Z( U+ R2 G: i I8 v - 0x95, 0x05, // REPORT_COUNT (5)
) }% H/ y( _& e& O$ f) k# y - 0x75, 0x01, // REPORT_SIZE (1)+ t7 _; o; p3 z& j6 ]7 K
- 0x05, 0x08, // USAGE_PAGE (LEDs)& Y/ k- z* i% b3 |: Q. f
- 0x19, 0x01, // USAGE_MINIMUM (Num Lock); N% J: R7 J5 G- F
- 0x29, 0x05, // USAGE_MAXIMUM (Kana)
) F. `" K2 K, x" r4 P - 0x91, 0x02, // OUTPUT (Data,Var,Abs)$ D o4 r7 I/ `7 u
- 0x95, 0x01, // REPORT_COUNT (1)
6 a7 W; h8 O/ N - 0x75, 0x03, // REPORT_SIZE (3)
5 Z: H: w0 S6 T7 n# G - 0x91, 0x03, // OUTPUT (Cnst,Var,Abs)
p2 T+ b3 @7 j7 z( P9 b0 W+ c - 0x95, 0x06, // REPORT_COUNT (6)
4 O D( r+ R8 Q3 _8 O# { - 0x75, 0x08, // REPORT_SIZE (8)
: H2 {3 f9 N! d/ W4 J! M - 0x15, 0x00, // LOGICAL_MINIMUM (0)5 M8 m0 q, y9 {; x7 C
- 0x25, 0x65, // LOGICAL_MAXIMUM (101)
8 x( @0 _$ ]2 P2 V: K - 0x05, 0x07, // USAGE_PAGE (Keyboard)$ {# {1 n6 ^/ g1 ~
- 0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated)): }6 A/ z5 x1 P
- 0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application)
2 [. I4 O8 B { [; S: P - 0x81, 0x00, // INPUT (Data,Ary,Abs)& r& H% y+ V$ ]/ @; v
- 0xc0 // END_COLLECTION: I- ~: H1 f( y W
- };
复制代码 / ~. M% Q; D3 k8 T$ h1 e- h
9 P) ]$ x# e1 Q2 ?% d 修改函数USBD_HID_Setup ' I/ u6 S) Z* n0 \8 z6 I9 W
! m7 A0 T8 Y6 @3 c
第五,函数USBD_HID_Setup中,需要修改一部分代码: 获取报告描述符的部分,之前这里是鼠标的报告描述符信息,现在换成了键盘的。
- v) o/ e7 u2 J7 ?5 w B+ [- O
# v" S' t9 W8 [+ D
修改main.c文件 ) u$ p( r7 y/ V4 @* G
3 N3 K0 M S2 d; ^7 ~
main.c文件中,添加头文件,并定义相关的数组: - <font color="#001000"><font style="background-color:rgb(255, 255, 255)"><font face="Tahoma">3 A: M! a' w1 z" O& | ~
- /* Private includes ----------------------------------------------------------*/
5 t% a, q8 U4 l. G - /* USER CODE BEGIN Includes */+ `( R0 I& B/ T. {$ v" N2 q" `, I
- #include "usbd_hid.h"# D' v% T _9 v: b! `
- /* USER CODE END Includes */! {5 H' }' Y5 Z5 m
3 i5 G% h: b5 E
/ j4 i1 i* x/ p$ j8 G- /* Private typedef -----------------------------------------------------------*/+ w( s1 z. o5 Z8 z
- /* USER CODE BEGIN PTD */
+ X2 }$ a! l$ }* S) G - uint8_t KeyBoard[8] = {0,0,4,0,0,0,0,0};5 ~ U( f. a9 [: N: r+ E8 p# h8 i
- uint8_t KeyBoard01[8] = {0,0,0,0,0,0,0,0};
# J7 |7 O* Y$ T A4 P! E* } - extern USBD_HandleTypeDef hUsbDeviceFS;</font></font></font><font color="#001000"><font style="background-color:rgb(255, 255, 255)">
& Q- v' a% _% Y0 [, O p - </font></font>
复制代码$ B$ f+ p8 _/ o& ^: N6 F
修改主函数 ( S; U2 C9 X4 t
Q- O f& R+ A- e
第七,主函数中循环发送英文字母A~Z。 - <font color="#001000"><font style="background-color:rgb(255, 255, 255)"><font face="Tahoma">
& d0 u o# t5 x* k/ n9 X6 T2 h6 F6 H - while (1)
" v4 G: x5 b& ^7 F4 R* K% \+ C9 J - {
, }+ h( z" t3 \- \: _# K: J - /* USER CODE END WHILE */7 e( L8 S5 G ~) X ^! x% R
- 8 i* P1 a: s7 \, k7 A/ y8 j4 e/ n7 D
- . p) Y* F9 p) |
- /* USER CODE BEGIN 3 */5 A1 r' x7 ]7 Q9 [
- if(KeyBoard[2] >= 29)6 h2 |& c* r0 c8 K. j! N
- {
W& {7 w+ v8 A8 ]0 P - KeyBoard[2] = 4;
7 y& U5 ]* h$ Q! l$ x4 \ - }* n+ M( E9 p" D1 b
- else
6 r3 e1 Z0 C( B9 X, p3 N% V- {2 y - {/ [3 Q6 w2 a, }2 Y o
- KeyBoard[2]++;
" {0 C7 J! \4 g O8 @9 U - }4 M! V2 z0 H$ ^
- USBD_HID_SendReport(&hUsbDeviceFS,(uint8_t*)&KeyBoard01,sizeof(KeyBoard));: n, [! [% Z( Z4 R9 S: N4 f6 W$ V
- HAL_Delay(15);) J9 R4 N \1 y; A: F3 m
- USBD_HID_SendReport(&hUsbDeviceFS,(uint8_t*)&KeyBoard,sizeof(KeyBoard01));) G" Y& Q: ]' y. \5 p- y I: @
- HAL_Delay(15);
Y. \- T8 e+ ~" \ - USBD_HID_SendReport(&hUsbDeviceFS,(uint8_t*)&KeyBoard01,sizeof(KeyBoard));
6 `0 X/ _" N' y2 s; ], J6 a1 v - HAL_Delay(1000);
( b/ N* F) [2 z. m7 D e: r - }</font></font></font>
9 V! r/ \/ G: u* s
复制代码
, A2 c( M; M) w, U) p$ Q% l0 C
9 e8 ]% f( v9 B5 Y& L6 {
为什么4~29对应英文字母A~Z? USB官网的文件hut1_12v2中,对键盘的每个按键对应的值,都有一个详细的定义,看第53页,我这里截一部分图:
0 ~$ p( P# W$ Y) |7 D# f6 H* `
最后,保存、编译、下载、上电!新建一个TXT文档,看键盘自动输出字母,爽不爽? ( V- t" V' ~+ x2 ?3 _/ a
: y& y( |5 e% U- A/ b2 {
$ m! ]( h" h) X6 L' P5 J基于STM32CUBE的USB鼠标键盘二合一
3 W8 w$ K( k- V/ ]; a' ?! P4 `
- {0 z h5 D& v# g1 r m/ \- ]
& f% J& A! c$ {9 ^首先,在5.3.0版本的STM32CubeMX上选择STM32F103C8T6芯片。具体操作和USB鼠标的操作一样,这里就不重复了。
1 j6 b. B1 ]' c% d) Y8 `6 m3 X, {, x 同理,VID和PID要和之前的设备不一样。设置完成以后,直接生成工程。. a& }" W6 E2 Q
第二,修改usbd_hid.c中的配置集合(USBD_HID_CfgFSDesc)。如下图所示,框住的地方是个宏定义。配置集合的长度,由之前的34,变为41. 端点个数,由1变成2. 接口协议,由2(鼠标)变成1(键盘)。 有的小伙伴会奇怪,我们不是鼠标键盘二合一吗?怎么还是键盘? 作为一个技术人员,我们要学会透过现象看本质。虽然表明上是鼠标与键盘二合一,但实际上是以键盘功能为主,而鼠标以一个附属功能加入到了键盘里。所以,这里虽然选的是键盘,但最终的效果是键盘鼠标功能都有。 好了,继续! 跟USB键盘的部分一样,配置集合最下面,增加一个输出端点的描述符: - <font color="#001000"><font style="background-color:rgb(255, 255, 255)"><font face="Tahoma"> |# ^2 C7 J5 |+ X! t
- /******************** Descriptor of Mouse Output endpoint ********************/
6 i* N4 I8 v g9 I \6 E - 0x07, /*bLength: Endpoint Descriptor size*/. o2 M! W6 o! P( D
- USB_DESC_TYPE_ENDPOINT, /*bDescriptorType:*/
: |* O7 f4 Y! _7 {% K; `2 e - 0x01,4 t- e- p/ x9 c6 U
- 0x03, /*bmAttributes: Interrupt endpoint*/- u1 n$ R* F" L2 ~6 Q; b
- 0x10,, D+ P1 X8 Z+ O1 W/ t) g
- 0x00,
7 `6 b0 z6 g) A f - HID_FS_BINTERVAL, /*bInterval: Polling Interval */</font></font></font>" X- D! d; X% Q: A1 C5 F
复制代码
: M% ]2 I' v1 S/ d( c
( g) c$ h: T/ `* Y8 a
第三,修改HID描述符中,报告描述符的长度: 之前这里是鼠标的报告描述符,长度有74.现在变成了117. 为什么是117? 看下文!
/ ?% b7 ^5 Z. I$ s7 n) p第四,修改报告描述符。STM32CubeMX工具自动生成的工程里,报告描述符是鼠标的。现在我们要实现的是键盘与鼠标二合一,要修改的核心位置就是报告描述符这里。
* k. P: u7 d, u
简单来说,就是把前面两个例程中的报告描述符合二为一。一个数组里面,上面放键盘的报告描述符,下面放鼠标的报告描述符。
, ?; E/ i- v5 {
这样的话,对USB主机(也就是电脑)来说,它收到的数据,有可能是鼠标的数据,也有可能是键盘的数据。那,怎么区分? 5 g+ o( P8 y L6 b
方法就是分别在键盘与鼠标的报告描述符中放一个报告ID,键盘的报告ID是1,鼠标的报告ID是2. 两个报告描述符,一个长65,一个长62,加起来117.
* K2 `" H e8 [5 S$ r$ m5 V7 {; x
向USB主机发送数据的时候,数组的第一个元素是报告ID,后面才是键盘数据或鼠标数据。实现前面两节的例程的时候,USB键盘我们定义了一个8元素的数组,USB鼠标我们定义了一个4元素的数组。现在我们只需要一个数组,它同一时间,只发送一种数据,所以大小为8,然后,还要包含报告ID,所以变成9. 在USB协议中,报告ID默认是数组的第一个元素。明白了这一点,我们可以去修改main.c文件了。 , b. Y+ u- L) L5 i! b
第五,添加头文件,并定义相关的数组。 - <font color="#001000"><font style="background-color:rgb(255, 255, 255)"><font face="Tahoma">
, i& s" y7 P6 E# P; C - /* Private includes ----------------------------------------------------------*/
$ a9 J9 ^+ u$ @/ l, Y - /* USER CODE BEGIN Includes */
: m- d' L3 k* \8 H$ @ - #include "usbd_hid.h"- k* M2 C- c* p; _6 {) y
- /* USER CODE END Includes */) }9 g; u& W) c+ h3 E1 _
% ~( y, p" D" e4 A6 H$ e& Q- ~% c7 x8 t3 B; n8 o& A
- /* Private typedef -----------------------------------------------------------*// I" b; A: Y7 p7 \4 K1 p+ v
- /* USER CODE BEGIN PTD */! E) p0 ]) X* z/ u6 H
- uint8_t KeyBoard[9] = {1,0,0,4,0,0,0,0,0};
" O- {- T* x! Y" n8 z - uint8_t KeyBoard01[9] = {1,0,0,0,0,0,0,0,0}; ]5 Y, F8 w9 X: q4 x
- uint8_t Mouse[9] = {2,0,0,0,0,0,0,0,0};
# S& O0 G$ r! X0 E6 ? \8 S1 R& F - extern USBD_HandleTypeDef hUsbDeviceFS;</font></font></font><font color="#001000"><font style="background-color:rgb(255, 255, 255)">
, O9 @1 @4 H) D0 z - </font></font>
复制代码6 `& J% q; q" m. r" s1 e/ G
; d4 I) G5 z" a$ B
KeyBoard 数组第一个元素是1,Mouse 数组第一个元素是2,这两个值分别对应键盘和鼠标的报告ID。KeyBoard01这个数组是为了表示键盘没有被按下的状态。 . N9 {9 @" e7 ]5 ?5 g: K$ s
第六,修改主函数。循环输出a到z字母,同时,鼠标左键每隔1秒触发一下。
2 ~9 r/ p3 l: s$ T- /* USER CODE BEGIN 3 */
+ D/ y1 }6 ?7 B9 l - if(KeyBoard[3] >= 29)
' ^ o3 r) x3 N2 F - {( z1 L5 ^; B' ]+ ?# w7 J
- KeyBoard[3] = 4;
( t* w" K/ H& Q$ w4 { - }& \# e4 p" ?/ Z& U( {* j
- else
, V7 A4 v, _3 a/ N2 V' z9 W' S - {
8 F. t( b% h2 s* M7 Q - KeyBoard[3]++;; y! T3 Z9 O! x
- }
$ Y# a# `" ?4 K" H - USBD_HID_SendReport(&hUsbDeviceFS,(uint8_t*)&KeyBoard01,sizeof(KeyBoard));
' A3 H( h$ C# }2 J1 E - HAL_Delay(15);3 C& m4 [5 l: Z
- USBD_HID_SendReport(&hUsbDeviceFS,(uint8_t*)&KeyBoard,sizeof(KeyBoard));/ l' Z9 S/ }, }6 N. @2 | k
- HAL_Delay(15);- e0 N- P6 ~3 U9 g* e {
- USBD_HID_SendReport(&hUsbDeviceFS,(uint8_t*)&KeyBoard01,sizeof(KeyBoard));3 `; F! e5 g f2 g. u, t7 w f
- HAL_Delay(1000);" h$ v- N4 j& h' S- D8 o7 s2 S
- 2 Y7 L; T- P7 E; t/ W; S. m- W7 E
- Mouse[1] = 0x01;
1 z4 S- j: t1 I) g, S - USBD_HID_SendReport(&hUsbDeviceFS,(uint8_t*)&Mouse,sizeof(Mouse));0 Q2 M8 R: T5 b2 p# k3 z+ k
- HAL_Delay(1000);% {) [. W8 | p) |% O6 r
- Mouse[1] = 0x00;
F: v6 s2 L" |5 ?/ I) u' k& x/ ? - USBD_HID_SendReport(&hUsbDeviceFS,(uint8_t*)&Mouse,sizeof(Mouse));
7 a2 ?# g. X4 F. ^# s/ J - HAL_Delay(1000);
复制代码
% e* X4 k& ^& p. o. X
7 d- \- F, Q* Y$ I; G
最后,保存、编译、下载、上电!新建一个TXT文档,可以看到字母自动输出,同时鼠标左键每隔1秒被触发一下。 |