cancel
Showing results for 
Search instead for 
Did you mean: 

Timer ISR return procedure

orionhere
Associate II
Posted on February 11, 2013 at 19:01

Hello to everyone.

I have just started to study STM32 microcontrolles (using STM32F4 Discovery kit) and can't understand one thing.

Test task is simple �? set up Timer 7 to generate 100000 update events per second, and in the interrupt service routine toggle one pin of the port to check settings of the timer.

So, here is my code (IAR Embedded Wokbench for ARM):

#include <stdio.h>

#include <stdint.h>

#include ''stm32f4xx.h''

void initiazliation(void);

void initiazliation(void)

{

  RCC->AHB1ENR|=0x09; // Enable clock on PORTA and PORTD

  GPIOA->MODER|=0x00000004;       // PA1 is output

  GPIOA->OTYPER|=0x00000000;       // Push pull output

  GPIOA->OSPEEDR|=0x00000004;     // 25 MHz output

  GPIOA->PUPDR|=0x00000000;          // No pull-up, no pull-down

  GPIOA->IDR|=0x0000;                        // Input register

  GPIOA->ODR|=0x0000;                      // Output register      

  

  DBGMCU->APB1FZ|=DBGMCU_APB1_FZ_DBG_TIM7_STOP;

  RCC->APB1ENR|=0x00000020;       // Enale clock on Timer/Counter 7

  

  TIM7->ARR=79; // Fsclk=8MHz, Fuev=8000000/(79+1)=100KHz

  TIM7->PSC=0;                                 // No clock prescaling

  TIM7->DIER=0x0001;                     // Enable update interrupt

  TIM7->CR1=0x0001;                       // Enable Timer

  NVIC_EnableIRQ(TIM7_IRQn);     // NVIC interrupt enable

}

void TIM7_IRQHandler(void)

{

//  if (TIM7->SR&0x0001)

//  {

    GPIOA->ODR^=0x0002;

    TIM7->SR&=0xFFFE;

//  }

}

void main(void)

{

  initiazliation();

  __enable_irq();

  while(1);

}

As you can see, I use Timer 7 to generate Update Events and PA1 pin to toggle. MCU uses HSE as a main system clock (8MHz crystal), AHB, APB1 and APB 2 prescalers are set to 1, prefetch for instructions is enabled and flash latency is zero.

I thought, that this code will output squarewave pulses at PA1, and its frequency and duty cycle have to be 50 KHz and 0.5 (50 %) correspondingly. But in reality I have squrewave at 100 KHz, and duty cycle 0.41 (41 %). 

When I uncomment commented lines, everything is OK �? frequency is 50 kHz and duty cycle is 50 %.

I can't understand, why it happens? Does such behaviour means, that ISR is entered again, after it was executed once? But why? I clear UIF flag in the Status Register of the Timer... And NVIC registers say's, that there is no pending interrupts from this source. Disassembler doesn't show difference betwee two variants of the ISR �? both of them finish with BX LR instruction and there is no pending requests.

Please, help me to understand, what actually happens in this case.

P.S.

Sorry for my bad English.
8 REPLIES 8
Posted on February 11, 2013 at 19:48

If you program at a register level you need to read the documentation, and understand the part, a lot better than someone using the library.

void TIM7_IRQHandler(void)
{
if (TIM7->SR&0x0001)
{
TIM7->SR = ~0x0001; // Clear the IRQ
GPIOA->ODR^=0x0002;
}
}

Be aware that there is a prefetch/write buffer hazard (race condition) when clearing interrupts and the NVIC causing a tail chain re-entry. Be aware that the registers in the peripheral are not always like memory, and doing a RMW is not always appropriate or correct.
Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
carl2399
Associate II
Posted on February 12, 2013 at 09:30

I was looking, by chance, at some timer interrupts today on STM32F4. I was using TIM4.

I'm also a bit of a register basher!

Do have a look at TIMx->EGR. I needed to use it, and you may too!

orionhere
Associate II
Posted on February 12, 2013 at 11:34

orionhere
Associate II
Posted on February 12, 2013 at 11:42

Posted on February 12, 2013 at 12:00

>> Be aware that the registers in the peripheral are not always like memory, and doing a RMW is not always appropriate or correct.

> And, I'm sorry, but what does ''RMW'' mean?

Read-modify-write. As in TIMx_SR &= mask. Read the manual carefully: all register descriptions have marks below the bits' names; they are explained at the very beginning of the manual. The TIMx_SR register's bits are all marked as ''rc_w0'', which means ''readable, can be cleared by writing 0''. Therefore, you should write the mask directly to the register as Clive did (TIMx_SR = mask); it's the hardware which performs the clear. If you read-modify-write, you can clear bits you did not intend to.

Note, that there are SFR bits which don't have an appropriate description, usually for flags which are cleared by a more complex sequence of events (e.g. SPIx_SR.OVR).

(ST: it might be a good idea to mark these bits in a different way).

JW

PS. Please don't write your answers below the original post.

orionhere
Associate II
Posted on February 12, 2013 at 19:03

Thanks everyone for the help.

Clive1, you were right. When I've changed operator's order, code started to work correctly:

void TIM7_IRQHandler(void)

{

  TIM7->SR&=0xFFFE; // If flag is cleared here, everything works OK

  GPIOA->ODR^=0x0002;

//  TIM7->SR&=0xFFFE; // But if here, then code execution fails

}

If I place __ISB() instruction, then position of the line, which clears interrupt flag is not significant.

Is is so simple, that I feel a bit of shame... I know, that ARM Cortex core does not guarantee the order of instruction execution, but i didn't expected it in this case...

So, cause of my situation is, that in spite of I clear interrupt flag in my ISR, actually it is executed by the microcontroller's core after return from the routine, and this causes tail-chaining of the interrupt? Do I correctly understand source of this problem?
Posted on February 12, 2013 at 19:30

You don't need &=, you should just write this register

The processor completes in order, but is pipelined, and the writes occur through a write buffer arrangement so they may retire somewhat later than you expect. The exact speed will depend on the bus(es) over which the transaction occurs and whatever else is going on, prefetch, contention, etc.

The write buffer will be fenced by a read, ie the write will complete before the read starts. A prefetch read on the instruction bus may not count.

The prefetch, pipelining, and write buffer create a hazard. Your operation on TIM->SR causes a write across the AHB/APB buses and will take at least 4 cycles, where as your internal register moves (LR, PC) will take 1. By the time the processor decides whether to tail-chain the interrupt is still signalling as the clear request is still propagating, the interrupt will re-enter (this or another), but a read to SR will fence and indicate one is not pending.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
orionhere
Associate II
Posted on February 13, 2013 at 11:47

Ok, everything is clear now. Thank You for your answers.