现象复现
/ H% z' o& {! J7 `; X" n7 B1 u9 @项目中一个是IAP程序,另一个是APP程序,两个程序都是使用STM32CubeIDE生成,当程序跳转到APP中并且执行到函数SystemClock_Config中的时候,在初始化锁相环调用HAL_RCC_OscConfig函数的时候返回HAL_ERROR或者直接卡死在里面。
, ?- b4 v- Y* a$ K7 w! l7 |2 p- i6 L! [. M/ L
为什么在IAP中可以初始化通过,但是在APP中却通不过,查阅STM32F4xx参考手册发现这么一段话:
% W ^7 g8 V8 Q' [' @1 m" H% Z% K7 K. a0 @1 f) x
& o. @+ J ~# \2 b* t
! ?8 }) }" w( g1 \7 y6 u那么出现这个问题的原因也就清楚了,因为在IAP中已经初始化了PLL,在APP中再次初始化就不成功。6 c8 a' z1 Y9 T7 I) L1 p p9 c6 l
( j( `8 U& i7 a
解决办法
4 d, ~8 U4 {- K* K- N我查找ST官方HAL固件库中对重复配置RCC时钟的时候,发现他是这么做的:
: Q8 T9 l% r2 v+ O1.先将时钟源选择为内部时钟- z- r& [! n6 G; b
2.初始化锁相环! _% Z4 k9 ?% Q5 Z. s2 a% `: Q! }: c
3.将时钟源在切换回外部时钟源7 D7 p/ W! G# ]: W( f
4.禁用内部高速时钟(可选步骤,如果内部高速时钟不用的话,建议禁掉)
9 x( X- n! N. k" C4 B, C/ _+ k6 K% f# ]1 X$ K
官方代码如下:
R. z1 E% G0 T/ S- G* u
8 U j, p+ M' d/ @2 D' z
" Z0 L+ I, z4 Z1 C
7 _$ }$ D% w2 g) D以下是我的示例代码: u3 A+ l- S( v9 L( E" I
1 O9 M% Y6 }8 Z& J4 W- void SystemClock_Config(void), R7 N! T/ H- b3 S1 N! q1 b
- {5 m: ^' |, n, F$ {. m" ?
- RCC_OscInitTypeDef RCC_OscInitStruct = {0};
( z# g, l6 \& Z1 f3 O. s( g( z - RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};8 m& N. m8 M- ^
- " T& h2 J4 i: O: f3 K
- /** Configure the main internal regulator output voltage
0 q8 ^, T5 N/ }1 }* s2 _ - */+ ? Z: F2 B3 b# }, I( p
- __HAL_RCC_PWR_CLK_ENABLE();; w" X# u2 R0 w% S0 m" p5 B" N
- __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
0 E9 ]& I# M) T - & W4 b: u \- C" z2 H3 |5 F7 ?' d- {
- // 先将时钟源选择为内部时钟
" L' m y7 I6 S/ p! S# ~9 H - RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK;% h. e& v1 w, }. M7 D6 @. }1 L
- RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
9 f3 ]# X2 {5 D0 T4 d* \ - if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
|' H! D f, P- Y2 f' z( f - {
0 Z& \ j! i' l, e* D - Error_Handler();
2 x1 F8 ~! B7 {: V# o' a; h% J8 ?# f4 A - }
7 X# p5 {! B# f6 x; R+ \) n
* R( B: m) F! o+ ] Y; [- // 初始化锁相环
/ o5 i* `$ ?- g' p' R( Q1 ]' v+ X) X; W - RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
! e8 \2 S1 b) l- O. X. w9 t - RCC_OscInitStruct.HSEState = RCC_HSE_ON;! t/ k* z/ l! [
- RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;( b# | R! {; ~* K1 _( x/ y) @
- RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;% s2 }7 R4 b' |
- RCC_OscInitStruct.PLL.PLLM = 4;3 G. M6 j/ W: W' |& v$ J
- RCC_OscInitStruct.PLL.PLLN = 168;
2 I% P. n, F# e/ w! O ? - RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;* i+ ~$ ]) O0 i! Q- ]; a# o% P
- RCC_OscInitStruct.PLL.PLLQ = 4;
! Y2 V- F( B o, R# { - if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
' a# O! N# G0 w+ k - {
# y5 o @7 b* |8 p* I# v0 | - Error_Handler();, c$ M$ G: s. L$ ~& J
- }
4 H0 ~; v/ y$ K
) E0 B# ]# }' e# m) f- // 锁相环已经初始化完毕了,将时钟源在切换回外部时钟源; A( T! u( O2 U9 y2 w7 t/ P
- RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK0 V( M8 u& I' `: ?2 |; Z4 ]
- |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
+ H- f8 ?$ r3 u; A& T - RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
& N. a6 u, D, Q& y# ] - RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;3 z& g6 v$ J4 b
- RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;% e0 ^$ Z& x- [- G6 C
- RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
6 k. G) [5 s) `; d - if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
/ ~! ?: {: V, s4 r. d - {
8 a6 L& |5 H2 z - Error_Handler();
0 B8 Z& Q0 w1 F z o& ]# w - }' e& M6 e& Z" B4 o6 i' v+ T
5 H; y2 Y8 @3 m0 e$ q- // 禁用内部高速时钟+ Y& T2 S+ q# E4 A
- RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
5 P x, R3 X7 e5 r - RCC_OscInitStruct.HSIState = RCC_HSI_OFF;& p0 B0 |4 N- Q6 i& ~
- RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;: k5 s: h u) H( u# i! h5 o
- if(HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
8 _- _' @+ F1 w" F& ~- c* F - {
( f8 ?( V! C0 e! t5 k! t7 q# ] - Error_Handler();7 i9 H |5 g* M! R& i# v k
- }
{8 Z8 [8 |. z3 x) h - }
复制代码 4 ~ e M1 u- H4 w, t, @
通过以上操作后,问题成功解决!) h1 W! {* l4 y: }$ y7 E5 N
- D+ p$ v1 j9 Q; }! |; c解决F4系列这个问题之后,我又去看F1系列的参考手册,发现:
* g' w9 \, y; y+ o' U
& I5 E7 D4 D% U$ }
; s. t( o; g( f/ ?; O" x
1 J+ w: {3 M1 O9 L, v) W
同样的也是需要在使能PLL之前完成时钟配置,不过在实际中发现,F1系列的IAP跳转到APP中并没有卡死在HAL_RCC_OscConfig函数中。
: P& ?7 r* J$ ~, }9 O& Z4 Q! m: j9 S9 c8 S8 ^$ a
还有一个更快捷方便的办法,就是在IAP中不使用PLL,直接使用HSE或HSI的8M晶振作为系统时钟,如下图时钟的配置:
+ q0 o& S1 I: G0 j0 m% B
3 r3 y2 a; a* a+ l
, O q9 r/ m/ }1 [
( I- i- {, \& l! X) T9 i% K
7 [9 D6 F* _ b- J' \) b
- h1 v5 W( m" g. }3 y2 n |