Skip to main content
Graduate II
March 14, 2025
Question

STM32F303 SPWM Generation

  • March 14, 2025
  • 1 reply
  • 2574 views

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

 

 

    This topic has been closed for replies.

    1 reply

    Visitor II
    March 14, 2025

    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
     }
    }
    SergenAuthor
    Graduate II
    March 14, 2025

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

     

    Technical Moderator
    March 15, 2025

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