STM32统计频率有许多种方式,有使用外部中断+定时器的方法,但是还有一种更加准确,可以计算占空比的方法。
+ ^ p* p; e9 E4 { x
; R. q& F7 j2 c- z3 E4 K即使用输入捕获来统计。
. ]' l/ T) }; m/ [9 Y8 D$ y1 N* a0 |7 E8 s7 P
输入捕获的基本原理是通过检测TIMx_CHx上的边沿信号(如上升沿或下降沿),在边沿信号发生跳变时,将当前定时器的值(TIMx_CNT)存放到对应的通道的捕获/比较寄存器(TIMx_CCRx)中,从而完成一次捕获。此外,还可以配置捕获时是否触发中断或DMA等。
0 G1 h3 ]$ X. h& L* C* o# x
" T6 O1 m4 d B5 R' n/ `我们可以配置一个外部中断的中断回调函数,在中断回调函数中,我们可以计算两个脉冲的时间差来准确的计算脉冲的周期(频率)5 C6 B" M' E& O- N5 o3 F3 T; j/ m
% r. M! A" ^! p! L- q7 D) W2 B
! c' j; H# g, N! T2 G
) t$ U4 m9 K: D$ e6 R4 m这里我使用蓝桥杯的竞赛平台,STM32G431原理图中有两个NE555产生方波连接至PA15和PB4。
$ e3 w {& i P8 b4 U. U
1 x$ y1 q; _! @& t
- c1 R: }$ A4 e z) A
. D1 n5 }& V: i( ]* b: w3 ]这里的PB4和PA15分别对应了TIM16的CHANNLE1和TIM8的CHANNLE1。
6 {: g4 }4 _/ }" \8 v
! p, F2 b, [" p8 X
2 E& p& I4 ?' n" H( p' W8 I/ j: Y( K" R6 s+ O
触发方式选择上升沿触发,这边定时器分频系数选择150-1,1us计数一次,最高计数65535即计数时间65.53ms,对应的最低频率为15HZ。
: k! @: N8 T/ o7 p- @! ]* {" V& P g8 l
最高计数频率为1us即100KHZ,滤波系数选择0(因为不是按键没有杂波)9 U! S$ r* s1 }, k1 S; w
/ v" U. ~1 o O% x3 J9 w, U3 I
: [6 F7 D g1 h& i9 ?
# o7 C0 `" U5 y% a
* S( A, ^+ z& W% |5 S开启定时器中断。
: G9 W2 [! k6 m7 ?& N" E: ?9 N( X& u3 `' `+ O' R' y. J' b, G
这里的中断触发有两种可能:第一是计数器溢出导致的中断触发,第二种是检测到上升下降沿导致的中断触发。 K t; U7 O$ d6 Q# _
6 B; z8 K: O9 T, a& g由于我们的测量频率在我们的计数频率之内,所以我们不考虑计数溢出导致的中断触发。, L+ f% O- B& [# ]0 S
* I/ m3 ]0 J6 S4 z! \
这里分频系数也不能太高,我就干脆不分频了。$ ~/ ^+ S7 e. ?' }0 X
, T) ~/ m- O& G8 Z0 H$ P
因为等会我们会清除计数器: \3 f& Q, n+ j9 [! B7 I
- J4 i9 K/ v% v9 [- struct Pre
' U/ h1 L* Q+ i" ? f( A/ A - {
0 ^: { k# [0 d( E- u - float HighTime;//高电平时间
3 I& ^6 F* H, E* Q/ m. b+ I - float LowTime;//低电平时间
0 ]3 ?5 w- X# C" L) W! S. l5 z+ S - uint8_t CapStatus;//计算是否是第二次捕获- Y) k3 U2 {* m
- float Frequent;//频率值
& |9 \1 r2 o9 ~: z. q) w - float Time;# l1 E2 y- u# Y8 {; g! m8 @% S
- uint8_t EndFlag;//捕获结束
Q- _5 [9 P1 y" Q# [: ?- D" {( I - float Duty;//占空比: d4 l5 M' I3 _4 l" q: ?
- };" n/ \6 A! s0 [1 R5 |8 Q5 O
! \5 \1 }" }( g4 A) H1 P1 _% D, h- T( c, w( ?- f7 c# O6 S( u2 M' z9 n
- ; ?6 q8 S9 P* g* S3 p3 _
- |( W* v" n. e8 V- void GetFreq(struct Pre*pre,TIM_HandleTypeDef * htim,uint32_t Channel)
7 h* Y" N, o* o; u* k: X) i' f - {5 ~( `7 m( S; l
- if(!pre->EndFlag)6 O: ^( [* c6 t
- {
9 B1 @4 z) T) E. ?5 {0 w - if(pre->CapStatus == 0) //第一个上升沿
& S$ n0 o# h! Q7 F9 ^9 m - {
3 j6 a+ ?8 f8 U) w - pre->CapStatus = 1;* Y* K; q# A8 \5 g% Q/ i
- __HAL_TIM_SET_CAPTUREPOLARITY(htim, Channel, TIM_INPUTCHANNELPOLARITY_FALLING); //设置成下降沿触发! ~# W/ O5 B! E4 j* v4 r
- __HAL_TIM_SetCounter(htim, 0); //清空定时器计数值
+ ~+ w) m3 k- k9 L' u$ R - pre->HighTime = HAL_TIM_ReadCapturedValue(htim, Channel); //由第一个上升沿设为起始位置
2 O/ [9 y/ V8 S2 f% a - }else if(pre->CapStatus == 1) //第一个下降沿$ k3 d6 b1 t4 u! l
- {
& ?6 H% m. g& |6 A3 b5 L& n! f - pre->CapStatus = 2;. N8 M0 G E2 v; s$ C
- pre->LowTime = HAL_TIM_ReadCapturedValue(htim, Channel); //低电平起始位置
$ I% u: d; S' W5 B/ Y+ _6 @ - __HAL_TIM_SET_CAPTUREPOLARITY(htim, Channel,TIM_INPUTCHANNELPOLARITY_RISING); //设置成上升沿触发
+ I2 M( h/ F6 z( H - }else if(pre->CapStatus == 2) //第二个上升沿; k, ^2 k. t$ X' G
- {* Q/ u! c. w: z: T% V
- pre->CapStatus = 0;$ x7 E$ e1 z2 f; K3 m: G
- pre->HighTime = HAL_TIM_ReadCapturedValue(htim, Channel);: h' ^9 p7 N0 _% E) C8 `; M
- //计算频率1 B! T o+ W! K! Z
- pre->Frequent = 1/pre->HighTime*100000;
; o, k9 }! X% `1 o9 x0 t - //计算占空比
0 e2 V/ o. ]0 }* a5 B& x; [1 G - pre->Duty = (float)pre->LowTime / (pre->HighTime+1);
! t7 S( ?; p: q6 G - pre->EndFlag = 1;2 j; |# M" v3 W5 P/ q, b' S" z, z3 m3 G( N
- }
, A' y; M* v, L; J( D: \6 B3 v8 H# p - }
+ u7 X! r* V) C8 \" d) Z4 h - ' {. ]2 i% B. }; R- I
6 y y+ c( F( G" |1 p$ l, |. h- }
复制代码
5 }" c/ W# `3 l9 p我们定义一个结构体存放计数值和频率值,之后写一个函数来计算频率值。; Z0 _* F& t, N8 F1 r
- H$ z' v1 c) D主要统计两个上升沿之间的时间。- `3 i* K$ [# l$ k/ f
# V0 r% C: D& i9 @- HAL_TIM_Base_Start_IT(&htim16);
; j6 X/ x2 D, ~3 H - HAL_TIM_IC_Start_IT(&htim16,TIM_CHANNEL_1);/ I- M1 c3 h! v7 j9 M0 p
- __HAL_TIM_ENABLE_IT(&htim16,TIM_IT_UPDATE); //一定要开启TIM16的更新中断
' N' s! T/ }, c: N3 ^! G - HAL_TIM_Base_Start_IT(&htim8);! R; |9 t0 b8 h% S. E
- HAL_TIM_IC_Start_IT(&htim8,TIM_CHANNEL_1);
( e0 u4 s* \3 G, h9 H# S: v - __HAL_TIM_ENABLE_IT(&htim8,TIM_IT_UPDATE); //一定要开启TIM8的更新中断
复制代码 8 n! l' ^# w7 K0 u+ j! X4 a
开启定时器计数以及使能中断。
9 g0 P* E) P; N t: k8 ]; J {/ C. P& ]" u8 m- A
- void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
7 I4 O, e5 p7 C - {
0 |$ p9 e2 r9 H# ~( _; g - if(htim==&htim8)
9 U, M! s1 [$ J - {2 z. V: w- P* l4 K8 H
- GetFreq(&pre1,htim,TIM_CHANNEL_1);
& ?% r$ O P; Q7 h4 x' v+ z - }! _& B) u/ t* ?, i9 c- X! D
- }
复制代码 8 h" U, e# l" ~* [8 Z4 s6 {
输入捕获到频率检测。
" }% A Y9 S* c/ ?
) q9 x0 [( @( }* k, {- if(pre1.EndFlag)+ O/ I; h* b' V* V, J) f2 T
- {4 S4 p0 g3 t$ r5 L" R3 e
- sprintf(pr,"FreQuent:%.2f",pre1.Frequent);
% d+ \! o4 _, X3 M6 y {, p - LCD_DisplayStringLine(Line4,pr);' W0 o8 K1 ^( ~) K: h
- pre1.EndFlag = 0;( X& Q) W1 f' ^2 g# B9 Y! t
- }
复制代码
) P' |) V& A* J8 G# m4 R如果检测结束则打印频率值。
3 p2 B% y* ]- i: r3 M7 y# c z% c8 j
3 G( B/ j( \- `: b8 h1 b
( [7 e* t+ ^6 J, i: U( S# y; a4 r& w0 l/ B3 ~/ H
, N8 \" ]: ]% t2 I& t! O
8 T# E" V$ h2 K# w; V) ^! o: P1 S! ^0 C4 P+ }" ~( L
# Z% U+ B6 A) G* g我们可以看到理论最低频率为716.41HZ4 _" e6 U. \! H1 o) ^' E( D& u
: r! J4 C4 f& m8 h; D0 g# y
9 o8 U5 c, b5 Q5 }! s0 G8 [' {- [% n
& Q3 C" `; O: `
可以看到测量最低频率为713.17HZ和理论值的差距非常的小。/ S0 L g9 @3 [2 Z3 {4 O" _; V
* r+ j ^) G S6 \) B9 F1 y
4 f5 T* q5 w# h: M! `; @( T7 S+ \
转载自:电路小白
6 N+ U F) ?) n& _8 h如有侵权请联系删除% }& c( g- J1 ^; r
" Z6 N) K+ Q# V0 t8 P* v
! E" e ]7 Z" G3 Y |