cancel
Showing results for 
Search instead for 
Did you mean: 

Improving STM32F767 PWM shutoff speed for BLDC motor control

igdfr
Associate

I'm trying to implement 6-step BLDC commutation with an STM32F767 MCU, commutation table shown below

igdfr_0-1724815520340.png

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?

igdfr_1-1724815768534.png

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);

}

 

 

1 ACCEPTED SOLUTION

Accepted Solutions
igdfr
Associate

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

igdfr_1-1724866118015.png

 

Without manual event

igdfr_0-1724866076944.png

 

View solution in original post

1 REPLY 1
igdfr
Associate

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

igdfr_1-1724866118015.png

 

Without manual event

igdfr_0-1724866076944.png