Skip to main content
Marc1
Associate III
March 18, 2026
Solved

STM32L052 - Output PWM and Input Capture with the same Counter simultaneously

  • March 18, 2026
  • 6 replies
  • 448 views

STM32L052
TIM21, Channel 1: Output PWM
TIM21, Channel 2: Switching between Output PWM and Input Compare
System Clock: 32 MHz

Hello,

I want to use TIM21 of STM32L052 for PWM output on channel 1 and input capture on channel 2 simultaneously.

 

uint16_t TIM_CCMR1_backup;
uint16_t TIM_CCER_backup;

// PWM on channel 1 and channel 2
void PWM_init(void)
{
 RCC->APB2ENR |= RCC_APB2ENR_TIM21EN;

 TIM21->CR1 &= ~TIM_CR1_CEN;
 TIM21->ARR = 0xFFF;
 TIM21->CR1 = TIM_CR1_ARPE;

 // TIM21, channel 1
 TIM21->CCMR1 |= (0b111 << TIM_CCMR1_OC1M_Pos);	// PWM mode 2
 TIM21->CCMR1 |= TIM_CCMR1_OC1PE;	// output compare preload enable
 TIM21->CCR1 = 0;			// reset output compare register
 TIM21->CCER |= TIM_CCER_CC1E;		// enable output

 // TIM21, channel 2
 TIM21->CCMR1 |= (0b111 << TIM_CCMR1_OC2M_Pos);	// PWM mode 2
 TIM21->CCMR1 |= TIM_CCMR1_OC2PE;		// output compare preload enable
 TIM21->CCR2 = 0;				// reset output compare register
 TIM21->CCER |= TIM_CCER_CC2E;			// enable output

 TIM21->CNT = 0;				// reset counter

 TIM21->EGR |= TIM_EGR_UG;
 TIM21->CR1 |= TIM_CR1_CEN;			// enable counter
}

void switch_channel2_to_input_compare(void)
{
 TIM_CCMR1_backup = TIM21->CCMR1;
 TIM_CCER_backup = TIM21->CCER;

 TIM21->CCER &= ~TIM_CCER_CC2E;		// disable output

 MODIFY_REG(TIM21->CCMR1, TIM_CCMR1_CC2S_Msk, 0b10 << TIM_CCMR1_CC2S_Pos);	// CC2 channel is configured as input, IC1 is mapped on TI1
 TIM21->OR = (0b101 << TIM21_OR_TI1_RMP_Pos);	// TI1 input connected to LSI clock
 TIM21->CCER = TIM_CCER_CC2E;			// capture on rising edge, enable CCR2
 TIM21->SR &= ~TIM_SR_CC2IF;			// clear interrupt flag
 TIM21->SR &= ~TIM_SR_CC2OF;			// clear interrupt flag
 TIM21->DIER = TIM_DIER_CC2IE;			// enable capture interrupt

 NVIC_EnableIRQ(TIM21_IRQn);			// enable interrupt

 TIM21->CR1 |= TIM_CR1_CEN;			// enable counter
}

void switch_channel2_back_to_output_PWM(void)
{
 TIM21->CCER &= ~TIM_CCER_CC2E;
 TIM21->CR1 &= ~TIM_CR1_CEN;
 TIM21->CCMR1 = TIM_CCMR1_backup;
 TIM21->CCER = TIM_CCER_backup;
 TIM21->CCR2 = 0;
 TIM21->CR1 |= TIM_CR1_CEN;
}

 

(1) PWM_init(): The application starts with both channels in output PWM mode.

(2) ... then switches channel 2 repeatedly to input compare mode, while channel 1 stays in output PWM mode (switch_channel2_to_input_compare()), does its measurments and ...

(3) ... then switches channel 2 back to output PWM mode (switch_channel2_back_to_output_PWM()).

The problem:
While channel 2 is in capture compare mode, channel 1 stops working (no more PWM).
Once channel 2 switches back to PWM mode, channel 1 PWM continues working.

Is it not possible to have channel 1 running in PWM mode while channel 2 being input capture mode?

Best answer by TDK

> TIM21->CCER = TIM_CCER_CC2E;

This clears CC1E. 

6 replies

gbm
Principal
March 18, 2026

timer channels are independent, so it is possible to use them like you describe. It is, however, hard to imagine how exactly are you going to connect this timer pin to anything reasonable if it is assumed to switch its direction on the fly,

 

<edit> I didn't scroll down the code so I missed the point. Right, the solution should work. For easier debugging, remove the unnecessary logic operations on timer control registers and replace them with simple assignments.

My STM32 stuff on github - compact USB device stack and more: https://github.com/gbm-ii/gbmUSBdevice
TDK
TDKBest answer
Super User
March 18, 2026

> TIM21->CCER = TIM_CCER_CC2E;

This clears CC1E. 

"If you feel a post has answered your question, please click ""Accept as Solution""."
Marc1
Marc1Author
Associate III
March 18, 2026

That's the point! I was so blind. Thank you! You saved my day.

Marc1
Marc1Author
Associate III
March 24, 2026

There is still a weird behavior if I use the mentioned settings:

(1) After executing a NVIC_SystemReset() ,TIM21 doesn't work any more. I need to hard reset the device to get TIM21 working again.

(2) If I use a SystemCoreClock < 8 MHz, I need to stop also PWM of channel 1, before I can switch channel 2 from PWM output to capture input. If I switch channel 2 from PWM output to capture input without stopping also channel 1, TIM21 doesn't work any more on both channels.

 

void switch_channel2_to_input_compare(void)
{
 TIM_CCMR1_backup = TIM21->CCMR1;
 TIM_CCER_backup = TIM21->CCER;

 TIM21->CCER &= ~TIM_CCER_CC2E;

 // Why do I need to stop channel 1 with SystemCoreClock < 8 MHz
 if (SystemCoreClock < 8000000)
 {
 TIM21->CCER &= ~TIM_CCER_CC1E;
 TIM21->CCMR1 = 0;
 }

 TIM21->CCMR1 &= 0x00FF; // clear channel 2, keep channel 1
 TIM21->CCMR1 |= (0b10 << TIM_CCMR1_CC2S_Pos); // CC2 channel is configured as input, IC1 is mapped on TI1
 TIM21->CCMR1 |= (1 << TIM_CCMR1_IC2PSC_Pos); // Input capture 2 prescaler
 TIM21->OR = (0b101 << TIM21_OR_TI1_RMP_Pos); // TI1 input connected to LSI clock
 TIM21->CCER |= TIM_CCER_CC2E; // capture on rising edge, enable CCR2
 TIM21->SR &= ~TIM_SR_CC2IF; // clear interrupt flag
 TIM21->SR &= ~TIM_SR_CC2OF; // clear interrupt flag
 TIM21->DIER = TIM_DIER_CC2IE; // enable capture interrupt
 NVIC_EnableIRQ(TIM21_IRQn); // enable interrupt
 TIM21->CR1 |= TIM_CR1_CEN; // enable counter
}

Did I miss something? I'm confused.

TDK
Super User
March 24, 2026

It's clear you have other things happening in the program other than what is presented. The issue is likely a logic error somewhere in the program. The timer is a dumb state machine. It doesn't know about other parts of the chip, or how it got into that state, it just does its thing.

The timer peripheral is independent of the clock settings, apart from the frequency. If the register settings are the same, it will behave the same. I suggest looking at the register settings in the "not working" vs "working" scenario and compare. Use the SFR viewer in STM32CubeIDE.

"If you feel a post has answered your question, please click ""Accept as Solution""."
Marc1
Marc1Author
Associate III
March 24, 2026

Whatever the program does, shouldn't a NVIC_SystemReset() bring back all peripherals to original state?

TIM21 registers look the same, in working state and in not working state.

TDK
Super User
March 24, 2026

Consider creating a compileable minimal working example which illustrates the problem and sharing that issue in a new thread. Without that, there's nothing to dig into here.

"If you feel a post has answered your question, please click ""Accept as Solution""."
Marc1
Marc1Author
Associate III
March 24, 2026

@TDK wrote:

Consider creating a compileable minimal working example which illustrates the problem and sharing that issue in a new thread. Without that, there's nothing to dig into here.


I'm trying.

Maybe I need to tell more about the firmware:

The bootloader is based on USB DFU_Standalone example from ST.
The application is based on USB CDC_Standalone example from ST.

While the bootloader or application is running, it uses both channels of TIM21 as PWM outputs.

Within the application it is possible to reboot into bootloader DFU mode, using NVIC_SystemReset().
After finishing or aborting DFU mode, the bootloader executes a second NVIC_SystemReset() and returns to the application.

So far everything worked well ...

... until I added the above mentioned code in order to change channel 2 of TIM21 periodically from output PWM to input capture and back.

Since that firmware change, the second NVIC_SystemReset() from bootloader makes the TIM21 PWM outputs not working anymore.

If I replace the second NVIC_SystemReset() (during DFU mode) by a power cycle, TIM21 PWM outputs work well.

If I replace the second NVIC_SystemReset() (during DFU mode) by a reset, done by an external python tool (pydfu.py), TIM21 PWM outputs work well.

 

Sequence:
(1) Power Cycle -> Passing the bootloader, starting the application. During the application: Switching channel 2 of TIM21 periodically between output and input. --> PWM outputs of TIM21 are working
(2) NVIC_SystemReset() -> Rebooting into DFU mode  --> PWM outputs of TIM21 are still working
(3) NVIC_SystemReset() -> Rebooting, passing the bootloader and starting the application again. --> PWM outputs of TIM21 are not working anymore

Bootloader and application each perform the TIM21 initialization PWM_init().

My main question is: What is the difference between a NVIC_SystemReset() and a hardware reset?

 

TDK
Super User
March 24, 2026

> (2) NVIC_SystemReset() -> ... --> PWM outputs of TIM21 are still working
> (3) NVIC_SystemReset() -> ... --> PWM outputs of TIM21 are not working anymore

NVIC_SystemReset() is not at fault here. The difference in behavior is due to the "..." between the events. We don't have much insight into that.

"If you feel a post has answered your question, please click ""Accept as Solution""."
waclawek.jan
Super User
March 24, 2026

> TIM21 registers look the same, in working state and in not working state.

> the second NVIC_SystemReset() from bootloader makes the TIM21 PWM outputs not working anymore.

If TIM21 registers "look the same" means you have verified them having the same content which is also consistent with the RM's description for given functionality; and you don't observe PWM outputs toggling, then there are two possible things, again observable through registers content:

- different GPIO settings for the related pins

- different TIM21 kernel clock setting (if TIM21 in 'L0 is capable of being clocked from a non-APB clock)

There is also a third option, something in your setup you did not tell us (e.g. you don't measure directly at the PWM pin using oscilloscope, and there is some difference in hardware between the two states).

With additional questions, please always start a new thread.

JW

Marc1
Marc1Author
Associate III
March 24, 2026

> - different GPIO settings for the related pins
I also checked the GPIO and RCC-Enable registers. They are ok.

> - different TIM21 kernel clock setting (if TIM21 in 'L0 is capable of being clocked from a non-APB clock)
How? I couldn't find anything about clock selection of TIM21 in the reference manual.

PWM outputs are connected to LEDs. So I can see if there is any signal or not.

waclawek.jan
Super User
March 24, 2026

There is very little difference between System Reset and Power-On Reset (and NRST-pin reset). I don't t

I agree with TDK that the difference is most likely in the code run at the different occassions.

 

> > - different TIM21 kernel clock setting (if TIM21 in 'L0 is capable of being clocked from a non-APB clock)
> How? I couldn't find anything about clock selection of TIM21 in the reference manual.

I said, "if". It's quite likely it's not.

 

> PWM outputs are connected to LEDs. So I can see if there is any signal or not.

Are you sure? Would you see it even if the frequency is very low or very high? How are those LEDs exactly connected?

Post content of TIM21 and relevant GPIO registers in the "non-working" case.

JW

Marc1
Marc1Author
Associate III
March 24, 2026

>> PWM outputs are connected to LEDs. So I can see if there is any signal or not.

> Are you sure? Would you see it even if the frequency is very low or very high? How are those LEDs exactly connected?
LEDs are directly connected to the Pins

Marc1
Marc1Author
Associate III
March 24, 2026

I got it! And you were right. The problem was within the code.

And the difference between NVIC_SystemReset() and a hardware reset seem to be the timing. Maybe different clock settings? In one case the TIM21 destroying code got executed before TIM21 initialization and in the other case after TIM21 initialization.

Thank you for your help!