stm32-rs / stm32l0xx-hal

A hardware abstraction layer (HAL) for the STM32L0 series microcontrollers written in Rust
BSD Zero Clause License
96 stars 60 forks source link

System clock of 32 MHz via PLLMul::Mul4 and PLLDiv::Div2 leads to hard fault because of default voltage range #139

Open henriheimann opened 3 years ago

henriheimann commented 3 years ago

When using the following configuration for a 32 MHz system clock, which is possible since #129, a hard fault is generated:

let mut rcc = dp.RCC.freeze(Config::pll(PLLSource::HSI16, PLLMul::Mul4, PLLDiv::Div2));

I traced the problem down to the default Vcore range. As shown by the following table (STM32L0 reference manual, p. 174), VcoreRange::Range1 would be required for frequencies greater than 16 MHz. The default configured Vcore range however, is VcoreRange::Range2 (STM32L0 reference manual, p. 158).

vcore_ranges

With the current interface of stm32l0xx-hal, it seems that configuration of the Vcore range is only possible after freezing the RCC, which is too late to prevent the hard fault:

let mut rcc = dp.RCC.freeze(Config::pll(PLLSource::HSI16, PLLMul::Mul4, PLLDiv::Div2));
let mut pwr = PWR::new(dp.PWR, &mut rcc);
pwr.switch_vcore_range(VcoreRange::Range1);

I would be glad to help resolve this issue myself, but as I am new to using Rust for embedded development it would be great if someone experienced could provide me with some hints on how to pass an instance of PWR to RCC.freeze / write to PWR registers from RCC.freeze / create PWR during RCC.freeze / or whatever the best way might be.

kaidokert commented 3 years ago

Hmm, i ran into the same issue, and "fixed" it by looking at what STMCube generated. STMCube modified the flash wait state

This worked:

// code lifted from flash.set_wait_state()
device.FLASH.acr.modify(|_, w| w.latency().variant(hal::pac::flash::acr::LATENCY_A::WS1));
let cfg = hal::rcc::Config::pll(hal::rcc::PLLSource::HSI16, hal::rcc::PLLMul::Mul4, hal::rcc::PLLDiv::Div2);
let mut rcc = device.RCC.freeze(cfg);

Note that this doesn't work:

let cfg = hal::rcc::Config::pll(hal::rcc::PLLSource::HSI16, hal::rcc::PLLMul::Mul4, hal::rcc::PLLDiv::Div2);
let mut rcc = device.RCC.freeze(cfg);
let mut flash = hal::flash::FLASH::new(device.FLASH, &mut rcc);
flash.set_wait_states(hal::pac::flash::acr::LATENCY_A::WS1);

As it's too late to modify it, the device has already locked up. Same problem as for modifying VCore

EDIT: Another note, i think it would be useful to add an example for configuring non-default clock, i.e. MSI and PLL, all the examples seem to be running with default 16 Mhz HSI

kaidokert commented 3 years ago

After poring over datasheet, to be fully in spec, it looks like both the wait state and vcore need to be set. The device ( L062 here ) powers up at 1.5V by default in range 2, not at 1.8V,

So for anyone following along at home, if you want 32mhz, do this, it'll most likely work:

device.FLASH.acr.modify(|_, w| w.latency().variant(hal::pac::flash::acr::LATENCY_A::WS1));
let mut rcc = device.RCC.freeze(hal::rcc::Config::pll(
            hal::rcc::PLLSource::HSI16, hal::rcc::PLLMul::Mul4, hal::rcc::PLLDiv::Div2,
        ));
let mut pwr = hal::pwr::PWR::new(device.PWR, &mut rcc);
pwr.switch_vcore_range(hal::pwr::VcoreRange::Range1);

Although the latter bit still looks sketchy as the voltage should be upped before clock change. Copying the PAC code from switch_vcore_range out would also work, if you want to be super sure.