2024-08-27 08:36 PM
I'm trying to implement 6-step BLDC commutation with an STM32F767 MCU, commutation table shown below
I have the 3 hall effects going to GPIO interrupt pins, the ISR code is pasted below. I used the generated code from the X-CUBE Motor workbench as a starting point for how to properly enable/disable the PWM lines.
void EXTI15_10_IRQHandler(void)
{
/* USER CODE BEGIN EXTI15_10_IRQn 0 */
/* USER CODE END EXTI15_10_IRQn 0 */
if (LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_13) != RESET)
{
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_13);
/* USER CODE BEGIN LL_EXTI_LINE_13 */
/* USER CODE END LL_EXTI_LINE_13 */
}
if (LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_14) != RESET)
{
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_14);
/* USER CODE BEGIN LL_EXTI_LINE_14 */
/* USER CODE END LL_EXTI_LINE_14 */
}
if (LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_15) != RESET)
{
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_15);
/* USER CODE BEGIN LL_EXTI_LINE_15 */
/* USER CODE END LL_EXTI_LINE_15 */
}
/* USER CODE BEGIN EXTI15_10_IRQn 1 */
HallInterruptCallback();
/* USER CODE END EXTI15_10_IRQn 1 */
}
void HallInterruptCallback(void)
{
GetCurrentHallStates();
switch ( hallState)
{
case STEP_1:
{
LL_TIM_CC_DisableChannel( TIM1, LL_TIM_CHANNEL_CH1 | LL_TIM_CHANNEL_CH1N );
// LL_TIM_OC_SetMode(TIM1, LL_TIM_CHANNEL_CH1 | LL_TIM_CHANNEL_CH1N, LL_TIM_OCMODE_FORCED_INACTIVE);
LL_TIM_GenerateEvent_UPDATE(TIM1);
LL_TIM_OC_SetCompareCH1 ( TIM1, 0 );
LL_TIM_OC_SetCompareCH2 ( TIM1, activeDutySetpoint );
LL_TIM_OC_SetCompareCH3 ( TIM1, 0 );
LL_TIM_CC_EnableChannel( TIM1, LL_TIM_CHANNEL_CH2 | LL_TIM_CHANNEL_CH2N | LL_TIM_CHANNEL_CH3 | LL_TIM_CHANNEL_CH3N );
}
break;
case STEP_2:
{
LL_TIM_CC_DisableChannel( TIM1, LL_TIM_CHANNEL_CH2 | LL_TIM_CHANNEL_CH2N );
// LL_TIM_OC_SetMode(TIM1, LL_TIM_CHANNEL_CH2 | LL_TIM_CHANNEL_CH2N, LL_TIM_OCMODE_FORCED_INACTIVE);
LL_TIM_GenerateEvent_UPDATE(TIM1);
LL_TIM_OC_SetCompareCH1 ( TIM1, activeDutySetpoint );
LL_TIM_OC_SetCompareCH2 ( TIM1, 0 );
LL_TIM_OC_SetCompareCH3 ( TIM1, 0 );
LL_TIM_CC_EnableChannel( TIM1, LL_TIM_CHANNEL_CH1 | LL_TIM_CHANNEL_CH1N | LL_TIM_CHANNEL_CH3 | LL_TIM_CHANNEL_CH3N );
}
break;
case STEP_3:
{
LL_TIM_CC_DisableChannel( TIM1, LL_TIM_CHANNEL_CH3 | LL_TIM_CHANNEL_CH3N );
// LL_TIM_OC_SetMode(TIM1, LL_TIM_CHANNEL_CH3 | LL_TIM_CHANNEL_CH3N, LL_TIM_OCMODE_FORCED_INACTIVE);
LL_TIM_GenerateEvent_UPDATE(TIM1);
LL_TIM_OC_SetCompareCH1 ( TIM1, activeDutySetpoint );
LL_TIM_OC_SetCompareCH2 ( TIM1, 0 );
LL_TIM_OC_SetCompareCH3 ( TIM1, 0 );
LL_TIM_CC_EnableChannel( TIM1, LL_TIM_CHANNEL_CH1 | LL_TIM_CHANNEL_CH1N | LL_TIM_CHANNEL_CH2 | LL_TIM_CHANNEL_CH2N);
}
break;
case STEP_4:
{
LL_TIM_CC_DisableChannel( TIM1, LL_TIM_CHANNEL_CH1 | LL_TIM_CHANNEL_CH1N );
// LL_TIM_OC_SetMode(TIM1, LL_TIM_CHANNEL_CH1 | LL_TIM_CHANNEL_CH1N, LL_TIM_OCMODE_FORCED_INACTIVE);
LL_TIM_GenerateEvent_UPDATE(TIM1);
LL_TIM_OC_SetCompareCH1 ( TIM1, 0 );
LL_TIM_OC_SetCompareCH2 ( TIM1, 0 );
LL_TIM_OC_SetCompareCH3 ( TIM1, activeDutySetpoint );
LL_TIM_CC_EnableChannel( TIM1, LL_TIM_CHANNEL_CH2 | LL_TIM_CHANNEL_CH2N | LL_TIM_CHANNEL_CH3 | LL_TIM_CHANNEL_CH3N );
}
break;
case STEP_5:
{
LL_TIM_CC_DisableChannel( TIM1, LL_TIM_CHANNEL_CH2 | LL_TIM_CHANNEL_CH2N );
// LL_TIM_OC_SetMode(TIM1, LL_TIM_CHANNEL_CH2 | LL_TIM_CHANNEL_CH2N, LL_TIM_OCMODE_FORCED_INACTIVE);
LL_TIM_GenerateEvent_UPDATE(TIM1);
LL_TIM_OC_SetCompareCH1 ( TIM1, 0 );
LL_TIM_OC_SetCompareCH2 ( TIM1, 0 );
LL_TIM_OC_SetCompareCH3 ( TIM1, activeDutySetpoint );
LL_TIM_CC_EnableChannel( TIM1, LL_TIM_CHANNEL_CH1 | LL_TIM_CHANNEL_CH1N | LL_TIM_CHANNEL_CH3 | LL_TIM_CHANNEL_CH3N );
}
break;
case STEP_6:
{
LL_TIM_CC_DisableChannel( TIM1, LL_TIM_CHANNEL_CH3 | LL_TIM_CHANNEL_CH3N );
// LL_TIM_OC_SetMode(TIM1, LL_TIM_CHANNEL_CH3 | LL_TIM_CHANNEL_CH3N, LL_TIM_OCMODE_FORCED_INACTIVE);
LL_TIM_GenerateEvent_UPDATE(TIM1);
LL_TIM_OC_SetCompareCH1 ( TIM1, 0 );
LL_TIM_OC_SetCompareCH2 ( TIM1, activeDutySetpoint );
LL_TIM_OC_SetCompareCH3 ( TIM1, 0 );
LL_TIM_CC_EnableChannel( TIM1, LL_TIM_CHANNEL_CH1 | LL_TIM_CHANNEL_CH1N | LL_TIM_CHANNEL_CH2 | LL_TIM_CHANNEL_CH2N );
}
break;
}
}
Overall the code works properly, however it takes longer than expected for the complementary PWM lines to shut off after the interrupt. As you can see in the code I've tried manually creating an update event, as well as using the forced inactive mode instead of disabling the channels. The above combination is the best so far but 3ms still seems excessively slow. Is there something else I can do to achieve faster shutoff (as well as startup) of the PWM channels in my ISR?
For reference my TIM1 initialization code is also pasted below
void MX_TIM1_Init(void)
{
/* USER CODE BEGIN TIM1_Init 0 */
/* USER CODE END TIM1_Init 0 */
LL_TIM_InitTypeDef TIM_InitStruct = {0};
LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0};
LL_TIM_BDTR_InitTypeDef TIM_BDTRInitStruct = {0};
LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
/* Peripheral clock enable */
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_TIM1);
/* TIM1 interrupt Init */
NVIC_SetPriority(TIM1_BRK_TIM9_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),5, 0));
NVIC_EnableIRQ(TIM1_BRK_TIM9_IRQn);
NVIC_SetPriority(TIM1_UP_TIM10_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),5, 0));
NVIC_EnableIRQ(TIM1_UP_TIM10_IRQn);
/* USER CODE BEGIN TIM1_Init 1 */
/* USER CODE END TIM1_Init 1 */
TIM_InitStruct.Prescaler = 109;
TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
TIM_InitStruct.Autoreload = 99;
TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
TIM_InitStruct.RepetitionCounter = 0;
LL_TIM_Init(TIM1, &TIM_InitStruct);
LL_TIM_EnableARRPreload(TIM1);
LL_TIM_SetClockSource(TIM1, LL_TIM_CLOCKSOURCE_INTERNAL);
LL_TIM_OC_EnablePreload(TIM1, LL_TIM_CHANNEL_CH1);
TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1;
TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_DISABLE;
TIM_OC_InitStruct.OCNState = LL_TIM_OCSTATE_DISABLE;
TIM_OC_InitStruct.CompareValue = 0;
TIM_OC_InitStruct.OCPolarity = LL_TIM_OCPOLARITY_HIGH;
TIM_OC_InitStruct.OCNPolarity = LL_TIM_OCPOLARITY_HIGH;
TIM_OC_InitStruct.OCIdleState = LL_TIM_OCIDLESTATE_LOW;
TIM_OC_InitStruct.OCNIdleState = LL_TIM_OCIDLESTATE_LOW;
LL_TIM_OC_Init(TIM1, LL_TIM_CHANNEL_CH1, &TIM_OC_InitStruct);
LL_TIM_OC_EnableFast(TIM1, LL_TIM_CHANNEL_CH1);
LL_TIM_OC_EnablePreload(TIM1, LL_TIM_CHANNEL_CH2);
LL_TIM_OC_Init(TIM1, LL_TIM_CHANNEL_CH2, &TIM_OC_InitStruct);
LL_TIM_OC_EnableFast(TIM1, LL_TIM_CHANNEL_CH2);
LL_TIM_OC_EnablePreload(TIM1, LL_TIM_CHANNEL_CH3);
LL_TIM_OC_Init(TIM1, LL_TIM_CHANNEL_CH3, &TIM_OC_InitStruct);
LL_TIM_OC_EnableFast(TIM1, LL_TIM_CHANNEL_CH3);
LL_TIM_SetTriggerOutput(TIM1, LL_TIM_TRGO_RESET);
LL_TIM_SetTriggerOutput2(TIM1, LL_TIM_TRGO2_RESET);
LL_TIM_DisableMasterSlaveMode(TIM1);
TIM_BDTRInitStruct.OSSRState = LL_TIM_OSSR_DISABLE;
TIM_BDTRInitStruct.OSSIState = LL_TIM_OSSI_DISABLE;
TIM_BDTRInitStruct.LockLevel = LL_TIM_LOCKLEVEL_OFF;
TIM_BDTRInitStruct.DeadTime = 0;
TIM_BDTRInitStruct.BreakState = LL_TIM_BREAK_DISABLE;
TIM_BDTRInitStruct.BreakPolarity = LL_TIM_BREAK_POLARITY_HIGH;
TIM_BDTRInitStruct.BreakFilter = LL_TIM_BREAK_FILTER_FDIV1;
TIM_BDTRInitStruct.Break2State = LL_TIM_BREAK2_DISABLE;
TIM_BDTRInitStruct.Break2Polarity = LL_TIM_BREAK2_POLARITY_HIGH;
TIM_BDTRInitStruct.Break2Filter = LL_TIM_BREAK2_FILTER_FDIV1;
TIM_BDTRInitStruct.AutomaticOutput = LL_TIM_AUTOMATICOUTPUT_DISABLE;
LL_TIM_BDTR_Init(TIM1, &TIM_BDTRInitStruct);
/* USER CODE BEGIN TIM1_Init 2 */
LL_TIM_EnableCounter(TIM1);
LL_TIM_EnableAllOutputs(TIM1);
/* USER CODE END TIM1_Init 2 */
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOB);
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOE);
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOA);
/**TIM1 GPIO Configuration
PB0 ------> TIM1_CH2N
PB1 ------> TIM1_CH3N
PE8 ------> TIM1_CH1N
PE9 ------> TIM1_CH1
PA9 ------> TIM1_CH2
PA10 ------> TIM1_CH3
*/
GPIO_InitStruct.Pin = MM_PWM_VL_Pin;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
GPIO_InitStruct.Alternate = LL_GPIO_AF_1;
LL_GPIO_Init(MM_PWM_VL_GPIO_Port, &GPIO_InitStruct);
GPIO_InitStruct.Pin = MM_PWM_WL_Pin;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
GPIO_InitStruct.Alternate = LL_GPIO_AF_1;
LL_GPIO_Init(MM_PWM_WL_GPIO_Port, &GPIO_InitStruct);
GPIO_InitStruct.Pin = MM_PWM_UL_Pin;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
GPIO_InitStruct.Alternate = LL_GPIO_AF_1;
LL_GPIO_Init(MM_PWM_UL_GPIO_Port, &GPIO_InitStruct);
GPIO_InitStruct.Pin = MM_PWM_UH_Pin;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
GPIO_InitStruct.Alternate = LL_GPIO_AF_1;
LL_GPIO_Init(MM_PWM_UH_GPIO_Port, &GPIO_InitStruct);
GPIO_InitStruct.Pin = MM_PWM_VH_Pin;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
GPIO_InitStruct.Alternate = LL_GPIO_AF_1;
LL_GPIO_Init(MM_PWM_VH_GPIO_Port, &GPIO_InitStruct);
GPIO_InitStruct.Pin = MM_PWM_WH_Pin;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
GPIO_InitStruct.Alternate = LL_GPIO_AF_1;
LL_GPIO_Init(MM_PWM_WH_GPIO_Port, &GPIO_InitStruct);
}
Solved! Go to Solution.
2024-08-28 10:28 AM
My issue turned out to be with my GPIO interrupt settings, not the timer. I thought I had the interrupts set to trigger on both rising and falling edges but they were incorrectly set to only trigger on rising edges. With that resolved my timing is much improved.
I also think I should have used a COM event rather than an update event to try and force the change. I've run the code with and without the event and it does not seem to make a difference
With manual COM event
Without manual event
2024-08-28 10:28 AM
My issue turned out to be with my GPIO interrupt settings, not the timer. I thought I had the interrupts set to trigger on both rising and falling edges but they were incorrectly set to only trigger on rising edges. With that resolved my timing is much improved.
I also think I should have used a COM event rather than an update event to try and force the change. I've run the code with and without the event and it does not seem to make a difference
With manual COM event
Without manual event