Skip to main content
Associate III
March 14, 2025
Question

STM32F303 SPWM Generation

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.

7 replies

Senior III
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
Associate III
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 */
}

 

mƎALLEm
ST 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()

 

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