Forum: Mikrocontroller und Digitale Elektronik STM32F4 TIM2 PWM-Capture


von Che. (Gast)


Lesenswert?

Hi an alle,

meine Problemstellung sieht folgendermaßen aus:

Ich möchte den GPIO-Pin PA5 mit einem externen PWM-Signal belegen.

Bei dem Signal handelt es sich um eine übersetzte Abstandsmessung.
Der Sensor sendet also eine Falling-Edge aus, sobald die Messung 
beginnt, und sendet eine Rising-Edge, sobald er das Signal, was er 
ausgesendet hatte, wieder empfängt.

Soweit ich es verstanden habe, hat dieses Signal keine feste Frequenz.

Auf dem STM32 läuft FreeRTOS als Manager.

Der Plan ist, den Timer zu starten, sobald der Interrupt durch die Edge 
erfolgt, und den gemessenen Zeit-Wert in eine Queue zu schreiben, die 
dann später weiter verarbeitet wird.

Es folgt mein Code der Konfiguration. Ich vermute den Fehler beim 
TIM_ITConfig.
1
void pwmInit(void)    
2
{
3
4
#define PWM_TIMER        TIM2
5
#define PWM_TIMER_RCC        RCC_APB1Periph_TIM2
6
#define PWM_TIMER_CH_Init      TIM_OC1Init
7
#define PWM_TIMER_CH_PreloadConfig    TIM_OC1PreloadConfig  
8
#define PWM_TIMER_CH_SetCompare      TIM_SetCompare1  
9
#define PWM_GPIO_RCC        RCC_AHB1Periph_GPIOA
10
#define PWM_GPIO_PORT        GPIOA
11
#define PWM_GPIO_PIN        GPIO_Pin_5
12
#define PWM_GPIO_SOURCE        GPIO_PinSource5
13
#define PWM_GPIO_AF        GPIO_AF_TIM2
14
15
#define PWM_TIM_PRESCALER      (84 - 1) // Wie wird der berechnet??
16
17
#define PWM_MIN_PPM_USEC      1150
18
#define PWM_MAX_PPM_USEC      1900
19
  // Deklarieren von Init Structures
20
  TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;  // Time Base Init structure definition
21
  TIM_ICInitTypeDef TIM_ICInitStructure;      // Input Capture Init structure definition
22
  GPIO_InitTypeDef GPIO_InitStructure;
23
  NVIC_InitTypeDef NVIC_InitStructure;
24
25
  /*Enable clock for GPIOB*/
26
  RCC_AHB1PeriphClockCmd(PWM_GPIO_RCC, ENABLE);  // GPIOClock einschalten (1)
27
  RCC_APB1PeriphClockCmd(PWM_TIMER_RCC, ENABLE);  // TimerClock einschalten
28
29
  // Configure the GPIO to be the timer (TIM2) input [PA5-Pin]
30
  GPIO_StructInit(&GPIO_InitStructure);
31
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
32
  GPIO_InitStructure.GPIO_Pin = PWM_GPIO_PIN;
33
  GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
34
  GPIO_InitStructure.GPIO_Speed = GPIO_Medium_Speed;
35
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;
36
  GPIO_Init(PWM_GPIO_PORT, &GPIO_InitStructure);
37
38
  GPIO_PinAFConfig(PWM_GPIO_PORT, PWM_GPIO_SOURCE, PWM_GPIO_AF);
39
40
  // Time base configuration. 1us tick.
41
  TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
42
  TIM_TimeBaseStructure.TIM_Prescaler = PWM_TIM_PRESCALER*100; //1 µS = PWM_TIM_PRESCALER mal 10
43
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
44
  TIM_TimeBaseInit(PWM_TIMER, &TIM_TimeBaseStructure);
45
46
  // Setup input capture
47
  TIM_ICStructInit(&TIM_ICInitStructure);
48
  TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_BothEdge;
49
  TIM_ICInit(PWM_TIMER, &TIM_ICInitStructure);
50
51
  NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
52
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
53
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
54
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
55
  NVIC_Init(&NVIC_InitStructure);
56
57
  captureQueue = xQueueCreate(64, sizeof(uint16_t));
58
59
  // Enables or disables the specified TIM interrupts
60
  //TIM_ITConfig(PWM_TIMER, TIM_IT_Update | TIM_IT_CC1, ENABLE); //FEHLER!!!
61
  TIM_ITConfig(PWM_TIMER, TIM_IT_Update | TIM_IT_CC1, ENABLE);
62
  // Enables or disables the specified TIM peripheral
63
  TIM_Cmd(PWM_TIMER, ENABLE);
64
}

Vielen Dank für jede Hilfe schonmal im Vorraus!

Liebe Grüße,
Che.

von Pieter (Gast)


Lesenswert?

...und was für ein Problem hast Du????

von Che. (Gast)


Lesenswert?

Pieter schrieb:
> ...und was für ein Problem hast Du????

Das hätte ich vielleicht erwähnen sollen :)

Wenn ich für TIM_IT (Arg2 von TIM_IT_Config) CCM1 bis 4 einsetze, 
bekomme ich zwei verschiedene Assertion Failures von FreeRTOS zurück.

Falls sich jemand in FreeRTOS auskennt, bitte HIER schreien, dann kann 
ich ins Detail gehen.

Ist denn meine Konfiguration oben prinzipiell richtig?

von Pieter (Gast)


Lesenswert?

habe ja nicht alles im Kopf...und ferkel auch nicht in C...

TIM_ITConfig(

soll das was mit Timer-Interrupt-Config sein?
- TIM_IT_Update wird doch bei Über/Unterlauf ausgelöst.

Dann würde ich eine ISR erwarten, und wenn es die nicht gibt ...

von Che. (Gast)


Lesenswert?

Pieter schrieb:
> habe ja nicht alles im Kopf...und ferkel auch nicht in C...
>
> TIM_ITConfig(
>
> soll das was mit Timer-Interrupt-Config sein?

Jau, soweit ich verstanden hab, sind die dafür zuständig, die Timer 
auszulösen, wenn ein Rising/Falling-Edge am GPIO anliegt.. Hab grad nur 
noch karussell im kopf..


> - TIM_IT_Update wird doch bei Über/Unterlauf ausgelöst.
>
> Dann würde ich eine ISR erwarten, und wenn es die nicht gibt ...

Hier die Funktion, die ich nicht mitgepostet hatte:
1
 void __attribute__((used)) TIM2_IRQHandler()
2
{
3
  /*Interrupt Request Handler*/
4
  uint16_t captureVal;
5
  uint16_t captureValDiff;
6
  portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;  
7
8
  if(TIM_GetITStatus(PWM_TIMER, TIM_IT_CC1) != RESET)
9
  {
10
    if(TIM_GetFlagStatus(PWM_TIMER, TIM_FLAG_CC1OF) != RESET)
11
    {
12
      //TODO: Handle overflow error
13
      DEBUG_PRINT("Overflow Error\n");
14
    }
15
16
    captureVal = TIM_GetCapture1(PWM_TIMER);    //Counterwert wenn Capture aufgetreten ist, in µSec gemessen, 10µ entsprechen 1cm
17
    //DEBUG_PRINT("captureVal = %i\n", captureVal);
18
    captureValDiff = captureVal - prevCaptureVal;  //captureValDiff = distance in mm
19
    prevCaptureVal = captureVal;
20
    //convert to centimeters
21
    //captureValDiff = captureValDiff/10;
22
    xQueueSendFromISR(captureQueue, &captureValDiff, &xHigherPriorityTaskWoken);  //Schreibe captureValDiff in Queue
23
24
    captureFlag = true;
25
    TIM_ClearITPendingBit(PWM_TIMER, TIM_IT_CC1);
26
  }
27
28
  if (TIM_GetITStatus(PWM_TIMER, TIM_IT_Update) != RESET)
29
  {
30
    //Update input status
31
    isAvailable = (captureFlag == true);
32
    captureFlag = false;
33
    TIM_ClearITPendingBit(PWM_TIMER, TIM_IT_Update);
34
  }
35
}

PS. danke auf jeden fall schon für die Ansätze.

von Pieter (Gast)


Lesenswert?

moin moin,

uint16_t captureValDiff;
ist sichergestellt, dass das eine statische Variable ist? (ausserhalb 
ISR)

muss TIM_IT_Update nicht generell resettet werden?

VG
Pieter

von Pieter (Gast)


Lesenswert?

ha, so schnell holt es mich ein.

Brauche für eine Messung auch das PWM-Capture.
Genauer: Frequenz und Tastverhältnis.

VG
Pieter

von m.n. (Gast)


Lesenswert?


von Pieter (Gast)


Lesenswert?

Gehört nicht zum fred: das f_meter läuft bei mir unter Pascal :-) macht 
aber nur Frequenz.

@Che
#define PWM_TIM_PRESCALER      (84 - 1) // Wie wird der berechnet??

TIM2 arbeitet mit 2*42MHz als Takt, der counter arbeitet 0..83.

VG
Pieter

von Pieter (Gast)


Lesenswert?

moin moin,

so, mein Prog läuft, allerdings brauche ich dazu keinen Interrupt.
Einfach TIM2_CCR1 und TIM2_CCR2 prüfen ob keine 0 und dann steht in 
TIM2_CCR1 die Periodendauer und TIM2_CCR2 die Impulslänge.
Da ich TIM2_PSC auf 84-1 gesetzt habe, ist das Ergebnis in µs.

VG
Pieter

von m.n. (Gast)


Lesenswert?

Pieter schrieb:
> Da ich TIM2_PSC auf 84-1 gesetzt habe, ist das Ergebnis in µs.

Das ist ja langweilig! Das schafft ja schon ein ATmega ;-)

Aber vielleicht zeigst Du dem TO auch den Code.

von Che. (Gast)


Lesenswert?

Also ich bin jetzt umgestiegen auf TIM9 als neuen Timer.
Der hat eine Auflösung von 16 bit bei maximaler Frequenz von 84 MHz.

Mein Problem vorher war die Preemption-Priority bei den NVICs, die muss 
bei FreeRTOS auf mindestens configMAX_SYSCALL_INTERRUPT_PRIORITY stehen.

Das ist allerdings sehr systemspezifisch, vielleicht hilfts ja doch 
irgend einer armen Seele..

Ich würde allerdings gern verstehen wie ich meinen Prescaler berechne, 
könnt ihr mir da weiterhelfen?

Viele Grüße,
Che

von m.n. (Gast)


Lesenswert?

Che. schrieb:
> Ich würde allerdings gern verstehen wie ich meinen Prescaler berechne,
> könnt ihr mir da weiterhelfen?

Die Antwort darauf ist so einfach, daß das wohl eine Fangfrage ist?

Der Timer läuft wohl mit Internal Clock (CK_INT), welcher auf 84 MHz 
eingestellt ist. Weitergeleitet geht dieser Takt über den Prescaler 
(TIM9_PSC) an den eigentlichen Zähler: TIM9_CNT in Deinem Fall.
Dieser Prescaler steht nach einem Reset auf 0, was einem Teiler von /1 
entspricht. Für ein anderes Teilungsverhältnis n lädt man den Prescaler 
auf n-1. Soll TIM9_CNT mit 1 MHz getaktet werden (1 µs Auflösung), wird 
PSC mit 84-1 = 83 geladen.

Dein oben verwendeter Faktor 100 ist Unfug.
> TIM_TimeBaseStructure.TIM_Prescaler = PWM_TIM_PRESCALER*100; //1 µS =
> PWM_TIM_PRESCALER mal 10

Im "Reference Manual" findest Du ein Blockschaltbild der diversen Timer 
nebst ausführlicher Bechreibung.

von Che. (Gast)


Angehängte Dateien:

Lesenswert?

Ok, danke m.n. für deine Antwort.

m.n. schrieb:
> Dein oben verwendeter Faktor 100 ist Unfug.
>> TIM_TimeBaseStructure.TIM_Prescaler = PWM_TIM_PRESCALER*100; //1 µS =
>> PWM_TIM_PRESCALER mal 10

Faktor 100 ist nicht unbedingt "Unfug", aber anwendungsbedingt.
Faktor 10 passt besser. Das PWM Signal meines Sensors entspricht einer 
LOW-Verhältnis von 10µs/cm, daraus folgt, dass Faktor 100 für 
Meter-Messung eingesetzt wird.

Allerdings funktioniert der Sensor so wie in Bild 1.

Wenn ich jetzt für meinen Timer-Trigger
1
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_BothEdge;
einstelle, bekomme ich immer so hässliche Peaks in meiner Messung wie in 
Bild 2.

Ausserdem stimmt der Offset noch nicht.

Habt ihr ne Idee?

von Che. (Gast)


Lesenswert?

PS. Müsste also versuchen, nur die LOW-Zeit zu messen.
Hab das gefühl, dass er bei jetzt die Low und dann die Highzeit misst.

von m.n. (Gast)


Lesenswert?

Che. schrieb:
> Faktor 100 ist nicht unbedingt "Unfug", aber anwendungsbedingt.
> Faktor 10 passt besser. Das PWM Signal meines Sensors entspricht einer
> LOW-Verhältnis von 10µs/cm, daraus folgt, dass Faktor 100 für
> Meter-Messung eingesetzt wird.

Bitte würfel Messung und Auswertung nicht durcheinander. Wenn Dir 1 µs 
Auflösung reichen, stelle den Vorteiler auf 84 ein. Später kannst Du 
dann nach Bedarf skalieren.
Andernfalls könnte es passieren, daß die 16 Bit breiten Register mit 
Werten beschrieben werden, die aus einer 'Berechnung' mit Überlauf 
stammen.

An welchen Eingang hast Du denn jetzt Dein Signal angeschlossen? Wenn Du 
Timer9 verwendest, wären PA2 oder PE5 die möglichen Eingänge für 
TIM9_CH1.

Im "Reference Manual" steht unter "PWM input mode" genau beschrieben, 
welche Flanken auf welches Capture-Register wirken und wie der Zähler 
zurückgesetzt wird, nachdem sein max. Wert in ein Capture-Register 
geschrieben wurde. Dieser Wert ist die Periodendauer des 
Eingangssignals. Im anderen Capture-Register steht die Pulsweite des 
Signals.
Wichtig: pos. bzw. neg. Flanken haben ihr eigenes Capture-Register!

Einmal initialisiert können die aktuellen Daten direkt aus den 
Capture-Registern gelesen werden. Wie Pieter schon geschrieben hatte, 
braucht man dafür keinerlei Interrupts.

Sieh Dir die Abbildung zu "PWM input mode timing" an.

von Pieter (Gast)


Lesenswert?

bin in eile...

> Da ich TIM2_PSC auf 84-1 gesetzt habe, ist das Ergebnis in µs.

bei TIM9 ist TIM9_PSC auf 168-1 zu setzen.

Die Null wird als Zustand mitgezählt!

In der Doku ist etwas versteckt...ist der APB-Teile auf 1 bekommen die 
TimerCLK den doppelten Takt.

Beim INT würde ich für steigende Flanke freigeben, dann kommt am Ende 
der Messung ein INT und man kann losrechnen.

So, Feierabend!

von Che. (Gast)


Lesenswert?

Hi m.n. und hi Pieter,

vielen Dank erstmal für eure Hilfe.

Ich konnte jetzt länger nicht an der Baustelle mit dem STM32 weiter 
arbeiten, wegen Hardware-Problemen. Jetzt geht wieder alles und ich kann 
weiter machen.

m.n. schrieb:
> An welchen Eingang hast Du denn jetzt Dein Signal angeschlossen? Wenn Du
> Timer9 verwendest, wären PA2 oder PE5 die möglichen Eingänge für
> TIM9_CH1.

Genau, ich arbeite mit PA2.


m.n. schrieb:
> Andernfalls könnte es passieren, daß die 16 Bit breiten Register mit
> Werten beschrieben werden, die aus einer 'Berechnung' mit Überlauf
> stammen.

Ich habe den Prescaler jetzt geändert und teile dann nachher seperat 
durch 10 um auf meinen Wert zu kommen.

Pieter schrieb:
> bei TIM9 ist TIM9_PSC auf 168-1 zu setzen.
Erledigt, die Abstandsmessung pendelt jetzt wieder um den richtigen 
Zahlenwert zumindest.

Pieter schrieb:
> Beim INT würde ich für steigende Flanke freigeben, dann kommt am Ende
> der Messung ein INT und man kann losrechnen.

Du meinst mit INT den Interrupt, richtig? Wie gebe ich den denn für die 
steigende Flanke frei?

von Che. (Gast)


Lesenswert?

Hat vielleicht jemand noch einen Tipp? Danke im Voraus.

von m.n. (Gast)


Lesenswert?

Che. schrieb:
> Pieter schrieb:
>> Beim INT würde ich für steigende Flanke freigeben, dann kommt am Ende
>> der Messung ein INT und man kann losrechnen.
>
> Du meinst mit INT den Interrupt, richtig? Wie gebe ich den denn für die
> steigende Flanke frei?

m.n. schrieb:
> Im "Reference Manual" steht unter "PWM input mode" genau beschrieben,
> welche Flanken auf welches Capture-Register wirken

... und wie man sie einstellt.

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.