STM32 One-Pulse Mode
Introduction
The STM32 microcontroller family offers a variety of timer peripherals with advanced features for precise timing control. One of these features is the One-Pulse Mode (OPM), which allows you to generate a single, precisely timed pulse when triggered. This mode is particularly useful in applications requiring exact timing control, such as:
- Triggering sensors or actuators once
- Generating precise delays
- Creating one-time control signals
- Timing critical events
In this tutorial, we'll explore how the One-Pulse Mode works, how to configure it, and walk through some practical examples to demonstrate its capabilities.
Understanding One-Pulse Mode
What is One-Pulse Mode?
One-Pulse Mode is a special timer operating mode where the timer automatically stops after generating a single pulse. This differs from the standard timer operation, which continuously generates pulses until explicitly stopped.
How Does It Work?
When configured in One-Pulse Mode:
- The timer waits for a trigger event (like an external signal or software trigger)
- Once triggered, it generates precisely one pulse with a programmable width
- After generating the pulse, the timer automatically stops and disables itself
- The timer needs to be re-enabled to generate another pulse
This behavior makes One-Pulse Mode perfect for applications requiring a single, accurate timing event without the need for continuous timer operation.
Timer Configuration for One-Pulse Mode
To use One-Pulse Mode, we need to configure several key parameters:
- Timer Selection: Choose an appropriate timer (e.g., TIM2, TIM3, etc.)
- Clock Source: Configure the timer clock source and prescaler
- Counter Mode: Set up as one-pulse mode
- Pulse Width: Configure through the auto-reload register (ARR) and compare register (CCR)
- Trigger Source: Select what will trigger the timer (external pin, software, etc.)
Let's look at the step-by-step process to set up One-Pulse Mode.
Basic Configuration Steps
Here's how to configure a timer for One-Pulse Mode using STM32CubeHAL:
void ConfigureOnePulseMode(void)
{
// 1. Enable the timer clock
__HAL_RCC_TIM2_CLK_ENABLE();
// 2. Initialize timer handle
TIM_HandleTypeDef htim2;
htim2.Instance = TIM2;
htim2.Init.Prescaler = 72 - 1; // Assuming 72MHz clock, gives 1MHz timer clock
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 1000 - 1; // 1ms period
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
// 3. Initialize the timer base
HAL_TIM_Base_Init(&htim2);
// 4. Configure One-Pulse Mode
TIM_OnePulse_InitTypeDef onePulseConfig;
onePulseConfig.OCMode = TIM_OCMODE_PWM2; // PWM mode 2 for proper pulse generation
onePulseConfig.Pulse = 500 - 1; // 500 ticks (half of period) = 0.5ms pulse width
onePulseConfig.OCPolarity = TIM_OCPOLARITY_HIGH;
onePulseConfig.OCNPolarity = TIM_OCNPOLARITY_HIGH;
onePulseConfig.OCIdleState = TIM_OCIDLESTATE_RESET;
onePulseConfig.OCNIdleState = TIM_OCNIDLESTATE_RESET;
// 5. Initialize One-Pulse Mode
HAL_TIM_OnePulse_Init(&htim2, TIM_OPMODE_SINGLE);
// 6. Configure the output channel (e.g., Channel 1)
HAL_TIM_OnePulse_ConfigChannel(&htim2, &onePulseConfig, TIM_CHANNEL_1, TIM_CHANNEL_2);
// 7. Enable the output channel
HAL_TIM_OnePulse_Start(&htim2, TIM_CHANNEL_1);
}
In this example:
- We configure TIM2 with a prescaler to get a 1MHz timer clock
- The period is set to 1000 ticks (1ms)
- The pulse width is set to 500 ticks (0.5ms)
- We use PWM mode 2, which is ideal for One-Pulse Mode operation
Trigger Sources in One-Pulse Mode
One-Pulse Mode can be triggered in different ways:
Software Trigger
You can trigger the pulse through software by setting the appropriate bit:
// Trigger the one-pulse mode via software
HAL_TIM_GenerateEvent(&htim2, TIM_EVENTSOURCE_TRIGGER);
External Trigger
You can also configure a timer to be triggered by an external signal:
void ConfigureExternalTrigger(void)
{
// Configure TIM2 to be triggered by an external signal on TI1 (PA0)
TIM_SlaveConfigTypeDef slaveConfig;
slaveConfig.SlaveMode = TIM_SLAVEMODE_TRIGGER;
slaveConfig.InputTrigger = TIM_TS_TI1FP1; // Use filtered TI1 as trigger
slaveConfig.TriggerPolarity = TIM_TRIGGERPOLARITY_RISING;
slaveConfig.TriggerFilter = 0;
HAL_TIM_SlaveConfigSynchro(&htim2, &slaveConfig);
}
This configuration makes the timer wait for a rising edge on the TI1 input (typically PA0 for TIM2) before generating the pulse.
Practical Examples
Let's explore some practical applications of One-Pulse Mode.
Example 1: Precise Delay Generator
This example shows how to use One-Pulse Mode to generate a precise delay before executing a task:
void PreciseDelayExample(void)
{
// Configure One-Pulse Mode
ConfigureOnePulseMode();
// Configure timer interrupt
HAL_NVIC_SetPriority(TIM2_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(TIM2_IRQn);
__HAL_TIM_ENABLE_IT(&htim2, TIM_IT_UPDATE);
// Trigger the timer to start the delay
HAL_TIM_GenerateEvent(&htim2, TIM_EVENTSOURCE_TRIGGER);
// The interrupt will be called after exactly the configured delay
// Main program continues executing...
}
// Timer interrupt handler
void TIM2_IRQHandler(void)
{
if (__HAL_TIM_GET_FLAG(&htim2, TIM_FLAG_UPDATE) != RESET)
{
if (__HAL_TIM_GET_IT_SOURCE(&htim2, TIM_IT_UPDATE) != RESET)
{
__HAL_TIM_CLEAR_IT(&htim2, TIM_IT_UPDATE);
// Execute your time-critical code here
DelayedTask();
}
}
}
void DelayedTask(void)
{
// This function will be called after the precise delay
// Perform your timing-critical operations here
}
Example 2: Sensor Trigger Controller
In this example, we'll create a system that triggers a sensor once and captures its response:
void SensorTriggerExample(void)
{
// Configure GPIO for sensor echo input
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitStruct.Pin = GPIO_PIN_1; // PA1 for echo input
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// Configure Timer for One-Pulse Mode to trigger sensor
ConfigureOnePulseMode(); // Configure TIM2 for a 10µs pulse (adjust parameters as needed)
// Start sensor measurement
HAL_TIM_OnePulse_Start(&htim2, TIM_CHANNEL_1);
HAL_TIM_GenerateEvent(&htim2, TIM_EVENTSOURCE_TRIGGER);
// Wait for sensor response (polling method)
uint32_t timeout = HAL_GetTick() + 100; // 100ms timeout
uint32_t startTime, endTime, duration;
// Wait for echo to start
while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET)
{
if (HAL_GetTick() >= timeout) return; // Timeout
}
startTime = HAL_GetTick();
// Wait for echo to end
while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_SET)
{
if (HAL_GetTick() >= timeout) return; // Timeout
}
endTime = HAL_GetTick();
// Calculate duration
duration = endTime - startTime;
// Process sensor data
ProcessSensorData(duration);
}
This example would be applicable for sensors like ultrasonic distance sensors that require a trigger pulse followed by echo duration measurement.
Example 3: Precise Motor Control Pulse
This example demonstrates using One-Pulse Mode to generate a precise control signal for a stepper motor:
void StepperPulseExample(void)
{
// Configure the stepper motor direction pin
HAL_GPIO_WritePin(DIRECTION_GPIO_Port, DIRECTION_Pin, GPIO_PIN_SET); // Set direction
// Configure One-Pulse Mode with appropriate timing for stepper motor
TIM_HandleTypeDef htim2;
htim2.Instance = TIM2;
htim2.Init.Prescaler = 72 - 1;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 100 - 1; // Shorter period for faster stepping
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
HAL_TIM_Base_Init(&htim2);
TIM_OnePulse_InitTypeDef onePulseConfig;
onePulseConfig.OCMode = TIM_OCMODE_PWM2;
onePulseConfig.Pulse = 50 - 1; // 50% duty cycle
onePulseConfig.OCPolarity = TIM_OCPOLARITY_HIGH;
onePulseConfig.OCNPolarity = TIM_OCNPOLARITY_HIGH;
onePulseConfig.OCIdleState = TIM_OCIDLESTATE_RESET;
onePulseConfig.OCNIdleState = TIM_OCNIDLESTATE_RESET;
HAL_TIM_OnePulse_Init(&htim2, TIM_OPMODE_SINGLE);
HAL_TIM_OnePulse_ConfigChannel(&htim2, &onePulseConfig, TIM_CHANNEL_1, TIM_CHANNEL_2);
// Generate a precise step pulse
HAL_TIM_OnePulse_Start(&htim2, TIM_CHANNEL_1);
HAL_TIM_GenerateEvent(&htim2, TIM_EVENTSOURCE_TRIGGER);
// Wait for pulse to complete
while (__HAL_TIM_GET_FLAG(&htim2, TIM_FLAG_UPDATE) == RESET) {}
// Reset the flag
__HAL_TIM_CLEAR_FLAG(&htim2, TIM_FLAG_UPDATE);
// Pulse completed, motor has moved one step
}
Advanced One-Pulse Mode Techniques
Combining with DMA
For more complex applications, you can combine One-Pulse Mode with DMA to automatically update pulse parameters:
void OnePulseWithDMAExample(void)
{
// DMA configuration for automatic pulse width update
static uint32_t pulseWidths[3] = {100, 200, 300}; // Different pulse widths
__HAL_RCC_DMA1_CLK_ENABLE();
hdma_tim2_up.Instance = DMA1_Channel2;
hdma_tim2_up.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_tim2_up.Init.PeriphInc = DMA_PERIPH_INC_DISABLE;
hdma_tim2_up.Init.MemInc = DMA_MEMORY_INC_ENABLE;
hdma_tim2_up.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
hdma_tim2_up.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
hdma_tim2_up.Init.Mode = DMA_NORMAL;
hdma_tim2_up.Init.Priority = DMA_PRIORITY_HIGH;
HAL_DMA_Init(&hdma_tim2_up);
// Link DMA to timer
__HAL_LINKDMA(&htim2, hdma[TIM_DMA_ID_UPDATE], hdma_tim2_up);
// Enable timer DMA request
__HAL_TIM_ENABLE_DMA(&htim2, TIM_DMA_UPDATE);
// Start DMA transfer to update CCR register
HAL_DMA_Start(&hdma_tim2_up, (uint32_t)pulseWidths, (uint32_t)&htim2.Instance->CCR1, 3);
// Configure and start one-pulse mode
// ... (similar to previous examples)
}
Timer Chaining
You can also chain multiple timers in One-Pulse Mode to create complex timing sequences:
void ChainedTimersExample(void)
{
// Configure first timer (TIM2) in One-Pulse Mode
// ... (configuration code similar to previous examples)
// Configure second timer (TIM3) to be triggered by TIM2's TRGO signal
TIM_HandleTypeDef htim3;
htim3.Instance = TIM3;
// ... (basic configuration)
// Set up slave mode to be triggered by TIM2
TIM_SlaveConfigTypeDef slaveConfig;
slaveConfig.SlaveMode = TIM_SLAVEMODE_TRIGGER;
slaveConfig.InputTrigger = TIM_TS_ITR1; // Internal Trigger 1 (TIM2)
slaveConfig.TriggerPolarity = TIM_TRIGGERPOLARITY_RISING;
slaveConfig.TriggerFilter = 0;
HAL_TIM_SlaveConfigSynchro(&htim3, &slaveConfig);
// Configure TIM2 to generate trigger output when it finishes
TIM_MasterConfigTypeDef masterConfig;
masterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
masterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_ENABLE;
HAL_TIMEx_MasterConfigSynchronization(&htim2, &masterConfig);
// Start both timers in One-Pulse Mode
HAL_TIM_OnePulse_Start(&htim2, TIM_CHANNEL_1);
HAL_TIM_OnePulse_Start(&htim3, TIM_CHANNEL_1);
// Trigger the first timer
HAL_TIM_GenerateEvent(&htim2, TIM_EVENTSOURCE_TRIGGER);
// Now TIM2 will generate a pulse, and when it finishes,
// it will automatically trigger TIM3 to generate its pulse
}
Common Issues and Troubleshooting
When working with One-Pulse Mode, you might encounter some common issues:
-
Pulse Not Generated
- Check timer clock configuration
- Verify trigger source is properly configured
- Ensure output channel is correctly set up
-
Unexpected Pulse Width
- Verify prescaler settings
- Check ARR and CCR values
- Confirm the OCMode setting (PWM1 vs PWM2)
-
Multiple Pulses Instead of One
- Ensure One-Pulse Mode is properly enabled
- Check for unintended retriggering
-
Timer Not Stopping After Pulse
- Verify OPMODE is set to SINGLE
- Check for conflicting timer configurations
Summary
STM32's One-Pulse Mode is a powerful feature for generating precise, single-pulse events. Key points to remember:
- One-Pulse Mode automatically stops the timer after generating a single pulse
- It can be triggered by software or external events
- The pulse width is precisely controlled by timer registers
- It's useful for sensor triggering, precise delays, and motor control
- It can be combined with DMA and other timers for more complex operations
By mastering One-Pulse Mode, you can create highly accurate timing systems that are perfect for a wide range of embedded applications where precision is critical.
Additional Resources
To deepen your understanding of STM32 Timers and One-Pulse Mode:
- Explore the STM32 Reference Manual for your specific device
- Review the timer-related sections in the STM32 HAL documentation
- Practice with different trigger sources and pulse widths
- Try implementing chained timer sequences
Exercises
- Configure a timer in One-Pulse Mode to generate a 100µs pulse when a button is pressed.
- Create a circuit that uses One-Pulse Mode to trigger an ultrasonic sensor and measure distance.
- Implement a system that generates three different pulse widths in sequence using One-Pulse Mode and DMA.
- Design a stepper motor controller that uses One-Pulse Mode for precise positioning.
- Modify the timer chain example to create a complex timing sequence with three timers.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)