cancel
Showing results for 
Search instead for 
Did you mean: 

stm32l476 nucleo board --- experimenting with output compare timer 3 channel 1??

SWenn.1
Senior III

Good afternoon everyone....

I am trying to learn more about the stm32 and am using a nucleo L476 and an oscilloscope. In particular I want to understand more about the output compares of the timers. I have read many an app-note , user guide and data sheet regarding these and am now at the stage of testing stuff out.....I am running into complete confusion with respect to this...

I have a 26MHz clock with a prescaler of 26 -1 (should be a 1MHz Cnt clock). I have the ARR register loaded with 100 - 1 (should create an UIF event every 100us). I am using CubeMX to set this up. There are a few questions:

  1. I had to code the macro (see below) bcz CubeMx doesn't seem to check the UIE bit when setting up as compare output.....Can anyone comment as to why?
__HAL_TIM_ENABLE_IT(&htim3, TIM_IT_UPDATE);

The following below is my ISR.....My attempt was to have UIF interrupt me every 10khz (see above comment on this), turn the output on and arm the CCR with counter + 10. My thought was that after this expires (CNTR clock is 1MHz so my expectation was 10us later) I would get a CC1R interrupt and then clear the output using FORCED_ACTIVE and INACTIVE statements. There are a few things here:

  1. I notice with breakpoints set neither the macro nor the call after it clear the UIF flag....Why is this??(I have attached pic below showing enables or IRQs set and flags set
  2. I never get into the CC1 portion of the interrupt...any thoughts as to why?? as the expression solves to a '1'
  3. Where do I find the various priorities of the flags that can cause an interrupt in TIMER3?? is the UIE higher priority than the CCxE?
void HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef *htim)
{
	if (htim == &htim3)
	{
		if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
		{
			if (__HAL_TIM_GET_FLAG(htim, TIM_FLAG_CC1))
			{
				__HAL_TIM_CLEAR_FLAG(htim, TIM_IT_CC1);
				htim->Instance->SR &= ~TIM_SR_CC1IF;
				htim->Instance->CCR1 = 0;
				htim->Instance->CCMR1 = 0; //clears mode output bits --- freezes output
				htim->Instance->CCMR1 |= TIM_OCMODE_FORCED_INACTIVE; //forces output to 0??
			}
 
			else if (__HAL_TIM_GET_FLAG(htim, TIM_FLAG_UPDATE))
			{
				__HAL_TIM_CLEAR_FLAG(htim, TIM_IT_UPDATE); //clear UIF Flag
				htim->Instance->SR &= ~TIM_SR_UIF;
				htim->Instance->CCR1 = htim->Instance->CNT + 10;
				htim->Instance->CCMR1 = 0; //clears mode output bits --- freezes output
				htim->Instance->CCMR1 |= TIM_OCMODE_FORCED_ACTIVE;  //set output to 1 immediately
			}
 
		}
	}
}

0693W00000Y7AAaQAN.png

1 ACCEPTED SOLUTION

Accepted Solutions

> htim3.Instance->CCR1 = htim3.Instance->CNT + 1000;

This does not result in interrupts exactly 1000 clocks apart. CNT keeps counting after the compare happens, so the next CCR1 includes interrupt latency and execution time, so the second CCR1 will be something like 2050, the third 3100, etc.

And when CCRx>ARR, compare thus interrupt happens at Update.

JW​

PS. note that if you have other, higher or same priority, interrupts​ in your system, interrupt latency may increase dramatically

View solution in original post

20 REPLIES 20
S.Ma
Principal

Use minimal compiler optimisation code to be able to put breakpoints in most places, even use asm nop as breakpoint spots.

Try using output compare first to make a pwm, and using jumper wire, feed it to the same timer, other channel, programmed as input capture. This may help with minimal lab equipment.needed (although oscilloscope -not logic analysers- is prime debug hw tool for embedded devices)

gbm
Lead III

To make the thing simple, get rid of HAL completely and operate on timer registers only. This way you get full control over the timer register settings and of course also reduce the number of lines in your code by at least 80%.

How do you know that the flags are not cleared? They are getting set every 100 us, so you can't notice them being cleared using the debugger. The timer is still running when you single-step.

Do not use logic operations on TIM SR for clearing the flags - this is very common error leading to events being lost.

Wrong:

htim->Instance->SR &= ~TIM_SR_UIF;

Good:

htim->Instance->SR = ~TIM_SR_UIF;

KnarfB
Principal III

How do you start the timer?

You may slow down everything by a factor of say 1000 by using a different prescaler. Then you might be able to better watch events, maybe with help of printf tracing, a cheap logic analyzer at the outputs, LED blinking, and so on.

Using of HAL or register level is a matter of the learning path you prefer. Mixing both to achieve relatively simple things might add unneccessary complexity.

hth

KnarfB

I have breakpoints right within the ISR Callback on the line that should clear the flag. I step over this and the state of the flag doesn't clear. I've set the break on both the HAL and the register line and neither do the job.

To your point about the Wrong: Good:

Yes the &= and |= is a habit of mine....I think I am safe doing your way bcz the register in question only supports writes of 0.  In MSP world I never have issues with this , would I really see issues in the STM by doing this?

gbm
Lead III

You step over this, and the flag clears the it sets again in 100 us. You cant's see it cleared.

Using &= means "clear the selected flag and all other flags set during the execution of this statement". It's not what you want - you may write more than one 0 to the SR this way.

got it on the first point...thanks

Not sure how &= clears other flags....

TIM_SR_UIF = 0x0000 0001 ---> ~TIM_SR_UIF = 0xFFFF FFF7 ----> "anding" this with anything ONLY affects bit 0, which according to the document is how you clear bit 0, by writing a 0 to the flag....all other bits will stay as they are AND be unaffected. This is what you want so when you leave the ISR callback you go right back into it if another flag has been sent...Withing the callback you must have conditional if statements to catch each and every flag....am I missing something here???

For what it's worth:

I am used to doing if else statements within the ISR with each of my if/else statements checking ONLY ONE flag. That way you only have to process a short piece of code within the ISR assuming only one flag has been set (which in my experience is most always the case). In the event two flags are set it is no big deal bcz you service the first, pop out and immediately pop back in to service the second. If I were to clear all flags I would have to attend to all flags in the ISR I would have to have an IF for each flag and go thru the process independent of whether the flag was set or not which would spend processing time and keep the processor awake (ie in high power mode).....I am coming at it from a background of having products that must sleep at 3uA - 5uA (max) of power and last battery life of 8 years.

Please check out the following instruction, from stm32l4xx_hal_tim.h:

#define __HAL_TIM_CLEAR_IT(__HANDLE__, __INTERRUPT__)   ((__HANDLE__)->Instance->SR = ~(__INTERRUPT__))

S.Ma
Principal

Sanity check : when breakpoint, the hw maybe still running. Watching timer registers will steal the code's job. Relaunch the code after breakpoint and watching timer values. Is timer frozen when breakpoint?

SWenn.1
Senior III

I have a SystemClk of 80MHz, an AHB1 clock of 20MHz, a TIM3 divisor of 4 and the counter period set to 500....Effectively giving me an UIF every 10kHz. I was able to get code working by deleting the HAL callback within TIM3_IRQHandler and write my own....I've written my own function within the IRQ and it is working....below is a snapshot of code (with 2 breakpoints and registers) I do have some questions....

  1. Placing one breakpoint with the UIF section I see counter and CCR1 register changing as expected....Placing that breakpoint RIGHT after flag clearing and yet I don't see the register update to a 0??
  2. Placing one breakpoint with the CCI1F section I see counter register changing as expected....Placing that breakpoint RIGHT after flag clearing and yet I don't see the register update to a 0??
  3. Placing both breakpoints it seems I only stop on the one that I previously set and it NEVER stops on the second one....So if I placed it in UIF then enable the CCI1F it never goes there and always goes with UIF, same if I placed it in CCI1F then enable UIF it never goes there and always goes into CCI1F section???
  4. Follow up....Can someone point me to information pertaining how many clock cycles it takes to enter and exit ISRs?

I understand breakpoints can be tricky especially within ISR but if the IDE isn't yielding accurate results how can you troubleshoot using it??? I have the Compiler optimizations set to None and Debugger set to Maximum.

0693W00000Y7DLWQA3.png