Electronic – STM32 – AHB Prescaler disabling Systick

armfirmwareinterruptsstm32stm32f0

I'm writing bare metal firmware for an STM32F070. When I set the AHB Prescaler (AHBCLKDivider) to a value greater than 1, the systick interrupt never happens, i.e. SysTick_Handler() is never called. This in turn makes HAL_Delay() hang the system when called.

Why is this happening? Is it possible to have the systick interrupt enabled when using the AHB Prescaler?

My clock initialization code is below. I tweaked it based on code generated by STM32CubeMX.

Also, what is the meaning of the parameter that is passed to HAL_SYSTICK_Config()? Does it need to be adjusted based on the prescaler settings?

void SystemClock_Config(void)
{
   RCC_OscInitTypeDef RCC_OscInitStruct;
   RCC_ClkInitTypeDef RCC_ClkInitStruct;
   RCC_PeriphCLKInitTypeDef PeriphClkInit;

   /**Initializes the CPU, AHB and APB busses clocks 
   */
   RCC_OscInitStruct.OscillatorType =
      RCC_OSCILLATORTYPE_HSE |
      RCC_OSCILLATORTYPE_HSI |
      RCC_OSCILLATORTYPE_LSE |
      RCC_OSCILLATORTYPE_LSI |
      RCC_OSCILLATORTYPE_HSI14;
   RCC_OscInitStruct.HSEState   = RCC_HSE_OFF;
   RCC_OscInitStruct.HSIState   = RCC_HSI_ON;
   RCC_OscInitStruct.HSICalibrationValue = 16;
   RCC_OscInitStruct.HSI14State = RCC_HSI14_OFF;
   RCC_OscInitStruct.LSEState   = RCC_LSE_OFF;
   RCC_OscInitStruct.LSIState   = RCC_LSI_ON;
   RCC_OscInitStruct.PLL.PLLState = RCC_PLL_OFF;

   if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
   {
      Error_Handler();
   }

   /**Initializes the CPU, AHB and APB busses clocks 
   */
   RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
                               | RCC_CLOCKTYPE_PCLK1;
   RCC_ClkInitStruct.SYSCLKSource   = RCC_SYSCLKSOURCE_HSI;
   RCC_ClkInitStruct.AHBCLKDivider  = RCC_SYSCLK_DIV1; //TODO
   RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;   //TODO

   if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
   {
      Error_Handler();
   }

   PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_RTC;
   PeriphClkInit.RTCClockSelection    = RCC_RTCCLKSOURCE_LSI;

   if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
   {
      Error_Handler();
   }

   GPIO_InitTypeDef gpioInit =
   {
      .Pin  = GPIO_PIN_0,
      .Mode = GPIO_MODE_OUTPUT_PP,
      .Pull = GPIO_NOPULL,
      //.Speed = GPIO_SPEED_FREQ_LOW,
      //.Alternate = 0,
   };

   /**Configure the Systick interrupt time 
   */
   HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq() / 8000); //TODO: is this correct when using prescalers?

   /**Configure the Systick 
   */
   HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK_DIV8);

   /* SysTick_IRQn interrupt configuration */
   HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}

Best Answer

The meaning of the parameter you pass to HAL_SYSTICK_Config is the number of ticks before a SYSTICK interrupt is generated. So in the example code, the SYSCLK is the HSI with 8 MHz. And the HCLK has no prescaler, so HCLK is also 8 MHz.

A SYSTICK interrupt will be generated every 8 000 000 / 8 000 = 1 000 ticks, because the SYSTICK clock has an own prescaler of 8 based on HCLK, the actual SYSTICK frequency will be 1 MHz. 1 000 ticks turns into a SYSTICK interrupt of 1 ms.

So if you set your HCLK to be 2 MHz, it will set it to 250 ticks, resulting in still 1 ms interrupts. So you don't have to change that value with changing prescaler.

I currently don't see a reason why this won't work. Maybe you can go into debug and see what the result of HAL_RCC_GetHCLKFreq() is when you have a divider in place. Maybe divide that line into two like to make it easier to debug:

uint32_t hclk = HAL_RCC_GetHCLKFreq();
HAL_SYSTICK_Config(hclk/8000);

RANT

Ugh this is a mess as always with STM32. One of the reasons I'm not using the ST HAL or Cube is the quality of documentation, it's already bad in the reference manual, but worse when you try to understand the API (for example will it return the frequency in Hz, kHz or MHz? nowhere to be found (it is Hz)).