3
\$\begingroup\$

I'm using a STM32 for PAC (phase-angle control) of a mains load (cos phi > 0.95) and I'm unsure how I should implement the firing of the pulses.

My first try was to use two timers and a EXTI pin which measures the zero-crossing.

When I detect a zero-crossing, I start a delay timer in one-shot mode and delay according my phase angle. After the delay (output compare trigger,) I trigger an interrupt, where I deactivate the delay timer (such that I don't need to finish the period) and start a pulse timer in single shot-mode that gives a 100us pulse.

Concept: enter image description here

Glitches: enter image description here

This works pretty good so far, but I have seen that when I change the PAC angle several times I sometimes get 2-3 mains periods that the delay timer does not trigger anymore. The timer is started correctly from the EXTI interrupt, but no delay timer interrupt is triggered.

I also attached the important code, where the ZC Interrupt triggers the CM_POWER_ZC_Trigger() function that starts the delay timer with TIMER_PAC_Single_Delay_Trigger(). The delay timer interrupt should then trigger HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef *htim) (but it does not always), which starts then again a pulse.

Any ideas how I can improve PAC? What I already tried is to use Timer3 (pulse timer) in slave mode of Timer2 (delay timer) and then not use any interrupts, but I could not achieve proper functions with just one-pulse mode. The thing is, that I cannot leave the delay timer period constant at 10ms (50Hz) since it should also work for 60Hz and that's why I disable the timer after the output-compare interrupt.

STM32 HAL Settings:

//PAC Delay Timer
static void MX_TIM2_Init(void)
{

  /* USER CODE BEGIN TIM2_Init 0 */

  /* USER CODE END TIM2_Init 0 */

  TIM_MasterConfigTypeDef sMasterConfig = {0};
  TIM_OC_InitTypeDef sConfigOC = {0};

  /* USER CODE BEGIN TIM2_Init 1 */

  /* USER CODE END TIM2_Init 1 */
  htim2.Instance = TIM2;
  htim2.Init.Prescaler = 31;
  htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim2.Init.Period = 65535;
  htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_OC_Init(&htim2) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_TIM_OnePulse_Init(&htim2, TIM_OPMODE_SINGLE) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sConfigOC.OCMode = TIM_OCMODE_TIMING;
  sConfigOC.Pulse = 0;
  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  if (HAL_TIM_OC_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM2_Init 2 */

  /* USER CODE END TIM2_Init 2 */

}

//PAC Puls Timer
static void MX_TIM3_Init(void)
{

  /* USER CODE BEGIN TIM3_Init 0 */

  /* USER CODE END TIM3_Init 0 */

  TIM_MasterConfigTypeDef sMasterConfig = {0};
  TIM_OC_InitTypeDef sConfigOC = {0};

  /* USER CODE BEGIN TIM3_Init 1 */

  /* USER CODE END TIM3_Init 1 */
  htim3.Instance = TIM3;
  htim3.Init.Prescaler = 0;
  htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim3.Init.Period = 3199;
  htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
  if (HAL_TIM_PWM_Init(&htim3) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_TIM_OnePulse_Init(&htim3, TIM_OPMODE_SINGLE) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sConfigOC.OCMode = TIM_OCMODE_PWM2;
  sConfigOC.Pulse = 0;
  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  if (HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_4) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM3_Init 2 */

  /* USER CODE END TIM3_Init 2 */
  HAL_TIM_MspPostInit(&htim3);

}

/****************************/
/* Defines in Header file   */
/****************************/
void TIMER_PAC_Single_Puls_Trigger()
{
    TIM3->CR1 &= ~TIM_CR1_CEN; //This re-enables the One-Shot Timer
    TIM3->CR1 |= TIM_CR1_CEN;
}

/****************************/
/* Defines in Header file   */
/****************************/
void TIMER_PAC_Single_Delay_Trigger()
{
    TIM2->CR1 &= ~TIM_CR1_CEN; //This re-enables the One-Shot Timer
    TIM2->CR1 |= TIM_CR1_CEN;
}

/********************/
/* External defined */
/********************/
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    if(GPIO_Pin == CM_ZC_PIN)
    {
        /************************/
        /* Set a trigger puls   */
        /************************/
        CM_POWER_ZC_Trigger();

        /****************************************/
        /* Update the cycle time from the ZC    */
        /****************************************/
        cm_master.power.zc_cycle_time_us = Get_F_Main_Time_us();

    }
}

/********************/
/* External defined */
/********************/
void HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef *htim)
{
    if(htim->Instance == TIM2)
    {
        /************************************************/
        /* Extremly important to stop the timer here    */
        /************************************************/
        TIM2->CR1 &= ~TIM_CR1_CEN;
        TIM2->CNT = 0;

        /************/
        /* Debug    */
        /************/
        HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_14);

        /****************/
        /* Trigger Puls */
        /****************/
        TIMER_PAC_Single_Puls_Trigger();
    }

}

/****************************/
/* Defines in Header file   */
/****************************/
void CM_POWER_ZC_Trigger()
{
    cm_master.power.zc_cnt++;
    if(!cm_master.power.pac_enabled)
    {
        return;
    }


    /************************************************/
    /* Trigger the delay according the PAC cycle    */
    /************************************************/
#ifdef PAC_TRIGGER_ACTIVE
    TIMER_PAC_Single_Delay_Trigger();
#endif

}

Edit: I tried now to use only Timer3 and start it over the EXTI interrupt and depicted above. The glitch is still there and I don't use any interrupt except the EXTI interrupt now and the EXTI interrupt comes reliable. Somehow I think the timer runs the full period of ARR = 65535 and is not stopped in some cases. How should I properly reset the timer in one-shot mode so that it won't glitch?

\$\endgroup\$
1
  • \$\begingroup\$ I cannot see where you dynamically change the PAC angle. You may need a double buffer concept to update the delay. So you cannot miss a trigger when you apply a shorter delay that already has passed. \$\endgroup\$
    – Jens
    Jun 12, 2022 at 19:23

1 Answer 1

1
\$\begingroup\$

I could only give you some basic habits.

  1. In these situation, you are sure of one thing : all could be wrong.
  2. If you want to set (or unset), don't use toggle, but set (or unset) it.
  3. It is not possible to follow the debugger at such a speed, so build your own 'on the fly' debug mechanics : fill your own stack on selected place where you put basic information : code source line number, high resolution time stamp, selected parameter.
  4. Run, then attempt to understand, looking at your stack, what's going on.
\$\endgroup\$

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.