Forum: Mikrocontroller und Digitale Elektronik STM32F4 Timer OVF Fehler


von Mark W. (mark_wer)


Lesenswert?

Hallo,

ich habe grad ein Problem, bei dem ich meinen Fehler nicht finde. 
Vielleicht kann mir ja einer von euch helfen. Ich messe mit Hilfe des 
Capture Interrupts eine Zeit zwischen zwei Flanken. Geht auch fast 
alles, bis auf ganz wenige Male, bei denen der Wert zwar richtig wär, 
aber um eine ganze OVF Periode abweicht. Also irgendwie muss der Wert 
OVF gesetzt sein, obwohl er das nicht dürfte und dann wird in der 
if-Abfrage berechnet und der Wert von OVF ist 1, obwohl er eigentlich 0 
sein sollte.

Hier mein Code inkl. Init:
1
void InterruptInit(void)
2
{
3
  GPIO_InitTypeDef GPIO_InitStructure;
4
  TIM_ICInitTypeDef TIM_ICInitStructure;
5
  NVIC_InitTypeDef NVIC_InitStructure;
6
  TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
7
8
  // Clock Enable
9
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
10
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
11
12
  // Clock enable
13
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
14
15
  // Config PD13 als Digital-Ausgang
16
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
17
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
18
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
19
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
20
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
21
  GPIO_Init(GPIOD, &GPIO_InitStructure);
22
23
  // Config des Pins PE11 als AF-Input
24
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
25
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
26
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
27
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
28
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
29
  GPIO_Init(GPIOE, &GPIO_InitStructure);
30
31
  // Connect PE11 with TIM1 for Input Capture
32
  GPIO_PinAFConfig(GPIOE, GPIO_PinSource11, GPIO_AF_TIM1);
33
34
  TIM_TimeBaseStructure.TIM_Period = 0xFFFF;
35
  TIM_TimeBaseStructure.TIM_Prescaler = 0;            
36
  TIM_TimeBaseStructure.TIM_ClockDivision = 0;
37
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
38
  TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
39
  TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
40
41
  NVIC_InitStructure.NVIC_IRQChannel = TIM1_CC_IRQn;
42
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
43
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
44
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
45
  NVIC_Init(&NVIC_InitStructure);
46
47
  NVIC_InitStructure.NVIC_IRQChannel = TIM1_UP_TIM10_IRQn;
48
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
49
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
50
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
51
  NVIC_Init(&NVIC_InitStructure);
52
53
  // Channel 2
54
  TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;
55
  TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Falling;
56
  TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
57
  TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
58
  TIM_ICInitStructure.TIM_ICFilter = 0x02;        //0x02 = 4 consecutive events are needed to validate a transition on the output
59
  TIM_ICInit(TIM1, &TIM_ICInitStructure);
60
61
  //Interrupt Enable
62
  TIM_ITConfig(TIM1, TIM_IT_CC2, ENABLE);
63
  TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE);
64
65
  // Timer enable
66
  TIM_Cmd(TIM1, ENABLE);
67
}
68
69
//-----------------
70
// TIM1 OVF Handler
71
//-----------------
72
void TIM1_UP_TIM10_IRQHandler(void)
73
{
74
  if (TIM_GetITStatus(TIM1, TIM_IT_Update) != RESET)
75
  {
76
    // Delete Interrupt Flags
77
    TIM_ClearITPendingBit(TIM1, TIM_IT_Update);
78
79
    OVF_cnt++;
80
  }
81
}
82
83
//-------------------------------
84
// TIM1 Capture Interrupt Handler
85
//-------------------------------
86
void TIM1_CC_IRQHandler(void)
87
{
88
  if (TIM_GetITStatus(TIM1, TIM_IT_CC2) != RESET)
89
  {
90
    __disable_irq();
91
92
    // Delete Interrupt Flags
93
    TIM_ClearITPendingBit(TIM1, TIM_IT_CC2);
94
95
    uint16_t new = TIM_GetCapture2(TIM1);
96
    uint8_t OVF = OVF_cnt;
97
98
    OVF_cnt = 0;
99
100
    if(UB_USB_CDC_GetStatus()==USB_CDC_CONNECTED)
101
    {
102
        uint16_t time = 0;
103
104
        if(OVF)
105
        {
106
          if(ICRnew > ICRold)
107
          {
108
            GPIO_ToggleBits(GPIOD, GPIO_Pin_14);
109
110
            time = (new - old);
111
          }
112
          else
113
          {
114
            time = ~(old - new);
115
            OVF--;       // Periodextra has to be decreased by one because one OVF is already included in the calculation
116
          }
117
        }
118
        else
119
        {
120
          time = (new - old);
121
        }  
122
    }  
123
    old = new;
124
    __enable_irq();
125
  }
126
}

Würde mich über Hilfe freuen und schon mal Danke im Vorraus
Mark

von Mark W. (mark_wer)


Lesenswert?

Ok, also das Problem müsste in den folgenden Zeilen liegen:
1
 if(OVF)
2
        {
3
          if(new > old)
4
          {
5
            GPIO_ToggleBits(GPIOD, GPIO_Pin_14);
6
7
            time = (new - old);
8
          }
9
          else
10
          {
11
            time = ~(old - new);
12
            OVF--;       // Periodextra has to be decreased by one because one OVF is already included in the calculation
13
          }
14
        }
15
        else
16
        {
17
          time = (new - old);
18
        }
Wenn ich es umschreibe zu:
1
 if(OVF)
2
        {
3
         //if(new > old)
4
         // {
5
            GPIO_ToggleBits(GPIOD, GPIO_Pin_14);
6
7
          // time = (new - old);
8
          //}
9
          //else
10
          //{
11
            time = ~(old - new);
12
            OVF--;       // Periodextra has to be decreased by one because one OVF is already included in the calculation
13
          //}
14
        }
15
        else
16
        {
17
          time = (new - old);
18
        }
funktioniert alles bei hohen Frequenzen, für Frequenzen, die aber über
einen OVF gehen brauche ich diese Zeilen. Aber das müsste ja heißen,
dass der Vergleich "new > old" fälschlicherweise als true bewertet wird?
Hat vielleicht jemand eine Idee, warum?

von Little B. (lil-b)


Lesenswert?

Ich sehe hier einen systematischen Fehler, nämlich dass "time" zu klein 
deklariert ist (nur uint16). Somit wird alles, was über eine Periode 
hinausgeht, oben abgeschnitten. Probiers mal mit uint32.

Desweiteren ist diese Berechnung falsch (jedenfalls nicht konsistent mit 
den anderen berechnungen):
1
time = ~(old - new);

Ich würde das so schreiben (time als uint32 vorausgesetzt):
1
time = ((uint32_t)new + (OFV<<16)) - old;

dann brauchst du auch diese unterscheidung nicht mehr:
1
if(new > old)

: Bearbeitet durch User
von Nop (Gast)


Lesenswert?

Poste mal als Anhang (!!) den GANZEN Quellcode. Da fehlt doch die 
Hälfte, beispielsweise die kompletten Variablendeklarationen mitsamt 
deren Datentypen.

von Little B. (lil-b)


Lesenswert?

Little B. schrieb:
> dann brauchst du auch diese unterscheidung nicht mehr:
>
1
>if(new > old)
2
>

tatsächlich brauchst du dann gar keine unterscheidung mehr.
Aus diesem code von dir:
1
        uint16_t time = 0;
2
3
        if(OVF)
4
        {
5
          if(ICRnew > ICRold)
6
          {
7
            GPIO_ToggleBits(GPIOD, GPIO_Pin_14);
8
9
            time = (new - old);
10
          }
11
          else
12
          {
13
            time = ~(old - new);
14
            OVF--;       // Periodextra has to be decreased by one because one OVF is already included in the calculation
15
          }
16
        }
17
        else
18
        {
19
          time = (new - old);
20
        }

kann dieser code werden:
1
uint16_t time = 0;
2
time = ((uint32_t)new + (OFV<<16)) - old;

von Mark W. (mark_wer)


Lesenswert?

Erstmal Dankeschön für eure Anmerkung, ist die bessere Lösung :) Aber 
der Fehler tritt trotzdem noch auf, allerdings bin ich schon einen 
kleinen Schritt weiter, der Fehler tritt nämlich nur auf, wenn new = 
0xFFFF ist. D.h. der OVF Interrupt wird schon in der Berechnung 
berücksichtigt, obwohl er erst danach auslösen sollte wegen 
Priority(zumindest war das eigentlich mein Gedanke!?). Irgendwo hab ich 
also immer noch einen Denkfehler..

Das ganze Projekt hab ich nicht angehängt, da ich dachte/denke der 
Fehler befindet sich in dem geposteten Teil, kann ich später aber ggf. 
noch hinzufügen. Die zwei Variablendefinitionen gehen oben aber noch ab:
1
volatile uint8_t OVF_cnt = 0;
2
volatile uint16_t old = 0;

: Bearbeitet durch User
von Mark W. (mark_wer)


Lesenswert?

Hat vielleicht jemand eine Idee? Ich komm leider gar nicht weiter. 
Nochmal kurz als Beispiel für den Fehler. Der Timer läuft mit 16MHz und 
wenn ich beim Funktionsgenerator z.B. 12kHz einstelle wird mir am Pc 
über USB meistens das richtige Ergebnis "1333" angezeigt. Teilweise 
kommt am PC aber der Wert "66869" an, also genau ein OVF, der da 
eigentlich nicht sein dürfte und das passiert nur, wenn der Wert 
new=65535 ist.. Würde mich sehr über Anregungen/Lösungen freuen :)

von FloMann (Gast)


Lesenswert?

Prüfe mal folgendes...
Capture interrupt kommt kurz vor überlauf, bevor du noch im interrupt
handler für capture an der stelle disable interrupt bist kommt der timer
Overfow int... hab jetzt nicht genau geschaut, hat dein timer ovr 
interrupt
höhere prio und kann denn capture interupt unterbrechen? So würde ovr 
cnt
hoch gezählt werden und es käme zu dem einen Überlauf Versatz.

Gruß florian

von Mark W. (mark_wer)


Lesenswert?

Hallo Florian,

So ähnlich ist auch mein Gedanke, also irgendwie muss der OVF Interrupt 
den Capture Interrupt unterbrechen, aber eigentlich hab ich dem 
Zeitmessen die höchste Priorität gegeben, also die Priority vom 
Interrupt Capture ist niedriger. Hab jetzt noch was ausprobiert, wenn 
ich folgenden Code einfüge hinter dem Zuweisen von OVF und new
1
if ((ICRnew == 0xFFFF) && (periodextra > 0))
2
{
3
  periodextra--;
4
}

bekommt der OVF bei new=0xFFFF den Wert 0x11111111. Vielleicht hilft der 
Fehler beim erkennen des eigentlichen Problems...

Gruß
mark

: Bearbeitet durch User
von m.n. (Gast)


Lesenswert?

Es ist mir etwas zu mühselig, Deinen Code Stück für Stück durchzugehen. 
Wie man den Überlauf bei Timern richtig erfassen kann, ist hier gezeigt: 
Beitrag "reziproker Frequenzzähler mit STM32F4Discovery"

von Peter D. (peda)


Lesenswert?

Das Problem ist immer das gleiche, man kann einen HW-Timer und eine 
Variable nicht gleichzeitig auslesen. Daher muß man feststellen, ob der 
Interrupt zum Hochzählen der Variable schon ausgeführt wurde oder noch 
nicht:

Beitrag "AVR Timer mit 32 Bit"

von Mark W. (mark_wer)


Lesenswert?

Vielen Dank für eure Hilfe, jetzt läuft alles gut! :)
Habe hier am Anfang zwar zwei Zeilen vertäuscht, normalerweise hatte ich 
schon die SubPriority bei Capture auf 0 und bei OVF bei 1, aber es lief 
trotzdem nicht, mit der Funktion NVIC_SetPriority() läuft es jetzt.
@peter: den OVF hatte ich schon berücksichtigt im CaptureInterrupt, also 
falls OVF Flag gesetzt ist, überprüfen ob vor oder nach CaptureInterrupt 
mit new < 0x8000. Aber dazu hätte ich noch eine Frage, warum genau 
0x8000/0x80 bei deinem Beispiel? Weil man davon ausgeht, dass der Code 
nicht so lange zum ausführen braucht, sodass man die Hälfte des Zählers 
wählt oder funktioniert das immer, auch wenn der Code theoretisch 40000 
Zählschritte zum ausführen bräuchte?

von Peter D. (peda)


Lesenswert?

Mark W. schrieb:
> oder funktioniert das immer

Nein, diese Methode ist auf den halben Zählbereich begrenzt, in der Zeit 
muß der Overflowinterrupt behandelt worden sein.
Falls das Auslesen in einem Captureinterrupt erfolgt, sollte dieser die 
gleiche Priorität haben, d.h. die beiden dürfen sich nicht gegenseitig 
unterbrechen können. Das atomar Kapseln entfällt dann.

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.