microcontroller - Unable to initialize STM32 timer in one pulse mode with ST HAL - Electrical Engineering Stack Exchange
stm32f4discovery - How trigge stm32 one pulse mode timer internally - Stack Overflow
STM32 Start ADC with timer in one-pulse mode - Electrical Engineering Stack Exchange
stm32cubemx - How to generate single pulse with STM32 timer - Electrical Engineering Stack Exchange
Does Retriggerable One Pulse Mode work on all STM32 timers?
Can One Pulse Mode work without an external trigger?
Can One Pulse Mode be combined with DMA or interrupts?
Videos
I have finally figured out, what was missing in my code.
Apart from HAL_TIM_OnePulse_Start_IT(), also HAL_TIM_Base_Start() has to be called in order to set up the timer operation in one pulse mode.
Now my main function looks like below and the interrupt is fired when after the set time.
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM10_Init();
HAL_TIM_Base_Start(&htim10);
HAL_TIM_OnePulse_Start_IT(&htim10, TIM_CHANNEL_1);
while (1)
{
}
}
You haven't shown us all the code. For example, where is the Timer and GPIO peripherals enabled? And where is the Timer interrupt enabled?
Typically with STM32Cube HAL applications, the functions like HAL_TIM_Base_Init() call another function like HAL_TIM_Base_MspInit(), which you may have to provide an implementation for. The xxx_MspInit() functions are typically implemented in a file called stm32xxxx_hal_msp.c. ST provides a template for this file with the HAL but I believe the expectation is that you will copy the template to your project file and customize it. (I'm not familiar with the STM32CubeMX tool, which might do some of this automatically.)
Typically within HAL_TIM_Base_MspInit() in stm32xxxx_hal_msp.c is where you will enable the timer and the timer interrupt. That code will look something like this:
// Enable the peripheral clock.
__HAL_RCC_TIM10_CLK_ENABLE();
// NVIC configuration for the TIM10 interrupt.
HAL_NVIC_SetPriority(TIM10_IRQn, 3, 0);
HAL_NVIC_EnableIRQ(TIM10_IRQn);
Then you also need to provide an implementation for the timer interrupt handler. Typically this is done in a file called stm32xxxx_it.c. This is another file provided as a template with the HAL but you're expected to customize it in your project directory. (And again, STM32CubeMX may do some of this automatically.)
/**
* @brief This function handles TIM10_IRQHandler interrupt request.
* @param None
* @retval None
*/
void TIM10_IRQHandler(void)
{
HAL_TIM_IRQHandler(&htim10);
}
It's HAL_TIM_IRQHandler() which then calls your HAL_TIM_PeriodElapsedCallback() function.
So make sure you've enabled the timer and the timer interrupt in HAL_TIM_Base_MspInit(). Then make sure you've provided an implementation for TIM10_IRQHandler() which calls HAL_TIM_IRQHandler(). Then use the debugger to step through the code. Set breakpoints in HAL_TIM_Base_MspInit() and TIM10_IRQHandler() and HAL_TIM_PeriodElapsedCallback() to figure out what is working and what is not working.
The HAL driver and STM32CubeIDE/STM32CubeMX are somewhat restricted in terms of allowing the full use of the One Pulse feature for the TIMx timers.
I noticed a few others (https://www.reddit.com/r/stm32/comments/dn3r9i/need_help_getting_onepulse_to_work_with_the_hal/) and (https://community.st.com/s/question/0D50X00009XkgopSAB/stm32cube-tim-onepulsepwmoc-configuration-issue) who had similar issues to me regarding the One Pulse TIMx features using the HAL and thought I'd write this up quickly to show what I did to get things working with the HAL.
Hopefully it helps someone, took me a few days to figure this all out.
I was trying to have TIM4 trigger TIM2 and couldn't get it working with the HAL. Turns out I had to modify some of the HAL functions since they are basically setup to ONLY work with channels 1 and 2 of a given TIMx.
For instance, HAL_TIM_OnePulse_ConfigChannel() is setup to only work with TI1FP1 or TI2FP2 usage, meaning only when you have the TIM's CH1 trigger CH2 or CH2 trigger CH1.
This can be seen in the code below which is from HAL_TIM_OnePulse_ConfigChannel():
/* Select the Trigger source */ htim->Instance->SMCR &= ~TIM_SMCR_TS; htim->Instance->SMCR |= TIM_TS_TI1FP1; /* Select the Slave Mode */ htim->Instance->SMCR &= ~TIM_SMCR_SMS; htim->Instance->SMCR |= TIM_SLAVEMODE_TRIGGER;
So, if you call the HAL_TIM_SlaveConfigSynchro() function BEFORE you call HAL_TIM_OnePulse_ConfigChannel() then basically the trigger input / slave mode settings would be reset.
Another thing is the HAL_TIM_OnePulse_Start() function which ONLY starts TIMx channels 1 and 2, it will not start channels 3 or 4, and in fact always starts both of them under the assumption that you are using the HAL_TIM_OnePulse_ConfigChannel() function which itself assumes using the TI1FP1 or TI2FP2 triggers.
Without going on and on, this is how I modified the HAL to allow the use of the TIMx channels 1-4 in the One Pulse mode and an example.
I added these function prototypes in stm32h7xx_hal_tim.h:
HAL_StatusTypeDef HAL_TIM_OnePulse_StartChannel(TIM_HandleTypeDef *htim, uint32_t OutputChannel); HAL_StatusTypeDef HAL_TIM_OnePulse_ConfigChannelOutput(TIM_HandleTypeDef *htim, TIM_OnePulse_InitTypeDef *sConfig, uint32_t OutputChannel);
HAL_TIM_OnePulse_StartChannel() starts each channel (channels 1 to 4) individually instead of HAL_TIM_OnePulse_Start() which only starts channels 1 and 2.
HAL_TIM_OnePulse_ConfigChannelOutput() configures each channel (channels 1 to 4) individually and does not 'reset' the slave mode and trigger settings like HAL_TIM_OnePulse_ConfigChannel() does.
In stm32h7xx_hal.tim.c I added the functions:
HAL_StatusTypeDef HAL_TIM_OnePulse_StartChannel(TIM_HandleTypeDef *htim, uint32_t OutputChannel)
{
/* Prevent unused argument(s) compilation warning */
UNUSED(OutputChannel);
TIM_CCxChannelCmd(htim->Instance, OutputChannel, TIM_CCx_ENABLE);
if (IS_TIM_BREAK_INSTANCE(htim->Instance) != RESET)
{
/* Enable the main output */
__HAL_TIM_MOE_ENABLE(htim);
}
/* Return function status */
return HAL_OK;
}
HAL_StatusTypeDef HAL_TIM_OnePulse_ConfigChannelOutput(TIM_HandleTypeDef *htim, TIM_OnePulse_InitTypeDef *sConfig, uint32_t OutputChannel)
{
TIM_OC_InitTypeDef temp1;
/* Check the parameters */
assert_param(IS_TIM_OPM_CHANNELS(OutputChannel));
/* Process Locked */
__HAL_LOCK(htim);
htim->State = HAL_TIM_STATE_BUSY;
/* Extract the Output compare configuration from sConfig structure */
temp1.OCMode = sConfig->OCMode;
temp1.Pulse = sConfig->Pulse;
temp1.OCPolarity = sConfig->OCPolarity;
temp1.OCNPolarity = sConfig->OCNPolarity;
temp1.OCIdleState = sConfig->OCIdleState;
temp1.OCNIdleState = sConfig->OCNIdleState;
switch (OutputChannel)
{
case TIM_CHANNEL_1:
{
assert_param(IS_TIM_CC1_INSTANCE(htim->Instance));
TIM_OC1_SetConfig(htim->Instance, &temp1);
break;
}
case TIM_CHANNEL_2:
{
assert_param(IS_TIM_CC2_INSTANCE(htim->Instance));
TIM_OC2_SetConfig(htim->Instance, &temp1);
break;
}
case TIM_CHANNEL_3:
{
assert_param(IS_TIM_CC3_INSTANCE(htim->Instance));
TIM_OC3_SetConfig(htim->Instance, &temp1);
break;
}
case TIM_CHANNEL_4:
{
assert_param(IS_TIM_CC4_INSTANCE(htim->Instance));
TIM_OC4_SetConfig(htim->Instance, &temp1);
break;
}
default:
break;
}
htim->State = HAL_TIM_STATE_READY;
__HAL_UNLOCK(htim);
return HAL_OK;
}As an example now, to configure TIM4 in One Pulse mode, triggered by TI2FP2, operating in master/slave mode with it's TRGO set to ENABLE, and having TIM2 in slave mode, triggered by TIM4, corresponding to ITR3, and enabling/starting channels 3 and 4 of TIM2 synchronously, etc:
static void MX_TIM4_Init(void)
{
TIM_OnePulse_InitTypeDef sConfig = {0};
TIM_SlaveConfigTypeDef sSlaveConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
htim4.Instance = TIM4;
htim4.Init.Period = 0xFFFF;
htim4.Init.Prescaler = 0;
htim4.Init.ClockDivision = 0;
htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
htim4.Init.RepetitionCounter = 0;
if (HAL_TIM_OnePulse_Init(&htim4, TIM_OPMODE_SINGLE) != HAL_OK)
{
Error_Handler();
}
sSlaveConfig.SlaveMode = TIM_SLAVEMODE_TRIGGER;
sSlaveConfig.InputTrigger = TIM_TS_TI2FP2;
sSlaveConfig.TriggerPolarity = TIM_TRIGGERPOLARITY_RISING;
sSlaveConfig.TriggerFilter = 0;
if (HAL_TIM_SlaveConfigSynchro(&htim4, &sSlaveConfig) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_ENABLE;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_ENABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
sConfig.OCMode = TIM_OCMODE_PWM2;
sConfig.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfig.Pulse = 1000;
sConfig.ICPolarity = TIM_ICPOLARITY_RISING;
sConfig.ICSelection = TIM_ICSELECTION_DIRECTTI;
sConfig.ICFilter = 0;
sConfig.OCNPolarity = TIM_OCNPOLARITY_HIGH;
sConfig.OCIdleState = TIM_OCIDLESTATE_RESET;
sConfig.OCNIdleState = TIM_OCNIDLESTATE_RESET;
if (HAL_TIM_OnePulse_ConfigChannel(&htim4, &sConfig, TIM_CHANNEL_1, TIM_CHANNEL_2) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_OnePulse_StartChannel(&htim4, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
}
static void MX_TIM2_Init(void)
{
TIM_OnePulse_InitTypeDef sConfig = {0};
TIM_SlaveConfigTypeDef sSlaveConfig = {0};
htim2.Instance = TIM2;
htim2.Init.Period = 0xFFFF;
htim2.Init.Prescaler = 0;
htim2.Init.ClockDivision = 0;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.RepetitionCounter = 0;
if (HAL_TIM_OnePulse_Init(&htim2, TIM_OPMODE_SINGLE) != HAL_OK)
{
Error_Handler();
}
sSlaveConfig.SlaveMode = TIM_SLAVEMODE_TRIGGER;
sSlaveConfig.InputTrigger = TIM_TS_ITR3;
if (HAL_TIM_SlaveConfigSynchro(&htim2, &sSlaveConfig) != HAL_OK)
{
Error_Handler();
}
sConfig.OCMode = TIM_OCMODE_PWM2;
sConfig.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfig.Pulse = 1000;
sConfig.ICPolarity = TIM_ICPOLARITY_RISING;
sConfig.ICSelection = TIM_ICSELECTION_DIRECTTI;
sConfig.ICFilter = 0;
sConfig.OCNPolarity = TIM_OCNPOLARITY_HIGH;
sConfig.OCIdleState = TIM_OCIDLESTATE_RESET;
sConfig.OCNIdleState = TIM_OCNIDLESTATE_RESET;
if (HAL_TIM_OnePulse_ConfigChannelOutput(&htim2, &sConfig, TIM_CHANNEL_3) != HAL_OK)
{
Error_Handler();
}
sConfig.Pulse = 2000;
if (HAL_TIM_OnePulse_ConfigChannelOutput(&htim2, &sConfig, TIM_CHANNEL_4) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_OnePulse_StartChannel(&htim2, TIM_CHANNEL_3) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_OnePulse_StartChannel(&htim2, TIM_CHANNEL_4) != HAL_OK)
{
Error_Handler();
}
}EDIT: I actually just used the above to have TIM1 trigger 6 other timers (7 total timers synchronized) as so:
TIM1 (using TI2FP2, PE11 (INPUT)) triggers: TIM1, CH1, E9 (OUTPUT), using TI2FP2 (TIM1, CH2) TIM2, CH4, PB11 (OUTPUT), using ITR0 (TIM1) TIM3, CH3, PB0 (OUTPUT), using ITR0 (TIM1) TIM4, CH1, PD12 (OUTPUT), using ITR0 (TIM1) TIM5, CH1, PA0 (OUTPUT), using ITR0 (TIM1) TIM8, CH2, PC7 (OUTPUT), using ITR0 (TIM1) TIM15, CH2, PE6 (OUTPUT), using ITR0 (TIM1)
All operating in One Pulse mode.
Here is an image showing the pulses from a Saleae Logic 16: https://imgur.com/a/lsbKAtg (The pulses look the same because I have them all the same for testing purposes, I will soon be implementing CDC USB to allow for configuration of each of the timers through VCP)