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?