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 */
}

 

 

4 REPLIES 4
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.