需求背景
+ \- }$ q3 _: G1 c, x在使用FreeRTOS时,如果需要统计每个任务的运行时长及百分比,则需要提供一个计时基准。
; G% T) v1 Z5 D) i0 u: f( R3 [- |
, Y2 H% c9 U# Q3 {8 N
9 c7 D6 D! f' x1 C, N分别实现以下2个函数:$ q1 ]! @" ?( H( e/ E
. c1 a p( G' h( K* J B4 ^4 [. w0 w5 o. m! U
void vConfigureTimerForRunTimeStats( void ); /* Prototype of function that initialises the run time counter. */
3 W) ?+ z+ E9 K7 @4 B( Tunsigned long ulGetRunTimeCounterValue( void ); /* Prototype of function that returns run time counter. */# I. H {: z8 o" w7 C
0 Y1 g2 e. V) d7 B
2 ?5 W& i' \ m: l$ p0 p
* r* M! O% I6 \. x( z* Q' s前一个函数需用初始化配置计时器;后一个函数用于在任务切换时获取当前计时值。
! f8 r5 ^8 Z/ D( v5 c' A/ H3 i( L0 O5 Z+ y1 ~: w0 Y9 l
1 z- T) y# L! D# ^! \要求计时的频率比tick的频率要高一个数量级。目前tick为1KHz,则计时频率为10K比较合适。
3 ]. ^" }3 L! h6 K# C
- B2 Y: c S. @ V# L/ n! F2 {. e; Q" d7 K+ k( V2 U' K$ j1 v
9 o- {6 R( P5 B o. L方案分析
h0 t5 B7 J9 y方案一:使用硬件定时器TIMx。1 l8 a3 T/ W+ o" [) f+ }4 ]
- <font face="微软雅黑" size="3">$ G7 d; D- Q, t# Y& s" O1 {
- void vConfigureTimerForRunTimeStats(void)( W9 p- r5 O) W! u
- {
, S- b! K8 {) u - TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;) j/ t' y9 b" g. e
- ' \0 K' N$ y- n; F9 n5 g6 K. m
- // 时钟使能" K& `6 a9 t0 a* j
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
6 d" @& y W. A0 U, Y: |% F - 2 }2 ~5 l) z) u, x% {8 H* n6 p
- //定时器TIMx初始化9 n+ g* s7 {# {& ?: X5 }
- TIM_TimeBaseStructure.TIM_Prescaler = SystemCoreClock / 1000000 - 1; //预分频后为1MHz, g. z& _6 j' i& p1 j
- TIM_TimeBaseStructure.TIM_Period = configTICK_RATE_HZ * 10 - 1;- Z+ g4 r2 ?: [" _, j8 I' W
- TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;/ s6 G' c1 ^: }* M
- TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
& }9 g; k5 W# m - TIM_TimeBaseInit(TIMER_FOR_RUN_TIME_STAT, &TIM_TimeBaseStructure);
7 H/ x# E, n- w9 { - : I! {6 V) i! ]8 i/ E' ^
- TIM_Cmd(TIM3, ENABLE);
5 v) O# _: k# E - }8 J/ i6 S6 S9 @; v" \6 j
- * ?, U ]' `) I2 j& L! M
- unsigned long ulGetRunTimeCounterValue(void)% h5 M( ~! P$ {5 f
- {
8 a' B! d5 z. d - return TIM_GetCounter(TIM3);. e/ X8 t; E+ C
- }2 C" e* o, I4 G6 S" F l
- </font>
复制代码 问题:STM32F103的硬件定时器是16位的。最大计数值为64K,当计数频率为40K时,只需要1秒多一点就溢出了。, Y e" `1 o9 U0 p
. |* ?% b/ i. w5 L2 S3 k7 |/ K- b" q+ Q
方案二:使用2个硬件定时器级联,组成一个32位计数器! }) D }0 f- ]( k( B/ k
3 o+ H, C8 f, D) M" v, j6 l! `9 N0 k" {8 M
方案三:使用os的tick
/ v S& D- a: d* Y t6 B代码如下。
7 O2 M3 u3 }; t* A' M- <font face="微软雅黑" size="3">{</font>
复制代码 问题:精度较低。
: j0 c4 A" h4 w2 Z- |' ]8 i( Q" q, h
, ^3 |- `4 o) c9 o6 u4 t7 ]( c p
方案四:使用RTC作为计数器& G- T& R/ W: n+ _9 b0 x) Z
具体如下。
: Y# Y. Q2 l2 N- r3 r0 M
8 z- K$ A6 r% x6 ^, m
: Q/ R8 p+ j5 {9 j使用RTC作为计数器3 f7 @* \7 Z2 P! o
在STM32F103中,RTC的本质并不是一个实时时钟模块,只是一个计数器而已。可以配置为每秒加一,然后软件将秒数转换为日历时间(类似于unix中,以1970-1-1为起点的秒计数)。2 X& O2 |5 c! I- G4 \' V
& G- K( J, Y& [$ S. h9 O
/ F7 E% `5 Q/ v$ c9 y" X现在不需要时钟了,就把它用于一个10KHz的计数器。最大可计时长为4G/10K = 400K秒,约为100多个小时。
( ]( L+ J* ]* ^ D9 T: z
% S$ d; k X5 } H$ c; N" [; r" H
RTC的时钟源:使用内部低速时钟LSI。然后再4分频,即可得到10KHz。
+ ~. v5 ]8 {0 a A& w& [' k
7 d0 D( Y& g, C: n& S4 y
1 [; O; V, F/ G, ^' R
4 T9 `3 f' x, r1 p$ ~$ B2 ?: W8 b% ^! e0 h. r
4 u& E! x# C' `RTC工作时需要配置以下几个寄存器:" |& D) A: R6 [
使能PWR和BKP模块时钟/ c# d7 j1 v: Q" g2 k
# R5 s1 {3 c* G5 q* O& L8 y' Z
9 B6 P- x* y6 j' M- q8 X
~( p, [8 B- O) i' W8 \9 i4 h9 lDisable Backup Domain write:% K* _ B: s! W ]4 @" F/ J
5 |7 P( Q6 g* v/ J% e3 n: L: J M( x) }2 e9 o% q* C/ O' F
# q& A2 K" B( i: d: r, k
开启LSI:
9 ?. }( m" Q$ k
P: W. T4 m' I5 }# f+ ^/ v
+ J4 w5 x) s4 H4 F3 {$ @ e/ N3 r# {( s
- x& a5 G! }6 V1 L复位备份域:" U4 `& Q" t, n" h4 c: R
w: {) j9 R8 l2 z! x4 e
* n/ p7 p' Q8 F( t先将BDRST设置为1,再设置为0。否则,不能更改RTCSEL等参数。
2 W2 l9 r3 Q$ _ \5 Y) J
" l1 ^1 S2 M& _$ Z1 R5 J
" i6 M% N, \8 z; D/ g( S6 r4 h0 d; ^/ t+ k" {3 \! r2 H
参见手册:6.3.9 备份域控制寄存器 (RCC_BDCR)" G& Z9 x8 P4 G# r8 H! Y& q% H
$ ]) u e7 ~( x* q3 J R6 p4 O: q3 ?' Z7 z4 l s" `
注意: 备份域控制寄存器中(RCC_BDCR)的LSEON、LSEBYP、RTCSEL和RTCEN位处于备份域。因此,这些位在复位后处于写保护状态,只有在电源控制寄存器(PWR_CR)中的DBP位置’1’后才能对这些位进行改动。进一步信息请参考5.1节。这些位只能由备份域复位清除(见6.1.3节)。任何内部或外部复位都不会影响这些位。, f# l3 V4 A* y
! u, q! y( o3 U% E6 t. D. V
0 J* q0 }0 d$ }7 |3 f" Z1 V
. z0 h8 P" w4 r4 `& c+ z' E# tRTC时钟选择LSI:
* j* J6 h! `, u- B1 L8 Q; ~- F' u9 t+ J
7 P% ]" _8 e, I4 U' z A6 g
1 `" `$ a& L' l# K z! Z% b& Q; b. B C
RTC时钟使能:2 m: y4 J, ]# L: L$ |
; _$ [0 b: o+ m. \" n# ]! y) g4 F! P! n5 P) @: p
. k7 |$ R, T+ p2 W
设置分频系数:9 x- @# w8 y- A5 `( X/ M" u
6 {; h8 H p/ p/ A3 i6 u3 @( k
+ y+ R E. n- Y
5 ~4 |8 s- ~1 J5 X. b% `0 ?3 `: h. k; V3 r1 N
0 E6 t6 I6 F* c7 z: l
+ h0 z5 u/ j6 a$ n
2 H' f- m& P% h
1 [4 N# |* O2 H) X( h代码如下:6 K' G4 b3 v4 t" n/ k) p
- <font face="微软雅黑" size="3">
6 v) u% c0 a9 Q& z0 z - void vConfigureTimerForRunTimeStats(void)6 P+ r& i3 }) o* M4 t
- {
1 ~! H# R# Z. [* H% a. b5 S! _ - // 使能模块时钟
( w, S" k7 e5 o* x4 i - RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP | RCC_APB1Periph_PWR, ENABLE);
% [4 {; f9 k X -
- ]! N2 ~/ A1 |( ?' A f - // 使能备份区域访问。将PWR.CR.DBP置1.% i. ^4 ^1 c* X: q$ P# d6 ~
- PWR_BackupAccessCmd(ENABLE);4 D9 l$ ` ]* h! ~: E" P
- 5 C& v& Y1 i1 S+ t, \/ N# b/ D
- // 复位备份域(Backup Domain)
3 Q X5 N6 p7 |; K - BKP_DeInit();# z7 S5 C: D7 l7 [" W
- + e$ F4 k4 O Z. E: A) _, Y: {
- //启动内部低速晶振
! h4 r0 j# p1 R& }6 x2 a - RCC_LSICmd(ENABLE);+ [7 M; @. }* I2 O# N( w
- & D; o+ {1 w8 n6 }/ W; a
- //等待外部低速晶振重启
$ D1 A/ t4 a; _) P. e4 G - while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET);
+ v/ j; C: c; J* q$ k - ! U; I6 v( v& Z# G8 Y3 F! }2 C
- RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);! }7 z* O' [/ Q" q: J2 S
-
- h1 |- u( n3 ~' A$ R' u; V$ k6 r* ? - RCC_RTCCLKCmd(ENABLE); //使能RTC时钟' o: b8 G# k( Q( ^3 w l: e7 Z
-
7 q" c5 k5 V0 { C1 s1 q - RTC_WaitForSynchro(); //等待RTC寄存器同步完成! [% c } K& Y* \9 \
- 9 K2 u3 s; D4 v4 T
- RTC_WaitForLastTask(); //等待最后一次对RTC的寄存器写操作完成7 ?! u$ h. a1 f
-
4 ^/ d" p) _( S; R - RTC_SetPrescaler(4); // 4分频后为10KHz& l5 g. n# S8 h9 L* s7 q' a
-
8 i, _( J, a6 n' G - RTC_WaitForLastTask();1 U& V8 @* q. U$ G5 D
-
) ?2 v$ U) c+ ?. G - }1 W8 ^# F+ Z3 I9 C5 x
- " T; z& U4 K$ G# X, ~
- /*-----------------------------------------------------------*/
1 z2 f; u; c2 y* x- h z& g - - [1 }' [& M, I6 q' a6 o w6 X
- unsigned long ulGetRunTimeCounterValue(void)! d* B) W4 `% t# E
- {8 W! M; b& G6 B: `$ q0 y
- return RTC_GetCounter();% b! i8 y( N8 c: {3 N
- }; G1 E2 C9 C' O
- </font>
复制代码 ) b' P# d- Z$ F' a
# A* Q2 ^0 Z. t0 I5 P |