前言
: n, b* a1 q D- n+ L, [某公司用 ST 的 STM32L476 在水表上,在开启时钟安全系统(CSS)之后,发现 MCU 重启后,LSE 无法正常启动,通过示波器看,发现 LSE 已经正常起振。这是由于他们对 CSS 的了解不够,应用错误导致,下面我们来深入了解一下 MCU 内部的时钟安全系统。4 Y! _) C c k2 \
V C1 [ L' s' A5 c3 }
时钟安全系统# d) q5 q7 x2 e
功能介绍$ E/ ~( Q% M* K! A. P( ]
时钟安全系统主要功能是检测外部时钟(HSE 和 LSE,LSE 只有在某些系列存在,如STM32L4),当外部时钟发生故障时,此振荡器将自动禁止,一个时钟故障时间将发送到高级控制定时器 TIM1 和 TIM8 的断路输出(刹车),并同时产生一个中断来向软件通知此故障(时钟系统中断,CSSI),以使 MCU 能够执行救援操作,这是一个不可屏蔽中断(NMI)。) g, w3 ~7 m, }9 P' [4 a
- N2 z5 E7 M5 P0 F
) \# J/ w6 W) L3 o3 [. q关键因素
! \1 b, \, @( Z' {# ?1:LCECSSON 必须在 LSERDY 置位后设置。
2 T& Z$ `5 F4 I; X |2 M; G2:CSS 可以工作在系统复位上(除了上电复位),因为它受控于备份域,所以要注意RCC_BDCR 上的寄存器,并不会在系统复位后被清除。
* B( L! I' G* W8 v9 D0 h/ I1 |% f3:CSS_LSE 和 RTC Tamper,TimeStamp 复用 EXIT Line19.所以不仅要使能 NVIC(复用TAMP_STAMP_IRQn),还要使能 EXIT(EXTI->IMR1 |= ((uint32_t)0x00080000))。) t6 K% a5 j; F' u
实际应用# I! O3 }+ G; C6 Y# r
1:时钟使能
+ E( ]' w) V; R( M- l# z7 X- SystemClock_Config(); //里面主要设置主时钟,LSE 的使能
复制代码
- F& d3 |& b" N3 t& Z' x2:CSS 以及中断使能* E$ w& }) U* `0 p* m
- HAL_RCCEx_EnableLSECSS_IT(); //CSS 中断使能
% m' ?" c: i l2 P - HAL_NVIC_SetPriority(TAMP_STAMP_IRQn, 0, 1); //CSS 的优先级以及 NVIC 使能* u2 a8 \) J$ g2 ^6 N9 P' Q
- HAL_NVIC_EnableIRQ(TAMP_STAMP_IRQn);/ u3 l& a$ T/ S- D
- __HAL_RTC_TAMPER_TIMESTAMP_EXTI_ENABLE_IT(); //CSS 的 EXIT 以及触发条件使能& {+ w, M& ~3 p9 f, W" E
- __HAL_RTC_TAMPER_TIMESTAMP_EXTI_ENABLE_RISING_EDGE();
复制代码 . r2 L" a' C( ^( z. |. G" [
3:CSS 的中断处理+ s# K: L, N, j2 l- N. _ ]
- void TAMP_STAMP_IRQHandler(void) //中断函数和 TAMP&STAMP 复用,在# k+ y; y+ x/ P3 s2 {" G9 q' c
- { //HAL_RCC_NMI_IRQHandler();二次检测即可
2 ^+ l' U0 b% @: B" X. } - HAL_RCC_NMI_IRQHandler();
" m2 Y% U0 k9 U# a6 A - }
复制代码- void HAL_RCC_NMI_IRQHandler(void)# T& m) Y% }9 G$ p1 Q! w0 ]: U7 s
- {
4 @6 h/ W# }" j6 C8 R0 o - /* Check RCC CSSF interrupt flag */
0 G' S; u. Q+ d( F4 o( t$ [ - if(__HAL_RCC_GET_IT(RCC_IT_CSS)|__HAL_RCC_GET_IT(RCC_IT_LSECSS)) //二次判断- S! K+ Q9 ?: y6 ~
- {
% j/ B" C" k; J7 A - /* RCC Clock Security System interrupt user callback */
/ ^0 o0 f* `4 n8 l - HAL_RCC_CSSCallback();
: f- B% s0 u& l$ [$ B" |/ H - }% ^- r+ k6 n7 b
- }
) v3 t! ?0 K1 h2 e" V - void HAL_RCC_CSSCallback(void) //中断函数处理 Y* b2 N/ S8 _6 G' e
- {
- U7 m/ G9 ~/ y; ?- V - RCC_OscInitTypeDef RCC_OscInitStruct;6 v1 t+ c/ l. o9 z
- HAL_RCCEx_DisableLSECSS();
6 u3 P+ j$ C6 Z$ J1 {$ H* @ - __HAL_RCC_ENABLE_IT(RCC_CICR_LSECSSC); //清除相关标志位
! ^) d9 j, z4 O! j; K - __HAL_RCC_ENABLE_IT(RCC_CICR_CSSC);
1 Q& k, m+ |+ J9 Z% z/ M& e# D - __HAL_RCC_CLEAR_IT(RCC_IT_CSS);6 |/ D. I' z0 C" R
- __HAL_RCC_CLEAR_IT(RCC_IT_LSECSS);
3 e# d5 `( o: H, b - __HAL_RCC_BACKUPRESET_FORCE(); //这部份最重要,当 CSS 中断触发之后,LSECSSD 被置位了,
( h# x( m+ z$ a0 ~( b+ E5 f - __HAL_RCC_BACKUPRESET_RELEASE(); //必须要在中断内复位清除, 不然会一直被触发,就算系统
s! T, k& _) K* k - //复位了,也会导致 LSE 不能置位 LSERDY* N: D6 H9 ]# X1 B# G, K4 ?
- __HAL_RTC_TAMPER_TIMESTAMP_EXTI_CLEAR_FLAG(); //清 LSE 的 EXIT 标志9 W: O. A5 S- d/ d7 S) a/ ` z
- RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI; //转为 LSI
9 Z) X7 O4 l$ |4 Z2 i6 u9 I" \ - RCC_OscInitStruct.LSIState = RCC_LSI_ON;: w* n4 H- k9 _$ e0 L
- RCC_OscInitStruct.LSEState = RCC_LSE_OFF;, T1 d6 x- ~0 I% Q4 T; m i) b' u, ]
- RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;6 a5 Q# t8 Y7 n
- if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
4 U! k' [# Q3 }1 t% f8 \9 Y ] - {
# U: B! x; {6 t$ f2 G. |& F0 Q7 s- H - Error_Handler();
2 L: F* k2 I) b7 Y - }1 l( l% G' f n1 `/ ~. d# H
- }
复制代码 , z2 a3 L# x( @% l/ o
* ?" ]- c+ m, X r5 v* {1 [, ^
|