STM32F103C8T6使用MDK 4.12中Custom_HID的修改方法
0 O# m' N ^! S目的,由上位机发送64字节,设备回应3个字节,功能自定。。
7 f" v1 R0 a9 j8 A7 |! C/ s) t: _/ ^- i; k& ~( H2 G
板子使用的是最小核心板,MCU为STM32F103C8T6。
! u8 }# K$ v T7 x2 dUSB的D-和D+分别接到PA11(USBDM)和PA12(USBDP)上,PA12(USBDP)即D+通过跳线使上拉1.5K电阻到VCC(3.3V)。 E& a b; L" r ^2 o$ z
1:打开MDK4.12,打开
+ ?' ~( u4 U8 `4 w+ DC:\Keil\ARM\Examples\ST\STM32F10xUSBLib\Demos\Custom_HID\project目录下的Custom_HID项目文件。
. y/ g( l- Y7 E: m$ b' y3 s
3 w) t! T: D2 L0 _( I- ~9 X2:选择菜单 Project->Options for Target 'STM3210E-EVAL'; n& r: b$ e5 }- ]4 ^
在Device选项卡里选择 STM32F103C8。2 L1 b- H! f3 l& h# ?. K
在Output选项卡里把Create HEX File选上,取消Browse Information。' j3 o. B* b3 C) S" k, n
在C/C++选项卡里的Preprocessor Symbols里的Define: USE_STM3210E_EVAL清除。
, ]+ a, x, d: k6 f: P
1 O" o& Q1 P' M/ _4 }3:从Application files组中的文件由上而下逐个修改3 N: @& w1 c; X/ y$ m) ~
第一个是usb_pwr.c) y. O9 d3 K) ^1 s8 Z
把PowerOn和PowerOff()面里的USB_Cable_Config(ENABLE);和USB_Cable_Config(DISABLE);这两句删除,因为我的板子上没有这个控制电路,
( X/ O; J2 `! d% W而是通过跳线把USBDP通过1.5K电阻接通到VCC(3.3V)的。
& n$ B9 h! o1 q" E% @# @& e接下来修改hw_config.c: a/ b i! E+ l/ C' \: B, i
把
: b! f/ G4 Y! Y6 |#define ADC1_DR_Address ((u32)0x4001244C)
8 {& Y# N' m9 q( r" \6 v, pu32 ADC_ConvertedValueX = 0;. J& k) a0 `9 x( ^8 {( y* Z4 T1 g- z
u32 ADC_ConvertedValueX_1 = 0;9 j4 [7 W+ r5 R" j$ n
这三句删除,因为我不用ADC,外部中断和DMA,只需要跟上位机通信点亮板上连接到PB0上的一个LED。2 b2 ]. }2 w" w9 |+ Y7 ~3 o
4 x7 o! u u/ T2 B
所以还要把Set_System函数里的& b' @- Y% s7 ^3 q( M6 ]" T
EXTI_Configuration();
: x: e1 O. A9 ^) g' nADC_Configuration();
" G' O, @. S5 k$ Q) B l这两句删除。" J N) \+ F7 u8 m- }$ ~
$ ~, O6 ?* B) ~5 c: B8 ~3 D# A8 J把USB_Interrupts_Config函数里的
) z- a) D8 q6 k/ ^7 XEXTI9_5 Interrupt# X+ R' A! g$ K0 i; R
EXTI15_10 Interrupt& \2 N# U" I# }5 E9 t0 A
DMA1 Channel1 Interrupt" t5 S5 x# Y3 l2 X7 T. V
这三个相关的语句都删除。 ^! j3 K8 R: t. Y& n+ }! S
/ V2 Q% c$ U T! r$ d9 \1 v; y D把USB_Cable_Config整个函数删除。
9 y: b+ m( b' U& j' {5 s1 Q7 q
+ s- L% K( P- I$ Q. b; O把GPIO_Configuration里面的/ E. {7 y% D4 M+ Q' Q _: L
KEY
; T2 c% {- I6 P5 r4 g% l, b# Y6 ?Tamper; b) j. f3 X5 z$ L1 R( U. U
Potentiometer4 e* I4 f3 C- K3 \1 H2 q
三个相关的配置语句都删除。
9 g5 {" K5 v; @& L. r下面的LED配置只配置LED1,用来指示上位机的指令,其它都删除。
( A0 D' h+ [# L6 P- y
8 ]) \3 ~: y5 {/ I$ y5 C7 y把
% P4 ?! B8 v+ U( o) \' CEXTI_Configuration7 `& |7 f, D& `- p
ADC_Configuration Y- ]2 \: Q+ o- h( m
这两个函数整个删除。
/ P( x) C7 I4 `5 e: P0 E0 q/ o7 S3 S' T1 M6 c+ ?
加上这句RCC_AHBPeriphClockCmd(RCC_AHBPeriph_CRC, ENABLE); 别忘了开CRC电源。计算序列号要用到的,否则序列号就会变成42949-67295,是0xffffffff,是错误的。( U. S) G0 q) J9 x8 {& ?( k
8 K$ L) w! F9 J# a# ?
把Get_SerialNum函数的功能改成如下
1 @# `# {) [* u: C7 z5 x目的是把96位ID作CRC运算后来做产品的序列号。
1 U4 ?4 u/ C' O5 U, R可以不要这个函数,只在usb_desc.c里定义序列号。同时其它文件里用到这个函数的地方就要删除。
) t1 k! B7 V/ M: U' L. w% K5 {+ `
增加RBIT这个函数,32位位倒置程序,CRC用到的。因为STM32的CRC计算结果跟一般的CRC校验工具计算结果不同,所以要特殊处理。: p# L/ L( [7 C1 P2 i% _
如) L* W! M7 h7 I! E
原始序列号: 39006C065834313149670543
# w8 `6 B% x8 D分成三个32位数。每一个32位按位倒置,然后给STM32计算CRC,结果跟0xffffffff异或运算后,再把32位的结果按位倒置。就对了。 d' R2 M, X# X2 ^9 G- m: ]7 v
39006C06=00111001000000000110110000000110 = 01100000001101100000000010011100 = 6036009C
% O! C" a* \) {" T& F7 p58343131=01011000001101000011000100110001 = 10001100100011000010110000011010 = 8C8C2C1A0 |% E+ `0 R+ m" B, }7 [: c" ?7 l2 s5 P
49670543=01001001011001110000010101000011 = 11000010101000001110011010010010 = C2A0E692
1 W* _% t- K; n. v/ M$ y$ `* x$ A: V# x6 E0 c% y c
STM32的CRC结果跟0xffffffff后为十进制4289354847=FFAA5C5F=11111111101010100101110001011111=倒过来11111010001110100101010111111111=FA3A55FF
+ }. _; l( ?# g$ C
& z" W3 Y, U+ ?2 l7 w/ K- __asm u32 RBIT(u32 value)
( I0 b" `! t' I4 o4 ^% x! ` - {+ L0 t$ o2 u- y) X
- rbit r0, r0
. `8 e' L' A. z" g5 i - bx lr
& e% f) @) f1 {6 o' Z4 x$ M/ [ - }6 e" z1 @' X0 x# {/ O
' U2 |' Z2 _1 m D& g
% d9 W2 g- x% z' y7 S+ B- void Get_SerialNum(void)3 t/ g' L( D- y+ K3 V, R
- {6 J2 B$ I/ h' c) R2 y
- u32 Device_Serial, Device_Serial0, Device_Serial1, Device_Serial2;& f, M3 |* s: g7 o, f$ s
- 0 D4 E8 {) [7 `& B
( Z8 E& f" W3 Q8 @- Device_Serial0 = *(u32*)(0x1FFFF7E8);: v# m f; X* v z1 `( I
- Device_Serial0 = RBIT(Device_Serial0);# |, }8 s- f j5 s* ]; [; |
- 3 `& X5 | B: G1 ^% r- I5 n
# X3 n2 J$ A& f" B# t- Device_Serial1 = *(u32*)(0x1FFFF7EC);
; _# |& h9 W7 N5 \1 M7 t - Device_Serial1 = RBIT(Device_Serial1);, X! j/ E; B/ k/ c
- ' w7 f8 C2 k2 S
2 t b' t2 y; {' T$ D3 T- Device_Serial2 = *(u32*)(0x1FFFF7F0);/ f+ ?7 I4 T0 y# O
- Device_Serial2 = RBIT(Device_Serial2);
, `/ C2 ?9 K% F* G/ W( ~7 l - . G/ I6 s; k. ^+ V
) V! b) j. G# K- z4 v8 u- `; q5 f, ]) `( Y9 o1 j9 q4 W/ s
- * [: }1 R- W& h% H- Q3 B) C
- CRC_ResetDR();! j, M/ e1 h! f7 `$ p/ B
- Device_Serial = CRC_CalcCRC(Device_Serial0);
% N( }- }" O$ e. |% V - Device_Serial = CRC_CalcCRC(Device_Serial1);2 t" s) v3 I" L$ \; D
- Device_Serial = CRC_CalcCRC(Device_Serial2);
" [$ S# z6 ]2 O- M- Q/ y" t1 I - 0 M _ t& @8 W5 X* `' z
- 7 v1 P* T5 K& c' q, P5 S# o
- Device_Serial ^= 0xFFFFFFFF;4 @$ Z; u2 v9 l# z3 F3 k
- Device_Serial = RBIT(Device_Serial);7 k% D4 J0 O% ^# ]
- 3 r% W9 @" a3 S7 M) r' i5 b
: J0 Z: y5 R, Y, }+ e- if(Device_Serial != 0)! h- M' N) l& M t" q/ J! q9 g
- {
/ X; N/ z" p! D0 E% e4 L4 a - CustomHID_StringSerial[0] = CUSTOMHID_SIZ_STRING_SERIAL;
2 i; h7 ]* N2 O - CustomHID_StringSerial[1] = USB_STRING_DESCRIPTOR_TYPE;% L: G7 ^1 @$ n2 ?: k: J; T
- CustomHID_StringSerial[2] = (u8)(Device_Serial/(u32)1000000000)+0x30;1 u2 u' ?) B; M" g- M
- CustomHID_StringSerial[3] = 0x00;
1 U: ~2 ]: D1 [& }1 B9 E! t - CustomHID_StringSerial[4] = (u8)((Device_Serial%(u32)1000000000)/(u32)100000000)+0x30;
" A2 o9 N' r5 e7 a6 L - CustomHID_StringSerial[5] = 0x00;. [: R7 y& u, x: B& S
- CustomHID_StringSerial[6] = (u8)((Device_Serial%(u32)100000000)/(u32)10000000)+0x30;7 j7 o+ j+ x; u1 `7 w
- CustomHID_StringSerial[7] = 0x00;
Z) a: J# l( E6 u. D( d - CustomHID_StringSerial[8] = (u8)((Device_Serial%(u32)10000000)/(u32)1000000)+0x30;& I. m. R% L9 C) f0 Z
- CustomHID_StringSerial[9] = 0x00;7 g( G0 `% D/ z3 s. j- c6 x/ j% _
- CustomHID_StringSerial[10] = (u8)((Device_Serial%(u32)1000000)/(u32)100000)+0x30;$ N$ T, |" q# s4 t! r% F
- CustomHID_StringSerial[11] = 0x00;
" G; } F5 t* S9 H4 B6 a& Z - CustomHID_StringSerial[12] = '-';
+ ^3 u. p, F4 x+ v8 B& t( }0 @' n - CustomHID_StringSerial[13] = 0x00;
4 {+ f4 x+ W2 K' t4 `* }) C" o - 1 ~! Q _- b: X9 u0 D
- 5 r# ], {( K; O5 u* x" |5 J
- CustomHID_StringSerial[14] = (u8)((Device_Serial%(u32)100000)/(u32)10000)+0x30;
8 i% v) R, u$ L7 O5 } - CustomHID_StringSerial[15] = 0x00;5 ?0 a7 c4 U# m/ y& L
- CustomHID_StringSerial[16] = (u8)((Device_Serial%(u32)10000)/(u32)1000)+0x30;9 [; n# p) E' s3 R0 R6 r
- CustomHID_StringSerial[17] = 0x00;. ]3 l' c# H1 Z3 \
- CustomHID_StringSerial[18] = (u8)((Device_Serial%(u32)1000)/(u32)100)+0x30;* A+ H0 Y4 a4 K% P: Q
- CustomHID_StringSerial[19] = 0x00;
& g; G6 i$ u: Y+ o: s - CustomHID_StringSerial[20] = (u8)((Device_Serial%(u32)100)/(u32)10)+0x30;- n, b0 [- j& ?8 `
- CustomHID_StringSerial[21] = 0x00;
3 O3 \, j1 |7 g6 U& {. a - CustomHID_StringSerial[22] = (u8)((Device_Serial%(u32)10)/(u32)1)+0x30;1 @2 \/ E$ l1 T7 V1 [+ O
- CustomHID_StringSerial[23] = 0x00;. r% g6 ]- f; `# ^9 q+ K. p g( h
- } 8 D- e& J5 Y( h8 i( T. j
- }
复制代码 8 V2 @% } s' L1 [2 W; f
然后修改对应的 hw_config.h* k' n/ E" \" o, m0 X: [
void USB_Cable_Config (FunctionalState NewState);
3 G, M4 |! p! y V" k$ svoid EXTI_Configuration(void);
7 B# }4 n5 T( L3 w' Mvoid ADC_Configuration(void);+ p# N8 A5 g/ e8 d
把这三条删除。8 K1 J" z. g% F: ?
Get_SerialNum也视情况决定要不要删除。# r7 Z4 }( n2 @# E* n. Y
7 R6 `4 B; t: D# Y8 {9 d
main.c不动。
! p: G) z9 G" G( q( k/ ]
3 D" J8 ]3 i0 y2 u" K接下来修改 stm32f10x_it.c
- X* h7 L; T% D5 c# {- h) I把u8 Send_Buffer[2]; 这句删除。3 { m. U2 d* k* s; L# Y8 r6 X
! W3 G$ }: {; B7 [把
) H- r" x6 S: Z% Y2 I8 f2 eextern u32 ADC_ConvertedValueX;
) n# u W' x9 h9 a _- J! aextern u32 ADC_ConvertedValueX_1;
4 A* O8 k. b: e$ F删除。
# D3 ~# ?) }& R& L7 X2 F- H8 c% @2 ^, x
把
9 |: w8 U. G+ w! t- g! gDMA1_Channel1_IRQHandler2 ?; T( c1 c) b
EXTI9_5_IRQHandler1 t1 ]4 j) @/ f# ]8 x. m
EXTI15_10_IRQHandler) N) N2 c# B. L- o/ k% B& J
这三个函数清空,是清空里面的内容,保留一个空的函数。
0 [" M t: w* a8 k" u4 w- W
. G0 p5 |- n% J0 s# |接下来改 usb_desc.c
7 y* `# t8 ~0 O+ I6 v+ h把报告描述符改成自已的:( a: q4 W+ _8 e: G9 e- j$ f
- /* CustomHID_ConfigDescriptor */" m; H% h0 C+ K; C( o9 ~+ d
- const u8 CustomHID_ReportDescriptor[CUSTOMHID_SIZ_REPORT_DESC] =
& q5 M" Q$ U1 ^9 V - {
7 }/ V1 L' V- c, ]& O5 B, d2 G - 0x05, 0x01, // USAGE_PAGE(User define)
8 c R+ P3 D) q# T- ~) V - 0x09, 0x00, // USAGE(User define)
0 a( V! x/ Q6 C- b% J- ^ - 0xa1, 0x01, // COLLECTION (Application)! ]. X% O$ \# v2 v _1 l8 Q: G
- 0x19, 0x00, // USAGE_MINIMUM(0)- F# g' c& I/ E9 g( ]/ J* J
- 0x29, 0xFF, // USAGE_MAXIMUM(255)
& F: C$ M7 ]; u; u0 @, { - 0x15, 0x00, // LOGICAL_MINIMUM (0)
' ~ \+ D0 w7 u/ m7 x, y: _: d - 0x25, 0xFF, // LOGICAL_MAXIMUM (255)
( b, |/ u8 c/ W$ }- c - 0x75, 0x08, // REPORT_SIZE (8). S5 w& `4 Y) g+ B
- 0x95, 0x03, // REPORT_COUNT (3)1 u1 h# x" ]4 M* b
- 0x81, 0x02, // INPUT (Data,Var,Abs)
) T. u9 T1 U3 R X8 `
5 c8 u; z5 M* W7 A0 }8 D7 D
+ G/ L( ?; h' S: s4 p- 0x05, 0x02, // USAGE_PAGE(2); d2 y' p5 b* p' {9 L
- 0x19, 0x00, // USAGE_MINIMUM (0)' n1 O& |' q8 T! ?) b5 [9 h
- 0x29, 0xFF, // USAGE_MAXIMUM (255)
2 N6 J0 w% Y' D( v9 B0 r5 v - 0x15, 0x00, // LOGICAL_MINIMUM (0)6 j& { N8 B- w5 z3 _; y
- 0x25, 0xFF, // LOGICAL_MAXIMUM (255)/ v& ^ F, b. [0 w" Z9 C
- 0x95, 0x40, // REPORT_COUNT (8)1 v7 W$ _' J5 o# Y- ]
- 0x75, 0x08, // REPORT_SIZE (64)
# ]7 W8 q& ~" U) X9 L; H( T - 0x91, 0x02, // OUTPUT (Data,Var,Abs)! q# v' M( C' O/ j, i
- ; ^7 n2 F' R5 c% W2 }# B) i
- 3 w+ m: V) H4 o3 m$ u) _0 M
- 0xc0 /* END_COLLECTION */- o/ w, I5 Y x6 \5 h5 E
- 0 K; I8 K( z' P. Q
2 P0 n3 a& F3 s7 F- }; /* CustomHID_ReportDescriptor */
复制代码 * w* o) d- D" k/ B! {
然后更改配置描述符中的输入输出端点的最大包数。输入端点改成3个字节,输出端点改成64字节。
& ]- ~% V( ]7 s4 L; z
$ q K) b( a5 v3 H6 v* _$ K) F再来改下面三个字符串描述符,使用的是UNICODE格式的,用记事本写好保存为UNICODE格式文件,再用十六进制编辑器打开就看到了。
8 c! V0 \& b3 @. d! z& h
! I9 W& T- l' j f; \" s改完以上的数据,还要更改usb_desc.h里的定义的大小。(注意字节数还包括描述符长度及标识),如上面的报告描述符大小应改成
3 ]# n9 p5 w" a1 M#define CUSTOMHID_SIZ_REPORT_DESC 37
3 l' r7 }: j( |, d% x' {1 B C8 T, \+ \, G5 ]7 R& n& J# \! n
接下来改usb_endp.c
( x4 L, a. L' E把
! O/ E& h3 |2 p7 L% uu8 Receive_Buffer[2];
" u1 J- A! M5 w4 s* A改成
6 V6 o5 f/ \5 _0 E# b7 x7 Lu8 Receive_Buffer[64]; //因本例中要求上位机发来64字节。
0 C$ u7 f# F2 _2 {1 t, Z7 q; h, |$ c! e% z( e* d
然后是EP1_OUT_Callback这个函数的修改,根据上位机发来的数据,作出相应回应。8 |) N! P2 l5 ~5 i
5 `4 X7 C7 J/ G
usb_istr.c不用改。
7 ?( z. f1 X, O: J
7 c) z0 `& C s. d: ~4 a- j% O
; Z) t0 B. o( ~% i" j! K然后改usb_prop.c
5 z3 g$ G/ W: J3 {% P4 x把CustomHID_Reset函数里的
3 A$ l; z6 _( u- v+ w SetEPTxCount(ENDP1, 2);
& @* J$ b* e, e; Q# z* |1 X3 Y SetEPRxCount(ENDP1, 2);
8 N+ V, Q4 L0 z2 t0 r# r9 s" A改成& x- S& O8 {. T( z7 A0 e
SetEPTxCount(ENDP1, 3);) O. o, `8 A1 G9 y! B( ^5 ]7 h
SetEPRxCount(ENDP1, 64);1 p! a) b* S# N E3 m7 x
/ j# M0 p& ]- \3 OCustomHID_init函数里调用了Get_SerialNum函数,如果使用usb_desc.c里定义的产品序列号,这里也不用了。。
) _' W" F3 S8 P+ n7 q1 j* f! i9 X. M( o4 K9 V! I7 b
还要修改platform_config.h
2 W: k" H( d0 g! V% b/ q; l( G把修改LED引脚,USB引脚的定义。
/ i/ s/ E3 m6 u# ~) w! c# f# c$ A
& y2 x7 \' R0 ]5 _( @# R终于改完了。。* o+ x5 t& Q/ H9 w9 Q' O s
; r5 z" D8 \ r( P+ w$ J以下是记录下来的发送程序段。) d. }2 T0 P+ O5 M
u8 Send_Buffer[3];
+ C4 }% Z8 F& d6 ESend_Buffer[0] = 0xxx;0 ? q0 }5 n6 k1 j" g
Send_Buffer[1] = 0xxx;
! Z( I. C6 Z% G* \Send_Buffer[2] = 0xxx;
0 F5 p1 m x9 `0 O# X4 |UserToPMABufferCopy(Send_Buffer, ENDP1_TXADDR, 3);
. b& p: [# N* x% \) E/ q6 |SetEPTxValid(ENDP1);
( k; |/ [' g: h# ^. G% ~7 b" |" l2 ^/ V9 @: p2 H
最后要还说一说跟上位机通信要注意的地方,上位机程序要限制只能用运行一个实例,可以用互斥量。运行多个程序会导致USB不知应该跟哪个3 L/ y- J/ A5 B4 q4 g: b [
5 k$ y# d1 M% D5 C: a. w8 ~ i$ e2 U程序通信,数据会错误。6 ]' `) {% E0 ^, k7 W8 f
注意程序关闭再打开后要获得设备的状态。而设备复位后也要通知上位机,以保证两者状态一致。8 e5 f$ m: ?( q% j6 \7 h' ?0 R
上位机程序的读写缓冲都要比设备定义的大一个字节(开始位置,0x00)。这个字节由电脑发到设备中会被去掉,所以设备端不用管。
0 S- B& g) J4 \1 B) l电脑接收到的数据也比设备发来的多一个字节,也要处理掉。。。- P3 j1 b1 o, F
还有ANSI跟UNICODE的处理,要清楚。如字符串描述符中的字符串的显示等。
# G6 Z" e+ L/ _$ u& {- D' U& B9 K! E2 x. }
1 z; S- R7 X& h& L5 Z5 \; x, w0 Z; z
转载自:xqhrs232
' {1 l( d$ E* _! \& l i' b% `, K3 i7 E0 p9 V( @, e
+ Q, p& W8 D8 b; |$ Q+ l! x |