你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

依据HID例程来分析STM32F107的USB的实现过程

[复制链接]
Apple789 发布时间:2017-1-10 13:49
看到很多坛友发布一些关于USB的资料,但是很多都不是基于例程来分析的,最近在搞USB这块的程序,在这里记录一下,自己学习USB的过程,同时也写出来分享给大家,希望能给大家在USB学习方面提供一些帮助。0 P2 `, w1 ]( G1 n
我使用的硬件是金龙107开发板,主芯片STM32F107VCT6,该芯片支持USB从机以及主机,支持OTG功能,例程是基于ST官网的例程,做了一些简单的修改,后面我会把完整的工程上传上来。! T# Z  k: |0 K7 S
基于金龙107开发板的所有例程都在另外一个帖子里面[url= http://www.amobbs.com/thread-5572266-1-1.html]金龙107开发板全部例程[/url] ,每个例程都有说明文档,有兴趣的大家可以查看一下。9 r" X: J9 u- ?  J

. K/ T# n7 z. q' u, S6 ^+ ?# O首先发一些关于USB的基础知识,是本人转来的。
( k3 Z1 {( M& ~3 ^USB的重要关键字:
/ x/ k1 g& S( c1 h! M
! x: A0 ~1 c/ d5 k+ Z: R1、端点:位于USB设备或主机上的一个数据缓冲区,用来存放和发送USB的各种数据,每一个端点都有惟一的确定地址,有不同的传输特性(如输入端点、输出端点、配置端点、批量传输端点)
# ?, Z3 M( K  O2 n; ]( a7 [. X+ Y# k$ l3 `  W" Z& ?- Y3 v7 d9 q
2、帧:时间概念,在USB中,一帧就是1MS,它是一个独立的单元,包含了一系列总线动作,USB将1帧分为好几份,每一份中是一个USB的传输动作。
+ C6 ?. A. I$ |2 K6 O' i
% k4 f7 e! z6 M- ^. a" R% g3、上行、下行:设备到主机为上行,主机到设备为下行
8 m: x/ N9 W& f. ~# Q
1 D- h% w1 w) ]! Q! w" P. C下面以一问一答的形式开始学习吧。
0 D  q, o8 o  C* Q5 S: {6 r- U: }; V  m! f( S. ^
问题一:USB的传输线结构是如何的呢?$ i/ T1 d2 Z8 M- I# B1 D
# q! N- @' |9 u' K# h8 h  ]6 N
答案一:一条USB的传输线分别由地线、电源线、D+、D-四条线构成,D+和D-是差分输入线,它使用的是3.3V的电压(注意哦,与CMOS的5V电平不同),而电源线和地线可向设备提供5V电压,最大电流为500MA(可以在编程中设置的,至于硬件的实现机制,就不要管它了)。% J3 w! j3 _! Y& Y
. g! w  _5 R/ C
问题二:数据是如何在USB传输线里面传送的$ A( w' a0 U2 G# |' s* i2 x% s

7 C& [% B' I! ^& i答案二:数据在USB线里传送是由低位到高位发送的。
0 r2 C& Z% Q. f% S+ e. ?+ f/ v7 H# u6 A  g. d6 K
问题三:USB的编码方案?
) z+ R( x! n+ H$ K: C" I8 T
- v- w0 Y* M+ S+ C  W& U答案三:USB采用不归零取反来传输数据,当传输线上的差分数据输入0时就取反,输入1时就保持原值,为了确保信号发送的准确性,当在USB总线上发送一个包时,传输设备就要进行位插入***作(即在数据流中每连续6个1后就插入一个0),从而强迫NRZI码发生变化。这个了解就行了,这些是由专门硬件处理的。
  k% Q  X  J3 w0 O0 a4 e' _- G% m8 ~% M: u5 w
问题四:USB的数据格式是怎么样的呢?
" j6 r4 r& J$ m5 V3 J- x- w# d" P; V: H) t; }  l. N8 C( y
答案四:和其他的一样,USB数据是由二进制数字串构成的,首先数字串构成域(有七种),域再构成包,包再构成事务(IN、OUT、SETUP),事务最后构成传输(中断传输、并行传输、批量传输和控制传输)。下面简单介绍一下域、包、事务、传输,请注意他们之间的关系。
+ w: @# `, p" |" j
3 i( C2 E* {$ K' M(一)域:是USB数据最小的单位,由若干位组成(至于是多少位由具体的域决定),域可分为七个类型:
6 R' B, J4 N$ d0 w+ v' x# @, y2 {$ l5 K/ |
1、同步域(SYNC),八位,值固定为0000 0001,用于本地时钟与输入同步
7 S! j9 p# v4 e1 H5 H7 \" X0 Y1 V+ ]. q( \+ v) ?
2、标识域(PID),由四位标识符+四位标识符反码构成,表明包的类型和格式,这是一个很重要的部分,这里可以计算出,USB的标识码有16种,具体分类请看问题五。3 `+ m8 {1 a3 \

) u" e% ?3 k' x) C- e" a' `! }. q+ B3、地址域(ADDR):七位地址,代表了设备在主机上的地址,地址000 0000被命名为零地址,是任何一个设备第一次连接到主机时,在被主机配置、枚举前的默认地址,由此可以知道为什么一个USB主机只能接127个设备的原因。
# W  m. \) Y3 b6 c6 C- E4 h; X
; y. f: t: T+ T" i$ j4、端点域(ENDP),四位,由此可知一个USB设备有的端点数量最大为16个。
' O$ G( F$ U' F& H, X9 v0 U6 h; x/ n& Q& d( N% q8 E9 v
5、帧号域(FRAM),11位,每一个帧都有一个特定的帧号,帧号域最大容量0x800,对于同步传输有重要意义(同步传输为四种传输类型之一,请看下面)。  G' d$ J4 l' y/ t# u; ]/ m4 `
- o$ Y0 U" F6 e3 e! C2 d2 G
6、数据域(DATA):长度为0~1023字节,在不同的传输类型中,数据域的长度各不相同,但必须为整数个字节的长度" s1 ?8 E- x9 C8 G
2 s* |  P" i! B7 q+ @
7、校验域(CRC):对令牌包和数据包(对于包的分类请看下面)中非PID域进行校验的一种方法,CRC校验在通讯中应用很泛,是一种很好的校验方法,至于具体的校验方法这里就不多说,请查阅相关资料,只须注意CRC码的除法是模2运算,不同于10进制中的除法。
" E. T- G( F2 N, V* w  Y7 S' v, Q! Y' z# e; H+ L  w0 u# ^+ F
(二)包:由域构成的包有四种类型,分别是令牌包、数据包、握手包和特殊包,前面三种是重要的包,不同的包的域结构不同,介绍如下3 S9 o/ x! U' C" |1 i* v
% e- y% Z) B. i& e3 Q+ n
1、令牌包:可分为输入包、输出包、设置包和帧起始包(注意这里的输入包是用于设置输入命令的,输出包是用来设置输出命令的,而不是放据数的)
8 j* V% O. J5 A
6 c5 K- q$ \1 }. t3 @0 Q- D3 r! H其中输入包、输出包和设置包的格式都是一样的:
/ I7 z, h# S, q% d9 {
3 [' w4 X+ P2 o: dSYNC+PID+ADDR+ENDP+CRC5(五位的校验码)5 y8 l+ U6 A) W7 e# c6 c" A

+ i! W. o  u* Q" \(上面的缩写解释请看上面域的介绍,PID码的具体定义请看问题五)
' A2 H0 q8 Y. O' U
$ }( ^! J  c$ _帧起始包的格式:
, \3 `8 F* P# B( m  y
; B& {8 s0 }4 K5 Y, HSYNC+PID+11位FRAM+CRC5(五位的校验码)/ e6 `8 \! A3 {8 [- t1 K

! e! l8 h: z9 d' ~7 J, s2、数据包:分为DATA0包和DATA1包,当USB发送数据的时候,当一次发送的数据长度大于相应端点的容量时,就需要把数据包分为好几个包,分批发送,DATA0包和DATA1包交替发送,即如果第一个数据包是 DATA0,那第二个数据包就是DATA1。但也有例外情况,在同步传输中(四类传输类型中之一),所有的数据包都是为DATA0,格式如下:$ N0 g7 ~5 M3 R  g$ R

) p( T7 s" [* g' XSYNC+PID+0~1023字节+CRC16
8 e& Y3 L3 y) E) J3 Z  Y! d" n
) f# ?# c0 |) ?4 F6 q2 S3、握手包:结构最为简单的包,格式如下
+ I/ Y- i! H( i) z+ ~7 ^( ?( g
% J' f; V6 [* w  J7 Q# gSYNC+PID8 \) c* o% q3 E% N4 f. n

2 G9 w  d  ?7 K3 |' w' d* |(注上面每种包都有不同类型的,USB1.1共定义了十种包,具体请见问题五), K6 S: t( N# C
  f4 k; \! I+ U; S9 m; O
(三)事务:分别有IN事务、OUT事务和SETUP事务三大事务,每一种事务都由令牌包、数据包、握手包三个阶段构成,这里用阶段的意思是因为这些包的发送是有一定的时间先后顺序的,事务的三个阶段如下:3 ]9 E  v8 q0 q! F) D
) }. p1 n' c6 s
1、令牌包阶段:启动一个输入、输出或设置的事务. Y9 d* ~5 b6 y: N
6 v$ @6 o; t  b0 t0 `0 S9 J
2、数据包阶段:按输入、输出发送相应的数据
7 O' y  F7 A9 _% v
* I0 y, c3 u0 C2 k( @8 k* H! K3、握手包阶段:返回数据接收情况,在同步传输的IN和OUT事务中没有这个阶段,这是比较特殊的。+ w3 G3 T6 n- ^
! d% V8 p2 j+ @8 B8 f
事务的三种类型如下(以下按三个阶段来说明一个事务):/ S' X! e3 ~: _9 V( x3 ^( t3 Q
6 a" |7 z4 |3 B; d' E5 S: }& {3 r
1、 IN事务:
! V" d' A; h% K2 ^8 o+ j' X, s) v0 K1 c9 _1 E! [! R7 J
令牌包阶段——主机发送一个PID为IN的输入包给设备,通知设备要往主机发送数据;- K% z; w0 _; P, p5 y
* [% t' `+ ?/ H3 y, C9 f
数据包阶段——设备根据情况会作出三种反应(要注意:数据包阶段也不总是传送数据的,根据传输情况还会提前进入握手包阶段)& K  V7 X, d. O4 ^# q% ^

! Y/ j- a( e7 H$ t$ T, d# B1) 设备端点正常,设备往入主机里面发出数据包(DATA0与DATA1交替);3 [) D2 ?# J4 Z7 ]1 f

+ v% S3 j1 I3 y  Y) C0 f# P2) 设备正在忙,无法往主机发出数据包就发送NAK无效包,IN事务提前结束,到了下一个IN事务才继续;( f* [6 g! v# z( Y" G- r
# d' i7 U( A1 M7 j
3) 相应设备端点被禁止,发送错误包STALL包,事务也就提前结束了,总线进入空闲状态。
6 ]: z& b" r0 Y6 n* G4 d) A: Y: T  P# G. @3 X( [9 }6 l$ B2 D% _# H6 v
握手包阶段——主机正确接收到数据之后就会向设备发送ACK包。
5 `" `% C: g$ U
3 _3 `  V) M+ q# X; M2、 OUT事务:9 G1 w+ I- W6 }
( {; y. F9 W1 w- A
令牌包阶段——主机发送一个PID为OUT的输出包给设备,通知设备要接收数据;
1 I6 _6 ~+ K6 z7 h( p8 D" }6 B' G) r- w, h7 {  W
数据包阶段——比较简单,就是主机会设备送数据,DATA0与DATA1交替
$ f$ N( o7 v1 U; r0 F. F* G2 M, G- D% X
% u3 X8 p. Z, X9 X握手包阶段——设备根据情况会作出三种反应$ r8 p' D1 H6 q+ A6 @1 G5 w
6 l* L, J# D* T
1)设备端点接收正确,设备往入主机返回ACK,通知主机可以发送新的数据,如果数据包发生了CRC校验错误,将不返回任何握手信息;6 l+ d- L' c+ ~3 H
4 j' j* C3 z' k
2) 设备正在忙,无法往主机发出数据包就发送NAK无效包,通知主机再次发送数据;) i" S% d  Z4 p; V' ]
% N5 E2 D. [9 V0 b
3) 相应设备端点被禁止,发送错误包STALL包,事务提前结束,总线直接进入空闲状态。
& t. z1 @1 ?2 v) K8 O' O! F" X9 W9 U7 H% }% C
3、SETUT事务:
' t' y; J9 i- P/ z- z
' k5 C' k* ~$ W; y% J令牌包阶段——主机发送一个PID为SETUP的输出包给设备,通知设备要接收数据;0 F6 Y& k6 T) z3 d# g$ W
0 X$ l" s0 C  e! Z& k# w' L+ F$ p
数据包阶段——比较简单,就是主机会设备送数据,注意,这里只有一个固定为8个字节的DATA0包,这8个字节的内容就是标准的USB设备请求命令(共有11条,具体请看问题七)
5 w# }! J$ G  ]% K5 b5 o, J- c9 {2 S% W/ Y9 f5 a& l( F' o- h( ]) u
握手包阶段——设备接收到主机的命令信息后,返回ACK,此后总线进入空闲状态,并准备下一个传输(在SETUP事务后通常是一个IN或OUT事务构成的传输)' _" j1 v3 l( D' q6 U
0 N& A, e' D1 I2 S% ~1 o9 S
(四)传输:传输由OUT、IN、SETUP事务其中的事务构成,传输有四种类型,中断传输、批量传输、同步传输、控制传输,其中中断传输和批量转输的结构一样,同步传输有最简单的结构,而控制传输是最重要的也是最复杂的传输。
1 R3 I2 G- ^/ P0 ~) W6 o7 X7 F3 g) O; |
1、中断传输:由OUT事务和IN事务构成,用于键盘、鼠标等HID设备的数据传输中
" e* q( a/ n& T/ M4 Z& P! K  q" j* Z# y
2、批量传输:由OUT事务和IN事务构成,用于大容量数据传输,没有固定的传输速率,也不占用带宽,当总线忙时,USB会优先进行其他类型的数据传输,而暂时停止批量转输。
! P$ n. c+ Q, G# e" A0 D/ L" R& A8 {+ ?( S) B( s3 X
3、同步传输:由OUT事务和IN事务构成,有两个特殊地方,第一,在同步传输的IN和OUT事务中是没有返回包阶段的;第二,在数据包阶段所有的数据包都为DATA0
6 Q" ~; p3 w+ b% a4 f5 T4 W, a  w' Y% W# w3 L
4、控制传输:最重要的也是最复杂的传输,控制传输由三个阶段构成(初始设置阶段、可选数据阶段、状态信息步骤),每一个阶段可以看成一个的传输,也就是说控制传输其实是由三个传输构成的,用来于USB设备初次加接到主机之后,主机通过控制传输来交换信息,设备地址和读取设备的描述符,使得主机识别设备,并安装相应的驱动程序,这是每一个USB开发者都要关心的问题。
; u6 ?( Y' K$ E
& ]6 [% q; H" P* c# g6 @1、初始设置步骤:就是一个由SET事务构成的传输
  i3 {" z; b4 W! i) |# P1 E2 `3 M6 ]8 w$ g+ I1 X% v0 `  N2 G
2、可选数据步骤:就是一个由IN或OUT事务构成的传输,这个步骤是可选的,要看初始设置步骤有没有要求读/写数据(由SET事务的数据包阶段发送的标准请求命令决定)+ y3 `6 W5 c. h7 W/ T! U$ e

1 P, h, ~4 x; T3 x3、 状态信息步骤:顾名思义,这个步骤就是要获取状态信息,由IN或OUT事务构成构成的传输,但是要注意这里的IN和OUT事务和之前的INT和OUT事务有两点不同:  a. r8 ^' H" z- F6 @/ e% A7 L
! q  f  R0 u9 M  k9 n
1) 传输方向相反,通常IN表示设备往主机送数据,OUT表示主机往设备送数据;在这里,IN表示主机往设备送数据,而OUT表示设备往主机送数据,这是为了和可选数据步骤相结合;: h3 l, v( x! d' |

2 L- Z2 S( `* S' h. J* j2 S2) 在这个步骤里,数据包阶段的数据包都是0长度的,即SYNC+PID+CRC16' g' }# r" m- B8 {, e
  s6 P: B/ l! }
除了以上两点有区别外,其他的一样,这里就不多说
8 P" L0 k5 H5 J8 z: g
4 `7 _. a6 E3 R(思考:这些传输模式在实际***作中应如何通过什么方式去设置?)9 b8 `- l4 _, _

: Y5 t; y* n9 M5 P3 a9 R$ f问题五:标识码有哪些?/ f6 ]6 h/ ?6 M; P, j- t+ r
/ B/ x) B$ d2 l* b
答案五:如同前面所说的标识码由四位数据组成,因此可以表示十六种标识码,在USB1.1规范里面,只用了十种标识码,USB2.0使用了十六种标识码,标识码的作用是用来说明包的属性的,标识码是和包联系在一起的,首先简单介绍一下数据包的类型,数据包分为令牌包、数据、握手包和特殊包四种(具体分类请看问题七),标识码分别有以下十六种:4 J+ O' K9 s' d, O5 A; I2 S

7 z  A" ^7 z8 ~5 e& p令牌包 :
2 a, t( G5 r2 u6 G7 X
! h4 r! @4 E$ ]' q5 @# m0x01 输出(OUT)启动一个方向为主机到设备的传输,并包含了设备地址和标号( k- Q5 c/ F9 X% n/ s% A2 u$ `& H
9 C7 Z+ S) J% Z) M% L4 l% z: t
0x09 输入 (IN) 启动一个方向为设备到主机的传输,并包含了设备地址和标号1 K: N) X( ]* O9 u- k3 ]6 \( ?1 j

" T. Y! i( y& D6 Z0x05 帧起始(SOF)表示一个帧的开始,并且包含了相应的帧号
- I) Y9 P% ]( w; s9 i6 \' X+ t/ x5 f/ m
0x0d 设置(SETUP)启动一个控制传输,用于主机对设备的初始化; u, j8 D) f" ~9 T& l
4 f$ w" C9 B2 X2 e6 I/ B: n
数据包 :2 ~" z  H( t0 p; o$ j8 x9 D, j

7 B2 U  i1 A& |' _0x03 偶数据包(DATA0),+ G1 B- `- |% [7 L3 J$ L) t' R

0 i4 N- b' y3 g; `7 q, G! |0x0b 奇数据包(DATA1)5 k9 @( z0 p$ i% M8 n6 L/ c$ d  {# X

; @! ~& V' X& b7 ~5 J% K- Z) b握手包:( x4 h0 M) }3 X+ V+ k, H) S5 d0 k
: e- M. |: e  C5 R% j& y2 L
0x02 确认接收到无误的数据包(ACK)* f# ~. ]5 n2 T" G8 ?$ W" w$ D

% t, _) ~. D( w. P5 I$ R0x0a 无效,接收(发送)端正在忙而无法接收(发送)信息
" A2 o7 U+ {9 q  ~; ]' |* h6 h
6 b7 B$ V6 K3 O5 }  z/ e, e0x0e 错误,端点被禁止或不支持控制管道请求8 h7 G1 E6 q, k- T4 Y
. u. ^# N" w- c) ~7 [
特殊包 0x0C 前导,用于启动下行端口的低速设备的数据传输4 [1 E' @  t: K6 L) L
( O9 B+ @: G) W% k& Q* A
问题六:USB主机是如何识别USB设备的?
* m5 B" S9 q+ Z% j8 n3 \9 O" n1 j! {
答案六:当USB设备插上主机时,主机就通过一系列的动作来对设备进行枚举配置(配置是属于枚举的一个态,态表示暂时的状态),这这些态如下:+ M' b6 H* D& P

1 I: S+ F& b3 Z' E1 U6 ?1 ]5 x1、接入态(Attached):设备接入主机后,主机通过检测信号线上的电平变化来发现设备的接入;9 h9 S5 h/ q' B; h8 D& D
" b  {1 j3 I6 r6 b
2、供电态(Powered):就是给设备供电,分为设备接入时的默认供电值,配置阶段后的供电值(按数据中要求的最大值,可通过编程设置)
" a% h' n* s. N
) |4 H: `5 }7 L( {/ P3、缺省态(Default):USB在被配置之前,通过缺省地址0与主机进行通信;) ], ?2 Y7 w6 H$ N& Z$ F& R$ u" I

1 o8 P& ?' [  Z. V4、地址态(Address):经过了配置,USB设备被复位后,就可以按主机分配给它的唯一地址来与主机通信,这种状态就是地址态;
, ^1 O7 H% C. j& a3 J' f! h8 E1 `. {# g/ e+ p5 R5 s
5、配置态(Configured):通过各种标准的USB请求命令来获取设备的各种信息,并对设备的某此信息进行改变或设置。- B& J# f; @7 d2 z/ M

, i6 i+ x- ^% _1 {8 D6 t; [. Y; T6、挂起态(Suspended):总线供电设备在3ms内没有总线***作,即USB总线处于空闲状态的话,该设备就要自动进入挂起状态,在进入挂起状态后,总的电流功耗不超过280UA。
9 V" ]( y+ P+ b9 I
, L+ S9 Y. m0 t2 C2 M/ I4 i" C问题七:刚才在答案四提到的标准的USB设备请求命令究竟是什么?
0 }! u7 _) k3 W8 B
4 _9 W& i2 ~% B) N' P1 ~7 {, `答案七:标准的USB设备请求命令是用在控制传输中的“初始设置步骤”里的数据包阶段(即DATA0,由八个字节构成),请看回问答四的内容。标准USB设备请求命令共有11个,大小都是8个字节,具有相同的结构,由5 个字段构成(字段是标准请求命令的数据部分),结构如下(括号中的数字表示字节数,首字母bm,b,w分别表示位图、字节,双字节):
& t' K/ _! B" K# S3 d) s) b" Q: A/ B/ h8 V4 ~1 @
bmRequestType(1)+bRequest(1)+wvalue(2)+wIndex(2)+wLength(2)
, ^& }8 g4 g1 V" H% M7 V1 o  h& x8 L! c% L) s7 _% ?" k9 ?& D
各字段的意义如下:, X' ?: e. Y# l0 o5 Q- Y

( X; g1 @3 O2 o0 W6 t" b1、bmRequestType:D7D6D5D4D3D2D1D0; [1 b$ L9 Z- r- s- p8 G+ S
1 `# H' ^6 r+ _3 M" P
D7=0主机到设备$ z* T1 |9 S, X. H& a3 C' {6 ]

' v0 K* l+ ^+ I8 j7 }. e=1设备到主机;  ^  u/ R3 ~& \* Y

) i& X& x# w1 h0 s3 m+ \D6D5=00标准请求命令& e" h8 Y4 h0 u: M9 p+ T. H* u

9 Y' z) \: L% f9 M# x=01 类请求命令5 Y) t& X" b+ D6 _/ Y

0 P. K( u  ?# _=10用户定义的命令
8 N) l+ w& W; o0 N, b1 f  N  b
0 T& l0 P5 m+ i  C+ ?$ ~4 [=11保留值
. G6 s& z7 p2 M/ A8 h4 X# t+ V8 o" ^1 D$ b; Y( G
D4D3D2D1D0=00000 接收者为设备1 d: J! t( p; Z" D

9 f, n, a% G7 S6 w1 j2 b4 ~=00001 接收者为设备
) z, O+ C9 g  [) n: }2 J* ?2 b
8 R- |2 Z1 |( W6 l" U" b8 b; Z=00010 接收者为端点& ^' n+ ?  c% Z- x5 F

1 ~, x* x) s, _; P6 V( H. t- n7 }3 t=00011 接收者为其他接收者6 g$ l2 o6 t' s0 I! P! `

# A1 ?) U& c: K=其他 其他值保留2 W7 c0 x0 K- c& q7 L4 y; ?

% @' G1 }0 i6 r, O% e) ]/ B2、bRequest:请求命令代码,在标准的USB命令中,每一个命令都定义了编号,编号的值就为字段的值,编号与命令名称如下(要注意这里的命令代码要与其他字段结合使用,可以说命令代码是标准请求命令代码的核心,正是因为这些命令代码而决定了11个USB标准请求命令):
, h+ z8 x  \5 Z5 W+ r* q- E: C( a' ~/ l+ t% ]- c/ c$ N
0) 0 GET_STATUS:用来返回特定接收者的状态
. g! E) C0 R/ C% U$ R- [, @2 |# d' y3 a
1) 1 CLEAR_FEATURE:用来清除或禁止接收者的某些特性
2 q2 A: T) c2 v0 ?# v/ b) U
9 }) z5 i- l! d2) 3 SET_FEATURE:用来启用或激活命令接收者的某些特性
$ A$ A% c8 [2 c; C. A7 p& d* h
; C% t5 {) |2 i3) 5 SET_ADDRESS:用来给设备分配地址* ]: ]5 r5 @* u) Y
5 n% G& v- h& _# X% X- C) X
4) 6 GET_DEs criptOR:用于主机获取设备的特定描述符
/ v2 Y' w6 C4 g1 d0 j
& O& e5 s/ K4 L" f+ Z5) 7 SET_DEs criptOR:修改设备中有关的描述符,或者增加新的描述符0 P# q# i, W5 A5 _! l% R  ]
+ q- m; W5 c# V  V
6) 8 GET_CONFIGURATION:用于主机获取设备当前设备的配置值(注同上面的不同)5 r- A: u, V2 G
% K8 t" ~3 b0 B% U, j+ n2 c
7) 9 SET_CONFIGURATION:用于主机指示设备采用的要求的配置
! f: l2 W  g) k) H6 m# R; X: P2 r- \( V
8) 10 GET_INTERFACE:用于获取当前某个接口描述符编号& e$ w" R" t9 r* l

) N# v0 `2 s5 S- k- ]9) 11 SET_INTERFACE:用于主机要求设备用某个描述符来描述接口) K3 X8 U; z0 E7 B0 D! p4 c
/ r# _% [6 Z8 v8 a% X7 D+ v
10) 12 SYNCH_FRAME:用于设备设置和报告一个端点的同步帧% [. U& l5 l% K2 q1 S5 F

5 ^' \' l9 e; N! [* A. ]以上的11个命令要说得明白真的有一匹布那么长,请各位去看书吧,这里就不多说了,控制传输是USB的重心,而这11个命令是控制传输的重心,所以这11个命令是重中之重,这个搞明白了,USB就算是入门了。
' n* g1 l& z( z0 d% |) J/ `8 Z  a3 Q9 P! {# M
问题八:在标准的USB请求命令中,经常会看到Des criptor,这是什么来的呢?5 Q6 N- X8 y1 j) \  m, ]; H

, W( G' [0 J8 O" E回答八:Des criptor即描述符,是一个完整的数据结构,可以通过C语言等编程实现,并存储在USB设备中,用于描述一个USB设备的所有属性,USB主机是通过一系列命令来要求设备发送这些信息的。它的作用就是通过如问答节中的命令***作来给主机传递信息,从而让主机知道设备具有什么功能、属于哪一类设备、要占用多少带宽、使用哪类传输方式及数据量的大小,只有主机确定了这些信息之后,设备才能真正开始工作,所以描述符也是十分重要的部分,要好好掌握。标准的描述符有5种,USB为这些描述符定义了编号:
4 d4 j, D. D' V) D6 S8 Y1 u
( d4 T( A* m* C1——设备描述符
3 Y+ e+ i; C; G% r: Y7 p3 s6 ^
2——配置描述符& x5 o6 ~& G$ Q% `2 X

0 E7 x* U" q* W2 n) S) P; f3——字符描述符# U; [% O9 \0 B4 F; s& o( o" Z
- f: i1 F0 i) J( Z2 e# a
4——接口描述符* T0 ~  r0 ^- m& V/ a2 E7 S. o
1 s2 k; d+ E4 f2 f5 k/ J0 h6 g, E
5——端点描述符' m* X3 H0 Y1 x) F9 B$ V
  h/ H' \3 a) V
上面的描述符之间有一定的关系,一个设备只有一个设备描述符,而一个设备描述符可以包含多个配置描述符,而一个配置描述符可以包含多个接口描述符,一个接口使用了几个端点,就有几个端点描述符。这间描述符是用一定的字段构成的,分别如下说明:! o0 `( v5 X1 |: W" f* o5 @
8 D6 E+ i' U5 a3 O+ E* ~! Y& k
1、设备描述符1 b( T/ _+ w9 a+ T

$ x) K. g3 ~! i1 estruct _DEVICE_DEs criptOR_STRUCT
; G, B+ O& X, G0 g" \7 G$ I, p, j3 l! o& |
{
* C) o! m- t' u! l& {# i% l+ ~# w  c" P; Z
BYTE bLength; //设备描述符的字节数大小,为0x12; Y1 D, `6 O0 `2 R

9 q! f& G) Q# B" t9 M# @, i6 B$ ]BYTE bDes criptorType; //描述符类型编号,为0x01$ T0 I# L, W) C) J- K7 U

( s6 Y( N' z  R- w& jWORD bcdUSB; //USB版本号
+ _$ _- Y8 g4 l0 B& q, E
0 y) Y, [) q3 f1 f2 O* i& Z& xBYTE bDeviceClass; //USB分配的设备类代码,0x01~0xfe为标准设备类,0xff为厂商自定义类型1 @8 N" E4 o' {: o

! ~. r4 g6 J+ r) }0 r1 X- f8 H//0x00不是在设备描述符中定义的,如HID
* o+ ?2 \4 }" F8 U  P7 h# k5 A: f
# x0 M. T  y9 b8 Z- q7 t  o# n8 rBYTE bDeviceSubClass; //usb分配的子类代码,同上,值由USB规定和分配的- B1 [0 H/ W0 A- J/ _
9 c1 F' C/ Z1 E2 r" s. b
BYTE bDeviceProtocl; //USB分配的设备协议代码,同上0 e' H: e3 A. j/ r# J. w7 P

' b/ F- m% {! r9 i5 `BYTE bMaxPacketSize0; //端点0的最大包的大小
- h! v6 B2 |  h, h
0 w4 V; W2 K% }" N) LWORD idVendor; //厂商编号
6 w6 v. b- p" N# ?
3 B8 I! {8 Q2 e: r/ o- p& {WORD idProduct; //产品编号
) ]4 ?& J2 E8 O6 r/ y" k8 ], [6 Z+ u0 M/ z5 |* `3 z( d
WORD bcdDevice; //设备出厂编号
7 E+ U( A, g8 p: `7 d6 i
5 c9 z! `# S! M# F% P0 @/ R3 eBYTE iManufacturer; //描述厂商字符串的索引5 T9 d- t1 q8 l  |
, v9 ~/ \$ B- N% S
BYTE iProduct; //描述产品字符串的索引
( b* u% V1 Y1 N1 ?& ~5 X" z. M0 ?& j% \! C- i3 t- A6 D" w
BYTE iSerialNumber; //描述设备序列号字符串的索引
$ k: C# j3 _! ~2 h- c# i% c- v& g# S1 m! k: d2 X
BYTE bNumConfiguration; //可能的配置数量
" f7 a% g+ X4 `3 p" ^* u& r7 n  T( Z& g# ^) S: j0 Z4 g: R% C
}% K( e8 V  T% s* g

0 e' j! f( w3 v; Z+ [2、配置描述符
4 y4 T1 ~' C, z' V/ @
. W& y5 b; u9 x- s& N6 |3 hstruct _CONFIGURATION_DEs criptOR_STRUCT
9 V4 l1 i1 t  R# W8 `& n) M, z( Z" P
{6 Y' o) F( q4 y& m! _+ w" D
# m" W8 l( Z  ]( K3 \
BYTE bLength; //设备描述符的字节数大小,为0x12
  f7 M: S$ _' D+ G* a$ p& \  o4 X; \. A( }/ S
BYTE bDes criptorType; //描述符类型编号,为0x01% B8 E8 X# B# |% B, p

( W& A+ |1 R' V( `3 |WORD wTotalLength; //配置所返回的所有数量的大小: p* ]! f& {- B8 ^" ?4 z7 _' y

2 S/ @* u2 t; d: w: C$ b: qBYTE bNumInterface; //此配置所支持的接口数量
3 M2 x7 f) U7 R7 ~5 t' g/ z
, J( S' s7 I% B/ wBYTE bConfigurationVale; //Set_Configuration命令需要的参数值
* |# i+ F6 o+ b- L' s2 ?/ \5 ^; }
# v3 [; D' M8 p1 wBYTE iConfiguration; //描述该配置的字符串的索引值
# V% S! x* e  `+ w/ u$ A) e& V  _) A/ C6 \6 l$ U+ Z
BYTE bmAttribute; //供电模式的选择" R8 u. A' C) w0 Y" x: I9 [

- w! i7 e' y  {; y- I- C7 y" V$ |: hBYTE MaxPower; //设备从总线提取的最大电流% m7 u6 c1 {+ _2 j7 u  o

, U# i. r. M. ~. v}6 ?. B9 z! f( ~4 }. p
' K9 V- v, G1 _* A" S: u
3、字符描述符
( W5 A( A" i0 E+ w& q% q
( N; x" j/ g+ @  \/ t& Ustruct _STRING_DEs criptOR_STRUCT2 \& J) s. y" l% @3 ?0 }
' ?" _  }& z6 c9 t8 a
{& t% x# T( _# u: P2 _, H* o" _
7 E2 S! d7 |( @
BYTE bLength; //设备描述符的字节数大小,为0x12/ e9 O; n: W8 Z! a$ f5 w7 V

* o# L. W# Z% l$ c3 c4 p( `' JBYTE bDes criptorType; //描述符类型编号,为0x019 C9 _- ~, b8 \( S- f# {, E! ~
6 F& u2 u. w4 f; e' U
BYTE SomeDes criptor[36]; //UNICODE编码的字符串" f1 s6 R2 Z8 B/ A$ E

- a2 I: T8 M; T}! a9 _# Y2 L$ N$ D3 M

- E  m4 G+ l1 _7 f3 U! G7 c4、接口描述符. c* |; S1 z/ m$ k0 v; h4 H

& P% d) c& j8 Q: r. A% p, k1 bstruct _INTERFACE_DEs criptOR_STRUCT& n$ t. W- W& }( j5 [- e

+ J6 M7 p6 V6 i; ?! M{  ^$ X2 G& T0 @7 q+ q5 [
) H$ Y) O7 ^) f( ]7 o: ?  j
BYTE bLength; //设备描述符的字节数大小,为0x12
, f0 ]/ K$ x; y
0 X8 Z1 \8 Q, ^BYTE bDes criptorType; //描述符类型编号,为0x01
7 r( S. T( S- W4 D' i( H# Y
/ u9 \4 R/ t: X0 s- h( pBYTE bInterfaceNunber; //接口的编号
, O5 a, U. }* j/ S, F, b" N4 l
6 [! b/ S6 P5 c% W4 h2 oBYTE bAlternateSetting;//备用的接口描述符编号
/ I' Q' l# }4 V0 _9 J  ?9 D; V; g" K+ Y  C# n9 j# ~7 t" K- E1 G
BYTE bNumEndpoints; //该接口使用端点数,不包括端点0
, s/ X6 S' T9 O: f/ a. ^
$ L; U/ |9 F6 ^- IBYTE bInterfaceClass; //接口类型5 M6 A( E# b* k: u  P5 N4 g

6 ]1 T8 Z2 o) q  CBYTE bInterfaceSubClass;//接口子类型0 e" d8 c% i. \0 L( t
- P2 T. P' P( H3 X7 U
BYTE bInterfaceProtocol;//接口所遵循的协议- i: {9 t' i! H

- K2 P% ~0 N( K7 d: P4 ^! WBYTE iInterface; //描述该接口的字符串索引值; _* w- @( z# @0 a0 C
& \( S3 V  u4 b0 ~. ~5 K
}
# ?8 i+ T; F" O& h: X. S3 {# O( c5 P' p' o/ b3 |
5、端点描述符
! V8 L& U5 j# C3 g( i) {. h9 r. }% ~. d
struct _ENDPOIN_DEs criptOR_STRUCT
2 e5 M+ C5 K2 N, t. P; g& w
! M* m  g) ]% }+ l: G1 O{# u8 e/ }- [( ]$ Z  p! h

% G, h/ Z, I3 o8 bBYTE bLength; //设备描述符的字节数大小,为0x12* I$ m. `: k4 ~; n- t  r6 ^. s0 C% v

: H$ d6 F( C' z' d4 \+ CBYTE bDes criptorType; //描述符类型编号,为0x01
4 n  i/ u* Q  x7 [6 Q0 |3 \3 o2 ~7 m9 n* W3 e0 Z
BYTE bEndpointAddress; //端点地址及输入输出属性
" `4 B* S! O$ `3 q4 n2 N  m, n6 M
6 X. D# y0 V6 l" z( e* CBYTE bmAttribute; //端点的传输类型属性1 `& Q& I4 D% {2 k9 J: q+ t
8 l0 w3 d, b7 b7 Y
WORD wMaxPacketSize; //端点收、发的最大包的大小; h& I" D# ~9 V+ {" i
: h  _( `* W6 R# W$ Q. \5 b0 [
BYTE bInterval; //主机查询端点的时间间隔
$ Z6 @; R' d3 O9 R! H4 L% I/ a% W  b- }% k. f# }% q- z
}; S& _& s; M8 I& o6 C6 \

; S% K$ T/ S5 s/ g: q* ?: G( f/ k了解了上述这些知识,会对于USB的学习提供一个较好的帮助。6 {3 s1 R" N5 [4 A
赞 收藏 3 评论8 发布时间:2017-1-10 13:49

举报

8个回答
Apple789 回答时间:2017-1-10 13:50:35
先把使用的例程工程文件传上来3 f- c7 s# H, n+ h
【1】金龙107_DEVICE_HID.rar (626.71 KB, 下载次数: 40)
Apple789 回答时间:2017-1-10 13:51:49
首先我们从主函数开始分析,主函数中就这一个USB的初始化函数。该函数的第一个参数是关于USB的配置以及寄存器的结构体地址,USB_OTG_FS_CORE_ID说明是全速的USB模式,然后USR_desc是描述符的结构体地址,USBD_HID_cb是关于HID的函数结构体地址,USR_cb是关于USB的一些用户函数的结构体地址。( Q, g: o  ^: O5 a0 y  g; W
USBD_Init(&USB_OTG_dev,
+ {) }# J% Q. x7 @# W7 |. N#ifdef USE_USB_OTG_HS . n0 a# A# h: H# Y
            USB_OTG_HS_CORE_ID,
2 {1 T0 ~8 ?7 U$ W#else            8 {) |' Z' d' [4 Y. R0 q
            USB_OTG_FS_CORE_ID,! N1 j# z2 N+ Q9 }) x6 w
#endif
  A0 F7 u1 a) V6 S% z4 x! O- J$ y            &USR_desc, - Q, ~' f; R6 a- a# o. U0 A
            &USBD_HID_cb,
) U+ g: X- K; `! T/ _0 z            &USR_cb);
* a( v+ @4 }, Q在这个初始化函数中,USB_OTG_BSP_Init();函数使能USB的时钟,第二个函数没作用,下面的函数是对USB的寄存器进行配置,以及使能USB的中断。
7 W  t: `, ?4 W* F+ i0 ^void USBD_Init(USB_OTG_CORE_HANDLE *pdev,
. f; V. X8 e: Q. l) L( w               USB_OTG_CORE_ID_TypeDef coreID,
" Y- I5 k# m0 \! p6 @) P2 n               USBD_DEVICE *pDevice,                  $ ^, t; N/ X7 A& c% _
               USBD_Class_cb_TypeDef *class_cb, + V3 ]- x* V( d! t' h
               USBD_Usr_cb_TypeDef *usr_cb)
' q3 P; ?( \/ s$ v{
: H# p5 _9 w5 V* K  P6 D  /* Hardware Init */, ~# Z: ?$ j2 N  `7 I9 }6 Z! E& B& _* w
  USB_OTG_BSP_Init(pdev);  / X- l) T5 f7 t. B0 @

! e6 |: t" R: ?! F' o" C* Z& P- x4 s  USBD_DeInit(pdev);
& K; |( ]1 `, o" H* w8 I5 \0 x3 G9 l
  /*Register class and user callbacks */
  G& J% t' n3 J0 f6 G8 a  pdev->dev.class_cb = class_cb;1 c5 i3 T  c  ?2 u0 O, N" M8 P
  pdev->dev.usr_cb = usr_cb;  
- A) S* W& ?; y7 Z) E( H! V% K1 j  pdev->dev.usr_device = pDevice;   
9 |! U) N, h# u; @' f+ J7 f; |: {* B
5 y) Z- H$ v& K! B( V* a& ?4 Q  /* set USB OTG core params */
) v: r9 |' m& M8 q  DCD_Init(pdev , coreID);. p+ c& o, b1 y4 k9 x+ z- b

9 B/ f3 F$ o! p2 Q, x  /* Upon Init call usr callback */( L$ v& b7 V7 w4 H, ~5 f
  pdev->dev.usr_cb->Init();$ B; K; ?/ }# h3 o' ^4 I. F3 Y

9 i$ k" r3 @4 J+ P7 z' g) x% ~  /* Enable Interrupts */
9 M6 \8 Q% e. T; E, Q& l' w  USB_OTG_BSP_EnableInterrupt(pdev);
: i; ]  [9 R* Y7 H) q% |# |( E}
Apple789 回答时间:2017-1-10 13:53:05
不好意思,最近有点事耽误了。没有继续更新。1 L0 `+ v* w/ ], D$ A) y
在对USB的寄存器初始化之后,就是对USB的枚举过程,枚举过程都是通过USB中断完成的,下面可以看USB的中断函数,在stm32fxxx_it.c中,找到USB中断的入口,其中断的原函数是在usb_dcd_int.c中。
' Y* W" |8 O- h3 |) huint32_t USBD_OTG_ISR_Handler (USB_OTG_CORE_HANDLE *pdev)  z3 U# Y% l, b# W# m2 ~
{
9 J# \% {5 Q& k% g  USB_OTG_GINTSTS_TypeDef  gintr_status;
" ^: a  ?! d% ^$ C/ p  uint32_t retval = 0;( _9 M- g% b- d( T2 c5 L; M* |

  y/ t  m* A/ Q* z& j2 O' [  if (USB_OTG_IsDeviceMode(pdev)) /* ensure that we are in device mode */8 w' V7 N2 B. J  K
  {3 Z- H6 G0 ?$ K. g+ L& i7 i
    gintr_status.d32 = USB_OTG_ReadCoreItr(pdev);
' O8 }) j2 Z2 c: W0 v+ S! J* ?8 w    if (!gintr_status.d32) /* avoid spurious interrupt */; w7 D+ d8 z5 s9 y: B
    {
$ |. X0 k, v8 x4 n$ G, Y3 l, d      return 0;
# U6 T& @& y- R" D$ a; S    }7 |) y0 W3 Q0 E1 }8 I/ L

' B* s" G+ u$ Z    if (gintr_status.b.outepintr)% L+ q; ?' y; M. K
    {3 d" l8 X$ z: l; L
      retval |= DCD_HandleOutEP_ISR(pdev);
  V! _( m% m6 ]0 B4 M( v    }   
: Q1 M5 c  I7 G7 k$ i7 @6 W' K7 f! M- x
    if (gintr_status.b.inepint)9 x* s% t4 d' d. A  P
    {# X  E+ ?) ?% ]* X' E' V6 R
      retval |= DCD_HandleInEP_ISR(pdev);
$ D6 J3 H' [" d& a5 n1 b: h" ~    }
# U0 A/ v' ^7 \2 U4 I+ G. m# L1 `- W$ S6 V  F9 `4 z
    if (gintr_status.b.modemismatch)( e5 J8 |  ?  w- E5 t( t
    {
. x2 w5 Q# D0 J) O2 U- i      USB_OTG_GINTSTS_TypeDef  gintsts;0 p0 @2 b; G$ Q! X6 p3 a) k

0 r5 `9 ]7 ?3 `% K" n3 Z  `* G, c      /* Clear interrupt */7 s1 B; ?% M' E$ q
      gintsts.d32 = 0;
# t$ Z* f2 [, U      gintsts.b.modemismatch = 1;
$ R! P0 r  l1 m" a0 B  c      USB_OTG_WRITE_REG32(&pdev->regs.GREGS->GINTSTS, gintsts.d32);" x: T- V8 t7 e- V* n
    }
) h1 ^9 c; Y! Z$ T# Z& b' ?& L/ I/ q* }
    if (gintr_status.b.wkupintr)
1 y; w' B  ?  k1 p( _    {
3 I( D' k/ i- o. ]      retval |= DCD_HandleResume_ISR(pdev);/ X6 @- F8 _7 g
    }
+ B# w7 k& a1 X! r/ Q; G# I, o) u0 @/ ~; t' {1 d3 \3 ^" u7 t7 W
    if (gintr_status.b.usbsuspend)0 b( b& ]- u- p$ {" I
    {4 m* c% x& X! m5 y# G+ T
      retval |= DCD_HandleUSBSuspend_ISR(pdev);
$ {% B4 y, S/ Z    }
; d' W0 K! ~" g# q3 k: T: Z8 \) p! B    if (gintr_status.b.sofintr)
4 N* i1 d9 \# G9 M2 E    {
( ^& `( u' _* ]$ e      retval |= DCD_HandleSof_ISR(pdev);
# R2 p3 l& f" ^, }5 h% y
# W' ^, y) Z6 l7 R8 X    }! x. v% ~- p/ U8 f& @0 _. C9 m

; L( j, }( S2 A% `0 r3 M    if (gintr_status.b.rxstsqlvl)
( m5 R- V; ]  l9 @" J4 X' B4 f    {
9 W) x( g' a# K. d      retval |= DCD_HandleRxStatusQueueLevel_ISR(pdev);' D4 _# y0 V* x6 Q
& i- b& S* t, i2 k3 q
    }
' K6 V6 u/ _. N$ X' o  K- H5 f! n( |" g  o9 c: a
    if (gintr_status.b.usbreset)4 m# M4 t& R+ K8 ^" m5 k. v! L9 e
    {$ z# Z$ g# K4 G/ z1 F% P, W0 D. v! Q
      retval |= DCD_HandleUsbReset_ISR(pdev);
/ Q) S: l  M! k. M5 K2 V5 V& ^0 {- v8 A
    }
: h9 G; C) V/ d6 h1 G: C7 S7 s& F    if (gintr_status.b.enumdone)' w( o* ]. v% `( p
    {
: n# G5 @3 z9 N/ l7 S' m      retval |= DCD_HandleEnumDone_ISR(pdev);6 U: B* N8 Z# ~; D. T$ S$ N) y
    }% E& |) P/ n0 A: }1 X9 ^2 x

2 q. h- e; E& H; k# v0 Y+ Z    if (gintr_status.b.incomplisoin)4 l5 R; `5 O$ h# }& K
    {
6 I7 f8 f6 ?, p! c      retval |= DCD_IsoINIncomplete_ISR(pdev);% N9 T+ h0 c/ c$ M' b
    }
. ~0 ^1 f1 u+ P1 T0 W+ `/ ~/ Q9 x# z) I
    if (gintr_status.b.incomplisoout)& S' p) r% @: ]  {" u+ e/ H' {) X
    {
( Z% _* s9 y! a; L2 U      retval |= DCD_IsoOUTIncomplete_ISR(pdev);" @9 D" `0 z  s; b) E( }3 U6 Y  w
    }   
5 l- a7 R% p+ N( N, Y#ifdef VBUS_SENSING_ENABLED0 c, J6 [& }. ?
    if (gintr_status.b.sessreqintr)
. v) a" U6 a! e) @0 N) E    {* u! [2 X# [2 s* s! a
      retval |= DCD_SessionRequest_ISR(pdev);
/ Q; l* P: L" e* E) M1 d: {0 ^7 z8 [    }
/ I) p) o9 ?2 g) Z4 u& h  S
# `9 j. Y9 i& N. ?+ c    if (gintr_status.b.otgintr)
+ n( f" K* m9 u0 U6 V    {
$ w9 |3 i5 [$ I; b. J, T; V      retval |= DCD_OTG_ISR(pdev);$ V; v/ d5 t) f4 H7 d6 I; f
    }   6 q; q( ?, U% `, ^# @
#endif    ) t3 h4 F4 q8 A/ ?6 ^) U
  }
% [9 Q; i/ u$ ?/ i" @; a3 `# o  o/ G) F  return retval;) T* t5 p5 C( r" j) L4 }) K
}* u9 ^+ O. I* q1 }

* ]# W$ _% S6 V5 r通过 gintr_status.d32 = USB_OTG_ReadCoreItr(pdev);这句话读取的是USB的中断标志位,来判断是什么引起的中断,这个我们可以查看USB的中断状态寄存器来了解这些中断源。  r$ w. D5 m( L1 A0 O$ N
同时大家注意gintr_status这是一个共用体,其中两个参数是b和d32,并且其中b的还是一个结构体,里面的变量分别对应了USB中断状态寄存器的对应位。' ]5 j# o- L, Q0 [& F* I( u& C9 m
这里面主要的是注意上面两个中断,一个是USB的输出数据的函数DCD_HandleOutEP_ISR,一个是写入数据的函数DCD_HandleInEP_ISR。
Apple789 回答时间:2017-1-10 13:54:51
* u' x, V4 M$ T3 |0 ^+ f
USB的枚举过程,也就是描述符。在usbd_desc.c中,配置了USB的设备描述符。' x) h+ f9 {) q& d6 }' I
__ALIGN_BEGIN uint8_t USBD_DeviceDesc[USB_SIZ_DEVICE_DESC] __ALIGN_END =
9 G7 {, V% g) x, j9 n  {
  k# g7 T" r4 U. ]) @0 |, @' Z3 V    0x12,                       /*bLength */
5 c; J) J. ^: f0 E* T& f    USB_DEVICE_DESCRIPTOR_TYPE, /*bDescriptorType*/5 L: Z+ Z4 L, g
    0x00,                       /*bcdUSB */& h6 W2 L6 X/ C$ ]7 x7 k+ W
    0x02,, c2 _  Y4 c2 ^; H. N. e
    0x00,                       /*bDeviceClass*/
, x- T3 q' o! S/ K5 b    0x00,                       /*bDeviceSubClass*/
0 M3 O& \+ T+ n7 X$ N    0x00,                       /*bDeviceProtocol*/- y7 j1 P# a+ d. v/ q
    USB_OTG_MAX_EP0_SIZE,      /*bMaxPacketSize*/2 _3 e; ?9 e7 W9 v$ v
    LOBYTE(USBD_VID),           /*idVendor*/
% y1 q/ v* F& W1 Y    HIBYTE(USBD_VID),           /*idVendor*// @. D4 F* c# [3 p. [8 I) T
    LOBYTE(USBD_PID),           /*idVendor*// a: q: H0 I& l( O2 h; x
    HIBYTE(USBD_PID),           /*idVendor*/
( R, R4 Q6 `, X' Z( j( x    0x00,                       /*bcdDevice rel. 2.00*/7 x5 |0 V. q- l; \2 Z, f
    0x02,7 U* W5 e  ~; b4 J2 m5 E8 s
    USBD_IDX_MFC_STR,           /*Index of manufacturer  string*/3 V" c# ~) q" m/ p9 y4 k' A
    USBD_IDX_PRODUCT_STR,       /*Index of product string*/0 y) F: g/ x2 ~5 @
    USBD_IDX_SERIAL_STR,        /*Index of serial number string*/8 R' b% r+ f2 C  ]. c1 b
    USBD_CFG_MAX_NUM            /*bNumConfigurations*/
; a! o( j6 x  ?% {$ n$ O  } ; /* USB_DeviceDescriptor */
* s6 a9 N$ ~; Q; w' M$ h
4 N* b  q" z/ n7 F; h  K0 j在usbd_hid_core.c中,配置了USB的配置描述符,接口描述符以及端点描述符,和HID报告描述符。
( [8 K3 |8 ~1 s6 u__ALIGN_BEGIN static uint8_t USBD_HID_CfgDesc[USB_HID_CONFIG_DESC_SIZ] __ALIGN_END =
9 C& x: j" O, k3 U{% t$ j( U% R$ H2 I) i0 i9 F  V
  0x09, /* bLength: Configuration Descriptor size */
+ d  J" L8 T) v# L  USB_CONFIGURATION_DESCRIPTOR_TYPE, /* bDescriptorType: Configuration */
5 F& M: F" w9 G  USB_HID_CONFIG_DESC_SIZ,
! d' @  t! E5 w. P' q  /* wTotalLength: Bytes returned */5 a- D8 M: v7 E- J" T  c
  0x00,
! W4 a( Y4 T3 N) o/ O  0x01,         /*bNumInterfaces: 1 interface*/! R; E2 q% w) }$ T, D- k
  0x01,         /*bConfigurationValue: Configuration value*/
- i; J' E, ^( }9 }5 c7 C9 @  0x00,         /*iConfiguration: Index of string descriptor describing
5 z- U! w0 R% o- A% h( t# c  the configuration*/
7 C6 z: S7 d( ^: ?) D3 N; A3 J5 P  0xE0,         /*bmAttributes: bus powered and Support Remote Wake-up */
. g- v9 j; p) O7 A" S) U1 y  0x32,         /*MaxPower 100 mA: this current is used for detecting Vbus*/! \- f6 K, ?7 H& z
. ?, S0 z7 o; c) ~9 R' [
  /************** Descriptor of Joystick Mouse interface ****************/9 h. A4 |1 }! }3 c0 D
  /* 09 */$ o# L. {7 P; v# @
  0x09,         /*bLength: Interface Descriptor size*/
5 \" c! d/ |) y" ~1 @1 _  USB_INTERFACE_DESCRIPTOR_TYPE,/*bDescriptorType: Interface descriptor type*/+ s- a$ t# C- x! l, f
  0x00,         /*bInterfaceNumber: Number of Interface*/; [4 Q  {1 `' G/ e
  0x00,         /*bAlternateSetting: Alternate setting*/
: T; Z$ C  ?* R4 ?$ y  0x01,         /*bNumEndpoints*/
0 {9 {# S' V/ {1 P) o/ O5 l  0x03,         /*bInterfaceClass: HID*/
3 R- k* Z$ h5 V9 q( U6 s  0x01,         /*bInterfaceSubClass : 1=BOOT, 0=no boot*/% U. O' s- {1 P( u  R; z
  0x02,         /*nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse*/( k+ f/ x- x1 x! b! Q! X4 D
  0,            /*iInterface: Index of string descriptor*/' O+ Q) j8 q$ b9 W4 L$ h5 _$ A
  /******************** Descriptor of Joystick Mouse HID ********************/) f. [# A  ?; ]* {
  /* 18 */# ~, j5 G. ^$ V, m1 m
  0x09,         /*bLength: HID Descriptor size*/
4 c: b1 I( Q% `1 k+ T  HID_DESCRIPTOR_TYPE, /*bDescriptorType: HID*/- H8 d6 ^( X" k+ k) S
  0x11,         /*bcdHID: HID Class Spec release number*/' j6 Q, C5 h2 f8 `8 l
  0x01,
5 H1 z" w' s3 t4 e0 @  0x00,         /*bCountryCode: Hardware target country*/' a- c, u; e3 C: w
  0x01,         /*bNumDescriptors: Number of HID class descriptors to follow*/
& k& M/ ~) h* z% ^/ N  0x22,         /*bDescriptorType*/# M2 u# p8 |+ @7 S5 t: L
  HID_MOUSE_REPORT_DESC_SIZE,/*wItemLength: Total length of Report descriptor*/
! K' s; D  N7 E# v0 D  0x00,
& E4 \2 q6 E# L  /******************** Descriptor of Mouse endpoint ********************/9 I/ J$ o& q5 E" B% ]' C$ R' W' L) A) M
  /* 27 */5 l) f' Z4 v) z, V1 |6 c
  0x07,          /*bLength: Endpoint Descriptor size*/
+ L8 s( H1 _. {+ E% B& p" h  USB_ENDPOINT_DESCRIPTOR_TYPE, /*bDescriptorType:*/% d6 S* w. i0 C. O

* P( \+ e2 ^" @' L: V+ }. V8 O+ S  HID_IN_EP,     /*bEndpointAddress: Endpoint Address (IN)*/
) x7 k: E, c& @0 c7 N  X1 S  0x03,          /*bmAttributes: Interrupt endpoint*/
& H1 y  k9 Z, `; x/ r0 P  `/ x8 }  HID_IN_PACKET, /*wMaxPacketSize: 4 Byte max */( r# N0 X% b6 @2 F
  0x00,
: h0 }0 H$ X6 n: I- [  0x0A,          /*bInterval: Polling Interval (10 ms)*/# a) a' k) X0 R
  /* 34 */% j: ^2 n8 \+ [/ a. ]8 e
} ;
1 T2 T; L' ?5 ]+ X" b  |6 ]. [2 R" x+ o0 ^
大家可以对应一楼的看一下。
zero99 回答时间:2017-1-10 15:00:46
看看  内容好多啊
( C% t& a- P1 A0 F( I
leo121_3006061 回答时间:2017-1-10 16:30:12
谢谢分享,学习了
Apple789 回答时间:2017-1-12 15:20:40
迎新年,免费学习开发板啦!
' ]4 ~  H7 L- v! Y* A+ fhttp://bbs.openmcu.com/forum.php ... amp;tid=22#lastpost
cloudmr 回答时间:2018-3-24 14:22:01

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版