一、特征及定义
0 E% I1 O8 t$ ]0 E RTC(Real Time Clock 实时时钟):是个独立的BCD定时器/计数器。RTC 提供一个日历时钟,两个可编程闹钟中断,以及一个具有中断功能的周期性可编程唤醒标志。RTC还包含用于管理低功耗模式的自动唤醒单元。: T0 U- A$ l3 A+ X( D
1.两个32位寄存器包含二进码十进制格式(BCD)的秒,分钟,小时(12或24小时制),星期几,日期,月份和年份。此外,还可以提供二进制的亚秒值。
, H$ {- |& H( d$ a5 T4 W2.系统可以自动将月份的天数补偿为28,29(闰年),30,31天。并且还可以进行夏令时补偿。
& F( U) k& P, ?3 q3.其他32位寄存器还包含可编程的闹钟亚秒,秒,分钟,小时,星期几和日期。/ U+ L) T4 M: [8 r1 X
4.此外,还可以使用数字校准功能对晶振精度的偏差进行补偿。
: @4 [6 O; _' r4 K5.上电复位后,所有的RTC寄存器都会受到保护,以防止可能的非正常写访问。. d' ^" S9 R- c3 `& X0 k
6.RTC模块和时钟配置是在后备区域,即在系统复位或者待机模式唤醒后RTC的设置和时间维持不变,只要后备区域供电正常,RTC将一直工作下去。但是在系统复位之后会自动禁止访问后备区域和RTC,以防止意外操作,所以在设置时间之前,要先取消后备区域写保护。6 C: z/ ]8 N1 t b# p( @8 \: {
二、RTC工作原理框图( x& v6 B$ s$ W/ l7 m) v2 `
& b/ \/ C% R' H
7 J u- z% s" P6 \8 a( r; I( c% T& B) a8 {0 }" m
. J; L) b4 j' R5 ?8 |6 ^8 @; R. ?
2 f$ p& \5 z$ \
# a; l! ?5 \- d! F8 \0 ]8 y
% F# b& @" i8 l; a
6 K( }2 d6 ^ W, l) I
% s' o/ B! X! b, {1 c三、RTC代码如下:$ Q1 N5 J0 I7 ~3 Y/ I0 w
包含日历、闹钟以及周期性唤醒配置: w+ _9 B0 P+ c/ @# s
* t. V5 U/ J' g" l, _
rtc.c:1 [( R" e& \& ~7 O& S+ R) S
3 P$ d5 E/ N) Y9 c" M( C" C
- #include "rtc.h"
# _7 Z5 m7 b' g5 `8 [# h& q - #include "led.h"
( f& t1 @6 e8 Q* G/ i# G& d - #include "delay.h"5 b! b* g9 G- A! ]5 m
- #include "usart.h"
) w9 {6 o, D/ P R5 p, [ - 8 O- Z+ ]5 S0 j5 u
- NVIC_InitTypeDef NVIC_InitStructure;( Z5 {) c) m& B) P4 P
& O! k( y7 ?& g% V- //RTC时间设置
6 o4 D: j1 m/ { - //hour,min,sec:小时,分钟,秒钟9 F8 s7 z4 T4 s) a8 F9 t: J
- //ampm:@RTC_AM_PM_Definitions :RTC_H12_AM/RTC_H12_PM4 Y- L3 n* S6 h6 c
- //返回值:SUCEE(1),成功4 H0 R- }' w9 }- G: K( Y: w
- // ERROR(0),进入初始化模式失败
( F+ @# f% c; Y4 t - ErrorStatus RTC_Set_Time(u8 hour,u8 min,u8 sec,u8 ampm)* V) }+ w- ~! M; n- G0 n
- {
' b# G5 C( f. ~/ {, z* U - RTC_TimeTypeDef RTC_TimeTypeInitStructure;# @* V" H2 q: b
-
6 v' p. c5 F8 I6 t$ P4 { - RTC_TimeTypeInitStructure.RTC_Hours=hour;/ U+ Y3 K! @4 N8 ]5 p2 F
- RTC_TimeTypeInitStructure.RTC_Minutes=min;* V, ]# O+ B; j6 Y$ V3 N( D
- RTC_TimeTypeInitStructure.RTC_Seconds=sec;; W( Q; L4 {- A: s. I9 Y/ f- y$ |" @
- RTC_TimeTypeInitStructure.RTC_H12=ampm;
% f- j; n- V! R7 j - * _3 n; K9 F$ [. h" s5 V- R
- return RTC_SetTime(RTC_Format_BIN,&RTC_TimeTypeInitStructure);. p' ?; k, B+ |9 ?
- }+ m$ L" `. { _
- 5 J/ X/ @) O1 G
- & C, j8 d( y9 f! x7 `8 v8 H
- //RTC日期设置# \' t% y. N6 s
- //year,month,date:年(0~99),月(1~12),日(0~31)4 Q& d' I* M) O+ j
- //week:星期(1~7,0,非法!): j7 I$ {2 L: M+ H& k
- //返回值:SUCEE(1),成功
. K5 @0 o' ?0 Q - // ERROR(0),进入初始化模式失败
6 @$ n/ g7 {' | - ErrorStatus RTC_Set_Date(u8 year,u8 month,u8 date,u8 week)" _8 p! \9 u1 K7 t! H6 g
- {
* B6 e' K. l; {$ D& R7 \ - % N% V1 X/ S' {6 ]9 |
- RTC_DateTypeDef RTC_DateTypeInitStructure;
/ \; |6 H3 s# I3 { - RTC_DateTypeInitStructure.RTC_Date=date;
& r8 U: l+ i7 {0 v; V - RTC_DateTypeInitStructure.RTC_Month=month;
( e) T4 w0 O7 ?! { - RTC_DateTypeInitStructure.RTC_WeekDay=week;1 n) @* j q B
- RTC_DateTypeInitStructure.RTC_Year=year;
7 a d/ b. m4 ]$ s1 Z - return RTC_SetDate(RTC_Format_BIN,&RTC_DateTypeInitStructure);
# @8 v$ Z$ J; d/ Y- v1 X" U2 C - }, Z3 v4 ^ a! \' I- U# D+ C( g
8 F* b, K5 M# u) g- //RTC初始化9 ~. s+ f+ D! o4 J
- //返回值:0,初始化成功;: m4 G' W/ e& v& @' x; X& ]
- // 1,LSE开启失败;- ]& Y+ d4 B+ c" Z# [
- // 2,进入初始化模式失败;
3 K0 u- m8 _2 [& z4 N/ N5 } - u8 My_RTC_Init(void)
+ r$ j, Z N+ O! v0 o- A( K3 J5 q0 _ - {) U; g" t2 p( F( p! F. @8 e) ^; S
- RTC_InitTypeDef RTC_InitStructure;
9 o, w9 I- w( x+ ~' Y& q - u16 retry=0X1FFF;
2 R" L4 R. V5 k/ y" k% O - RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);//使能PWR时钟
3 X; P# ?/ m' `6 _. N - PWR_BackupAccessCmd(ENABLE); //使能后备寄存器访问
4 n! [ F* E: R - ( q/ c$ c: Z# Z* ?
- if(RTC_ReadBackupRegister(RTC_BKP_DR0)!=0x5050) //是否第一次配置?! s! Z5 I7 D" `) `- R$ }" R
- {
9 n5 H9 J/ k) X% w$ b/ v - RCC_LSEConfig(RCC_LSE_ON);//LSE 开启
: [5 \! {+ M2 a9 o' p0 j Q# k - while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET) //检查指定的RCC标志位设置与否,等待低速晶振就绪
+ |/ L) H \; I j - {
7 ~ z# U2 N1 L8 H! e - retry++;
" A1 i$ n! z6 j3 n - delay_ms(10);! T8 t, R# L' @& I; v
- }. C9 ^3 }1 j. X
- if(retry==0)return 1; //LSE 开启失败. ; X, D% l( k j2 {1 Z. k4 |5 s
-
0 g, ?# g- z' H - RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); //设置RTC时钟(RTCCLK),选择LSE作为RTC时钟 4 J2 I- Y) ~0 }# E
- RCC_RTCCLKCmd(ENABLE); //使能RTC时钟 & Q2 ~* j+ w9 ]' q4 w$ a6 A8 z8 V
- ' l" |0 j8 E' i
- RTC_InitStructure.RTC_AsynchPrediv = 0x7F;//RTC异步分频系数(1~0X7F)5 \* l: }9 H9 ^6 b8 G1 H: W2 U
- RTC_InitStructure.RTC_SynchPrediv = 0xFF;//RTC同步分频系数(0~7FFF)
% O( r9 z ~5 x- n, @ - RTC_InitStructure.RTC_HourFormat = RTC_HourFormat_24;//RTC设置为,24小时格式
+ J; w$ C2 `( @) f - RTC_Init(&RTC_InitStructure);
2 b4 T& b: v" d+ h) X2 B
/ L3 p+ b5 {5 [- RTC_Set_Time(23,59,56,RTC_H12_AM); //设置时间
& H* Q# s# A: I8 |6 [2 ~2 G- ]: s - RTC_Set_Date(14,5,5,1); //设置日期: R0 h) k I; h9 p4 X4 E
-
" x0 f) V% |- v0 D - RTC_WriteBackupRegister(RTC_BKP_DR0,0x5050); //标记已经初始化过了
% I: [4 a% `. I - }
9 \; N( i5 h3 ^1 D
- |# h, Z$ C) y2 C. _2 o( y/ E- return 0;
' r* e$ n: X$ O" `; p' S" B( \ j - }- ~: E D; l* F4 v) n, r
8 Q8 @; H5 g( j5 X3 e R' i1 W- //设置闹钟时间(按星期闹铃,24小时制)
9 T4 F& _, [2 g3 V9 X - //week:星期几(1~7) @ref RTC_Alarm_Definitions
0 D6 U; z& u: k$ a3 ~7 I& ^ - //hour,min,sec:小时,分钟,秒钟% T7 x, P6 S4 g
- void RTC_Set_AlarmA(u8 week,u8 hour,u8 min,u8 sec)4 [% f$ I! u7 e& h
- {
7 ] P1 A7 ^5 E+ p. C, r - EXTI_InitTypeDef EXTI_InitStructure;
! T: y! |9 h9 y* Y5 h) e1 N - RTC_AlarmTypeDef RTC_AlarmTypeInitStructure;
$ b5 l1 B2 b) Z" o# M" g - RTC_TimeTypeDef RTC_TimeTypeInitStructure;
0 [% G u+ a3 I1 A- }5 M -
$ P0 w$ i! E$ G: e - RTC_AlarmCmd(RTC_Alarm_A,DISABLE);//关闭闹钟A ) Z7 L1 s% _2 v, z# M& g
-
" [) z) H& k+ {4 O5 i - RTC_TimeTypeInitStructure.RTC_Hours=hour;//小时% k5 I8 H# _5 x6 G' S$ [$ F
- RTC_TimeTypeInitStructure.RTC_Minutes=min;//分钟
( _4 r/ d$ e$ O$ D) w6 M1 e( ` - RTC_TimeTypeInitStructure.RTC_Seconds=sec;//秒" } u* c, }9 t, k3 b2 `9 |
- RTC_TimeTypeInitStructure.RTC_H12=RTC_H12_AM;( y4 v9 [9 D4 o8 e4 E
- _$ p; f& z) ~% a% \. S" z
- RTC_AlarmTypeInitStructure.RTC_AlarmDateWeekDay=week;//星期, a- e. O) Y, Q3 \* E3 z2 J9 P
- RTC_AlarmTypeInitStructure.RTC_AlarmDateWeekDaySel=RTC_AlarmDateWeekDaySel_WeekDay;//按星期闹
/ d1 y* @) |. K. A( J" h - RTC_AlarmTypeInitStructure.RTC_AlarmMask=RTC_AlarmMask_None;//精确匹配星期,时分秒1 N, T# L% q0 o' S: b
- RTC_AlarmTypeInitStructure.RTC_AlarmTime=RTC_TimeTypeInitStructure;
9 U( i0 U# [$ ~$ q9 d - RTC_SetAlarm(RTC_Format_BIN,RTC_Alarm_A,&RTC_AlarmTypeInitStructure);8 x+ B- B7 k1 z4 Q
- ' _8 x$ v( r! M" S% R3 c
-
4 y1 U; o* V/ p - RTC_ClearITPendingBit(RTC_IT_ALRA);//清除RTC闹钟A的标志! e5 b$ L: q, S4 _# A- W7 E
- EXTI_ClearITPendingBit(EXTI_Line17);//清除LINE17上的中断标志位 ; ?' f# m5 X, K! A
- 7 s0 ~8 b; @+ q% \- I
- RTC_ITConfig(RTC_IT_ALRA,ENABLE);//开启闹钟A中断4 S* X6 j2 H9 q1 `8 g* _8 ~
- RTC_AlarmCmd(RTC_Alarm_A,ENABLE);//开启闹钟A : [5 e* v% X, @6 }$ v
-
/ y! F5 J$ P: {+ ^9 T. C& F6 ` - EXTI_InitStructure.EXTI_Line = EXTI_Line17;//LINE178 }/ T6 J- x( Y' ? d9 y
- EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//中断事件* K- P0 v) @2 ~* ~, M
- EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; //上升沿触发 & ?, P2 x4 U/ v5 F6 ~
- EXTI_InitStructure.EXTI_LineCmd = ENABLE;//使能LINE17
$ u2 V5 f: @* L# }7 } - EXTI_Init(&EXTI_InitStructure);//配置7 e5 r2 A. V* R% ? W( N
- ! b2 B- r) n% c1 q! |8 U
- NVIC_InitStructure.NVIC_IRQChannel = RTC_Alarm_IRQn; + m1 S2 r5 ]1 U; s( y2 i# A3 C
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;//抢占优先级1
# i3 V+ t5 z5 q2 D! d - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;//子优先级2; u3 B& i$ }) B* `
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能外部中断通道* C: G* W1 E+ i
- NVIC_Init(&NVIC_InitStructure);//配置4 h& D7 B4 |( h; n- h) B
- }4 s. f4 c& H' H/ Y# n T
: s# R* O& z5 P# ^8 D( J6 }- //周期性唤醒定时器设置 . Q6 S5 A$ {3 a# o" ?
- /*wksel: @ref RTC_Wakeup_Timer_Definitions' W J& {1 _8 f# v7 _1 A3 D
- #define RTC_WakeUpClock_RTCCLK_Div16 ((uint32_t)0x00000000)
$ c7 |) q! i/ Y5 Q2 j, B, k; S - #define RTC_WakeUpClock_RTCCLK_Div8 ((uint32_t)0x00000001)
, ]9 }. z* l5 Q. R' Y' J - #define RTC_WakeUpClock_RTCCLK_Div4 ((uint32_t)0x00000002)2 \! Z; G! A3 f, |
- #define RTC_WakeUpClock_RTCCLK_Div2 ((uint32_t)0x00000003)! i' L: l3 Y9 T. ~) \
- #define RTC_WakeUpClock_CK_SPRE_16bits ((uint32_t)0x00000004)" [" Z; c# P; c8 W: [+ d: s5 D
- #define RTC_WakeUpClock_CK_SPRE_17bits ((uint32_t)0x00000006)
- g0 Z. Y, S3 p' Y" [2 K - */2 K+ o! j* J+ ~0 u' ~& v
- //cnt:自动重装载值.减到0,产生中断.
7 p! X1 u+ @1 v7 l" [, ? - void RTC_Set_WakeUp(u32 wksel,u16 cnt)
, e$ z6 f0 {; U$ `+ k' a - { # P1 z5 m. a3 i0 w$ n" k- G' |( ]. }
- EXTI_InitTypeDef EXTI_InitStructure;$ `- l6 N/ W1 L, l- C* `# ^" P8 u0 w
-
3 l! J: j( d% {$ L' n0 P8 c3 Z - RTC_WakeUpCmd(DISABLE);//关闭WAKE UP% k) _5 p, W% M4 c5 _. }9 @
-
* {7 N; X. ]( W* Z; J - RTC_WakeUpClockConfig(wksel);//唤醒时钟选择
- D/ V, q/ F5 |- }1 G/ g - 5 G# j9 i: J' `0 ]
- RTC_SetWakeUpCounter(cnt);//设置WAKE UP自动重装载寄存器/ Q3 h! ]. v! {
-
4 R U j! ]* m - / `0 A/ |) h* ^8 D9 E% Q* G
- RTC_ClearITPendingBit(RTC_IT_WUT); //清除RTC WAKE UP的标志) H# \; D" M; m+ N! m+ }
- EXTI_ClearITPendingBit(EXTI_Line22);//清除LINE22上的中断标志位
; h* _! a; w; ]% T, d) [ - : r* Y3 s0 ^, S4 Q+ r7 h
- RTC_ITConfig(RTC_IT_WUT,ENABLE);//开启WAKE UP 定时器中断
, d; ?( u! x; u* p0 v/ Y - RTC_WakeUpCmd( ENABLE);//开启WAKE UP 定时器 8 n- d& R0 Q# h
-
% j5 Z7 U4 O$ @& L' x! U - EXTI_InitStructure.EXTI_Line = EXTI_Line22;//LINE22 D, [2 g- J- Q' U) r
- EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//中断事件
: `, B( y1 E! u. X8 c - EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; //上升沿触发
# |5 X: l- e4 N* I - EXTI_InitStructure.EXTI_LineCmd = ENABLE;//使能LINE22
! F7 S5 m5 K" {. _ - EXTI_Init(&EXTI_InitStructure);//配置0 V8 M) G: r7 R: n5 J( Y/ C' _6 m; o: v
+ l# X' _; q0 ^2 P- / P+ T8 L& \4 r8 E) g
- NVIC_InitStructure.NVIC_IRQChannel = RTC_WKUP_IRQn;
) F0 {) V4 s( t7 R9 M( ^; Z! U - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;//抢占优先级1
2 v. x% ]7 Q( o5 ?0 h2 \% @ - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;//子优先级2
- ]/ S2 X6 ~: l6 E - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能外部中断通道
8 |" N2 U# ]# E5 @- i& W - NVIC_Init(&NVIC_InitStructure);//配置
( h# J4 Q! ]# K - }/ B$ a: r1 {- H. S5 O+ {. U/ p
0 g- t4 o; ^$ Q/ x- a' ?- //RTC闹钟中断服务函数
* P0 z9 x& n7 b. Q2 k, x - void RTC_Alarm_IRQHandler(void)/ z9 \" g% K, M% V) E
- { ( ]7 w1 U/ A! m | a
- if(RTC_GetFlagStatus(RTC_FLAG_ALRAF)==SET)//ALARM A中断?
8 d) j' ]; X6 u7 ? - {6 k# m4 m3 [4 a% ]# i" {
- RTC_ClearFlag(RTC_FLAG_ALRAF);//清除中断标志8 ~# o0 D3 @. J8 O! M; U
- printf("ALARM A!\r\n");& A& r; R5 Z* h, E
- }
- x! H0 R5 w1 ?( Q) B - EXTI_ClearITPendingBit(EXTI_Line17); //清除中断线17的中断标志 0 a& o4 {( P; N" n& `" P+ P: V) Q
- }
0 ~% |8 D/ K9 C+ Q
. z8 E4 t. n' H- k- //RTC WAKE UP中断服务函数
, g0 Q2 m- Q( Z7 u. _! S - void RTC_WKUP_IRQHandler(void)8 ?2 n7 x. y: I1 x* B5 s
- { 4 x/ G1 r9 X" n3 }
- if(RTC_GetFlagStatus(RTC_FLAG_WUTF)==SET)//WK_UP中断?
/ n% W0 X8 o3 r - { 7 x+ ?' N9 ^, k" D' E) t
- RTC_ClearFlag(RTC_FLAG_WUTF); //清除中断标志
D: T: z4 |; b+ `4 j# | - LED1=!LED1; 3 @% W F' m6 f8 v6 }- t
- }
* y! D! {# @& v. Y x - EXTI_ClearITPendingBit(EXTI_Line22);//清除中断线22的中断标志 * q- ]% z' \* Z4 P4 [% J
- }' g8 }5 k9 X' b
复制代码 1 N6 m' ~. _" G" Z: J7 I1 v7 u
main.c+ {% @' V1 i6 D! F4 M$ i* H
$ K/ F0 R7 W+ { ?7 y2 y5 A* g% p& N1 o- #include "sys.h"
5 ^+ A1 I7 Z. G, T; |5 u - #include "delay.h" , [+ {9 i; z' F4 B
- #include "usart.h" * t6 _% o3 ^, F2 N8 E
- #include "rtc.h"+ Z7 `" k9 S' O7 a1 K3 e
- 8 p2 W7 g) r1 ?3 C
- int main(void)- z: d0 \- G1 i
- {
8 s8 C- F6 z) f; d6 g9 U - RTC_TimeTypeDef RTC_TimeStruct;8 i- h/ ]6 r& K( U F/ N
- RTC_DateTypeDef RTC_DateStruct;9 _ [: O( M: a6 Z0 {, R4 z% E
- 1 g5 a2 z, d3 h$ U+ b' \; Y( g
- u8 tbuf[40];/ O1 q0 p }2 f+ T
- u8 t=0;6 L9 h! z0 r; O" O0 K
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2 l2 T% X: @- {: F5 a& V7 @& k/ i
- delay_init(168); //初始化延时函数" W# X& X9 ]' g' ?# \& I) G* Z O
- uart_init(115200); //初始化串口波特率为115200
- }) a7 f+ @& J2 {* y - My_RTC_Init(); //初始化RTC) f4 y+ L2 ^9 c- ?/ j, _! e
- H% J# W0 v& J# m; M4 @+ D
- RTC_Set_WakeUp(RTC_WakeUpClock_CK_SPRE_16bits,0); //配置WAKE UP中断,1秒钟中断一次
" l2 b5 \. V3 X: M -
0 J, C3 }) `5 c* J, d( n1 @ - while(1) ) c. K7 l; r1 r+ y# |! z+ S+ N
- { . S$ y H' R n4 U" F
- t++;
& V) _ N! B+ b/ T5 T' R - if((t%10)==0) //每100ms更新一次显示数据% r8 E* o9 C4 }7 }* \! G
- {5 l; N# k3 {' L7 [
- RTC_GetTime(RTC_Format_BIN,&RTC_TimeStruct);
% F2 B+ {* S! J8 V4 I2 C0 L2 N$ l& m: S$ e - sprintf((char*)tbuf,"Time:%02d:%02d:%02d",RTC_TimeStruct.RTC_Hours,RTC_TimeStruct.RTC_Minutes,RTC_TimeStruct.RTC_Seconds);
# O& d. Y, k: \0 g/ S - printf("%s\r\n",tbuf);
" |, g" E) `) p - 5 b; n- h3 A7 H3 e2 V0 L' l
- RTC_GetDate(RTC_Format_BIN, &RTC_DateStruct);
# D8 L5 n6 B# ~5 { - sprintf((char*)tbuf,"Date:20%02d-%02d-%02d",RTC_DateStruct.RTC_Year,RTC_DateStruct.RTC_Month,RTC_DateStruct.RTC_Date);
! }, x: L( V7 @. b - printf("%s\r\n",tbuf); 6 v _6 U! e r: f0 E8 y) }
-
! u. ~& b, e' O C; q - sprintf((char*)tbuf,"Week:%d",RTC_DateStruct.RTC_WeekDay);
u8 `* g6 h- C( E1 _ - printf("%s\r\n",tbuf);
# J4 w' `" {/ n J% o2 `' @6 S - }
" f* I1 \/ s6 @9 A; R. c - if((t%20)==0)LED0=!LED0; //每200ms,翻转一次LED0 * h7 d v& C1 f2 M q
- delay_ms(10);: V, C! v" y& _" x
- }
9 j: _0 Z p& ` - }
复制代码
7 ~4 A" ?* L( n: C0 H% o
! T. y$ m; p$ r, m' a4 Z& v r: _' P8 Y0 N+ p3 U; }) c" R
G9 Y* ?, b- Z- p; w+ ]
) T% O5 T/ c0 ]& n: O0 m |