cancel
Showing results for 
Search instead for 
Did you mean: 

STM32 timer with toggled output triggered by CCx - is there any way to reset the state of OCxREF at a given point?

LGrov.1
Associate II

I have set up a timer to generate a given waveform using the capture-compare register, fed from DMA. The OCxREF output is set to TOGGLE mode. The waveform consists of a pattern, repeating n times, after each ARR update. A new pattern is loaded into DMA every n cycles.

Everything works, except for the fact that:

  • there seems to be no way of guaranteeing the start point of OCxREF is '0'
  • the OCxREF state appears to change every so often (usually after nth repeat, when new data is loaded into the DMA array), resulting in the pattern becoming inverted.Sometimes this occurs with setting CNT = 0, but not always.
  • At regular intervals, the first timer run (of the n repeats, from '0' to ARR) does NOT react to the DMA settings (or only starts part way through the timer count). This also often then affects the state of OCxREF going forward.

It would help if:

  • I could reliably return OCxREF to '0' at the start of each run of n repeats (I have tried first forcing it low via assignment in OCxM, then to 'toggle', but that doesn't work. Also, OCxCE only seems to respond to ETRF, so cannot clear OCxREF in firmware).
  • I could ensure that in the first 0->ARR interval the CCx is definitely loaded when it should be. I have tried: a) full re-initialisation of both DMA and timer each n-cycle. b) changing the order of initialisation (timer vs DMA) c) preloading the CCx, etc, etc.

I am losing the will to live! Any ideas? I can share code if that helps.

11 REPLIES 11

> tried first forcing it low via assignment in OCxM,

> then to 'toggle', but that doesn't work

You mean, using some of the forced modes? Why wouldn't that work? Don't you use preload ob OCxM?

Prepare a minimal but complete compilable example exhibiting the problem. Don't bother of you use any "library" though.

JW

LGrov.1
Associate II

Hi Jan,

Many thanks for your rapid response. As regards preload of OCxM...do you mean OCxPE? The exact function of preload is not really clear to me. Do I set OCxPE and then UG bit of EGR?

Attached is the src and inc for a compilable version. Drop me a line if you need anything additional...and thanks again for your help.

Hi Jan,

Sorry, learning to use this forum. I replied to you, but it looks like I did it as a 2nd answer instead! Code attached to that reply.

Do I really need preload enabled? My understanding is that a) Without preload and changes are carried across instantly (which is what I want). b) Preload would be used in the case where the register normally updates at an event, and you then set the UG bit to immediately software-update.

You misunderstood me.

I surely don't intend to untangle your program and penetrate is logic. I don't even want to understand what exactly this program in its entirety is going to do.

By minimal program I meant program exhibiting the problem you've raised above, i.e. that the forced modes don't set OCxRef. I expected a single-file, few lines program, just basic setup of pins and timers and the perhaps a loop with the CCMR settings in question, separated by appropriately set loopdelays do that the effect is observable.

I also don't intend to actually compile and run it. I write compilable, so that users present real programs, not fragments which were never even compiled yet alone run. There's no point in examining such.

At the end of the day, I hope that the user, by preparing and testing such minimal program find out himself what's the issue.

Now what I see in the timer-related portion of your program, mentioning the forced mode, appears to be OK. I don't know why are you under the impression that it does not work. There are other settings after the "forced" which maybe can blur the behaviour, e.g. changing CNT to 0, while CCRx==0, maybe results in a toggle too.

Toggle a pin between individual changes to TIM registers, as delays do the changes are not visible, and observe.

By preload I here meant CR2.CCPC=1 (only in advanced timers). You don't appear to set that so that won't be the culprit.

JW

alister
Lead

>there seems to be no way of guaranteeing the start point of OCxREF is '0'

This snippet resets the output to your idle state configured in OC2.

It works by changing mode to force inactive then back to toggle.

The timer must be enabled.

uint16_t tmpccmr1 = TIM1->CCMR1 & ~TIM_CCMR1_OC1M;
 TIM1->CCMR1 = tmpccmr1 | TIM_CCMR1_OC1M_2;
 TIM1->CCMR1 = tmpccmr1 | (TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_0);

> The timer must be enabled.

What do you mean by this? TIM_SR.CEN must be 1? Are you sure this is a requirement?

JW

TIM_CR1.CEN = 1. Yes that's what I meant. I'd used this method to start an encode by OC toggle-mode immediately before enabling its DMA. It was necessary because an encode may be interrupted by another encode, leaving the output state be unknown. I'd commented the timer must be enabled. So must have observed that. But could be mistaken.

This

// blinky for the 'F091 Nucleo
// GREEN LED = PA5 (no other LED on Nucleo is controlled by the mcu)
// PA5 = TIM2_CH1_ETR @ AF2
 
 
#include <stdint.h>
#include <stm32f0xx.h>
 
 
void LoopDelay(volatile uint32_t n) {
	while(n > 0) n--;
}
 
#define DELAY_CONSTANT 100000
 
int main(void) {
  RCC->AHBENR |= 0
    | RCC_AHBENR_GPIOAEN
    | RCC_AHBENR_GPIOBEN
  ;
  RCC->APB1ENR |= 0
    | RCC_APB1ENR_TIM2EN
  ;
 
  GPIOA->MODER = (GPIOA->MODER
    & (~GPIO_MODER_MODER5)      // LED
  ) | (0
    | (0b10 * GPIO_MODER_MODER5_0)   // LED - AF mode
  );
  GPIOA->AFR[0] = GPIOA->AFR[0] | (0
    | (2 << (4 * 5))   // AF2 - TIM2
  );
 
 
  TIM2->PSC=10000 - 1;
  TIM2->ARR = 800 - 1;
  TIM2->CCR1 = 400;
  TIM2->CCER = 0
    | (1 * TIM_CCER_CC1E)  // enable TIM2_CH1
  ;
/*
  TIM2->CR1 = 0
    | ( 1                       * TIM_CR1_CEN   )  // enable the timer to run freely
  ;
*/
 
  while(1) {
    TIM2->CCMR1 = 0
      | (0b100   * TIM_CCMR1_OC1M_0)  // force low
    ;
    LoopDelay(DELAY_CONSTANT);
    TIM2->CCMR1 = 0
      | (0b101   * TIM_CCMR1_OC1M_0)  // force high
    ;
    LoopDelay(DELAY_CONSTANT);
 
 
  }
}

makes a nice blinky for the 'F091 Nucleo, so I bet CEN does not need to be set for the FORCED modes to work.

JW

The OP observes he loses track of OC state.

I'd circumstances where I'd observe that too. I was generating encodes using DMA to write the TIM->ARR, and a new encode needed to abruptly terminate any previous encode, and if this occurred, the number of DMA cycles completed was unknown and the output state would need resetting.

Before starting an encode, I'd abort any DMA and set TIM->ARR = 0. The last DMA of each encode would also write TIM->ARR = 0. Disabling the TIM was never necessary.