正点原子 STM32F7 HAL库教程实验九——输入捕获实验一章,用输入捕获测量高电平的长度,输入捕获中断回调函数 HAL_TIM_IC_CaptureCallback 和更新中断回调函数 HAL_TIM_PeriodElapsedCallback 的逻辑让人费解,而且原子有的注释不是很准确。现分析如下,以备遗忘。
9 _+ ^% v0 \9 {: x* J
) e+ t, _& Y) O! q$ q1.输入捕获原理. ^' H; \) t0 v8 {4 k
用输入捕获测量一段高电平持续时间的原理如图,先设置上升沿触发中断,当捕获到上升沿时,让定时器重新计数,并设置下降沿触发中断。当下降沿到来时,记录下此时定时器的值CCRx2。在高电平期间,定时器可能有N次溢出,所以总共计数次数为N ∗ A R R + C C R x 2 N*ARR+CCRx2N∗ARR+CCRx2,再乘以一次计数的时间就得到高电平长度。
# t9 ?/ E& y, A8 H
! V/ `! D0 i- D& q6 U4 u% [1 t
5 h. J! R& u: ]/ T4 ?7 X
% v( f# i4 G {: Q2 A2.输入捕获中断回调函数
* H" L( Y8 O( ^) r8 S( W正点原子在输入捕获中断回调函数中用到了两个很重要的变量,如下,这里的注释和原子的不一样,我认为原子的注释写错了。
8 _. t# ?9 f2 y0 p
4 E" k' S; m7 S" i8 e- //捕获状态: q9 t% {% _3 }* A* N
- //位[7]:0,没有捕获到一段高电平;1,捕获到一段高电平。0 R" S0 [* o. W0 X; V
- //位[6]:0,还没捕获到上升沿;1,已经捕获到上升沿。
, U: ^0 ^5 G: E( I% y& m4 H( P - //[5:0]:捕获到上升沿后,计数器溢出的次数4 }9 o0 W5 o: L$ Q7 X2 ]* g8 n" U
- u8 TIM5CH1_CAPTURE_STA=0; //输入捕获状态 5 ]# }) X9 ]" Z4 t+ f% U4 X
- u32 TIM5CH1_CAPTURE_VAL; //发生输入捕获中断时,计数器的值
复制代码
4 b3 {, Z5 G$ j" d! [+ a& T9 [测量高电平长度一定是有两次输入捕获中断,第一次是上升沿触发,第二次是下降沿触发,所以在回调函数 HAL_TIM_IC_CaptureCallback 中要判断是上升沿触发还是下降沿触发。变量 TIM5CH1_CAPTURE_STA 位[6]的作用就是记录是否捕获到上升沿,若位[6]为1,说明当前进入中断是下降沿触发;若位[6]为0,说明当前进入中断是上升沿触发。( e! c& a: N! S D: N
+ w: m' {& b. @3 W' Y. {: L3 \# Y6 t1 C( J正点原子输入捕获中断回调函数的流程图如下
1 |$ L/ X. v8 X" p( |& ?8 j0 ^+ }7 t) b. \# o% R( h% c0 u, @
: O8 M' M2 `* R* y8 Z+ ~5 O* p. T9 s) H+ b6 Z
这里为什么要判断是否已经捕获了一段高电平呢?我的理解是,在main()函数要计算已经捕获的高电平的时间,必须要用到 TIM5CH1_CAPTURE_STA 的位[7]进行判断,如果高电平的频率很高,main()函数中还没执行到这一步,上升沿又触发了输入捕获中断,这时 TIM5CH1_CAPTURE_STA 被清零,那么main()函数中下面的代码就不会执行了。所以要判断是否已经捕获了一段高电平。( w6 w M0 F5 V4 k2 n [4 @! B9 G: ]
$ t4 g; L4 O9 V# U9 o( A
- if(TIM5CH1_CAPTURE_STA&0X80) //成功捕获到了一次高电平2 {- ^/ |6 P/ j) A/ v
- {
4 D) Y' t2 M8 C) \3 a" L9 {' i - temp=TIM5CH1_CAPTURE_STA&0X3F;
& u* \- Q [- M0 f2 S - temp*=0XFFFFFFFF; //溢出时间总和. I. s& V& ]( C2 n2 a5 V; [
- temp+=TIM5CH1_CAPTURE_VAL; //得到总的高电平时间
& |# w) s% q+ I; H6 [1 w; h& V8 w - printf("HIGH:%lld us\r\n",temp);//打印总的高电平时间
# x1 G$ _/ x$ ]5 ?' C' V) g; o - TIM5CH1_CAPTURE_STA=0; //开启下一次捕获9 P) @$ r) K! v7 o* W
- }
复制代码 7 I; |! f7 Q/ U/ L& H
现在把原子的回调函数贴上
C6 k6 a0 a/ b( e5 U# g2 n. I7 o9 X% h( n7 m+ a8 W) @ W: A
- //定时器输入捕获中断处理回调函数,该函数在HAL_TIM_IRQHandler中会被调用1 O- j. `) o8 {! ]: _8 t; \
- void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)//捕获中断发生时执行" }: T: Z @# _+ r
- {
) z0 h$ K. H: f1 R$ h - if((TIM5CH1_CAPTURE_STA&0X80)==0)//等于0则没有捕获到一段高电平) J) q, ~( v. e* U5 Y2 S: s+ j
- {( x; N: A! Z; m* k
- if(TIM5CH1_CAPTURE_STA&0X40) //等于1说明以前捕获一个上升沿,当前进入中断是下降沿触发
1 s* Q. O# P1 f* A3 s - { : e4 ]3 R9 q$ a m# L. @
- TIM5CH1_CAPTURE_STA|=0X80; //标记捕获到一段高电平6 o- N" Q! j9 t6 B: ?- V
- TIM5CH1_CAPTURE_VAL=HAL_TIM_ReadCapturedValue(&TIM5_Handler,TIM_CHANNEL_1);//获取当前计数器的值( q1 ~$ i* p; O5 R/ T
- TIM_RESET_CAPTUREPOLARITY(&TIM5_Handler,TIM_CHANNEL_1); //一定要清除原来的设置
2 Y1 {1 ^/ k5 O4 ]* S9 r; J' ^ - TIM_SET_CAPTUREPOLARITY(&TIM5_Handler,TIM_CHANNEL_1,TIM_ICPOLARITY_RISING);//配置TIM5通道1上升沿触发捕获
2 r, S9 ? e% z! i, I2 I - }# L9 t# I* m7 z( A; p" d1 G) a
- else //当前进入中断是上升沿触发( u/ P4 \6 x" _" C
- {
2 x( m8 R I: E j- X. u3 |) z& N( v' p5 R - TIM5CH1_CAPTURE_STA=0; //清空
, q# c- m! P; B4 w8 R - TIM5CH1_CAPTURE_VAL=0;1 g" a r& G" W5 w. \8 E" `
- TIM5CH1_CAPTURE_STA|=0X40; //标记捕获了上升沿& \1 f q, B, O3 [
- __HAL_TIM_DISABLE(&TIM5_Handler); //关闭定时器5
! T+ I: ?$ G% `+ ]! `; w. I7 D6 [ - __HAL_TIM_SET_COUNTER(&TIM5_Handler,0);
1 m0 d' w( I# M! K9 }' w - TIM_RESET_CAPTUREPOLARITY(&TIM5_Handler,TIM_CHANNEL_1); //一定要先清除原来的设置* N" w9 O# u3 l6 c2 Y
- TIM_SET_CAPTUREPOLARITY(&TIM5_Handler,TIM_CHANNEL_1,TIM_ICPOLARITY_FALLING);//定时器5通道1设置为下降沿触发捕获4 d! m; g# ^1 [. N- X
- __HAL_TIM_ENABLE(&TIM5_Handler);//使能定时器5
9 L$ h" C% G4 p! ^: i3 b0 D/ n+ h - } $ X6 z+ e% C# j1 a
- }
0 S7 W8 d- [! t2 {$ h7 P - }
复制代码 & ?3 C) V6 y8 Q# B6 Y9 d/ o
" Q; n k. Y/ ?* y K) T% i% d* J
3.更新中断回调函数7 h+ w w" f1 J' b3 R
理解了输入捕获中断回调函数后,理解更新中断回调函数就会容易许多。我们要明白,更新中断回调函数的目的是记录在上升沿和下降沿之间计时器溢出的次数。更新中断回调函数的流程图如下,若溢出次数达到 TIM5CH1_CAPTURE_STA 低六位能记录次数的上限,则不能继续记录高电平的长度,我们就强制结束,也就是令 TIM5CH1_CAPTURE_STA 的位[7]等于1,main()函数就会执行输出高电平的时间。/ b7 J7 _; B! U0 V M
) k8 }1 R5 o6 M; G
5 g6 @$ g; N ^% [* }- y1 Z! l下面将原子的代码贴上
1 F1 ^ {3 A! m* L: T! S' k4 o
" m% ?2 E6 A3 g- //定时器更新中断(计数溢出)中断处理回调函数,该函数在 HAL_TIM_IRQHandler 中会被调用% a% O y+ E e, k
- void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)//更新中断发生时执行
; ~# M$ M0 u) h% I9 s3 Z1 r O - { \" P" S! p' u ]/ B
- if((TIM5CH1_CAPTURE_STA&0X80)==0)//等于0说明还没捕获到一段高电平
( U. H7 A! ^; _3 H8 D - {+ C2 B* o0 A; @0 o9 K
- if(TIM5CH1_CAPTURE_STA&0X40)//等于1说明已经捕获到上升沿了
9 d% y5 G* F: ?1 U7 v8 ^% u* t - {
$ o2 g+ a- i. |+ Z9 t8 @ - if((TIM5CH1_CAPTURE_STA&0X3F)==0X3F)//高电平太长,超过能够记录溢出次数的上限/ p+ Q- M$ `& @( \; q
- {! d& L8 w) y# x) Q' y2 j; k; k! [
- TIM5CH1_CAPTURE_STA|=0X80; //强制标记捕获了一段高电平
/ F( Y8 i i2 B' F( S* r# e3 a - TIM5CH1_CAPTURE_VAL=0XFFFFFFFF; //本来要通过函数读取计数器的值,但此时溢出,计数器一定是最大值,直接赋值更快
' g9 w) Y. T+ R/ ]( t4 }4 g6 a/ ~ - }: u; o6 {5 N8 J/ V9 e5 H
- else
5 L2 S. d2 U9 Q# t7 P - {9 d1 t* j7 G* L0 g
- TIM5CH1_CAPTURE_STA++; //溢出次数加1
3 S, h B4 r2 N0 P" U - }5 b8 e2 w. V# F) b* ^+ T
- }
" t6 W& H2 N3 n( B A/ J2 s - } ) F9 J- O, B- j5 E& u/ N% E
- }
3 i7 }' h3 z. r3 z3 n
复制代码
# n6 Y! U/ i: h6 X+ s! l3 k2 I0 I* y0 N9 p/ l
" b3 x' t* u: N# k& D: }- z: g
|