1
votes

I am working with the STM32F767 Nucleo board and currently, I trying to set the PLL as the system clock. While I have been able, thanks to an example generated by the CubeMX, I really don't understand why it must be done so. The setup is:

  • HSI = 16MHz
  • PLLM = 8
  • VCO_Input_Frequency = 16/8 = 2MHz
  • PLLN = 144
  • Frequency = 144*2 = 288MHz
  • PLLP = 6
  • PLL_Output_Frequency = 288/6 = 48MHz

  • PPRE1 = 2
  • APB1_Frequency = 24MHz
  • APB1_Frequency_Timer = 2*24MHz = 48MHz

The following line of code is what is buging me:

//Sets the voltage scaling mode to 3, VOS = 0x1 = b1
PWR->CR1 |= PWR_CR1_VOS_0;
a = PWR->CR1;       //Small delay

When this line is commented the period is 19.7ms and when is active the period is 20ms, as expected. It is very strange, this is how the generated code from CubeMX does. It makes the voltage scaling to 1 (low performance). I don't understand how seting the voltage scaling equals to 1 makes the PLL works correctly.

Down below is the code that configure the PLL:

void sys_clock_init(void){
int a;

//Sets the wait states to 1
FLASH->ACR |= 0x01;
a = FLASH->ACR;         //Small delay

//Enables the power interface (for the power controller)
RCC->APB1ENR |= RCC_APB1ENR_PWREN;
a = RCC->APB1ENR;       //Small delay

//Clears the bits for the voltage scaling
PWR->CR1 &= ~(PWR_CR1_VOS);

//Sets the voltage scaling mode to 3, VOS = 0x1 = b1
PWR->CR1 |= PWR_CR1_VOS_0;
a = PWR->CR1;       //Small delay

//Makes HSI the source of the PLL
RCC->PLLCFGR &= ~(0x400000);

//Clears the bits for the different factors
RCC->PLLCFGR &= ~(RCC_PLLCFGR_PLLM);

//Sets the PLLM = 0x08 = b100
RCC->PLLCFGR |= (RCC_PLLCFGR_PLLM_3);

//Clears the PLLN bits
RCC->PLLCFGR &= ~(RCC_PLLCFGR_PLLN);

//Sets PLLN = 0x90 = b10010000
RCC->PLLCFGR |=(RCC_PLLCFGR_PLLN_7 | RCC_PLLCFGR_PLLN_4);

//Clears the PLLP bits
RCC->PLLCFGR &= ~(RCC_PLLCFGR_PLLP);

//Sets the PLLP = 0x02 = b10
RCC->PLLCFGR |=(RCC_PLLCFGR_PLLP_1);.

//Clears the PPRE1 bits
RCC->CFGR &= ~(RCC_CFGR_PPRE1_2 | RCC_CFGR_PPRE1_1 | RCC_CFGR_PPRE1_0);

//Set bit PPRE1 = 0x02 = b100
RCC->CFGR |= (RCC_CFGR_PPRE1_2);// | RCC_CFGR_PPRE1_0);

//Turns the PLL ON
RCC->CR |= RCC_CR_PLLON;

//Waits for the PLL to be ready
while(!((RCC->CR & RCC_CR_PLLRDY) == RCC_CR_PLLRDY));

//Clears the switch bits
RCC->CFGR &= ~(RCC_CFGR_SW);

//Set the PLL as the System Clock
RCC->CFGR |= (RCC_CFGR_SW_1);}

I have also tested commenting the lines that sets VOS bits on the CubeMX code and the period is 19.75ms like mine.

1
first thought is that if you look at the reference manual for those bits it shows that 00 is reserved and you get scaling 3, it also says refer to the datasheet. in the datasheet it shows that scaling 3 only gets you to 144Mhz, to get beyond that you need to adjust to scaling 2 or 1. Now understandably you are aiming for well under all of that yes? So scaling 3 should work. - old_timer
there is a vos ready bit, did you examine that? - old_timer
hmm looks like I have a STM32F767 nucleo board as well...see if I tried the pll yet. - old_timer
period of what? What/where are you measuring a period? - old_timer
48MHz is a period of 20.8 not 20 not 19.7. those are in the 50+mhz range. - old_timer

1 Answers

2
votes

This is my code to get that board to 16Mhz using the PLL and the external clock.

static int clock_init ( void )
{
    unsigned int ra;

    //switch to external clock.
    ra=GET32(RCC_CR);
    ra|=1<<16;
    PUT32(RCC_CR,ra);
    while(1) if(GET32(RCC_CR)&(1<<17)) break;
    if(1)
    {
        ra=GET32(RCC_CFGR);
        ra&=~3;
        ra|=1;
        PUT32(RCC_CFGR,ra);
        while(1) if(((GET32(RCC_CFGR)>>2)&3)==1) break;
    }
    //HSE ready
    //PLLM aim for 2mhz so 8/4=2
    //PLLN input is 2, want >=100 and <=432 so between 50 and 216
    //PLLP  16Mhz*8 = 128, 16MHz*6 = 96, not enough
    //so PLLP is 8 VCO 128 so PLLN is 64
    //don't really care about PLLQ but have to have something so 8
    PUT32(RCC_PLLCFGR,0x20000000|(8<<24)|(1<<22)|(3<<16)|(64<<6)|(4<<0));
    ra=GET32(RCC_CR);
    ra|=1<<24;
    PUT32(RCC_CR,ra);
    while(1) if(GET32(RCC_CR)&(1<<25)) break;
    ra=GET32(RCC_CFGR);
    ra&=~3;
    ra|=2;
    PUT32(RCC_CFGR,ra);
    while(1) if(((GET32(RCC_CFGR)>>2)&3)==2) break;

    return(0);
}

PUT32/GET32 are abstraction functions to do str/ldr. I will try either 48 HSE or 48Mhz HSI, and post what I find.

static int clock_init ( void )
{
    unsigned int ra;
    //PLLM aim for 2mhx so 16/8 = 2;
    //PLLN input is 2, want >=100 and <=432 so between 50 and 216
    //PLLN = 144, VCO 288
    //PLLP = 6, output 288/6 = 48MHz
    //don't really care about PLLQ but have to have something so 6
    PUT32(RCC_PLLCFGR,0x20000000|(6<<24)|(0<<22)|(2<<16)|(144<<6)|(8<<0));
    ra=GET32(RCC_CR);
    ra|=1<<24;
    PUT32(RCC_CR,ra);
    while(1) if(GET32(RCC_CR)&(1<<25)) break;
    ra=GET32(RCC_CFGR);
    ra&=~3;
    ra|=2;
    PUT32(RCC_CFGR,ra);
    while(1) if(((GET32(RCC_CFGR)>>2)&3)==2) break;

    return(0);
}

The uart works, but that is not saying much.

With the default of scale 1 I do see that a little fast. but if I use the 8MHz HSE then it looks better. I use systick to count 120 seconds. set systick to roll over every 1million counts then wait for 120 rollovers, compare to a stopwatch/timer.

Next using 16000000 in systick and counting 900 rollovers that should be 5 minutes and it is to within a second since comparing visually to a timer is only that accurate. Using HSE, scaling 1 (default)

Using HSI scaling 1 it is off by a few seconds to get 19.7ns though would be a lot of seconds and I don't see that many.

Now using HSI scaling 3:

static int clock_init ( void )
{
    unsigned int ra;

    ra=GET32(RCC_APB1ENR);
    ra|=1<<28; //PWR enable
    PUT32(RCC_APB1ENR,ra);

    PUT32(FLASH_ACR,0x00000001);

    PUT32(PWR_CR1,0x4000);
    GET32(PWR_CR1);
    //PLLM aim for 2mhx so 16/8 = 2;
    //PLLN input is 2, want >=100 and <=432 so between 50 and 216
    //PLLN = 144, VCO 288
    //PLLP = 6, output 288/6 = 48MHz
    //don't really care about PLLQ but have to have something so 6
    PUT32(RCC_PLLCFGR,0x20000000|(6<<24)|(0<<22)|(2<<16)|(144<<6)|(8<<0));
    ra=GET32(RCC_CR);
    ra|=1<<24;
    PUT32(RCC_CR,ra);
    while(1) if(GET32(RCC_CR)&(1<<25)) break;
    ra=GET32(RCC_CFGR);
    ra&=~3;
    ra|=2;
    PUT32(RCC_CFGR,ra);
    while(1) if(((GET32(RCC_CFGR)>>2)&3)==2) break;

    return(0);
}

Does appear to be more accurate. 5 minutes measured as 5 minutes to the second. So it appears perhaps that the documentation isn't correct with respect to the accuracy of HSI (as there is this exception using default scaling).

48Mhz -> 20.83ns

20.62 - 21.04 with the documented error.

There is a reason for using external clocks. If you are interested in more accuracy since you have the NUCLEO board use the external clock HSE not the internal HSI.

Hmmm, actually 1% for the 16Mhz which is 3% when multiplied by 3 to get 48MHz. I think using the divisor in the PLL makes it worse, but I would have to ponder that some more.

20.21 to 21.46 is the range you should see at the calibration temperature, then vary from that based on die temp.