Forum: Mikrocontroller und Digitale Elektronik STM32F411 Timer1 PWM


von S. D. (der_nachtfuchs)


Lesenswert?

Hallo zusammen!

Ich möchte auf einem STM32F411CEU6 den Timer1 so konfigurieren, dass die 
zugehörigen Kanäle 1 bis 3 jeweils komplementäres PWM-Signal ausgeben:

Kanal 1: Ausgang PA8 und PA7
Kanal 2: Ausgang PA9 und PB0
Kanal 3: Ausgang PA10 und PB1



Der Timer soll dabei hoch- und wieder runterzählen (das tut er schonmal) 
aber ich bekomme es nicht hin, dass die Ausgänge entsprechend schalten.

Kann mir da bitte jemand helfen?



Bisher hab ich folgendes zusammengebracht:
1
  RCC -> APB2ENR |= RCC_APB2ENR_TIM1EN;
2
3
  //Global TIM1 settings
4
  TIM1->PSC     = 1000;             //100 MHz divided by this value
5
  TIM1->ARR     = 65534;            //Overflow value  
6
7
  //Control register 1 (TIMx_CR1)
8
  TIM1->CR1    &= ~TIM_CR1_OPM;     //Continuous mode
9
  TIM1->CR1    |=  TIM_CR1_CMS;     //PWM center-aligned mode 3
10
  TIM1->CR1    |=  TIM_CR1_ARPE;    //Auto-reload buffering
11
  TIM1->CR1    |=  TIM_CR1_CEN;     //Enable counter
12
  
13
  //Capture/compare enable register (TIMx_CCER)
14
  TIM1->CCER   &= ~TIM_CCER_CC1P;   //polarity high when active (bit=0)
15
  TIM1->CCER   &= ~TIM_CCER_CC2P;   //polarity high when active (bit=0)
16
  TIM1->CCER   &= ~TIM_CCER_CC3P;   //polarity high when active (bit=0)
17
  TIM1->CCER   &= ~TIM_CCER_CC1NP;  //polarity high when active (bit=0)
18
  TIM1->CCER   &= ~TIM_CCER_CC2NP;  //polarity high when active (bit=0)
19
  TIM1->CCER   &= ~TIM_CCER_CC3NP;  //polarity high when active (bit=0)
20
  
21
  //Capture/compare enable register (TIMx_CCER)
22
  TIM1->CCER   |=  TIM_CCER_CC1E;   //Enable output compare (bit=1)
23
  TIM1->CCER   |=  TIM_CCER_CC2E;   //Enable output compare (bit=1)
24
  TIM1->CCER   |=  TIM_CCER_CC3E;   //Enable output compare (bit=1)
25
  TIM1->CCER   |=  TIM_CCER_CC1NE;  //Enable output compare (bit=1)
26
  TIM1->CCER   |=  TIM_CCER_CC2NE;  //Enable output compare (bit=1)
27
  TIM1->CCER   |=  TIM_CCER_CC3NE;  //Enable output compare (bit=1)
28
29
  //Break/dead time register (TIMx_BDTR)
30
  TIM1->BDTR   |=  TIM_BDTR_MOE;    //Master output enable - required
31
32
  //Capture/compare mode register 1 (TIMx_CCMR1)
33
  TIM1->CCMR1  &= ~TIM_CCMR1_CC1S;  //CC1 output conf
34
  TIM1->CCMR1  &= ~TIM_CCMR1_CC2S;  //CC2 output conf
35
  TIM1->CCMR1  |=  TIM_CCMR1_OC1M;  //CC1 output mode PWM2
36
  TIM1->CCMR1  |=  TIM_CCMR1_OC2M;  //CC2 output mode PWM2
37
38
  //Capture/compare mode register 2 (TIMx_CCMR2)
39
  TIM1->CCMR2  &= ~TIM_CCMR2_CC3S;  //CC3 output conf
40
  TIM1->CCMR2  |=  TIM_CCMR2_OC3M;  //CC3 output mode PWM2
41
42
43
  //This should output a 50/50 signal on each pin
44
  TIM1->CCR1    = 32767;
45
  TIM1->CCR2    = 32767;
46
  TIM1->CCR3    = 32767;

von STM Apprentice (Gast)


Lesenswert?

S. D. schrieb:
> Kann mir da bitte jemand helfen?

Clock der Ausgänge (Ports) enabled?
Ausgänge auf alternate Function gesetzt?

Wenn beide Antworten mit "ja, natürlich" gegeben werden, warum
zeigst du es nicht. Du solltest alle relevanten Sources zeigen
damit hier vom Verständnis nichts schief geht. Sonst müssen
die geneigten Leser/Zuhörer dauernd nachfragen. Und deine
Antworten tendieren vielleicht zur Salamitaktik.

von S. D. (der_nachtfuchs)


Lesenswert?

"Ja, natürlich" könnte ich da nicht antworten, aber meine Ausgänge sehen 
folgendermaßen aus:
1
  RCC->AHB1ENR  |=  (1<<0);          // Enable the GPIOA clock
2
  RCC->AHB1ENR  |=  (1<<1);          // Enable the GPIOB clock
3
4
  //GPIO register map, page 163
5
  GPIOA->MODER  |=  (1<<15);         // pin PA7  (bits 15:14) as alternate function (10)
6
  GPIOA->MODER  |=  (1<<17);         // pin PA8  (bits 17:16) as alternate function (10)
7
  GPIOA->MODER  |=  (1<<19);         // pin PA9  (bits 19:18) as alternate function (10)
8
  GPIOA->MODER  |=  (1<<21);         // pin PA10 (bits 21:20) as alternate function (10)
9
  GPIOB->MODER  |=  (1<<1);          // pin PB0  (bits  1: 0) as alternate function (10)
10
  GPIOB->MODER  |=  (1<<3);          // pin PB1  (bits  3: 2) as alternate function (10)
11
  
12
  GPIOA->OTYPER &= ~(1<<7);          // bit  7=0 --> Output push pull
13
  GPIOA->OTYPER &= ~(1<<8);          // bit  8=0 --> Output push pull
14
  GPIOA->OTYPER &= ~(1<<9);          // bit  9=0 --> Output push pull
15
  GPIOA->OTYPER &= ~(1<<10);         // bit 10=0 --> Output push pull
16
  GPIOB->OTYPER &= ~(1<<0);          // bit  0=0 --> Output push pull
17
  GPIOB->OTYPER &= ~(1<<1);          // bit  1=0 --> Output push pull

Wie ich den Ausgängen die alternativen Funktionen zuweisen muss, weiß 
ich leider nicht. Also wie ich AF2 zuweise, meine ich...

von STM Apprentice (Gast)


Angehängte Dateien:

Lesenswert?

S. D. schrieb:
> (1<<19)

Sorry aber mit den Magic Numbers (Beispiel: (1<<19) ) wird dir
hier kaum jemand weiterhelfen wollen. Ich jedenfalls nicht.
Bitte verwende die in den Header-Dateien vorgesehenen Defines
damit man deine Gedankengänge einigermassen leicht nachvollziehen
kann. Und wenn du in den Header-Dateien schon nachschaust könntest
du auch gleich die deklarierten Setz-Funktionen nutzen um deine
Initialisierung übersichtlicher und leichter verständlich zu
gestalten. Auch die Fehleranfälligkeit sinkt da dort auch
Gültigkeites-Bereiche für die Parameter der Setz-Funktionen
angegeben sind.

Im Übrigen kann man nicht jeden Pin für jeden Timer nutzen.
Wenn du CubeMX nutzt kannst du leicht sehen was geht und hast
vielleicht eine bessere Übersicht. Man muss ja CubeMX nicht zum
Code-Generieren nutzen, aber auch das wird einem erleichtert
sodass man sich um höhere Aufgaben schneller kümmern kann.

von A. B. (Gast)


Lesenswert?

S. D. schrieb:

> Wie ich den Ausgängen die alternativen Funktionen zuweisen muss, weiß
> ich leider nicht. Also wie ich AF2 zuweise, meine ich...

Z. B. so:
Für den jeweiligen Pin so etwas:
#define T1_CH1_Pin 5
#define T1_CH1_GPIO_Port GPIOA

dann
#define GPIO_ALT(pin, alt) \
pin ## _GPIO_Port->AFR[pin ## _Pin >> 3] = (pin ## _GPIO_Port->AFR[pin 
## _Pin >> 3] &  ~(0xFU << ((pin ## _Pin & 0x7U) << 2))) | (GPIO_ ## alt 
<< ((pin ## _Pin & 0x7U) << 2));

und dann ähnlich zu:
GPIO_ALT(T1_CH1, AF2_TIM1)

Was da statt AF2_TIM1 eventuell gebraucht wird, findet man im Datenblatt 
zusammen mit der passenden Header-Datei, etwa "stm32f0xx_hal_gpio_ex.h" 
für den F0.

von S. D. (der_nachtfuchs)


Angehängte Dateien:

Lesenswert?

Vielen Dank schonmal! Dann werde ich mal die Header-Dateien durchlesen.

STM Apprentice schrieb:
> Im Übrigen kann man nicht jeden Pin für jeden Timer nutzen.

Ich hab die Pins von dieser Grafik aus dem CubeMX.

von S. D. (der_nachtfuchs)


Lesenswert?

A. B. schrieb:
> Z. B. so:
> Für den jeweiligen Pin so etwas:
> #define T1_CH1_Pin 5
> #define T1_CH1_GPIO_Port GPIOA
> [...]

Danke schön! Das probiere ich mal und lese mir die Header durch.

von STM Apprentice (Gast)


Lesenswert?

A. B. schrieb:
> dann
> #define GPIO_ALT(pin, alt) \
> pin ## _GPIO_Port->AFR[pin ## _Pin >> 3] = (pin ## _GPIO_Port->AFR[pin
> ## _Pin >> 3] &  ~(0xFU << ((pin ## _Pin & 0x7U) << 2))) | (GPIO_ ## alt
> << ((pin ## _Pin & 0x7U) << 2));

Leicht verständlich und übersichtlich, besser kann man es
nicht machen.

von STM Apprentice (Gast)


Angehängte Dateien:

Lesenswert?

Und so unübersichtlich wie im Anhang sieht es aus wenn man
normale LL Funktionen, also ohne HAL, nutzt um einen Portpin
auf einen Timer zu mappen.

Da nehm ich doch lieber ein vierzeiliges Makro wie im Beispiel
zwei Beiträge weiter oben das jeder sofort versteht. Programmieren
kann doch so eingängig sein.

von S. D. (der_nachtfuchs)


Lesenswert?

Ich hab's ja dank eurer Hilfe geschafft ;)

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.