Timer Interrupt Problem

Gast #2458378
Lesenswert?

Hallo zusammen,

ich habe einen STM32F100RB µC. Ich möchte die Timer-Overflow-Interrupt 
Funktion testen. Zu diesem Zweck habe ich Code in der Coocox-IDE
kompiliert und getestet.

Wie erwartet wird auch ein Interrupt zur erwarteten Zeit ausgelöst, wie 
ich mit Hilfe eines getoggleten Pins erkennen konnte.

Allerdings tritt immer nach dem erwarteten Interrupt nach etwa 1µS ein 
weiterer Interrupt auf. Warum dieser zweite Interrupt ausgelöst wird 
habe ich nicht verstanden. Wenn ich in der Interrupt Routine das UIF 
flag zweimal nacheinander lösche, tritt der zweite Interrupt nicht mehr 
auf.

Vermutlich handelt es sich um etwas wirklich Triviales, aber dies sind 
meine ersten Versuche mit diesem µC. Vielleicht kann mich jemand mit 
einem Hinweis in die richtige Richtung stupsen ;-)

Dies ist der Code:
///////////////////////////////////////////////////////////////////////
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_usart.h"
#include "stm32f10x_tim.h"
#include "misc.h"

GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;

int main(void)
{
  int i;
    SetSysClockTo24(); // Setzt den System-CLK auf 24 MHz

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP; // 
GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_8 ;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
    //RCC_APB1PeriphResetCmd(RCC_APB1Periph_TIM2,DISABLE);

    /* Time base configuration */
       TIM_TimeBaseStructure.TIM_Period = 0x1000;
       TIM_TimeBaseStructure.TIM_Prescaler = 2;
       TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
       TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
       TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
       TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

       TIM2->DIER |= TIM_DIER_UIE; // (1<<0); // UPDATE INTERRUPT ENABLE

       NVIC_InitTypeDef NVIC_InitStructure; //create NVIC structure
       NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
       NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
       NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
       NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
       NVIC_Init(&NVIC_InitStructure);

       // Enable timer counting
         TIM_Cmd(TIM2,ENABLE);

    while(1){

            }
}

void TIM2_IRQHandler()
{

    GPIOA->BSRR = GPIO_Pin_8;
    GPIOA->BRR = GPIO_Pin_8;
    TIM2->SR &= ~(1<<0); // clear UIF flag
}
///////////////////////////////////////////////////////////////////////

Danke und Grüße

stm32_beginner
#2458386
Lesenswert?

Könnte sein, dass die Zeit zwischen dem Rücksetzen des Interrupt-Flags 
in der ISR und dem Return der ISR so kurz ist, dass der Interrupt mit 
dem Return noch als aktiv gesehen und die ISR postwendend nochmal 
aufgerufen wird.

Probier also mal
1
void TIM2_IRQHandler()
2
{
3
    TIM2->SR = ~(1<<0); // clear UIF flag (=> rc_w0)
4
    GPIOA->BSRR = GPIO_Pin_8;
5
    GPIOA->BRR = GPIO_Pin_8;
6
}

PS: Das SR ist rc_w0, man kann das Bit löschen ohne vorher zu lesen.
Gast #2458424
Lesenswert?

@A.K

Danke für den Hinweis! Ich habe es mal getestet. Nun wird tatsächlich 
nur noch ein Interrupt ausgelöst.

Ich verstehe dennoch nicht, warum solch ein Effekt auftreten kann. Wenn 
ich mir das streng sequentiell vorstelle, wird doch erst mal das 
UIF-Flag gelöscht und danach erst die Interrupt-Funktion verlassen. 
Weshalb wird das Löschen des Flags erst verspätet wirksam?

Zweite Frage:

Der Puls, welchen ich durch das Setzen und Zurücksetzen des PORT-PINS 
erzeuge dauert 80nS. Dies entspricht bei einer Frequenz von 24MHz 2 
Takten. Warum dauert das nicht nur einen Takt? Ist es prinzipiell 
möglich den Puls einen Takt lang zu machen?

Gruß
stm32_beginner
#2458435
Lesenswert?

stm32_beginner schrieb:

> Ich verstehe dennoch nicht, warum solch ein Effekt auftreten kann. Wenn
> ich mir das streng sequentiell vorstelle, wird doch erst mal das
> UIF-Flag gelöscht und danach erst die Interrupt-Funktion verlassen.
> Weshalb wird das Löschen des Flags erst verspätet wirksam?

Der exakte Ablauf überlappt sich etwas. Wenn der Return-Befehl direkt 
auf den Store-Befehl folgt, dann findet das Rücksetzen des Flags 
effektiv zur gleichen Zeit wie der Return-Befehl statt. Selbst wenn da 
1-2 Takte dazwischen liegen, dann können chipinterne Register evtl. 
immer noch für eine entsprechende Verzögerung des Interrupt-Signals 
zwischen Timersignal und Auswertung durch den Prozessor sorgen.

> Takten. Warum dauert das nicht nur einen Takt?

Weil da ein Store-Befehl dazwischen liegt und der mindestens 2 Takte 
benötigt.
Gast #2458526
Lesenswert?

Um sicher zu gehen, dass er das Flag auch garantiert gelöscht hat, habe 
ich nun eine while-Schleife eingefügt, damit das Ganze definierter 
abläuft:

while( (TIM2->SR & (1<<0)) > 0 ) TIM2->SR &= ~(1<<0); // clear UIF flag
#2458645
Lesenswert?

Kannst du natürlich machen, aber wenn einige Takte zwischen dem Löschen 
vom Flag und dem Ende des Handlers sind, dann reicht das auch. Als 
Daumenregel sollte man also das Interrupt-Flag nicht am Schluss löschen, 
sondern eher vorne.

Ausserdem würde ich empfehlen, mit
   SR = ~(1<<n);
exakt nur das gewünschte Bit zurück zu setzen. Andernfalls riskierst du, 
dass bei mehreren möglichen Timer-Events welche verloren gehen können. 
Das passiert nämlich, wenn während der Ausführung von
   SR &= ~(1<<n):
     (a) register = SR
     (b) clear bit in register
     (c) SR = register
ein Interrupt-Flag in (a) noch als 0 geladen,
während (b) durch das Event im SR gesetzt,
und durch (c) gleich wieder zurückgesetzt wird.
Gast #2458753
Lesenswert?

Danke für den Tip!

SR = ~(1<<n);
------------------

Blöde Frage: Ich setze damit zwar das eine Bit zurück, aber die anderen 
Bits werden dann auf '1' gesetzt. Was wäre denn wenn die auf 0 liegen? 
Damit würde ich doch ungewollt Bits setzen, die ich gar nicht 
manipulieren will. Oder habe ich da einen Denkfehler?
Gast #2458806
Lesenswert?

Danke!

Deine Tips haben mir sehr weiter geholfen. Das Referenzdatenblatt habe 
ich schon begonnen mir zu Gemüte zu führen. Die meisten Unklarheiten 
basieren ja häufig auf Details, die man einfach übersieht. So war es 
auch in diesem Fall.

Insgesamt bin ich sehr angetan vom STM32. In Kombination mit der freien 
CooCox IDE ist das schon eine feine Sache. Zumal der µC nicht besonders 
viel kostet und um Welten leistungsfähiger ist als zb. ein ATMEGA (bei 
weniger Preis).

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren