cancel
Showing results for 
Search instead for 
Did you mean: 

Using a TIM2 for µs measurement in thread in combination with FreeRTOS leads to HardFault_Handler being called.

JHöfl.1
Associate II

I'm working with the Nucleo-G474RE Board. With the STM32CubeIDE I set up FreeRTOS and assign TIM6 as Timebase Source (as recomended by the IDE). I create a basic Thread to test a basic scoped timer I want to use for timing measurement. The ScopedTimer Class is supposed to use TIM2 for it's measurement. TIM2s Prescaler is set to 170 and its Counter Period is set to 0xffffffff (max value for 32bit).

ScopedTimer.h

#ifndef SRC_SCOPEDTIMER_HPP_
#define SRC_SCOPEDTIMER_HPP_
 
#include "stm32g4xx_hal.h"
#include <string.h>
#include <stdio.h>
 
 
class ScopedTimer {
private:
	UART_HandleTypeDef* m_uartHandle;
	TIM_HandleTypeDef* m_timerHandle;
	const char* m_name;
	uint32_t m_startTime;
public:
	ScopedTimer(UART_HandleTypeDef* uartHandle, TIM_HandleTypeDef* timerHandle, const char* name);
	virtual ~ScopedTimer();
};
 
 
#endif /* SRC_SCOPEDTIMER_HPP_ */

ScopedTimer.cpp

#include "ScopedTimer.h"
 
ScopedTimer::ScopedTimer(UART_HandleTypeDef* uartHandle, TIM_HandleTypeDef* timerHandle, const char* name)
	: m_uartHandle(uartHandle), m_timerHandle(timerHandle), m_name(name), m_startTime(timerHandle->Instance->CNT)
{
 
}
 
ScopedTimer::~ScopedTimer()
{
	uint8_t buffer[strlen(m_name) + 20];
	uint32_t dt = m_timerHandle->Instance->CNT - m_startTime;
	sprintf((char*)buffer, "%s: %lu ms\r\n", m_name, dt);
	HAL_UART_Transmit(m_uartHandle, buffer, strlen((char*)buffer), HAL_MAX_DELAY);
}

MX_TIM2_Init created by STM32CubeIDE:

static void MX_TIM2_Init(void)
{
  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};
 
  htim2.Instance = TIM2;
  htim2.Init.Prescaler = 170;
  htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim2.Init.Period = 4.294967295E9;
  htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
  {
    Error_Handler();
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  } 
}

my Task:

void UpdateSpeedTask(void *argument)
{
  /* USER CODE BEGIN 5 */
  /* Infinite loop */
  for(;;)
  {
	  {
		  ScopedTimer adcTimer(&hlpuart1, &htim2, "ADC-Timer");
		  osDelay(1000);
	  }
  }
  /* USER CODE END 5 */ 
}

The Console Output:0693W000003BaODQA0.png

So It's working perfectly fine so far.

Now I read analog Values from ADC1 inside the Thread and store them in a history array for future useage. This is where the code brakes.

Here is the ADC1 configuration created by STM32CubeIDE:

static void MX_ADC1_Init(void)
{ 
  ADC_MultiModeTypeDef multimode = {0};
  ADC_ChannelConfTypeDef sConfig = {0};
 
  hadc1.Instance = ADC1;
  hadc1.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;
  hadc1.Init.Resolution = ADC_RESOLUTION_12B;
  hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc1.Init.GainCompensation = 0;
  hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
  hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
  hadc1.Init.LowPowerAutoWait = DISABLE;
  hadc1.Init.ContinuousConvMode = DISABLE;
  hadc1.Init.NbrOfConversion = 1;
  hadc1.Init.DiscontinuousConvMode = DISABLE;
  hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
  hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
  hadc1.Init.DMAContinuousRequests = DISABLE;
  hadc1.Init.Overrun = ADC_OVR_DATA_PRESERVED;
  hadc1.Init.OversamplingMode = DISABLE;
  if (HAL_ADC_Init(&hadc1) != HAL_OK)
  {
    Error_Handler();
  }
 
  multimode.Mode = ADC_MODE_INDEPENDENT;
  if (HAL_ADCEx_MultiModeConfigChannel(&hadc1, &multimode) != HAL_OK)
  {
    Error_Handler();
  }
 
  sConfig.Channel = ADC_CHANNEL_1;
  sConfig.Rank = ADC_REGULAR_RANK_1;
  sConfig.SamplingTime = ADC_SAMPLETIME_2CYCLES_5;
  sConfig.SingleDiff = ADC_SINGLE_ENDED;
  sConfig.OffsetNumber = ADC_OFFSET_NONE;
  sConfig.Offset = 0;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
}

Now the Task-Code looks like this:

void UpdateSpeedTask(void *argument)
{
  uint32_t rawConversion;
  const size_t histCount = 10;
  uint32_t rawHistory[histCount];
 
  for(;;)
  {
	  {
		ScopedTimer adcTimer(&hlpuart1, &htim2, "ADC-Timer");
 
		HAL_ADC_Start(&hadc1);
		HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY);
		rawConversion = HAL_ADC_GetValue(&hadc1);
 
		uint32_t copyHist[histCount];
		copyHist[0] = rawConversion;
 
		for (size_t i = histCount - 1; i > 0; i--)
			copyHist[i] = rawHistory[i-1];
 
		for (size_t i = 0; i < histCount; i++)
			rawHistory[i] = copyHist[i];
	  }
	  osDelay(1000);
  }
}

And the output on the console looks like this:

0693W000003BaeBQAS.png

The Deconstructor of my ScopedTimer Class gets called once. After that the task never gets called again. when I pause the Program to step through it, I end up inside HAL_TIM_IRQHandler for TIM6 (Timebase Timer) and never return to my Thread.

Does anyone know what the problem is? If I use HAL_GetTick() inside my ScopedTimer instead of TIM2 it works perfectly fine, but I need the µs resolution since ms would display 0ms.

Thank you in advance!

Greetings

Johannes

1 REPLY 1
JHöfl.1
Associate II

The console output says ms but it's acutally µs. I forgot to change it after testing with HAL_GetTick() insted of m_timerHandler->Instance->CNT.