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

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

[复制链接]
swang93 发布时间:2015-3-21 21:37
本帖最后由 swang93 于 2015-3-21 21:37 编辑   C( S5 @. n, o, ^( C$ S$ A
! C: y* Y# P, W# ]2 u, s: H( U
虽然作为嵌入式方向的大学生,但是并没有接触多少硬件。平时只学习了各种语言,忽略了硬件的学习。之前也只是在Arduino上做了一些开发,自己做了个小东西。前不久决定学习stm32,看了一些资料,算是刚入门,有些地方说的不严谨,大家尽管提出来,一起学习。想和大家分享的就是如何控制stm32的硬件Timer,大神轻喷。

4 P* X% ?3 r# w; A
1、  硬件timer是一个独立的计时器,在计时时以一个给定的速度在0和最大值之间计时,同时产生各种事件。它独立于你的C/C++程序,而且它的值遵循下面的规律:
01-timer.png
4 ?# K$ G0 {8 N) S- t
2、我通过一个简单的程序高明白了这个timer。我设置timer的周期为500,当timer的值达到400时让LED灯亮,当值达到500时再关闭LED。Timer每次达到500时就会自动重置为0。
02-timer.png

# i: ^2 S' `/ C5 {* M- V
3、注意,在我们对timer进行任何动作的之前,需通过调用RCC_APB1PeriphClockCmd()来启动它:
  1. RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);1 u9 x+ B( i: J6 A

  2. & Z  Q9 x4 }6 V& p) U* O
  3. TIM_TimeBaseInitTypeDef timerInitStructure;
    9 s7 X3 e4 g! p2 ?0 R: @( ~6 f
  4. timerInitStructure.TIM_Prescaler = 40000;
    2 L5 {! [  I, \* A
  5. timerInitStructure.TIM_CounterMode = TIM_CounterMode_Up;/ C3 ^- s: x9 u6 y# ^9 U
  6. timerInitStructure.TIM_Period = 500;
    ; j" b. y+ [6 F) h& ?! j& b7 b0 P% S
  7. timerInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;0 j# g. Q  [* R3 Y; \
  8. timerInitStructure.TIM_RepetitionCounter = 0;
    2 G! P7 w+ ^$ k6 c
  9. TIM_TimeBaseInit(TIM2, &timerInitStructure);
    & ?6 h1 b  ^/ n' w
  10. TIM_Cmd(TIM2, ENABLE);7 C: j3 Q7 |( s' ?5 d* ]  c
复制代码

8 @& ]9 Q/ U* q: o" E
4、在main函数里,我们只简单地检查timer的值,并通过这个值来控制LED。第一版的完整程序如下,下面还会修改代码。

9 [1 l, Z0 u# G6 u7 g6 Q
  1. #include <stm32f4xx_gpio.h>; v( Z$ S" {0 l; d
  2. #include <stm32f4xx_tim.h>
    1 m2 y) ]) g& i9 h2 D
  3. #include <stm32f4xx_rcc.h>5 O4 ]' O, q4 U) P) Y9 O, f
  4. #include <misc.h>3 z' ^  {; _* r
  5. . G% i, g, ], V$ U; K& l7 I
  6. void InitializeLEDs()- q; J$ Q6 I8 P; X$ E$ r
  7. {
    0 \9 ?. z) h; K/ f: B
  8.     RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
    % j$ C7 \) u* l, v. S# [

  9. # @, e1 O: s5 Z& [# i, E
  10.     GPIO_InitTypeDef gpioStructure;
    ! W0 l3 j, {* p8 ]1 q4 T) u/ o4 B
  11.     gpioStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13;) A6 ^) g0 B  \" q4 O* r& Q- @
  12.     gpioStructure.GPIO_Mode = GPIO_Mode_OUT;
    + ~. H  [) \( ?7 Z
  13.     gpioStructure.GPIO_Speed = GPIO_Speed_50MHz;
    ) O0 L) {) p' }& t
  14.     GPIO_Init(GPIOD, &gpioStructure);
    ; I/ d- v$ K0 g$ i! d& {3 N6 ~

  15. + Q+ i& A* [: s( u( N$ m
  16.     GPIO_WriteBit(GPIOD, GPIO_Pin_12 | GPIO_Pin_13, Bit_RESET);
    / p4 J( J. _; H' }0 q0 Z/ a
  17. }
    2 c) U" A# E0 U# B6 s: y. a1 w

  18. ! U' z$ _; t6 A' |1 T3 U* H
  19. void InitializeTimer()
    6 H7 {! A2 e6 N- i
  20. {% h3 G7 F4 g2 ^! ~: Z$ S
  21.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);" v. q2 X- E' C5 {) C
  22. ; }+ V7 i* b2 e/ G7 h, g' q
  23.     TIM_TimeBaseInitTypeDef timerInitStructure; / T. C2 J* O* Q9 x
  24.     timerInitStructure.TIM_Prescaler = 40000;% }. q+ d/ [7 ~# T# m
  25.     timerInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
    2 a9 l; ^* G; W' z: P; s
  26.     timerInitStructure.TIM_Period = 500;
    % h- z2 E/ \: ^4 g! g$ P# g. V) R
  27.     timerInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    , n0 q: k/ z* ^; n
  28.     timerInitStructure.TIM_RepetitionCounter = 0;( h  H5 N8 x5 U6 e2 i$ `
  29.     TIM_TimeBaseInit(TIM2, &timerInitStructure);
    & C% W: `( E# B) O2 H4 ]$ n9 x' d, A
  30.     TIM_Cmd(TIM2, ENABLE);8 A- u$ }$ t/ K+ o2 k* N
  31. }
    5 g& o1 x3 U# j" p4 M% U
  32. " A" a5 {$ Q( P! m  e" a1 [
  33. int main()
    4 |" G3 Y. X6 ?- C( e
  34. {0 |- B6 {! ~9 Q
  35.     InitializeLEDs();+ O6 \( A4 ?7 l" z$ h
  36.     InitializeTimer();
    ( K. c7 F! o5 H* x
  37. # l3 u4 ~6 @- r6 B% y- z
  38.     for (;;)% k& A+ c  ]( t- t5 ^. |
  39.     {
    2 D# ?& }( j$ @4 W$ [7 b
  40.         int timerValue = TIM_GetCounter(TIM2);/ G1 M) [3 H2 R8 b+ W& \2 c
  41.         if (timerValue == 400)% ^. v) h: M0 c* F5 K; X
  42.             GPIO_WriteBit(GPIOD, GPIO_Pin_12, Bit_SET);3 \8 S) p' I( P
  43.         else if (timerValue == 500)
    % `/ {+ Z$ g5 F+ D) [0 U: d: M
  44.             GPIO_WriteBit(GPIOD, GPIO_Pin_12, Bit_RESET);
    / x0 @& a) g6 \5 P# l
  45.     }" `! Z  C8 N) K* x' j
  46. }/ U8 m; d) t0 ~
复制代码
! ^( J2 G% u" Z$ z# c* {- C" [2 u
5、如果像下面那样设置一个断点,我们就可以观察到,每次执行到这里,timerValue的值都会在0-500之间。这就和我们设置的TIM_Period是符合的。
03-timer.png
6 v  M- q9 c/ J" T/ x
6、  每次STM32的timers达到周期值是,就可以自动的产生”update”事件。在TIM_Cmd(TIM2,ENABLE)下添加如下代码:
  1. TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
复制代码
这就可以产生update事件。这里需要注意一下,由于我们没有设置中断控制器,所以这个中断不会影响代码的执行,我们需要手动的检查。
' Q0 ^4 t( q4 G
7、 如果用如下代码替换main函数的代码:
  1. InitializeLEDs();0 h9 t. f+ ?  E& X0 j; ]1 I) M; ]
  2. InitializeTimer();8 g2 w/ W: W% [9 o" J, M
  3. for (;;): B4 H2 S" g. Q1 P
  4. {
    5 o& p8 W3 Z0 ?+ C: i
  5.     if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
    - M: K/ s2 B7 L. W; {
  6.     {
    . v9 ~' U! F! Y9 E# Z
  7.         TIM_ClearITPendingBit(TIM2, TIM_IT_Update);, E# D+ [, G7 N) u0 c$ P
  8.         GPIO_ToggleBits(GPIOD, GPIO_Pin_12);0 l5 d# V  p% c1 w" r0 N& B
  9.     }4 A- n/ ^& R8 v4 f' ~# a9 M
  10. }
    8 D5 U. J# v8 o% i! j" |2 G
复制代码
运行代码,我们就可以发现LED的闪烁模式已经改变了。2 t( j' I5 I9 M& @- P4 x' N: c4 G

9 i/ X( H' N2 u1 E1 e8、硬件timer真正强大的地方是,他们可以向CPU发送中断,这样就可以不用手动检查这些状态。在代码里添加下面这个函数,然后在main函数里在调用IntializeTimer()之后再调用它。( ~4 c( `6 w3 ^; H6 d- N
  1. void EnableTimerInterrupt()
    . G$ [8 M' @1 z3 @, [& J
  2. {- y* a  I  X( g# J, \  I; ?
  3.     NVIC_InitTypeDef nvicStructure;
    - q. Z3 N7 ^3 i
  4.     nvicStructure.NVIC_IRQChannel = TIM2_IRQn;
    8 i8 M" D+ D  ]$ P! O+ S
  5.     nvicStructure.NVIC_IRQChannelPreemptionPriority = 0;3 I- k: m% Y4 q6 H. e" R/ Q, r
  6.     nvicStructure.NVIC_IRQChannelSubPriority = 1;2 r4 ?9 G8 Y" N% y
  7.     nvicStructure.NVIC_IRQChannelCmd = ENABLE;9 [; Q% v9 L: S& U. `; u
  8.     NVIC_Init(&nvicStructure);* S# {6 }8 d1 j3 e3 [" L7 w: ~" c2 E
  9. }0 z, F1 P) B6 _5 G
复制代码
# x! E' |$ i  g4 Z
9、 最后去掉main函数里for循环里的代码,然后把这些代码添加到TIM_IRQHandler()函数里:0 R) v  ]/ C: s: W6 s
  1. extern "C" void TIM2_IRQHandler()
    2 d2 v/ _" `" `: N' `+ f) K1 Q
  2. {
    2 Z. Q% ~- \+ W. k
  3.     if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
    ) ^% {  \- z2 s; b$ l! A
  4.     {% }- b) O( p' |) {' j( D7 I
  5.         TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
    : [) ^+ Z9 Y6 ^- g& [9 D  ?2 T
  6.         GPIO_ToggleBits(GPIOD, GPIO_Pin_12);
    " c0 \4 W. y, t$ M9 Z: M
  7.     }
    , q1 Q, |2 j# e6 T
  8. }
    & M, Q. h4 @6 a6 q$ F( M1 y- V
复制代码
注意,如果你创建的是C++文件,就需要extern “C”。6 g5 D6 ]6 j0 I/ `/ K8 `

- l- i0 [+ B9 V3 T) [" R2 r
9 I$ ]% S3 x4 C
10、把代码跑起,观察LED闪烁的情况。设置断点,观察寄存器的值:
0 L' f7 a7 ^! s. H 04-timer.png
  O. X% c4 _9 _% P; u8 |  g  N: q2 p& ^( C. L% f& p. a
11、注意,链接寄存器(lr)通常包含函数的返回地址0xfffffff9,这是个特别的值,和ARMCortex CPU文档里说的EXC_RETURN一样,表明当前函数是被CPU自动调用用于处理中断的。2 l$ D8 w  ?1 C5 k! ?

1 e6 _1 g8 n  w: l9 y3 |7 L- w  C
% p0 s3 u4 Z# f; I) G* O
12、 用下面的代码代替main函数里for循环的代码:
  1. for (;;)
    $ J8 l7 M% p7 K1 E
  2.     {/ }& S* [8 J* \
  3.         for (int i = 0; i < 1000000; i++): J5 R. T1 h2 W- h; Z$ {6 \
  4.             asm("nop");" `9 F3 D* S' ?7 J' o7 h
  5.         GPIO_ToggleBits(GPIOD, GPIO_Pin_13);+ H3 s% {- F0 P+ _1 O
  6.     }
复制代码
# K: H  n- g7 Y' Y0 A6 S2 D; P0 w) k
0 h' f; y& D2 M/ e: o# q3 R7 ]
13、跑起代码,我们就可以看到LED灯以不同的频率闪烁,互不影响。7 h$ a7 X  N- L& u2 T$ f

; l) z. c+ s  c: W% o最后把代码贴出来,一起学习! 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的人少而已。今天就趁晚上的时间大致介绍介绍,有说的不严谨的地方,大家指出来就行了,多多交流。
% C  g! }% `: t* I7 M$ x( ?- F# H
用VS创建Keil下的工程,VS本身是不支持创建的。这里要用到一款工具VisualGDB,安装VisualGDB也不复杂,为了方便有兴趣的小伙伴尝试一下,我上传了安装包。多说一句,压缩包里有个“Crack”文件夹,把里面的文件代替安装目录下的文件,就成功破解VisualGDB了。
- e! u2 f1 J7 S: e2 M. A3 Y/ X" L5 A
4 m1 x0 F' ^+ V( k) z& l. {2 v安装的图就不贴了,贴几张图创建工程的图,供大家参考一下。创建过程不需要做什么修改,也就不一一解释了,大家动手做了印象会更深刻,下面贴图:: _+ v2 p  z, R/ a+ p; h% O3 J" f

6 u! }" O6 y' P7 v5 r" G  e2 t
. B  S( w' Q. `/ n- E& t+ @5 a8 G 01-testGDB.png
2 J" h5 V) W: A" R+ L
9 p) E; |, t, {4 Q, ?* u 02-testGDB.png
: f% s: n8 }2 l- ~3 `6 o2 @) q6 P0 J& r: U; E, `
在这步的时候需要先选择一个toolchain,第一次需要下载安装。9 x* {: G, ^. A. Y; _) E' d8 q+ V/ T+ Q
03-testGDB.png - g' G1 K, e/ `6 F2 V4 V

% K. p' U1 N7 M  U/ I 04-testGDB.png
0 U8 _7 x- d6 E) x, K: e0 B
' i- \0 [* K6 p* b" n- {8 J 05-testGDB.png ) j0 ?" q) |: k& h; U

7 g7 D5 Z; s  D8 `5 T' b 06-testGDB.png
2 G$ o8 @* Z; W& O7 A2 @
. o! e  B. |9 Z5 k, `( }2 c5 _3 c工程创建好之后,在本地调试,就会生成elf文件,也就出现了最开始的画面。: ?- H" q2 Z8 g% q2 F% ?$ ?
' Q( \8 P4 g6 ^4 ]8 _8 s5 O
这个是VisualGDB的安装包,手痒的小伙伴下载下来试试吧。 VisualGDB-4.2r4.rar (4.85 MB, 下载次数: 0)
党国特派员 回答时间:2015-3-24 10:43:13
swang93 发表于 2015-3-24 00:10
- c$ D' K8 @5 X& V/ `7 J2 [# v下面介绍了一下,感兴趣看一下吧,多多交流
- I/ V- f! |6 M! h4 `' ]; x+ L
受益非浅。 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# ~' d! g2 [% M! p7 }! e6 H
感觉LZ写的挺好的,我这几天在搞高级定时器,驱动电机。
& E0 F) }& p% [) I
: U# {# O- q5 b) x' J感觉你的这个教程写的不错
, ]  U) F0 w& X8 k% |) i' z7 s! _
谢谢!细看一下跑的其实是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写的挺好的,我这几天在搞高级定时器,驱动电机。: }5 P- J, Z- a/ {* H; {2 k
) D! P. {" y1 V5 O9 F) R: F: O0 o8 D
感觉你的这个教程写的不错
0 y: K) a0 K# p5 d8 V1 t2 C$ `' z  ^' U! G9 T3 J" g* f
还有你用VS编辑??可以吗?
swang93 回答时间:2015-3-22 12:06:19
nocoyou 发表于 2015-3-22 03:07/ [& L* p" K3 y: b$ X
vs2015?

7 c, [$ R) S5 s- R8 y用的13
wamcncn 回答时间:2015-3-22 12:29:22
nocoyou 发表于 2015-3-22 03:07. O' U9 R! X- f5 U0 N
vs2015?

, s4 B& ?# L" u4 E) `好像是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
$ p: H- E0 r+ r9 A7 h  L8 v8 g! T) `学习了

/ ~! R- N$ ~1 O& r0 H8 e& p谢谢
swang93 回答时间:2015-3-22 22:41:23
coloree 发表于 2015-3-22 15:05
& G4 ~8 d7 Y  X9 S支持一下

  b7 e& V" V8 s) O; D1 i谢谢支持了
swang93 回答时间:2015-3-22 22:41:54
foxglove 发表于 2015-3-22 16:32
- ?3 _/ B+ t( U& f7 B5 L( c教程写的不错
0 n% e' U+ @6 ~: O& Q$ m. N$ ?
谢谢支持,共同学习
swang93 回答时间:2015-3-22 22:42:22

9 K, K. q, T+ r( [3 T4 C  O9 l2 I谢谢谢谢
埃斯提爱慕 回答时间:2015-3-22 22:47:04
提示: 作者被禁止或删除 内容自动屏蔽
123下一页

所属标签

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