cancel
Showing results for 
Search instead for 
Did you mean: 

Zero crossing interrupt timer implementation

SPetr.6
Associate II

I am working on creating a reverse phase dimmer where the on/off timing for the 120/240 VAC sine wave is handled by an STM32L051C8Tx MCU. There is a separate analog circuit that generates a 3V3 pulse at every zero crossing and I take this as an input on a GPIO with the mode "External Interrupt Mode with Rising Edge trigger detection" (I am using CubeMx for the MCU config).

In gpio.c I have the following:

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    HAL_GPIO_WritePin(TRIAC_GPIO_Port, TRIAC_Pin, GPIO_PIN_SET);
    htim6.Instance->CNT = 0;
    HAL_TIM_Base_Start_IT(&htim6);
}

I then have TIM6 setup with "One Pulse Mode" with the following interrupt handler in tim.c:

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
  if(htim->Instance == TIM6) {
    HAL_GPIO_WritePin(TRIAC_GPIO_Port, TRIAC_Pin, GPIO_PIN_RESET);
  }
}

In main.c I have:

/* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_TIM6_Init();
  /* USER CODE BEGIN 2 */
  HAL_TIM_Base_Start_IT(&htim6);
  /* USER CODE END 2 */

For the moment I tested out this code on a Discovery STM32F030 board and it works fine, but I am not sure if I am doing this the best way. For example, once the interrupt fires, it needs to be reset. What is the proper way of resetting the timer interrupt when in Once Pulse Mode? For the moment I am just calling HAL_TIM_Base_Start_IT(&htim6) every time I get a zero crossing GPIO interrupt. Also, I have noticed that the first iteration does not work correctly. That is, on the very first zero crossing GPIO interrupt, the timer completes immediately and I just get a very brief (couple microseconds) pulse to the triac. I read somewhere that I needed to call

__HAL_TIM_CLEAR_IT(&htim6, TIM_IT_UPDATE);

before the first HAL_TIM_Base_Start_IT(&htim6) in the main file, but this did not work.

What is the proper way to adjust the counter period in real time here? The counter period will essentially be updated a few times per second from a PID loop.

Perhaps there is a better way to be implementing this entirely using timers that I am not aware of. For example, I do have TIM21 available, which might have some feature that I am not aware of that makes the solution overall more elegant. For example, one thought I had was using TIM21 in "Input Capture direct mode", which would allow me to get the time between zero crossings (handy for determining the maximum possible counter period value, if in 50 or 60Hz) easily with something like:

void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) {
  if(htim->Instance == TIM21) {
    // Read TIM21 channel 1 input capture value
    g_tim_zero crossing period = __HAL_TIM_GetCompare(&htim2, TIM_CHANNEL_1);
    // Reset counter after input capture interrupt occurs
    __HAL_TIM_SetCounter(&htim2, 0);
  }
}

With the zero crossing pulse as input to the timer. But would this require me to have another separate timer for handling turning the triac off?

Anyway, I would greatly appreciate some insight on how I can improve this setup or just some general feedback. Thank you for your time.

0 REPLIES 0