Forum: Mikrocontroller und Digitale Elektronik Timer Interrupt Problem


von stm32_beginner (Gast)


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

von (prx) A. K. (prx)


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.

von stm32_beginner (Gast)


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

von (prx) A. K. (prx)


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.

von stm32_beginner (Gast)


Lesenswert?

Super!

Danke für die Antwort. Jetzt bin ich schon wieder etwas schlauer. Ich 
werde mir bezüglich der Verzögerungszeiten nochmal das Datenblatt 
vornehmen.

Grüße!

von stm32_beginner (Gast)


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

von (prx) A. K. (prx)


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.

von stm32_beginner (Gast)


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?

von (prx) A. K. (prx)


Lesenswert?

stm32_beginner schrieb:

> 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?

Die Bits im SR sind in der Referenz als rc_w0 gekennzeichnet. Und was 
diese Bezeichnung bedeutet ist an anderer Stelle erklärt: Eine 1 zu 
schreiben hat keine Auswirkung.

von stm32_beginner (Gast)


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).

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.