cancel
Showing results for 
Search instead for 
Did you mean: 

STM32G483VET6 3-phase 120 degree phase shifted PWM with complementary channels and dead time

Jony404
Associate II
Hi,
 
I am working on a motor control project that need to generate 3 sine waves with 120 degree phase shift to power a 3-phase AC motor. The target MCU is an STM32G483VET6 with 100 pins, and I though that for the first step  to generate 3 PWM with phase shift shouldn't be a problem with this MCU, but it is 🙂
 
Ok, so as stated in the reference manual, there are TIMx internal trigger connections, which I thought can be used for chaining 3 timers: TIM1, TIM8, TIM20 because they can generate complementary outputs as well:
 
TIMx_internal_trigger_connections.PNG
According to Table 252, I should have easily set this up with MX. I started to process this table, TIM1 is the first timer which can trigger TIM8 through ITR0, and TIM8 can trigger TIM20 through ITR5. 
 
TIM1 setup:
- Channel 1 -> PWM Generation CH1 CH1N
- Channel 2 -> Output Compare No Output
- Counter Settings:
   - Prescaler: 47 (Because I am using 48MHz clock from HSI)
   - Counter Period: 9999 (100 Hz, nothing fancy)
- Trigger Output (TRGO) Parameters:
   - Trigger Event Selection TRGO: Output Compare (OC2REF)
- PWM Generation Channel 1 and 1N
   - Mode: PWM Mode 1
   - Pulse: 5000 (50% duty)
- Output Compare No Output Channel 2
   - Mode: Active Level On Match
   - Pulse: 3333 (9999 / 3 = 3333, because 360 degree / 3 = 120 degree)
 
TIM8 setup:
- Slave Mode: Trigger Mode
   - Trigger Source: ITR0
- Channel 1 -> PWM Generation CH1 CH1N
- Channel 2 -> Output Compare No Output
- Counter Settings:
   - Prescaler: 47 (Because I am using 48MHz clock from HSI)
   - Counter Period: 9999 (100 Hz, nothing fancy)
- Trigger Output (TRGO) Parameters:
   - Trigger Event Selection TRGO: Output Compare (OC2REF)
- PWM Generation Channel 1 and 1N
   - Mode: PWM Mode 1
   - Pulse: 5000 (50% duty)
- Output Compare No Output Channel 2
   - Mode: Active Level On Match
   - Pulse: 3333 (9999 / 3 = 3333, because 360 degree / 3 = 120 degree)
 
TIM20 setup:
- Slave Mode: Trigger Mode
   - Trigger Source: ITR5
- Channel 1 -> PWM Generation CH1 CH1N
- Counter Settings:
   - Prescaler: 47 (Because I am using 48MHz clock from HSI)
   - Counter Period: 9999 (100 Hz, nothing fancy)
- PWM Generation Channel 1 and 1N
   - Mode: PWM Mode 1
   - Pulse: 5000 (50% duty)
 
My custom code:

 

  /* USER CODE BEGIN 2 */
  if (HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1) != HAL_OK)
	  Error_Handler();
  if (HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_1) != HAL_OK) // turn on complementary channel
	  Error_Handler();
  if (HAL_TIM_OC_Start(&htim1, TIM_CHANNEL_2) != HAL_OK)
	  Error_Handler();


  if (HAL_TIM_PWM_Start(&htim8, TIM_CHANNEL_1) != HAL_OK)
	  Error_Handler();
  if (HAL_TIMEx_PWMN_Start(&htim8, TIM_CHANNEL_1) != HAL_OK) // turn on complementary channel
	  Error_Handler();
  if (HAL_TIM_OC_Start(&htim8, TIM_CHANNEL_2) != HAL_OK)
	  Error_Handler();


  if (HAL_TIM_PWM_Start(&htim20, TIM_CHANNEL_1) != HAL_OK)
	  Error_Handler();
  if (HAL_TIMEx_PWMN_Start(&htim20, TIM_CHANNEL_1) != HAL_OK) // turn on complementary channel
	  Error_Handler();
  /* USER CODE END 2 */

 

 
The outcome is that TIM1 and TIM8 are basically the same, with just a few microseconds delay between each other:
Screenshot_20231113_151908_Scoppy.jpg
 
TIM20 is 120 degree phase shifted from TIM8:
Screenshot_20231113_152037_Scoppy.jpg
 
What am I doing wrong ?
Every idea is highly appreciated.
 
Bests,
Zsolt
1 ACCEPTED SOLUTION

Accepted Solutions

Oh, I forget to post the solution for the problem:

  /* USER CODE BEGIN 2 */

  htim20.Instance->CCER |= (TIM_CCx_ENABLE << TIM_CHANNEL_1);
  htim20.Instance->CCER |= (TIM_CCx_ENABLE << 0x02);			// complementary channel
  htim20.Instance->BDTR |= (TIM_BDTR_MOE);  // set outputs
  htim20.Instance->CR1 &= ~(TIM_CR1_CEN);   // disable timer
  htim20.Instance->CNT = 0;                 // reset timer

  htim8.Instance->CCER |= (TIM_CCx_ENABLE << TIM_CHANNEL_1);
  htim8.Instance->CCER |= (TIM_CCx_ENABLE << 0x02);			// complementary channel
  htim8.Instance->CCER |= (TIM_CCx_ENABLE << 0x04);			// Compare on Channel 2 output
  htim8.Instance->BDTR |= (TIM_BDTR_MOE);  // set outputs
  htim8.Instance->CR1 &= ~(TIM_CR1_CEN);   // disable timer
  htim8.Instance->CNT = 0;                 // reset timer

  htim1.Instance->CCER |= (TIM_CCx_ENABLE << TIM_CHANNEL_1);
  htim1.Instance->CCER |= (TIM_CCx_ENABLE << 0x02);			// complementary channel
  htim1.Instance->CCER |= (TIM_CCx_ENABLE << 0x04);			// Compare on Channel 2 output
  htim1.Instance->BDTR |= (TIM_BDTR_MOE);  // set outputs
  htim1.Instance->CR1 &= ~(TIM_CR1_CEN);   // disable timer
  htim1.Instance->CNT = 0;                 // reset timer
  htim1.Instance->CR1 |= (TIM_CR1_CEN);    // enable timer

  /* USER CODE END 2 */

View solution in original post

6 REPLIES 6
cedric H
ST Employee

Hello @Jony404 ,

If your main point is to drive only one motor with three phases, you need only one timer. The 3 sine waves are generated thanks to the variation of the duty cycles of the channels CH1/CH1N, CH2/CH2N, CH3/CH3N. Those duty cycles are configured with the respective Capture and Compare registers respectively CCR1,CCR2,CCR3.

Now the question, I would ask is what kind of AC motor do you use ? Synchronous or Asynchronous ?

The best advice I can give you is to download the MCSDK and generate a motor control project based on STM32G4. MCSDK fully supports Synchronous ( PMSM ) motors and provides example for Asynchronous motor. In any case, the MCSDK will generate for you the complete source code of your project and also a cubeMX IOC file. It will be a very good starting point for learning the basics of STM32 configuration for motor control application.

Hope it helps.

Cedric

Hello @cedric H ,

Wow, I didn't assume that such an advanced tool is available, I am really impressed, nice job!

I dowloaded it, and trying to configure what I would like to achieve, and I will come back with the result.

Many thank!

Zsolt

The original question's problem usually lies in incorrectly started slave timers. The point of slave-mode controller in Trigger mode is, that when trigger arrives, the TIMx_CR1.CEN bit is set, that's all. In other words, you are not supposed to set that bit "manually", and you are also not supposed to enable the master timer (set its TIMx_CR.CEN) before all slave timers are set up. A good way to check if this all is fulfilled is to set up everything except setting master timer's TIMx_CR.CEN, and at that point read out and check all timer's registers if they are set as expected.

When it comes to something which can't be get working simply by clicking in CubeMX, Cube usually gets more into way than helps, as the various Cube/HAL functions have intertwined behaviour and consequences of calling each function may not be fully inferred just from that function's name, and they don't map directly to functionality described in the RM.

You may want to explore the path Cedric outlined above, but IMO even then having an understanding how the hardware works makes life easier. Such understanding is achieved by writing a bunch of simple experimental programs working at the register level, or even just experimenting with the timers' registers in debugger, and observing the resulting behaviour.

JW

@cedric H and @waclawek.jan thank you for your suggestions!

I finally solved it with a help of this thread, and with your suggestions.

Thank you very much.

Oh, I forget to post the solution for the problem:

  /* USER CODE BEGIN 2 */

  htim20.Instance->CCER |= (TIM_CCx_ENABLE << TIM_CHANNEL_1);
  htim20.Instance->CCER |= (TIM_CCx_ENABLE << 0x02);			// complementary channel
  htim20.Instance->BDTR |= (TIM_BDTR_MOE);  // set outputs
  htim20.Instance->CR1 &= ~(TIM_CR1_CEN);   // disable timer
  htim20.Instance->CNT = 0;                 // reset timer

  htim8.Instance->CCER |= (TIM_CCx_ENABLE << TIM_CHANNEL_1);
  htim8.Instance->CCER |= (TIM_CCx_ENABLE << 0x02);			// complementary channel
  htim8.Instance->CCER |= (TIM_CCx_ENABLE << 0x04);			// Compare on Channel 2 output
  htim8.Instance->BDTR |= (TIM_BDTR_MOE);  // set outputs
  htim8.Instance->CR1 &= ~(TIM_CR1_CEN);   // disable timer
  htim8.Instance->CNT = 0;                 // reset timer

  htim1.Instance->CCER |= (TIM_CCx_ENABLE << TIM_CHANNEL_1);
  htim1.Instance->CCER |= (TIM_CCx_ENABLE << 0x02);			// complementary channel
  htim1.Instance->CCER |= (TIM_CCx_ENABLE << 0x04);			// Compare on Channel 2 output
  htim1.Instance->BDTR |= (TIM_BDTR_MOE);  // set outputs
  htim1.Instance->CR1 &= ~(TIM_CR1_CEN);   // disable timer
  htim1.Instance->CNT = 0;                 // reset timer
  htim1.Instance->CR1 |= (TIM_CR1_CEN);    // enable timer

  /* USER CODE END 2 */

Hi mate, i have a question. 

I used mcsdk, i want to drive my pmsm motor 200v. I used stm32g431 and coustem board. So after rectifier Ac 220v i will get 310v DC but my motor only suport 200v, so how to reduce duty cycle to around 55-60%MAX?? So i will get avarage not more than 200v. This use by pulse value?? 

Because some people tell me used __weak PWMC_setphasavoltage sir. 

Can u help me?? Still stuck on this configuration