cancel
Showing results for 
Search instead for 
Did you mean: 

Cannot switch to PLL1

Stanislav Husár
Associate II

Hello,

I am trying to switch system clock on H743ZI to PPL1. Using following code, it hangs up on checking SWS flag - clock switch begins, but never ends.

I have tried setting domain prescalers before or after clock switch. The same for flash latency.

I can switch to HSE, but not to PLL. I have checked PLL status, PLLRDY is asserted.

What am I doing wrong?

PWR->CR3 |= PWR_CR3_SCUEN;
PWR->CR3 |= PWR_CR3_LDOEN;
PWR->D3CR |= PWR_D3CR_VOS;
while ((PWR->D3CR & PWR_D3CR_VOSRDY) != PWR_D3CR_VOSRDY) ;
	
FLASH->ACR |= FLASH_ACR_LATENCY_5WS;
	
RCC->CR |= RCC_CR_HSEON;
while ((RCC->CR & RCC_CR_HSERDY) != RCC_CR_HSERDY);
	
RCC->PLLCKSELR |= RCC_PLLCKSELR_PLLSRC_HSE;
RCC->PLLCKSELR |= RCC_PLLCKSELR_DIVM1_2;	// DIVM1 /4
	
RCC->PLLCFGR &= ~RCC_PLLCFGR_DIVQ1EN;
RCC->PLLCFGR &= ~RCC_PLLCFGR_DIVR1EN;
RCC->PLLCFGR |= RCC_PLLCFGR_PLL1RGE_2;
 
RCC->PLL1DIVR |= 1 << RCC_PLL1DIVR_P1_Pos;	//DIVP /2
RCC->PLL1DIVR |= (0x7FUL) << RCC_PLL1DIVR_N1_Pos; // DIVN1 x128
	
RCC->CR |= RCC_CR_PLL1ON;
while ((RCC->CR & RCC_CR_PLL1RDY) != RCC_CR_PLL1RDY) ;
	
RCC->CFGR |= RCC_CFGR_SW_PLL1;
while ((RCC->CFGR & RCC_CFGR_SWS_Msk) != RCC_CFGR_SWS_PLL1) ;
	
RCC->D1CFGR |= RCC_D1CFGR_HPRE_3;
RCC->D1CFGR |= RCC_D1CFGR_D1PPRE_2;
	
RCC->D2CFGR |= RCC_D2CFGR_D2PPRE1_2;
RCC->D2CFGR |= RCC_D2CFGR_D2PPRE2_2;
	
RCC->D3CFGR |= RCC_D3CFGR_D3PPRE_2;

Thanks in advance,

Stanislav

8 REPLIES 8

Do you take into account that not all registers have zero initial value (e.g. PLLCKSELR)?

JW

I may have missed something, but I did look at reset values in ref manual.

Anyway, I don’t understand, why PLL starts(and locks), but can’t be selected as sysclk.

I don't understand it either, but I don't have a 'H7 to try.

> RCC->PLLCKSELR |= RCC_PLLCKSELR_DIVM1_2; // DIVM1 /4

Reset value of PLLCKSELR according to RM0433 rev.5 is 0x02020200, which for the DIVM1 field means 0x100000=32. You OR this field by 0x000100, resulting in 0x100100=36. Is this what you intended? The comment says otherwise.

> RCC->PLL1DIVR |= (0x7FUL) << RCC_PLL1DIVR_N1_Pos; // DIVN1 x128

The default value of DIVN1 field is 0x80, so the result after the OR is 0xFF, making the multiplcicator being x256.

Now both these factors may result in PLL being off its nominal working area. I did not check other registers.

I know that you wait until PLL locks, but the indicator may be a crude counter, and PLL itself may be unstable enough so that some of the protection mechanisms kick in and switch back to HSI automatically.

[style] What I usually do is that I don't RMW most of the registers containing multiple fields, but I directly write the value for the whole register at once, even if alculated from several values

RCC->PLLCKSELR = 0
  | RCC_PLLCKSELR_PLLSRC_HSE
  | (4 <<  RCC_PLLCKSELR_DIVM1_Pos)
;

Style is of course matter of personal preference, and in this case shouldn't make any difference in functionality - except for the nonzero initial value. [/style]

JW

Thank you. I have modified code to account for these reset values. Unfortunately, I am still unable to switch.

//PWR->CR3 |= PWR_CR3_SCUEN;
//PWR->CR3 |= PWR_CR3_LDOEN;
PWR->D3CR |= PWR_D3CR_VOS;
while ((PWR->D3CR & PWR_D3CR_VOSRDY) != PWR_D3CR_VOSRDY) ;
	
RCC->CR |= RCC_CR_HSEON;
while ((RCC->CR & RCC_CR_HSERDY) != RCC_CR_HSERDY);
	
RCC->PLLCKSELR |= RCC_PLLCKSELR_PLLSRC_HSE;
RCC->PLLCKSELR &= ~RCC_PLLCKSELR_DIVM1_Msk;
RCC->PLLCKSELR |= RCC_PLLCKSELR_DIVM1_2;	// DIVM1 /4
	
	
RCC->PLLCFGR &= ~RCC_PLLCFGR_DIVQ1EN;
RCC->PLLCFGR &= ~RCC_PLLCFGR_DIVR1EN;
RCC->PLLCFGR |= RCC_PLLCFGR_PLL1RGE_2;
	
//RCC->PLL1DIVR |= 1 << RCC_PLL1DIVR_P1_Pos;	//DIVP /2
RCC->PLL1DIVR &= ~RCC_PLL1DIVR_N1_Msk;
RCC->PLL1DIVR |= (0x7FUL) << RCC_PLL1DIVR_N1_Pos; // DIVN1 x128
	
RCC->CR |= RCC_CR_PLL1ON;
while ((RCC->CR & RCC_CR_PLL1RDY) != RCC_CR_PLL1RDY) ;
	
RCC->CFGR |= RCC_CFGR_SW_PLL1;
while ((RCC->CFGR & RCC_CFGR_SWS_Msk) != RCC_CFGR_SWS_PLL1) ;
	
RCC->D1CFGR |= RCC_D1CFGR_HPRE_3;
RCC->D1CFGR |= RCC_D1CFGR_D1PPRE_2;
	
RCC->D2CFGR |= RCC_D2CFGR_D2PPRE1_2;
RCC->D2CFGR |= RCC_D2CFGR_D2PPRE2_2;
	
RCC->D3CFGR |= RCC_D3CFGR_D3PPRE_2;
	
FLASH->ACR &= ~FLASH_ACR_LATENCY_Msk;
FLASH->ACR |= FLASH_ACR_LATENCY_2WS;

 Also, I prefer to mask register field before modification. Using the way you mentioned, you're actually trying to zero reserved fields. I think(hope) hw is smart enough to refuse this....but if it's not, few people can tell what will happen.

UPDATE: I have found in HAL sources that I should change prescalers before clock switch (I am increasing divider).

> you're actually trying to zero reserved fields

Only in rare cases are they not zero.

> I have found in HAL sources that I should change prescalers before clock switch (I am increasing divider).

And did that help?

FLASH latency should be changed before the switch, too. You've had that that way originally.

Reading the Cube/HAL sources might help, even if it is tedious.

You might also read back the registers you are writing in the debugger, to check, whether they have actually changed. Sometimes there are surprising timing constraints, not described clearly in the manual, and the "libraries" hide their effect by the crappy way how they are written.

JW

Modifying prescalers before/after clk switch did not resolve the issue. With FLASH latency, I have tried both, also no success.

Debugger, I am using Visual Studio 2019 with VisualGDB. Sometimes I am encountering debugger issues, which prevent me from reading back registers.

What I do know is that PLLRDY was asserted.

Maybe I should invest some time to setup a better debug environment. I have already tried Stm32CubeIDE, but for some reason, I am unfamiliar with Eclipse-based IDE windows. Also CubeIDE is a way too much oriented in using official HAL, I prefer going down to direct register access.

Stanislav Husár
Associate II

Okay, now it gets interesting. I have discovered, that I actually don't have HSE crystal.

Really, no idea what magic is happening on my board.

I have tried to clock PLL from HSI, and then do a clock switch....not working.