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

STM32之定时器配置

[复制链接]
STMCU小助手 发布时间:2022-12-24 17:40
定时器的配置部分可以分为两个部分:初始化NVIC(中断优先级设置)和初始化定时器,下面我们来分别说明。
1 D. N, z  @/ M, w  F. A: p% A3 @2 }
( w; Y' }5 I$ C* l# J
中断系统NVIC
+ C. Z/ `3 `0 x. n# Y. u这一部分要明确中断优先级的概念:stm32将优先级分为抢占优先级和响应优先级,相应的在初始化每个中断源时,都需要设定这两个优先级!!!
  [5 y) x$ c  |3 ]/ t
. {6 Y' D% P5 b  [抢占优先级:决定中断是否可嵌套,高抢占优先级可嵌套低抢占优先级,如果抢占优先级相同,则中断没有嵌套关系。  x( o; v2 b! C9 }
响应优先级:抢占优先级相同且两个中断同时到达的条件下,响应优先级的高低可决定先处理哪一个中断(高者在前);抢占优先级相同但是两个中断不同时到达,则不管响应优先级的高低,都要等先发生的中断处理完才可以接着处理后到来的中断。+ h1 ~, z' [0 ?! K' j; Q" p
在优先级分组的表格中,序号越小,优先级越高。3 N0 p, p2 _7 J. @, `5 y) c
stm32的优先级分组: 共分为5组 (NVIC_PriorityGroup_0到NVIC_PriorityGroup_4),抢占优先级数从小到大,响应优先级数从大到小,其中:NVIC_PriorityGroup_0无抢占优先级,NVIC_PriorityGroup_4无响应优先级。" ~% V( R- S4 q
响应优先级无要求的情况下:初始化时将优先级分组设置为NVIC_PriorityGroup_4的方式(只设置抢占优先级),或者不设置优先级分组(系统默认优先级),一般情况下把响应优先级也设置为0。
, J( o$ ^) H4 x( {: ?7 \5 L
初始化NVIC主要是初始化四个部分:中断通道、抢占优先级、响应优先级、通道使能,这四个部分我们也能在相关的库函数(misc.h)中找到定义:
  R, j& [* t/ d+ O3 F
8 _6 t+ q9 _: t4 K
20190418171732130.png
) R" p2 O$ B4 R6 I
; w& m6 B5 V' d, q5 a
代码部分:2 H7 Y( p2 ~' w7 O: m; H
  1. void NVIC_TIM4Enable(void)9 N6 r/ _& E+ ~4 e/ p
  2. {3 O4 `5 {2 v) v( J
  3.         NVIC_InitTypeDef NVIC_InitStructure;& `1 W; P) O' b' J9 X  X& b
  4.         ! @: S9 W  j+ z( f+ w
  5.         NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;//选择中断通道TIM4* i, y, f7 u8 L4 i. `1 y( T) A
  6.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;//设定抢占优先级为0
    / I" W) A0 a2 ~) {: r: y& [% ?; q
  7.     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;//设定响应优先级为0
    . O# m4 n5 C0 ]3 [
  8.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能中断通道
    ; L3 e# H9 y! a1 v! H% r  A, f3 e/ F9 d
  9.     NVIC_Init(&NVIC_InitStructure);//调用NVIC初始化函数
    0 ^" J2 u; F) k) Z' w
  10. }7 x9 F7 E/ G. q9 d; q. l2 I
复制代码

$ v  {# e. m, c9 ]+ H$ y& f
+ m. e; R  W' _5 R; A做几点说明:
+ o2 [8 I6 r+ g5 L我们没有用到中断的嵌套,所以把抢占优先级也设置为0;
; c" H/ G& V2 `: j! F- [关于中断通道:stm32使用了Cortex-M3中的84个中断通道,(16个异常,68个中断)具体的相关信息可以在中文手册中查找到。7 [! H# G/ I, I; m
' o1 K+ y2 T) b( D! f5 J9 J6 p7 |- o  W
初始化定时器8 c, S2 Q. M  B& k1 J" Y% w" ~5 T
stm32(STM32F103ZET6功能较为全面的一种)内部含有11个定时器:2个高级控制定时器(TIM1和TIM8)、4个通用定时器(TIMX:TIM2、TIM3、TIM4、TIM5)、2个基本定时器(TIM6和TIM7),他们的功能性是依次减少的,具体信息也可查手册获得(一定要利用好手册!!!),下面贴出:
) e/ F8 ?# |' A. d5 a6 |  ]
3 C' l/ `3 \5 X% L' \3 }' N
高级控制定时器:
) [; P; E- j" v- d' C5 U2 i$ p* g. u+ i2 u5 {- u( q
2019041816483539.png 0 {8 ?4 M& s! U. p9 Z, A/ z! m! s
0 {4 r" o. _6 S+ Y, O* F
通用定时器(TIMX):8 V; k3 g/ h# x: z
9 s# d9 X( R# |$ m
20190418165334904.png . b: U; q+ Y% @0 B

& C2 m  e8 I3 e+ M基本定时器:

6 I5 m- N/ t: \# _
" ^' k9 H/ l. N4 C7 M
20190418165552861.png " W3 C2 f8 A/ h" z8 d

) e0 ~, z7 A% [' |2 l$ DNIOTICE!!!注意:当计数模式为向上计数时:是从0开始向上计数,一直到计数值和重装载寄存器的数值相等时,计数器会自动归零,并产生一个溢出事件(也就是触发一次中断),这点是和51不同的。0 S. S. V  f8 M3 L5 ^% ?" ]

% J9 ]) f0 u$ G2 s! D关于预分频系数: 51单片机的学**,我们知道定时器的数值是每经过一个时钟周期自动加一的,但在32中我们可以通过设定预分频系数(N),使得定时器每经过(N+1)个时钟周期自动加一,这里为什么是N+1个时钟周期加一呢?因为我们的分频系数是从0开始的,假设设定预分频系数是3,那么实际上(0-3)总共是4个数,也就是4分频。
' f( r+ X2 q8 a在开始定时器的配置之前,我们首先要使能时钟:先看一下定时器是挂接在哪条总线上的,可在手册中系统构架部分找到总的构架图:
) D1 r: Q3 I& z" P/ x0 {9 `2 h8 r: r0 d& C2 s  ]0 q
2019041817240992.png 5 c- m' h+ I. R

1 t3 J0 T; m+ m9 t/ w4 F2 w# Q3 K6 f可以看到除TIM1外的定时器都是挂接在APB1总线上的,所以我们要使能定时器时钟就要使能APB1外设时钟寄存器,看一下函数定义:3 G! \9 ?4 u! z
+ w; }. l# \. X$ z* N, A1 ?# a
20190418172645953.png
1 L0 G! P: b/ {5 Q  t
7 r7 o/ V6 P5 |: f+ D# I) ?- T
定时器的配置主要配置4个部分:自动重装载寄存器数值、预分频系数、时钟分割(一般的定时功能用不到,可设为0)、计数模式, 同样的,我们可以在库函数(stm32f10x_tim.h)中找到相关的结构体定义:
2 r% e% ~2 t# j9 f* ]
3 j- Q( C1 F7 e! D2 c, u! C
  i7 }! N: u# i

, \5 \9 C6 N( A, e( N, m配置完定时器之后,我们还需要清除中断标志位(emmm,一般情况下好像没什么影响):用到的函数:
# J  G; |1 \7 n7 x& x0 A
% X  u; ]) M5 q9 {7 o8 D1 B6 s
20190418173210368.png ; L3 d( M. C( E7 {2 Q* j2 R
* {* {$ E$ _0 ^: ^# J' `
TIMX:是写明用到哪个中断;TIM_IT:表示中断源类型
. J  _  Y9 u% L: s8 X- I关于中断源类型: stm32有6种中断源类型,在相应的库函数(stm32f10x_tim.h)里也可以找到定义:
5 W5 }( T; _$ m
  E; B  n1 J  u
20190418173932662.png
, z; W  s4 B% F
- Q( N- d2 q* C: j2 h
我们的程序里用到的是更新事件中断(TIM_IT_Update)
- V& i" W% I/ w. w/ F% t. l- Y使能定时器: 可以理解为51中的(TR0 = 1)这行代码,就是开启定时器,同样的,一旦使能了定时器,就会开始计数。 用到的函数:+ h6 `9 y$ g2 C$ R! j

3 t' c5 t) ~% \: U# Y. z5 p
20190418174454176.png
$ o0 h9 k/ ?3 ~2 B8 O' S( t7 _- T
7 ?9 W5 C5 w% T
开启中断(中断初始化): 相当于(ET0 = 1)这行代码,用到的函数:
" h8 Y3 [3 ~/ L8 F4 w1 K* n- ]. z0 J. M7 K
20190418174515446.png 9 d. E# ?/ s- z' T
( y  f! D* S, l5 q4 w$ H5 n+ K
加入中断优先级设置部分的代码后就是完整的定时器配置的内容了,下面给出的代码中把两者写入了一个函数,读者也可以分两个函数来写:
! B# a/ Q5 _' D8 ~) M/ W
  1. #include "stm32f10x.h"0 D5 {/ @, k2 R" {6 ]% M6 m
  2. 0 v; M8 z, J* k9 K
  3. void TIM4_Init(u32 period, u32 prescaler)//period-计数周期(实际值),prescaler-预分频值(实际值)8 L( z+ A% n4 {7 f2 [/ `' p
  4. {
    ) S( Y) f+ p$ N" V( ?4 I, ^
  5.     TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    , @( L, V- ?2 o) o1 u. p' A) A
  6.     NVIC_InitTypeDef NVIC_InitStructure;
    3 o6 G8 U$ ^! ], O/ V! B
  7.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);//使能TIM4时钟
    + g# x- u) g/ a4 u
  8.     /*没有设置优先级分组,使用系统默认优先级*/- m8 B1 [  n" k3 Y  o
  9.     NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;//选择中断通道TIM4
    2 r/ S" C  |% P' |! o/ z8 X5 f
  10.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;//设定抢占优先级为08 y9 C' r5 q/ ~8 S3 c; m
  11.     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;//设定响应优先级为08 n  w. v, H" {9 X0 m8 f6 ^& @
  12.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能中断通道0 c$ Q0 _- m! n7 X" _# @$ Y
  13.     NVIC_Init(&NVIC_InitStructure);//调用NVIC初始化函数+ e. @4 J- q# a4 B5 z) c& y( z! y
  14.    
    - W  H% M- N  J8 T) u
  15.     TIM_TimeBaseStructure.TIM_Period = period - 1;//设定自动重装载计数值8 b* G& R7 I: ^8 E, w$ N
  16.     TIM_TimeBaseStructure.TIM_Prescaler = prescaler - 1;//设定预分频系数
    4 r6 @& F& ]% Q( }
  17.     TIM_TimeBaseStructure.TIM_ClockDivision = 0;//基本定时器没有预分频功能,此项会被忽略1 J7 ~% c+ r  I1 }, c/ ]' v9 E8 D
  18.     TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;//设置计数模式为向上计数9 R) O7 u$ `6 |, U. B! T" |  T
  19.     TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);//初始化TIM4
    2 w. B! F- w3 w/ Q- |# E% f& M
  20.    
    * O- c# S' C. [6 Y
  21.     TIM_Cmd(TIM4, ENABLE);//使能TIM4定时器2 Q. \; {  j  v' }
  22.     TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE);//使能TIM4中断2 K% r# H! A4 y/ p
  23. }
    8 P0 h% I+ y7 O( k: u+ v
复制代码
+ e) k: S9 J* P1 l+ L" T$ g
说明:" |4 k. u6 z% x2 }3 L
为什么自动重装载计数值是实际值减一呢? 这个其实和上面我们解释的预分频值是一样的,因为计数值是从0开始计数的,假设我们想要计1000个值就归0,那么我们只需要从0计到999就可以了,也就是说自动重装载寄存器的数值要是999(1000-1)。
" L. h; W; O9 N# X7 [$ A
2 W/ f1 ?; f4 U: y& F
实战:(利用定时器实现L1一秒闪烁)
1 [9 z5 Q8 m" D. W$ m8 R
& P; Z2 n3 `/ o+ K( `stm32f10x_it.c文件
" y6 X% E* [3 p7 }5 t* |

  1. # f4 f# I* s9 E4 O- C
  2. void TIM4_IRQHandler(void)//每次产生一个中断溢出事件就会进入一次中断函数
    1 M5 [: W0 C( e! D9 Y  u: j7 R
  3. {
    1 q" S$ a3 d# L: w
  4.     if (TIM_GetITStatus(TIM4, TIM_IT_Update) == SET)//检测是否为溢出中断
    % y) p2 s/ M2 H3 X8 g' |
  5.     {
    , W; J% g% B6 `$ n. y4 y  l
  6.         TIM_ClearITPendingBit(TIM4, TIM_IT_Update);//清除中断标志位,否则启动时会进入中断服务函数$ @# Q4 Z, g: L# D& d% W
  7.         ; E, \4 v+ p8 \5 t3 N" i$ _) l
  8.         GPIOC->ODR ^= (1 << 8);2 K! o& t$ x3 g1 h
  9.         GPIOD->ODR |= (1 << 2);
    , Z& W0 l0 E  z/ \) |, a# {8 Q
  10.         GPIOD->ODR &= ~(1 << 2);
    4 z" D' q0 C2 H
  11.     }
    % ?# A4 Q. R9 L- t( E
  12. }
    1 E- Y5 m' v& E- Q9 N  Q
  13. 、、、
复制代码

% k5 R9 G* f0 A: o0 p6 e
5 `/ {; A% x! j5 ?, `- s
main.c文件
1 X. }) K9 q+ ?$ p1 c% I2 m# i/ N
  1. #include "stm32f10x.h"$ Q! [! K1 v6 s8 h+ G# E1 G7 V! Z
  2. #include "led.h"
    ! j' O/ X2 S4 }: v/ x+ o- X: Q/ U
  3. #include "timer.h"
    4 A' _7 i' b& v
  4.   F5 w8 E' h0 f8 ?9 U2 ?- I
  5. u32 TimingDelay = 0;
    & P, l1 t/ G& [" \( I( Y% v) J

  6. ' t/ k. P1 r5 V/ ~  F
  7. void Delay_Ms(u32 nTime);
    3 M6 F/ i7 g6 d  C

  8. 0 i% X( {  l) Z. B4 m
  9. //Main Body
    6 `8 t0 }+ l# r  h) l. `( j; y
  10. int main(void)& e- b# P7 r, B
  11. {
    8 r- I  _2 r1 M
  12.     SysTick_Config(SystemCoreClock/1000);
    + M3 j  a7 a, L/ U9 ~) @
  13.    
    ; B9 G6 i( p1 I
  14.         LED_Init();
    1 {) O% F4 R$ ^  b( y
  15.     TIM4_Init(10000, 7200);//定时1S
    : a* e# i. \1 S( N6 ^
  16.     . {# x5 Y% b0 q
  17.         while(1)
    8 D' D2 Q6 z4 ~0 l( s' T7 A3 h- [& j
  18.     {
    ; G' G2 L8 _: |3 Z( N! N
  19.    
    ) I' _4 Q' J0 ]) l
  20.     }
    5 a6 z, f' }: o- [8 u! i! G
  21. }
    5 z3 T7 v/ m4 O/ Y
  22. , `" ^  i1 {+ |* `  D
  23. void Delay_Ms(u32 nTime)% A* L6 p/ w  J$ V
  24. {
    / u; v; u8 e; S8 r# d
  25.         TimingDelay = nTime;
    7 A. n  S/ `- q. ~3 ]- U
  26.         while(TimingDelay != 0);        0 g: r/ V3 w, k' B% U
  27. }
    . r0 X) t+ y& e4 q
  28. * S7 n6 t' x6 l/ [
复制代码
1 O2 C  x, q1 T$ k
注意!!!!:6 c1 R" @' z5 r2 D3 L, j
0 ?# k3 D( }. K: l  N* C9 p: `
中断服务函数官方规定统一写在(stm32f10x_it.c)这个文件里,(emmm,当然也可以写在别的文件里)。9 |* }0 x) m' t
定时时间的计算公式:预装载值(period) /(72M /预分频值(prescaler )) ;$ |5 P; z2 w. q( ^
时钟频率为72M,那么一个时钟周期就是:1/72M;2 v# e$ `$ K# c. P7 |: ~6 F  \' q
计一个数需要的时间:预分频值*(1/72M);' B* ~; q) p$ a
定时时间:预装载值(period)* 预分频值*(1/72M);也可以写为:预装载值(period) /(72M /预分频值(prescaler ))
( h* U; a9 @: B, X: ~0 B1s定时:10000(period)/72000000/7200(prescaler) = 1s。4 P  i5 M4 M: J! X8 Q! J
一个快速写底层的方法:
, C: L7 w+ j) E% K3 ~9 D: m( g理解之后,可以参照stm32固件库里的底层参考例程,用到哪些地方就粘贴那一部分的内容,只需要改变里面的参数即可:
8 h% k1 p# p; i8 X1 n: f底层文件查找路径:stm32固件库->STM32F10x文件->Project->STM32F10x_StdPeriph_Examples  ]$ @) w3 S& w5 F

: K' ~  v4 \3 g' c% O: l 20190418183757850.png 6 f1 v+ g8 G$ Q6 \4 o3 _
! ~$ a! L; z7 d+ T+ f
————————————————; j) W2 N: @+ [  z. Q* _5 i( ~
版权声明:ReRrain
/ H* h; v& j6 z* u% U# P0 g5 ~5 N3 V7 }) B; [4 l9 D7 P# `+ ^
; P6 `- I- b) O: |; }
20190418163128855.png
收藏 评论0 发布时间:2022-12-24 17:40

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版