需求背景
# x! f$ F+ a0 M; I在使用FreeRTOS时,如果需要统计每个任务的运行时长及百分比,则需要提供一个计时基准。5 U% v- y0 T6 `3 N4 |
: J* M$ J& O7 h2 }' s1 s1 T" |. R
. N: t N5 i, w" a5 U5 a分别实现以下2个函数:
& [: h* b5 d, B* ]7 O- ]- v0 d! X! S8 r: W9 H/ A
+ {. b6 x0 N9 @4 C$ O' n
void vConfigureTimerForRunTimeStats( void ); /* Prototype of function that initialises the run time counter. */0 t1 v, m3 o% x
unsigned long ulGetRunTimeCounterValue( void ); /* Prototype of function that returns run time counter. */
2 n9 T% i3 ?: s' T( G1 R" X
. |/ V5 b0 U' ]* P1 a1 }; _. k& t9 j) W& O+ T2 `+ I3 y* W
% P3 g9 X4 k# g% t M( l/ {前一个函数需用初始化配置计时器;后一个函数用于在任务切换时获取当前计时值。
) R3 g" G! o; D8 S
v7 w) }9 F1 O9 a( @1 r9 ^
& V8 z; v2 W* T要求计时的频率比tick的频率要高一个数量级。目前tick为1KHz,则计时频率为10K比较合适。
# `% g, d& a' o! f7 M, C0 H" n- f
6 U7 q, L0 }2 y5 p/ C0 p
* r+ r5 |( b, R* _. ^ 8 G+ Q- d) b! A) B
方案分析- e6 |7 c4 G2 q' T' |
方案一:使用硬件定时器TIMx。3 h7 G( y. g$ P+ v x
- <font face="微软雅黑" size="3">
* K2 u/ t0 T5 u# Y8 h - void vConfigureTimerForRunTimeStats(void)
2 `4 P5 |7 z3 M# D: G* g, e5 K9 V - {
. s/ ]' r& x! W. @, b - TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;9 n1 C+ e0 r. a
-
& M1 d$ B- {2 T( U5 K - // 时钟使能
% R M& ]6 Z U: |$ I - RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
( A4 l0 \/ F2 Y1 s: e F5 u" u -
$ ^/ v0 @& W `8 ] - //定时器TIMx初始化, \+ x* v0 \# ]( V
- TIM_TimeBaseStructure.TIM_Prescaler = SystemCoreClock / 1000000 - 1; //预分频后为1MHz
5 Y( e2 a) T6 X1 M- ?; {* Z - TIM_TimeBaseStructure.TIM_Period = configTICK_RATE_HZ * 10 - 1;( ^1 F# `7 T; r- u' R9 ?& n
- TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
L1 `& u) b/ |9 g9 H( N% P - TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;1 l6 B: S/ T. M7 o. o
- TIM_TimeBaseInit(TIMER_FOR_RUN_TIME_STAT, &TIM_TimeBaseStructure);
% B q. x, `2 m$ }# W, E% u" `, | - 1 t* a+ ~; B% L; g
- TIM_Cmd(TIM3, ENABLE);; K! c' z! P/ ^) v. Z1 O, h
- }
( @; M0 j! {5 P. H+ J6 P -
5 N2 N8 D$ R {- R7 m! N: y+ H - unsigned long ulGetRunTimeCounterValue(void)
. d# q5 [# O( c9 C* c* G - {
" _* ?0 ~& b+ D0 ~: X$ ~- P& k l - return TIM_GetCounter(TIM3);" C$ l9 r! C- x: l
- }+ S% t. [% _6 I* E# P2 U" \
- </font>
复制代码 问题:STM32F103的硬件定时器是16位的。最大计数值为64K,当计数频率为40K时,只需要1秒多一点就溢出了。
" {. X ~! E/ @; g. E; M: X+ i6 X. b
, t% h" c+ Y% Y6 q. J# G) `
方案二:使用2个硬件定时器级联,组成一个32位计数器4 ^# \1 _4 r8 |9 m8 k
% z) o# @5 @, Y1 O$ e' M1 p
* I5 f c2 L4 z, U: F: s, c
方案三:使用os的tick
( w5 @ y7 n7 C9 x4 w代码如下。
, }# D. J" {# S$ O- <font face="微软雅黑" size="3">{</font>
复制代码 问题:精度较低。
. N- A; U: E5 f1 a8 f n8 t; T
. n4 k6 G l" e& X7 R5 i, o8 S0 _. Y4 M! x
方案四:使用RTC作为计数器7 I7 d% p8 T, O, L; t
具体如下。# E6 e# N+ [4 V& ~
4 U9 D h9 M3 z1 p$ O% U
. A, r+ V) n2 [: H
使用RTC作为计数器
) K/ ~* F5 c% o在STM32F103中,RTC的本质并不是一个实时时钟模块,只是一个计数器而已。可以配置为每秒加一,然后软件将秒数转换为日历时间(类似于unix中,以1970-1-1为起点的秒计数)。1 _) F5 }4 @* z, c* H0 w0 i
- @0 e6 u9 h+ |! ?2 O
5 q, B0 p+ A7 {, @现在不需要时钟了,就把它用于一个10KHz的计数器。最大可计时长为4G/10K = 400K秒,约为100多个小时。: k3 G0 y9 i& m5 W' X& U+ s5 L
0 u9 a% C% J: b7 Y* E- q
6 a$ ~) r5 L8 v+ t4 t" ?+ N. t7 LRTC的时钟源:使用内部低速时钟LSI。然后再4分频,即可得到10KHz。: a. K: \4 W! ]$ V/ H
- ~: E7 T0 v C" b# n1 |; q: g% `+ p+ m
# p* L3 ]& J" k9 _
3 M% z" T0 g' ^& p- h$ @9 o. {# L8 n, {7 m9 N
RTC工作时需要配置以下几个寄存器:9 G3 I( g8 [6 z7 Z& t5 q9 {
使能PWR和BKP模块时钟6 b" k( R! B7 A7 `2 `) ]# e' i/ a
; r4 h# \( f x' R. R$ @
5 w+ P' x& L. T' ~& _% c: ^0 A
: ~2 p+ H. f0 k* c* t( s: F/ |# m
Disable Backup Domain write:
4 v( S" S! O6 y7 A7 K
& q& C$ l, ^" e1 P& B/ F: z
8 {( ^1 X9 Y1 N: N9 a z8 t8 h' S3 O
3 q0 L3 w; X1 O; O4 L/ p4 g w, ]开启LSI:
2 i( t' d9 P0 D3 W' E' }9 {
0 Q3 z: R9 f* S9 L- u. |( y
' l$ ?8 A9 S# q/ F6 N- q$ d
6 A ?! f8 U* G& W8 g复位备份域:' \5 @! F6 U& }) S" M9 _- v
+ M/ P8 V' I$ ^9 o! V& z7 ?
7 d/ u% \9 w/ G先将BDRST设置为1,再设置为0。否则,不能更改RTCSEL等参数。
+ Q% \) R& J! l( b& K
% N' J' k- o( Z& z8 r
7 g, ?: f; t, b6 ^* g! I+ O7 ]1 m0 N5 c" H: _
参见手册:6.3.9 备份域控制寄存器 (RCC_BDCR)( S6 d. ^6 O* L* d* B
" ?9 f* r/ J/ p9 N) s' Y
0 x r. H& T* a8 M
注意: 备份域控制寄存器中(RCC_BDCR)的LSEON、LSEBYP、RTCSEL和RTCEN位处于备份域。因此,这些位在复位后处于写保护状态,只有在电源控制寄存器(PWR_CR)中的DBP位置’1’后才能对这些位进行改动。进一步信息请参考5.1节。这些位只能由备份域复位清除(见6.1.3节)。任何内部或外部复位都不会影响这些位。
% t7 N& ^5 p5 w6 d/ v+ `
7 M) |+ j. c1 c4 K
" |& j) Z( h v9 P
8 \5 f0 ]4 F9 _4 n1 z% H* @
RTC时钟选择LSI:3 c* g3 S3 F* ?; u% \+ @3 d4 E
4 w7 W' F& ?) D
9 e/ a# b& v$ p! j0 i F+ ^( c! R3 Z' D- ?! r; N- e
RTC时钟使能:
0 j0 A3 f. |8 B! H
$ y% n0 p0 G. Z$ Y" S
& c7 I* v- P5 Q. r5 Z
, @; ^1 P- N+ N设置分频系数:
6 g3 u/ \0 j) N8 j1 r/ |% P
! M$ m6 T6 p, m( `/ b
$ M- b, p5 f, W, G
# z8 H# O( n' Y' j) B
' K2 L% s# K; z3 V% B6 W& l% C7 y; c- [+ v5 t/ P! `, F3 ] M% d
& v1 @( a) J" z! f- Z' l9 n' `0 l' H
* {! N% E2 m2 q) U! D6 i
代码如下:
) `2 l2 I/ H# U7 }, d* z- q3 X- <font face="微软雅黑" size="3">" R3 M: i# ?. X9 p b) g. q- i$ X
- void vConfigureTimerForRunTimeStats(void)
; y8 f+ Q1 u' y+ X - {! m$ x ]5 B0 H' B5 s- {3 ?4 L+ K
- // 使能模块时钟4 q9 }6 Q. `+ p7 {( u6 [7 K/ \
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP | RCC_APB1Periph_PWR, ENABLE);" {3 w \' E1 x, y4 o
-
& T/ ?. v3 i* ^9 G: D - // 使能备份区域访问。将PWR.CR.DBP置1.
8 h' q m& [ e/ M ^; D5 F - PWR_BackupAccessCmd(ENABLE);2 g8 o/ E! p$ n0 J( l) ^
- - x3 I) |' r3 Q- {9 g9 C
- // 复位备份域(Backup Domain)
! }$ w, y; k8 P. y: E+ F$ [# b - BKP_DeInit();* e+ D. H* w& a; _- n
-
& D' y9 t i$ t% J- X# x4 h$ j# O - //启动内部低速晶振1 X [! r& g D! E1 |
- RCC_LSICmd(ENABLE);
2 o) R+ X! w% k* U" c7 ] -
?; c+ | r# @8 n. h. F, q - //等待外部低速晶振重启
) L) M; J4 h* _- o) X - while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET);9 h/ ^- L! I3 J& ~" }1 W. K" m
-
. M. _( {+ Q! f, T% K e - RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);1 c% o: L2 s; X) |' y
-
' s" S6 j; F, z1 @" C% [9 G, k z - RCC_RTCCLKCmd(ENABLE); //使能RTC时钟+ H" f1 Y7 |, v4 p& a
- ' F5 ]! D4 k/ Y+ O
- RTC_WaitForSynchro(); //等待RTC寄存器同步完成
8 u" u) @3 F7 A) N - ! g6 E5 R. `. Q7 X6 m; y- @
- RTC_WaitForLastTask(); //等待最后一次对RTC的寄存器写操作完成 P8 N* k* ]& V2 c( V3 V0 ~4 n
-
! i9 S' |! v9 X8 X' i/ d5 \' } - RTC_SetPrescaler(4); // 4分频后为10KHz! u5 q4 A* w: {( ^# N9 [
-
" H$ H. f8 u$ m& T1 I - RTC_WaitForLastTask();
* y+ b8 y3 V, m1 V9 }, y - 8 Q& v1 d! J7 x9 c/ H9 b
- }
$ T, y: u6 J4 Y$ h5 J - 3 I, H% {% o8 R
- /*-----------------------------------------------------------*/
$ G f+ J. ~( {8 p+ N -
' F; ~! D: Q% h6 j$ L - unsigned long ulGetRunTimeCounterValue(void)
/ {9 G& g/ `# c; G - {
& O6 A! d1 u( x0 x# M1 C - return RTC_GetCounter();
1 |( k! s0 X/ J, o e" ^ - }
5 x! g) f) }0 U' X' f - </font>
复制代码 ' |# E/ ^) P$ o$ B
8 i0 R( S: o) S1 M) m
|