Forum: Mikrocontroller und Digitale Elektronik STM32F4 - Timerproblem (Output Compare)


von Andreas S. (igel1)


Lesenswert?

Liebe Forenteilnehmer,

ich möchte die Timer auf meinem STM32F4-Discovery Board nutzen,
um Pin PC6 zu toggeln. Aus Lerngründen soll das explizit über
den Output Compare Modus passieren.

Das klappt auch alles wunderbar, wenn ich TIM3 dafür verwende.
Sobald ich jedoch TIM3 durch TIM8 ersetze kommt kein Signal mehr
am Pin PC6 an.

Hier mein nicht funktionierender Code (für TIM8):
1
void TIM8_init()
2
{
3
    GPIO_InitTypeDef    gpio_C;                                 // GPIOC structure
4
    TIM_OCInitTypeDef   TIM8_OC;                                // Output Compare structure
5
    //NVIC_InitTypeDef    NVIC_TIM8;                            // NVIC structure
6
7
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);       // Clocking GPIOC (AHB1 = 84MHz)
8
9
    gpio_C.GPIO_Pin   = GPIO_Pin_6;                             // Ch.1 (PC6)
10
    gpio_C.GPIO_Mode  = GPIO_Mode_AF;                           // Alternative function
11
    gpio_C.GPIO_Speed = GPIO_High_Speed;                        // 100 MHz
12
    gpio_C.GPIO_OType = GPIO_OType_PP;                          // Push-pull
13
    gpio_C.GPIO_PuPd  = GPIO_PuPd_UP ;                          // Pulling-up
14
    GPIO_Init(GPIOC, &gpio_C);                                  // Initializing GPIOC structure
15
    GPIO_PinAFConfig(GPIOC, GPIO_PinSource6, GPIO_AF_TIM8);     // Routing TIM8 output to PC6  (P2 , Pin47 on STM32F4disco)
16
17
18
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM8, ENABLE);        // Clocking TIM8  (APB2 = 84MHz => CK_INT = 168MHz)
19
    //RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
20
21
    TIM_TimeBaseInitTypeDef timerInitStructure;
22
    timerInitStructure.TIM_Prescaler = 1680-1;
23
    timerInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
24
    timerInitStructure.TIM_Period = 10000-1;
25
    timerInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
26
    timerInitStructure.TIM_RepetitionCounter = 0;
27
    TIM_TimeBaseInit(TIM8, &timerInitStructure);
28
29
30
    TIM8_OC.TIM_OCMode      = TIM_OCMode_Toggle;                // Output compare toggling mode
31
    TIM8_OC.TIM_OutputState = TIM_OutputState_Enable;           // Enabling the Output Compare state
32
    TIM8_OC.TIM_Pulse       = 4999;                                // Output Compare 1 reg value
33
    TIM8_OC.TIM_OCPolarity  = TIM_OCPolarity_Low;               // Reverse polarity
34
    TIM_OC1Init(TIM8, &TIM8_OC);                                // Initializing Output Compare 1 structure
35
    TIM_OC1PreloadConfig(TIM8, TIM_OCPreload_Disable);          // Disabling Ch.1 Output Compare preload
36
37
38
    TIM_Cmd(TIM8, ENABLE);
39
}

Wenn ich in obigem Code alle "8"en durch "3"en ersetze und
zusätzlich den Teilstring "RCC_APB1" durch "RCC_APB2" ersetze,
funktioniert der Code und PC6 toggelt mit 2,5Hz.

Habt Ihr eine Idee?
Wo ist mein Denkfehler?
Worin unterscheidet sich TIM3 von TIM8 in Sachen Output Compare?

Viele Grüße

Igel1

von dochgast (Gast)


Lesenswert?

Hallo Andreas,

ich bin noch totaler Anfänger, deshalb muss meine Idee nicht stimmen. 
Falls das zutrifft, bitte den Kommentar einfach überlesen.

Ich arbeite mich im Moment ein wenig in den STM32F411 ein. Dieser hat 
laut Datenblatt und meinem Verständnis gar keinen Timer 8.

Könnte das dein Problem sein?

Viele Grüße
dochgast

von ziegler (Gast)


Lesenswert?

dochgast schrieb:
> Hallo Andreas,
> ich bin noch totaler Anfänger, deshalb muss meine Idee nicht stimmen.
> Falls das zutrifft, bitte den Kommentar einfach überlesen.
> Ich arbeite mich im Moment ein wenig in den STM32F411 ein. Dieser hat
> laut Datenblatt und meinem Verständnis gar keinen Timer 8.
> Könnte das dein Problem sein?
>
> Viele Grüße
> dochgast

nachdem sein code richtig compiliert wurde muss er wohl einen tim8 
haben.
aber ohne genaue bezeichnung ist das ein ratespiel.

von Martin (Gast)


Lesenswert?

du Musst das MOE Bit setzen (Master output enable), auch bei TIM1 ist 
das so

von Hans-Georg L. (h-g-l)


Lesenswert?

Der Pin muss wissen an welcher AF er hängt.
Beim STM32F446 ist z.B. Timer 1 AF2 und Timer 3 AF3 ...

Master output enable brauchst du nur wenn du andere Timer syncronisieren 
willst.

von Christopher J. (christopher_j23)


Lesenswert?

Hans-Georg L. schrieb:
> Der Pin muss wissen an welcher AF er hängt.
> Beim STM32F446 ist z.B. Timer 1 AF2 und Timer 3 AF3 ...

Dachte auch erst es würde daran liegen aber Martin hat glaube ich recht:

Martin schrieb:
> du Musst das MOE Bit setzen (Master output enable), auch bei TIM1 ist
> das so

weil Timer 1 und 8 "Advanced Timer" sind muss man das MOE Bit zwingend 
setzen, sonst tut sich am Ausgang nichts. In Tabelle 94. im Handbuch 
(RM0090) gibt es noch eine Übersicht wie welches Bit den Ausgang 
beeinflusst.

von Andreas S. (igel1)


Angehängte Dateien:

Lesenswert?

dochgast schrieb:

> Ich arbeite mich im Moment ein wenig in den STM32F411 ein.
> Dieser hat laut Datenblatt und meinem Verständnis gar keinen Timer 8.
> Könnte das dein Problem sein?

Das ist suess :)

Ich hatte vor meiner Frage natürlich das Reference Manual und auch das
Internet befragt. Wenn mir dann nicht aufgefallen wäre, dass mein Chip
gar keinen TIM8 hat, so hätte ich wirklich ein dickes Brett vorm Kopf
gehabt. Aber auch das soll schon einmal vorkommen,
Daher: Danke für Deinen Hinweis.

------------------------------------------------

ziegler schrieb:

> nachdem sein code richtig compiliert wurde muss er wohl einen
> tim8 haben. aber ohne genaue bezeichnung ist das ein ratespiel.

Hmmm - ich dachte eigentlich mit meiner Angabe STM32F4-Discovery Board
sei alles gesagt und hatte daher Deine Frage zunächst nicht verstanden.

Aber Du hast natürlich recht: nicht jeder kennt den Chip auf dem
STM32F4-Disco-Board - es ist ein STM32F407VGT6 mit 12 Zylindern und
Einspritzung.

-------------------------------------------------

Martin schrieb:

> du Musst das MOE Bit setzen (Master output enable),
> auch bei TIM1 ist das so

Chapeau!!
Du hast den Vogel auf Anhieb abgeschossen!
(ich glaube, so etwas nennt man "Erfahrung" ...)

Nachdem ich das MOE Bit gesetzt hatte, schnurrte die Kiste
sofort wie Schmitz' Katze.

Und für die Nachwelt gibt's den korrigierten, lauffähigen Code:
1
void TIM8_init()
2
{
3
    GPIO_InitTypeDef    gpio_C;                                 // GPIOC structure
4
    TIM_OCInitTypeDef   TIM8_OC;                                // Output Compare structure
5
    //NVIC_InitTypeDef    NVIC_TIM8;                            // NVIC structure
6
7
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);       // Clocking GPIOC (AHB1 = 84MHz)
8
9
    gpio_C.GPIO_Pin   = GPIO_Pin_6;                             // Ch.1 (PC6)
10
    gpio_C.GPIO_Mode  = GPIO_Mode_AF;                           // Alternative function
11
    gpio_C.GPIO_Speed = GPIO_High_Speed;                        // 100 MHz
12
    gpio_C.GPIO_OType = GPIO_OType_PP;                          // Push-pull
13
    gpio_C.GPIO_PuPd  = GPIO_PuPd_UP ;                          // Pulling-up
14
    GPIO_Init(GPIOC, &gpio_C);                                  // Initializing GPIOC structure
15
    GPIO_PinAFConfig(GPIOC, GPIO_PinSource6, GPIO_AF_TIM8);     // Routing TIM8 output to PC6  (P2 , Pin47 on STM32F4disco)
16
17
18
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM8, ENABLE);        // Clocking TIM8  (APB2 = 84MHz => CK_INT = 168MHz)
19
    //RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
20
21
    TIM_TimeBaseInitTypeDef timerInitStructure;
22
    timerInitStructure.TIM_Prescaler = 1680-1;
23
    timerInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
24
    timerInitStructure.TIM_Period = 10000-1;
25
    timerInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
26
    timerInitStructure.TIM_RepetitionCounter = 0;
27
    TIM_TimeBaseInit(TIM8, &timerInitStructure);
28
29
30
    TIM8_OC.TIM_OCMode      = TIM_OCMode_Toggle;                // Output compare toggling mode
31
    TIM8_OC.TIM_OutputState = TIM_OutputState_Enable;           // Enabling the Output Compare state
32
    TIM8_OC.TIM_Pulse       = 4999;                                // Output Compare 1 reg value
33
    TIM8_OC.TIM_OCPolarity  = TIM_OCPolarity_Low;               // Reverse polarity
34
    TIM_OC1Init(TIM8, &TIM8_OC);                                // Initializing Output Compare 1 structure
35
    TIM_OC1PreloadConfig(TIM8, TIM_OCPreload_Disable);          // Disabling Ch.1 Output Compare preload
36
37
    TIM_CtrlPWMOutputs(TIM8, ENABLE);    // Set the Main Output Enable Bit (MOE)
38
                                         // In gratefulness to Martin
39
40
    TIM_Cmd(TIM8, ENABLE);
41
}

--------------------------------------------------------

Hans-Georg Lehnard schrieb:

> Der Pin muss wissen an welcher AF er hängt.
> Beim STM32F446 ist z.B. Timer 1 AF2 und Timer 3 AF3 ...

Das weiss er durch Aufruf von:
1
GPIO_PinAFConfig(GPIOC, GPIO_PinSource6, GPIO_AF_TIM8);     // Routing TIM8 output to PC6  (P2 , Pin47 on STM32F4disco)

... hattest Du vermutlich überlesen.

> Master output enable brauchst du nur wenn du andere Timer
> syncronisieren willst.

Damit liegst Du leider falsch (ich war ebenfalls überrascht ...)
Den Gegenbeweis findest Du als Bild im Anhang.

---------------------------------------------------------

Christopher Johnson schrieb:

> weil Timer 1 und 8 "Advanced Timer" sind muss man das MOE Bit
> zwingend setzen, sonst tut sich am Ausgang nichts.
> In Tabelle 94. im Handbuch (RM0090) gibt es noch eine Übersicht
> wie welches Bit den Ausgang beeinflusst.

Auch dieser Tipp ein Treffer ins Schwarze (sogar mit Refman Verweis!).
Dickes Dankeschön daher auch an Dich!

-----------------------------------------------------------

Viele Grüße und Danke nochmals an alle Helfer für die Mühen -
insbesondere natürlich an Martin und Christopher für die Gold-Tipps.


Igel1


PS:

Und wenn ich hier schon einmal die Profis an der Angel habe:

Habt Ihr vielleicht einen Zusatztipp, wie man dieses Timer-Zeugs
vernünftig debuggt? Am liebsten würde ich per Einzelschritte
die Zustände aller internen "Leitungen" aus den Bildern im
Reference-Manual sehen ...

von Hans-Georg L. (h-g-l)


Lesenswert?

> Martin schrieb:
>
> du Musst das MOE Bit setzen (Master output enable),
> auch bei TIM1 ist das so
>

Hast Recht das ist diese Break Feature womit man die Ausgänge abschalten 
kann.

> Andreas schrieb:
> Habt Ihr vielleicht einen Zusatztipp, wie man dieses Timer-Zeugs
> vernünftig debuggt? Am liebsten würde ich per Einzelschritte
> die Zustände aller internen "Leitungen" aus den Bildern im
> Reference-Manual sehen ...

Ich schau mir die Timer Register im Debugger an und die Ausgangssignale 
auf dem Osci.

Es gibt auch einen Abschnitt "Debug support for timers .." im Reference 
Manual habe ich aber noch nicht ausprobiert.

von Hans-Georg L. (h-g-l)


Lesenswert?

Andreas S. schrieb:

> GPIO_PinAFConfig(GPIOC, GPIO_PinSource6, GPIO_AF_TIM8);     //

Wo kommt diese Funktion her ? bringt bei mir einen Syntax Error.

Und was macht die Funktion wenn ich sie so aufrufe

GPIO_PinAFConfig(GPIOC, GPIO_PinSource6, GPIO_AF_TIM6);

oder

GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_TIM8);

: Bearbeitet durch User
von Chris D. (myfairtux) (Moderator) Benutzerseite


Lesenswert?

Auch hier nochmal der Tipp von mir, sich die Beispiele bspw. der StdLib 
bzgl. Timer-PWM anzusehen.

Üblicherweise finden sich diese unter 
Project/Peripheral_Examples/TIM_PWM_Output. Dort findet sich bei der 
Initialisierung des Timers am Ende so etwas,
1
  /* TIM1 counter enable */
2
  TIM_Cmd(TIM1, ENABLE);
3
4
  /* TIM1 Main Output Enable */
5
  TIM_CtrlPWMOutputs(TIM1, ENABLE);
6
7
  /* Infinite loop */
8
  while (1)
9
  {}

was einen hellhörig werden lassen sollte :-)

Ich hatte das Bit übrigens damals auch übersehen ("Break and dead-time 
register? Brauch ich nicht.") und kam erst durch das Beispiel darauf 
<:-)

: Bearbeitet durch Moderator
von Chris D. (myfairtux) (Moderator) Benutzerseite


Lesenswert?

Hans-Georg L. schrieb:
> Andreas S. schrieb:
>
>> GPIO_PinAFConfig(GPIOC, GPIO_PinSource6, GPIO_AF_TIM8);     //
>
> Wo kommt diese Funktion her ? bringt bei mir einen Syntax Error.

Die ist bspw. in der StdLib enthalten. Du musst den entsprechenden 
Header mit #include "stm32f0xx_gpio.h" einbinden (und natürlich die 
entsprechenden Sourcen mitkompilieren).

> Und was macht die Funktion wenn ich sie so aufrufe
>
> GPIO_PinAFConfig(GPIOC, GPIO_PinSource6, GPIO_AF_TIM6);
>
> oder
>
> GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_TIM8);

Damit verknüpfst Du den Pin mit dem entsprechenden internen Modul, also 
z.B. mit einem OC-Ausgang eines Timers. Du kannst aber nicht jedes Modul 
mit jedem Pin verbinden.

: Bearbeitet durch Moderator
von Hans-Georg L. (h-g-l)


Lesenswert?

Chris D. schrieb:
> Hans-Georg L. schrieb:
>> Andreas S. schrieb:
>>
>>> GPIO_PinAFConfig(GPIOC, GPIO_PinSource6, GPIO_AF_TIM8);     //
>>
>> Wo kommt diese Funktion her ? bringt bei mir einen Syntax Error.
>
> Die ist bspw. in der StdLib enthalten. Du musst den entsprechenden
> Header mit #include "stm32f0xx_gpio.h" einbinden (und natürlich die
> entsprechenden Sourcen mitkompilieren).
>
>> Und was macht die Funktion wenn ich sie so aufrufe
>>
>> GPIO_PinAFConfig(GPIOC, GPIO_PinSource6, GPIO_AF_TIM6);
>>
>> oder
>>
>> GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_TIM8);
>
> Damit verknüpfst Du den Pin mit dem entsprechenden internen Modul, also
> z.B. mit einem OC-Ausgang eines Timers. Du kannst aber nicht jedes Modul
> mit jedem Pin verbinden.

Das ist mir schon klar ;-)

Ich wollte nur darauf hinaus das dieses keineswegs eine universelle 
Funktion ist wie der Name vermuten lässt ... Timer 6 hat keinen Ausgang, 
und PortA pion 6 kann nicht mit Timer8 verbunden werden.

Bei cubemx erzeugten Code gibts nur ein "stm32f4xx_gpio_hal.h" und 
keinen "stm32f4xx_gpio.h"

Wenn ich die HAL Version benutze habe ich das gleiche Problem
(ist jetzt ein anderer Pin und ein anderer Timer)

    /**TIM5 GPIO Configuration
     */
    GPIO_InitStruct.Pin = GPIO_PIN_0;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    GPIO_InitStruct.Alternate = GPIO_AF2_TIM5;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);   // Richtig

wenn ich den falschen Port benutze
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);     // falsch
Wird das vom Compiler nicht angemeckert

Einen Timer der übehaupt keinen OC Ausgang hat wird als Syntax Fehler 
erkannt weil es keine definition GPIO_AFx_TIM6; oder TIM7 gibt;

enums für die AF/PIN/Port Kombinationen würden da helfen ...
Das ist noch ein Grund für mich auf c++ und templates um zu steigen ;-)

von Andreas S. (igel1)


Lesenswert?

Chris D. schrieb:

> Üblicherweise finden sich diese unter
> Project/Peripheral_Examples/TIM_PWM_Output.
> Dort findet sich bei der
> Initialisierung des Timers am Ende so etwas,
> [...]
> was einen hellhörig werden lassen sollte :-)

Ja - diesen Weg gehe ich auch immer - und auch diesmal hatte ich
zunächst in die Peripheral_Examples reingeschaut und dort das OC
Beispiel gut beäugt - nur ist jenes Beispiel halt mit TIM3 realisiert,
der (wie wir inzwischen wissen) kein MOE-Bit benötigt.
Na ja - und an dieser Stelle bin ich dann halt eben in die Falle 
getappt.

Aber wofür habe ich Euch? ;-)

Hier im Forum bekommt man von so vielen netten Leuten so geniale Tipps,
da schäme ich mich nicht, nach 2-3h Suche auch mal aufzugeben und hier
um Rat und Tat zu fragen.

Es gilt ja stets:
"Experience is something you don't get until just after you need it"

In diesem Sinne Danke auch an Hans-Georg für seine Debug-Tipps.
Dein Tipp, Hans-Georg, war mir endlich Ansporn genug, mich um die
nicht funktionierende Anzeige der Peripheral Registers in meiner
IDE zu kümmern (ich nutze die "emIDE").

Hier fand ich dann auch tatsächlich einen Hinweis:
http://emide.org/documentation_debugging_registers.html
Letztendlich genügte es, meinen Prozessortyp "STM32F407VG" als
Target Device in den Debugger-Einstellungen meines Projektes zu
hinterlegen. Et voilà - jetzt kann ich mich vor Registern kaum
noch retten ...

(... 20 Minuten später ...)

... allein ich stelle gerade fest, dass der Registerinhalt in meiner
emIDE nicht angezeigt wird (alles 0x00000000) - Mist.

Liegt evtl. daran, dass ich ST-LINKv2 und nicht J-Link nutze.
Anyway - ich wollte sowieso den ST-Linkv2 in J-Link umflashen - mal
sehen, ob's dann funktioniert.

Viele Grüße

Igel1

von Hans-Georg L. (h-g-l)


Lesenswert?

Andreas S. schrieb:
> Chris D. schrieb:
>
> ... allein ich stelle gerade fest, dass der Registerinhalt in meiner
> emIDE nicht angezeigt wird (alles 0x00000000) - Mist.
>
> Liegt evtl. daran, dass ich ST-LINKv2 und nicht J-Link nutze.
> Anyway - ich wollte sowieso den ST-Linkv2 in J-Link umflashen - mal
> sehen, ob's dann funktioniert.
>

Der Stlink läuft bei mir unter STM AC6 (eclipse) fehl dir vielleicht das 
richtige svd File ?
Darin sind die Register und die dazugehörigen Bits mit ihren Adressen 
beschrieben ...

Wenn es dir fehlt schau hier:

https://github.com/posborne/cmsis-svd

von Andreas S. (igel1)


Lesenswert?

> Der Stlink läuft bei mir unter STM AC6 (eclipse) fehl
> dir vielleicht das richtige svd File ?

Nein, das war's nicht.

Ich mußte tatsächlich meinen ST-Linkv2 in einen J-Link umflashen
und dann noch ein bißchen in den Projekteinstellungen meiner
emIDE rumwurschteln (ohne 100%ig zu verstehen, was ich da tat ..).

Seitdem geht der Programmupload gefühlt mindestens doppelt so
schnell und die Peripheral Register kann ich nun auch sehen.
Besser hätte es gar nicht laufen können - supi.

Viele Grüße

Igel1

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.