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

STM32之定时器配置

[复制链接]
STMCU小助手 发布时间:2022-12-24 17:40
定时器的配置部分可以分为两个部分:初始化NVIC(中断优先级设置)和初始化定时器,下面我们来分别说明。. R' Q" Z: H/ u6 ~6 Y" Q9 c, u3 s! E
6 _# Q! H' r0 p2 t0 u. a4 d
中断系统NVIC8 P( \; `* A( \
这一部分要明确中断优先级的概念:stm32将优先级分为抢占优先级和响应优先级,相应的在初始化每个中断源时,都需要设定这两个优先级!!!
. ?) i# t# S& u4 K8 l. {( b9 U3 a/ y
抢占优先级:决定中断是否可嵌套,高抢占优先级可嵌套低抢占优先级,如果抢占优先级相同,则中断没有嵌套关系。1 ^$ t8 k# d) I6 [) V
响应优先级:抢占优先级相同且两个中断同时到达的条件下,响应优先级的高低可决定先处理哪一个中断(高者在前);抢占优先级相同但是两个中断不同时到达,则不管响应优先级的高低,都要等先发生的中断处理完才可以接着处理后到来的中断。
7 c8 b, g/ I. Z0 @在优先级分组的表格中,序号越小,优先级越高。% Y2 e0 f+ Z- Y1 q9 I
stm32的优先级分组: 共分为5组 (NVIC_PriorityGroup_0到NVIC_PriorityGroup_4),抢占优先级数从小到大,响应优先级数从大到小,其中:NVIC_PriorityGroup_0无抢占优先级,NVIC_PriorityGroup_4无响应优先级。. H+ k( M5 `0 a
响应优先级无要求的情况下:初始化时将优先级分组设置为NVIC_PriorityGroup_4的方式(只设置抢占优先级),或者不设置优先级分组(系统默认优先级),一般情况下把响应优先级也设置为0。
5 r. p3 u$ [2 u  X, p9 B
初始化NVIC主要是初始化四个部分:中断通道、抢占优先级、响应优先级、通道使能,这四个部分我们也能在相关的库函数(misc.h)中找到定义:4 T8 B+ U; x# f1 t5 f

; @0 m5 o7 [0 X9 {; d+ m7 k
20190418171732130.png
6 E% C" Q# d; _/ U7 G% f6 @

. P  e' t# b( F. `# _; P9 B! a代码部分:" R8 B: i) t' L! p: E
  1. void NVIC_TIM4Enable(void)
    & m. ]* t+ {  u! r
  2. {
    + }8 K) a9 Q: q. U. A& o8 o- a( w& m
  3.         NVIC_InitTypeDef NVIC_InitStructure;
    0 F( s7 i' r# A
  4.        
    ' ^! ]! T9 R+ y$ B. S  k# S- X
  5.         NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;//选择中断通道TIM47 ^* P. R! I" a9 d* {3 U. C' E, g" k
  6.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;//设定抢占优先级为09 r9 |7 P* x+ X' M" ?
  7.     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;//设定响应优先级为0
    1 l4 |& Z0 k! M( f7 }# z
  8.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能中断通道; _( G. K$ r  `
  9.     NVIC_Init(&NVIC_InitStructure);//调用NVIC初始化函数$ o  J# `4 Y: J
  10. }
    ! C- S5 D' T+ Q: ^
复制代码

: B, s) c; F+ d6 d4 r$ `( T  D. D, [
做几点说明:
. ~! x- g( X9 D* ?我们没有用到中断的嵌套,所以把抢占优先级也设置为0;
2 t/ V& S+ v" n, ]& X6 X关于中断通道:stm32使用了Cortex-M3中的84个中断通道,(16个异常,68个中断)具体的相关信息可以在中文手册中查找到。: e; C- f5 |1 d8 ^6 U7 {" ~  B5 D
% U2 q0 K, K% ]9 b7 s
初始化定时器
( b5 ?  p( o" u' rstm32(STM32F103ZET6功能较为全面的一种)内部含有11个定时器:2个高级控制定时器(TIM1和TIM8)、4个通用定时器(TIMX:TIM2、TIM3、TIM4、TIM5)、2个基本定时器(TIM6和TIM7),他们的功能性是依次减少的,具体信息也可查手册获得(一定要利用好手册!!!),下面贴出:; L* _+ j. M- K3 O; T4 l, h/ ^! d. ^# K

) [+ P& [8 P- w# ]8 ]5 }高级控制定时器:" e3 L' \3 C7 X. H! W' y
" q. `6 }* S! g  n( |3 C# {* y
2019041816483539.png
5 C# z. i/ `) r' f) I
0 L8 t' F* ]. X) y+ f: M- p& G5 ^3 e
通用定时器(TIMX):
. Z  x  ~. G/ p1 K6 @
" Z. n: o  M! G. X" }
20190418165334904.png + o/ p* o& g- O$ F# A! p

3 l  t& {) {3 G" H) W基本定时器:

2 M; k1 D3 i" i  w, Y/ ?0 B$ q2 D- a) r) I% V3 Y8 e
20190418165552861.png
( z- ?) d0 y7 R1 K
$ Y% T- m7 r$ ?% }6 X9 j! h
NIOTICE!!!注意:当计数模式为向上计数时:是从0开始向上计数,一直到计数值和重装载寄存器的数值相等时,计数器会自动归零,并产生一个溢出事件(也就是触发一次中断),这点是和51不同的。: ^% h$ j' J/ e

! _* U) B% q, r4 z1 l关于预分频系数: 51单片机的学**,我们知道定时器的数值是每经过一个时钟周期自动加一的,但在32中我们可以通过设定预分频系数(N),使得定时器每经过(N+1)个时钟周期自动加一,这里为什么是N+1个时钟周期加一呢?因为我们的分频系数是从0开始的,假设设定预分频系数是3,那么实际上(0-3)总共是4个数,也就是4分频。
. u5 k+ y! p+ ~& l/ |在开始定时器的配置之前,我们首先要使能时钟:先看一下定时器是挂接在哪条总线上的,可在手册中系统构架部分找到总的构架图:
4 v. W2 s% W  V/ C; t1 F" Y8 G" \% Y) Z/ Q8 l
2019041817240992.png   U5 J, C0 f' w9 p( ~$ {
4 L) R3 v( @, F) ]/ y; b1 r7 w5 ~, m
可以看到除TIM1外的定时器都是挂接在APB1总线上的,所以我们要使能定时器时钟就要使能APB1外设时钟寄存器,看一下函数定义:2 i1 V3 ^3 B) G" N( R8 J9 _

  ~$ F: A6 C1 H' T5 c; J+ Y- A
20190418172645953.png
$ r2 Q# P2 B2 s, O) }4 S
. R  I. W; M, v1 X, }) ]) N
定时器的配置主要配置4个部分:自动重装载寄存器数值、预分频系数、时钟分割(一般的定时功能用不到,可设为0)、计数模式, 同样的,我们可以在库函数(stm32f10x_tim.h)中找到相关的结构体定义:
, P9 k( M0 [1 x$ I, S2 J. [9 g, U& c7 S8 n* g1 {

! A* c0 P& {1 d# f  i, R: D
2 M( f# f( s) R6 h+ ]
配置完定时器之后,我们还需要清除中断标志位(emmm,一般情况下好像没什么影响):用到的函数:" n# \; a! C9 F+ W% }, S! D

0 ^+ {' r. v  j, C: ~
20190418173210368.png 0 ]7 O2 \$ K2 }& R, K& W
/ u/ V' ^) a7 `$ B; \# A
TIMX:是写明用到哪个中断;TIM_IT:表示中断源类型
/ H# r" v1 D5 p1 Q: s关于中断源类型: stm32有6种中断源类型,在相应的库函数(stm32f10x_tim.h)里也可以找到定义:9 m  o) Y- E' f% M; i

9 d1 _! W7 t; X. F1 V& M: K/ i5 _
20190418173932662.png
3 W0 V3 {& }% ~" G& R

% m+ [3 L$ `+ y9 E  g5 V我们的程序里用到的是更新事件中断(TIM_IT_Update)
6 |# I% i) x. y3 M0 w; \使能定时器: 可以理解为51中的(TR0 = 1)这行代码,就是开启定时器,同样的,一旦使能了定时器,就会开始计数。 用到的函数:
& L" D9 p2 R$ {9 {* i8 t' H3 W! u, V
20190418174454176.png 5 u2 F8 @# Y4 @8 B
1 A& k& x; R3 x4 u* _& M
开启中断(中断初始化): 相当于(ET0 = 1)这行代码,用到的函数:( l, i# @+ q3 ^
2 q) f8 t+ J9 I* Y' t' H8 ^
20190418174515446.png
3 g. |4 F: c9 m

/ U3 ~4 a3 Q; [# F( Y加入中断优先级设置部分的代码后就是完整的定时器配置的内容了,下面给出的代码中把两者写入了一个函数,读者也可以分两个函数来写:  c. O9 k# G# \% Y* h" `- ?/ H
  1. #include "stm32f10x.h"
    , n2 n2 n1 k1 Y. [- r

  2. % x9 l( }4 W( r- i7 z
  3. void TIM4_Init(u32 period, u32 prescaler)//period-计数周期(实际值),prescaler-预分频值(实际值)/ @+ |3 L1 P1 I6 ?; J% E$ D9 U
  4. {
    " H# q5 T4 S& v- }
  5.     TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    : t8 y: Y3 S$ {8 t; V
  6.     NVIC_InitTypeDef NVIC_InitStructure;
    / `% q2 r6 `4 s$ g
  7.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);//使能TIM4时钟
    . L6 F0 M6 n, Y! c: ]
  8.     /*没有设置优先级分组,使用系统默认优先级*/, t4 W1 h  A# N, G
  9.     NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;//选择中断通道TIM4
    & B) r1 o1 W# o& U' D7 x& c0 t
  10.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;//设定抢占优先级为0
    ( [$ y/ y4 f8 s  N7 s+ P
  11.     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;//设定响应优先级为0) D1 k' c7 j% [# m, v1 z1 g# Z
  12.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能中断通道" R0 G  d9 H( b+ k- d: K8 i
  13.     NVIC_Init(&NVIC_InitStructure);//调用NVIC初始化函数/ r7 o( p) |, v- C7 m5 [
  14.     * `) n" O* ]; n4 Q9 j2 {' K1 b+ G
  15.     TIM_TimeBaseStructure.TIM_Period = period - 1;//设定自动重装载计数值! c! ]3 T0 l' g% @5 @/ x3 C; x
  16.     TIM_TimeBaseStructure.TIM_Prescaler = prescaler - 1;//设定预分频系数
    ) t8 k: d0 v0 @8 c7 ^1 z' }2 A' l- R
  17.     TIM_TimeBaseStructure.TIM_ClockDivision = 0;//基本定时器没有预分频功能,此项会被忽略: o/ u; F: G& y% j6 k- O1 U
  18.     TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;//设置计数模式为向上计数
    7 g# L4 E6 W- Z
  19.     TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);//初始化TIM4- z8 d; ]6 f5 ~4 e. ?
  20.    
    9 @; D, Y5 L9 ~
  21.     TIM_Cmd(TIM4, ENABLE);//使能TIM4定时器& c2 P5 I) {& y* l7 W
  22.     TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE);//使能TIM4中断
    5 \9 D* {( l7 y
  23. }3 O+ l8 R, O& [0 e) n3 ~
复制代码
' V. B4 C4 @9 t2 J8 c% i" x8 ~5 Q. g
说明:
6 ~+ d9 D! C  P( Q) o5 d" l* C! |" B为什么自动重装载计数值是实际值减一呢? 这个其实和上面我们解释的预分频值是一样的,因为计数值是从0开始计数的,假设我们想要计1000个值就归0,那么我们只需要从0计到999就可以了,也就是说自动重装载寄存器的数值要是999(1000-1)。2 M* l! T5 W  E6 J
5 a, r4 j1 o6 A% J1 ]" w: K
实战:(利用定时器实现L1一秒闪烁)# Q8 ~% q: W; O9 `( N8 q$ Y

. ^1 f# M5 j+ |5 w; N1 xstm32f10x_it.c文件
, v! {1 Q) x9 A: J

  1. 0 q# g+ K; P7 w0 {6 g
  2. void TIM4_IRQHandler(void)//每次产生一个中断溢出事件就会进入一次中断函数- a2 Z/ P9 [6 C1 }  k2 {+ I4 {
  3. {0 F: A7 m8 s# t; k/ E" J3 ^
  4.     if (TIM_GetITStatus(TIM4, TIM_IT_Update) == SET)//检测是否为溢出中断, Q( M9 f2 ^2 A1 r5 X
  5.     {
    - L0 m% Y4 q2 K1 @" o- @6 ?
  6.         TIM_ClearITPendingBit(TIM4, TIM_IT_Update);//清除中断标志位,否则启动时会进入中断服务函数" ?- D7 @3 K1 P6 ?' ~2 r" ^
  7.         
      U5 a* O% ]7 _7 [# R4 b
  8.         GPIOC->ODR ^= (1 << 8);' p* A5 `( `+ P, ~& P
  9.         GPIOD->ODR |= (1 << 2);* T& q7 E/ |9 t; b+ q
  10.         GPIOD->ODR &= ~(1 << 2);
    ; P2 X4 o; c2 m2 p8 X0 ?
  11.     }
    8 L3 U' t; a* Y
  12. }
    ) L/ P; M/ k+ X' D8 N
  13. 、、、
复制代码

! N6 C4 P2 N3 l( B* t: `/ ^/ b

5 z) F4 y9 D  t3 G. e4 wmain.c文件
: {; i+ n" z! }( `. h0 O9 z/ {
  1. #include "stm32f10x.h"- {% O0 V8 u+ C* Y* V( c( D. v
  2. #include "led.h"& g: ~6 U; i; f& k2 v; w- x
  3. #include "timer.h"- V- o' `7 }- K9 V

  4. 1 n) c- F6 d1 r4 u" r+ h6 N
  5. u32 TimingDelay = 0;" e6 P8 _1 h" B7 |& h& Y4 A
  6. 8 n  |$ n5 ?  O2 z& u5 j" P
  7. void Delay_Ms(u32 nTime);3 T6 A4 G' [7 W6 u) S1 g  V* \

  8. 2 I9 J: \8 B' `7 D' k- ~% k$ O
  9. //Main Body
    ( ]5 U2 b; G" k7 U' w# Z
  10. int main(void)% ?: n% V2 [. c2 Y8 v' Y
  11. {
    ! i% l2 p3 ^" \. ~
  12.     SysTick_Config(SystemCoreClock/1000);
    # \% |! ^( x3 Z: t/ J8 T
  13.    
    9 P! h2 R; Z! ]3 Y4 T8 V
  14.         LED_Init();( D$ \" r7 p: ]& n" h, d
  15.     TIM4_Init(10000, 7200);//定时1S
    , D' N1 Q! h  Z, C
  16.    
    7 C) w! q0 D" j* q9 O: w  O
  17.         while(1), V) T7 i' r- d9 \% z" o
  18.     {
    1 u+ A- G; H- z4 P$ L8 L
  19.     / ~6 c/ v5 ]) B- a0 B4 D6 H# ]
  20.     }
    $ e- h' \5 `7 L" S9 L
  21. }  J* ~* m  B! A

  22. * \! l& G$ o. K: t! ~
  23. void Delay_Ms(u32 nTime)
    9 r- y) g2 d7 S. a/ ~
  24. {
    & t' G; D+ X6 E; U
  25.         TimingDelay = nTime;
    $ D, H. [; A: ^& ^6 _  r
  26.         while(TimingDelay != 0);        4 J0 b  D  s& K. K- ?
  27. }! \. o. {$ T; B- Q: ?
  28. 9 v; |% C! G0 x% {' J
复制代码
3 X) a; w1 ^% O  g8 M# B
注意!!!!:* I6 \5 A0 o9 l5 }
8 |/ y  |" j3 t9 Y% F
中断服务函数官方规定统一写在(stm32f10x_it.c)这个文件里,(emmm,当然也可以写在别的文件里)。
/ g6 s% W" S! u8 ^( N& X1 p定时时间的计算公式:预装载值(period) /(72M /预分频值(prescaler )) ;
4 D! z/ y, v8 n7 v$ `时钟频率为72M,那么一个时钟周期就是:1/72M;
* z  t1 M! P+ l/ @# f  E" r6 s) h计一个数需要的时间:预分频值*(1/72M);
7 s- w" p# r8 k* W$ W8 a' p定时时间:预装载值(period)* 预分频值*(1/72M);也可以写为:预装载值(period) /(72M /预分频值(prescaler ))
  M2 o2 H5 a- h1s定时:10000(period)/72000000/7200(prescaler) = 1s。
) v3 \! t; G& J( d9 h% j: K一个快速写底层的方法:; n! A" x/ Y: v$ K) u
理解之后,可以参照stm32固件库里的底层参考例程,用到哪些地方就粘贴那一部分的内容,只需要改变里面的参数即可:
/ \* d" ?+ L  i6 l/ u# v0 f  T' f1 X底层文件查找路径:stm32固件库->STM32F10x文件->Project->STM32F10x_StdPeriph_Examples
: S# Y: \6 ]' f* i: |0 z8 _/ b4 R, [9 d6 p) _( p
20190418183757850.png
9 X. B* g2 u8 E# Z; _5 e
$ s* \8 W" X4 t5 J- G2 l————————————————9 t/ M8 y4 l. l6 ?9 }6 A
版权声明:ReRrain
% H8 K& b9 c- i, [- l) k' e7 a/ P6 y& `# |2 x5 m; V& C' M2 T
$ R6 x' J. i2 V9 O9 d
20190418163128855.png
赞 收藏 评论0 发布时间:2022-12-24 17:40

举报

0个回答

所属标签

相似技术帖

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