stm32-rs / stm32f0xx-hal

A Rust `embedded-hal` implementation for all MCUs in the STM32 F0 family
BSD Zero Clause License
126 stars 57 forks source link

Problem when using external clock source #77

Closed katyo closed 4 years ago

katyo commented 4 years ago

Usually I clock my precision devices from external 8MHz crystal and it fails to work here. There is my setup:

            // Setup clock from 8 MHz external crystal and 48 MHz PLL
            let mut rcc = p.RCC
                .configure()
                .hse(8.mhz())
                .sysclk(48.mhz())
                .freeze(&mut p.FLASH);

There infinite busy-loop occurs on PLL ready flag awaiting. To exclude hardware problems I tried to load code from other project in C with similar setup and it works as expected. It seems PLL multiplier calculation is wrong because in case of using HSE as a clock source according to datasheet the input frequency does not divided by two alike a case when HSI is used. I may mistake but that my debugger says:

// before enable_pll()
(gdb) svd RCC CR
Fields in RCC CR:
        HSION:     1  Internal High Speed clock enable
        HSIRDY:    1  Internal High Speed clock ready flag
        HSITRIM:  16  Internal High Speed clock trimming
        HSICAL:   93  Internal High Speed clock Calibration
        HSEON:     1  External High Speed clock enable
        HSERDY:    1  External High Speed clock ready flag
        HSEBYP:    0  External High Speed clock Bypass
        CSSON:     1  Clock Security System enable
        PLLON:     0  PLL enable
        PLLRDY:    0  PLL clock ready flag
(gdb) s
stm32f0xx_hal::rcc::inner::enable_pll (rcc=<optimized out>, c_src=0x20000fe0, ppre_bits=<optimized out>,
    hpre_bits=<optimized out>, pllmul_bits=<optimized out>)
// after enable_pll()
(gdb) svd RCC CR
Fields in RCC CR:
        HSION:     1  Internal High Speed clock enable
        HSIRDY:    1  Internal High Speed clock ready flag
        HSITRIM:  16  Internal High Speed clock trimming
        HSICAL:   93  Internal High Speed clock Calibration
        HSEON:     0  External High Speed clock enable
        HSERDY:    0  External High Speed clock ready flag
        HSEBYP:    0  External High Speed clock Bypass
        CSSON:     0  Clock Security System enable
        PLLON:     1  PLL enable
        PLLRDY:    0  PLL clock ready flag
(gdb) svd RCC CFGR
Fields in RCC CFGR:
        SW:         0  System clock Switch
        SWS:        0  System Clock Switch Status
        HPRE:       0  AHB prescaler
        PPRE:       0  APB Low speed prescaler (APB1)
        ADCPRE:     0  ADC prescaler
        PLLSRC:     2  PLL input clock source
        PLLXTPRE:   0  HSE divider for PLL entry
        PLLMUL:    10  PLL Multiplication Factor
        MCO:        0  Microcontroller clock output
        MCOPRE:     0  Microcontroller Clock Output Prescaler
        PLLNODIV:   0  PLL clock not divided for MCO

Unexpectedly HSE turned off and the PLLMUL is 10 (x12 multiplier) when 4 (x6) is expected.

katyo commented 4 years ago

I tried to set correct PLLMUL but it doesn't help.

Strange, I have no problems with project in C where I setup clock in a next way:

  RCC_CR |= (RCC_CR_HSEON | RCC_CR_CSSON);
  for (; !((RCC_CR & (RCC_CR_HSERDY))); );
  RCC_CFGR = (RCC_CFGR & ~(RCC_CFGR_SW | RCC_CFGR_HPRE | RCC_CFGR_PPRE | RCC_CFGR_PLLMUL | RCC_CFGR_PLLSRC))
    | (RCC_CFGR_SW_HSE | RCC_CFGR_HPRE_DIV1 | RCC_CFGR_PPRE_DIV1 | RCC_CFGR_PLLMUL_MUL6 | RCC_CFGR_PLLSRC_HSE);
  FLASH_ACR |= (FLASH_ACR_LATENCY_1WS);
  RCC_CR |= (RCC_CR_PLLON);
  for (; !((RCC_CR & (RCC_CR_PLLRDY))); );
  RCC_CFGR = (RCC_CFGR & ~(RCC_CFGR_SW))
    | (RCC_CFGR_SW_PLL);
pigrew commented 4 years ago

I've created a patch that has the extra logic for determining the divider when HSI is the source. It's in my branch https://github.com/pigrew/stm32f0xx-hal/tree/pllsrc

I still need to test it (though it does pass the CI checks)... I'll create a PR once I'm more confident in it.