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.
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?

