Forum: Mikrocontroller und Digitale Elektronik STM32F4 Discovery PWM mit unterschiedlicher Frequenz


von Michi Müller (Gast)


Lesenswert?

Hey zusammen,

ich hab mal eine kurze Verständnisfrage zum STM32F4 Discovery Board.

Ich möchte gerne ein Ferngesteuerten Auto bauen bei welchem es einen 
Antriebsmotor und einen Servomotor zur Lenkung gibt. Daher benötige ich 
zwei PWM Signale an zwei unterschiedlichen Pins. Um den Antriebsmotor 
nicht surren zu lassen wäre es ideal ihn mit einer höheren Frequenz als 
den Servo zu betreiben. Darum meine Frage:

Ist es generell bei dem Disco Board möglich zwei PWM Signale mit 
unterschiedlicher Frequenz zu erzeugen? Wenn ja muss man bei der 
Initialisierung irgendetwas besonderes beachten?

Z.B. TIM3 mit 50 Hz und 20 ms Periode
und  TIM4 mit 10kHz und 100 µs Periode (Werte als Beispiel angenommen)


Hab bisher leider nur Beispiele gefunden, welche mit einem Timer das 
selbe PWM Signal auf mehrere Pins ausgeben. Auch im RefMan hab ich 
bisher nichts passendes gefunden.

Über eine Antwort wäre ich sehr dankbar.

Gruß Michael

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Das geht - du musst dir nur freie Pins suchen. PA1 z.b. kann auf Timer2 
Channel 2 gemappt werden und PA2 auf Timer5 Channel 3.
Wenn die die Pins gemappt hast, initialisierst du die beiden Timer per 
Period auf die gewünschte Wiederholfrequenz und dann den gewünschten 
Channel auf PWM.

von Bernd (Gast)


Lesenswert?

Wie schon von Matthias geschrieben geht das.
Unterschiedliche PWM Frequenzen sind aber nur mit unterschiedlichen 
Timern möglich. Wenn du mehrere Channel eines Timers nutzt, haben die 
auch die gleiche Frequenz.

von Michi Müller (Gast)


Lesenswert?

Hey vielen Dank für eure Antworten.

Dann habe ich wohl einen Fehler im Code den ich bereinigen muss. Hab 
jetzt mal beide PWM's auf die GPIOs gelegt, welche Matthias 
vorgeschlagen hat. Leider tut sich bei meinen Motoren immer noch nichts.

Anbei hab ich mal den Code gepostet. Ziel ist es einen Servo und einen 
DC Motor zu betreiben um das Fahrzeug zu steuern.

Vielleicht könnt ihr ja nochmal über den Code schauen und mir evtl 
Verbesserungsvorschläge mitteilen.


Gruß Michi

1
// Init der GPIO's
2
3
  void InitGPIO (void)
4
  {
5
    // GPIO für PWMServo (PA1)
6
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
7
8
    GPIO_InitTypeDef GPIO_InitStructurePWMServo;              // GPIOC Configuration: TIM2 CH2 (PA1)
9
      GPIO_InitStructurePWMServo.GPIO_Pin=GPIO_Pin_1;            // PWM Signalausgang an PA1
10
      GPIO_InitStructurePWMServo.GPIO_Mode=GPIO_Mode_AF;          // _IN  _AF:Alternate  _AN:Analog
11
      GPIO_InitStructurePWMServo.GPIO_Speed=GPIO_Speed_100MHz;      // _2MHz  _10MHz
12
      GPIO_InitStructurePWMServo.GPIO_OType=GPIO_OType_PP;        // _PP:Push/Pull _OD:OpenDrain
13
      GPIO_InitStructurePWMServo.GPIO_PuPd=GPIO_PuPd_UP;          // _No Pull    _UP   _DOWN
14
    GPIO_Init(GPIOA,&GPIO_InitStructurePWMServo);
15
16
    GPIO_PinAFConfig(GPIOA,GPIO_PinSource1,GPIO_AF_TIM2);          // TIM2, CH2, AF2
17
18
19
20
    // GPIO für PWMEngine (PA2)
21
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
22
23
    GPIO_InitTypeDef GPIO_InitStructurePWMEngine;              // GPIOC Configuration: TIM5 CH3 (PA2)
24
      GPIO_InitStructurePWMEngine.GPIO_Pin=GPIO_Pin_2;          // PWM Signalausgang an PA2
25
      GPIO_InitStructurePWMEngine.GPIO_Mode=GPIO_Mode_AF;          // _IN  _AF:Alternate  _AN:Analog
26
      GPIO_InitStructurePWMEngine.GPIO_Speed=GPIO_Speed_100MHz;      // _2MHz  _10MHz
27
      GPIO_InitStructurePWMEngine.GPIO_OType=GPIO_OType_PP;        // _PP:Push/Pull _OD:OpenDrain
28
      GPIO_InitStructurePWMEngine.GPIO_PuPd=GPIO_PuPd_UP;          // _No Pull    _UP   _DOWN
29
    GPIO_Init(GPIOA,&GPIO_InitStructurePWMEngine);
30
31
    GPIO_PinAFConfig(GPIOA,GPIO_PinSource2,GPIO_AF_TIM5);          // TIM5, CH3, AF2
32
33
}
34
35
36
37
38
// Init der Timer
39
40
  void InitPWMServo (void)
41
  {
42
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);      // 42 MHz * 2 = 84 MHz
43
44
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructurePWMServo;
45
      TIM_TimeBaseStructurePWMServo.TIM_Period=400;          
46
      TIM_TimeBaseStructurePWMServo.TIM_Prescaler=839;
47
      TIM_TimeBaseStructurePWMServo.TIM_CounterMode=TIM_CounterMode_Up;
48
      TIM_TimeBaseStructurePWMServo.TIM_ClockDivision=0;
49
    TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStructurePWMServo);
50
51
    TIM_OCInitTypeDef TIM_OCInitStructurePWMServo;
52
      TIM_OCInitStructurePWMServo.TIM_OCMode=TIM_OCMode_PWM1;
53
      TIM_OCInitStructurePWMServo.TIM_OutputState=TIM_OutputState_Enable;
54
      TIM_OCInitStructurePWMServo.TIM_Pulse=CCR2Servo_Val; // CCR2Servo_Val = 30 für Servo Mittelstellung zu Beginn
55
      TIM_OCInitStructurePWMServo.TIM_OCPolarity=TIM_OCPolarity_High;
56
    TIM_OC2Init(TIM2,&TIM_OCInitStructurePWMServo);
57
58
    TIM_OC2PreloadConfig(TIM2,TIM_OCPreload_Enable);
59
60
    TIM_Cmd(TIM2,ENABLE);
61
62
  }
63
64
65
  void InitPWMEngine (void)
66
  {
67
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5,ENABLE);      // 42 MHz * 2 = 84 MHz
68
    
69
70
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructurePWMEngine;
71
      TIM_TimeBaseStructurePWMEngine.TIM_Period=30;          
72
      TIM_TimeBaseStructurePWMEngine.TIM_Prescaler=249;
73
      TIM_TimeBaseStructurePWMEngine.TIM_CounterMode=TIM_CounterMode_Up;
74
      TIM_TimeBaseStructurePWMEngine.TIM_ClockDivision=0;
75
    TIM_TimeBaseInit(TIM5,&TIM_TimeBaseStructurePWMEngine);
76
77
    TIM_OCInitTypeDef TIM_OCInitStructurePWMEngine;
78
      TIM_OCInitStructurePWMEngine.TIM_OCMode=TIM_OCMode_PWM1;
79
      TIM_OCInitStructurePWMEngine.TIM_OutputState=TIM_OutputState_Enable;
80
      TIM_OCInitStructurePWMEngine.TIM_Pulse=CCR3Engine_Val;    // CCR3Engine_Val= 0 für Motorstillstand zu Beginn
81
      TIM_OCInitStructurePWMEngine.TIM_OCPolarity=TIM_OCPolarity_High;
82
    TIM_OC3Init(TIM5,&TIM_OCInitStructurePWMEngine);
83
84
    TIM_OC3PreloadConfig(TIM5,TIM_OCPreload_Enable);
85
86
    TIM_Cmd(TIM5,ENABLE);
87
  }
88
89
90
91
92
93
// Main - Abfahren von Min bis Max Werte zum testen des Servo Motors und des DC Motors
94
95
96
          for (WertServo = Servo_MIN_VALUE; WertServo <= Servo_MAX_VALUE; WertServo = WertServo+5)
97
          {
98
            int a;
99
100
              for (a = 0; a < 0x000AFFFF; ++a)
101
                {
102
                }
103
104
                SERVO_PWM_VALUE = WertServo;    // #define SERVO_PWM_VALUE    TIM2->CCR2
105
          }
106
107
108
109
          for (WertEngine = 0; WertEngine <= 25; WertEngine = WertEngine+1)
110
          {
111
            int i;
112
113
              for (i = 0; i < 0x000AFFFF; ++i)
114
                {
115
                }
116
117
                ENGINE_PWM_VALUE = WertEngine;    // #define ENGINE_PWM_VALUE  TIM5->CCR3     
118
          }

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Michi Müller schrieb:
> TIM_TimeBaseStructurePWMServo.TIM_ClockDivision=0;

Diese beiden Zeilen machen mich etwas unruhig. Ich guck jetzt nicht ins 
Datenbuch, aber schreib doch sowas hier:
1
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
Rufst du in der main() irgendwann SystemInit() auf? Wenn nein, tue es. 
Mach dir am besten auch mal ein paar LED an die Ausgänge.
Wenn du übrigens mehrere Pins am Port auf gleich konfigurieren willst, 
kannst du sowas machen:
1
     GPIO_InitStructurePWMEngine.GPIO_Pin=GPIO_Pin_2 | GPIO_Pin_1;
Der Rest sieht eigentlich richtig aus, habe ich so auch bei 
funktionierender PWM. Wenn du LED an den Ausgängen hast, kannst du ja 
den Prescaler auch mal richtig gross machen, dann solltest du es blinken 
sehen. Zum Testen kannst du auch mal TIM4 auf Kanal 2 nehmen (Pin PD13), 
der sollte die orangene LED blinken lassen.
Auf den 4 LED (PD12 - PD15) sind ja CH1 bis CH4 von TIM4.

: Bearbeitet durch User
von Michi Müller (Gast)


Lesenswert?

Danke für deine Antwort Matthias.


Soo PWM läuft jetzt (nachdem ich SystemInit() AUSkommentiert habe) 
Allerdings kann ich mir das nicht erklären :-)

Ich lasse mir zum Debuggen von anderen Funktionen noch einige Werte mit 
sprintf auf dem Display ausgeben.


Variante1:
SystemInit() auskommentiert
PWM funktioniert
sprintf funktioiert nicht (nur flimmern auf dem Display)

Variante 2:
SystemInit() einkommentiert
PWM funktioniert nicht (Servo fährt auf Anschlag und rattert->falsche 
Frequenz/Periodendauer)
sprintf funktioniert

Kann es sein, dass sich die PWM und sprintf in die Quere kommen? 
SystemCoreClock ist mit und ohne SystemInit() bei 180 MHz.


Ab nächster Woche habe ich auch wieder ein Oszi zur Verfügung, dann 
werde ich das nochmals an den µC hängen und schauen welche Werte 
ausgegeben werden solange SystemInit() einkommentiert ist.



Gruß Michi

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Michi Müller schrieb:
> SystemCoreClock ist mit und ohne SystemInit() bei 180 MHz.

Je nach Entwicklungsystem und CMSIS oder HAL wird der Clocktree 
unterschiedlich initialisiert.
Die 180 MHz sind demnach nicht unbedingt richtig bzw. gegeben. Hier mit 
Coocox und dem STM32F4 Disco wird z.B. auf 168MHz initialisiert, wobei 
ich da mächtig drin rumfummeln musste, weil z.B. die I2S Clock nicht 
richtig gestartet wurde.

von Michi Müller (Gast)


Lesenswert?

Wenn ich mich nicht täusche wird die SystemClock doch wie folgt 
berechnet:
1
Core Clock = ((HSE_VALUE / PLL_M) * PLL_N) / PLL_P
2
#define HSE_VALUE    ((uint32_t)8000000) /*!< Value of the External oscillator in Hz */
3
#define PLL_M      8
4
#define PLL_N      360
5
#define PLL_P      2
6
--> Core Clock = 180000000 Hz
7
8
PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N
9
-->PLL_VCO = 360000000 Hz
10
11
SYSCLK = PLL_VCO / PLL_P
12
--> SYSCLK = 180000000 Hz

Somit sollte sie bei mir mit 180 MHz definiert sein. Ich verwende 
übrigens auch CoIDE.

Was ich einfach bisher nicht nachvollziehen kann warum die PWM ohne die 
Verwendung von SystemInit() funktioniert und nach dem Einbinden von 
SystemInit() nicht mehr. Der Zusammenhang kann doch nur aus einem 
Umkonfigurieren einer Varibalen innerhalb dieser Funktion kommen.

Wie  gesagt ich werde mich kommende Woche mal vor das Oszi setzen. Ohne 
ist es ein bisschen wie stochern im Nebel.

Ich melde mich sobald ich Neuigkeiten oder eine Lösung gefunden habe.


Vielen Dank nochmal für deine/eure Hilfe.

Gruß Michi

von Michi Müller (Gast)


Lesenswert?

Soo hab jetzt endlich die Lösung gefunden. Nach einigen Stunden suchen 
;-)

Die Funktion SystemInit() initialisiert bei meinem Programm die 
SystemClock mit 180 MHz. In dem Beispiel aus welchem ich den Code für 
die PWM des Servo-Motor genommen habe, wurde die SystemInit nicht 
durchgeführt. Anscheinend war also eine andere Systemclock und/oder 
andere Prescaler für die AHB's eingestellt.

Habe jetzt durch das Zurückrechnen der am Oszi ausgegebenen PWM Frequenz 
mit eingestellten Prescaler und der Periode herausgefunden, dass die 
Timer TIM3 und TIM4 mit 90 MHz laufen.

--> Ändern der Werte für Prescaler und Periode und schon läuft alles und 
ist durch die bekannte Berechnung auch nachvollziehbar:
1
TIM Update Frequency = TIM Clock / (P * Q)
2
Prescaler = P - 1, and Period = Q - 1


Kann mir vielleicht noch jemand verraten ob es Variablen gibt, welche 
ich beim Debuggen (oder mit printf) dafür verwenden kann um 
herauszufinden mit welchem Takt AHB1 und AHB2 initialisiert sind? Gibt 
es diese Variablen oder kann ich mir das nur über die eingestellten 
PLL_-Werte und der SysClock berechnen? Dann tue ich mich beim nächsten 
Mal evtl etwas leichter den Fehler zu ermitteln.

Vielen Dank nochmals für eure Hilfe. Und ein schönes Wochenende !!!

Gruß Michi

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Michi Müller schrieb:
> Kann mir vielleicht noch jemand verraten ob es Variablen gibt, welche
> ich beim Debuggen (oder mit printf) dafür verwenden kann um
> herauszufinden mit welchem Takt AHB1 und AHB2 initialisiert sind?

Du kannst die RCC Register auslesen und auf die Konsole werfen. 
Interessant sind hier vor allem RCC->CR, RCC->PLLCFGR und RCC->CFGR.

: Bearbeitet durch User
von holger (Gast)


Lesenswert?

>Kann mir vielleicht noch jemand verraten ob es Variablen gibt, welche
>ich beim Debuggen (oder mit printf) dafür verwenden kann um
>herauszufinden mit welchem Takt AHB1 und AHB2 initialisiert sind?

Es gibt eine Funktion dafür

void RCC_GetClocksFreq(RCC_ClocksTypeDef* RCC_Clocks)

von Michi Müller (Gast)


Lesenswert?

Ihr seid die Besten :-)

Vielen Dank

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.