定时器的配置部分可以分为两个部分:初始化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
6 E% C" Q# d; _/ U7 G% f6 @
. P e' t# b( F. `# _; P9 B! a代码部分:" R8 B: i) t' L! p: E
- void NVIC_TIM4Enable(void)
& m. ]* t+ { u! r - {
+ }8 K) a9 Q: q. U. A& o8 o- a( w& m - NVIC_InitTypeDef NVIC_InitStructure;
0 F( s7 i' r# A -
' ^! ]! T9 R+ y$ B. S k# S- X - NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;//选择中断通道TIM47 ^* P. R! I" a9 d* {3 U. C' E, g" k
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;//设定抢占优先级为09 r9 |7 P* x+ X' M" ?
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;//设定响应优先级为0
1 l4 |& Z0 k! M( f7 }# z - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能中断通道; _( G. K$ r `
- NVIC_Init(&NVIC_InitStructure);//调用NVIC初始化函数$ o J# `4 Y: J
- }
! 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
5 C# z. i/ `) r' f) I0 L8 t' F* ]. X) y+ f: M- p& G5 ^3 e
通用定时器(TIMX):
. Z x ~. G/ p1 K6 @
" Z. n: o M! G. X" }
+ 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
( 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
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
$ 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: D2 M( f# f( s) R6 h+ ]
配置完定时器之后,我们还需要清除中断标志位(emmm,一般情况下好像没什么影响):用到的函数:" n# \; a! C9 F+ W% }, S! D
0 ^+ {' r. v j, C: ~
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 _
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
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 ^
3 g. |4 F: c9 m
/ U3 ~4 a3 Q; [# F( Y加入中断优先级设置部分的代码后就是完整的定时器配置的内容了,下面给出的代码中把两者写入了一个函数,读者也可以分两个函数来写: c. O9 k# G# \% Y* h" `- ?/ H
- #include "stm32f10x.h"
, n2 n2 n1 k1 Y. [- r
% x9 l( }4 W( r- i7 z- void TIM4_Init(u32 period, u32 prescaler)//period-计数周期(实际值),prescaler-预分频值(实际值)/ @+ |3 L1 P1 I6 ?; J% E$ D9 U
- {
" H# q5 T4 S& v- } - TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
: t8 y: Y3 S$ {8 t; V - NVIC_InitTypeDef NVIC_InitStructure;
/ `% q2 r6 `4 s$ g - RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);//使能TIM4时钟
. L6 F0 M6 n, Y! c: ] - /*没有设置优先级分组,使用系统默认优先级*/, t4 W1 h A# N, G
- NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;//选择中断通道TIM4
& B) r1 o1 W# o& U' D7 x& c0 t - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;//设定抢占优先级为0
( [$ y/ y4 f8 s N7 s+ P - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;//设定响应优先级为0) D1 k' c7 j% [# m, v1 z1 g# Z
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能中断通道" R0 G d9 H( b+ k- d: K8 i
- NVIC_Init(&NVIC_InitStructure);//调用NVIC初始化函数/ r7 o( p) |, v- C7 m5 [
- * `) n" O* ]; n4 Q9 j2 {' K1 b+ G
- TIM_TimeBaseStructure.TIM_Period = period - 1;//设定自动重装载计数值! c! ]3 T0 l' g% @5 @/ x3 C; x
- TIM_TimeBaseStructure.TIM_Prescaler = prescaler - 1;//设定预分频系数
) t8 k: d0 v0 @8 c7 ^1 z' }2 A' l- R - TIM_TimeBaseStructure.TIM_ClockDivision = 0;//基本定时器没有预分频功能,此项会被忽略: o/ u; F: G& y% j6 k- O1 U
- TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;//设置计数模式为向上计数
7 g# L4 E6 W- Z - TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);//初始化TIM4- z8 d; ]6 f5 ~4 e. ?
-
9 @; D, Y5 L9 ~ - TIM_Cmd(TIM4, ENABLE);//使能TIM4定时器& c2 P5 I) {& y* l7 W
- TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE);//使能TIM4中断
5 \9 D* {( l7 y - }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
0 q# g+ K; P7 w0 {6 g- void TIM4_IRQHandler(void)//每次产生一个中断溢出事件就会进入一次中断函数- a2 Z/ P9 [6 C1 } k2 {+ I4 {
- {0 F: A7 m8 s# t; k/ E" J3 ^
- if (TIM_GetITStatus(TIM4, TIM_IT_Update) == SET)//检测是否为溢出中断, Q( M9 f2 ^2 A1 r5 X
- {
- L0 m% Y4 q2 K1 @" o- @6 ? - TIM_ClearITPendingBit(TIM4, TIM_IT_Update);//清除中断标志位,否则启动时会进入中断服务函数" ?- D7 @3 K1 P6 ?' ~2 r" ^
-
U5 a* O% ]7 _7 [# R4 b - GPIOC->ODR ^= (1 << 8);' p* A5 `( `+ P, ~& P
- GPIOD->ODR |= (1 << 2);* T& q7 E/ |9 t; b+ q
- GPIOD->ODR &= ~(1 << 2);
; P2 X4 o; c2 m2 p8 X0 ? - }
8 L3 U' t; a* Y - }
) L/ P; M/ k+ X' D8 N - 、、、
复制代码
! N6 C4 P2 N3 l( B* t: `/ ^/ b
5 z) F4 y9 D t3 G. e4 wmain.c文件: {; i+ n" z! }( `. h0 O9 z/ {
- #include "stm32f10x.h"- {% O0 V8 u+ C* Y* V( c( D. v
- #include "led.h"& g: ~6 U; i; f& k2 v; w- x
- #include "timer.h"- V- o' `7 }- K9 V
1 n) c- F6 d1 r4 u" r+ h6 N- u32 TimingDelay = 0;" e6 P8 _1 h" B7 |& h& Y4 A
- 8 n |$ n5 ? O2 z& u5 j" P
- void Delay_Ms(u32 nTime);3 T6 A4 G' [7 W6 u) S1 g V* \
2 I9 J: \8 B' `7 D' k- ~% k$ O- //Main Body
( ]5 U2 b; G" k7 U' w# Z - int main(void)% ?: n% V2 [. c2 Y8 v' Y
- {
! i% l2 p3 ^" \. ~ - SysTick_Config(SystemCoreClock/1000);
# \% |! ^( x3 Z: t/ J8 T -
9 P! h2 R; Z! ]3 Y4 T8 V - LED_Init();( D$ \" r7 p: ]& n" h, d
- TIM4_Init(10000, 7200);//定时1S
, D' N1 Q! h Z, C -
7 C) w! q0 D" j* q9 O: w O - while(1), V) T7 i' r- d9 \% z" o
- {
1 u+ A- G; H- z4 P$ L8 L - / ~6 c/ v5 ]) B- a0 B4 D6 H# ]
- }
$ e- h' \5 `7 L" S9 L - } J* ~* m B! A
* \! l& G$ o. K: t! ~- void Delay_Ms(u32 nTime)
9 r- y) g2 d7 S. a/ ~ - {
& t' G; D+ X6 E; U - TimingDelay = nTime;
$ D, H. [; A: ^& ^6 _ r - while(TimingDelay != 0); 4 J0 b D s& K. K- ?
- }! \. o. {$ T; B- Q: ?
- 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
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
|