需求背景 L+ F( S, T, ^* b Q
在使用FreeRTOS时,如果需要统计每个任务的运行时长及百分比,则需要提供一个计时基准。9 ]0 K0 A; `- C: `. T! t8 o
, f! q# G) e" P' _2 c
/ Z+ j" f3 {6 G9 j0 a+ L分别实现以下2个函数:
, h/ D; l x5 D1 h/ G" W+ q2 C
$ e) i; ^; g" Zvoid vConfigureTimerForRunTimeStats( void ); /* Prototype of function that initialises the run time counter. */& z7 w5 Z" p: y/ o5 k* G
unsigned long ulGetRunTimeCounterValue( void ); /* Prototype of function that returns run time counter. */, d" i9 n: m' x V9 | S/ O: q
: n# j! c2 J# X5 \8 C( y
+ \" r, u- a9 T0 N, c( _
" j3 P0 F7 S, ]4 E" R前一个函数需用初始化配置计时器;后一个函数用于在任务切换时获取当前计时值。
, c" |* U c$ y' y# w7 @" N$ J/ f2 S+ ^. H
8 T' r) s5 L) T3 [3 R要求计时的频率比tick的频率要高一个数量级。目前tick为1KHz,则计时频率为10K比较合适。
8 Q5 [' d/ E% M+ ^6 G' Q* }
. P$ O \' Z7 |
5 r: Q$ D# C& K4 A `7 p$ X3 Z - _7 [" Q+ Q) \. r. M2 ?
方案分析
8 F! z# }! Z; Z4 U! }方案一:使用硬件定时器TIMx。4 P Z8 f r; J4 N* }- k
- <font face="微软雅黑" size="3">: H6 U" ]+ ]) N1 B. x
- void vConfigureTimerForRunTimeStats(void)
1 m" Y- i" ~6 z/ j G) o$ s# w. Q - {
) R( [3 `3 ]2 L$ c" U6 K: Y- X - TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
5 `( u. S5 ]' n5 K/ ]0 r1 i -
8 G% j5 J k$ U* N$ ?- @' ^ - // 时钟使能
4 n9 J' j8 e1 }' m$ o* P - RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);- M* P( u( O6 |, T( D; g6 o
-
! {) M3 X& w$ \: Q4 F - //定时器TIMx初始化- o4 M. P& A" ~0 Q/ a4 [) s, Q
- TIM_TimeBaseStructure.TIM_Prescaler = SystemCoreClock / 1000000 - 1; //预分频后为1MHz
# O1 T- P. J; I' I5 X - TIM_TimeBaseStructure.TIM_Period = configTICK_RATE_HZ * 10 - 1;8 r* Z0 Q' Q3 ?! j" J6 {4 K
- TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
6 V/ O) X6 ^/ r u9 E* v - TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;& u+ Q! H% q8 H( \2 \; Z# m8 O
- TIM_TimeBaseInit(TIMER_FOR_RUN_TIME_STAT, &TIM_TimeBaseStructure);' z; t- h/ `5 r5 H
- 1 G9 f% M7 M/ N4 X$ r
- TIM_Cmd(TIM3, ENABLE);9 @0 L: z$ T5 z4 |1 w- C
- }$ o! v( ?9 _+ g4 ~$ @8 j% `
- : O! _6 c7 ?8 H. m( Z: G- v
- unsigned long ulGetRunTimeCounterValue(void)
3 c6 g3 ]$ u& a! t - {
1 X$ P' C* @! z; o - return TIM_GetCounter(TIM3);) h% W" K3 H# E3 ?
- }
, \6 S7 w2 n& E; Z& Q - </font>
复制代码 问题:STM32F103的硬件定时器是16位的。最大计数值为64K,当计数频率为40K时,只需要1秒多一点就溢出了。4 V! s7 l- J$ n5 _" M/ ?0 O
3 V( c! f: g7 K( ]
& N* N5 `; N0 y+ Q
方案二:使用2个硬件定时器级联,组成一个32位计数器
9 Q" t. ~. e8 E) l7 o3 c) |, D7 G3 }# Q5 Y* ~1 \
# C, G4 [! H% h$ b9 P, E' D9 G7 ?
方案三:使用os的tick r0 A& j2 c, D' |* a
代码如下。" \9 I$ C: Z' Y' G o/ B$ @
- <font face="微软雅黑" size="3">{</font>
复制代码 问题:精度较低。) S1 h7 v: t2 }/ O8 h7 `6 ?
. Y, P) C/ M' ]5 b; h- D- {7 i* N
% p% ^' \! S% x. h' n方案四:使用RTC作为计数器
+ @2 V" j' Z6 H! R: B/ {' E具体如下。: @+ ~# R% Y' k: ^* z9 ^" B1 J7 N0 a
- l$ W3 ^5 C/ m- _: w$ u
/ T/ s6 X( l4 G; h- ]( F使用RTC作为计数器8 K3 D+ L6 {6 F) M8 s
在STM32F103中,RTC的本质并不是一个实时时钟模块,只是一个计数器而已。可以配置为每秒加一,然后软件将秒数转换为日历时间(类似于unix中,以1970-1-1为起点的秒计数)。6 E4 W2 g7 f- I$ L2 y- s' ?) s5 E# Z
6 w* O7 X D5 E& |$ x
" S& H/ g' P2 }# E0 f现在不需要时钟了,就把它用于一个10KHz的计数器。最大可计时长为4G/10K = 400K秒,约为100多个小时。
' ]% P) r" h# U5 q+ @4 d/ M
# x& X1 \0 a' Y/ t% a
* W5 R3 p7 \8 \RTC的时钟源:使用内部低速时钟LSI。然后再4分频,即可得到10KHz。
! H0 L- Y2 t- ^5 [6 b. s* J
$ E$ e* V' {# V, V. V( x% F/ f* t }. f' Z' f
! O# t G. I& i/ T6 g) r% ?7 R9 j7 l( u3 U J% @+ O) S% e; J! S
* l) ] D2 L+ O) C7 H" M: QRTC工作时需要配置以下几个寄存器:6 L& K' Q: W9 E5 [* W! j
使能PWR和BKP模块时钟! q2 R) \# @3 A7 [. m4 T
& [+ G$ O& \( z `5 J
* [, I* t- @/ T) ^/ F+ [# O
; e) `5 E$ u7 C9 h" A+ t5 {; d) o% KDisable Backup Domain write:
0 }. V4 p6 Q- q6 C
- M5 [+ p7 _. T' ~' _1 t3 ^8 g, ?! w1 h+ W) i, }- m8 w& U
1 G3 s4 B2 L- ?开启LSI:0 d& x7 ^& ]/ k7 C
6 _6 l% B4 B4 |1 `( C
) ^! b& O! J! y
6 a+ E0 w/ p2 t. @复位备份域:$ C5 c+ _3 J$ y1 _' r. q8 @' J
7 \# l8 E+ B- p8 C3 G; j- ^" M- g- j) H7 k7 _4 j
先将BDRST设置为1,再设置为0。否则,不能更改RTCSEL等参数。
9 [( V: ], f& A+ X% O
4 W+ Z; [. y& n2 H6 t* y; t) k
) n) O8 R7 `' q5 y N; T9 Z6 t2 z6 j, T: {. B6 j
参见手册:6.3.9 备份域控制寄存器 (RCC_BDCR)" Y7 ^$ M% r# b" X/ Q' ^2 I! M0 r
) l. p5 z& [, l% \$ Z5 {
) A' T$ L4 t/ [* x- ]" D$ t
注意: 备份域控制寄存器中(RCC_BDCR)的LSEON、LSEBYP、RTCSEL和RTCEN位处于备份域。因此,这些位在复位后处于写保护状态,只有在电源控制寄存器(PWR_CR)中的DBP位置’1’后才能对这些位进行改动。进一步信息请参考5.1节。这些位只能由备份域复位清除(见6.1.3节)。任何内部或外部复位都不会影响这些位。! t ~2 W$ J0 w7 Q) J% p
: Y" N4 M, v' ~$ k
+ I& t, J7 r1 z+ T
9 y5 D; m* i+ j
RTC时钟选择LSI:8 j2 Q5 g0 z& n
" y8 w y! j) _0 z/ X
- i, K& y& I0 r% d; p- D: O6 v; f, z$ y; G8 p9 t
RTC时钟使能:
' I+ `9 P) p) L1 u- R
! V! Y. q A5 R) I# W4 s5 A1 ]
1 \% V Y6 `" o, |: |6 T- A* y3 D) Y/ ^! O, H' I% S0 c0 a! A" n3 V
设置分频系数:
% q* o( k _& \% i
+ ?- h9 x) c5 {) y
4 ]7 }9 A8 @$ Q/ `( u; w+ { ?1 ^" A$ H3 W
4 ]" X x x. t2 q* a8 F$ P0 T) ?) S5 w5 p
q H, @. f' A 0 G+ }/ Q$ M7 U, f
, b- I' `- @- s! o& ^$ H! t* @/ y
+ ?* \0 @$ x- p代码如下:+ a+ D- e- P* A& [' k! X
- <font face="微软雅黑" size="3">: Q. ]1 K2 _8 C
- void vConfigureTimerForRunTimeStats(void)
- ?) d& `5 I. ?# v - {7 X" v; I: _+ e
- // 使能模块时钟
8 `& Q% ?! _* A+ R* s - RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP | RCC_APB1Periph_PWR, ENABLE);
- R' T& A D: y* L1 W4 L -
6 y% m$ _/ y/ T, `; @ - // 使能备份区域访问。将PWR.CR.DBP置1.
! D, F1 v# S; T M, T - PWR_BackupAccessCmd(ENABLE);
, a% }2 E, A t2 F4 a8 M" W1 q3 c -
- }$ Q- k% w: H- e - // 复位备份域(Backup Domain)
: U, N: a9 `7 K! i+ R* @ - BKP_DeInit();
1 U% X& A8 e" J# t4 j7 h R$ f -
( f2 u, L$ H% @ - //启动内部低速晶振
0 H: h) U$ |6 G# ?( \" t4 n - RCC_LSICmd(ENABLE);: _0 U# H5 @' F% g, ~
- ' n' |+ x; Q8 N; t
- //等待外部低速晶振重启
! T+ Q( v9 a- |& j: e) j7 w& a3 S - while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET);
9 \6 e* q) W5 G% r/ N% B, H -
* P+ W; {# f* h* z - RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);
8 {! a9 G4 h- O* @$ k$ D+ _0 g( A, ~0 t - 4 z1 V1 J: O1 N! k
- RCC_RTCCLKCmd(ENABLE); //使能RTC时钟
3 B$ `2 S5 k. d0 I2 z -
7 S5 }+ Y7 U6 f8 {6 ? - RTC_WaitForSynchro(); //等待RTC寄存器同步完成
2 [. N1 `" F( N! J -
0 {/ {4 ]. L! u8 a - RTC_WaitForLastTask(); //等待最后一次对RTC的寄存器写操作完成
% ~: ?( d7 O; g' z) h( w - 8 u: k% d! @* i* J( |: _7 R
- RTC_SetPrescaler(4); // 4分频后为10KHz& A( D, m' i- i1 T$ j
- % r5 ?9 T; L% T+ ]$ \1 D7 {! i
- RTC_WaitForLastTask();% c; Y, {0 H5 B3 I, [3 P: m
-
0 N9 \. R7 {% x# ^$ s - }& H( F" P" S/ a
-
: K$ V- D+ H# C9 m - /*-----------------------------------------------------------*/
# W6 K& r2 m* @3 j( v J- T, R - ( j% O" C+ J. z
- unsigned long ulGetRunTimeCounterValue(void)' K7 l% ?9 b! {& V5 h5 T) u
- {5 v5 j0 C% x% [" i) p
- return RTC_GetCounter();
2 W) {8 [: |. h3 h( z; ^2 U: m3 z! t - }
6 M. q" g5 Z! F4 L# A$ G/ S9 A - </font>
复制代码 4 r# a. c# F) [/ {0 F6 T
# I! m( t% r, v, l. I: z
|