cancel
Showing results for 
Search instead for 
Did you mean: 

STM32L072 slow start-up time

MBles.1
Associate II

I'm using a STM32L072CZT6 in a mobile application in which I need the MCU to start up as fast as it can. Currently the start-up time is 6ms. That appears to be too long based on this thread (Start-up time too long), although I haven't found a typical number anywhere.

I'm using STM32CubeIDE with all the standard HAL libraries. I'm using GPIO, ADC, I2C, SPI and USB. I measure the start-up time by setting a GPIO output after all initialization and before the while loop. To answer any questions regarding the power supply, I attached a scope image of the of the 3.3V line (yellow) and the GPIO output (blue); the 3.3V supply is stable 6ms before the pin is toggled. (This initial spike is probably from using 6" ground leads...bad probing). I have taken this steps to diagnose the problem. Code follows.

  • With all peripherals enabled, it takes 13ms to start up.
  • If I disable the USB initialization, the startup is reduced to 10ms.
  • Changing between debug and release configurations change nothing. I don't know if there is any optimization invoked by default.
  • Removing the 100nF capacitor on NRST as suggested here (Start-up time too long) reduces the start-up time to 7ms.
  • Disabling ADC, I2C and SPI initialization reduces the start-up time to 6ms. Now only HAL_Init(), SystemClock_Config() and MX_GPIO_Init() exist before toggling the GPIO pin.
  • Changing the BOR setting in STM32CubeProgrammer has no effect. It was already disabled, but turning it on does nothing. The datasheet says ~2.3ms is added to the startup time with BOR enabled.

Does anyone have suggestions for reducing the start-up time to be as short as possible? I assume there is some optimization than can happen in HAL_Init and SystemClock_Config, but I don't know where to begin. Thank you for any help!

EDIT: BOOT0 is tied to ground with a 10kΩ resistor. I also tried shorting directly to ground with zero change observed.

Here's the code.

int main(void)
{
  /* USER CODE BEGIN 1 */
 
  /* USER CODE END 1 */
 
  /* MCU Configuration--------------------------------------------------------*/
 
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();
 
  /* Configure the system clock */
  SystemClock_Config();
 
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
//  MX_ADC_Init();
//  MX_I2C2_Init();
//  MX_SPI1_Init();
//  MX_SPI2_Init();
//  MX_USB_DEVICE_Init();
 
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
 
  HAL_GPIO_WritePin(DIAG_GPIO_Port, DIAG_Pin, GPIO_PIN_SET); // turn on diagnostics pin to flag MCU ready (end of initialization)
 
  while (1)
  {
 
  }
  /* USER CODE END 3 */
}
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
 
  /** Configure the main internal regulator output voltage
  */
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_HSI48;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.HSI48State = RCC_HSI48_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLLMUL_4;
  RCC_OscInitStruct.PLL.PLLDIV = RCC_PLLDIV_2;
  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_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
 
  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
  {
    Error_Handler();
  }
  PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USB;
  PeriphClkInit.UsbClockSelection = RCC_USBCLKSOURCE_HSI48;
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
  {
    Error_Handler();
  }
}

8 REPLIES 8
MBles.1
Associate II

I did some more digging and found out that NRST does not go high until 3ms after the 3.3V rail is stable. See the attached image where yellow is my voltage in, cyan is 3.3V, magenta is NRST, and blue is the GPIO pin that is toggled high before the main while loop.

It takes 3ms for NRST to be released and go high after 3.3V rail is stable. It then takes another 2.4ms for the MCU to go through the initialization functions to then toggle the GPIO pin.

Why is NRST taking so long be released? I've tried different BOR levels, so is there something else holding it low?

Uwe Bonnes
Principal III

There is the internal NRST pullup ( ~45 kOhm) and probably you have a 100 nF capacitor to GND. That give a tau of 4.5 ms. Then there is "reset temporization" (6.3.2) Check with BOR disabled, with all the risks involved...

Thank you for the response.

I already removed the 100nF cap in the first post, which reduced the start-up time by 3ms.

The remaining 3ms may indeed be the reset temporization, but I also already disabled BOR in STM32CubeProgrammer. Enabling it to the highest level did not increase the start-up time, so is the MCU ignoring the BOR option bytes or is there something else?

Remove all initialized (global) variables to reduce the startup code duration, or simply modify the startup code so that it won't initialize/zero global/static variables.

Then throw out *all* Cube stuff, simply enable GPIO clock in RCC, set given pin to output in GPIOx_MODER and toggle it by writing to GPIOx_ODR/GPIOx_BSRR.

JW

At the very least get critical things tight into the ResetHandler's direct path and run with the clocks the part started on.

Far too much convoluted crap in HAL/CUBE.

If someone were sufficiently keen on code generators that could probably take the .IOC and generate the entire pin/clock initialization code in some tight assembler, optimizing for the actual minimum of load/store instructions rather than some of the comical stuff we see here too often.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

I've seen stuff about adding a noinit section to the linker script so a variable isn't initialized, but I don't know how to apply it to the whole code. Wouldn't this as well as optimizing the Cube code only affect the time after NRST goes high? Or does this somehow prevent NRST from going high?

> Wouldn't this as well as optimizing the Cube code only affect the time after NRST goes high?

Yes, but that's almost half of the total time you have now.

The idea with 'L0 is, that it's powered from battery all the time, and goes to some of the low-power modes while not active. Check out some of the wakeup times, and check out reset "temporization" times for some other families.

JW

Thanks, I see what you're saying. I looked at other families and they all have max reset temporization of 3-4.5ms except for the G series, which is 0.4ms. So it appears to only hardware time decrease is by switching to a STM32G MCU.

For the software side can you point to a source on how to change the startup code so that it won't initialize any variables?