Skip to main content
Explorer II
March 29, 2025
Solved

Wrong (duplicated) PWM pulse

  • March 29, 2025
  • 6 replies
  • 1537 views

I'm a software developer and love electronics, specifically making metal detectors. I'm trying to develop a new Pulse Induction metal detector and I'm using this development board (STM32F407VET6).

Although I'm a software developer, I work with high level programming languages like Javascript and Python, so programming in plain C (register manipulation) is not easy for me, but I want to learn, so, I'm building the code with the help of AI (ChatGpt).

The code below is supposed to generate 2 pulses with PWM inside a 1ms pulse period. One pulse is 100us (or the metal detector TX pulse) generated to PE13 and another is 500us (for the metal detector power supply charge pump) generated to PA8.

I tested the pulse generation with an oscilloscope 100uS/Div and the first pulse is correct (100us) but it appears one time at the start and another at the end of the and it should appear only one time at the start of the period. I tried everything with the IA but I can't solve this problem and don't understand why it happens.

So, can someone look at my code and give an idea of the problem source?

#include "stm32f4xx.h"

// Function Prototypes
void SystemClock_Config(void);
void GPIO_Init(void);
void TIM1_PWM_Init(void);

int main(void) {
 // Configure system clock
 SystemClock_Config();

 // Initialize GPIO (PE13 as TIM1_CH3 Alternate Function)
 GPIO_Init();

 // Initialize TIM1 for PWM on PE13
 TIM1_PWM_Init();

 while (1) {
 // PWM runs automatically in hardware
 }
}

void SystemClock_Config(void) {
 // Enable HSE (High-Speed External Clock)
 RCC->CR |= RCC_CR_HSEON;
 while (!(RCC->CR & RCC_CR_HSERDY)); // Wait for HSE to be ready

 // Configure PLL: PLL_M = 8, PLL_N = 336, PLL_P = 2, PLL_Q = 7
 RCC->PLLCFGR = (8 << RCC_PLLCFGR_PLLM_Pos) | // PLLM = 8
 (336 << RCC_PLLCFGR_PLLN_Pos) | // PLLN = 336
 (0 << RCC_PLLCFGR_PLLP_Pos) | // PLLP = 2 (00)
 (7 << RCC_PLLCFGR_PLLQ_Pos) | // PLLQ = 7
 RCC_PLLCFGR_PLLSRC_HSE; // Use HSE as PLL source

 // Enable PLL
 RCC->CR |= RCC_CR_PLLON;
 while (!(RCC->CR & RCC_CR_PLLRDY)); // Wait for PLL to lock

 // Set Flash Latency and Enable Prefetch Buffer
 FLASH->ACR |= FLASH_ACR_LATENCY_5WS | FLASH_ACR_PRFTEN | FLASH_ACR_ICEN | FLASH_ACR_DCEN;

 // Set system clock to PLL
 RCC->CFGR |= RCC_CFGR_SW_PLL;
 while (!(RCC->CFGR & RCC_CFGR_SWS_PLL)); // Wait for PLL to be used as SYSCLK

 // Set AHB = 168 MHz, APB1 = 42 MHz, APB2 = 84 MHz
 RCC->CFGR |= RCC_CFGR_HPRE_DIV1; // AHB Prescaler = 1
 RCC->CFGR |= RCC_CFGR_PPRE1_DIV4; // APB1 Prescaler = 4 (42 MHz)
 RCC->CFGR |= RCC_CFGR_PPRE2_DIV2; // APB2 Prescaler = 2 (84 MHz)
}

void GPIO_Init(void) {
 // Habilitar clock dos GPIOs A e E
 RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN | RCC_AHB1ENR_GPIOEEN;

 // Configurar PE13 como função alternativa (TIM1_CH3)
 GPIOE->MODER &= ~(3 << (13 * 2));
 GPIOE->MODER |= (2 << (13 * 2));
 GPIOE->OSPEEDR |= (3 << (13 * 2)); // Alta velocidade
 GPIOE->AFR[1] &= ~(0xF << ((13 - 8) * 4));
 GPIOE->AFR[1] |= (1 << ((13 - 8) * 4)); // AF1 - TIM1_CH3

 // Configurar PA8 como função alternativa (TIM1_CH1)
 GPIOA->MODER &= ~(3 << (8 * 2));
 GPIOA->MODER |= (2 << (8 * 2));
 GPIOA->OSPEEDR |= (3 << (8 * 2)); // Alta velocidade
 GPIOA->AFR[1] &= ~(0xF << ((8 - 8) * 4));
 GPIOA->AFR[1] |= (1 << ((8 - 8) * 4)); // AF1 - TIM1_CH1
}

// testar abaixo (foi removido o charge pump pra teste tambem)
void TIM1_PWM_Init(void) {
 // Enable TIM1 clock
 RCC->APB2ENR |= RCC_APB2ENR_TIM1EN;
 
 // Set TIM1 Prescaler for 1 MHz Timer Clock (168 MHz / 168 = 1 MHz)
 TIM1->PSC = (84 * 2) - 1;
 
 // Set TIM1 Period for 1 kHz PRF (1 ms period)
 TIM1->ARR = 999;
 
 // Set Duty Cycle to 100 µs Pulse Width (10% of 1 ms)
 TIM1->CCR3 = 100;
 
 // Configure PWM Mode 1 (edge-aligned)
 TIM1->CCMR2 &= ~TIM_CCMR2_OC3M; 
 TIM1->CCMR2 |= (6 << TIM_CCMR2_OC3M_Pos); // PWM mode 1
 TIM1->CCMR2 |= TIM_CCMR2_OC3PE; // Enable preload
 
 // Set output polarity (active high)
 TIM1->CCER &= ~TIM_CCER_CC3P;
 TIM1->CCER |= TIM_CCER_CC3E; // Enable output
 
 // Force edge-aligned mode
 TIM1->CR1 &= ~TIM_CR1_CMS; // Up-counting mode
 TIM1->CR1 |= TIM_CR1_ARPE; // Auto-reload preload enable
 
 // Main output enable
 TIM1->BDTR |= TIM_BDTR_MOE;
 
 // Generate update event to load all settings
 TIM1->EGR |= TIM_EGR_UG;
 
 // Start timer
 TIM1->CR1 |= TIM_CR1_CEN;
}

Please don't mind the code comments.

Thanks!

    This topic has been closed for replies.
    Best answer by cristianosar

    The problem was just my oscilloscope (not calibrated), so I calibrated it through an internal trimmer and now it's working properly as you can see below (100us/Div)

    100us_pulse.png

    Anyway, the final code I have to generate de 100us pulse is the following (there are some Portuguese comments and additional code I added for debugging with GDB, please don't mind it).

     

    //#include <stdint.h>
    //#include <stdio.h>
    #include "stm32f4xx.h"
    
    // Function Prototypes
    void SystemClock_Config(void);
    void GPIO_Init(void);
    void TIM1_PWM_Init(void);
    
    // interrupcao do systick
    void SysTick_Init(void);
    
    //volatile uint32_t hse_value = HSE_VALUE;
    
    volatile uint32_t pulse_count = 0;
    volatile uint32_t pulses_per_second = 0;
    volatile uint32_t seconds_elapsed = 0;
    
    int main(void) {
     // Configure system clock
     SystemClock_Config();
    
     // Initialize GPIO (PE13 as TIM1_CH3 Alternate Function)
     GPIO_Init();
    
     // Initialize TIM1 for PWM on PE13
     TIM1_PWM_Init();
    
     SysTick_Init(); // Para debugar
    
     while (1) {
     // PWM runs automatically in hardware
     }
    }
    
    // Configura o SysTick para 1 segundo
    void SysTick_Init(void) {
     // Configura para interrupção a cada 1 segundo
     // Como SysTick é 24-bit, precisamos dividir o clock
     uint32_t ticks = SystemCoreClock / 1000; // 1ms (168000 ticks @ 168MHz)
     SysTick->LOAD = ticks - 1; // Configura para 1ms
     SysTick->VAL = 0; // Zera o contador
     SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
     SysTick_CTRL_TICKINT_Msk |
     SysTick_CTRL_ENABLE_Msk;
     
     // Configura prioridade da interrupção (opcional)
     NVIC_SetPriority(SysTick_IRQn, (1 << __NVIC_PRIO_BITS) - 1);
    }
    
    // Handler do SysTick
    volatile uint32_t milliseconds = 0;
    void SysTick_Handler(void) {
     milliseconds++;
     if(milliseconds >= 1000) { // A cada 1000ms = 1s
     milliseconds = 0;
     pulses_per_second = pulse_count;
     pulse_count = 0;
     seconds_elapsed++;
     }
    }
    
    void SystemClock_Config(void) {
     // 1. Habilita o HSE (High-Speed External Clock, normalmente 8 MHz)
     RCC->CR |= RCC_CR_HSEON; 
     while (!(RCC->CR & RCC_CR_HSERDY)); // Espera o HSE estabilizar
     
     // 2. Configura o PLL para multiplicar a frequência
     RCC->PLLCFGR = (8 << RCC_PLLCFGR_PLLM_Pos) | // HSE 8MHz /8 = 1MHz
     (336 << RCC_PLLCFGR_PLLN_Pos) | // 1MHz × 336 = 336MHz
     (0 << RCC_PLLCFGR_PLLP_Pos) | // 00 = divide by 2 → 168MHz
     (7 << RCC_PLLCFGR_PLLQ_Pos) | // USB clock = 336/7 = 48MHz
     RCC_PLLCFGR_PLLSRC_HSE; // Use HSE as source
    
     // 3. Ativa o PLL
     RCC->CR |= RCC_CR_PLLON;
     while (!(RCC->CR & RCC_CR_PLLRDY)); // Espera o PLL estabilizar
     
     // 4. Configura os barramentos para evitar overclock
     // define a freq. maxima de cada barramento
     RCC->CFGR |= RCC_CFGR_HPRE_DIV1; // AHB Prescaler = 1 (168 MHz)
     RCC->CFGR |= RCC_CFGR_PPRE1_DIV4; // APB1 Prescaler = 4 (42 MHz)
     RCC->CFGR |= RCC_CFGR_PPRE2_DIV2; // APB2 Prescaler = 2 (84 MHz)
     
     // 5. Configura o Flash para rodar a 168 MHz
     // Set Flash Latency and Enable Prefetch Buffer
     // essa linha so funciona aqui e nessa ordem (o chat gpt havia criado outra ordem no final)
     // e nao funcionava!
     FLASH->ACR |= FLASH_ACR_LATENCY_5WS | FLASH_ACR_PRFTEN | FLASH_ACR_ICEN | FLASH_ACR_DCEN;
    
     // 6. Troca o System Clock para o PLL
     RCC->CFGR |= RCC_CFGR_SW_PLL;
     while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL); // Espera a troca
    
     // Atualiza a variável global com a nova frequência do clock
     // SystemCoreClock = 168000000;
     // ou, se quiser usar a função CMSIS:
     SystemCoreClockUpdate();
    }
    
    void GPIO_Init(void) {
     // Habilitar clock dos GPIOs A e E (porque no Stm32 os clocks estao desaticados por padrao)
     // 7.3.10 - pag 244 do reference manual
     // RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; // Habilita o clock do GPIOA (pra pa8)
     RCC->AHB1ENR |= RCC_AHB1ENR_GPIOEEN; // Habilita o clock do GPIOE
    
     // Ver Memory Map (2.2 para ver os enderecos de memoria) no reference manual
    
     // Configurar PE13 como função alternativa (TIM1_CH3)
     // gpio_set_mode(GPIOE, 13 /* pin */, GPIO_MODE_AF); // Set PE13 to alternate function
     GPIOE->MODER &= ~(3 << (13 * 2));
     GPIOE->MODER |= (2 << (13 * 2));
     GPIOE->OSPEEDR |= (3 << (13 * 2)); // Alta velocidade para PE13
    
     // todo: repetir processo para o PA8 depois
     
     // AFR[1] → Configura funções alternativas para os pinos PE8 a PE15.
     // (13 - 8) * 4 → Calcula a posição dos bits no AFR[1] para o pino PE13.
     // AF1 (valor 1) → Faz PE13 trabalhar com TIM1_CH3.
     GPIOE->AFR[1] &= ~(0xF << ((13 - 8) * 4)); // Zera os bits do AFR[1] para PE13
     GPIOE->AFR[1] |= (1 << ((13 - 8) * 4)); // Define AF1 para PE13 (TIM1_CH3)
    }
    
    void TIM1_PWM_Init(void) {
     // Habilita o clock do timer TIM1 no barramento APB2 (Advanced Peripheral Bus 2)
     RCC->APB2ENR |= RCC_APB2ENR_TIM1EN;
    
     // Define a frequência do timer (reduz clock para 1 MHz, ou seja, 1 tick = 1 µs)
     TIM1->PSC = 168-1; // Prescaler (divide o clock do timer)
     // Define o período do PWM (1ms = 1000 µs)
     TIM1->ARR = 1000 - 1; // Define a contagem máxima (Período do PWM)
     // Define o duty cycle (100 µs)
     TIM1->CCR3 = 100; // para compensar eventuais atrasos de clock
    
     // todo: fazer o necessario para PA8
     // Configurar o canal 3 do TIM1 para operar no modo PWM1.
     TIM1->CCMR2 &= ~TIM_CCMR2_OC3M; // Zerar os bits que definem o modo de saída do Canal 3
     TIM1->CCMR2 |= (6 << TIM_CCMR2_OC3M_Pos); // Configurar o Canal 3 no modo PWM1 
     TIM1->CCMR2 |= TIM_CCMR2_OC3PE; // Habilitar o Preload para CCR3
     // Configura a polaridade (ativo alto)
     TIM1->CCER &= ~TIM_CCER_CC3P;
     TIM1->CCER |= TIM_CCER_CC3E; // Habilitar a saída PWM no Canal 3
     
     // as duas linhas abaixo sao para habilitar o interrupt q faz o pulse_count (remover)
     // :down_arrow: Habilita interrupção de atualização
     TIM1->DIER |= TIM_DIER_UIE;
     // :down_arrow: Habilita a interrupção do TIM1 no NVIC
     NVIC_EnableIRQ(TIM1_UP_TIM10_IRQn);
     
     TIM1->CR1 |= TIM_CR1_CEN; // Inicia o contador primeiro do TIM1, fazendo com que o PWM comece a ser gerado
     // Ativar a saída PWM nos pinos físicos (somente para timers avançados como TIM1 e TIM8)
     TIM1->BDTR |= TIM_BDTR_MOE; // Ativa a saída PWM depois
    }
    
    // Handler da interrupção de update do TIM1 (a cada 1ms)
    void TIM1_UP_TIM10_IRQHandler(void) {
     if (TIM1->SR & TIM_SR_UIF) {
     TIM1->SR &= ~TIM_SR_UIF; // Limpa flag de update
     pulse_count++; // Incrementa contador de pulsos
     }
    }

    Thanks all for the help!

     

    6 replies

    Super User
    March 29, 2025

    Try to post a screenshot from the oscilloscope and explain (perhaps make a drawing) of how it is different from the expectations.

    JW

    Explorer II
    March 29, 2025

    Below are the actual pulses and the drawing is what I expect:

    PE13PE13

    PA8PA8

    ExpectedExpected

    Graduate
    March 30, 2025

    The scope screen shows one full period and the beginning of another period. I can't see anything wrong.

    Graduate II
    March 30, 2025

    As others have stated, it looks like you are getting a regular PWM sequence instead of a single pulse. Can you confirm that your pulses keep going? Or are their only two?

     

    In general the way ChatGPT has setup this code is wrong. One should never assume an initial value of any register. Set the complete value yourself. As it is, there is no way to know exactly what is in register CR1.

     // Force edge-aligned mode
     TIM1->CR1 &= ~TIM_CR1_CMS; // Up-counting mode
     TIM1->CR1 |= TIM_CR1_ARPE; // Auto-reload preload enable

    In any event, you could try

     TIM1->CR1 |= (TIM_CR1_ARPE | TIM_CR1_OPM); // Auto-reload preload enable, One Pulse Mode Enabled

     

    Explorer II
    March 30, 2025

    Thanks, the pulses keep going, What I dont understand is why I see the 100uS pulse 2 times in the timeline ( of 1ms ) where in my understanding I should see it only one time in this time window. Or maybe I understand things wrong.

    Graduate II
    March 30, 2025

    I think your understanding is correct. There should only be 1 pulse within the 1ms window based on the configuration I see. Obviously, something is not configured correctly, but its not jumping out at me. technically the duty cycle also gets a '-1' so

    TIM1->CCR3 = 100-1;

    But I don't see that as a contributing factor. Where is your configuration for the 500us pulse?

    Are you sure your scope plots match the code? I don't see any way to get other than a 1/10 duty cycle with the ARR and CCR2 set as they are. However, you are showing 1/9. Your registers don't match your scope plot. Be sure you are setting up the correct CCR register for the signal you are plotting.

    Graduate II
    March 30, 2025
    Graduate
    March 31, 2025

    You are seeing MORE THAN ONE PERIOD of your PWM on the oscilloscope screen.The pulse at the right side is the next period pulse.

    cristianosarAuthorAnswer
    Explorer II
    April 5, 2025

    The problem was just my oscilloscope (not calibrated), so I calibrated it through an internal trimmer and now it's working properly as you can see below (100us/Div)

    100us_pulse.png

    Anyway, the final code I have to generate de 100us pulse is the following (there are some Portuguese comments and additional code I added for debugging with GDB, please don't mind it).

     

    //#include <stdint.h>
    //#include <stdio.h>
    #include "stm32f4xx.h"
    
    // Function Prototypes
    void SystemClock_Config(void);
    void GPIO_Init(void);
    void TIM1_PWM_Init(void);
    
    // interrupcao do systick
    void SysTick_Init(void);
    
    //volatile uint32_t hse_value = HSE_VALUE;
    
    volatile uint32_t pulse_count = 0;
    volatile uint32_t pulses_per_second = 0;
    volatile uint32_t seconds_elapsed = 0;
    
    int main(void) {
     // Configure system clock
     SystemClock_Config();
    
     // Initialize GPIO (PE13 as TIM1_CH3 Alternate Function)
     GPIO_Init();
    
     // Initialize TIM1 for PWM on PE13
     TIM1_PWM_Init();
    
     SysTick_Init(); // Para debugar
    
     while (1) {
     // PWM runs automatically in hardware
     }
    }
    
    // Configura o SysTick para 1 segundo
    void SysTick_Init(void) {
     // Configura para interrupção a cada 1 segundo
     // Como SysTick é 24-bit, precisamos dividir o clock
     uint32_t ticks = SystemCoreClock / 1000; // 1ms (168000 ticks @ 168MHz)
     SysTick->LOAD = ticks - 1; // Configura para 1ms
     SysTick->VAL = 0; // Zera o contador
     SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
     SysTick_CTRL_TICKINT_Msk |
     SysTick_CTRL_ENABLE_Msk;
     
     // Configura prioridade da interrupção (opcional)
     NVIC_SetPriority(SysTick_IRQn, (1 << __NVIC_PRIO_BITS) - 1);
    }
    
    // Handler do SysTick
    volatile uint32_t milliseconds = 0;
    void SysTick_Handler(void) {
     milliseconds++;
     if(milliseconds >= 1000) { // A cada 1000ms = 1s
     milliseconds = 0;
     pulses_per_second = pulse_count;
     pulse_count = 0;
     seconds_elapsed++;
     }
    }
    
    void SystemClock_Config(void) {
     // 1. Habilita o HSE (High-Speed External Clock, normalmente 8 MHz)
     RCC->CR |= RCC_CR_HSEON; 
     while (!(RCC->CR & RCC_CR_HSERDY)); // Espera o HSE estabilizar
     
     // 2. Configura o PLL para multiplicar a frequência
     RCC->PLLCFGR = (8 << RCC_PLLCFGR_PLLM_Pos) | // HSE 8MHz /8 = 1MHz
     (336 << RCC_PLLCFGR_PLLN_Pos) | // 1MHz × 336 = 336MHz
     (0 << RCC_PLLCFGR_PLLP_Pos) | // 00 = divide by 2 → 168MHz
     (7 << RCC_PLLCFGR_PLLQ_Pos) | // USB clock = 336/7 = 48MHz
     RCC_PLLCFGR_PLLSRC_HSE; // Use HSE as source
    
     // 3. Ativa o PLL
     RCC->CR |= RCC_CR_PLLON;
     while (!(RCC->CR & RCC_CR_PLLRDY)); // Espera o PLL estabilizar
     
     // 4. Configura os barramentos para evitar overclock
     // define a freq. maxima de cada barramento
     RCC->CFGR |= RCC_CFGR_HPRE_DIV1; // AHB Prescaler = 1 (168 MHz)
     RCC->CFGR |= RCC_CFGR_PPRE1_DIV4; // APB1 Prescaler = 4 (42 MHz)
     RCC->CFGR |= RCC_CFGR_PPRE2_DIV2; // APB2 Prescaler = 2 (84 MHz)
     
     // 5. Configura o Flash para rodar a 168 MHz
     // Set Flash Latency and Enable Prefetch Buffer
     // essa linha so funciona aqui e nessa ordem (o chat gpt havia criado outra ordem no final)
     // e nao funcionava!
     FLASH->ACR |= FLASH_ACR_LATENCY_5WS | FLASH_ACR_PRFTEN | FLASH_ACR_ICEN | FLASH_ACR_DCEN;
    
     // 6. Troca o System Clock para o PLL
     RCC->CFGR |= RCC_CFGR_SW_PLL;
     while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL); // Espera a troca
    
     // Atualiza a variável global com a nova frequência do clock
     // SystemCoreClock = 168000000;
     // ou, se quiser usar a função CMSIS:
     SystemCoreClockUpdate();
    }
    
    void GPIO_Init(void) {
     // Habilitar clock dos GPIOs A e E (porque no Stm32 os clocks estao desaticados por padrao)
     // 7.3.10 - pag 244 do reference manual
     // RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; // Habilita o clock do GPIOA (pra pa8)
     RCC->AHB1ENR |= RCC_AHB1ENR_GPIOEEN; // Habilita o clock do GPIOE
    
     // Ver Memory Map (2.2 para ver os enderecos de memoria) no reference manual
    
     // Configurar PE13 como função alternativa (TIM1_CH3)
     // gpio_set_mode(GPIOE, 13 /* pin */, GPIO_MODE_AF); // Set PE13 to alternate function
     GPIOE->MODER &= ~(3 << (13 * 2));
     GPIOE->MODER |= (2 << (13 * 2));
     GPIOE->OSPEEDR |= (3 << (13 * 2)); // Alta velocidade para PE13
    
     // todo: repetir processo para o PA8 depois
     
     // AFR[1] → Configura funções alternativas para os pinos PE8 a PE15.
     // (13 - 8) * 4 → Calcula a posição dos bits no AFR[1] para o pino PE13.
     // AF1 (valor 1) → Faz PE13 trabalhar com TIM1_CH3.
     GPIOE->AFR[1] &= ~(0xF << ((13 - 8) * 4)); // Zera os bits do AFR[1] para PE13
     GPIOE->AFR[1] |= (1 << ((13 - 8) * 4)); // Define AF1 para PE13 (TIM1_CH3)
    }
    
    void TIM1_PWM_Init(void) {
     // Habilita o clock do timer TIM1 no barramento APB2 (Advanced Peripheral Bus 2)
     RCC->APB2ENR |= RCC_APB2ENR_TIM1EN;
    
     // Define a frequência do timer (reduz clock para 1 MHz, ou seja, 1 tick = 1 µs)
     TIM1->PSC = 168-1; // Prescaler (divide o clock do timer)
     // Define o período do PWM (1ms = 1000 µs)
     TIM1->ARR = 1000 - 1; // Define a contagem máxima (Período do PWM)
     // Define o duty cycle (100 µs)
     TIM1->CCR3 = 100; // para compensar eventuais atrasos de clock
    
     // todo: fazer o necessario para PA8
     // Configurar o canal 3 do TIM1 para operar no modo PWM1.
     TIM1->CCMR2 &= ~TIM_CCMR2_OC3M; // Zerar os bits que definem o modo de saída do Canal 3
     TIM1->CCMR2 |= (6 << TIM_CCMR2_OC3M_Pos); // Configurar o Canal 3 no modo PWM1 
     TIM1->CCMR2 |= TIM_CCMR2_OC3PE; // Habilitar o Preload para CCR3
     // Configura a polaridade (ativo alto)
     TIM1->CCER &= ~TIM_CCER_CC3P;
     TIM1->CCER |= TIM_CCER_CC3E; // Habilitar a saída PWM no Canal 3
     
     // as duas linhas abaixo sao para habilitar o interrupt q faz o pulse_count (remover)
     // :down_arrow: Habilita interrupção de atualização
     TIM1->DIER |= TIM_DIER_UIE;
     // :down_arrow: Habilita a interrupção do TIM1 no NVIC
     NVIC_EnableIRQ(TIM1_UP_TIM10_IRQn);
     
     TIM1->CR1 |= TIM_CR1_CEN; // Inicia o contador primeiro do TIM1, fazendo com que o PWM comece a ser gerado
     // Ativar a saída PWM nos pinos físicos (somente para timers avançados como TIM1 e TIM8)
     TIM1->BDTR |= TIM_BDTR_MOE; // Ativa a saída PWM depois
    }
    
    // Handler da interrupção de update do TIM1 (a cada 1ms)
    void TIM1_UP_TIM10_IRQHandler(void) {
     if (TIM1->SR & TIM_SR_UIF) {
     TIM1->SR &= ~TIM_SR_UIF; // Limpa flag de update
     pulse_count++; // Incrementa contador de pulsos
     }
    }

    Thanks all for the help!