3

I am using an STM32Cube IDE and HAL library to generate two PWM using two timers.

I want these two PWMs to stop i.e go LOW after one of the PWMs has generated N pulses.

I don't know what to use in what mode. Should I use a third timer with a mode or something else?

EDIT: After reading the answer, I'm getting closer but still some issues PWM does not stop.

Here is my settings(NUCLEO-F302R8):

I set PA0 as PWM output and PB1(myInterrupt_Input) as external trigger input.

enter image description here

enter image description here

enter image description here

enter image description here

enter image description here

enter image description here

Here my main.c:

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2022 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 */

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------/ / USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------/ / USER CODE BEGIN PD / / USER CODE END PD */

/* Private macro -------------------------------------------------------------/ / USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/ TIM_HandleTypeDef htim2;

UART_HandleTypeDef huart2;

/* USER CODE BEGIN PV */

/* USER CODE END PV */

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

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------/ / USER CODE BEGIN 0 / int global_pulse_flag = 0; / 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_USART2_UART_Init(); MX_TIM2_Init(); / USER CODE BEGIN 2 */

/* USER CODE END 2 */

/* Infinite loop / / USER CODE BEGIN WHILE / while (1) { / USER CODE END WHILE */

/* USER CODE BEGIN 3 */

} /* USER CODE END 3 */ }

/**

  • @brief System Clock Configuration
  • @retval None

*/ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

/** Initializes the RCC Oscillators according to the specified parameters

  • in the RCC_OscInitTypeDef structure.

/ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI; RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL16; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } /* Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) { Error_Handler(); } }

/**

  • @brief TIM2 Initialization Function
  • @param None
  • @retval None

*/ static void MX_TIM2_Init(void) {

/* USER CODE BEGIN TIM2_Init 0 */

/* USER CODE END TIM2_Init 0 */

TIM_ClockConfigTypeDef sClockSourceConfig = {0}; TIM_MasterConfigTypeDef sMasterConfig = {0}; TIM_OC_InitTypeDef sConfigOC = {0};

/* USER CODE BEGIN TIM2_Init 1 */

/* USER CODE END TIM2_Init 1 / htim2.Instance = TIM2; htim2.Init.Prescaler = 640-1; htim2.Init.CounterMode = TIM_COUNTERMODE_UP; htim2.Init.Period = 1000; htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; if (HAL_TIM_Base_Init(&htim2) != HAL_OK) { Error_Handler(); } sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL; if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK) { Error_Handler(); } if (HAL_TIM_PWM_Init(&htim2) != HAL_OK) { Error_Handler(); } sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK) { Error_Handler(); } sConfigOC.OCMode = TIM_OCMODE_PWM1; sConfigOC.Pulse = 500; sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; sConfigOC.OCFastMode = TIM_OCFAST_DISABLE; if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1) != HAL_OK) { Error_Handler(); } / USER CODE BEGIN TIM2_Init 2 */

/* USER CODE END TIM2_Init 2 */ HAL_TIM_MspPostInit(&htim2);

}

/**

  • @brief USART2 Initialization Function
  • @param None
  • @retval None

*/ static void MX_USART2_UART_Init(void) {

/* USER CODE BEGIN USART2_Init 0 */

/* USER CODE END USART2_Init 0 */

/* USER CODE BEGIN USART2_Init 1 */

/* USER CODE END USART2_Init 1 / huart2.Instance = USART2; huart2.Init.BaudRate = 38400; huart2.Init.WordLength = UART_WORDLENGTH_8B; huart2.Init.StopBits = UART_STOPBITS_1; huart2.Init.Parity = UART_PARITY_NONE; huart2.Init.Mode = UART_MODE_TX_RX; huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart2.Init.OverSampling = UART_OVERSAMPLING_16; huart2.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE; huart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT; if (HAL_UART_Init(&huart2) != HAL_OK) { Error_Handler(); } / USER CODE BEGIN USART2_Init 2 */

/* USER CODE END USART2_Init 2 */

}

/**

  • @brief GPIO Initialization Function
  • @param None
  • @retval None

*/ static void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0};

/* GPIO Ports Clock Enable */ __HAL_RCC_GPIOC_CLK_ENABLE(); __HAL_RCC_GPIOF_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE();

/Configure GPIO pin Output Level / HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_RESET);

/Configure GPIO pin : B1_Pin / GPIO_InitStruct.Pin = B1_Pin; GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(B1_GPIO_Port, &GPIO_InitStruct);

/Configure GPIO pin : myInterrupt_Input_Pin / GPIO_InitStruct.Pin = myInterrupt_Input_Pin; GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(myInterrupt_Input_GPIO_Port, &GPIO_InitStruct);

/Configure GPIO pin : LD2_Pin / GPIO_InitStruct.Pin = LD2_Pin; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(LD2_GPIO_Port, &GPIO_InitStruct);

/* EXTI interrupt init*/ HAL_NVIC_SetPriority(EXTI1_IRQn, 0, 0); HAL_NVIC_EnableIRQ(EXTI1_IRQn);

}

/* USER CODE BEGIN 4 / void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef htim) { if(htim -> Instance == TIM2) { if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1) { global_pulse_flag++; if(global_pulse_flag>55){ global_pulse_flag = 0; HAL_TIM_PWM_Stop_IT(&htim2,TIM_CHANNEL_1); }

    }
}

} /* USER CODE END 4 */

/**

  • @brief This function is executed in case of error occurrence.
  • @retval None

/ void Error_Handler(void) { / USER CODE BEGIN Error_Handler_Debug / / User can add his own implementation to report the HAL error return state / __disable_irq(); while (1) { } / USER CODE END Error_Handler_Debug */ }

#ifdef USE_FULL_ASSERT /**

  • @brief Reports the name of the source file and the source line number
  •     where the assert_param error has occurred.
    
  • @param file: pointer to the source file name
  • @param line: assert_param error line source number
  • @retval None

/ void assert_failed(uint8_t file, uint32_t line) { /* USER CODE BEGIN 6 / / User can add his own implementation to report the file name and line number, ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) / / USER CODE END 6 / } #endif / USE_FULL_ASSERT */

Here is stm32f3xx_it.c:

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    stm32f3xx_it.c
  * @brief   Interrupt Service Routines.
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2022 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" #include "stm32f3xx_it.h" / Private includes ----------------------------------------------------------/ / USER CODE BEGIN Includes / / USER CODE END Includes */

/* Private typedef -----------------------------------------------------------/ / USER CODE BEGIN TD */

/* USER CODE END TD */

/* Private define ------------------------------------------------------------/ / USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------/ / USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------/ / USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------/ / USER CODE BEGIN PFP */

/* USER CODE END PFP */

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

/* USER CODE END 0 */

/* External variables --------------------------------------------------------/ extern TIM_HandleTypeDef htim2; / USER CODE BEGIN EV */

/* USER CODE END EV */

/****************************************************************************/ /* Cortex-M4 Processor Interruption and Exception Handlers */ /**************************************************************************/ /

  • @brief This function handles Non maskable interrupt.

/ void NMI_Handler(void) { / USER CODE BEGIN NonMaskableInt_IRQn 0 */

/* USER CODE END NonMaskableInt_IRQn 0 / / USER CODE BEGIN NonMaskableInt_IRQn 1 / while (1) { } / USER CODE END NonMaskableInt_IRQn 1 */ }

/**

  • @brief This function handles Hard fault interrupt.

/ void HardFault_Handler(void) { / USER CODE BEGIN HardFault_IRQn 0 */

/* USER CODE END HardFault_IRQn 0 / while (1) { / USER CODE BEGIN W1_HardFault_IRQn 0 / / USER CODE END W1_HardFault_IRQn 0 */ } }

/**

  • @brief This function handles Memory management fault.

/ void MemManage_Handler(void) { / USER CODE BEGIN MemoryManagement_IRQn 0 */

/* USER CODE END MemoryManagement_IRQn 0 / while (1) { / USER CODE BEGIN W1_MemoryManagement_IRQn 0 / / USER CODE END W1_MemoryManagement_IRQn 0 */ } }

/**

  • @brief This function handles Pre-fetch fault, memory access fault.

/ void BusFault_Handler(void) { / USER CODE BEGIN BusFault_IRQn 0 */

/* USER CODE END BusFault_IRQn 0 / while (1) { / USER CODE BEGIN W1_BusFault_IRQn 0 / / USER CODE END W1_BusFault_IRQn 0 */ } }

/**

  • @brief This function handles Undefined instruction or illegal state.

/ void UsageFault_Handler(void) { / USER CODE BEGIN UsageFault_IRQn 0 */

/* USER CODE END UsageFault_IRQn 0 / while (1) { / USER CODE BEGIN W1_UsageFault_IRQn 0 / / USER CODE END W1_UsageFault_IRQn 0 */ } }

/**

  • @brief This function handles System service call via SWI instruction.

/ void SVC_Handler(void) { / USER CODE BEGIN SVCall_IRQn 0 */

/* USER CODE END SVCall_IRQn 0 / / USER CODE BEGIN SVCall_IRQn 1 */

/* USER CODE END SVCall_IRQn 1 */ }

/**

  • @brief This function handles Debug monitor.

/ void DebugMon_Handler(void) { / USER CODE BEGIN DebugMonitor_IRQn 0 */

/* USER CODE END DebugMonitor_IRQn 0 / / USER CODE BEGIN DebugMonitor_IRQn 1 */

/* USER CODE END DebugMonitor_IRQn 1 */ }

/**

  • @brief This function handles Pendable request for system service.

/ void PendSV_Handler(void) { / USER CODE BEGIN PendSV_IRQn 0 */

/* USER CODE END PendSV_IRQn 0 / / USER CODE BEGIN PendSV_IRQn 1 */

/* USER CODE END PendSV_IRQn 1 */ }

/**

  • @brief This function handles System tick timer.

/ void SysTick_Handler(void) { / USER CODE BEGIN SysTick_IRQn 0 */

/* USER CODE END SysTick_IRQn 0 / HAL_IncTick(); / USER CODE BEGIN SysTick_IRQn 1 */

/* USER CODE END SysTick_IRQn 1 */ }

/****************************************************************************/ /* STM32F3xx Peripheral Interrupt Handlers / / Add here the Interrupt Handlers for the used peripherals. / / For the available peripheral interrupt handler names, / / please refer to the startup file (startup_stm32f3xx.s). */ /****************************************************************************/

/**

  • @brief This function handles EXTI line1 interrupt.

/ void EXTI1_IRQHandler(void) { / USER CODE BEGIN EXTI1_IRQn 0 / if(HAL_TIM_PWM_Start_IT(&htim2, TIM_CHANNEL_1) != HAL_OK) { Error_Handler(); } / USER CODE END EXTI1_IRQn 0 / HAL_GPIO_EXTI_IRQHandler(myInterrupt_Input_Pin); / USER CODE BEGIN EXTI1_IRQn 1 */

/* USER CODE END EXTI1_IRQn 1 */ }

/**

  • @brief This function handles TIM2 global interrupt.

/ void TIM2_IRQHandler(void) { / USER CODE BEGIN TIM2_IRQn 0 */

/* USER CODE END TIM2_IRQn 0 / HAL_TIM_IRQHandler(&htim2); / USER CODE BEGIN TIM2_IRQn 1 */

/* USER CODE END TIM2_IRQn 1 */ }

/* USER CODE BEGIN 1 */

/* USER CODE END 1 */

cm64
  • 2,139
  • 1
  • 19
  • 46
  • Can you route one of them to a count down counter? Preload the countdown with the number of pulses you want, and when the counter reaches zero it triggers an interrupt and the ISR stops the PWM output? Something like that. – user57037 Jan 28 '22 at 08:12
  • That preloaded counter means a third timer to be used correct? – cm64 Jan 28 '22 at 08:42
  • But I was also wondering which mode I need to use for that. Like input capture or ect? Im using CubeMX to set pins and timers. – cm64 Jan 28 '22 at 08:43
  • 1
    This may be related: https://electronics.stackexchange.com/questions/541521/generate-n-pulse-with-a-fix-frequency-stm32f107 – Tagli Jan 28 '22 at 08:57
  • STM32 can do gated timers, so you can run a timer only when another timer oc is high. – Jeroen3 Jan 28 '22 at 10:27
  • 1
    Can you not just fire an interrupt every time the PWM completes a cycle and use a variable counting down in the interrupt service handler? I don't know these chips so I'm just thinking MCU generic. – Ian Bland Jan 28 '22 at 13:25
  • ´@IanBland Challanges start when you start implementing. See how Im struggling already with one of the method. – cm64 Jan 28 '22 at 14:12
  • at a glance, the code looks fine. If you're using a nucleo board, then you can step through the code and see where the code fails. Put a breakpoint at the pwm callback and see if it ever gets there. If it doesn't, you may be triggering the interrupt faster than it can be cleared. – ChrisD91 Jan 28 '22 at 14:46
  • 1
    @IanBland That is exactly what I've suggested, I've just explained how to do it in STM32 HAL – ChrisD91 Jan 28 '22 at 14:47
  • I would also suggest reducing the interrupt priority for tim2 and EXTI1, it may be clashing with systick that HAL uses if you're triggering constantly. Again ,you'd see all of this if you stepped through the code with a debugger. – ChrisD91 Jan 28 '22 at 14:53
  • @ChrisD91 You mean sub priority here https://i.stack.imgur.com/Bf3x5.png? They are all set to zero. To reduce does it mean I need to set the other to 1? – cm64 Jan 28 '22 at 14:59
  • Priority is confusingly split into "priority" and "sub-priority" to just give the programmer more granularity if you have multiple interrupts. If two interrupts trigger at the same time and has the same priority, it then checks the sub priority to avoid clashes. Zero is the highest priority. Change the sub priority to 1 for tim2 – ChrisD91 Jan 28 '22 at 16:42

2 Answers2

6

There's an easy solution to this using HAL as there are drivers that call a function every time a timer (in this case PWM) is called. You can just count the number of times the function is called and then stop your second PWM.

  1. Enable PWM as interrupt-based in the .ioc / cubemx page in stm32cubeide and generate code
  2. go to Drivers/xxx_HAL_Driver/Src/xx_hal_tim.c in project explorer window
  3. Look for the driver called HAL_TIM_PWM_Start_IT(TIM_HandleTypeDef *htim, uint32_t Channel) and copy and paste it into your main. Obviously you will need to provide the correct timer handle and channel
  4. In the timer driver file also look for a function called __weak void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim). Copy and paste this function in your main.c
  5. Remove the __weak and define the pulse finished callback function yourself. HAL will call this function every time a PWM pulse is finished. You need to have a flag that increments every time the function is called. Once it is called N times you can set your second to LOW.

Example code: before the main while loop ,start the PWM using timer 3 and channel 1:

 if(HAL_TIM_PWM_Start_IT(&htim3, TIM_CHANNEL_1) != HAL_OK) 
 {
     Error_Handler();
 }

And defining what to do with the pulse interrupt call. In this case if the timer that called the function is timer 3, make sure that it was also channel 1 (in case you have multiple timers and channels). If it is as expected increment a global falg.

/* USER CODE BEGIN 4 */
void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
{
    if(htim -> Instance == TIM3)
    {
        if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
        {
            global_pulse_flag++;
    }
}

} /* USER CODE END 4 */

CubeMX screenshot to enable PWM interrupt:

enter image description here

ChrisD91
  • 695
  • 1
  • 9
  • 18
  • Interesting and I want to try this. But how do you set PWM as interrupt base in Cube MX? – cm64 Jan 28 '22 at 10:42
  • 1
    Added a screenshot to the answer. In cubemx you need to configure your parameter settings (I assume you've already done this) and then in the NVIC settings tab you have the option to enable global interrupts for that timer – ChrisD91 Jan 28 '22 at 10:49
  • Ok I will try. Will this: "if(HAL_TIM_PWM_Start_IT(&htim3, TIM_CHANNEL_1) != HAL_OK)" be inside the while loop or before? – cm64 Jan 28 '22 at 11:09
  • 1
    If you declare it inside the while loop, it will keep restarting the PWM initialisation every loop. Do it before the while loop, I'd recommend it in the /* USER CODE BEGIN 2 */ part the cubemx generates for you – ChrisD91 Jan 28 '22 at 11:11
  • I generate the PWM now. But after global_pulse_flag++; I have now
            if(global_pulse_flag == 1000){
             HAL_TIM_Base_Stop(&htim2);
             HAL_TIM_Base_Stop_IT(&htim2);
            } to stop PWM after 1000 counts. But PWM still runs I use timer2 in my case
    
    – cm64 Jan 28 '22 at 11:18
  • Okay I think I should use " if(global_pulse_flag == 1000){ HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_1) ; }" – cm64 Jan 28 '22 at 11:24
  • I would also not say if(global_flag == 1000) but (global_flag > 999 && global_flag < 1000) with an else statement to throw an error if it goes above 1000. That way you can catch the error – ChrisD91 Jan 28 '22 at 11:26
  • But I need to trigger this event by a digital input. I mean the pulse train starts after a trigger input(3.3V to a digital input pin) and then stops after 1000 count. But next time with trigger or digital HIGH this can happen again. Should I reset something at the end of the callback? – cm64 Jan 28 '22 at 11:26
  • Works very well if I use reset button. Each time I push reset. All I need to do is is use digital input and obtain the same result. – cm64 Jan 28 '22 at 11:30
  • In that case, set your digital input to interrupt mode and enable the HAL_TIM_PWM_Start_IT in your digital interrupt handler. Stop the PWM in the pwm callback and repeat. – ChrisD91 Jan 28 '22 at 11:32
  • Thanks, I did that like this void EXTI1_IRQHandler(void) { HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_1);

    if(HAL_TIM_PWM_Start_IT(&htim2, TIM_CHANNEL_1) != HAL_OK) { Error_Handler(); } } But now the event starts with digital input rising edge but never stops

    – cm64 Jan 28 '22 at 12:06
  • And in main I have this: void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim) { if(htim -> Instance == TIM2) { if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1) { global_pulse_flag++;
            if(global_pulse_flag > 55){
             HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_1) ;
            }
    
        }
    }
    

    }

    – cm64 Jan 28 '22 at 12:06
  • Here is the entire project: https://file.io/iybKyE7hhkx5 – cm64 Jan 28 '22 at 12:12
  • If the event starts correctly but never stops after your specific number of pulses, you're not stopping the PWM correctly or you're not counting the number of pulses correctly. You never stated how you're getting the digital input, if it's from a button you may also be getting a number of triggers on the interrupt due to bouncing. There's too many things to consider without knowing everything about your project and what you want to do. – ChrisD91 Jan 28 '22 at 12:23
  • It is digital input with voltage applied. I apply 3V with function generator to a digital pin. I set PB1 as GPIO_Ext1 with rising edge. I uploaded the project – cm64 Jan 28 '22 at 12:27
  • All is here: https://file.io/oLYzqsDrS7S6 I would appreciate this last bit or here https://easyupload.io/wg4mkz – cm64 Jan 28 '22 at 12:28
  • You're using HAL_TIM_PWM_Start_IT to start the PWM but to stop the PWM you're using HAL_PWM_Stop. You need at least to use HAL_TIM_PWM_Stop_IT. Check the driver file again and use the correct stop function for your code. You are using PWM in interrupt mode! – ChrisD91 Jan 28 '22 at 12:34
  • I tried that as well but PWM starts but never stops. It seems HAL_TIM_PWM_PulseFinishedCallback is not called at all. – cm64 Jan 28 '22 at 13:13
  • I will edit and share the files and config. – cm64 Jan 28 '22 at 13:13
  • See my edit. I shared everything there. The PWM starts when PB1 is HIGH. But never stops. I might be using your code wrong way. I hope you might have a look at this. This is the last step to achieve my goal. Thanks anyways. – cm64 Jan 28 '22 at 13:23
  • Its a bit out of scope of the Q, but when adapting this answer to work on TIM1 one is faced with the dilemma that TIM1 has not only the global interrupt but four distinct ones (TIM1 break and TIM9 global int., TIM1 update and TIM10 global int., TIM1 trigger and commutation and T11 global int., TIM1 capture compare int)... What's the appropriate interrupt to use in this case? – antipattern Aug 02 '23 at 11:25
2

I see several possibilities :

  1. compute the time neededto complete the N pulses, and stop the PWM after that time : you have 2 sub options :
  • just get current time when you start the PWM, and check from your main loop if the time has elapsed : depending PWM frequency and what else you do in your main loop, you might stop it a few pulses too late if your main loop is busy doing something else when the moment comes to stop it

  • use another timer, and generate an interup when time is come to stop it

  1. There might be a way to chain timers, in order for a second timer to count the number of pulses (or resets) from your PWM.

  2. If your PWM is rather low frequency, you don't need extreme accuracy, and can spare some CPU : you could just generate some "soft PWM" using an interupt based on a timer : in this interupt, instead of just switching your pin, you also check if you reached N pulses : if so, you no longer switch

EDIT : adding pseudo code for option 1.2 :

void interupt_routine_timer_2()
{
    stop_pwm_on_timer_1();
}

void main() { int nbr_of_pulses_timer_2= ...; //compute the number of pulses needed on timer 2 to get the duration of N pulses (depends on clock frequency, prescaler, PWM frequency and N) set_timer_2_counting_down(initial_value=nbr_of_pulses_timer_2, interrupt_on_zero=interupt_routine_timer_2); start_pwm_on_timer_1();

}

So when the time of N pulses is elapsed, you call an interupt, which turns off PWM.

Basically, all you need to know is :

  • how to start a PWM on timer 1

  • how to stop a PWM on timer 1

  • how to generate an interuption after a fixed delay (using a timer).

For all those 3 things, you should be able to find examples on internet. Then you just have to merge them.

Sandro
  • 7,277
  • 11
  • 37
  • "use another timer, and generate an interrupt when time is come to stop it" I cannot find any examples on this method. How to set what to set ect. – cm64 Jan 28 '22 at 09:57
  • I added some pseudo code : is it clearer now? – Sandro Jan 28 '22 at 10:11
  • I would like to try this in STM32 Cube IDE. But don't you need to set timer_2 as slave of timer_1 ect? Can you try to implement this as well and see the result? – cm64 Jan 28 '22 at 10:17
  • And timer_2 should count pulses(rising edges) of timer_1; it seems in your code timer_2 is counting itself. Isnt it? – cm64 Jan 28 '22 at 10:21
  • No. That's the nice thing of method 1.2 : you just measure absolute time, instead of counting pulses, which makes the code more "simple" (ie avoids complex things like chaining timers).

    Let's say you want to generate 10 pulses at 100Hz : you know that each pulse takes 10ms, so 10 pulses take 100ms. So you can simply say "I stop the PWM after 100ms" instead of ssaying "I stop the PWM after 10 pulses".

    NB : I would recommend centered PWM, as to avoid starting pulse N+1 at exactly time N*period. Otherwise, you might need to reduce the duration by a small fraction of period

    – Sandro Jan 28 '22 at 10:35
  • But my pulse duration of PWM is 655us – cm64 Jan 28 '22 at 10:38
  • So? If you use centered PWM, just put the duration for timer 2 to N655µs : where is the problem? If you cannot use centered PWM, you might try a duration of N655µs - 50µs (provided your duty cyle is smaller than (655-50)/655 – Sandro Jan 28 '22 at 10:42
  • And dont you need to start timer_2? – cm64 Jan 28 '22 at 10:43
  • yes, you do start it, just before (or just after) starting timer 1. I was including it in "set_timer_2_counting_down" – Sandro Jan 28 '22 at 10:48