embassy-rs / embassy

Modern embedded framework, using Rust and async.
https://embassy.dev
Apache License 2.0
5.51k stars 767 forks source link

STM32H7 Power Configuration issues leading to general unreliability to start-up #2806

Open birdistheword96 opened 6 months ago

birdistheword96 commented 6 months ago

This is a placeholder issue to track the knowledge we have on why STM32H7's are so unreliable to start-up. all of my testing has been done on three different STM32H745 boards: a Nucleo, a Discovery board, and a custom in-house board. This is what I have found so far, and will update as I discover more:

bsodmike commented 6 months ago

I had this issue for a while on the H747xi but managed to resolve it after much testing. Let me dig up the config I'm using.

I have a spare NUCLEO-H743ZI2 which I can test for you if you like.

birdistheword96 commented 6 months ago

This is the code I have configured for my custom board, which to be fair has an external oscillator, but I have equivalent C code which I can use t- get it running reliably. so i am trying to work out the difference. This is the config I have:

    let mut config = Config::default();
    {
        use embassy_stm32::rcc::*;
        // config.rcc.hsi = Some(HSIPrescaler::DIV1);
        config.rcc.hsi = None; // Since we're using HSE
        config.rcc.hse = Some(Hse { freq: mhz(20), mode: HseMode::Bypass});
        config.rcc.csi = false;
        config.rcc.pll1 = Some(Pll {
            source: PllSource::HSE,
            prediv: PllPreDiv::DIV2,
            mul: PllMul::MUL32,
            divp: Some(PllDiv::DIV2),
            divq: Some(PllDiv::DIV8),
            divr: Some(PllDiv::DIV2),
        });
        config.rcc.sys = Sysclk::PLL1_P; // 400 Mhz
        config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz
        config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz
        config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz
        config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz
        config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz
        config.rcc.voltage_scale = VoltageScale::Scale3;
        config.rcc.supply_config = SupplyConfig::DirectSMPS;
    }
    let p = embassy_stm32::init(config);

And this is the equivalent config generated from STMCube:

void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Supply configuration update enable
  */
  HAL_PWREx_ConfigSupply(PWR_DIRECT_SMPS_SUPPLY);

  /** Configure the main internal regulator output voltage
  */
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE3);

  while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}

  /** Macro to configure the PLL clock source
  */
  __HAL_RCC_PLL_PLLSOURCE_CONFIG(RCC_PLLSOURCE_HSE);

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_BYPASS;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 2;
  RCC_OscInitStruct.PLL.PLLN = 32;
  RCC_OscInitStruct.PLL.PLLP = 2;
  RCC_OscInitStruct.PLL.PLLQ = 8;
  RCC_OscInitStruct.PLL.PLLR = 2;
  RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_3;
  RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
  RCC_OscInitStruct.PLL.PLLFRACN = 0;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2
                              |RCC_CLOCKTYPE_D3PCLK1|RCC_CLOCKTYPE_D1PCLK1;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;
  RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
  {
    Error_Handler();
  }
}
birdistheword96 commented 6 months ago

When I read out the PWR.CR3 register just before waiting for VOSRDY, the configuration from STMCube sets the register up as 0x5010044, whereas Embassy has it set up as 0x10044. Looking at RM0399, the difference is that the the register setup from the STMCUBE also has the USB33RDY and USB33DEN enabled, which are both USB specific and not something I am using.

I can only assume that the STMCube HAL is adding bloat and enabling those even though they arent specifically being used

bsodmike commented 6 months ago

Also tested on both STM32h7hxi Arduino Portenta H7 & GIGA R1 WiFi and hits the full 480MHz and 1.21 giga-watts!!

0.000000 TRACE BDCR configured: 00008113
└─ embassy_stm32::rcc::bd::{impl#3}::init::{closure#4} @ /Users/mdesilva/.cargo/git/checkouts/embassy-9312dcb0ed774b29/49807c0/embassy-stm32/src/fmt.rs:117 
0.000000 DEBUG rcc: Clocks { csi: Some(Hertz(4000000)), hclk1: Some(Hertz(240000000)), hclk2: Some(Hertz(240000000)), hclk3: Some(Hertz(240000000)), hclk4: Some(Hertz(240000000)), hse: None, hsi: Some(Hertz(64000000)), hsi48: Some(Hertz(48000000)), i2s_ckin: None, lse: None, lsi: None, pclk1: Some(Hertz(120000000)), pclk1_tim: Some(Hertz(240000000)), pclk2: Some(Hertz(120000000)), pclk2_tim: Some(Hertz(240000000)), pclk3: Some(Hertz(120000000)), pclk4: Some(Hertz(120000000)), pll1_q: Some(Hertz(120000000)), pll2_p: Some(Hertz(100000000)), pll2_q: None, pll2_r: None, pll3_p: None, pll3_q: None, pll3_r: None, rtc: Some(Hertz(32768)), sys: Some(Hertz(480000000)) }

Use the following config:


pub fn init() -> (embassy_stm32::Peripherals, cortex_m::Peripherals) {
    let mut config = Config::default();
    {
        use embassy_stm32::rcc::*;
        config.rcc.supply_config = SupplyConfig::LDO;
        config.rcc.hsi = Some(HSIPrescaler::DIV1); // // 64MHz
        config.rcc.csi = true;
        config.rcc.hsi48 = Some(Hsi48Config {
            sync_from_usb: true,
        }); // needed for USB

        #[cfg(feature = "stm32h747_400")]
        {
            config.rcc.pll1 = Some(Pll {
                source: PllSource::HSI,
                prediv: PllPreDiv::DIV4,
                mul: PllMul::MUL50,
                divp: Some(PllDiv::DIV2), // ((64/4)*50)/2 = 400MHz
                divq: Some(PllDiv::DIV8), // ((64/4)*50)/8 = 100MHz / SPI1 cksel defaults to pll1_q
                divr: None,
            });
            config.rcc.pll2 = Some(Pll {
                source: PllSource::HSI,
                prediv: PllPreDiv::DIV8,
                mul: PllMul::MUL50,
                divp: Some(PllDiv::DIV4), // ((64/8)*50)/4 = 100MHz
                divq: None,
                divr: None,
            });
        }
        #[cfg(feature = "stm32h747_480")]
        {
            config.rcc.pll1 = Some(Pll {
                source: PllSource::HSI,
                prediv: PllPreDiv::DIV8,
                mul: PllMul::MUL120,
                divp: Some(PllDiv::DIV2), // ((64/8)*120)/2 = 480MHz
                divq: Some(PllDiv::DIV8), // ((64/8)*120)/8 = 120MHz / SPI1 cksel defaults to pll1_q
                divr: None,
            });
            config.rcc.pll2 = Some(Pll {
                source: PllSource::HSI,
                prediv: PllPreDiv::DIV8,
                mul: PllMul::MUL50,
                divp: Some(PllDiv::DIV4), // ((64/8)*50)/4 = 100MHz
                divq: None,
                divr: None,
            });
        }
        config.rcc.sys = Sysclk::PLL1_P; // 400 Mhz
        config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz
        config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz
        config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz
        config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz
        config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz
        config.rcc.voltage_scale = VoltageScale::Scale0;

        let mut mux = rcc::mux::ClockMux::default();
        mux.adcsel = rcc::mux::Adcsel::PLL2_P;
        config.rcc.mux = mux;

        // RTC
        config.rcc.ls = LsConfig::default_lse();

        trace!("Voltage::Scale{=i32}", config.rcc.voltage_scale as i32);
    }

    let p: embassy_stm32::Peripherals = embassy_stm32::init(config);
    let core_peri = cortex_m::Peripherals::take().unwrap();

    (p, core_peri)
}

This is my config, I don't think I had much luck with config.rcc.hse.

bsodmike commented 6 months ago

Also, this works on the STM32h7hxi Arduino Portenta H7 https://github.com/embassy-rs/embassy/blob/main/examples/stm32h7/src/bin/dac_dma.rs

bsodmike commented 6 months ago

I've found some instability in my power configuration for the STM32h7hxi and have shared https://github.com/bsodmike/stm32h747xi-embassy-uart-troubleshoot which can be flashed onto an Arduino Portenta H7. See REDME for details.

If anyone has a working config for UART with stm32h747xi please let me know.

romainreignier commented 5 months ago

Also, this works on the STM32h7hxi Arduino Portenta H7 https://github.com/embassy-rs/embassy/blob/main/examples/stm32h7/src/bin/dac_dma.rs

I have tested the same rcc config in the blinky sketch on a Portenta and it seems that the clock speed is not right, the blink seems quite slow (will need to check with a logic analyzer).

This zephyr PR has some interesting comments and seems to have a working RCC config to get to 400 MHz.

bsodmike commented 5 months ago