需求背景
5 g4 W8 |1 p* P' N: p5 I在使用FreeRTOS时,如果需要统计每个任务的运行时长及百分比,则需要提供一个计时基准。/ M0 S4 s# J y
4 K/ H8 C2 `1 U" n0 b
8 G4 w! L0 V$ e
分别实现以下2个函数:" _0 v/ Y8 u( |6 N! g5 h
# m: m& F, c/ X: L2 `" U
# R# d9 [) B. _' e# m0 I6 {void vConfigureTimerForRunTimeStats( void ); /* Prototype of function that initialises the run time counter. */* y$ s ? V: {" o* _" ~
unsigned long ulGetRunTimeCounterValue( void ); /* Prototype of function that returns run time counter. */
; X# p/ @% O& d! ~ p! U
1 o3 N( K6 s& B, I6 n. P
4 O9 g5 f. A' d9 D1 T1 v4 V" B' D) H( K
前一个函数需用初始化配置计时器;后一个函数用于在任务切换时获取当前计时值。+ J( M! w3 O2 J- l
U2 S: A1 h. {7 X
5 ~' J& X2 L) }) z% n
要求计时的频率比tick的频率要高一个数量级。目前tick为1KHz,则计时频率为10K比较合适。2 @9 ^4 z4 b( b% Y0 c* [/ p+ A
/ _9 M* m8 Q) K( \# u
0 y- {- Q c3 a; o7 {
% I% v$ L7 x/ P! ]4 ?方案分析
9 w& p( k- `$ Y7 i' p1 y: V方案一:使用硬件定时器TIMx。
" G! Y0 v t' |: @- <font face="微软雅黑" size="3">, v/ |- g( X1 g) C* k8 V$ @8 R3 ?
- void vConfigureTimerForRunTimeStats(void). J" M6 @1 @- M5 ]! U
- {/ E$ U& ~+ h/ a, |1 U
- TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;$ o4 W* R0 B% o4 }+ K, E# E2 S I
-
# l, w3 ?, ^" U6 i5 E* ^( [ - // 时钟使能
2 N4 j+ h3 J1 B - RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
2 f& Z4 @9 ~! E9 c9 ? - , _4 B+ g2 M! ^ e# E2 ~" q
- //定时器TIMx初始化0 ^ r* g- w* `+ {9 x+ b
- TIM_TimeBaseStructure.TIM_Prescaler = SystemCoreClock / 1000000 - 1; //预分频后为1MHz# a0 }3 t J m% ~
- TIM_TimeBaseStructure.TIM_Period = configTICK_RATE_HZ * 10 - 1;& n* L( d' d& k" V$ u `
- TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;$ x" m+ G7 m8 R7 E
- TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
2 S% h' r7 l% h7 u# y - TIM_TimeBaseInit(TIMER_FOR_RUN_TIME_STAT, &TIM_TimeBaseStructure);
" t, T9 P+ D( w* `5 S -
0 c; |* ~' f) X, O, k7 R* N - TIM_Cmd(TIM3, ENABLE);
0 F# H" h; V: D - }& f$ e, Y/ G2 w0 o! |
- 7 Y- p# h X0 {7 g W u* k
- unsigned long ulGetRunTimeCounterValue(void)
6 i3 X V7 ]9 e% u: o! h. E$ `7 s - {
+ N$ G5 |" p: N - return TIM_GetCounter(TIM3);
1 [( Y. i9 w% j" t% Q. G - }
. j3 a$ t. w9 d6 W - </font>
复制代码 问题:STM32F103的硬件定时器是16位的。最大计数值为64K,当计数频率为40K时,只需要1秒多一点就溢出了。4 J, D+ I4 A( t% I7 [0 G
3 W& k E L+ f) B
) t. I0 h1 y8 H4 O3 g) S/ v$ a方案二:使用2个硬件定时器级联,组成一个32位计数器 X4 _8 |5 n! a( J4 H
y+ f3 \% k7 H% n3 j
) t* m6 ]# z+ c( W0 q2 {
方案三:使用os的tick
6 J: ^/ ~: z5 W6 d代码如下。. r$ b6 a' Q; ]' h/ L4 X. B1 W
- <font face="微软雅黑" size="3">{</font>
复制代码 问题:精度较低。
: k2 k* O1 f5 v
5 H7 W2 I: q$ m0 A9 c) m# ^: D; b x5 z" U
方案四:使用RTC作为计数器7 ]: {4 [9 B( g3 m; S
具体如下。' \8 {, g: x1 K2 r" n3 E: B
& q ]2 Q* f& |+ [
; e2 `: C6 g+ \; U- z" ^使用RTC作为计数器
9 z; |6 o$ Q! c9 r在STM32F103中,RTC的本质并不是一个实时时钟模块,只是一个计数器而已。可以配置为每秒加一,然后软件将秒数转换为日历时间(类似于unix中,以1970-1-1为起点的秒计数)。
% o' m5 i# x' F6 C' \
* f5 D2 a% W! T6 r5 w0 k W" Y( D2 d! ]
现在不需要时钟了,就把它用于一个10KHz的计数器。最大可计时长为4G/10K = 400K秒,约为100多个小时。
. U/ o2 W# H4 b5 |9 q% g# ~' I6 s6 |; m4 H
& [, E p: e8 F0 X+ `RTC的时钟源:使用内部低速时钟LSI。然后再4分频,即可得到10KHz。2 E/ Y% c3 A' C( \0 k- A: g+ Q
8 C% S5 u2 @# _" w0 l; q5 g/ p; J# p
$ r5 X# ?$ T1 O4 d0 H3 I2 Z$ Q) r& |
: [+ x! n2 j, k- D4 h% I# v& r5 S ^4 _( L
RTC工作时需要配置以下几个寄存器:9 k$ @3 J ^# m; ^, i; `' `& X
使能PWR和BKP模块时钟
1 v4 P- O5 P4 F! k9 g$ T) l3 p
+ f, z- U" v2 c
7 l1 Z- ^2 n) C6 }: j9 W- B5 d
: D9 m H- h% h, j A. d, vDisable Backup Domain write:8 y! T, J+ Y" [: G' i1 W; O. l
* \1 o% N" Q# |; I. z+ H
0 D5 v( t- E! K9 f3 \2 d4 P" \( w% o6 q, b' W/ h% K. ?
开启LSI:
: y9 _3 s0 K6 y) h# a
8 H3 V- `% o. Z* h @4 E9 B* X' h! ?; M- H7 q+ {& C: ?
; s, ^) m0 ~; F复位备份域:8 s, `4 J# k! ^. S1 L4 w0 o6 _
3 g; |0 a) T( ~6 Q9 E+ t, X
! ~/ c4 x; x+ @* E0 d1 [, Q先将BDRST设置为1,再设置为0。否则,不能更改RTCSEL等参数。, ~' w7 Y7 R) y
- l: [, z( I/ G7 V+ |& s
4 c5 Q; b; F5 O) |# J3 h
( w; n8 g+ Y# l4 e) k5 U参见手册:6.3.9 备份域控制寄存器 (RCC_BDCR)' e4 _( ]' T* e3 t2 y( C
% ]/ w d g: L k" a: A
7 l0 j$ G" n& r9 m0 y2 [+ @- X* e注意: 备份域控制寄存器中(RCC_BDCR)的LSEON、LSEBYP、RTCSEL和RTCEN位处于备份域。因此,这些位在复位后处于写保护状态,只有在电源控制寄存器(PWR_CR)中的DBP位置’1’后才能对这些位进行改动。进一步信息请参考5.1节。这些位只能由备份域复位清除(见6.1.3节)。任何内部或外部复位都不会影响这些位。. g; r' ?' y9 C* H. s
2 z- R0 N) U8 [ x5 R
4 O/ C! N. t/ n4 a- Y
4 T" e4 m: U; wRTC时钟选择LSI:
: |5 T& F& V; {' l; v
& Q, n; Y; T4 l9 W) r6 t* C+ x8 l& K3 m3 o! h: d
& }2 P8 Y: M& ^! S. T$ q
RTC时钟使能:
& K) e5 Q6 P# m& ]' R
$ M% q7 g2 {/ Z g4 v- K
: f/ ]% @. [. ]
2 }: u+ T' ?/ C4 W6 M; d/ L/ r设置分频系数:
$ M" T/ ?; C1 `$ @. |& Z) w' H
, p+ g8 E' u4 e$ }
7 y/ S& x' w! q7 U0 ?+ v* f1 S
, z& H% _. U9 k7 t
4 p( X" F$ q+ y7 V* ~% z
* |" U' e7 P$ P; N" c 3 B& R8 a" s6 ?' Q* Q2 y! s
7 n1 R* c0 z! h: [
: N# a* {! T1 f$ w6 u1 a+ A代码如下:5 h0 C( d0 ]; W$ K
- <font face="微软雅黑" size="3">
& L7 s9 G6 ?, C+ N' b! R0 q" B5 { - void vConfigureTimerForRunTimeStats(void)
7 O1 e6 o; x' d" H5 ] - {
( U- |8 v) }& G! b# B/ h - // 使能模块时钟9 I/ I% U* |. I9 R2 Q0 D
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP | RCC_APB1Periph_PWR, ENABLE);6 {- n: |( K9 J B7 q# u# A" C
-
4 i" C" e9 x `' S - // 使能备份区域访问。将PWR.CR.DBP置1.* X" t! S+ \) I F) a3 t
- PWR_BackupAccessCmd(ENABLE);
( c9 f; E+ c+ J9 ]! Z; b) K2 K, X( p - - Q N( L& V4 B2 b+ F$ ?$ p6 L! h
- // 复位备份域(Backup Domain)3 i8 {! J% L4 E% P O
- BKP_DeInit();
/ T9 c1 S) l( O* } - . A9 t% l0 [7 U* o0 h
- //启动内部低速晶振9 t$ p) P+ N. {6 n& z
- RCC_LSICmd(ENABLE);4 m& X |% \9 U
-
9 y, u, X$ L" H6 f - //等待外部低速晶振重启
- U( Y7 B: e2 U5 w* v' | - while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET);7 l. }; O1 G* Z; Z/ e
- & I- |+ h0 e( _4 Z& B" l$ t% [
- RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);, d% v6 p4 X% O, y9 F& B! s' z5 \4 C
- ! x0 l x, c$ K2 E
- RCC_RTCCLKCmd(ENABLE); //使能RTC时钟
! w* G% ?) l" a! p0 u! J6 L - * X/ X" I# e1 F
- RTC_WaitForSynchro(); //等待RTC寄存器同步完成3 ], A0 P+ _1 w% f4 g
-
( l) K' e: D6 J: j/ g3 h - RTC_WaitForLastTask(); //等待最后一次对RTC的寄存器写操作完成
) c5 Z/ z% K8 \; h -
) b6 R" D* E6 o$ L9 A# e" C9 _ - RTC_SetPrescaler(4); // 4分频后为10KHz
* d: g u3 t/ Y! D -
: e/ E6 p3 v6 \ - RTC_WaitForLastTask();7 q$ C+ R) d- _6 F, E# y/ m K
- 2 A" f) N, D( p
- }8 z6 S7 t+ z$ K- W! a; K F
-
( J3 \2 @( u* k7 _& ?. D - /*-----------------------------------------------------------*/$ O: [7 ] i: i+ f$ f! ]- Y8 Q
-
r% n" k$ ?+ V% q% K; `0 p - unsigned long ulGetRunTimeCounterValue(void)8 H, k1 e" Z" W/ r& Z6 u0 V, h8 O
- {/ I4 l5 t* C4 U
- return RTC_GetCounter();6 s( Z2 X. n, X8 e4 e
- }
7 u ]6 P5 X. }" K, m+ A& [ - </font>
复制代码
5 x: T7 M& i: `& }4 r" f" m/ j: r9 K4 I( T% }
|