cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F303 SPWM Generation

Sergen
Associate III

Hello,
I designed a BLDC motor driver for myself and I am currently in the software phase.

I am using the 1st, 2nd and 3rd channels of the TIM8 timer for 3 phases, but I do not have any information about how to create SPWM in stm32 or how to trigger these 3 phases and their configuration. I wrote a sample code from the internet, but the motor draws very high current and rotates with vibration.

 

 

#define PI 3.14159265359

uint16_t SINE_A = 0;
uint16_t SINE_B = 120;
uint16_t SINE_C = 240;

void generateSPWM()
{
    // Add 1 so the rotation will continue 1 by 1
	SINE_A = (SINE_A + 1) % 360;   // Sine Wave A
	SINE_B = (SINE_A + 120) % 360; // Sine Wave B, 120 derece faz farkı
	SINE_C = (SINE_A + 240) % 360; // Sine Wave C, 240 derece faz farkı

    // Calculate the PWM values for creating a sine wave (SPWM)
    int SINE_A_PWM = (int)(sin((double)SINE_A * PI / 180) * 499.5 + 499.5);  // Convert to PWM range
    int SINE_B_PWM = (int)(sin((double)SINE_B * PI / 180) * 499.5 + 499.5);  // Convert to PWM range
    int SINE_C_PWM = (int)(sin((double)SINE_C * PI / 180) * 499.5 + 499.5);  // Convert to PWM range

    // Set the PWM duty cycles for each phase
    // PWM değerini daha düşük bir oranda kullan
    __HAL_TIM_SET_COMPARE(&htim8, TIM_CHANNEL_1, SINE_A_PWM * 0.2);  // %40 Duty Cycle
    __HAL_TIM_SET_COMPARE(&htim8, TIM_CHANNEL_2, SINE_B_PWM * 0.2);  // %40 Duty Cycle
    __HAL_TIM_SET_COMPARE(&htim8, TIM_CHANNEL_3, SINE_C_PWM * 0.2);  // %40 Duty Cycle

}

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == TIM8)
    {
        generateSPWM();  // Generate SPWM on Timer overflow
    }
}

int main(void)
{

  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_TIM1_Init();
  MX_SPI3_Init();
  MX_TIM2_Init();
  MX_ADC1_Init();
  MX_I2C2_Init();
  MX_TIM8_Init();
  MX_USART1_UART_Init();
  MX_USART3_UART_Init();
  /* USER CODE BEGIN 2 */
  HAL_GPIO_WritePin(GPIOB, RUNL_Pin, GPIO_PIN_SET);
  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, GPIO_PIN_SET); // Enable Half1
  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_14, GPIO_PIN_SET); // Enable Half2
  HAL_TIM_PWM_Start(&htim8,TIM_CHANNEL_1);
  HAL_TIM_PWM_Start(&htim8,TIM_CHANNEL_2);
  HAL_TIM_PWM_Start(&htim8,TIM_CHANNEL_3);
  // Start PWM signals
  HAL_TIM_Base_Start_IT(&htim8);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while(1){

  }
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  /* USER CODE END 3 */
}

 

 

7 REPLIES 7
ahsrabrifat
Associate III

You can improve the SPWM Implementation by editing your code.

#include "math.h"
#include "stdint.h"

#define PI 3.14159265359
#define PWM_MAX 999 // Assume TIM8 ARR = 999 for 100% duty cycle
#define TABLE_SIZE 360

uint16_t sineTable[TABLE_SIZE]; // Precomputed sine lookup table
uint16_t stepIndex = 0; // Angle step for sine wave generation

void generateSineTable() {
    for (uint16_t i = 0; i < TABLE_SIZE; i++) {
        sineTable[i] = (uint16_t)((sin((double)i * PI / 180) * (PWM_MAX / 2)) + (PWM_MAX / 2));
    }
}

void generateSPWM() {
    // Update phase shift
    stepIndex = (stepIndex + 1) % TABLE_SIZE;

    // Get sine wave values for three phases with 120° phase shift
    uint16_t SINE_A_PWM = sineTable[stepIndex];
    uint16_t SINE_B_PWM = sineTable[(stepIndex + 120) % TABLE_SIZE];
    uint16_t SINE_C_PWM = sineTable[(stepIndex + 240) % TABLE_SIZE];

    // Set the PWM duty cycles for each phase
    __HAL_TIM_SET_COMPARE(&htim8, TIM_CHANNEL_1, SINE_A_PWM);
    __HAL_TIM_SET_COMPARE(&htim8, TIM_CHANNEL_2, SINE_B_PWM);
    __HAL_TIM_SET_COMPARE(&htim8, TIM_CHANNEL_3, SINE_C_PWM);
}

// Timer Interrupt Handler
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
    if (htim->Instance == TIM8) {
        generateSPWM(); // Generate SPWM on Timer overflow
    }
}

int main(void) {
    HAL_Init();
    SystemClock_Config();

    MX_GPIO_Init();
    MX_TIM8_Init();

    // Generate sine lookup table
    generateSineTable();

    // Enable all motor drive pins
    HAL_GPIO_WritePin(GPIOB, RUNL_Pin, GPIO_PIN_SET);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, GPIO_PIN_SET); // Enable Half1
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_14, GPIO_PIN_SET); // Enable Half2

    // Start PWM signals
    HAL_TIM_PWM_Start(&htim8, TIM_CHANNEL_1);
    HAL_TIM_PWM_Start(&htim8, TIM_CHANNEL_2);
    HAL_TIM_PWM_Start(&htim8, TIM_CHANNEL_3);
    HAL_TIM_Base_Start_IT(&htim8); // Start Timer Interrupt

    while(1) {
        // Infinite loop
    }
}

Hello,

First of all, thank you for your answer. I tried this code, it works better than the previous one, but I have some problems.
I have an esc driver, when I connect it to it, it rotates at a higher speed and more stable at the same currents, while on my own PCB board it continues to rotate a little more vibrating and slowly, and it also heats up more, I probably need to optimize it a little more, how do you think I can optimize it?

Last Code :

#define PI 3.14159265359
#define PWM_MAX 999 // Assume TIM8 ARR = 999 for 100% duty cycle
#define TABLE_SIZE 360

uint16_t sineTable[TABLE_SIZE]; // Precomputed sine lookup table
uint16_t stepIndex = 0; // Angle step for sine wave generation

void generateSineTable() {
    for (uint16_t i = 0; i < TABLE_SIZE; i++) {
        sineTable[i] = (uint16_t)((sin((double)i * PI / 180) * (PWM_MAX / 2)) + (PWM_MAX / 2));
    }
}

void generateSPWM() {
    // Update phase shift
    stepIndex = (stepIndex + 1) % TABLE_SIZE;

    // Get sine wave values for three phases with 120° phase shift
    uint16_t SINE_A_PWM = sineTable[stepIndex];
    uint16_t SINE_B_PWM = sineTable[(stepIndex + 120) % TABLE_SIZE];
    uint16_t SINE_C_PWM = sineTable[(stepIndex + 240) % TABLE_SIZE];

    // Set the PWM duty cycles for each phase
    __HAL_TIM_SET_COMPARE(&htim8, TIM_CHANNEL_1, SINE_A_PWM * 0.1);
    __HAL_TIM_SET_COMPARE(&htim8, TIM_CHANNEL_2, SINE_B_PWM * 0.1);
    __HAL_TIM_SET_COMPARE(&htim8, TIM_CHANNEL_3, SINE_C_PWM * 0.1);
}

// Timer Interrupt Handler
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
    if (htim->Instance == TIM8) {
        generateSPWM(); // Generate SPWM on Timer overflow
    }
}
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{

  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_TIM1_Init();
  MX_SPI3_Init();
  MX_TIM2_Init();
  MX_ADC1_Init();
  MX_I2C2_Init();
  MX_TIM8_Init();
  MX_USART1_UART_Init();
  MX_USART3_UART_Init();
  /* USER CODE BEGIN 2 */
  generateSineTable();
  HAL_GPIO_WritePin(GPIOB, RUNL_Pin, GPIO_PIN_SET);
  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, GPIO_PIN_SET); // Enable Half1
  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_14, GPIO_PIN_SET); // Enable Half2
  HAL_TIM_PWM_Start(&htim8,TIM_CHANNEL_1);
  HAL_TIM_PWM_Start(&htim8,TIM_CHANNEL_2);
  HAL_TIM_PWM_Start(&htim8,TIM_CHANNEL_3);
  // Start PWM signals
  HAL_TIM_Base_Start_IT(&htim8);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while(1){

  }
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  /* USER CODE END 3 */
}

 

Hello,

I have a doubt about that multiplication to set the timer output compare:

SINE_x_PWM * 0.1

The input needs to be an integer while you are multiplying that sine value by 0.1. It could be a cast problem. So check if you have the intended input value to:

__HAL_TIM_SET_COMPARE()

 

To give better visibility on the answered topics, please click on "Accept as Solution" on the reply which solved your issue or answered your question.

 

Hello,
I wrote the 0.1 multiplication there on purpose because the motor draws extremely high current while rotating, and since I don't have an oscilloscope to understand that my microcontroller is actually creating spwm, I am currently testing my motor by trial and error.


@Sergen wrote:


I wrote the 0.1 multiplication there on purpose because the motor draws extremely high current while rotating, 


I'm not asking why are you multiplying these values by 0.1. I recommended to check the intended values to set to the timers.

Declare a temporary values before setting them to the timer. Check their values in the debugger (C1, C2 and C3):

__IO uint16_t C1,C2,C3;

C1 = SINE_A_PWM * 0.1;
C2 = SINE_A_PWM * 0.1;
C3 = SINE_A_PWM * 0.1;

__HAL_TIM_SET_COMPARE(&htim8, TIM_CHANNEL_1, C1);
__HAL_TIM_SET_COMPARE(&htim8, TIM_CHANNEL_2, C2);
__HAL_TIM_SET_COMPARE(&htim8, TIM_CHANNEL_3, C3);

 

To give better visibility on the answered topics, please click on "Accept as Solution" on the reply which solved your issue or answered your question.

Hello,

I'm sorry I didn't fully understand, first of all I added the code as follows.

    stepIndex = (stepIndex + 1) % TABLE_SIZE;

    // Get sine wave values for three phases with 120° phase shift
    uint16_t SINE_A_PWM = sineTable[stepIndex];
    uint16_t SINE_B_PWM = sineTable[(stepIndex + 120) % TABLE_SIZE];
    uint16_t SINE_C_PWM = sineTable[(stepIndex + 240) % TABLE_SIZE];

    // Set the PWM duty cycles for each phase
//    __HAL_TIM_SET_COMPARE(&htim8, TIM_CHANNEL_1, SINE_A_PWM * 0.1);
//    __HAL_TIM_SET_COMPARE(&htim8, TIM_CHANNEL_2, SINE_B_PWM * 0.1);
//    __HAL_TIM_SET_COMPARE(&htim8, TIM_CHANNEL_3, SINE_C_PWM * 0.1);
    __IO uint16_t C1,C2,C3;

    C1 = SINE_A_PWM * 0.1;
    C2 = SINE_A_PWM * 0.1;
    C3 = SINE_A_PWM * 0.1;

    __HAL_TIM_SET_COMPARE(&htim8, TIM_CHANNEL_1, C1);
    __HAL_TIM_SET_COMPARE(&htim8, TIM_CHANNEL_2, C2);
    __HAL_TIM_SET_COMPARE(&htim8, TIM_CHANNEL_3, C3);

the values ​​in the debugger are in the attached picture.

 

 

Sorry I meant:

C1 = SINE_A_PWM * 0.1;
C2 = SINE_B_PWM * 0.1;
C3 = SINE_C_PWM * 0.1;

And I have no idea what are the expected Cx values. It's up to you to know what you need to set at the timer compare output. 

 

 

To give better visibility on the answered topics, please click on "Accept as Solution" on the reply which solved your issue or answered your question.