cancel
Showing results for 
Search instead for 
Did you mean: 

OC1M set active/inactive on match immediatly changes output

Svan .41
Associate II

In the capture/compare ISR, I want to program the next CCR1 value and the associated action: to change the output to active or inactive when CNT matches CCR1. For this I need to set TIM1_CCMR1 OC1M to 0001 or 0010.

However, as soon as I program OC1M with 0001, the output becomes active (and inactive on 0010). It ignores the CNT/CCR1 matching condition?

10 REPLIES 10
TDK
Guru

It shouldn't. Show your code and relevant register values. Note that setting OC1M to 0001 or 0010 is a one-way street. The OC1REF value doesn't reset itself when the counter changes.

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

Thank you.

I use small function to update register bits:

void ProgRegister(uint32_t *RegAddr, uint32_t Bits, uint32_t BitMask){
// Updates register in one go, as to protect from inproper intermediate values
 *RegAddr = (*RegAddr & ~BitMask) | Bits;
}

Then, to initialise the stm32

// Set registers
ProgRegister((uint32_t*) &TIM1->CR2, 0, TIM_CR2_CCPC_Msk);
TIM1->ARR = 40000;
ProgRegister((uint32_t*) &TIM1->CR1, 0, TIM_CR1_CMS_0 | TIM_CR1_CMS_1); // Set PWM strategy Edge Aligned
ProgRegister((uint32_t*) &TIM1->CR1, TIM_CR1_UDIS, TIM_CR1_UDIS);// Disable Update Event Generation
ProgRegister((uint32_t*) &TIM1->CR1, TIM_CR1_CKD_0, TIM_CR1_CKD_0) ; // Divides the timer clock by 4 meaning input clock is now 10MHz.
ProgRegister((uint32_t*) &TIM1->BDTR, 0x3C, TIM_BDTR_DTG); // This should read about 1.5us of dead time.
ProgRegister((uint32_t*) &TIM1->BDTR, 0, TIM_BDTR_OSSI); // When inactive outputs turn to High-Z
ProgRegister((uint32_t*) &TIM1->BDTR, 0, TIM_BDTR_OSSR); // When inactive outputs turn to High-Z
ProgRegister((uint32_t*) &TIM1->CCMR1, 0, TIM_CCMR1_CC1S_Msk | TIM_CCMR1_CC2S_Msk); //Set Timers for output compare
ProgRegister((uint32_t*) &TIM1->CCMR1, 0, TIM_CCMR1_CC1S_Msk | TIM_CCMR1_CC2S_Msk); //Set Timer as output 
ProgRegister((uint32_t*) &TIM1->CCMR1, 0, TIM_CCMR1_CC1S_Msk | TIM_CCMR1_CC2S_Msk);//Set Timers Output Compare 1 fast enable
 
// Disable and init output
ProgRegister((uint32_t*) &TIM1->CCER, 0, TIM_CCER_CC1E | TIM_CCER_CC1NE); // Disable CC1
TIM1->CCR1 = 0; // Wait for next period
ProgRegister((uint32_t*) &TIM1->EGR, TIM_EGR_CC1G, TIM_EGR_CC1G); // Generate interrupt on CC
 
ProgRegister((uint32_t*) &TIM1->SR, TIM_SR_B2IF |TIM_SR_B2IF, TIM_SR_B2IF |TIM_SR_B2IF);// TIM_SR_UIF | TIM_SR_B2IF |TIM_SR_B2IF; // Enable update and break interrupts.
 
//Now enable counter Tim1
TIM1->CR1 |= TIM_CR1_CEN;
 
// Setup Interrupts
SetUpPWMInterrupts();

With SetUpPWMInterrupts:

int SetUpPWMInterrupts(void){
 
	__disable_irq();
	NVIC_EnableIRQ(TIM1_CC_IRQn);//TIM1 Capture Compare Interrupt
	ProgRegister((uint32_t*) &TIM1->DIER, TIM_DIER_CC1IE ,TIM_DIER_CC1IE ); // enable interrupts on capture compare
	TIM1->BDTR |= TIM_BDTR_MOE; // Enable outputs on next UIE
	__enable_irq();
	return 0;
}

And in the TIM1_CC_IRQHandler, at some point the output is enabled:

ProgRegister((uint32_t*) &TIM1->CCMR1, TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_0, TIM_CCMR1_OC1M); // 0101 force output high
ProgRegister((uint32_t*) &TIM1->CCER, TIM_CCER_CC1E | TIM_CCER_CC1NE, TIM_CCER_CC1E | TIM_CCER_CC1NE); // Enable CC1

And depending on the CCR1 value that is inside TIM1->CCR1, I want to program a net CCR1 value and a rising edge or a falling edge:

But as soon as I program

ProgRegister((uint32_t*) &TIM1->CCMR1, TIM_CCMR1_OC1M_1, TIM_CCMR1_OC1M); //(0010)

The output goes low

And alternatively

ProgRegister((uint32_t*) &TIM1->CCMR1, TIM_CCMR1_OC1M_0, TIM_CCMR1_OC1M); //(0001)

The output goes low

TDK
Guru

> But as soon as I program

> ...

> The output goes low

How are you monitoring this? If your timer is running, it's going to hit CNT = CCR1 to trigger the condition. Timers aren't stopped by default during debugging, if that's how you're checking. You appear to have a relatively quick timer update frequency (PSC=0, ARR=40000).

Consider using the CMSIS header files for register addresses rather than redefining things. For example, I have to take your word that CCMR_Register is defined correctly, whereas you could have used &TIM1->CCMR1. It looks like you did this in other spots.

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

I am stepping through the code, and watching on a PicoScope. When I let the code run freely, the pulses appear to be jittering, hinting that it is the code that does the edges and not the Capture Compare (which should be virtually jitter free)

Svan .41
Associate II

> Timers aren't stopped by default during debugging

So it seems, thanks. But that means my jitter comes from somewhere else? It is approx. 800 PPM (!)

Would increasing the prescaler do that? It would reduce my pulse width timing resolution.

TDK
Guru

Timers don't have any internal jitter. They are hardware state machines with a very fixed behavior. They have delays in some cases, but those delays are fixed. 800 ppm is a relative value, how many clocks does that translate to in absolute terms? Note that HSI has much more jitter than HSE.

There can be jitter in handling interrupts. There can be jitter from waiting for flash. It looks like you're disabling interrupts in your code, that can also introduce jitter by delaying the ISR.

It's not clear what behavior you're trying to achieve. It's not typical to need to mess with the timers inside of an ISR. There may be a way to produce the behavior you want without cpu intervention.

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

Thank you for sticking 😉

The goal is to calculate how long a pulse needs to be directly after it has started. This is for some control algorithm:

0693W00000GWQSnQAP.pngFor each of the 3 outputs the start is 120 degrees delayed. The length of output is to be controlled individually. Eventually, something similar needs to be done with TIM8, but 60 degrees out of phase with TIM1.

To show you the jitter I am experiencing, I have made a screenshot of the oscilloscope. Output 1 (blue) is programmed as intended, with the rising edge at CNT==00, Output 2 (Red) is set to toggle, every time CNT==0. The generated frequencies are therefor 2 kHz and 1 kHz.

Every rising edge in output 2 aligns perfectly with that of output 1.

0693W00000GWQgpQAH.pngAbove, the trigger is set to the rising edge of output 2. The next rising edge, at 1 ms is showing around 800ns jitter (800PPM).

TDK
Guru

What is your clock source?

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

So, I checked the jitter with another piece of SW (with a more normal method of PWM generation). It does look like something is wrong with the clock I'm using (HSI16)

int SetupClocks(){
 
	// MSI clock at 4MHz is used as the system clock from reset. This means we can use the HSI16 clock without a problem.
	//
 
	// Mod flash latency to make the processor work.
	FLASH->ACR |= 0x03;
 
	// Turn on HSI16 clock oscillator and check that it's started properly. Need to do something intelligent here or it'll hang with the green LED on. Need an error handler function.
	//
	uint32_t StartCount = 0;
	RCC->CR |= (0x01 << 8);
	while (!(RCC->CR & (0x1 << 10))){
 
		GPIOA->BSRR |= 0x20;
 
		if(StartCount > 1000000){
 
			RCC->CR = 0x63;
 
		}else if(StartCount > 2000000){
 
			RCC->CR |= (0x01 << 8);
			StartCount=0;
 
		}
 
		StartCount++;
 
	}
 
	//GPIOA->BSRR |= 0x200000;
 
 
	// Select PLL source as HSI16 and set PLLM to 1 meaning input to PLL is 16MHz.
	//
 	RCC->PLLCFGR = 0x0; //Clear the register. Datasheet says it's 0x1000 on a reset which explains why a daft frequency appears from the PLL.
	RCC->PLLCFGR |= RCC_PLLCFGR_PLLSRC_HSI;
 
	// Set PLLN multiplier. Currently set to 10 for this test so output is 160MHz. PLLR divides the output of the PLL by 2 so that output is 80MHz.
	//
	RCC->PLLCFGR |= RCC_PLLCFGR_PLLN_1 | RCC_PLLCFGR_PLLN_3;
 
	// Turn on main PLL and check it's in lock.
	//
	RCC->CR |= (0x1 << 24);
	while(!(RCC->CR & (0x01 << 25))){
 
		GPIOA->BSRR |= 0x20;
 
				if(StartCount > 1000000){
 
					RCC->CR = 0x63;
 
				}else if(StartCount > 2000000){
 
					RCC->CR |= (0x01 << 8);
					StartCount=0;
 
				}
 
				StartCount++;
 
	}
 
	GPIOA->BSRR |= 0x200000;
 
	// Turn on main PLL output.
	//
	RCC->PLLCFGR |= (0x1 << 24);
 
	// Set the SysClk to the output of the PLL. The processor is now running at full fat 80MHz.
	//
	RCC->CFGR |= 0x03;
 
	// Drop APB1 to 20MHz.
	//
	RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;
 
	return 0;
}