STM32通用定时器使用详解 #1.通用定时器基本介绍 # d t2 J4 ]% w& y
; Z5 t s# F0 f9 W( x+ V) w. S
※ 通用定时器包括TIM2、TIM3、TIM4和TIM5 ※ STM32通用定时器是一个通过可编程预分频器驱动的16位自动装载计数器构成。 ※ 每个定时器都是完全独立的,没有互相共享任何资源。它们可以一起同步操作。 ※ 定时器可以进行定时器基本定时,输出4路PWM,输入捕获 ※ 本文详细介绍这三个功能并且利用定时器3并且示例代码使用
4 s( \# d# [2 N#2.开发环境
: E. H# |3 d. Q. o
9 g) L% A% l, W0 `2 w1 W开发平台:keil5 单片机:STM32F103ZET6
6 [: K% }0 \' W3 g, i+ K#3.基本定时功能
3 a9 O" `' [' y" I, u/ S7 |# C+ o / A* S) W& C8 |# p
## 3.1定时器时钟来源分析 STM32部分时钟树: # W$ H2 z/ d) D
3.1.1 首先我们我们的系统时钟(SYSCLK 72MHz) 经过AHB分频器给APB1外设,但是APB1外设最大的只能到36Mhz,所以必须要系统时钟的二分频。下面又规定了如果APB1预分频系数为1则频率不变,否则频率X2至定时器27,所以定时器27的时钟频率为还是72MHz 7 l; _) f4 c# b3 b/ L6 D/ X
. @) ?# i3 a9 `& O+ q 3.1.2 分配给我们定时器的时钟是72MHz,我们可以根据自己的需求再设置定时器的分频,设置它的定时值 - /*1 i8 \1 Y0 Y z2 | x
- * 初始化定时器的时候指定我们分频系数psc,这里是将我们的系统时钟(72MHz)进行分频% ?& X9 d. {+ j0 k2 T, o
- * 然后指定重装载值arr,这个重装载值的意思就是当 我们的定时器的计数值 达到这个arr时,定时器就会重新装载其他值.8 Y! e+ o8 W; P' h4 |6 k* U
- 例如当我们设置定时器为向上计数时,定时器计数的值等于arr之后就会被清0重新计数
8 ~" d: U" X8 s. \ - * 定时器计数的值被重装载一次被就是一个更新(Update)
/ [# }! L$ q- @3 ^, @2 ~ - * 计算Update时间公式
' H: A1 j# e! s9 _; I - Tout = ((arr+1)*(psc+1))/Tclk
0 C% L- Q: @' {. Z {. O - 公式推导详解: L. y2 b# W1 z) s/ `' t6 y
- Tclk是定时器时钟源,在这里就是72Mhz 5 T$ O' k; |. F7 e
- 我们将分配的时钟进行分频,指定分频值为psc,就将我们的Tclk分了psc+1,我们定时器的最终频率就是Tclk/(psc+1) MHz
! t' @2 F1 k1 B6 M( |- u, a& X - 这里的频率的意思就是1s中记 Tclk/(psc+1)M个数 (1M=10的6次方) ,每记一个数的时间为(psc+1)/Tclk ,很好理解频率的倒数是周期,这里每一个数的周期就是(psc+1)/Tclk 秒
7 I0 T: y9 p4 w3 ^ - 然后我们从0记到arr 就是 (arr+1)*(psc+1)/Tclk5 a9 b' q# f6 e" D1 W
- 举例:比如我们设置arr=7199,psc=9999
0 ^# K1 F' _/ Y8 ]( \ - 我们将72MHz (1M等于10的6次方) 分成了(9999+1)等于 7200Hz
( @9 ^/ Y2 s7 A1 j. |+ k* d - 就是一秒钟记录9000数,每记录一个数就是1/7200秒7 e( m- ]' P O. X7 G3 i7 R
- 我们这里记录9000个数进入定时器更新(7199+1)*(1/7200)=1s,也就是1s进入一次更新Update( v8 ]6 ?4 h( E8 ]% J2 t- i
- */
' N% g( y* O1 L7 \ \, B9 Q - //简单进行定时器初始化,设置 预装载值 和 分频系数
6 ^3 b% }1 g; j% B - void MY_TIM3_Init(u16 arr,u16 psc){
; l/ F3 I [. w) m* K - 3 `! _& P8 \' n8 u3 b. C3 y
- //初始化结构体
/ \' K2 ^# f0 ~2 ~! X, b - TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;/ J1 |& N8 h3 A( M9 r; D7 P
-
8 a( i- ~5 B" f( ?& C - //1.分配时钟
! n! i4 ^% d( M - RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);4 Q% c+ g7 _4 t8 m5 `! c+ I+ s
- % z& i6 t. @8 h3 H# L
- //2.初始化定时器相关配置, E$ M' T4 ~ Q2 h
- TIM_TimeBaseStructure.TIM_Period = arr;1 g- w2 C+ Q0 K4 e( n
- TIM_TimeBaseStructure.TIM_Prescaler = psc;
; A8 t4 {: n2 C% U' e) ~) l - ; Y+ j6 a( V* C, t) n
- /*在这里说一下这个TIM_ClockDivision 是设置与进行输入捕获相关的分频
& \! B. ?3 d7 @; n2 C" T - 设置的这个值不会影响定时器的时钟频率,我们一般设置为TIM_CKD_DIV1,也就是不分频*/
. j* y! q+ n$ ^5 F5 |4 N: r - TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;% ]7 D9 ~3 ~, f* @
- TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数6 e# K5 n4 n2 J1 h" y3 ?- h3 L. m
- TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure);
3 ]( l0 l a* G' d- R0 g9 H+ a -
' }/ k8 k2 B# X. R; E# F - //3.打开定时器/ R. `; d a- C% ]
- TIM_Cmd(TIM3,ENABLE);
; x; z! _0 q6 F: P* p - }
8 Y* E* \1 X+ q' i5 v - , \# ]0 w& v' p7 [* g4 h
- /****************** 主函数 ********************/4 a" s6 ^: s' l, C1 k0 Z
- //在主函数中我们可以调用初始化4 R( `+ k5 z# i; q
- int main(){( e* c. c. Z5 o8 B+ ]1 F
- //定时器初始化
* _# v% p& \- o- f6 T - MY_TIM3_Init(7199,9999);
4 J: Z1 {4 Y+ ^. f2 ? - while(1){
+ a* `0 @3 Z7 A" F( D* q1 w" ? -
( E: B! n# F3 q1 O7 W3 r - //检测更新标志位
4 C+ A/ K: \, E% A0 Z* {6 r! v& b - if(TIM_GetFlagStatus(TIM3,TIM_IT_Update)){' p* f2 j) i2 N
- //清除标志位) ]3 t" l- y( T( d) G1 L) D. Y a) H1 Q
- TIM_ClearFlag(TIM3,TIM_IT_Update);; F7 y$ `$ f1 D( U/ V
- //....(每隔一秒执行任务)( k q! F# A% M
- }! u4 S$ ^, X1 N: M* _3 f* U! Q
-
0 \! v6 N4 }% ~, m1 S) i" m& n - }% d" ?/ t# t/ x
- }
复制代码
0 m& M) q; C; {: W1 v#4.定时器输出PWM / b9 F% g) P3 ]% {) s6 e$ Y& S8 A
## 4.1基本介绍
4 Y' |3 w) K% q. X3 z6 u' l
4.1.1PWM是脉冲宽度调制,我们是通过改变脉冲的宽度来达到改变输出电压的效果,本质上就是调节占空比实现的,STM32除了基本定时器(TIM6,TIM7)不能输出PWM以外,其它的定时器都具有输出PWM,其中高级定时器(TIM1和TIM8)还能输出7路PWM,基本定时器(TIM2,TIM3,TIM4,TIM5)也可以输出4路PWM > 输出PWM是很有用的,比如我们可以通过控制电机来玩小车,或者通过输出PWM改变LED的亮度,制造呼吸灯等等
( f+ w% R) v9 e- T3 }6 X: m" a8 C. g 4.1.2 我们通用定时器能输出PWM的IO口是固定的,虽然我们可以通过重映射可以改变引脚,具体是哪一些IO口我们要通过查阅STM32的参考手册 + k* Y1 _ v) S$ h6 n$ ^
这里涉及到一个重映射的概念,重映射就是管脚的外设功能映射到另一个管脚,但是不是可以随便映射的,具体对应关系参考手册上的管脚说明。这样优点是可以优化电路设计;扩展功能,减少外设芯片资源
: j, i8 J5 Y9 d6 i5 ~- J7 Y1 v- 时器3,可产生四路的PWM输出,四个通道分别对应的引脚情况如下1 Z5 S S+ g6 ^+ }- l" Z& _
- TIM3_CH1,TIM3_CH2,TIM3_CH3,TIM3_CH46 [8 v, h' x% S4 w, x8 U
- 没有重映像的对应情况:
/ {( T) O1 g' G0 D2 a - PA6,PA7,PB0,PB1
' t' Z' C$ u* ?. X - 部分重映像:
7 E! ^/ D4 H: E; w/ W - PB4,PB5,PB0,PB1
" q6 o. [1 f' ~: k$ D) l - 完全重映像:
1 s& m1 `# B+ z: K: e& X! {/ S - PC6,PC7,PC8,PC9
! H* b2 f/ ]- M' [ - 8 f% ~# T( S$ t4 E7 N0 T( q# z
- 当我们的IO口不仅仅是做普通的输入输出使用的时候,作为别的外设(AD,串口,定时器等)的特定功能引脚,就需要开启外设.$ e, t# Y6 B& Q: n8 T. j. W
- 这里我们还需要开启APB2外设上的复用时钟AFIO,同时IO口采用的是复用输出!
- h# @* j) P: {$ b4 |$ Y; q
2 F1 o) W* H, [* E, G0 x# x: T- 我们这里是没有使用重映射功能.$ P4 m! F$ u/ E8 ~# Y0 w( z
- */( A1 f+ o- G9 N! _! t( a, b% _3 ~
- // 宏定义 F$ M( C1 ^ a$ ~* ~1 M# B2 E5 m' R
- //判断当前是处于哪一种模式,以便于我们初始化IO口7 D1 w2 P* Z3 U9 T
- #define NO_REAMP 05 e5 N/ c2 `% F' a) N
- #define PART_REAMP 14 k1 x. F7 k" O* o9 a" P
- #define FULL_REAMP 2/ U4 h h5 \ i, u
- + i T" a( K- Y' a
- // ---> 这里是需要制定的参数
z- l1 _% {$ f% x3 w" k
& {8 V- D' e' P" }8 W- //指定这里的 当前的模式,我们给她默认指定是 没有重映射) K* N1 m C: W3 ]( f- D
- #define CURRENT_MODE NO_REAMP
- l9 k5 [& X5 J# ?7 E- V, Y
! s* ^' D1 v0 B6 `- //*************根据当前模式初始化IO口 函数
( R% g! y p) q* S - void MY_TIM3_GPIO_Init(void){( [% G K, K; o
- 1 u% Z0 F& @' P% g- g8 Q
- GPIO_InitTypeDef GPIO_InitStructure;# z7 T$ W' }% R9 j8 e
- 5 R! S" Q- p8 V$ f- H$ _ f
- //1.开启AFIO时钟
7 y% T3 t9 x+ Q# D3 w - RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
7 v% f& R8 i( K2 F, I q2 P - 9 I! M5 z/ {1 l3 X8 i
- //2. 根据当前的重映像的模式 配置时钟 和 初始化相关引脚4 L5 |* K* D* g7 u! ?7 Z
- switch(CURRENT_MODE){. }; _2 U7 [" l; ^6 S9 {/ Q
-
/ I" f' g0 |) B5 c1 ]7 F - //2.1 如果没有重映射
' z" @( U! K' w! Q( ^ - case NO_REAMP:{
- J& T q5 V( n1 F& e - ( M% Z; N& {, s9 e7 f/ s0 f
- // 时钟分配" d0 \7 L3 ^$ q* y$ p* X
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB,ENABLE);
4 ~& q: p0 _8 X( e - // 初始化IO口: o$ e/ ^8 ~6 s% w
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;3 ~8 W) t2 B( C. D" @
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;7 p8 b1 d1 q9 L7 c8 n& H
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;; }2 o$ [4 B% h
- GPIO_Init(GPIOA,&GPIO_InitStructure);: v- o- C7 H) t: d
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1;
, h# a- W9 b' h% n3 I* C - GPIO_Init(GPIOB,&GPIO_InitStructure);; u/ n" x9 |( e5 H1 i/ l6 K- |
- : F7 Q# s7 w/ ]+ |) e% {
- break;4 Y' B3 y( M& l( @ @
- }; Q) ^2 T) `, F1 _# Z/ f* W/ U0 S
- //2.2 部分重映射
, [3 h4 ]! H P/ c9 o& {% w, f - case PART_REAMP:{
# Z: B! N, j, z; X. s" o -
4 g$ m+ I: I3 N7 x/ ] - // 时钟分配9 W4 N7 u" w5 A/ n. O
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);: y$ T& C$ J+ L2 o4 ~ h! e9 `" g
- // 初始化IO口) U+ H6 j" r7 j1 |9 b
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;6 o) u+ Z% o4 P( D8 f- T4 h: u4 _
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;! T+ Z7 O2 h& ^" E8 W. a
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_4|GPIO_Pin_5;
: r. l- B. G- S3 X - GPIO_Init(GPIOB,&GPIO_InitStructure);9 a0 C8 m3 y D( p, @& s+ s
-
/ Q% h5 Q( y# V6 \5 e' q& l0 U - break;% e' Z& H! V7 I9 n- E; G- v
- }
7 `$ t% u8 O6 |6 I, x - //2.3 全映射
! O) Y1 h+ U" c6 x4 k% j - case FULL_REAMP:{- l, N# _ H$ S! g4 i P; \0 w
- ' ^0 J+ B( f1 H6 l: m
- // 时钟分配; e. D) p8 k& d4 z
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);, W" ]& L) n# }/ I8 A% h
- // 初始化IO口+ B! z# n. U, d( x9 t4 _
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
* v$ S+ t' ?5 Y' r& z - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
$ F9 T( X+ E+ r( \6 E - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9;+ j' U6 r5 |! I" C
- GPIO_Init(GPIOB,&GPIO_InitStructure);
2 ]2 r$ s8 W* J7 Y2 s -
0 X# _" f" \3 R1 G, ? - break;" y% j; O* F) H) m- O
- }
) O4 L; t! t0 e2 v6 | - default:break;, A% c! s& O! j: w. t
- } / }" i) C9 S" _! i
- }( K8 A" g* \: C1 {; ~& T
" ~( N. D, k9 O6 T6 T b- //***************** 定时器PWM输出初始化函数
. S* ~& i/ L6 Z4 u; Q, r - void MY_TIM3_PWM_Init(u16 arr,u16 psc){' H: u0 P6 l. h( l$ M7 g
-
2 {5 _; g% W) r$ p; @' [ - //初始化结构体- @/ R, |# L! `( \: h' |7 Y9 a
- TIM_OCInitTypeDef TIM_OCInitstrcuture;
* r* I% Y: z7 I2 R - 3 m# G. a& D" B' X4 M' F
- //1.初始化定时器 和 相关的IO口/ l8 ^/ C; D2 S. n
- MY_TIM3_Init(arr,psc); ! d. ~ ^4 a% l0 K: S
- MY_TIM3_GPIO_Init();7 o7 H- p2 Q$ `9 _/ F
-
+ H- ?* }! F) [/ ]$ k3 A" q6 X+ N - //2.初始化PWM的模式$ E7 g' ~" `3 f0 A2 G: l, ]4 @
- 1 x; g% T4 e0 c" M
- /**# ?! Y$ M2 I( ~) ~# a
- 选择PWM模式:
' Q/ r4 G0 l/ p" n, B1 G( G - PWM1模式:
3 K* d0 M/ Z' ?. \( G& l - 向上计数时,当我们 当前的 计数值 小于我们的设置阈值为有效电平,否则为无效电平,向下计数时与向上计数时相反3 Y' k5 M% }8 h C9 h0 \5 M
- PWM2模式:' S7 r1 t% l2 q _
- 与PWM1模式向上向下计数时完全相反" v; U9 \2 `9 b0 x' U2 W7 |! @
- */
, K) J# O9 o; q5 S3 } - TIM_OCInitstrcuture.TIM_OCMode = TIM_OCMode_PWM1;( M. w3 |6 y5 e4 T7 G. C
- TIM_OCInitstrcuture.TIM_OutputState = TIM_OutputState_Enable;
) i' e$ q6 H' }4 d - TIM_OCInitstrcuture.TIM_OCPolarity = TIM_OCPolarity_High; //输出电平为高,也就是有效电平为高9 @. k2 {* A3 e" ~
- TIM_OC1Init(TIM3,&TIM_OCInitstrcuture); //这里是设置利用通道1输出
9 l! E: {6 u& a% E( Z, a4 O - 2 Q% ^ A0 P3 X8 f/ P& A: \$ E
- //这里只初始化通道1,我们可以根据自己需求初始化其它通道) M" v1 _5 N5 O6 H% ^: W& O3 L% C
- B8 `7 h0 {( }# _4 [! ?
- // TIM_OC2Init(TIM3,&TIM_OCInitstrcuture);
+ b% P& T( e( v0 Y, L) k - // TIM_OC3Init(TIM3,&TIM_OCInitstrcuture);
* _$ K' |7 t3 p# t: ]2 A - // TIM_OC4Init(TIM3,&TIM_OCInitstrcuture);
# o" a' [- v$ }) q7 q
, C) x6 L( Y4 s4 m- TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能预装载寄存器
+ x0 X" `5 `# C1 { - }
/ ]8 _1 |8 n+ t L
. t# b7 m" q$ V G- //*********************主函数调用
" M, d3 @ Q% } - int main(){
: W+ ?. L% [" I - ) O _7 D: c) V9 s t' l5 X
- //因为我们单片机引脚输出电压3.3V左右,我们设置预装载值为330! I G$ z) Y% D# a# N
- MY_TIM3_PWM_Init(330,0);- C; n9 `$ o, V- T0 s1 d+ Q
- : \3 Y: S3 I8 \+ C2 p& a+ X: C; `
- //我们初始化的时候选择的是PWM1模式,当计数值小于我们的设定值100时为有效电平,这里是高电平
: x n0 e3 v6 q6 I; A - //所以对于的1通道(PA6)电压是大概就是 3.3 * (100/330) = 1V 左右,我们可以用万用表测量- [; g9 e+ B) J# s* X9 M2 q$ P
- TIM_SetCompare1(TIM3,100);
( y+ ^" W8 X0 t* x -
3 l+ I3 ^, P( D5 D) D+ B& q - while(1);
复制代码
( ]2 X4 K, r9 ~4 ^& `, b#5.定时器输入捕获
0 _6 `* z3 n2 ~
## 5.1基本介绍 3 `! y7 i7 D" i; v
※ 上面介绍了定时器的四路通道可以输出PWM,同样的我们也可以捕获该定时器这四路通道上的边沿状态(上升沿,下降沿) 6 t0 N6 v6 e& s' v9 R" f( G9 ^
※ 由此可见基本定时器也不能进行输入捕获,没有思路通道
3 p5 U, m. N! P" I" Z$ G我们可以通过输入捕获的来测量高电平脉宽时间,首先捕获到高电平,记录下改时间,然后切换为捕获低电平,得到时间 % S; M& q% {3 r6 C8 n0 _+ q z
## 5.2开发步骤 ### 输入捕获 (捕获边沿信号,上升沿和下降沿) 首先我们需要以一定的频率检测电平的跳变,然后对部分跳变(也就是部分输入的波形)进行过滤------ 这就是定时器里面的滤波器的任务
1 @+ |; s6 @) J# ?$ Q e1 g1. 指定输入滤波器时钟频率,首先是系统时钟分给定时器72Mhz,我们首先初始化定时器的时候指定了TIM_TimeBaseStructure.TIM_ClockDivision= TIM_CKD_DIV1; 没有分频,输入给滤波器的时钟频率还是72MHz,TIM_ClockDivision也可以指定为2分频或者4分频 5 _* L4 q% o" ~" e8 @
2. 波形过滤(TIM_ICFilter),这里有一个指定过滤器的参数(参考芯片手册),例如我们设置参数为0101(二进制),采样频率(fsampling)为 滤波器频率/2 = 36Mhz,N=8.当检测到一个上升沿的时候,再以fsampling频率连续8次检测到高电平才确认是一个有效的上升沿,这样可以滤除那些高电平脉宽低于8个采样周期的脉冲信号,从而达到滤高频波的效果。
- p5 W _+ S: F4 ^$ m
* U5 B7 Y# c E: v) }! d3. 配置输入分频(TIM_ICPrescaler),如果我们设置不分频,一个边沿(上升沿或者下降沿)就触发一次捕获,二分频就是两次边沿触发捕获,这里这个分频可以为1,2,4,8
0 v2 ?% V% |6 V/ k$ E2 O- //定时器输入捕获初始化
* l. W9 l" d8 ~( T( R - void MY_TIM3_Cap_Init(u16 arr,u16 psc){& q/ U2 i4 H3 n- R( d
- b( h8 A6 M r2 _ G" Q- //初始化结构体' }& h q( y$ m/ \, S# ?6 U
- TIM_ICInitTypeDef TIM_ICInitStructure;
# N" a0 _0 X& F2 O - * p& I# r1 r/ ?( M. D+ H1 }
- //1.初始化定时器 和 相关的IO口
% N2 {7 x$ P | - MY_TIM3_Init(arr,psc); ! g. N/ ?( N7 P! |/ e ^
- % F3 E% C: |3 e m
- //这里的IO口根据自己需求改成输入,我这改成下拉输入,具体代码就不展现了
" Y( m+ _0 k6 r; u - MY_TIM3_GPIO_Init();% v1 z" @ y2 |% U1 Y$ h0 z' w+ S D
- % M9 B( J v6 M3 A
- //2.初始化定时器输入捕获. }3 ]5 B6 p6 Q
- TIM_ICInitStructure.TIM_Channel = TIM_Channel_1 ; // 设置输入捕获的通道4 L# B. @6 D1 z9 I9 b1 W
-
% V* l4 X+ F& D2 X" _ - //不使用过滤器,假设我们想使用,例如上述举例使用0101
: q- b1 _) A' e) n) C7 }# \* _" w; [ - //我们就给TIM_ICFilter = 0x05 ,(0000 0101),根据上表可以知道这个值范围(0x00~0x0F)
1 |5 t1 O( o4 \. U/ A - TIM_ICInitStructure.TIM_ICFilter = 0x00;
4 o% [ L7 q9 m5 M& F W) t2 ]8 _ - 0 Q) V/ g0 T# l# _* z; t9 o
- TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //上升沿捕获
; |9 h8 p/ C7 h3 r" h+ N3 B9 a - TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //配置输入分频,这里不分频,1次检测到边沿信号就发生捕获3 ^! o. u: c% m# i" K
- * d9 O9 K1 ?8 u3 |: z) L4 c
- /*3 h4 Z4 {8 i8 p: d6 T4 I$ p7 \# q5 x' G
- 这里说一下定时器通道可以进行交叉捕获,通道1捕获通道2引脚上的边沿信号,通道2捕获通道1引脚,通道3可以捕获通道4对应引脚,...
3 y4 a( E3 n3 n( E - 但是只能相邻一对可以相互捕获,例如通道2不能捕获通道3引脚边沿信号* _, D2 H. `: b3 M. E! R
- TIM_ICSelection_DirectTI 表示直接捕获,通道1对应通道1引脚,通道2对应通道2引脚6 o) d: @; i, r$ [2 M
- TIM_ICSelection_IndirectTI 表示进行交叉捕获
5 B' Q% u+ x0 n+ L8 l - */4 o2 K) E# W. R$ q, m d8 [
- TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射捕获对应通道的引脚
" A1 I+ S- x! I5 m - TIM_ICInit(TIM3,&TIM_ICInitStructure);
/ N+ R0 K" a8 r0 N( M& U+ |% K% @ ^ -
/ ^: |. T& b! O% d - }
8 f5 ?6 Y# M! C" ` - //****************主函数
# Q" g. B7 K% D* a7 F6 ? - int main(){
3 X4 J( G/ o; y2 ~: @4 V0 @; c, T - //初始化输入捕获
& d: C, X- u. V) V - MY_TIM3_Cap_Init(1000,0);
2 G1 d) B7 E: v9 j - * c0 }9 q0 P0 z6 u+ v% v) q
- while(1){
8 ] Q2 j+ J0 I! G8 H% m - //检测是否捕获到上升沿
" Q) c# `; H. t6 V. R+ E - if(TIM_GetFlagStatus(TIM3,TIM_IT_CC1)){% Q4 L7 o# D3 i' X# n" _; ^
- TIM_ClearFlag(TIM3,TIM_IT_CC1);
# {! C$ G/ V' Y: [ - //捕获到上升沿之后的任务...6 Z% L# }6 Q1 H5 M# ~
- //一般测量高电平脉宽,我们可以先捕获上升沿再捕获下降沿( h+ e) j* ?( d5 E" J; ]/ C8 [' u
- //TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Falling); 修改为下降沿捕获. p: S, k' v9 D* m b) _# m7 a- R
- }
- }9 M& \; s0 c) \; v+ V -
; k5 h1 f5 V# J- Z/ N1 g - }) F3 o2 W$ n% D& t5 g! j$ G; i
- }
复制代码 ( }2 ~ U( D. P- x8 b, M
& H) ?) R0 l z- Q/ m f; Z) L+ a#6.定时器中断 . O+ C% \0 K# _! M. e2 D: `' ^
1.谈及到中断,我们就必须涉及到NVIC,具体关于NVIC请参考我的另外一篇,这里是直接使用,我们使能定时器3中断并且配置完抢占优先级和响应优先级之后,再在主函数中使能其更新中断和输入捕获中断 " k1 c4 I9 m: L2 S; R5 t
- //使能更新中断和输入捕获通道1的中断
4 D. J' r1 [5 q% J- }/ H# D, @ - TIM_ITConfig(TIM3,TIM_IT_Update|TIM_IT_CC1,ENABLE);
复制代码 7 `2 ?0 z/ V1 q+ I6 t0 R. R* x
4 G$ Y6 Z( r6 L* V$ i4 U* Y2 p
2.我们使用中断的一个主要目的就是能够及时处理信息,不用在主函数的while循环里面等待
& d: i! I! ^9 l& N) g1 x- //定时器3的中断处理函数
/ ?! v* ?6 r9 I# J! O - void TIM3_IRQHandler(void){
* p' |; o. v h7 u( n2 J -
2 z& N! x5 f H; ~* P - //1.判断是什么中断 {, Q. n+ V# K, E! q$ ~6 g
-
; O9 A3 l- o; h: G$ a) V5 S - // 1.1定时器更新中断2 W5 f5 i" {; y; S% Y* R/ k
- if(TIM_GetITStatus(TIM3,TIM_IT_Update)){( |" T. F; U+ u: l5 ]
- //...处理定时器更新之后任务
, y, a& v( X) e! o t* } - } q; x+ W, R1 T
- // 1.2如果是定时器 通道1的捕获中断" x% T" E, R3 y. J# H
- else if( TIM_GetITStatus(TIM3,TIM_IT_CC1) ){5 V* }) f7 }2 T% x3 U9 W/ O
- //处理输入捕获之后的任务
5 B+ \1 U2 K3 N8 B% A. S& t3 Q' V - //TIM_OC1PolarityConfig(TIM3,TIM_ICPolarity_Falling);更改为下降沿捕获
1 e2 h) \: a+ }9 H2 D/ A - }- K: A$ C6 Z( l, g/ g
-
" d$ U; y8 g* W- p - //2.最后将中断标志位都清理掉
9 L+ N. ^4 J1 Y - TIM_ClearITPendingBit(TIM3,TIM_IT_Update|TIM_IT_CC1);% d1 g2 R: T( u9 t( T) d
- }
复制代码 9 Q/ f8 {1 F& l
( ^+ J3 C$ Y/ G& s) L8 R文章出处: 智能车杂谈 4 b+ o# p( S, N [ [- Q5 [$ h
|