有人使用STM32G4系列的通用型TIMER基于捕获功能对外来信号进行周期及占空比的测量。他用TIM3产生频率、占空比可调的PWM输出做为被测信号。TIM4用来进行频率测量,工作在复位从模式,被测信号接到其通道2的输入脚。然后经内部边沿检测和内部滤波电路后,兵分两路分别连接到IC1和IC2。显然IC2使用直接输入模式,IC1使用间接输入模式,如下图功能框图示意。
8 o2 P5 A S/ `0 h r8 J0 D9 O- n* K" N
* O4 x- o# R) L- `
3 |( ]4 k$ F9 ^# w9 C1 [% p# T, `9 S3 l
但他发现被测信号频率较高时,误差就明显变大了。当然,他也知道,任何测量肯定是有极限的。他现在就是想知道,能否基于现有方案将测量极限拉高点。比方说,他现在测量20KHz信号时就明显误差过大,导致测算结果难以采用。是否可以将可靠的测量结果提升到25KHz或更高呢。0 W! l4 U2 X# @" n% w
6 {/ S4 _9 D7 D) s/ R- p
. ]" @# N- f8 m5 J9 r* { B
3 N' Y2 X. J; q- r
我们不妨一起看看这个问题。依然保持相同的测量方案,TIM4工作在复位从模式,TIM3输出的被测信号连接到TIM4的CH2,然后兵分两路连接都TIM4的IC1与IC2。
) y5 [- D0 \& w2 p! v( P* A
- x/ T& M( ]3 n& k+ m+ Y2 n显然,我们先要确定测量的计时起点。可以采用IC2的上沿捕获事件作为测量起点,即进入测试状态。也可以基于上沿触发信号产生定时器复位导致的更新事件加触发事件作为测量起点,我在下面就是使用后者来进行测量并组织相应代码。' R3 ?1 F7 k- Z! w: U
; N; W9 o& H4 A! a+ w
IC1的下降沿触发捕获,捕获到的计数器值存放于变量Value_1stCap【结合上图来看】。
% F( _1 a1 Y2 g9 U6 |& \: m
$ ]# K# b- r& ?; ~8 \IC2的上升沿作为TIM4的触发复位信号,且基于上升沿事件进行捕获, 捕获值存于Value_2ndCap。* L8 B1 \: ?0 u3 V6 l
* r8 v M4 ~; w' \4 G) x5 L, t
从发生复位事件到发生第2次捕获期间,对TIM4的更新事件次数进行统计,总的更新事件次数计为Total_Num_OvEvent。) ^+ A; _8 p! X4 ~
( k' x, ^3 ^/ u$ N" Z1 `; [/ Z从发生复位事件到发生第1次捕获期间,对TIM4的更新事件次数进行统计后,计为Front_Num_OvEvent。
+ {" w) W: ^% H6 m+ g
* v% ]3 j( @, [- W8 d2 N4 f% K现在使用STM32CubeMx进行配置。重点关注TIM4的配置。TIM4的时基和捕获配置参数如下:( t. y3 {6 [ M5 M( g
; Z5 n, R0 D7 { B6 f
/ q4 Y/ B# y' C3 T* m/ a( g* F* H3 E" s' |
结合上面配置我们不难看出,TIM4的溢出周期为20ms,TI2FP2作为TIM4的复位触发信号,上沿触发,同时IC2针对输入信号的上沿进行捕获。IC1针对输入信号的下沿进行捕获。+ ]! s5 v# \) {. C6 n( O
- b+ t% ?& i# G q9 r/ P5 x
至于TIM3的配置没啥特别的,就是产生PWM输出信号,在代码里将调整其PSC分频系数和ARR参数以及CCR参数,以改变其频率和占空比。选择其通道1做PWM输出。下面测试中,TIM3输出的PWM波形的占空比固定为40%。+ [; t8 f6 q8 F) `: u5 c5 m O" Q
6 L+ R! Q, w- M1 [$ m
! @; T0 F f0 m3 m3 T
0 m6 f9 V# e( w; |2 x) O' Q' o4 ^' Z
另外,STM32G4的系统主频配置为170MHz,使用HSE 时钟源。开启TIM4的中断响应使能。完成配置后创建工程。添加必要的用户初始及启动代码。$ a- W, w2 G7 N, N" I
# ^# E5 v. M, {( V' [: D- __IO uint32_t Vaule_2ndCap = 0u;8 D( e% ?# {1 O/ b4 R, W
- __IO uint32_t Vaule_1stCap = 0u;
3 m7 j& f4 ~6 Z% s
7 Y$ H8 } K" _% U& y3 x- 7 f4 X2 t8 K' T5 Q
- __IO float Signal_Cycle= 0.0f;
+ ~0 d; C: V) i; P2 D4 m1 Q - __IO float Signal_Duty = 0.0f;
% ~$ q, H6 ^* M3 C" @5 p - __IO float Signal_Freq = 0.0f;& p3 _$ I+ r0 t; D8 e
- - v& L6 A; H( {% C6 n
+ Y7 ], R# J# @; c0 @! O- __IO uint32_t Total_Num_OvEvent=0u;* ?4 V" H- O6 E( j( k9 ]
- __IO uint32_t Front_Num_OvEvent=0u;
% N7 D4 m1 i; }5 I* v
8 K! b/ t6 E, W2 U; v- n2 D- 9 c8 `/ R8 _/ I4 {: Y9 s" L
- __IO uint32_t Num_OvEvent =0u ;; g+ V* _5 V* p5 o0 \# `& O9 d) ^
- __IO uint32_t Measure_State = 0u;
- D$ a/ J* A5 d: }2 j; l- t& q( M - # M! @% X: v: ^+ i; t
- ! U" g& w/ [' V3 \5 [" B% q2 H
- __IO uint32_t Clk_Internal;
" W, K2 n% h: @! }! z) D - / O/ \ [/ x! P" H% y/ F+ D( }$ N
0 d& h x0 }" ^ A* Q- ; ~8 B( l. t2 f
- 0 X; u% ^; l+ I0 F' l$ a& v/ ^
- __HAL_TIM_CLEAR_IT(&htim4, TIM_IT_UPDATE);
3 c5 h) u! x, D5 x: U5 c; z - __HAL_TIM_ENABLE_IT(&htim4, TIM_IT_UPDATE);
4 i, K" b2 g7 ^4 q) z' j
$ `* ?; J" B5 v
: O+ F* ~0 z/ B- HAL_TIM_IC_Start_IT(&htim4, TIM_CHANNEL_2); //enable IC1 interrupt of TIM48 b) X; Y3 S$ n% n6 @
- HAL_TIM_IC_Start_IT(&htim4, TIM_CHANNEL_1); //enable IC2 interrupt of TIM4' Y! v- v: G5 `$ ?$ g& @/ W4 @
- 7 C& l# F2 Q0 I
- " G& q2 c; o) a6 {( \9 u+ C# K/ I
- __HAL_TIM_CLEAR_IT(&htim4, TIM_IT_UPDATE);4 R7 z4 o% L" F2 \$ Q% L+ [8 m; k
- __HAL_TIM_CLEAR_IT(&htim4, TIM_IT_TRIGGER);//enable Update interrupt of TIM4
$ z& y, \. ]8 N7 x8 i -
; H, }* r1 n: \6 V7 _2 z p - Clk_Internal = HAL_RCC_GetHCLKFreq(); //170MHz for G4 Series
" J1 A$ s' G9 b% n - * k0 g5 [9 ~" s0 \6 A) E7 r
- " J7 Z8 Z' ]; E( y' w# e
- Measure_State = 0x00; //initial state of Measuring
! j: B, Y5 i; A# X# J8 e4 ~7 o - Num_OvEvent = 0x00; //initial value of update count of TIM4- T4 M: q. l# R7 V( r
- 7 v& h$ ? W* P" e2 \$ w, a1 T+ n* ]
- 5 q5 Q* X4 S3 T$ \
- ; ^" Z+ [: w; j! l( g- B0 T, ~
- ' X% o8 O0 c# [- ?* u
- HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
复制代码
# R2 R! T; R9 uTIM4事件的所有中断共用同一个中断矢量入口。为了尽可能地测得较高的信号频率,中断处理代码尽量要简洁些,避免过多、过深的函数调用。
4 B& l$ J% }+ H5 m, W# F4 N! K" V j# c0 m0 w
0 g/ `# l* A- g6 q- c
* Y: X% W( _8 _, J
有关TIM4的捕获中断及更新中断的处理代码如下,包括IC1、IC2的捕获中断处理和更新中断处理代码,以及相关计算处理代码。7 q6 T+ C2 m8 \' P
2 N% \, x6 {1 ?* w2 `& i5 ~) x, f. V
! l2 o# c/ L( C4 }8 A$ ^/ j$ T! d- void HAL_TIM4_PWM_Measure_IRQ(TIM_HandleTypeDef *htim)
; V$ R8 ^9 Y7 e' k0 D, J - {
, E, W; h. |# }9 _( r - ; g! b7 y5 y0 V/ E8 I1 s
- if(__HAL_TIM_GET_FLAG(htim, TIM_FLAG_UPDATE) != RESET)
0 \& k Q1 t3 x2 _6 B: Z8 H - {
! _9 g/ R, o* H1 t: p7 }' ? - __HAL_TIM_CLEAR_IT(htim, TIM_IT_UPDATE);
- J/ n- }. m, `
: r$ R* V& a6 J7 T- if(__HAL_TIM_GET_FLAG(htim, TIM_FLAG_TRIGGER) != RESET)
7 I- J# M3 @ @9 ` - {
' m; u8 q/ R; o9 u8 C3 b- H - __HAL_TIM_CLEAR_IT(htim, TIM_IT_TRIGGER);
3 @' u! I: a7 ?! d
4 r1 N5 S& S- |( e# f# R; q- if (Measure_State == 0)
" B9 V5 ^) o/ D - ( Y- o- q: Q, w9 d/ M+ H0 b
- {
' S. I! X" _! o# s - Num_OvEvent = 0; // prepare count overflow events
% V) {8 e! J7 J7 J$ A, }, u - Measure_State = 0x01; //start measurement
5 j: l3 e9 k; ?( Q6 G( s* k, u - }( D% d2 ~7 q3 D4 m: J% u2 j
- }
$ G7 V1 O# V& r9 C
( M$ W T u8 P( D8 [- else
; h( P( E: r ~/ \4 B- J7 B - {- A/ Z1 F' s1 M* X0 G7 a% L
- if(Measure_State != 0) ; L) r$ m- t$ C5 ?$ }( s$ {" p
- {
# v8 S7 ^, y6 x+ U8 k - Num_OvEvent++;3 A6 M9 n8 j% C5 ^! A. D5 y
- } / Y+ s/ z0 m- Q1 f+ }
- }# ^4 `* d$ [; B# j1 T
- }) A4 w; u2 g$ B
+ `/ x) H5 d3 o; [- /* Capture compare 1 event */3 o7 B& T5 L3 j5 d. b- F4 j; _
- if(__HAL_TIM_GET_FLAG(htim, TIM_FLAG_CC1) != RESET) e0 z, @7 t0 p; f
- {: E# l0 S' L: y! [1 [2 {
0 r+ g7 P2 Y4 @9 _- __HAL_TIM_CLEAR_IT(htim, TIM_IT_CC1); , e9 j3 B% l! y6 p. f4 Y
4 ^2 g2 q. {! L3 X5 J2 I* U% h2 f
2 q. T8 ^, P4 t% ]* f" a( p- if (Measure_State == 0x01)
% J4 w; a( F5 `; a& h: k6 ? - {
. n. w, V1 b2 M" Y9 s - Front_Num_OvEvent = Num_OvEvent;4 r" A+ H" a/ O& N2 H- Y
- Measure_State =0x02;( ^. i# B- w, I! a* h7 {. S
- } . F1 T* {0 u$ z5 R# w4 P# A, ^
- , O4 o/ g- L/ H; c8 U
- }/ S9 w+ Y2 t i( N/ g7 H2 I
- * U: {2 D. t, d7 R* _6 Q
- /* Capture compare 2 event */
& h& e! a8 g9 I - if(__HAL_TIM_GET_FLAG(htim, TIM_FLAG_CC2) != RESET)
# B f, I/ ]$ b0 o3 [- l - {
6 e9 s+ [% ~$ z" P3 H4 b - __HAL_TIM_CLEAR_IT(htim, TIM_IT_CC2);
9 i3 e; g( k! c A - / h0 V- J0 \5 t
1 \5 @6 N- k; w- if(Measure_State == 0x02)* x! K0 |% Y4 T! \& @7 c
- {1 i/ g y8 a: h* [& ]- n
- Measure_State =0x03;
& D: T; M# Y, o7 T1 {- m9 B4 J+ D/ ? - Total_Num_OvEvent = Num_OvEvent;+ R, K) X0 T, ~% O% x7 ^& @/ y8 }0 t
- HAL_TIM4_IC_CaptureCallback(htim); //go to calculate pulse width and duty
2 D ~7 N- J4 p! j7 Z; p - ) X6 Y1 ~8 b$ k6 t* I
- }
1 X: f- w" L; Q( E - 2 R% D- b1 d7 R; I8 l! {
- } * A& N; t4 {2 M: g# z
- 3 n" ^6 D! Y! F& ]* L& Q) ^
- * J- q- `6 I8 C# ]9 a2 [9 Z; o4 {
- }# E8 o' l3 Y) c7 J% c3 x- }. b
$ d4 _0 v3 F4 \) J. F- void HAL_TIM4_IC_CaptureCallback(TIM_HandleTypeDef *htim)3 Y8 @0 ^: ]3 I2 u! n8 G" A2 t3 ]
- {
: K- H. q% U7 w9 r7 } - ; M' s9 h3 l0 R( M/ }
- /* Get the Input Capture value */
; I$ g8 d6 Y3 V; ~4 j1 @4 M - Vaule_1stCap = TIM4->CCR1;//HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
; D+ F" J7 D( Q3 Y. ~, e U7 ] - Vaule_2ndCap = TIM4->CCR2;//HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_2);! _# ?$ ?% e0 ?
- 4 f, x5 D$ i0 s( u4 L9 j
0 u& R& |% s, j4 y) d" G- Signal_Duty =(float)((Vaule_1stCap+(Front_Num_OvEvent*(TIM4_PERIOD+1)))*100u)/ \
2 f, v" o! U) | - (float)(Vaule_2ndCap+(Total_Num_OvEvent*(TIM4_PERIOD+1)));
5 v4 ~1 T% n! H8 _ - ; G8 h/ c. `: X O
- // Clk_Internal = HAL_RCC_GetHCLKFreq();$ k0 K8 Y! n# g" M% g( U) Z- y! R7 ]* e
. S& H# [ B" Z; I- Signal_Freq =(float)(Clk_Internal/(TIM4_PSC+1))/(float)((Vaule_2ndCap+(Total_Num_OvEvent*(TIM4_PERIOD+1))));
1 e" U. e& N- e0 e - Signal_Cycle = (float) (1.000f/Signal_Freq) ;5 t6 Y6 E6 c( Y3 j) y
- ' R/ F9 ?1 ~) @4 k) q8 Q
1 f9 N' [: h( D) U7 f- Measure_State = 0x00;
4 j3 L3 i' ^ y& g) ` - Num_OvEvent = 0x00;# ^; i9 o# L* J+ Z, B% b; @
- __HAL_TIM_CLEAR_IT(htim, TIM_IT_CC1);
. j' V8 M3 M" V - __HAL_TIM_CLEAR_IT(htim, TIM_IT_CC2);
) n& a6 M4 I, L) P - __HAL_TIM_CLEAR_IT(htim, TIM_IT_UPDATE);
9 x7 c; B, q. P, X# O# V9 [# N - __HAL_TIM_CLEAR_IT(htim, TIM_IT_TRIGGER);
- F: @% d9 W$ E3 _ - / z. [& L! x% k0 @* C O* c
- }
复制代码 . `. c- w7 k ]4 x6 p
现在基于上面的配置及用户代码进行测量验证。我们发现当TIM3输出的待测信号频率达到25KHz时,测得的频率及占空比就发生了明显的偏差。见下图标注问号的第4栏信息。! s2 H. a6 v6 G2 _+ a, C( y
6 _2 x% p$ x' r W4 ^- t4 r
3 j& ` x: _$ R# G% ]1 d
即当被测频率为25KHz,占空比为40%时,测得结果是下面的样子,明显偏差过大。
1 [, d N* l7 ]! |3 H; V E/ L+ f3 e6 S
" S J o$ t) c, i0 {如果说被测信号频率进一步提升的话,误差会变得更大。那么,这种情形是否有改善机会呢?即在当前的测试方案下,可准确测量的被测信号频率是否可以提高。1 v% I2 |5 u8 X( K
6 W+ H; O# W0 `. E; R: M5 K目前的中断处理代码应该说比较精简,没有什么可以优化的余地了。% W' a8 v4 z" r
+ U; T5 I6 ]8 ?/ x聊到这里,有人可能发现了,我前面配置TIM4时,它是用来完成测量任务的,它的分频系数PSC为169。即TIM4的计数器计数时钟为1MHz,其计数分辨率为1us。不难理解,这个PSC系数应该会直接影响TIM4的计数精度,按理会影响到测量结果,尤其被测信号频率较高时。* L! C$ c2 x) X+ y
- f0 K) p7 N1 U1 @. @既然这样,我们将TIM4的PSC系数改为0,并适当调整其ARR值再来实施测量,看看结果会怎么样。见下图,TIM3输出的信号频率仍然是占空比为40% 、频率为25KHz的PWM信号。测量结果显著地明显改善,应该说此时结果是可以接受的,毕竟浮点运算也会带来些偏差。
/ U7 _, E% ]# g% e
Q+ \0 ]: @# y1 `! B
; g$ m6 L7 `3 |8 W2 H$ R) L6 _" T/ i0 d8 ?7 ?
我们不妨在保持TIM3的PSC为0的条件下,将输出频率提升到50KHz、100KHz。继续看看测量结果,见下图:2 Y" g! p% ~8 k% _; \- x
, z& V; o8 n# `) O
1 v; M& h$ w0 A& x( t% e
; B' d* x$ u' E& S从测试结果来看,当被测信号频率提升到50KHz,测量结果仍然很好,完全可以采用。即使当被测信号频率提升到100KHz时,测量结果虽发生了一些偏差,但此时的偏差相比PSC=169、被测信号为25KHz时的测试结果还要好得多。我把二者单列出来一起比较,见下图:
8 F2 Q6 h1 g5 @$ D. S4 g, N6 R- E
2 X3 D2 [: i- Q$ N& y6 L' ?0 Z$ @, z5 {1 N8 v, r. B
8 l1 s* l- N+ s. b2 q! w: P
经过上面的讨论和验证,我们知道,在使用TIMER做信号的频率及占空比的测量时,当确定好测试方案后,为了尽可能地提升可以准确测试信号的频率,一方面代码要尽可能精简、优化,另一方面,因测试TIMER的分频系数会影响测试结果的精度及准确性,此时测试TIMER的分频系数要尽可能设置小、或不做分频,这点结合具体应用场景来定。# G2 b6 p4 l' b M/ b3 @1 T
! C( f P( b8 u" W0 D
关于上面的的话题,其实还可以有很多继续延伸的空间,因时间和篇幅问题,就先聊到这里。有兴趣的话,可以基于上面配置和分享的参考源码做些快速测试验证,也欢迎进一步地讨论交流。) F$ P6 K: M* e0 Y
+ u0 i3 u6 L; _' j/ A2 s/ O5 R
: J) O% g7 k+ i6 T转载自; 茶话MCU* L. u. M# \$ X; Q' i/ ^
如有侵权请联系删除
# t7 I3 w9 p6 u! {& U5 A7 h; _2 P9 z O5 W/ ]( |
* O/ s/ }( B4 M; {+ f4 h
|