: F. N, Y2 I' d2 @# c+ f
STM32学习笔记1——点亮第一盏LED灯 本文不特殊说明都以stc32F103C8为例,软件平台为KEIL5V5.15 9 @/ w3 ~7 Q3 h" ^6 h! {2 c( x
首先这次我们用寄存器直接点亮LED灯 - v5 \( G3 K% K' c6 }- D+ x
LED电路图如下图: N+ {$ I. i; W" ^
* ]% Y) l# S) v$ T& g! F `
可以看出如果需要点亮一盏LED灯只需要IO口输出高电平即可,本次以GPIOB为例子。
! L* q7 B8 v" U5 R! k- M
6 f& W1 o8 Z. a) }$ a( E5 X/ d5 m3 s( W1 k, u4 ]/ P* k! g3 K4 a1 z
点亮LED灯% S$ U3 S6 S5 u- T# a
- N/ W: \% C& b3 K ]
5 c- b2 [% F" Z# \- G控制IO输出高电平通常需要三步:
9 k0 I' w4 ?* _# y" f1.开启时钟:因为STM32为了节约消耗,故上电时候外设时钟默认不开启。故我们需要开始GPIO的时钟。
8 u5 ]3 q1 J+ m6 e0 X8 t' @ @2.设置GPIO输出模式:stm32F103提供了4种输出模式和3种输出速度: + M/ z0 R" @% l+ r! Y
- 通用输出推挽 4 P7 h5 k* L; [6 o. u/ I
- 通用输出开漏
! ~9 f' W G6 Y; s9 i8 w- 复用输出推挽:串口输出
; o: H- n/ p1 Z- 复用输出开漏:IIC
( f0 E* w& {& p8 r4 E
& K# R8 }2 V5 L6 `0 d& _2 j. @% e0 c7 _* b( y; ^) v
推挽输出电路如下图:9 J; {8 o* P T1 A
h) N5 {8 g( K) ?9 Y: N" q
两个三极管都是半个周期导通,轮流导通。从而减低了功耗,提高了每个管的承受能力。又由于不论走哪一路,管子导通电阻都很小,使RC常数很小,转变速度很快。因此,推拉式输出级既提高电路的负载能力,又提高开关速度。所以推挽输出可以输出高低电平,但输出幅度取决于输入,不可控。% ]5 K" y% M4 n
- @# `5 v' a5 ^8 B" k S) G' P5 R j: o: ^5 d9 V0 J
开漏输出电路如下图:
% R) d7 O# n& r# q& i0 F7 V
( q/ }6 { D6 i2 f, R# V从图中可以看出,当没有上拉电平和上拉电阻,当IN输出低电平时候,OUT输出低电平,当IN输出高电平时,OUT处于开漏状态,电平状态未知,当加上拉电阻时候,则上拉电平跟上拉电阻有关。
( r$ h: Z7 D u% k' W& [& h开漏输出相当于三极管集电极,故它只能输出低电平,需要接上拉电阻才能得到高电平。开漏是用来连接不同电平的器件。很好的一个优点是通过改变上拉电源的电压,便可以改变传输电平。比如加上上拉电阻就可以提供TTL/CMOS电平输出等。(上拉电阻的阻值决定了逻辑电平转换的沿的速度 。阻值越大,速度越低功耗越小,所以负载电阻的选择要兼顾功耗和速度。)
1 B7 i$ D8 Y# [而且可以将多个开漏输出的管脚,连接到一条线上。通过一只上拉电阻,在不增加任何器件的情况下,形成“与逻辑”关系。这也是I2C,SMBus等总线判断总线占用状态的原理。3 ^5 G/ b9 U5 p4 J! l( N
3.输出高电平
5 w- {+ R) L# j: P. _ F- ?
) X5 j0 N F% w; W8 ~2 x. n
& O( r6 y7 D9 q1 Z& d$ }) m寄存器设置8 r8 _4 v) J1 V5 W" C" L/ y2 z- e
; ~* _1 O/ k1 s9 w" l
1.设置时钟寄存器 9 j u$ _9 M- E( A6 C
首先GPIO是挂载在APB2总线上的,APB2时钟使能寄存器为RCC_APB2ENR,如下图
9 _' m; {9 |! t% q1 t
6 ~5 ~" N4 k7 f1 G
故只需开启GPIOB的时钟,即将RCC_APB2ENR第3位置1
* E- h& A, G/ ^' k- J3 a2 l' q; Y' U: m1 ?' x) S3 Q' f
( Y" q) e; F0 ]/ l6 J& z2 z
2.设置IO口输出状态
- i) H' v6 M% K5 @( o: H7 Y有关寄存器为GPIOB_CRL和GPIOB_CRH,如下图:( A% }" `0 I' v! Z$ i* I/ A
- y) h& N- W# t+ ?5 h& F0 O可以看出只需要设置MODEx[1:0]即可,我们可将他置01,设置成中速即可,则默认为推挽输出
. q3 j/ h2 Q2 n$ Z8 u5 C6 {% j
; m% M' x- }+ N
s& U4 t8 C1 ^8 ?+ q4 @3.设置IO口输出电平
- w% |9 {5 [. ?& W+ }5 lIO电平有3个寄存器有关,分别是GPIOB_ODR,GOIOB_BSRR,和GPIOB_BRR;其中GOIOB_BSRR为置位/复位寄存器,GPIOB_BRR为复位寄存器,这两个寄存器都是间接控制GPIOB_ODR即端口数据输出寄存器,故为了初学方便,我们直接控制GPIOB_ODR寄存器。寄存器相关内容如下:- @- }" r2 N5 Q1 b% p1 ~* B
: S: \8 `8 R( w% i5 l9 u6 {因为寄存器可以写和读,故可以直接置1即可。
0 I& ]- D: i) L* \# c1 N
5 Y( m2 f4 i8 E9 X
; y9 |7 u$ n+ j3 p- F. m# y& V注意事项:
0 |9 v3 C( a. m' f3 ]9 v1.需要提前设定相关总线和寄存器地址,如下表: 3 f( p' t6 [. |% Q5 J0 m0 N/ h/ ?
& p7 X7 i" S# H2 H2.初学者为了简单,通常都操作GPIOx_ODR寄存器,但实际上我还是推荐使用GPIOx_BSRR寄存器,因为BSRR只有写操作,和ODR要经历读——写操作,时间显然慢很多,而且使用BSRR不会被中断打断。
& [, ?% p. J$ Z1 u
. S2 j. E) |7 e2 Q6 E5 F& m F/ b. b- g" V
以下附代码(灯接GPIOB_8~15)2 M' Q/ G1 F) X& g6 g9 [. |7 x- m
- /*定义相关寄存器地址*/
7 a# R& Y: X+ j4 S; I4 r- x - #define GPIOB_ODR *(unsigned int *)(0x40010C00+0x0c)
7 d3 u/ d6 {' h3 k4 ^& D* b8 [: Q - #define GPIOB_CRH *(unsigned int *)(0x40010C00+0x04)
# u3 q1 }7 n" e - #define RCC_APB2ENR *(unsigned int *)(0x40021000+0x18)
; D0 T: {. f, L - /*延时函数*/
) ~& _% C: b' S& o% Z2 t - void delay(){
/ R" t$ U9 u, I: R4 z! |0 } - unsigned long n = 30000,j;: ^' n/ ^6 F; C1 D
- while(n--){
! W4 R; V8 M5 a3 E( Y! N - j = 12;
. O, I# q% e+ j0 D1 c - while(j--);: z8 M) Y/ \, u* y
- }
. |& a. ^ z1 G9 Q/ L - }# C. ?) i6 _( D8 ]0 ^% x+ L1 i/ {
- int main(){# g$ X% _9 z$ M. A7 {4 f
- unsigned char i;
- c" d, c( D/ n- d - : K' K. w. c z3 j! o
- /*开启时钟*/2 T$ A& ?/ `9 d% P0 \
- RCC_APB2ENR |= 1<<3;
5 j* D. O' k1 \6 i
1 q* P8 u9 `( P5 c5 G& b* W' K) A8 s- /*配置PB8到PB15推挽输出,中速模式*/
3 b+ c; l- u" H4 x, n - GPIOB_CRH |= 0x33333333;
( r9 p; c7 q3 [8 W" C, O9 T# I } - 1 o- d% |" p+ [- F' ]# @3 }
- while(1){- ~: v9 h, Y6 I
- /*闪烁两次*/
/ o4 M0 C, E8 f. h7 k' Q3 Y - GPIOB_ODR |= 0XFF00;
, x5 i" s3 }0 n8 q8 _8 s, W4 e - delay();
: `: c; v9 M/ J - GPIOB_ODR &= ~0XFF00;' X% \7 @: s; q4 N+ }
- delay();. Y$ G4 t, o' k2 p4 s0 S5 p
- ' u; `% `" Z Q3 n
- GPIOB_ODR |= 0XFF00;
+ c7 ^! N4 }& G - delay();
; e. R! c3 |: u" x( { - GPIOB_ODR &= ~0XFF00;
; b1 g; o* ^& \$ J5 N - delay();
3 S0 \* x1 I: ~9 Z! {! h - GPIOB_ODR = 0x0100;! S, p: I) W* c1 Q
- /*流水灯*/
6 z, z u: r" X5 i' M8 z- T/ h p - for(i = 0;i<7;i++){
$ B7 Q: }( K8 m8 M) x4 ~$ N2 ^. X5 S - GPIOB_ODR<<=1;
4 R* p$ q o8 L) C5 ^, J8 D - delay();. K8 C) ?/ [4 }
- }
1 F7 X% w4 t: z: e. i, w - for(i = 0;i<7;i++){
. i( Z( ~6 B0 F% ] - GPIOB_ODR>>=1; ) H/ F+ F3 n) j% v/ A) T
- delay();2 m4 T* G4 g3 ~( q0 m
- }
# M8 S+ b, K6 e8 D. V - . v4 c- w5 U9 f1 d0 o, a5 q
- }
+ _* N' s; C0 J1 o5 V$ ?- a# {
1 K9 |+ k1 y' l! b& k9 B- }
复制代码 " ]+ J7 ?& D* c
: h8 L2 E5 Y/ T, g* k9 ^6 _2 Z. Y
|