2
\$\begingroup\$

I am trying to implement IDLE detection described in AN3109. The idea is simple:

  • Run timer in slave reset mode with maximum possible period;
  • Configure CH2 for input capture on rising edge, however do not use it for capture. Instead mux the edge detection signal TI2FP2 as slave reset trigger. Connect this input to UART Rx wire;
  • Configure CH1 for output compare with period equal to desired IDLE time.

Without intervention timer will simply count up, rolling over infrequently. However every rising edge on UART Rx will reset count back to 0. When Rx pulses stop coming counter is allowed to grow and eventually reach CCR value, at which point IDLE interrupt is generated.

For testing purposes I configured timer for 5s period and 1s CCR pulse. The expected behavior is to see update event every 5s and output compare 1s after that. What I see, however, is both events coming simultaneously every 5s.

Here is timer initialization generated by Cube:

  TIM_SlaveConfigTypeDef sSlaveConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};
  TIM_OC_InitTypeDef sConfigOC = {0};
  TIM_IC_InitTypeDef sConfigIC = {0};
  TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0};

  htim1.Instance = TIM1;
  htim1.Init.Prescaler = 5493;
  htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim1.Init.Period = 65535;
  htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim1.Init.RepetitionCounter = 0;
  htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_Base_Init(&htim1) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_TIM_OC_Init(&htim1) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_TIM_IC_Init(&htim1) != HAL_OK)
  {
    Error_Handler();
  }
  sSlaveConfig.SlaveMode = TIM_SLAVEMODE_RESET;
  sSlaveConfig.InputTrigger = TIM_TS_TI2FP2;
  sSlaveConfig.TriggerPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
  sSlaveConfig.TriggerFilter = 0;
  if (HAL_TIM_SlaveConfigSynchro(&htim1, &sSlaveConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sConfigOC.OCMode = TIM_OCMODE_TIMING;
  sConfigOC.Pulse = 13107;
  sConfigOC.OCPolarity = TIM_OCPOLARITY_LOW;
  sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
  sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
  if (HAL_TIM_OC_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
  {
    Error_Handler();
  }
  sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
  sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
  sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
  sConfigIC.ICFilter = 0;
  if (HAL_TIM_IC_ConfigChannel(&htim1, &sConfigIC, TIM_CHANNEL_2) != HAL_OK)
  {
    Error_Handler();
  }
  sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE;
  sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE;
  sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;
  sBreakDeadTimeConfig.DeadTime = 0;
  sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;
  sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
  sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;
  if (HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig) != HAL_OK)
  {
    Error_Handler();
  }

This is how I start timers:

HAL_TIM_OC_Start_IT(&htim1, TIM_CHANNEL_1);
HAL_TIM_Base_Start_IT(&htim1);

And here are event handlers:

void HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef *htim)
{
    printf("OC %lu\r\n", HAL_GetTick());
}

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    printf("UEV %lu\r\n", HAL_GetTick());
}

Any suggestions on what I am doing wrong? I suspect it could be some tiny detail in the configuration, but HAL documentation is not very helpful in this regard.

\$\endgroup\$
  • 1
    \$\begingroup\$ Why don't you use IDLE detection hardware? Do you want to support more then 2x baudrate period blanking time? \$\endgroup\$ – stiebrs Dec 11 '19 at 13:26
  • \$\begingroup\$ @stiebrs Not all STMs have hardware support for it. Also, I have to use HAL for this project and HAL does not properly support it either. \$\endgroup\$ – Maple Dec 11 '19 at 17:17
1
\$\begingroup\$

Just in case anybody tries to follow AN3109, here is what I did wrong:

  • forgot to configure TIM1 clock source as TIM_CLOCKSOURCE_INTERNAL;
  • did not enable TIM1 update interrupt in NVIC (it is not necessary for IDLE detection but required for this test);

In the end everything works as expected. Well... I'd expect HAL to support HW IDLE flag but that is probably to much to ask.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ I'd expect HAL to support HW IDLE flag [...] Are you looking for __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);? \$\endgroup\$ – Num Lock Oct 9 at 9:25
  • \$\begingroup\$ Also there is __HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE); as a check and __HAL_UART_CLEAR_IDLEFLAG(&huart1); to clear. \$\endgroup\$ – Num Lock Oct 9 at 9:27
  • \$\begingroup\$ @NumLock what I am looking for is something like "void HAL_UART_IdleCallback(UART_HandleTypeDef *huart);" that can be used alongside other callback functions, e.g. HAL_UART_TxCpltCallback. Without this one has to substitute default IRQ handling and basically keep copying changes every time new HAL version gets downloaded. \$\endgroup\$ – Maple Oct 9 at 22:31
  • \$\begingroup\$ BTW, the feature request for this sits on STM forum for more than 3 years already \$\endgroup\$ – Maple Oct 10 at 5:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.