cancel
Showing results for 
Search instead for 
Did you mean: 

Configuration issue with Combined PWM mode on STM32F303RE

AB4
Visitor

Hello everyone,

 

I’m new to STM32 and I’m currently stuck with using timers.

I’m working with an STM32F303RE, and the goal of my project is to generate 6 signals at 100 kHz (high time between 100 and 500 ns), each phase-shifted by 60°. These signals will be used to phase-shift power commands, in order to limit current spikes by staggering their switching times.

 

For now, I’m focusing on generating just a single 100 kHz signal with a longer high time so I can easily observe it on an oscilloscope (precise timings are not important at this stage).

 

To generate the signal, I thought about using the combined PWM mode of Timer 1. If I understand correctly, this mode allows the output to go high when the timer reaches CCR1, then go low when it reaches CCR2, and remain low until the ARR is reached or the timer restarts. This method has the advantage of potentially generating 2 (or 3?) signals using TIM1 and CCR1–6, and makes it easy to handle phase shifts.

 

I have already tested standard PWM with TIM1 and output them on the board pins; I can clearly see the signal on PA8 on my oscilloscope with the correct frequency, so my external setup works fine.

 

However, as soon as I switch to the combined PWM mode, nothing comes out of the board.

 

Being a bit lost, any help would be greatly appreciated, especially regarding the points below:

 

  1. Do you have a simple working PWM combined example that could help me understand what I’m doing wrong?

  2. Do you have any idea what might be wrong in the attached code?

  3. At the moment, I generate my signal using CCR1 and CCR2. I could generate another one with CCR3 and CCR4, but CCR5 and CCR6 do not have external outputs, so they cannot generate signals. However, would it be possible to pair CCR1 and CCR5, CCR2 and CCR6, CCR3 and CCR4 to get 3 signals from TIM1?

    I was hoping (although I could manage with an additional timer if needed) that I could use the PWM combination of CCR1 and CCR5 on OC1REF and thus benefit from CCR5 even though it doesn’t have a directly accessible GPIO. And similarly for CCR2 and CCR6.

 

 

Thank you very much to anyone who can help.

 

AB4

 

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2025 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdbool.h>
#include "stm32f3xx_ll_bus.h"
#include "stm32f3xx_ll_rcc.h"
#include "stm32f3xx_ll_gpio.h"
#include "stm32f3xx_ll_tim.h"
#include "stm32f3xx_ll_usart.h"
#include "stm32f3xx_ll_utils.h"
#include "stm32f3xx_ll_system.h"
/* USER CODE END Includes */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define F_TIM          72000000    // Timer frequency
#define F_SW           100000      // Desired PWM frequency
#define CPT_PERIOD_VALUE ((F_TIM / F_SW) - 1)
/* USER CODE END PD */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART2_UART_Init(void);
/* USER CODE BEGIN PFP */
static void TIM1_Init_Custom(void);
/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  Main application entry point.
  * @retval int
  */
int main(void)
{
  /* MCU Configuration--------------------------------------------------------*/

  /* Reset all peripherals, initialize Flash interface and Systick */
  LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SYSCFG);
  LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_PWR);

  /* System interrupt init */
  NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_0);

  /* Configure system clock */
  SystemClock_Config();

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART2_UART_Init();

  /* USER CODE BEGIN 2 */
  // Enable GPIOA clock
  LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOA);

  // Configure PA8 in alternate function for TIM1_CH1
  LL_GPIO_SetPinMode(GPIOA, LL_GPIO_PIN_8, LL_GPIO_MODE_ALTERNATE);
  LL_GPIO_SetPinOutputType(GPIOA, LL_GPIO_PIN_8, LL_GPIO_OUTPUT_PUSHPULL);
  LL_GPIO_SetPinSpeed(GPIOA, LL_GPIO_PIN_8, LL_GPIO_SPEED_FREQ_HIGH);
  LL_GPIO_SetAFPin_8_15(GPIOA, LL_GPIO_PIN_8, LL_GPIO_AF_6); // TIM1_CH1

  // Initialize TIM1 in Set/Reset mode
  TIM1_Init_Custom();
  /* USER CODE END 2 */

  /* Infinite loop */
  while (1)
  {
    // Nothing to do, timer hardware handles the Set/Reset signal
  }
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  LL_FLASH_SetLatency(LL_FLASH_LATENCY_1);
  while(LL_FLASH_GetLatency() != LL_FLASH_LATENCY_1) {}

  LL_RCC_HSI_Enable();
  while(!LL_RCC_HSI_IsReady()) {}

  LL_RCC_HSI_SetCalibTrimming(16);
  LL_RCC_PLL_ConfigDomain_SYS(LL_RCC_PLLSOURCE_HSI, LL_RCC_PLL_MUL_9, LL_RCC_PREDIV_DIV_1);
  LL_RCC_PLL_Enable();
  while(!LL_RCC_PLL_IsReady()) {}

  LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1);
  LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_2);
  LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_1);
  LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL);
  while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL) {}

  LL_Init1msTick(36000000);
  LL_SetSystemCoreClock(36000000);
  LL_RCC_SetUSARTClockSource(LL_RCC_USART2_CLKSOURCE_PCLK1);
}

/**
  * @brief USART2 Initialization Function
  * @PAram None
  * @retval None
  */
static void MX_USART2_UART_Init(void)
{
  LL_USART_InitTypeDef USART_InitStruct = {0};
  LL_GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* Peripheral clock enable */
  LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_USART2);
  LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOA);

  /**USART2 GPIO Configuration
  PA2 -> USART2_TX
  PA3 -> USART2_RX
  */
  GPIO_InitStruct.Pin = USART_TX_Pin|USART_RX_Pin;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
  GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
  GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
  GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
  GPIO_InitStruct.Alternate = LL_GPIO_AF_7;
  LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  USART_InitStruct.BaudRate = 38400;
  USART_InitStruct.DataWidth = LL_USART_DATAWIDTH_8B;
  USART_InitStruct.StopBits = LL_USART_STOPBITS_1;
  USART_InitStruct.Parity = LL_USART_PARITY_NONE;
  USART_InitStruct.TransferDirection = LL_USART_DIRECTION_TX_RX;
  USART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE;
  USART_InitStruct.OverSampling = LL_USART_OVERSAMPLING_16;
  LL_USART_Init(USART2, &USART_InitStruct);
  LL_USART_DisableIT_CTS(USART2);
  LL_USART_ConfigAsyncMode(USART2);
  LL_USART_Enable(USART2);
}

/**
  * @brief GPIO Initialization Function
  * @PAram None
  * @retval None
  */
static void MX_GPIO_Init(void)
{
  // Only GPIO clocks are enabled, no pins configured
  LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOA);
  LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOB);
  LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOC);
  LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOF);
}

/* USER CODE BEGIN 4 */
void TIM1_Init_Custom(void)
{
    // Enable TIM1 clock
    LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_TIM1);

    // --- General parameters ---
    TIM1->PSC = 0;            // No prescaler
    TIM1->ARR = 719;          // 100 kHz @ 72 MHz
    TIM1->CCR1 = 200;         // Tick at which output will be SET
    TIM1->CCR2 = 500;         // Tick at which output will be RESET

    // --- Set/Reset mode ---
    TIM1->CCMR1 &= ~((7 << TIM_CCMR1_OC1M_Pos) | (7 << TIM_CCMR1_OC2M_Pos));
    TIM1->CCMR1 |= (4 << TIM_CCMR1_OC1M_Pos) | (4 << TIM_CCMR1_OC2M_Pos); // 100b = Set/Reset
    TIM1->CCMR1 |= TIM_CCMR1_OC1PE | TIM_CCMR1_OC2PE; // Preload enable

    // --- Enable outputs ---
    TIM1->CCER = TIM_CCER_CC1E | TIM_CCER_CC2E; // Enable CH1 and CH2
    TIM1->BDTR = TIM_BDTR_MOE;                  // Main output enable

    // --- Start timer ---
    TIM1->CR1 = TIM_CR1_ARPE;                   // ARR preload enable
    TIM1->EGR = TIM_EGR_UG;                     // Force update
    TIM1->CR1 |= TIM_CR1_CEN;                   // Start timer
}
/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  __disable_irq();
  while (1) {}
}

#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line)
{
  // User can implement reporting of file name and line number
}
#endif /* USE_FULL_ASSERT */

 

2 REPLIES 2
TDK
Super User

> // --- Set/Reset mode ---
> TIM1->CCMR1 &= ~((7 << TIM_CCMR1_OC1M_Pos) | (7 << TIM_CCMR1_OC2M_Pos));
> TIM1->CCMR1 |= (4 << TIM_CCMR1_OC1M_Pos) | (4 << TIM_CCMR1_OC2M_Pos); // 100b = Set/Reset

This sets OC1M to 0b0100 which forces the level inactive. Not combined mode. Need to set the OC1M[3] bit, which is not contiguous with the others.

TDK_0-1762913241846.png

> However, would it be possible to pair CCR1 and CCR5, CCR2 and CCR6, CCR3 and CCR4 to get 3 signals from TIM1?

No. TIM1 doesn't have CCR5 or 6 anyway.

 

If you feel a post has answered your question, please click "Accept as Solution".
waclawek.jan
Super User

> Need to set the OC1M[3] bit, which is not contiguous with the others.

Nice catch.

> No. TIM1 doesn't have CCR5 or 6 anyway.

In 'F3, it does.

You can't combine CH6, though; but you can combine CH5, see TIMx_CCR5.GC5Cx. 

The idea presumably is, that for 3 mutually shifted waveform you need to phase-shift (combine) only 2 out of 3 output channels, so you e.g. output CH1 plain, combine CH2 with CH5 and combine CH3 with CH4.

For the remaining 3 channels, you'd need to use a different timer (TIM8/TIM20) - how exactly would you synchronize them depends on the particular application. If the frequency never changes, then it's enough to start one timer used as master, set up an appropriately phase-shifted CHx used as TRGO; in second timer set slave-mode to be triggered from first timer and Slave-mode controller set to Trigger, and don't enable counter of second timer manually, wait until slave-mode controller enables it; after that you can reprogram CHx in first timer as needed.

JW