2025-11-11 3:47 PM
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:
Do you have a simple working PWM combined example that could help me understand what I’m doing wrong?
Do you have any idea what might be wrong in the attached code?
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 */
2025-11-11 6:10 PM
> // --- 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.
> 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.
2025-11-12 12:11 AM - edited 2025-11-12 12:41 AM
> 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