需求背景
7 \+ D ? q% \3 i: |4 p; S/ R3 p: ^在使用FreeRTOS时,如果需要统计每个任务的运行时长及百分比,则需要提供一个计时基准。& y: B- R ?; E4 l2 @6 {
5 |4 U$ a) e! H) {% Z0 K
5 ~( @" i" F4 d9 S% ]分别实现以下2个函数:( P) s$ k y! y$ H! p
$ E' u: W ~5 B
# C8 g1 O; s( z6 C3 E
void vConfigureTimerForRunTimeStats( void ); /* Prototype of function that initialises the run time counter. */
1 H+ x" z. \. g: d8 Uunsigned long ulGetRunTimeCounterValue( void ); /* Prototype of function that returns run time counter. */
& h# Y& J J; h8 T
3 S$ M+ j3 p( _: O5 }3 d- t8 \7 r( Z. P) p" \) n% \
^8 @% }: m# ]! m% W
前一个函数需用初始化配置计时器;后一个函数用于在任务切换时获取当前计时值。
c* T8 `# ^; P0 b3 @; f7 `7 o$ R
8 g$ j! U* n8 F' v a' L$ M
2 x+ S _+ Z8 P要求计时的频率比tick的频率要高一个数量级。目前tick为1KHz,则计时频率为10K比较合适。
" k( x7 u6 _$ y, o1 a T1 {: d) G2 q# S0 e# d5 Y/ Z
& R1 y6 i7 \# @5 j/ s/ w
# `3 \! v8 E' \% _ G% [ j5 Y4 ]
方案分析
, J+ P4 f! j& @( }9 p+ F' q方案一:使用硬件定时器TIMx。/ f) s& E3 K: a1 ^' ~
- <font face="微软雅黑" size="3">
" _2 R# ^4 I( x' e& k: u9 X - void vConfigureTimerForRunTimeStats(void)) K- j; H0 _* K/ t4 f- x
- {1 s" L& y& r% C# F
- TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
- ]6 o2 k% V: n& O# l -
! K& U$ Z- q; k( o1 H* z- Y - // 时钟使能
' P: r% {+ D+ O% e7 m - RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);) g# K. F' V9 I3 r
- 5 M8 a4 {7 ~5 z' ?( x: }9 j
- //定时器TIMx初始化
C1 V0 i& S4 L8 x6 ~) w& e* r - TIM_TimeBaseStructure.TIM_Prescaler = SystemCoreClock / 1000000 - 1; //预分频后为1MHz% f# t/ D4 K* S, v! X
- TIM_TimeBaseStructure.TIM_Period = configTICK_RATE_HZ * 10 - 1;3 f# U7 |" g) w9 f
- TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;9 K: m1 a# j& b7 n
- TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
& E" x5 c- ~5 M" B: [ - TIM_TimeBaseInit(TIMER_FOR_RUN_TIME_STAT, &TIM_TimeBaseStructure);
F( e& N1 \3 L5 o1 f - & O( @; k" Y1 i* A( h' Y) H
- TIM_Cmd(TIM3, ENABLE);% \, Y; }4 k4 f: X& ^. C& N
- }
, }" a2 Q- J# m$ {( p- S -
8 \' y, W1 d! b8 l - unsigned long ulGetRunTimeCounterValue(void)
?) U/ z [, B* _ - {# w0 P5 l/ m) m' c) e
- return TIM_GetCounter(TIM3);" }% j* ~4 o% P- F+ @/ T* B% Y
- }
# b2 n' N: v2 q( `2 y. v - </font>
复制代码 问题:STM32F103的硬件定时器是16位的。最大计数值为64K,当计数频率为40K时,只需要1秒多一点就溢出了。
5 Q* b a3 b0 q! d. b5 u
m0 D/ E R' s: K3 t$ S S+ ?0 y& q9 q5 O; k
方案二:使用2个硬件定时器级联,组成一个32位计数器. }# ]4 t/ N5 `. `& y' b5 C! C
w. e# J- c& `4 @9 l* l
4 c( c Q' n! F8 D3 ]( O方案三:使用os的tick- q( O _7 H0 U/ e
代码如下。
) A$ M3 o) O* r; e" w- <font face="微软雅黑" size="3">{</font>
复制代码 问题:精度较低。
6 \( f2 ]/ G7 t
" ]! _( y7 U9 h4 q
/ ~( _# H( N) g( t方案四:使用RTC作为计数器
" Q+ L5 X: [. n- q) l% s' a9 k' s具体如下。/ Q: ?" ~& f" L
8 o( W+ m) R" `
X; W' K6 k0 v- |; @使用RTC作为计数器
/ | V2 I+ }; m在STM32F103中,RTC的本质并不是一个实时时钟模块,只是一个计数器而已。可以配置为每秒加一,然后软件将秒数转换为日历时间(类似于unix中,以1970-1-1为起点的秒计数)。
+ }3 `. ~- E0 i. C% d1 d
+ U6 l0 l# q b+ P0 f h
j" g, `& E2 I& n' Z+ S# R2 M: E7 R现在不需要时钟了,就把它用于一个10KHz的计数器。最大可计时长为4G/10K = 400K秒,约为100多个小时。- u P" e- l$ c: W; ]
1 U1 a7 ~, O, }8 W' @) u+ p; l6 ]
# R$ \5 [5 ], tRTC的时钟源:使用内部低速时钟LSI。然后再4分频,即可得到10KHz。
, f( m' _# I- ?6 \
% \1 Y; A( E7 H& S& d0 j
8 [, O& W' J* Q% q# ~+ D+ P, `: ~4 x# r; \0 K
2 E6 J, q* l; c1 m: u
, [! u' ~7 R. t. E, F
RTC工作时需要配置以下几个寄存器:
. J3 M0 y- I5 t. E, C, e使能PWR和BKP模块时钟5 E; Z, O) \( i. F6 F
4 `. M! a3 T1 ^: n- _2 ^/ J4 c
! G6 L7 U8 h) d
7 v- n/ u9 I9 Y2 h7 a# iDisable Backup Domain write:, x3 a; t0 `" I- N
) b- w- w: x: u1 n) T8 {: S
* Z7 C. s0 h! m, Y
7 ]* [: `3 Q- N, m- K开启LSI:
3 P. h7 \7 b+ d: Q( \! T0 h, G
3 B! f, l0 B( N0 Z4 S% v3 `' ]8 B5 C d, C2 u X1 e+ m; x
# U2 E9 [, c: ]
复位备份域:% \& Q' ]" Q5 l7 x
* K+ ~/ i+ s& G5 L; p C( }" M7 Q- Q+ p7 V3 B6 t: X, R4 C$ r
先将BDRST设置为1,再设置为0。否则,不能更改RTCSEL等参数。5 q! `6 z6 H! x& `! ]$ y8 E
4 l! W$ e+ d) K+ ~( V/ C5 _: n' ?
7 L7 L0 e, x* h2 V E
" I& }* d7 N5 o" a参见手册:6.3.9 备份域控制寄存器 (RCC_BDCR)
7 q/ | T' m- ~( y0 C$ P1 U$ K3 L3 h: ~- _+ k
; [* M; a. X6 I9 V4 |
注意: 备份域控制寄存器中(RCC_BDCR)的LSEON、LSEBYP、RTCSEL和RTCEN位处于备份域。因此,这些位在复位后处于写保护状态,只有在电源控制寄存器(PWR_CR)中的DBP位置’1’后才能对这些位进行改动。进一步信息请参考5.1节。这些位只能由备份域复位清除(见6.1.3节)。任何内部或外部复位都不会影响这些位。% m8 @7 x1 h% A1 p. Z
" Q# _, C9 A1 t& n5 Q& p3 g
9 S9 M7 r: F5 Z- e5 x7 w
' s% f8 k, v1 W9 d$ nRTC时钟选择LSI: Z9 g* I1 [, p0 c7 r$ @$ V
: \: @4 g% w0 X
8 X0 v+ x2 ~4 ]6 m" G; A3 }
0 k3 D! S$ S j( V2 G2 l" ZRTC时钟使能:
; d# f1 b" h6 {6 r2 |
; c( H, o- ]- L( K) W9 t# i$ J0 f
1 u9 S6 W z0 w6 Q5 `* n1 ?5 h. Y8 b4 G5 ^1 x
设置分频系数:
- @: e* b/ x/ |. }; {* [* Q' }9 M
% N) i1 B4 H& N' j5 x- @! d
5 g. G' {$ A* R/ ?1 a: x) ^. ]* ^; r$ H& {
' o1 ^, ]! p+ h) y
* D$ |! R: t8 B) d3 k1 j! y4 [! k1 u & K0 s7 D: r' w9 c/ n- n: t
$ s1 C- |5 g' d, D
, J! K [# y3 h- D1 Q6 ]0 z5 p5 h
代码如下:
$ R; o8 l, n7 y" \ U# Y- <font face="微软雅黑" size="3">
4 ~; I. c8 K* N* a, k! B7 { - void vConfigureTimerForRunTimeStats(void)6 ^6 o9 L& p' M6 u
- {
7 Z+ i4 ^+ z" }- o: w n. ^ - // 使能模块时钟' K) i+ t4 X$ Y. d& o- _; O
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP | RCC_APB1Periph_PWR, ENABLE);, e/ H+ V& W) A& d3 Z* |( {: p
- 3 s: M7 v' P+ J1 c
- // 使能备份区域访问。将PWR.CR.DBP置1.1 V8 U7 W4 h) I
- PWR_BackupAccessCmd(ENABLE);
( F% ?# O8 ?, ]* P0 W! P -
( f, s' |4 M/ Z8 p# s% k8 ~1 M - // 复位备份域(Backup Domain)/ O# X9 y4 _2 }8 T5 S
- BKP_DeInit();
+ J8 P4 \3 z' I, V6 c( Z -
' J% I% n# k/ q! [0 R& @ - //启动内部低速晶振
, c A. a j! A7 k - RCC_LSICmd(ENABLE);+ K' I0 R- Z( }3 y. |% Q/ W
- - Y- @2 U! | C8 H/ } X
- //等待外部低速晶振重启
' [, ?" K) m2 I - while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET);) B: H- Y' B; p6 h
-
2 q: J* _" L" F% P# l+ ]; T$ f& D- ]1 S - RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);
( g: i- g9 a4 p$ M) y - 2 j, |! s1 R. C( t% s
- RCC_RTCCLKCmd(ENABLE); //使能RTC时钟
- t& Y# _+ Z y% ^ s+ _ -
k' X& Q1 x' @1 J; Q" Z - RTC_WaitForSynchro(); //等待RTC寄存器同步完成
8 p2 r! m; h3 d- E: P# J+ K v -
w- e5 t; J, k$ \ - RTC_WaitForLastTask(); //等待最后一次对RTC的寄存器写操作完成
8 ^' E- ~8 `. Y* d6 J* u8 y - 9 v; T) F5 g; O9 N+ y0 K' l% x F
- RTC_SetPrescaler(4); // 4分频后为10KHz
+ Z/ M6 d5 C# U$ f/ w - " P% g2 t: v4 w& t. A0 S
- RTC_WaitForLastTask();" S2 V" a f$ b& V |
-
, }# v! a1 p6 w9 g4 d+ r: C6 g - }
/ |3 D0 r4 s# u$ N5 t - 9 `8 K) h2 a9 ~. a
- /*-----------------------------------------------------------*/
% e% L$ o7 K) K6 ?$ R' E) ~7 U - # N8 F& m5 l) H9 S; N- H$ X
- unsigned long ulGetRunTimeCounterValue(void)4 i+ t: |: m; D2 S% w
- {
# O( c" B( H! u6 N0 P' M - return RTC_GetCounter();5 t' T5 E) s: ?, m# J; G. _" I
- }
# k! @: k; F- a# G - </font>
复制代码
, `2 u+ }/ s1 ^" q- X1 d- \2 V! p; T% E. ^% `- o+ |
|