cancel
Showing results for 
Search instead for 
Did you mean: 

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

Marc1
Associate III

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?

1 ACCEPTED SOLUTION

Accepted Solutions
TDK
Super User

> TIM21->CCER = TIM_CCER_CC2E;

This clears CC1E. 

If you feel a post has answered your question, please click "Accept as Solution".

View solution in original post

15 REPLIES 15
gbm
Principal

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
Super User

> TIM21->CCER = TIM_CCER_CC2E;

This clears CC1E. 

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

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

Marc1
Associate III

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.

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
Associate III

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

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
Associate III

@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?

 

waclawek.jan
Super User

> 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