2025-12-10 4:40 AM
Hi, I'm stuck on a problem with setting PWR_CR_VOS on NUCLEO-32 STM32L031K6
- I want to select HSI16-PLL as clock source
- VDD is 3.3V
- FLASH latency is set to 1WS
But I can only set PWR_CR_VOS to RANGE 2 and RANGE 3.
RANGE 1 cannot be selected, so I can't set HSI-PLL to 32MHz
where am I going wrong?
Example code:
// Set the Flash ACR to use 1 wait-state
// and enable the prefetch buffer and pre-read.
FLASH->ACR |= (FLASH_ACR_LATENCY | FLASH_ACR_PRFTEN | FLASH_ACR_PRE_READ);
//----------------------------------------------------
// 1. POWER ENABLE CLOCK and VOLTAGE REGULATOR
//----------------------------------------------------
// APB2ENR->SYSCFGEN
RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN;
// APB1ENR->PWREN: Power interface clock enable bit for PWR
RCC->APB1ENR |= RCC_APB1ENR_PWREN;
// PWR: VOS[11:2] bits (Voltage scaling range selection)
// !!! 0x0UL: FORBIDDEN
// 0x1UL: 0b01 RANGE 1 1.8V 32MHz
// 0x2UL: 0b10 RANGE 2 1.5V 16MHz
// 0x3UL: 0b11 RANGE 3 1.2V 4.2MHz
// Clear VOS bits
PWR->CR &= ~PWR_CR_VOS;
// SET RANGE 1
PWR->CR |= (0x1UL << 11U);
// Wait until ready
while (PWR->CSR & PWR_CSR_VOSF);
The value in the register is still 0x2UL, when trying to change to 0x1UL the register is 0x3UL
Where am I making a mistake that RANGE1 cannot be set to 0x1UL?
Solved! Go to Solution.
2025-12-10 8:00 AM
The reset value of the VOS field is 0b01, if you OR it with 0b10 the result is 0b11.
You may want to do something like:
uint32_t tmp;
tmp = PWR->CR & (~PWR_CR_VOS);
tmp = tmp |= (0x1UL << 11U);
PWR->CR = tmp;JW
2025-12-10 4:47 AM
Interestingly, if I generate the CubeMX code with the LL library, it works. PWR_CR_VOS set to 0x1UL
I've taken the code step by step, but up to the PWR_CR_VOS setting, everything is the same except for the NVIC setting for SysTick, which shouldn't affect the registry settings.
2025-12-10 6:01 AM - edited 2025-12-10 6:03 AM
It's in the comments
// PWR: VOS[11:2] bits (Voltage scaling range selection)
// !!! 0x0UL: FORBIDDEN
yet you still do it
PWR->CR &= ~PWR_CR_VOS;
NEVER split a read-clear_bits-set_bits-write operation into two (i.e. read-clear_bits-write, read-set_bits-write).
JW
2025-12-10 7:22 AM
2025-12-10 7:49 AM
An incomprehensible phenomenon for me.
If I try to change the VOS bit value in the PWR_CR register,
// SET RANGE 1
PWR->CR |= (0x1UL << 11U);
It doesn't work. The value changes from 0x2UL to 0x3UL
If I change the value of the entire PWR_CR register in hard mode,
// SET RANGE 1
PWR-> CR = 0x00000800;
it works. The value changes from 0x2UL to 0x1UL
Why doesn't it work the first way? Overwriting the entire register is not a solution.
2025-12-10 8:00 AM
The reset value of the VOS field is 0b01, if you OR it with 0b10 the result is 0b11.
You may want to do something like:
uint32_t tmp;
tmp = PWR->CR & (~PWR_CR_VOS);
tmp = tmp |= (0x1UL << 11U);
PWR->CR = tmp;JW
2025-12-10 11:01 AM
Yes, but it's basically the same as writing to the register
PWR-> CR = 0x00000800;This method is not about bit manipulation, but writing a 32bit value to the register.
2025-12-11 1:53 AM - edited 2025-12-11 2:01 AM
And what?
PWR->CR |= (0x1UL << 11U) *is* writing a 32-bit value to the register, too.
There's no way to change individual bits in the peripheral registers, except in cases where those registers do have a special hardware arrangement to facilitate such individual bit change (e.g. you can inidividually change bits in GPIO_ODR through the special arrangement which that register has in hardware with the GPIO_BSRR register). The PWR_CR register has no such arrangement, so you can write only all 32 bits of it at once.
JW
2025-12-11 2:30 AM
The method proposed by @waclawek.jan is not the same as writing directly 0x00000800 to PWR->CR.
On line 3 tmp has the content of PWR->CR with the bits related to VOS set to 0. The next line is setting VOS as suited for your application. This way you don't care about the content of the whole register, you only change the bits you need or care about.
2025-12-12 6:12 AM
I think I understand now.
I had to stupidly phase it out.
1. load the value of the entire register into "tmp"
2. clear the bits for PWR_CR_VOS
3. set the desired value of PWR_CR_VOS
4. insert the entire modified 32-bit value back into the register
...
So I will assume that this is not possible with this register because:
- STM32L031k has PWR_CR_VOS resetting hardware disabled:
// HW disabled:
PWR->CR &= ~(0x3UL << 11U); // 0b11and it is not possible to set/reset to values less than 0x2 with a regular command:
// not possible to set
PWR->CR |= (0x1UL << 11U); // 0b01The default value of PWR_CR_VOS is 0x2 (0b10)
So it can only be modified to values 0x2 and 0x3
To set the value:
PWR->CR |= (0x1UL << 11U);
It is probably only possible to do this in the way JW states
If I understood correctly,
thank you very much.