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

【初学】中断控制STM32硬件timer

[复制链接]
swang93 发布时间:2015-3-21 21:37
本帖最后由 swang93 于 2015-3-21 21:37 编辑
& m  n1 F; y5 c  Y' \
4 _* P# E6 P2 }6 y* w; o: W0 ^6 t2 |
虽然作为嵌入式方向的大学生,但是并没有接触多少硬件。平时只学习了各种语言,忽略了硬件的学习。之前也只是在Arduino上做了一些开发,自己做了个小东西。前不久决定学习stm32,看了一些资料,算是刚入门,有些地方说的不严谨,大家尽管提出来,一起学习。想和大家分享的就是如何控制stm32的硬件Timer,大神轻喷。
, G3 f6 d% n/ x( T( L
1、  硬件timer是一个独立的计时器,在计时时以一个给定的速度在0和最大值之间计时,同时产生各种事件。它独立于你的C/C++程序,而且它的值遵循下面的规律:
01-timer.png
& ~! ^. w! ]0 d) W3 f" y3 n
2、我通过一个简单的程序高明白了这个timer。我设置timer的周期为500,当timer的值达到400时让LED灯亮,当值达到500时再关闭LED。Timer每次达到500时就会自动重置为0。
02-timer.png
1 R7 R' k8 y- G# ~
3、注意,在我们对timer进行任何动作的之前,需通过调用RCC_APB1PeriphClockCmd()来启动它:
  1. RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);# m0 }) u5 a3 K" y6 `( ^) G
  2. - N) ?: j* n; ]1 j
  3. TIM_TimeBaseInitTypeDef timerInitStructure; / S+ Z7 E" @8 s. ~9 Q& c
  4. timerInitStructure.TIM_Prescaler = 40000;, h$ Y! o5 T+ E1 K
  5. timerInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
    ' [: y; Z8 ?' p( s9 r! u
  6. timerInitStructure.TIM_Period = 500;
    7 i- T3 l  A. m) z5 t
  7. timerInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    ) D/ g9 U$ G5 ]& r, m. [
  8. timerInitStructure.TIM_RepetitionCounter = 0;, N2 ^; X5 f; O7 _' d
  9. TIM_TimeBaseInit(TIM2, &timerInitStructure);
    . {; p) K3 `3 `! K2 B& _# o1 y# F" N
  10. TIM_Cmd(TIM2, ENABLE);
    2 \0 u* Z. F& u3 f7 r( [
复制代码
8 Z9 p2 E. k# {
4、在main函数里,我们只简单地检查timer的值,并通过这个值来控制LED。第一版的完整程序如下,下面还会修改代码。
& V: M6 V- p- h0 z
  1. #include <stm32f4xx_gpio.h>0 G" Y! x% j1 H6 L
  2. #include <stm32f4xx_tim.h>: O3 K  P8 l5 y
  3. #include <stm32f4xx_rcc.h>  T, Z2 ]  V6 A; o! d! ]9 O$ o( F: x/ Y
  4. #include <misc.h>7 s/ I+ J# F+ \, W
  5. ' W, i0 c6 Z8 k5 I7 k! b. k1 _
  6. void InitializeLEDs()
    3 o/ n% c3 J6 q9 ?1 k( s
  7. {* ~( \) I' V/ J; D3 i$ N
  8.     RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
    3 D/ W! L% Z* S4 o1 F; P
  9. $ J. v+ ]6 D, [5 Y  s
  10.     GPIO_InitTypeDef gpioStructure;' H* m4 V/ ^9 k7 P& p
  11.     gpioStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13;3 z5 P/ J) r$ m. f
  12.     gpioStructure.GPIO_Mode = GPIO_Mode_OUT;8 l! p% b8 ?2 d/ q$ B
  13.     gpioStructure.GPIO_Speed = GPIO_Speed_50MHz;3 N# k( Y/ A7 v& G' g
  14.     GPIO_Init(GPIOD, &gpioStructure);
    : U; @. q- {# w% o$ W3 M

  15. ' H. M( ]! }; t7 Q" t- g
  16.     GPIO_WriteBit(GPIOD, GPIO_Pin_12 | GPIO_Pin_13, Bit_RESET);
    & c; `# x5 [1 y9 W
  17. }+ H8 G3 K* l% [
  18. & e# @& p! X) o: }/ O5 ?
  19. void InitializeTimer()9 \4 _" A" c! ~8 _$ D
  20. {# `1 t& r: _- I/ K8 E% w, o2 @
  21.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);7 c7 [/ d: a% s" \3 S

  22. . k' A$ R5 g7 m/ p: K7 N* g
  23.     TIM_TimeBaseInitTypeDef timerInitStructure; ) \1 g5 o! c9 y3 d3 L
  24.     timerInitStructure.TIM_Prescaler = 40000;) T9 f' @2 h4 a; d$ ^$ B6 [
  25.     timerInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
    , U2 _8 u& k: `, a* O
  26.     timerInitStructure.TIM_Period = 500;
    / [  r1 w+ X* T- y/ H
  27.     timerInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    ! z: K; u0 |4 S; \* o! s6 C7 j
  28.     timerInitStructure.TIM_RepetitionCounter = 0;
    2 u- r7 d  ?" M
  29.     TIM_TimeBaseInit(TIM2, &timerInitStructure);5 w" @$ a, t( t" Y
  30.     TIM_Cmd(TIM2, ENABLE);0 c6 t6 T9 ^5 `
  31. }0 O* E5 D: ]# E2 S& d' h" u

  32. ' W+ b& _* N) [
  33. int main()
    2 J1 N4 \" C% ]+ m$ N9 b
  34. {, n% n& e% F2 i: n9 q
  35.     InitializeLEDs();
    2 Q- R9 w2 [5 V" ^
  36.     InitializeTimer();, Y( q: q( r) u
  37. # [- P2 G, M3 x  ]% T8 {! i; K
  38.     for (;;)6 S4 J+ v4 W2 K0 x2 E, D1 ?2 R$ G
  39.     {: c! F( X" ]% [' C) P$ d3 M3 z
  40.         int timerValue = TIM_GetCounter(TIM2);- r. Q$ c1 D  F; t7 k
  41.         if (timerValue == 400)
    ' j$ Y5 v, |9 B/ r6 O
  42.             GPIO_WriteBit(GPIOD, GPIO_Pin_12, Bit_SET);/ y+ U: p- x2 w7 S* F  @! U
  43.         else if (timerValue == 500)
    * J7 O. `4 h" L
  44.             GPIO_WriteBit(GPIOD, GPIO_Pin_12, Bit_RESET);. f' b1 p0 i" i0 l
  45.     }: s. n, j. c* L$ t4 N+ i# Q& `2 ~
  46. }
      Y4 {2 M. i3 [# M- Q. e5 F8 z
复制代码
: I, p/ \. Z# x5 a
5、如果像下面那样设置一个断点,我们就可以观察到,每次执行到这里,timerValue的值都会在0-500之间。这就和我们设置的TIM_Period是符合的。
03-timer.png

/ |# a" N5 y9 {* W' e) m
6、  每次STM32的timers达到周期值是,就可以自动的产生”update”事件。在TIM_Cmd(TIM2,ENABLE)下添加如下代码:
  1. TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
复制代码
这就可以产生update事件。这里需要注意一下,由于我们没有设置中断控制器,所以这个中断不会影响代码的执行,我们需要手动的检查。

/ o# L3 {* g. b  _- U1 m1 @
7、 如果用如下代码替换main函数的代码:
  1. InitializeLEDs();
    - @) U, [' k; Q. m
  2. InitializeTimer();0 F; H/ R3 s# u+ c
  3. for (;;); R% ^. M8 b- P+ b+ M( u/ \! X
  4. {8 j. S: Y7 d* }, P9 K! [
  5.     if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
      V" S  t1 Q8 b) F5 L
  6.     {
    " g2 c: L+ }& P2 r
  7.         TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
    ( C% x. T- A4 K  N
  8.         GPIO_ToggleBits(GPIOD, GPIO_Pin_12);
    , V2 j0 e/ j/ }3 d! R8 a
  9.     }$ H) c; f1 _" _+ W  e
  10. }, S# q0 i7 s' E) D( p# m2 j$ [
复制代码
运行代码,我们就可以发现LED的闪烁模式已经改变了。
4 D6 w- S3 {% R  @) P! t

* W! g6 Q. W* L: h* X/ X8、硬件timer真正强大的地方是,他们可以向CPU发送中断,这样就可以不用手动检查这些状态。在代码里添加下面这个函数,然后在main函数里在调用IntializeTimer()之后再调用它。
- h+ x9 j$ |8 k
  1. void EnableTimerInterrupt()) |' c2 `- N8 O, c2 l( \* G+ }
  2. {2 \" F# V; I  d9 q1 c, c
  3.     NVIC_InitTypeDef nvicStructure;
    $ R6 i# P' w! n7 j
  4.     nvicStructure.NVIC_IRQChannel = TIM2_IRQn;
    / V5 ]! j  D. T
  5.     nvicStructure.NVIC_IRQChannelPreemptionPriority = 0;8 m" ]  y6 ^) a5 L( X! d
  6.     nvicStructure.NVIC_IRQChannelSubPriority = 1;% e7 M: l- b8 w+ J1 U6 ]0 a
  7.     nvicStructure.NVIC_IRQChannelCmd = ENABLE;
    * u0 U! M, h" S# ?
  8.     NVIC_Init(&nvicStructure);* u! I4 U+ _; d% n  {1 @
  9. }3 L6 C, h5 A8 {5 L0 z* K
复制代码
1 B6 H) d7 E: X* T2 `) d4 A; O
9、 最后去掉main函数里for循环里的代码,然后把这些代码添加到TIM_IRQHandler()函数里:
5 D4 j/ u+ ^' f' e. V: I3 F/ ~
  1. extern "C" void TIM2_IRQHandler()
    ! q/ g/ m' [. P& d/ {  G
  2. {, ?7 Q5 I- M2 T) ]! G) A. R
  3.     if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)1 I/ b; y7 R! d, h  q9 I
  4.     {" c; k2 ]# f! u& i+ k. a1 C' \6 ]' w
  5.         TIM_ClearITPendingBit(TIM2, TIM_IT_Update);' o# Q4 q# ^5 Y) e( _. V1 z$ b
  6.         GPIO_ToggleBits(GPIOD, GPIO_Pin_12);
    2 C5 g9 }* I! c& K9 O/ y( {' l0 B) J
  7.     }! b4 ?9 H' H& v/ w, A0 V
  8. }) l& [3 r+ b7 `* {' f" h* H
复制代码
注意,如果你创建的是C++文件,就需要extern “C”。7 K! U7 C7 f! L1 d6 k4 q. s3 ]

3 p; ^% M9 k& Q0 F" E5 s4 W
1 Y) {$ q# P0 m
10、把代码跑起,观察LED闪烁的情况。设置断点,观察寄存器的值:" w4 s. n) [) j* m: c
04-timer.png
/ G' n" I. S  u9 L7 q
$ t+ i" }1 y' F11、注意,链接寄存器(lr)通常包含函数的返回地址0xfffffff9,这是个特别的值,和ARMCortex CPU文档里说的EXC_RETURN一样,表明当前函数是被CPU自动调用用于处理中断的。5 d: Y/ f! A9 B$ m9 |/ W& I8 S

4 u" p/ f# Q. P& P) A! r$ `$ B
+ h$ @& c+ X& P" ?
12、 用下面的代码代替main函数里for循环的代码:
  1. for (;;); X4 I) |1 g1 z1 X; W( Y
  2.     {, i9 ]# K- v! F1 a9 x) V
  3.         for (int i = 0; i < 1000000; i++)
    , X0 h4 W8 t* O, t+ B9 B3 M
  4.             asm("nop");
    : {" \; ]: U. l2 d/ v
  5.         GPIO_ToggleBits(GPIOD, GPIO_Pin_13);
    ' Z" N+ R1 G  \/ N  s/ f) M
  6.     }
复制代码

9 O$ f5 P6 t$ {1 d7 Y$ t
1 m- k5 B1 g. x' N2 Y8 N13、跑起代码,我们就可以看到LED灯以不同的频率闪烁,互不影响。
0 G/ u/ [  q5 v: j
% i" C6 d) s9 j最后把代码贴出来,一起学习! demo.zip (758 Bytes, 下载次数: 12)
赞 收藏 1 评论38 发布时间:2015-3-21 21:37

举报

38个回答
swang93 回答时间:2015-3-24 00:09:23
有几个小伙伴对VS2013创建Embedded Project比较感兴趣,其实不复杂,只是大家平时都用Keil创建工程,用VS的人少而已。今天就趁晚上的时间大致介绍介绍,有说的不严谨的地方,大家指出来就行了,多多交流。8 v9 R% b( H- s. ?  r$ K
* V0 M' J* P$ j: x2 o1 I2 G6 P
用VS创建Keil下的工程,VS本身是不支持创建的。这里要用到一款工具VisualGDB,安装VisualGDB也不复杂,为了方便有兴趣的小伙伴尝试一下,我上传了安装包。多说一句,压缩包里有个“Crack”文件夹,把里面的文件代替安装目录下的文件,就成功破解VisualGDB了。
/ V) b$ j* U; U  l& I- _5 z( ]- u% W
安装的图就不贴了,贴几张图创建工程的图,供大家参考一下。创建过程不需要做什么修改,也就不一一解释了,大家动手做了印象会更深刻,下面贴图:
6 m2 C2 P4 z9 ~' M
" t) D  R& Q4 _4 c) E+ S8 Y; A" S# a$ ?& r
01-testGDB.png 9 ?3 H  h  K- S% c5 m+ N" S
0 a0 c. z# |( q# a
02-testGDB.png - ]* k, v9 }: R. W. S, }7 _# L' F

$ A& ]. ~5 k# h4 l: V5 D/ U0 f在这步的时候需要先选择一个toolchain,第一次需要下载安装。6 K  ?/ ~3 |8 M/ y$ S5 H3 d7 p
03-testGDB.png ! K0 J; A' g! i+ M8 `4 }
7 T+ u, O) c, C# G7 D& @5 u
04-testGDB.png ( o0 R9 |8 M3 @, ]: q" T5 N) ?
& o" \1 m- i4 u0 X
05-testGDB.png " B( \5 s2 c3 y; I
, m; L- b5 U( X3 \. ~
06-testGDB.png
, \! d8 w' L, \5 P* H  w, v
* ^7 K9 k( e$ ?1 E工程创建好之后,在本地调试,就会生成elf文件,也就出现了最开始的画面。
: i2 c5 Y4 I2 u9 E$ r+ C6 D8 v' O& F, {& s- |9 E! c
这个是VisualGDB的安装包,手痒的小伙伴下载下来试试吧。 VisualGDB-4.2r4.rar (4.85 MB, 下载次数: 0)
党国特派员 回答时间:2015-3-24 10:43:13
swang93 发表于 2015-3-24 00:10
0 f& E8 r2 r0 c3 s2 j5 U下面介绍了一下,感兴趣看一下吧,多多交流

5 E- D$ T, K1 D  Q受益非浅。 blank.png blank.png blank.png blank.png blank.png blank.png blank.png blank.png blank.png blank.png
swang93 回答时间:2015-3-22 12:03:44
_ilikerome_ 发表于 2015-3-22 10:01' u6 m6 j9 l4 i0 N5 }
感觉LZ写的挺好的,我这几天在搞高级定时器,驱动电机。% s# ~) L9 [( b8 |; U$ K

  p" A. Y1 E$ e感觉你的这个教程写的不错

/ G+ e' E; j9 F5 [" v谢谢!细看一下跑的其实是elf,用keil当然是更方便了
kqh1120 回答时间:2015-3-22 00:38:03
学习了 3.gif
nocoyou 回答时间:2015-3-22 03:07:07
vs2015?
_ilikerome_ 回答时间:2015-3-22 10:01:28
感觉LZ写的挺好的,我这几天在搞高级定时器,驱动电机。
, {2 _" {9 M$ C  x" H
( D, y. D& N4 s# J2 b/ a0 D感觉你的这个教程写的不错) U& `) g6 ]' o0 e2 j

" u$ h9 m% K2 x% B: c- z9 M2 j还有你用VS编辑??可以吗?
swang93 回答时间:2015-3-22 12:06:19
nocoyou 发表于 2015-3-22 03:070 m' |$ J, L7 S: D' c, c
vs2015?
8 [# {. W! ]& H" a3 g
用的13
wamcncn 回答时间:2015-3-22 12:29:22
nocoyou 发表于 2015-3-22 03:071 R. u% C) |3 }, B/ g/ x: i
vs2015?
4 v! ?# ~- ]- i  ^! s. }
好像是mbed
coloree 回答时间:2015-3-22 15:05:47
支持一下
foxglove 回答时间:2015-3-22 16:32:47
教程写的不错
拼命三郎 回答时间:2015-3-22 19:19:50
xxxxxxxxxx.jpg
swang93 回答时间:2015-3-22 22:40:56
zfz0122 发表于 2015-3-22 00:38
3 I4 v7 i) d! {3 [  {1 J学习了
# I9 ^  N& |) D: ]
谢谢
swang93 回答时间:2015-3-22 22:41:23
coloree 发表于 2015-3-22 15:05
) j6 U1 E& q5 C0 Q0 F% f/ c支持一下

% Z$ h) X- {0 ^. h$ n谢谢支持了
swang93 回答时间:2015-3-22 22:41:54
foxglove 发表于 2015-3-22 16:32, O! v; v  p. O* I8 b0 T/ V2 j+ f$ _( A" ?
教程写的不错
4 U$ b  |3 W0 g5 W3 w
谢谢支持,共同学习
swang93 回答时间:2015-3-22 22:42:22

8 U5 u/ o: ?% k# R( e3 e谢谢谢谢
埃斯提爱慕 回答时间:2015-3-22 22:47:04
提示: 作者被禁止或删除 内容自动屏蔽
123下一页

所属标签

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