Forum: Mikrocontroller und Digitale Elektronik STM32F103: Timer 2 als PWM Generator läuft nicht


von Christian J. (Gast)


Angehängte Dateien:

Lesenswert?

Mahlzeit,

vielleicht nur eine Kleinigkeit aber aus Pin PB11 kommt zum Verrecken 
kein Signal raus.

Ziel: 40 Khz Frequenz mit 50% Duty Cyle
Takt: 56 Mhz
Hardware: Timer 2, Compare Kanal 4.

Reload berechnet zu 56Mhz / 40Khz = 1400 Takte. Also geht ohne 
Prescaler.

PB11 ist Remapped, ok, man muss was umschalten. Gibt da die 
Möglichkeiten

GPIO_PartialRemap1_TIM2     : TIM2 Partial1 Alternate Function mapping
GPIO_PartialRemap2_TIM2     : TIM2 Partial2 Alternate Function mapping
GPIO_FullRemap_TIM2         : TIM2 Full Alternate Function mapping

Alles durchprobiert, auf dem Oszi ist nichts zu sehen? Habe ich was 
übersehen? Doppelt gemoppelt?

Nebenbei: Mein APB2 Bus läuft mit 1/2 HSE Sysclock, also 28Mhz. 
Seltsamerweise aber laufen die Timer mit SystemCoreClock. Dahinter bin 
ich auch noch nicht gekommen, warum das so ist. CLK_DIV sind nämlich nur 
Glitch-Filter. Aber woher der TIM2 seinen Clock kriegt... ?


1
/* Stellt auf Timer 2, Ausgang PB11, TIM2_CH4 ein 40khz Signal bereit */
2
3
uint16_t reload;
4
void Init_Timer2_PWM()
5
{
6
    #define FREQ       40000      // 40 Khz für IRED, 20khz für Überlauf;
7
8
    TIM_TimeBaseInitTypeDef TimerBaseInitStructure;
9
    GPIO_InitTypeDef  GPIO_InitStructure;
10
11
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);         // Timer mit Clock versorgen
12
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
13
14
    /* Port B, Pin 11 */
15
    GPIO_StructInit(&GPIO_InitStructure);
16
    GPIO_InitStructure.GPIO_Pin   = GPIO_PinSource11;
17
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
18
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;
19
    GPIO_Init(GPIOB, &GPIO_InitStructure);
20
21
    reload = (SystemCoreClock / FREQ);
22
23
    GPIO_PinRemapConfig(GPIO_FullRemap_TIM2,ENABLE);
24
25
    TIM_TimeBaseStructInit(&TimerBaseInitStructure);
26
    TimerBaseInitStructure.TIM_Prescaler         = 0;
27
    TimerBaseInitStructure.TIM_Period            = reload;
28
    TimerBaseInitStructure.TIM_ClockDivision     = TIM_CKD_DIV1;
29
    TimerBaseInitStructure.TIM_CounterMode       = TIM_CounterMode_Up;
30
    TIM_TimeBaseInit(TIM2, &TimerBaseInitStructure);
31
32
    /* PWM Modul einstellen auf 50% */
33
34
    TIM_OCInitTypeDef  TIM_OCInitStructure;
35
    TIM_OCStructInit (&TIM_OCInitStructure);
36
37
    TIM_OCInitStructure.TIM_OCMode               = TIM_OCMode_PWM1;
38
    TIM_OCInitStructure.TIM_OutputState          = TIM_OutputState_Enable;
39
    TIM_OCInitStructure.TIM_Pulse                = 0;
40
    TIM_OCInitStructure.TIM_OCPolarity           = TIM_OCPolarity_Low;
41
    TIM_OC4Init (TIM2, &TIM_OCInitStructure);
42
43
    TIM_SelectOCxM(TIM2,TIM_Channel_4,TIM_OCMode_PWM1);
44
    TIM_CCxCmd(TIM2,TIM_Channel_4,ENABLE);
45
46
    TIM_SetCompare4(TIM2,reload/2);
47
    TIM_Cmd (TIM2, ENABLE);
48
}

von Christian J. (Gast)


Angehängte Dateien:

Lesenswert?

Keiner der STM32 Päpste da? :-(

Ich habe ein Signal, GPIO_PinSource_11 mit GPIO_Pin_11 vertauscht. Und 
noch einiges dazu gesucht aus dem Netz aber verstehen warum es läuft tue 
ich leider nicht :-((

Nachdem es lief strich ich nach und nach Befehle weg und es klappt immer 
noch, auch ohne das unten auskommentierte. Was mich ziemlich wundert, 
denn die API wurde ja sinnvoll geschrieben.

Der ist notwendig:
TIM_CtrlPWMOutputs(TIM2,ENABLE);

die nicht:
TIM_SelectOCxM(TIM2,TIM_Channel_4,TIM_OCMode_PWM1);
TIM_CCxCmd(TIM2,TIM_Channel_4,TIM_CCx_Enable);
TIM_OC4PreloadConfig(TIM2, TIM_OCPreload_Enable);
TIM_ARRPreloadConfig(TIM2,ENABLE);

Wenigstens der TIM_CCxCmd sollte nötig sein... bei ARM wird ja alles 
irgendwie enabled.

Rätsel über Rätsel.....
1
uint16_t reload;
2
void Init_Timer2_PWM()
3
{
4
    #define FREQ       40000      // 40 Khz für IRED, 20khz für Überlauf;
5
6
    TIM_TimeBaseInitTypeDef TimerBaseInitStructure;
7
    GPIO_InitTypeDef  GPIO_InitStructure;
8
9
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);    /* Timer 2 mit Clock versorgen */
10
    GPIO_PinRemapConfig(GPIO_PartialRemap2_TIM2,ENABLE);        /* Timer 2 remappen */
11
12
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
13
14
    /* Port B, Pin 11 */
15
    GPIO_StructInit(&GPIO_InitStructure);
16
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_11;
17
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
18
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;
19
    GPIO_Init(GPIOB, &GPIO_InitStructure);
20
21
    reload = (SystemCoreClock / FREQ);
22
23
    TIM_TimeBaseStructInit(&TimerBaseInitStructure);
24
    TimerBaseInitStructure.TIM_Prescaler         = 0;
25
    TimerBaseInitStructure.TIM_Period            = reload;
26
    TimerBaseInitStructure.TIM_ClockDivision     = TIM_CKD_DIV1;
27
    TimerBaseInitStructure.TIM_CounterMode       = TIM_CounterMode_Up;
28
    TIM_TimeBaseInit(TIM2, &TimerBaseInitStructure);
29
30
    /* PWM Modul einstellen auf 50% */
31
    TIM_OCInitTypeDef  TIM_OCInitStructure;
32
33
    TIM_OCStructInit (&TIM_OCInitStructure);
34
    TIM_OCInitStructure.TIM_OCMode               = TIM_OCMode_PWM1;
35
    TIM_OCInitStructure.TIM_OutputState          = TIM_OutputState_Enable;
36
    TIM_OCInitStructure.TIM_Pulse                = reload / 2;
37
    TIM_OCInitStructure.TIM_OCPolarity           = TIM_OCPolarity_Low;
38
    TIM_OC4Init (TIM2, &TIM_OCInitStructure);
39
40
    TIM_SetCompare4(TIM2,reload/2);
41
42
    /* Kanal 4 aktivieren */
43
    // TIM_SelectOCxM(TIM2,TIM_Channel_4,TIM_OCMode_PWM1);
44
    // TIM_CCxCmd(TIM2,TIM_Channel_4,TIM_CCx_Enable);
45
46
    // TIM_OC4PreloadConfig(TIM2, TIM_OCPreload_Enable);   /* Reload aktivieren */
47
    // TIM_ARRPreloadConfig(TIM2,ENABLE);
48
    TIM_CtrlPWMOutputs(TIM2,ENABLE);
49
50
    TIM_Cmd (TIM2, ENABLE);
51
}

von Pieter (Gast)


Lesenswert?

ich bin nicht der Papst, aber TIM2 bekommt seinen Clock über APB1.

von Christian J. (Gast)


Angehängte Dateien:

Lesenswert?

Pieter schrieb:
> ich bin nicht der Papst, aber TIM2 bekommt seinen Clock über APB1.

Der taktet nachweislich hier bei mir mit SystemCoreClock  HCLK  AHB = 
(56 Mhz) und nicht mit 28Mhz, da ich beide P-Busse auf 1/2 HCLOCK 
getaktet habe, weil APB1 = HCLK mir die SPI zu schnell macht, da komme 
ich mit /64 nicht unter 400khz. APB2 ist ja von haus aus nur max. 36 
Mhz.

Sonst würden ja auch keine 40khz bei rauskommen, weil ich mit 56Mhz / 
40khz reloade.

Eines der großen Rätsel dieser Zeit....

Zum Rest... dieser IniStruct scheint eine Sache zu sein, manches daraus 
lässt sich auch einzeln aufrufen da sie alle auf TIMx-CER wirken. Im 
Struct setzt man nur alles auf einmal und ist fertig.

Ich hoffe mal, dass ich die PWM so richtig abschalte, man kann auch den 
Timer anhalten, PWM abklemmen usw. Aber ich muss bei Idle auf Low 
liegen, sonst brennt mir die IRED durch.
1
/* Schaltet die 40khz ein oder aus */
2
void IRED_40khz(FunctionalState status)
3
{
4
    if (status)
5
        TIM_CCxCmd(TIM2,TIM_Channel_4,TIM_CCx_Enable);
6
    else
7
        TIM_CCxCmd(TIM2,TIM_Channel_4,TIM_CCx_Enable);
8
}

von Pieter (Gast)


Angehängte Dateien:

Lesenswert?

>>Der taktet nachweislich hier bei mir mit SystemCoreClock  HCLK  AHB =
>>(56 Mhz) und nicht mit 28Mhz, da ich beide P-Busse auf 1/2 HCLOCK
>>getaktet habe, weil APB1 = HCLK mir die SPI zu schnell macht, da komme

der macht genau das was er soll!
Anbei ein Bild aus der Doku.
Wenn Du APB1 :2 wegen SPI teilst, wird der Takt für TIMx wieder 
verdoppelt.

In Pascal habe ich ein PWM an T4C1 so realisiert:

Procedure Init_Tim4( P, A : Word );

Begin

// Timer4_CH1 für die Frequenz F ( aus Clock/ P ) an PB6 einrichten

  RCC_APB2ENR.IOPBEN = 1;        // GPIOB Clock on

  GPIOB_CRL          = ( GPIOB_CRL and 0xF0FFFFFF ) or 0x0B000000; // 
PB6 as AF-PP

  RCC_APB1ENR.TIM4EN = 1;        // Enable clock gating for timer4

  TIM4_CR1.B0        = 0;        // Bit0, CEN, Disable timer

  TIM4_PSC           = P-1;

  TIM4_ARR           = A-1;

  TIM4_CCR1          = ( TIM4_ARR +1 ) / 2;

  TIM4_CCER          = 3;        // OC1 signal is output on the
corresponding output pin. PB6

  TIM4_CCMR1_Output  = 0x6C;

  TIM4_CR1.B0        = 1;        // Bit0, CEN, Enable timer

End;

von xyz (Gast)


Lesenswert?

Man muss uebrigens nicht unbedingt Pascal benutzen, um die
Register so zu initialisieren. Das geht mit dem Headerfile
ganz ohne merkwuerdige Initstructs auch in C.
Mit dem Referenzmanual ist das Initialisieren durchaus
straightforward und allerschnellstens erledigt.

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


Lesenswert?

Christian J. schrieb:
> /* Schaltet die 40khz ein oder aus */
> void IRED_40khz(FunctionalState status)
> {
>     if (status)
>         TIM_CCxCmd(TIM2,TIM_Channel_4,TIM_CCx_Enable);
>     else
>         TIM_CCxCmd(TIM2,TIM_Channel_4,TIM_CCx_Enable);
> }
Beide Funktionen machen hier genau das gleiche - status ist wurscht. Das 
ist sicher nicht, was du möchtest.

von Christian J. (Gast)


Angehängte Dateien:

Lesenswert?

Matthias S. schrieb:
> Beide Funktionen machen hier genau das gleiche - status ist wurscht. Das
> ist sicher nicht, was du möchtest.

Solche Sachen sind schnell behoben. Und ich bin absolut kein Fan davon 
irgendwelche Register mit Zahlen zu beschreiben, wenn es eine API gibt, 
die zehntausendfach verwendet und bewährt ist und wo es keiner 
Kommentare bedarf um zu verstehen, was da gemacht wird.

Der Hinweis mit dem APB1 Bus war gut, das hatte ich auch echt übersehen!
Faszinierend an der Reference ist, dass man sich tot sucht, um heraus zu 
finden, wieviele Timer der F103 überhaupt hat, da zig Varianten in einem 
Manual verquickt sind.

So muss es ausschauen, dann schnubbeln die Bytes auch durch die Luft in 
meinen Debug Empfänger rein. Sieht cool aus mit einem Handy als Kamera 
und wie zu Modemzeiten mit 2400 baud und einem Terminal-Programm.
1
#ifdef IRED_TRANSMIT
2
#define BURST   410     /* 410us Burst Länge */
3
void Init_Timer2_PWM()
4
{
5
    #define FREQ       40000      // 40 Khz für IRED, 20khz für Überlauf;
6
7
    TIM_TimeBaseInitTypeDef TimerBaseInitStructure;
8
    TIM_OCInitTypeDef  TIM_OCInitStructure;
9
    GPIO_InitTypeDef  GPIO_InitStructure;
10
11
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);    /* Timer 2 mit Clock versorgen */
12
    GPIO_PinRemapConfig(GPIO_PartialRemap2_TIM2,ENABLE);    /* Timer 2 remappen */
13
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
14
15
    /* Port B, Pin 11 */
16
    GPIO_StructInit(&GPIO_InitStructure);
17
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_11;
18
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
19
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;
20
    GPIO_Init(GPIOB, &GPIO_InitStructure);
21
22
    TIM_TimeBaseStructInit(&TimerBaseInitStructure);
23
    TimerBaseInitStructure.TIM_Prescaler         = 0;
24
    TimerBaseInitStructure.TIM_Period            = SystemCoreClock/FREQ;
25
    TimerBaseInitStructure.TIM_ClockDivision     = TIM_CKD_DIV1;
26
    TimerBaseInitStructure.TIM_CounterMode       = TIM_CounterMode_Up;
27
    TIM_TimeBaseInit(TIM2, &TimerBaseInitStructure);
28
29
    /* PWM Modul einstellen auf 50% */
30
    TIM_OCStructInit (&TIM_OCInitStructure);
31
    TIM_OCInitStructure.TIM_OCMode               = TIM_OCMode_PWM1;
32
    TIM_OCInitStructure.TIM_Pulse                = (SystemCoreClock/FREQ) / 2;
33
    TIM_OCInitStructure.TIM_OCPolarity           = TIM_OCPolarity_Low;
34
    TIM_OCInitStructure.TIM_OutputState          = TIM_OutputState_Disable;
35
    TIM_OC4Init (TIM2, &TIM_OCInitStructure);
36
37
    /* Maschine starten! */
38
    TIM_CtrlPWMOutputs(TIM2,ENABLE);
39
    TIM_Cmd(TIM2, ENABLE);
40
}
41
42
#define CREATE_BURST() { TIM_CCxCmd(TIM2,TIM_Channel_4,ENABLE);  \
43
                       delayUS_DWT(BURST);                     \
44
                       TIM_CCxCmd(TIM2,TIM_Channel_4,DISABLE); }
45
46
/* Sende ein IR Byte im RS232 Mode, 8N1 */
47
void ired_putc(uint8_t db)
48
{
49
  /* Startbit senden */
50
    CREATE_BURST();
51
52
  /* Datenbits hinterher */
53
    uint8_t mask = 0x01;
54
  for (uint8_t i = 0; i < 8; i++)  {
55
    if (db & mask)    // Bit = 1;
56
      delayUS_DWT(BURST);
57
    else          // Bit = 0;
58
            CREATE_BURST();
59
60
    mask = (mask << 1);
61
  }
62
63
  delayUS_DWT(BURST);    // Stop Bit
64
  delayUS_DWT(BURST);    // Delay
65
    TIM_CCxCmd(TIM2,TIM_Channel_4,DISABLE);
66
}
67
#endif

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


Lesenswert?

Christian J. schrieb:
> Solche Sachen sind schnell behoben.

Ich benutze ja auch SPL und kein Cube Kram, aber du solltest einfach ein 
wenig sorgfältiger coden. Der Syntaxfehler im ersten Posting sollte dir 
eigentlich sofort aufgefallen sein. So gings mir jedenfalls.

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.