你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

STM32F103的RTC模块用作32位计数器

[复制链接]
STMCU小助手 发布时间:2021-8-13 14:27
需求背景
+ \- }$ 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  B
4 ^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 ]
  1. <font face="微软雅黑" size="3">$ G7 d; D- Q, t# Y& s" O1 {
  2. void vConfigureTimerForRunTimeStats(void)( W9 p- r5 O) W! u
  3. {
    , S- b! K8 {) u
  4.         TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;) j/ t' y9 b" g. e
  5. ' \0 K' N$ y- n; F9 n5 g6 K. m
  6.         // 时钟使能" K& `6 a9 t0 a* j
  7.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
    6 d" @& y  W. A0 U, Y: |% F
  8. 2 }2 ~5 l) z) u, x% {8 H* n6 p
  9.         //定时器TIMx初始化9 n+ g* s7 {# {& ?: X5 }
  10.         TIM_TimeBaseStructure.TIM_Prescaler = SystemCoreClock / 1000000 - 1; //预分频后为1MHz, g. z& _6 j' i& p1 j
  11.         TIM_TimeBaseStructure.TIM_Period = configTICK_RATE_HZ * 10 - 1;- Z+ g4 r2 ?: [" _, j8 I' W
  12.         TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;/ s6 G' c1 ^: }* M
  13.         TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    & }9 g; k5 W# m
  14.         TIM_TimeBaseInit(TIMER_FOR_RUN_TIME_STAT, &TIM_TimeBaseStructure);
    7 H/ x# E, n- w9 {
  15. : I! {6 V) i! ]8 i/ E' ^
  16.         TIM_Cmd(TIM3, ENABLE);
    5 v) O# _: k# E
  17. }8 J/ i6 S6 S9 @; v" \6 j
  18. * ?, U  ]' `) I2 j& L! M
  19. unsigned long ulGetRunTimeCounterValue(void)% h5 M( ~! P$ {5 f
  20. {
    8 a' B! d5 z. d
  21.         return TIM_GetCounter(TIM3);. e/ X8 t; E+ C
  22. 2 C" e* o, I4 G6 S" F  l
  23. </font>
复制代码
问题:STM32F103的硬件定时器是16位的。最大计数值为64K,当计数频率为40K时,只需要1秒多一点就溢出了。, Y  e" `1 o9 U0 p

. |* ?% b/ i. w5 L2 S
3 k7 |/ K- b" q+ Q
方案二:使用2个硬件定时器级联,组成一个32位计数器! }) D  }0 f- ]( k( B/ k

3 o+ H, C8 f, D) M" v, j
6 l! `9 N0 k" {8 M
方案三:使用os的tick
/ v  S& D- a: d* Y  t6 B代码如下。
7 O2 M3 u3 }; t* A' M
  1. <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 1.png 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
2.png # 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
3.png
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 4.png
  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 5.png " 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 p
4 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
6.png
! 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.png 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$ |
8.png
; _$ [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
9.png 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
  1. <font face="微软雅黑" size="3">
    6 v) u% c0 a9 Q& z0 z
  2. void vConfigureTimerForRunTimeStats(void)6 P+ r& i3 }) o* M4 t
  3. {
    1 ~! H# R# Z. [* H% a. b5 S! _
  4.         // 使能模块时钟
    ( w, S" k7 e5 o* x4 i
  5.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP | RCC_APB1Periph_PWR, ENABLE);
    % [4 {; f9 k  X

  6. - ]! N2 ~/ A1 |( ?' A  f
  7.         // 使能备份区域访问。将PWR.CR.DBP置1.% i. ^4 ^1 c* X: q$ P# d6 ~
  8.         PWR_BackupAccessCmd(ENABLE);4 D9 l$ `  ]* h! ~: E" P
  9. 5 C& v& Y1 i1 S+ t, \/ N# b/ D
  10.         // 复位备份域(Backup Domain)
    3 Q  X5 N6 p7 |; K
  11.         BKP_DeInit();# z7 S5 C: D7 l7 [" W
  12. + e$ F4 k4 O  Z. E: A) _, Y: {
  13.         //启动内部低速晶振
    ! h4 r0 j# p1 R& }6 x2 a
  14.         RCC_LSICmd(ENABLE);+ [7 M; @. }* I2 O# N( w
  15. & D; o+ {1 w8 n6 }/ W; a
  16.         //等待外部低速晶振重启
    $ D1 A/ t4 a; _) P. e4 G
  17.         while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET);
    + v/ j; C: c; J* q$ k
  18. ! U; I6 v( v& Z# G8 Y3 F! }2 C
  19.         RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);! }7 z* O' [/ Q" q: J2 S

  20. - h1 |- u( n3 ~' A$ R' u; V$ k6 r* ?
  21.         RCC_RTCCLKCmd(ENABLE); //使能RTC时钟' o: b8 G# k( Q( ^3 w  l: e7 Z

  22. 7 q" c5 k5 V0 {  C1 s1 q
  23.         RTC_WaitForSynchro(); //等待RTC寄存器同步完成! [% c  }  K& Y* \9 \
  24. 9 K2 u3 s; D4 v4 T
  25.         RTC_WaitForLastTask(); //等待最后一次对RTC的寄存器写操作完成7 ?! u$ h. a1 f

  26. 4 ^/ d" p) _( S; R
  27.         RTC_SetPrescaler(4);        // 4分频后为10KHz& l5 g. n# S8 h9 L* s7 q' a

  28. 8 i, _( J, a6 n' G
  29.         RTC_WaitForLastTask();1 U& V8 @* q. U$ G5 D

  30. ) ?2 v$ U) c+ ?. G
  31. }1 W8 ^# F+ Z3 I9 C5 x
  32. " T; z& U4 K$ G# X, ~
  33. /*-----------------------------------------------------------*/
    1 z2 f; u; c2 y* x- h  z& g
  34. - [1 }' [& M, I6 q' a6 o  w6 X
  35. unsigned long ulGetRunTimeCounterValue(void)! d* B) W4 `% t# E
  36. {8 W! M; b& G6 B: `$ q0 y
  37.         return RTC_GetCounter();% b! i8 y( N8 c: {3 N
  38. }; G1 E2 C9 C' O
  39. </font>
复制代码
) b' P# d- Z$ F' a

# A* Q2 ^0 Z. t0 I5 P
收藏 评论0 发布时间:2021-8-13 14:27

举报

0个回答

所属标签

相似技术帖

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版