Skip to main content
Senior
April 12, 2021
Question

How to setting TIM CHx/CHxN for six step BLDC motor control?

  • April 12, 2021
  • 9 replies
  • 8258 views

I want to control BLDC motor in six step method using STM32G474RE (nucleo board). 

I'm using TIM1 CHx/CHxN complementary PWM output for 6 pwm signal, like below:

 0693W000008zFe0QAE.pngThe problem I have is that the PWM output does not switch synchronously when switching steps, like below:

0693W000008zFePQAU.pngThe desired switch looks like this:

0693W000008zFhdQAE.pngHow can I synchronaize the PWM when switching the steps, with STM32G474 MPU?

Please give me some advice.

My setting for TIM1 PWM:

 0693W000008zFtqQAE.png0693W000008zG5VQAU.pngAnd, a part of code for six step:

void MC_NEXT_step(mc_six_step_t *p)

{

    switch (p->step_count)

    {

    case 0:

        HAL_TIM_PWM_Stop(&htim1, TIM_CHANNEL_3);

        HAL_TIMEx_PWMN_Stop(&htim1, TIM_CHANNEL_3);

        __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, p->compare_value);

        HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);

        HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_1);

        break;

    case 1:

        HAL_TIMEx_PWMN_Stop(&htim1, TIM_CHANNEL_2);

        __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_3, p->pwm.period);

        HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_3);

        break;

    case 2:

        HAL_TIM_PWM_Stop(&htim1, TIM_CHANNEL_1);

        HAL_TIMEx_PWMN_Stop(&htim1, TIM_CHANNEL_1);

        __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2, p->compare_value);

        HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2);

        HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_2);

        break;

    case 3:

        HAL_TIMEx_PWMN_Stop(&htim1, TIM_CHANNEL_3);

        __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, p->pwm.period);

        HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_1);

        break;

    case 4:

        HAL_TIM_PWM_Stop(&htim1, TIM_CHANNEL_2);

        HAL_TIMEx_PWMN_Stop(&htim1, TIM_CHANNEL_2);

        __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_3, p->compare_value);

        HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_3);

        HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_3);

        break;

    case 5:

        HAL_TIMEx_PWMN_Stop(&htim1, TIM_CHANNEL_1);

        __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2, p->pwm.period);

        HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_2);

        break;

    default:

        break;

    }

}

This topic has been closed for replies.

9 replies

Javier1
Principal
April 12, 2021

i also had this issue, dont remember exactly how we fixed it but check the datasheet about the pwm modes.

You should check the pwm forced inactive modes of every pwm channel driving each one of your H bridge´s low branch mosfet.

#define TIM_OCMODE_TIMING                  0x00000000U                                             /*!< Frozen                                */

#define TIM_OCMODE_ACTIVE                  TIM_CCMR1_OC1M_0                                        /*!< Set channel to active level on match  */

#define TIM_OCMODE_INACTIVE                TIM_CCMR1_OC1M_1                                        /*!< Set channel to inactive level on match */

#define TIM_OCMODE_TOGGLE                  (TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_0)                   /*!< Toggle                                */

#define TIM_OCMODE_PWM1                    (TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1)                   /*!< PWM mode 1                            */

#define TIM_OCMODE_PWM2                    (TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_0) /*!< PWM mode 2                            */

#define TIM_OCMODE_FORCED_ACTIVE           (TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_0)                   /*!< Force active level                    */

#define TIM_OCMODE_FORCED_INACTIVE         TIM_CCMR1_OC1M_2                                        /*!< Force inactive level                  */

hit me up in https://www.linkedin.com/in/javiermuñoz/
Kenji1Author
Senior
April 12, 2021

Javier-san

Thank you. I will check the data sheet.

Kenji1Author
Senior
April 16, 2021

Hi Javier-san

I had modified the code with reference to a AN4776 and your advice. I can synchronize the six step output using TIM1 and TIM2. 

Now, I have another problem.

Please show the figure above, PWM-UH, PWM-H and Six Step Trig signal.

#1. When switch, PWM-UL output set to high. The desired output is low. I set TIM_OCMODE_FORCED_INACTIVE in the code. I've tried other settings, but I can't get it to low. Do you know the way to set low both PWM-UL and PWM-UH on switching timing?

#2. When use TIM_OCMODE_FORCED_INACTIVE, I restarted the output with TIM_OCMODE_FORCED_ACTIVE and the PWM output does not come out. It possible to use TIM_OCMODE_INACTIVE/TIM_OCMODE_ACTIVE, but there problem on switching. It have a problem that the output does not switch properly when switching six steps. Do you have any experience about this?

0693W000008zkgMQAQ.pngAlso, the code I used for this test:

void MC_SixStep_SetNextStep(void)
{
 
 HAL_GPIO_TogglePin(DBG2_GPIO_Port, DBG2_Pin);
 
 switch (hBLDC.sixStepCount)
 {
 case 0:
 /* UH, VL */
 htim1.Instance->CCER &= (uint16_t) ~TIM_CCER_CC2NE;
 
 __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_3, hBLDC.pwm.period);
 htim1.Instance->CCER |= (uint16_t) TIM_CCER_CC3NE;
 break;
 
 case 1:
 /* UH, WL */
// htim1.Instance->CCER &= (uint16_t) ~TIM_CCER_CC1E;
// htim1.Instance->CCER &= (uint16_t) ~TIM_CCER_CC1NE;
 
 /* Reset the OC1M bits in the CCMR1 register */
 TIM1->CCMR1 &= TIM_CCMR1_OC2M;
 TIM1->CCMR1 |= TIM_OCMODE_FORCED_INACTIVE;
 
 __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2, hBLDC.pwm.width - 1);
 htim1.Instance->CCER |= (uint16_t) TIM_CCER_CC2E;
 htim1.Instance->CCER |= (uint16_t) TIM_CCER_CC2NE;
 break;
 
 case 2:
 /* VH, WL */
 htim1.Instance->CCER &= (uint16_t) ~TIM_CCER_CC3NE;
 
 __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, hBLDC.pwm.period);
 htim1.Instance->CCER |= (uint16_t) TIM_CCER_CC1NE;
 
 break;
 
 case 3:
 /* VH, UL */
 htim1.Instance->CCER &= (uint16_t) ~TIM_CCER_CC2E;
 htim1.Instance->CCER &= (uint16_t) ~TIM_CCER_CC2NE;
 
 __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_3, hBLDC.pwm.width - 1);
 htim1.Instance->CCER |= (uint16_t) TIM_CCER_CC3E;
 htim1.Instance->CCER |= (uint16_t) TIM_CCER_CC3NE;
 break;
 
 case 4:
 /* WH, UL */
 htim1.Instance->CCER &= (uint16_t) ~TIM_CCER_CC1NE;
 
 __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2, hBLDC.pwm.period);
 htim1.Instance->CCER |= (uint16_t) TIM_CCER_CC2NE;
 break;
 
 case 5:
 /* WH, VL */
 
 __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, hBLDC.pwm.width - 1);
// htim1.Instance->CCER |= (uint16_t) TIM_CCER_CC1E;
// htim1.Instance->CCER |= (uint16_t) TIM_CCER_CC1NE;
 
 TIM1->CCMR1 |= TIM_OCMODE_FORCED_ACTIVE;
 TIM1->CCER |= TIM_CCER_CC1E;
 TIM1->CCER |= TIM_CCER_CC1NE;
 
 htim1.Instance->CCER &= (uint16_t) ~TIM_CCER_CC3E;
 htim1.Instance->CCER &= (uint16_t) ~TIM_CCER_CC3NE;
 
 break;
 
 default:
 break;
 }
 
 hBLDC.sixStepCount++;
 hBLDC.sixStepCount %= 6;
}

Javier1
Principal
April 19, 2021

@Kenji​  sorry i didnt noticed your reply, for some reason this forum only notifies me if my name is mentioned.

for your problem 1 Do you know the way to set low both PWM-UL and PWM-UH on switching timing?

Yo should disable the channels during that step.

for your problem 2 i didnt understood sorry

hit me up in https://www.linkedin.com/in/javiermuñoz/
Kenji1Author
Senior
April 19, 2021

Hi Javier-san

#.1 The way to set low both PWM-UL and PWM-UH is to write code like this:

 htim1.Instance->CCER &= (uint16_t) ~TIM_CCER_CC1E;
 htim1.Instance->CCER &= (uint16_t) ~TIM_CCER_CC1NE;

​But this code not not switch properly like below:

0693W000008zvUWQAY.png#2. To switch properly, I can use this code.

 TIM1->CCMR1 &= TIM_CCMR1_OC2M;
 TIM1->CCMR1 |= TIM_OCMODE_FORCED_INACTIVE;

But when use this code, the PWM output cannot be restarted as shown in the figure.

waclawek.jan
Super User
April 16, 2021

Draw a table with content of individual bits/bitfields of relevant timer registers, corresponding to the individual fields in the captured waveform.

JW

Kenji1Author
Senior
April 16, 2021

Hi Jan-san

I'm a little confused. You want to see the TIM1/TIM2 setting?

waclawek.jan
Super User
April 16, 2021

For each "field" in the waveform snapshot, post what is the conten of TIM registers.

In other words, in debugger, stop execution after you've made every change for given step, and read out the content of TIM registers and write them down for that step; then repeat for all steps. Post the resulting table.

JW

Kenji1Author
Senior
April 17, 2021

Hi Jan-san

These are setting of the TIM1 after preset the commutation.

How you see, Is there anything wrong with TIM1 setting?

Step0:

 switch (hBLDC.sixStepCount)
 {
 case 0:
 /* UH, VL */
 htim1.Instance->CCER &= (uint16_t) ~TIM_CCER_CC2NE;
 
 __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_3, hBLDC.pwm.period);
 htim1.Instance->CCER |= (uint16_t) TIM_CCER_CC3NE;
 break;

0693W000008zq6ZQAQ.pngStep1:

 switch (hBLDC.sixStepCount)
 {
 case 1:
 /* UH, WL */
// htim1.Instance->CCER &= (uint16_t) ~TIM_CCER_CC1E;
// htim1.Instance->CCER &= (uint16_t) ~TIM_CCER_CC1NE;
 
 /* Reset the OC1M bits in the CCMR1 register */
 TIM1->CCMR1 &= TIM_CCMR1_OC2M;
 TIM1->CCMR1 |= TIM_OCMODE_FORCED_INACTIVE;
 
 __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2, hBLDC.pwm.width - 1);
 htim1.Instance->CCER |= (uint16_t) TIM_CCER_CC2E;
 htim1.Instance->CCER |= (uint16_t) TIM_CCER_CC2NE;
 break;

0693W000008zq6eQAA.pngStep2:

 switch (hBLDC.sixStepCount)
 {
 case 2:
 /* VH, WL */
 htim1.Instance->CCER &= (uint16_t) ~TIM_CCER_CC3NE;
 
 __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, hBLDC.pwm.period);
 htim1.Instance->CCER |= (uint16_t) TIM_CCER_CC1NE;
 
 break;

0693W000008zq6tQAA.pngStep3:

switch (hBLDC.sixStepCount)
 {
 case 3:
 /* VH, UL */
 htim1.Instance->CCER &= (uint16_t) ~TIM_CCER_CC2E;
 htim1.Instance->CCER &= (uint16_t) ~TIM_CCER_CC2NE;
 
 __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_3, hBLDC.pwm.width - 1);
 htim1.Instance->CCER |= (uint16_t) TIM_CCER_CC3E;
 htim1.Instance->CCER |= (uint16_t) TIM_CCER_CC3NE;
 break;

0693W000008zq6yQAA.pngStep4:

 switch (hBLDC.sixStepCount)
 {
 case 4:
 /* WH, UL */
 htim1.Instance->CCER &= (uint16_t) ~TIM_CCER_CC1NE;
 
 __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2, hBLDC.pwm.period);
 htim1.Instance->CCER |= (uint16_t) TIM_CCER_CC2NE;
 break;

0693W000008zq7DQAQ.pngStep5:

 switch (hBLDC.sixStepCount)
 {
 case 5:
 /* WH, VL */
 
 __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, hBLDC.pwm.width - 1);
// htim1.Instance->CCER |= (uint16_t) TIM_CCER_CC1E;
// htim1.Instance->CCER |= (uint16_t) TIM_CCER_CC1NE;
 
 TIM1->CCMR1 |= TIM_OCMODE_FORCED_ACTIVE;
 TIM1->CCER |= TIM_CCER_CC1E;
 TIM1->CCER |= TIM_CCER_CC1NE;
 
 htim1.Instance->CCER &= (uint16_t) ~TIM_CCER_CC3E;
 htim1.Instance->CCER &= (uint16_t) ~TIM_CCER_CC3NE;
 
 break;

0693W000008zq7IQAQ.png

waclawek.jan
Super User
April 18, 2021

I don't do motor control and I don't know what are the expected waveforms, but I would expect the CCMR and CCER settings for the 3 channels to "rotate" between the steps, which they don't.

JW

Karthik DM
Associate III
April 20, 2021

I think your triggering the required channel for every case. you need to stop the third floating channel in every case.

Kenji1Author
Senior
April 21, 2021

Hi Karthik-san

The combination of these registers in TIM1 and the functions used for commutation updates changes the situation.

I don't know the best combination for now. PWMs are not synchronized.

0693W00000AM3F8QAL.png

/***************************************/
/* Disabling OCx and OCNx outputs */
/***************************************/
	
/* Disable the Channel: Reset the CC1E Bit */
 TIM1->CCER &= (uint16_t) ~TIM_CCER_CC1E;
	
/* Disable the Channel: Reset the CC1E Bit */
 TIM1->CCER &= (uint16_t) ~TIM_CCER_CC1NE;
	
#elif defined (SET_CHANNEL_TO_INACTIVE_STATE) /* not DISABLING_CHANNELS_OUTPUTS */
	
/****************************************/
/* Using OCxM bits to inactive channel */
/****************************************/
	
/* Reset the OC1M bits in the CCMR1 register */
TIM1->CCMR1 &= TIM_CCMR1_OC2M;
 
/* Configure the OC2M bits in the CCMRx register to inactive mode*/
TIM1->CCMR1 |= TIM_OCMODE_FORCED_INACTIVE;

I just want to do this, using STM32 MPU.

That all PWMs are synchronized on commutation timing.

0693W00000AM3GpQAL.png