f t g m
Copyright 2024 - Internet Of Things & M2M Communication Experts

Technology Blogs

STM32 - Chaining Two 16 bit Timers to Create 32 Bit Timer

Some low cost embedded devices support only 16 bit timers and sometimes in our applications we actually need 32 bit timers for whatever purpose it might be (E.g. measuring some events micro seconds, providing delays in micro seconds, etc).

Not long ago we had to scale down application to low power, lower cost Arm Cortex MCU but downside was that smaller, more energy efficient part did not have 32 bit timer required by the application. We were scaling from STM32F401RE down to STM32L100RCT device.

There is plethora of material on the net on how to do it in theory but very few actual examples on how to do it so if you are in need of quick solution to the same problem here is short guide on how to do it on real life example.

 

We used two 16 bit timers TIM2 and TIM3 to provide microsecond tick required by the Mbed framework. TIM2 increments until 0xFFFF and on overflow increments TIM3 once.

So here is how to set it all up.

Cut and paste this code, add some other config if you need to, compile, load it to device and and observe how LED blinks at one second interval.

We used STM32L100 Discovery board but any other similar one will do.

 

Timer Setup

#define HAL_TICK_DELAY (1000)

uint32_t PreviousVal = 0;

TIM_HandleTypeDef htim2;
TIM_HandleTypeDef htim3;

/* TIM2 init function */
void MX_TIM2_Init(void)
{
  TIM_ClockConfigTypeDef sClockSourceConfig;
  TIM_MasterConfigTypeDef sMasterConfig;  htim2.Instance = TIM2;
  htim2.Init.Prescaler = (SystemCoreClock / 1000000) - 1;  // 1 us tick
  htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim2.Init.Period = 0xFFFF;
  htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  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_UPDATE;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }

  NVIC_EnableIRQ(TIM2_IRQn);

  // Channel 1 for mbed timeout
  HAL_TIM_OC_Start(&htim2, TIM_CHANNEL_1);
  // Channel 2 for HAL tick
  HAL_TIM_OC_Start(&htim2, TIM_CHANNEL_2);
  PreviousVal = __HAL_TIM_GetCounter(&htim2);
  __HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_2, PreviousVal + HAL_TICK_DELAY);
  __HAL_TIM_ENABLE_IT(&htim2, TIM_IT_CC2);

  __HAL_TIM_ENABLE(&htim2);
  HAL_TIM_Base_Start(&htim2);
}

/* TIM3 init function */
void MX_TIM3_Init(void)
{
  TIM_SlaveConfigTypeDef sSlaveConfig;
  TIM_MasterConfigTypeDef sMasterConfig;
  htim3.Instance = TIM3;
  htim3.Init.Prescaler = 0;
  htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim3.Init.Period = 0xFFFF;
  htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  if (HAL_TIM_Base_Init(&htim3) != HAL_OK)
  {
    Error_Handler();
  }

  sSlaveConfig.SlaveMode = TIM_SLAVEMODE_EXTERNAL1;
  sSlaveConfig.InputTrigger = TIM_TS_ITR1;
  if (HAL_TIM_SlaveConfigSynchronization(&htim3, &sSlaveConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }

  __HAL_TIM_ENABLE(&htim3);
  HAL_TIM_Base_Start(&htim3);
}

void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
{
if(tim_baseHandle->Instance==TIM2)
  {
    /* Peripheral clock enable */
    __HAL_RCC_TIM2_CLK_ENABLE();
  }
  else if(tim_baseHandle->Instance==TIM3)
  {
    /* Peripheral clock enable */
    __HAL_RCC_TIM3_CLK_ENABLE();
  }
}

IRQ Handler

void TIM2_IRQHandler()
{
TIM_HandleTypeDef *htim = &htim2;

    // Channel 1 for mbed timeout
    if (__HAL_TIM_GET_ITSTATUS(htim, TIM_IT_CC1) == SET)
    {
        us_ticker_irq_handler();
    }

// Channel 2 for HAL tick
    if (__HAL_TIM_GET_ITSTATUS(htim, TIM_IT_CC2) == SET)
    {
        __HAL_TIM_CLEAR_IT(htim, TIM_IT_CC2);        uint32_t val = __HAL_TIM_GetCounter(htim);
        if ((val - PreviousVal) >= HAL_TICK_DELAY)
        {
            // Increment HAL variable
            HAL_IncTick();

            // Prepare next interrupt
            __HAL_TIM_SetCompare(htim, TIM_CHANNEL_2, val + HAL_TICK_DELAY);
            PreviousVal = val;

// Toggle PIN to indicate HAL Tick IRQ
            HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_6);
        }
    }
}

 Main

int main(void)
{
/* 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(); // Configure LD4 Port and PIN to output
  MX_TIM2_Init();
  MX_TIM3_Init();

  while (1)
  {
  HAL_Delay(1000);
   HAL_GPIO_TogglePin(LD4_GPIO_Port, LD4_Pin);
}
}