Forum: Mikrocontroller und Digitale Elektronik STM32 Capture Interrupt Initialisierung


von Jens (Gast)


Lesenswert?

Moin Leute,

ich probier hier schon seit ein paar Stunden vergeblich herum, um einen 
InputCapture mit dem STM32 zu erhalten. Vielleicht könnt ihr mal über 
meinen Init-Code schauen, ob ich irgendwas wichtiges übersehen habe.

Im Prinzip liegt an PE13 ein Signal an, welches ab und an für 1µs auf 
High geht und darauf möchte ich gern einen Timerinterrupt haben.
1
RCC->APB2ENR |= (1 << 6);  // enable peripheral clock for GPIOE
2
3
/* INT input PE13 -> mit timer1 verwenden */  
4
GPIOE->CRH &= ~0x00F00000;    // reset port-pin 
5
GPIOE->CRH |=  0x00800000;  // set input mode with pullup or pulldown
6
7
8
RCC->APB2ENR |= RCC_APB2Periph_TIM1;
9
10
11
/* Enable the TIM3 global Interrupt */
12
NVIC_InitStructure.NVIC_IRQChannel = TIM1_CC_IRQChannel;
13
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
14
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
15
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
16
NVIC_Init(&NVIC_InitStructure);
17
18
TIM_TimeBaseStructure.TIM_Period       = 0xFFFF;
19
TIM_TimeBaseStructure.TIM_Prescaler     = 1;
20
TIM_TimeBaseStructure.TIM_ClockDivision    = 0;
21
TIM_TimeBaseStructure.TIM_CounterMode     = TIM_CounterMode_Up;
22
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
23
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
24
25
26
TIM_ICInitStructure.TIM_Channel = TIM_Channel_3;
27
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
28
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_IndirectTI; //PE13 
29
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;        // Div:1, every edge 
30
TIM_ICInitStructure.TIM_ICFilter = 0x0;
31
TIM_ICInit(TIM1, &TIM_ICInitStructure);
32
33
TIM_ClearITPendingBit(TIM1, TIM_IT_CC3);
34
TIM_ITConfig(TIM1, TIM_IT_CC3, ENABLE);
35
36
/* TIM enable counter */
37
TIM_Cmd(TIM1, ENABLE);
38
39
40
/* TIMER interrupt routine: wird nie aufgerufen */
41
42
void TIM1_CC_IRQHandler (void)
43
{
44
  if (TIM1->SR & TIM_IT_CC3)
45
  { /* capture timer */
46
      TIM1->SR = (unsigned short)~TIM_FLAG_CC3;
47
  }
48
}

Mfg
Jens

von Lutz (Gast)


Lesenswert?

Du solltest dich für -einen_ Timer entscheiden...

von Lutz (Gast)


Lesenswert?

Ähem, vergiß das, hab nur was von TIM1 und TIM 3 gesehen.

Aber laut Manual soll man beim Input Capture die GPIO configuration 
input floating nehmen und nicht input pull-up/down. Du hast durch den 
ODR-Resetwert 0 den Pulldown aktiviert ...
Also must du im CRH 0x00400000 schreiben.

von Jens (Gast)


Lesenswert?

Lutz schrieb:
> Du solltest dich für -einen_ Timer entscheiden...

wie meinst du das genau? Ich möchte gern vom Timer1 den dritten Channel 
verwenden, weil dieser bin dem PE13 Pin verheiratet ist bzw. werden 
kann.

von Jens (Gast)


Lesenswert?

1
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_IndirectTI; //PE13

hab ich das hier richtig interpretiert? Das PE13 über die alternative 
Selection (TIM_ICSelection_IndirectTI) ausgewählt wird. Da bin ich mir 
nämlich nicht 100%ig sicher, weil das im User-Manual etwas komisch 
dargestellt ist mit drei verschiedenen Remapping Optionen.

Datenblatt Seite 32:
http://www.st.com/internet/com/TECHNICAL_RESOURCES/TECHNICAL_LITERATURE/DATASHEET/CD00191185.pdf

und im User-Manual Seite 174 (TIM1 alternate function remapping)
http://www.st.com/internet/com/TECHNICAL_RESOURCES/TECHNICAL_LITERATURE/REFERENCE_MANUAL/CD00171190.pdf

Als Gehäuseform verwende ich das LQFP144 Package.

von Lutz (Gast)


Lesenswert?

Diesen Punkt blicke ich in der Tat auch nicht so wirklich. Echt 
unglücklich geschrieben. Das CHM der Driver-Lib hilft mir da auch nicht 
weiter.
Ganz sicher gehst du aber, wenn du ohne die Driver-Lib im AFIO_MAPR (S. 
179) die bits 6 und 7 per Hand auf 1 setzt. Weil das gemäß S. 174 Table 
46 eindeutig dazu führt, daß TIM1_CH3 von PE10 auf PE13 gemapt wird.

Und noch dran denken, den pull-down abzuschalten (Table 20 auf S. 156).

von Jens (Gast)


Lesenswert?

Danke für die Tipps; werd ich morgn gleich ausprobieren und entsprechend 
reporten.

von Jens (Gast)


Lesenswert?

Jens schrieb:
> Danke für die Tipps; werd ich morgn gleich ausprobieren und entsprechend
> reporten.

jetzt läuft es. Man muss allerdings noch die RCC Clock für das AFIO 
enablen.

von Jens (Gast)


Lesenswert?

gehe ich eigentlich in der Annahme richtig, dass im TIM3->CCR3 die 
Anzahl an aufgetretenen Events (rising-edges in meinem Fall) gezählt 
wird bis die Timerperiode 0xFFFFF wieder von vorne anfängt?

von Jens (Gast)


Lesenswert?

hatte einen kleinen Schreibfehler drinnen -> jetzt erhalte ich ein paar 
Werte im CCR3 Register. Wenn ich allerdings die Frequenz des ankommenden 
Signals berechnen möchte, dann hab ich irgendwo noch einen Fehler 
drinnen.
1
unsigend short prescaler = (36000000 - 1000) - 1; 
2
3
TIM_TimeBaseStructure.TIM_Period       = 0xFFFF;
4
TIM_TimeBaseStructure.TIM_Prescaler     = 0;
5
TIM_TimeBaseStructure.TIM_ClockDivision    = 0;
6
TIM_TimeBaseStructure.TIM_CounterMode     = TIM_CounterMode_Up;
7
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
8
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
9
10
TIM_PrescalerConfig(TIM1,prescaler, TIM_PSCReloadMode_Immediate);

Das Eingangssignal hat z.B. 24fps. Hier erhalte ich dann einen Wert von 
capture = 0x4B als Differenz von zwei nacheinanderfolgenden 
CCR3-Interrupts.
1
volatile unsigned char counter = 0x00; 
2
volatile unsigned short capture = 0x00;
3
volatile unsigned short frequency = 0x00; 
4
5
void TIM1_CC_IRQHandler(void)
6
{
7
  if (TIM_GetITStatus(TIM1, TIM_IT_CC3) !=RESET)
8
  {
9
10
      if(!counter)
11
      {  /* Get the Input Capture value */
12
         IC1Value = TIM1->CCR3;
13
         counter++; 
14
      }
15
      else if(counter)
16
      {
17
         counter = 0x00;
18
         IC2Value = TIM1->CCR3;
19
20
         if(IC2Value > IC1Value) 
21
            capture = IC2Value - IC1Value;
22
         else
23
            capture = (0xFFFF - IC1Value) + IC2Value;
24
25
         // -> hier ist capture = 0x4B
26
         
27
         //berechnete frequenz vom signal?
28
         frequency = 36000 / (capture);
29
      }
30
   
31
  
32
     TIM_ClearITPendingBit(TIM1, TIM_IT_CC3);
33
  }   
34
}

von Lutz (Gast)


Lesenswert?

Na ja, in der Ecke kenne ich mich nun gar nichts aus. Meinst du frames 
per second?

Aber copy&paste hast du mit dem Quellcode schon mal nicht gemacht, denn
> unsigend short prescaler = (36000000 - 1000) - 1;
enthält neben eine Schreibfehler auch einen Wert, der bei 16 bit diverse 
Male overflowed (schönes Wort, finde ich).

Bei "else if" kannst du dir den "if"-Test sparen, da du ja in der 
vorherigen schon auf das Gegenteil der beiden Möglichkeiten testest. 
Also reicht einfach nur "else {...}".

Auch ist es mit den anderen Datentypen nicht so ersichtlich. Wenn man 
uint16_t usw. benutzt, weiß man immer, was man hat. Und kann auch auf 
einer anderen Architektur nicht so schnell auf die Schnauze fallen. 
Zumal es auch verwirrt, wenn du bei einem 16 bit-Wert eine Zuweisung mit 
einem 8 bit-Wert machst.

von Hayati A. (haya)


Lesenswert?

Ich versteh beide Beispiele nicht: Nirgends taucht die Funktion 
'TIM1_CC_IRQHandler' auf. Muss diese nicht irgendwo eingetragen werden 
damit sie mit jedem Interrupt aufgerufen wird? Erklärt das den folgenden 
Kommentar?
/* TIMER interrupt routine: wird nie aufgerufen */

Ich bin neu in der Mikrocontroller-Ecke und hab mir ein STM32L-Discovery 
Board zum 'Spielen' zugelegt. Hiermit möchte ich ein Programm erstellen, 
welches ausschließlich in einer Timer-Interrupt-Routine ein wenig 
arbeitet und in der main() Hauptschleife sich nur schlafen-legt um 
Energie zu sparen. Hat jemand ein einfaches Beispiel?

von Noname (Gast)


Lesenswert?

>Muss diese nicht irgendwo eingetragen werden
>damit sie mit jedem Interrupt aufgerufen wird? Erklärt das den folgenden
>Kommentar?
>/* TIMER interrupt routine: wird nie aufgerufen */

Ja. Sie muss "irgendwo" eintragen werden. In der Tabelle der 
Interrupt-Vektoren.
Nein. Der Kommentar bezieht sich nicht auf die Frage ob eine Funktion in 
die Interrupt-Vektor-Tabelle eingetragen ist oder nicht.
Deine Frage beruht auf einer Fehldeutung bzw. nicht genügend engen 
Interpretation des Wortes "aufrufen".
Hier (und allgemein beim Programmieren) kann man das auf folgendes 
beziehen:

Die Nennung eines Funktionsnamens mit einer (evtl. leeren 
Parameterliste) in der Definition einer anderen Funktion (ich beziehe 
das jetzt mal alles auf C) bewirkt deren "ausführung".

D.h. Du siehst irgendwann im Code eine Zeile à la
1
FunktionRechneWas ();
Das ist ein Aufruf der Funktion "FunktionRechneWas". Der Programmierer 
will an einer bestimmten Stelle im Ablauf, nach einer vorherigen 
Anweisung und vor einer nachfolgenden Anweisung, das genau das passiert. 
Daher "ruft er sie auf", d.h. genauer bewirkt deren Ausführung genau an 
dieser Stelle.

In diesem Sinne wird eine Interrupt-Funktion wie niemals (OK. Abgesehen 
von ziemlich trickreichen Fällen die uns hier nicht interessieren) in 
einer Funktionsdefinition erscheinen.
Sie wird also niemals "aufgerufen".
Das heisst nicht, das sie niemals ausgeführt wird. Sie wird nur nicht 
"aufgerufen".

Sie aber dann ausgeführt, wenn etwa ein Timer überläuft oder an einem 
Port-Pin der Pegel verändert wird.

Um es noch einmal in Kürze zu sagen:
"Aufrufen" heisst vom Programmautor aus gesehen: Im Programmtext an 
einer bestimmten Stelle einer Funktionsdefinition nennen. Von der CPU 
aus gesehen: Im Programmtext die gegenwärte Adresse auf den Stack 
schieben, zu der genannten Adresse der Funktion springen, die Befehle 
dort ausführen, die letze Adresse wieder vom Stack holen und wieder mit 
dem Befehl nach dem Aufruf der Funktion weitermachen. (Es gehört noch 
ein wenig mehr dazu, aber das lasse ich hier mal weg).

von Hayati A. (haya)


Lesenswert?

Genau dieses Eintragen in die Tabelle der Interrupt-Vektoren fehlt mir. 
Ich finde das Nirgends. Wo und Wie passiert das? Gibt's genau hierzu ein 
Beispiel?


Noname schrieb:
>>Muss diese nicht irgendwo eingetragen werden
>>damit sie mit jedem Interrupt aufgerufen wird? ..
> Ja. Sie muss "irgendwo" eintragen werden. In der Tabelle der
> Interrupt-Vektoren.

von Noname (Gast)


Lesenswert?

>Gibt's genau hierzu ein Beispiel?

Schau Dir die Beispielcodes aus der STM Standard Peripheral Library an.

von Hayati A. (haya)


Lesenswert?

Hmmm. Ok. Hab ein Beispiel abgwandelt und es funktioniert  :-)

Ich hätte allerdings erwartet, dass irgendwo explizit der 
Funktionszeiger in die Tabelle eingetragen wird. Bin erstaunt, dass der 
Compiler/Linker keine Fehlermeldung schmeisst weil die ganzen 
Funktionen, die in der Tabelle (in startup_stm32l1xx_md.s) eingetragen 
sind, nicht existieren. Das wird offensichtlich mit
  .weak SysTick_Handler
  .thumb_set SysTick_Handler,Default_Handler
umgangen.

Hat jemand parat wie man das dynamisch - durch Eintragen des 
Funktionszeigers - macht? Dafür hab ich kein Beispiel gefunden. In 
irgendeiner ARM Doku stand recht kompliziertes Zeug drin, dass man 
verschiedene Fälle (Code im Flash oder sonstwo) unterscheiden müsse ...
Das wär für einen Zustandsautomaten ganz hilfreich um ohne grosse 
"switch case" bzw. "if / else if" -Wüsten im Handler auszukommen.

> Schau Dir die Beispielcodes aus der STM Standard Peripheral Library an.

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.