cancel
Showing results for 
Search instead for 
Did you mean: 

STM32L0 cannot change PWR_CR_ VOS

RinGO
Associate III

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?

1 ACCEPTED SOLUTION

Accepted Solutions
waclawek.jan
Super User

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

View solution in original post

9 REPLIES 9
RinGO
Associate III

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.

waclawek.jan
Super User

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

Okay, I canceled the PWR_CR_VOS register reset. Still the same result. I want to write 0x1UL to the VOS register, but it changes from 0x2UL to 0x3UL
Why?

RinGO
Associate III

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.

waclawek.jan
Super User

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

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.

waclawek.jan
Super User

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

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.

RinGO
Associate III

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); // 0b11

and it is not possible to set/reset to values ​​less than 0x2 with a regular command:

// not possible to set
PWR->CR |= (0x1UL << 11U); // 0b01

The 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.