GPIO最经典应用:LED灯。
# u. H |& u5 Y2 i, X9 [7 n5 `. S' [7 L
先看电路。声明:参考正点原子战舰开发板。4 p/ h# q: v2 I( X1 H
, \5 y* W) U3 E F" r7 r
& p# E: ^" `5 Y$ [& @& I/ e, ?- D% a) K! X2 W: D; x$ {+ U9 O
- j# h; P" `/ `- z
: x! c" z9 D- @4 ^8 c, B+ {: h) z+ I/ R1 p
- K9 u) \, u: N0 p- b与LED串联的电阻称为限流电阻。+ h7 a& j$ g- n) Y/ \$ [
& N) D( w& s7 e' `8 L, |限流电阻计算公式:R=(U-LED压降)/20ma。. l& Y9 A2 F5 y1 R
0 S, P; r6 d! U& F
U为LED工作电压,LED一般最大电流为20ma。
8 i/ h5 U O, v* {4 t4 X" u8 v. t" t2 A- z9 Y3 y3 [* y
在此R=(3.3-0.7)/0.02=130Ω。
- z; _$ d. w$ @% H0 y- @
( s8 S% \8 {- O- ~因此本次示例中限流电阻阻值大于130Ω,才不会烧坏LED。
4 R5 k' q& R7 m7 Y$ a7 s t* b$ A9 u) ?8 O
9 s w3 w p* R' J0 t! Y再看代码。 s/ P. l$ X5 @4 Y
. ~0 v3 L) M2 W: c& NGPIO初始化。
! l" u! B$ b* d6 F7 s' @- void LED_Init(void)
k h+ q) I! ]) u' z* ^4 l0 Q - {& L# ?- v6 e6 R M, S8 @) S2 j
- GPIO_InitTypeDef GPIO_InitStructure;" x- |# R2 s" M& R
- 4 N- b5 V1 B. C- p1 _* b! ~7 x
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOE, ENABLE);
7 c0 W) E1 V6 T- t& q - 6 S& W! [& Z; v3 z. c( V- D
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;, X- z* C/ y( H
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;3 a5 X' U- l; ^ j; j, e
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
. Y2 ^5 L2 ~+ ]( l - GPIO_Init(GPIOB, &GPIO_InitStructure);
# W: I, {5 z- @4 Y* L - GPIO_SetBits(GPIOB, GPIO_Pin_5);
% j8 ]+ @2 d. ^0 B6 h - , A( Y2 g9 B' X
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;. E' R1 k' T7 o4 E; ~
- GPIO_Init(GPIOE, &GPIO_InitStructure);
- k, v0 c# a/ p' e- j, y5 \# r8 Y - GPIO_SetBits(GPIOE, GPIO_Pin_5); 2 Q$ D7 a: B, `6 y* I) z/ \" K# i5 b% O
- }
复制代码 5 Q7 v+ F# r5 l5 y3 _3 f! F
LED应用
y2 a) L, U" T9 |" j% [# D# j- void HW_Led0_Off(void)
4 `) m) _' y/ P2 B7 T - {1 b7 v# Z7 f/ S6 R1 m2 M
- GPIO_SetBits(GPIOB, GPIO_Pin_5); //设置GPIO输出1. `' }7 \: i+ P' }+ a+ a/ R% J
- }
2 Q9 Z/ z6 T# v, s& { E& M& T - & U6 }! M3 H" D8 u
- void HW_Led0_on(void): j3 V0 R; A" E9 d9 P
- {
/ R, I2 ~" h- S - GPIO_ResetBits(GPIOB, GPIO_Pin_5); //设置GPIO输出0& v0 ]" P z4 u3 b: j: }
- }4 O: c s$ A: b* C" g6 C
! Q" \! N. f- S* P& S5 G- y$ p- void HW_Led1_Off(void)
1 g& w- ^( N2 ?( }2 W - { J. x6 T: [/ ? Q8 F4 ^
- GPIO_SetBits(GPIOE, GPIO_Pin_5); //设置GPIO输出1
9 T; {) O8 b. p - }
! K: d9 x. S6 M3 q - + O3 H# A2 u1 \" E
- void HW_Led1_on(void)
$ T/ Q! f6 T( P! q" R3 g3 f4 `+ f- L# B - {
! X# X; E) |# n% Q) f% q3 T - GPIO_ResetBits(GPIOE, GPIO_Pin_5); //设置GPIO输出0: {0 |9 P6 \# \+ R5 o9 a* A
- }
复制代码 5 Q6 k3 y* I/ O7 a& H; n2 O6 Y1 N4 q
思考:为什么IO输出低电平时LED亮,而不是输出高电平?
7 k; e- `0 z2 i
* ~& ]1 X" Q9 S T1 W2 ]思路:* G8 B4 p3 B6 j
* r0 Z: b8 u+ {0 O5 S: [0 u
1、由于单片机的I/O口的结构决定了它灌电流能力较强,而拉电流能力比较弱(即IO口的低平驱动能力较强而高电平驱动能力较差);- A1 f( j- \1 H5 h- [7 s
e; z+ [" @, V5 W, p1 S 2、为了简化单片机接口的设计。高电平驱动和低电平驱动是同样的效果,另外,低电平驱动也简化了控制代码,避免了单片机上电复位时端口置高电平后对led的影响;* J9 _+ s5 A% O' ^& h$ a' v
- M5 y1 n7 ?1 w4 K' ?: A从STM32中文参考手册内得知,STM32的GPIO可配置为八种模式。
5 M! U" @# i1 t7 p: j8 |2 X. @% r0 O1 ^5 ?8 n5 V/ d% p( ]
1、浮空输入(Input floating)* M( Z& \$ O6 I: \& W8 j9 I; l2 `+ Q
5 v% x' ~" I8 j+ w, ^) w" M" V 2、上拉输入(Input pull-up)
! }4 f$ i M+ x+ b, {& X) _0 @$ [; t& j. u6 g. }& @5 b* E
3、下拉输入(Input pull-down)
& D4 o. _- C# d, ~+ f; k' l- A* ]
) ^% K% `( f( ? K+ R& X1 N 4、模拟输入(Analog)
/ \- ]9 f0 l# h+ h
' _# q6 ?5 ~, C: Z( W2 Y 5、开漏输出(Output open-drain)
/ g- T. W. z/ p; g* B$ g$ ?2 d; k) T( C g/ w
6、推挽式输出(Output push-pull)" @) I; ~+ {: ?! d5 }4 [7 _
$ E; Q" s$ t8 A, }: F' |$ n 7、推挽式复用功能(Alternate function push-pull)4 O) i# a% H, F2 b
/ b9 V, k) B9 M% t. Y; B! r6 A 8、开漏复用功能(Alternate function open-drain); g! g2 i* D( s. }; O( n
4 y0 w* W4 a, ^0 G: Y. z
- typedef enum
$ I- P4 {4 ~( D; S. ^ - {
$ |+ W) N5 ]) k& L: S - GPIO_Mode_AIN = 0x0,
9 s [( m5 f. ^& F - GPIO_Mode_IN_FLOATING = 0x04,
6 K; b& u9 {! k' I% O - GPIO_Mode_IPD = 0x28,$ @/ T$ A/ r& j, ?) u$ e
- GPIO_Mode_IPU = 0x48,3 I+ |1 }# |0 p9 v7 T
- GPIO_Mode_Out_OD = 0x14,
6 {- J) }- r' D+ P% P. u( | - GPIO_Mode_Out_PP = 0x10,
" N7 H1 M& A2 d% P6 t3 e - GPIO_Mode_AF_OD = 0x1C,
) q0 F( ^# L$ G/ R8 M: X5 S6 D+ G - GPIO_Mode_AF_PP = 0x18; c$ m7 t8 j k! c9 I( N
- }GPIOMode_TypeDef;
复制代码 , n) A# U; B) H% q+ y, C: {
驱动LED使用的是推挽输出。推挽电路(push-pull)就是两个不同极性晶体管间连接的输出电路。
( M& R2 x% ?! v
' o) v4 u8 k! v( k5 f) O8 S
9 H" `0 N$ q2 H6 Q4 a
6 e. Y, y- d2 t& `3 A$ J简单理解推挽和开漏的区别:驱动能力不同。推挽驱动能力强。
$ h$ C1 q% b* P* p7 D1 l! D5 t5 h: u9 B9 _' g- I
复用意思是GPIO不作为普通IO,而是特殊功能使用,比如ADC、USART等。/ R) i! P1 W5 w% x
2 F$ ~4 M# p o" r引脚输出速度有3种:
9 W( h' J9 o& v5 }- typedef enum$ r) x( T5 x3 u" w# A! X3 z
- { ' l2 O; H- b- ]4 b" Q
- GPIO_Speed_10MHz = 1,8 E+ Z6 h- q1 t4 Q9 u2 O( i+ k! U3 N' z
- GPIO_Speed_2MHz,
8 E2 Y+ ^. s, B9 d - GPIO_Speed_50MHz( {/ v6 X w6 w- @/ [( @" I# i
- }GPIOSpeed_TypeDef;
复制代码 0 i# g) l, E9 w$ k" ?1 w6 k. Q
GPIO的速度应该与应用匹配。速度配置越高,噪声越大,功耗越大。
+ }( S$ q2 J/ O) J% @; S
& |0 \. I9 e. d1 G' e3 I$ Z比如配置为串口应用,波特率为115200,此时GPIO的速度配置为2MHz就够了,既省电也噪声小。' j, M4 a+ ]; K2 @& |+ t" F
6 ?' Z# M/ p0 q* C/ a& P- d
I2C接口,400K波特率,若想把余量留大些,可以配置为10MHz。. i, T2 g9 d; R& M9 r( V
! q+ |- z1 p, L; \1 v4 { QSPI接口,18M或9M需要选用50MHz的GPIO速度。
]; K3 q! w2 w0 L2 A- ?* E6 q8 y. X& z( @
1 n& T- c0 g$ Z {
|