Forum: Mikrocontroller und Digitale Elektronik STM32 - Servoansteuerung


von Bernd (Gast)


Lesenswert?

Hallo Zusammen,

die Timer im STM32 sind sehr komplex und unverständlich,
daher meine, hat schon mal jemand mit einem STM32 eine
Servoansteuerung gemacht?

Danke & Viele Grüße
Bernd

von Bernd (Gast)


Lesenswert?

Hallo Zusammen,

habe leider nichts gefunden, bleibt mir garnichts anderes übrig,
als es selbst zu versuchen. Vielleich kann mir ja jemand helfen.
Ich habe gerade ein MiniScript gebaut
1
volatile s32 myTimerCounter = 0;
2
3
void TIM2_IRQHandler(void) {
4
  myTimerCounter++;
5
}
6
7
int main(void) {
8
9
  // Initalisieren, STM32_Init, GPIO, UART ...
10
11
  // TIM2CLK = 32 MHz, Prescaler = 100, TIM2 counter clock = 32 KHz
12
  TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
13
  //TIM_TimeBaseStructure.TIM_Period = 0;
14
  TIM_TimeBaseStructure.TIM_Prescaler = 100;
15
  TIM_TimeBaseStructure.TIM_ClockDivision = 0;
16
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
17
  TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
18
  TIM_Cmd(TIM2, ENABLE);
19
20
  while (1) {
21
    Delay(250);
22
    LED2_OFF();
23
    Delay(250);
24
    LED2_ON();
25
26
    rprintf("Stand2: %d\r", myTimerCounter);
27
    }
28
}
29
30
// Ergebnis:
31
// Stand2: 0
32
// Stand2: 0
33
// Stand2: 0
34
// Stand2: 0

Da wird der Interupt leider nicht angesprochen. Kann mir vielleicht
jemand sagen wieso?

(Oder ggf. was die Entwickler sich mit TIM_TimeBaseStructure.TIM_Period 
= 0; gedacht haben? In AVR gibts das leider nicht, da läuft zumindest
Servoansteuerung, wenn auch sehr langsam... :))

Wenn jemand etwas weiß, wäre klasse.

Danke & Viele Grüße
Bernd

von (prx) A. K. (prx)


Lesenswert?

An der Lib hatte ich kein rechte Freude, weil man sowieso in den 
Quellcode reinschauen muss um zu verstehen was der Kram soll. Also kann 
ich auch gleich an die Reigister ran, die sind ohnehin besser 
dokumentiert.

Ein paar der Parameter ergeben nur bei den erweiterten Timern Sinn.

32MHz / 32KHz gibt 1000, nicht 100.

von (prx) A. K. (prx)


Lesenswert?

Du solltest dem Timer den Tipp geben, dass er Interrupts erzeugen soll. 
In deinem Fall ist das wohl der Update-Interrupt.

Du solltest dem NVIC dann noch den Tipp geben, dass er die auch 
durchlassen soll und eine passable Priorität mitteilen.

von (prx) A. K. (prx)


Lesenswert?

Wie wird ein Servo angesteuert? Vor langer Zeit was das mal ein 
PWM-Signal, mit Tastverhältnis=Position. Das jedenfalls ist kein 
Problem, wobei dafür kein Interrupt verwendet werden sollte, sondern 
eben PWM.

von (prx) A. K. (prx)


Lesenswert?

Der Timer wird übrigens ohne Takt nicht funktionieren. Von Haus aus ist 
beim STM32 alle Peripherie abgeschaltet (=>RCC).

Und ist der Takt wirklich 32MHz? Anfangs kommt der Bursche nämlich mit 
dem internen R/C-Oszillator hoch und du darfst erst einmal den Takt 
konfigurieren.

Vielleicht solltest du mal in Beispielcode reinschauen. Ein STM32 ist 
ein klitzekleines Bischen komplexer als ein AVR. Einfach loslegen ist 
nicht drin, das haben Controller dieser Grössenordnung alle so an sich.

von Bernd (Gast)


Lesenswert?

Hallo A.K,

vielen Dank. Servoansteuerung läuft bereits auf dem AVR (nur eben
viel zu langsam - ruckelte ziehmlich - seidenweich ist anders). PWM 
hatte
ich auch schon gedacht, nur ist die Frage ob das auf 6 Kanälen
gleichzeitig geht. (???)

Für die Ausgabe von 6 ServoImpulsen z.B.
1
servo_out[0] = 1500; // PA8
2
servo_out[1] = 2000; // PA11
3
servo_out[2] = 2500; // PA12
4
servo_out[3] = 3000; // PA13
5
servo_out[4] = 3500; // PA14
6
servo_out[5] = 4000; // PA15

bräuchte man

- Einen Timer mit Output Compare
- PA8 wird eingeschaltet
- Compare = servo_out[i];
- Wenn der Compare abgelaufen ist, wird PA8 ausgeschaltet.
- Der Timer wird alle 21ms  (jedenfalls > 21ms)  neu gestartet (?) und 
Pin wird erhöht (nächster PA11)

Müßte dann ergeben (ein Zeichen (|,.,_) entspricht 500ms)
1
PA8  |.|_____________________________________|.|_____________________________________
2
PA11 __|..|____________________________________|..|__________________________________
3
PA12 _____|...|___________________________________|...|______________________________
4
PA13 _________|....|__________________________________|....|_________________________
5
PA14 ______________|.....|_________________________________|.....|___________________
6
PA15 ____________________|......|________________________________|......|____________

In der Zwischenzeit funktioniert auch mein Timer
1
  // TIM2CLK = 32 MHz
2
  TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
3
4
  // Specifies the period value to be loaded into the active
5
  // Auto-Reload Register at the next update event. This parameter
6
  // must be a number between 0x0000 and 0xFFFF.
7
  TIM_TimeBaseStructure.TIM_Period = 65535;
8
  TIM_TimeBaseStructure.TIM_Prescaler = 0;
9
  TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
10
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
11
  TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
12
13
  TIM_ClearFlag(TIM2, TIM_FLAG_Update);
14
  TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
15
  TIM_ClearFlag(TIM2, TIM_FLAG_Update); /* clear int flag */
16
17
  TIM_Cmd(TIM2, ENABLE);
18
19
  while (1) {
20
    // Zusammen 1 Sekunde...
21
    Delay(250);
22
    LED2_OFF();
23
    Delay(250);
24
    LED2_ON();
25
    Delay(250);
26
    LED2_OFF();
27
    Delay(250);
28
    LED2_ON();
29
30
    rprintf("Stand2: %d\r", myTimerCounter);
31
    }

Allerdings nur mit Periode.... (vom AVR kenn ich das überhaupt nicht).
Das Ergebnis ist jedenfalls
1
Stand2: 13214    
2
Stand2: 13703  Differenz 489  
3
Stand2: 14193  Differenz 490
4
Stand2: 14682  Differenz 489
5
Stand2: 15172  Differenz 490
6
Stand2: 15661  Differenz 489
7
Stand2: 16151  Differenz 490
8
Stand2: 16640  Differenz 489
9
Stand2: 17130  Differenz 490
10
Stand2: 17619  Differenz 489

Weiß jemand wie ich das anpassen kann, so das der auch wirklich 20ms
braucht?

Vielen Dank für die Hilfe.

Viele Grüße
Bernd

von Bernd (Gast)


Lesenswert?

achso, noch der zugehörige Interupt
1
volatile s32 myTimerCounter = 0;
2
3
void TIM2_IRQHandler (void) {
4
  TIM_ClearFlag(TIM2, TIM_FLAG_Update);
5
  myTimerCounter++;
6
}

von (prx) A. K. (prx)


Lesenswert?

Bernd schrieb:

> ich auch schon gedacht, nur ist die Frage ob das auf 6 Kanälen
> gleichzeitig geht. (???)

Bei dieser Art von Problembeschreibung brauchst du jemanden, der sowohl 
STM32 kennt als auch schon ganz genau weiss was dein(e) Servo(s) 
benötig(t/en). Was den Helferkreis etwas einschränkt.

Es könnte vielleicht nützlich sein, wenn du bei einer der beiden 
Wissensbereichen etwas genauer nachhilfst.

von Bernd (Gast)


Lesenswert?

ok, ich hab's Periode funktioniert nicht anders als ein Vorteile
1
  TIM_TimeBaseStructure.TIM_Period = 32000;
2
  TIM_TimeBaseStructure.TIM_Prescaler = 19;
3
[c]
4
5
gibt 50khz oder 20ms.
6
7
[c]
8
32000000 (mhz) / 32000 Period =  1000 / 20 Prescaler = 50

Jetzt kommt das eigentlich spannende, der Output Compare :)
Mal schaun.

von Bernd (Gast)


Lesenswert?

Hallo A.K,
1
Bei dieser Art von Problembeschreibung brauchst du jemanden, der sowohl
2
STM32 kennt als auch schon ganz genau weiss was dein(e) Servo(s)
3
benötig(t/en). Was den Helferkreis etwas einschränkt.

Wenn die Servos alle 20ms einen Impuls bekommen wie oben laufen die
seidenweich, besser geht es dann nicht. Der Impuls selbst ist dann in 
etwa
1,5 bis 2,5ms lang, und wird von dem Servo als Stellvorgabe 
interpretiert.
1,5ms = Stellung ganz links, 2,5ms = Stellung ganz rechts.

Viele Grüße
Bernd

von (prx) A. K. (prx)


Lesenswert?

Dann wirst du mit 4 PWM-Kanälen pro Timer und wohl mindestens 4 Timern 
auch noch mehr als 6 Kanäle problemlos bedienen können.

Und wirst, wenn das nötig sein sollte, die Kanäle auch allesamt synchron 
fahren können, weil man die beiden dafür verwendeten Timer über deren 
Eventsystem exakt gleichzeitig starten kann.

Ich habe PWM damit schon gemacht, nur wird dir aus dem oben skizzierten 
Grund mein Code nicht helfen. Hoffentlich hast du ein Oszi oder 
Logikanalysator um zu sehen was rauskommt (Soundkarten-Scope tut es 
auch).

von (prx) A. K. (prx)


Lesenswert?

Bernd schrieb:

> vielen Dank. Servoansteuerung läuft bereits auf dem AVR (nur eben
> viel zu langsam - ruckelte ziehmlich - seidenweich ist anders).

Software-PWM? Neuere AVRs haben zwar 6 Hardware-PWMs, allerdings 
teilweise mit 8 Bit Auflösung, was bei einem Delta von 1ms in einer 
Periode von 20ms, also bestenfalls 256/20=12 Stufen, natürlich ziemlich 
grob wird.

Diesen die Auflösung reduzierenden Effekt kriegt du beim STM32 natürlich 
auch, nur eben hier mit beispielsweise 32000/20=1600 Stufen. Was auch 
heisst: Timer 64000 und Prescaler 25 ist trotzdem besser.

von Bernd (Gast)


Lesenswert?

Hallo,

vielen Dank, ich versuch jetzt erstmal den OutputCompare anzubinden.
PWM ggf. später, wenn es nicht gut läuft. Gleichzeitiges Ansprechen
aller Servos ist auch nicht so gut für die Stromversorgung (Modellbau 
BEC).

Viele Grüße
Bernd

von Bernd (Gast)


Lesenswert?

Hallo Zusammen,

ich versuche gerade zu meinem 20ms Counter zusätzlich einen Output
Compare einzurichten. Was mir lediglich noch fehlt, einfach ein
Zeitlimit innerhalb der 20ms zu setzen, um dann die entsprechenden
Pins an und auszuschalten.
1
volatile s32 myTimerCounter1 = 0;
2
void TIM2_IRQHandler(void) {
3
4
  // Also cleared the wrong interrupt flag in the ISR
5
  TIM_ClearFlag(TIM2, TIM_FLAG_Update);
6
7
  // TIM_GetITStatus(TIM2, TIM_IT_CC1) != RESET
8
  TIM_ClearITPendingBit(TIM2, TIM_IT_CC1); // Clear the interrupt flag
9
10
  TIM2->CCR1 = 20;
11
12
  myTimerCounter1++;
13
14
}
15
16
int main(void) {
17
18
  // ++ Initalisieren
19
20
  // Description : Initialize the Timer2 channel-1.
21
  // TIM2CLK = 36 MHz, counter clock = 20 ms
22
  TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
23
  TIM_OCInitTypeDef TIM_OCInitStructure;
24
25
  TIM_TimeBaseStructure.TIM_Period = 32000; // auto reaload register
26
  TIM_TimeBaseStructure.TIM_Prescaler = 0;
27
  TIM_TimeBaseStructure.TIM_ClockDivision = 0;
28
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
29
  TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
30
31
  TIM_PrescalerConfig(TIM2, 19, TIM_PSCReloadMode_Immediate);
32
33
  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Timing ;
34
  TIM_OCInitStructure.TIM_OutputState = TIM_Channel_1;
35
  TIM_OCInitStructure.TIM_Pulse = 0;
36
  TIM_OC1Init(TIM2, &TIM_OCInitStructure);
37
  TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Disable);
38
39
  TIM_ClearFlag(TIM2, TIM_FLAG_Update);
40
  TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
41
  TIM_ClearFlag(TIM2, TIM_FLAG_Update);
42
43
  TIM_Cmd(TIM2, ENABLE);
44
45
  while (1) {
46
    // Zusammen 1 Sekunde...
47
    Delay(250);
48
    LED2_OFF();
49
    Delay(250);
50
    LED2_ON();
51
    Delay(250);
52
    LED2_OFF();
53
    Delay(250);
54
    LED2_ON();
55
56
    rprintf("Result: %d\r", myTimerCounter1);
57
    }
58
}

Mit
1
TIM2->CCR1 = 20;

Versuche ich den Counter fürs Compare auf 20 zusetzen, was eigentlich
den Effekt haben müßte, das der Interrupt häufiger aufgerufen wird.

Allerdings bleibt die 20ms Taktung exakt gleich...

Über Hilfe würde ich mich sehr freuen, Timer und STM32 sind echt
schwierig. Im Web finde fast nur Leute mit ähnlichen Problemen,
ohne Lösung.

Wäre klasse wenn man das einmal zum Nachlesen vormachen könnten.

Super, Vielen Dank & Viele Grüße
Bernd

von (prx) A. K. (prx)


Lesenswert?

Soll ich meinen Text oben nochmal posten? Ein Interrupt den man nicht 
einschaltet interruptet nicht. Den Update-Int kriegst du jetzt, den 
CC-Int nicht.

von (prx) A. K. (prx)


Lesenswert?

Bernd schrieb:

> Wäre klasse wenn man das einmal zum Nachlesen vormachen könnten.

Das hat ST freundlicherweise schon getan. In den Beispielen zur 
Peripherie-Lib. Hast du da mal reingesehen? 16 Beispiele für den Umgang 
mit Timern.

von Bernd (Gast)


Lesenswert?

Hallo A.K.,

vielen Dank, ja, hab ich natürlich schon durchforstet, ist aber alles
ohne Interruptroutine, das einzige was jetzt da noch fehlen könnte
im Direkten Vergleich wäre
1
TIM_ARRPreloadConfig(TIM2, ENABLE);

Meintest Du das mit "CC-Int" (Compare Interrupt)?

Danke & Viele Grüße
Bernd

von Bernd (Gast)


Lesenswert?

Jedenfalls, vor lauter Verzweifelung, hab ich schonmal versucht das ohne
die Lib hinzubekommen.

1
     void TIM2_IRQHandler (void) {
2
  // Interrupt Flag muß per Software gelöscht werden
3
  TIM_ClearFlag(TIM2, TIM_FLAG_Update);
4
  myTimerCounter1++;
5
     }
6
7
     // Counter Max. der ins Auto-Load Register geschrieben wird
8
     // Läuft bis 24287, dann 0
9
  TIM2->ARR   = 24287;
10
  // Prescaler, Clock Teiler, Counter = Clock/Psc+1
11
  TIM2->PSC   = 255;
12
  // Vergleichswert für Counter
13
  TIM2->CCR1  = 4095;
14
  // Compare Mode Register
15
  // - Freeze - mit OC1REF (definierter PIN?) nichts anstellen
16
  // - Auto Preload Reload enable - CCR1 kann verändert werden
17
  TIM2->CCMR1 = 0x0008;
18
  // Compare Enable Register
19
  // - OC1 Low ?
20
  // - OC1 enabled
21
  TIM2->CCER  = 0x0003; // 00 geht auch
22
  // DMA Interrupt Enable Register
23
  // - Interrupt Enable
24
  TIM2->DIER  = 0x0002;
25
  // Controll Register
26
  // - Counter Enable
27
  TIM2->CR1   = 0x0001;
28
29
30
  while (1) {
31
    // Zusammen 1 Sekunde...
32
    Delay(250);
33
    LED2_OFF();
34
    Delay(250);
35
    LED2_ON();
36
    Delay(250);
37
    LED2_OFF();
38
    Delay(250);
39
    LED2_ON();
40
41
    static s8 toggle = 1;
42
43
    if (toggle) {
44
      toggle = 0;
45
      TIM2->CCR1 = 10;
46
    } else {
47
      toggle = 1;
48
      TIM2->CCR1 = 20000;
49
    }
50
51
    rprintf("Result: %d\r", myTimerCounter1);
52
    myTimerCounter1 = 0;
53
        }

Interrupt sprich aber auch hier nicht auf eine Veränderung
von TIM2->CCR1 an.

Wenn jemand eine Idee hat, warum nicht wäre super.

Danke & Viele Grüße
Bernd

von (prx) A. K. (prx)


Lesenswert?

Bernd schrieb:

>   TIM2->DIER  = 0x0002;

Da sind noch mehr Bits drin.

von Michael B. (bubi)


Lesenswert?

Du must dem NVIC mitteilen, das er den Interrupt überhaupt weiterleitet 
und mit welcher Priorität...
Nochmal die Beispiele studieren...
Als Beispiel

  /* Enable the TIM2 global Interrupt */
  NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQChannel;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);

Und die Timer haben auch nochmal ein Register zum starten und Stopen vom 
Timerinterrupt.. Das kann ich jetzt nicht aus den Registern rauslesen. 
Benutze da eigentlich ausschließlich die FWLib... Zum initialisieren 
funktioniert sie bestens.

von (prx) A. K. (prx)


Lesenswert?

Den NVIC hatten wir schon. Offenbar geklärt, sonst käme garkein 
Interrupt. Aber der Update-Interrupt kommt ja. Nö, der CC-Interrupt muss 
einfach nur freigegeben werden, egal ob mit Lib (TIM_ITConfig) oder 
direkt (DIER).

Exakt dieses Thema hatten wir oben beim Update-Int auch schon. Aber 
mancher macht den gleichen Fehler eben nochmal.

von Bernd (Gast)


Lesenswert?

Hallo A.K.
1
  // DMA Interrupt Enable Register
2
  // - Interrupt Enable
3
  // - Update Interrupt Enable
4
  TIM2->DIER  = 0x0003;

Dann bleibt alles stehen, kein Compilerfehler, aber Board bleibt tot.

Hast Du irgendeine Idee was das sein könnte?

Danke & Viele Grüße
Bernd

von (prx) A. K. (prx)


Lesenswert?

Vielleicht weil du das Interrupt-Flag in der ISR auch wieder 
zurücksetzen solltest? Das richtige wohlgemerkt.

von Bernd (Gast)


Lesenswert?

Hallo,

Ok, super Danke. Läßt sich aber so leider immer noch verändern
1
void TIM2_IRQHandler (void) {
2
3
  // Interrupt Flag' muß per Software gelöscht werden
4
  TIM2->SR &= ~2;
5
  TIM2->SR &= ~1;
6
7
  myTimerCounter1++;
8
}
9
10
int main(void) {
11
12
  // ++ Initalisieren
13
14
  // Counter Max. der ins Auto-Load Register geschrieben wird
15
  // Läuft bis 24287, dann 0
16
  TIM2->ARR   = 24287;
17
  // Prescaler, Clock Teiler, Counter = Clock/Psc+1
18
  TIM2->PSC   = 55;
19
  // Vergleichswert für Counter
20
  TIM2->CCR1  = 4095;
21
  // Compare Mode Register
22
  // - Freeze - mit OC1REF (definierter PIN?) nichts anstellen
23
  // - Auto Preload Reload enable - CCR1 kann verändert werden
24
  TIM2->CCMR1 = 0x0008;
25
  // Compare Enable Register
26
  // - OC1 Low ?
27
  // - OC1 enabled
28
  TIM2->CCER  = 0x0003; // 00 geht auch
29
  // DMA Interrupt Enable Register
30
  // - Interrupt Enable
31
  // - Update Interrupt Enable
32
  TIM2->DIER  = 0x0003;
33
  // Controll Register
34
  // - Counter Enable
35
  TIM2->CR1   = 0x0001;
36
37
38
  while (1) {
39
    // Zusammen 1 Sekunde...
40
    Delay(250);
41
    LED2_OFF();
42
    Delay(250);
43
    LED2_ON();
44
    Delay(250);
45
    LED2_OFF();
46
    Delay(250);
47
    LED2_ON();
48
49
    static s8 toggle = 1;
50
51
    if (toggle) {
52
      toggle = 0;
53
      TIM2->CCR1 = 10;
54
    } else {
55
      toggle = 1;
56
      TIM2->CCR1 = 15000;
57
    }
58
59
    rprintf("Result: %d\r", myTimerCounter1);
60
    myTimerCounter1 = 0;
61
    }

Mit dem unterschiedlichen Vergleichswert sollte die gemessenen
Schritte pro Sekunde stark unterschiedlich sein, bleibt aber
relativ konstant bei 47-49 Durchläufe pro Sekunde.

Kann man TIM2->CCR1 einfach so verändern, oder muß dies
noch speziell 'aktiviert' werden.

Hifle wäre super, würde ich mich sehr freuen.

Viele Grüße
Bernd

von (prx) A. K. (prx)


Lesenswert?

Bernd schrieb:

> Mit dem unterschiedlichen Vergleichswert sollte die gemessenen
> Schritte pro Sekunde stark unterschiedlich sein, bleibt aber
> relativ konstant bei 47-49 Durchläufe pro Sekunde.

Wär komisch wenn nicht. Ist beim AVR mit seinen OCRs auch nicht anders.

Pro komplettem Durchlauf des Counters solltest du 2 Interrupts kriegen. 
Einen am Ende der Periode (Update) und einen wenn der CC passt. Mit dem 
CCR änderst du nur den Abstand zwischen den beiden.

von Bernd (Gast)


Lesenswert?

Hallo A.K.

herzlichen Dank für Deine Hifle - mitlerweile geht man einfach davon
aus das nicht funktioniert - sieht aber so aus als ob es läuf :)
War das ein Ding....

Viele Grüße
Bernd

von Bernd (Gast)


Lesenswert?

Hallo Zusammen,

ich habe jetzt auch mal versucht, das gleiche Signal per Input Capture
mit STM32 zu erfassen. So penibel wie möglich das Datenblatt studiert,
leider habe ich wohl irgendetwas übersehen.

Bei diesem Code
1
volatile s32 mySignal1 = 0;
2
volatile s32 mySignal2 = 0;
3
4
void TIM4_IRQHandler (void) {
5
6
  static s8  falling = 0; // Starte mit einer steigenden Flanke, siehe CCER Config
7
8
  mySignal1 = TIM4->CCR1; // bewirkt TIM4->SR &= ~2; Compare Interrupt Flag = 0
9
  mySignal2 = TIM4->CNT;
10
11
  if (falling) {
12
    falling = 0;
13
    TIM4->CCER = 0x0001; // Umschalten auf steigende Flanke
14
  } else {
15
    falling = 1;
16
    TIM4->CCER = 0x0003; // Umschalten auf fallende Flanke
17
  }
18
19
  TIM4->SR &= ~2;  // ??
20
  TIM4->SR &= ~1;  // Update Interrupt Enable zurücksetzen
21
  TIM4->SR &= ~10; // Ggf. Overcapture zurücksetzen (noch unbehandelt)
22
}
23
24
25
// Counter Mode Register
26
// - Input Captuere auf IC2 - TI2
27
//    (Produziert SR 1 - Capture Compare Interrupt Flag)
28
//     (Produziert SR 9 - Capture Compare Overflow Flag)
29
// - No Prescaler, Jedes Event
30
// - Filter N8 - 8 gleiche Impulse = Capture
31
TIM4->CCMR1 = 0x0031;
32
33
// Compare Enable Register
34
// - Enable Counter (Counter in CCR1 Speichern)
35
// - Rising Edge (Bit1 = 0 High / 1 Low)
36
TIM4->CCER = 0x0001;
37
38
// DMA Interrupt Enable Register
39
// - Interrupt Enable (Produziert SR 1 - Hier aber nicht, da es nur Compare Interrupts gibt)
40
// - Update Interrupt Enable (Produziert SR 0)
41
TIM4->DIER  = 0x0003;
42
43
// Controll Register
44
// - Counter Enabled
45
// - Update Enabled
46
// - ..
47
TIM4->CR1   = 0x0001;
48
49
while (1) {
50
  rprintf("Result: %d  %d\r", mySignal1, mySignal2);
51
}

Läuft der Timer komplett durch, mit oder ohne Signal ist das gleiche.
TIM4->CCR1; wo eigentlich der aktuelle Zählerstand zum Capture 
gespeichert
sein soll ist immer 0. TIM4->CNT pendelt so zwischen 29 und 32...

Sehr merkwürdig. Auch ohne das umschalten der Flanken (TIM4->CCER)
das gleiche Ergebnis.

Enabled hab ich hoffentlich soweit alles, Status Bits dürften auch
zurückgesetzt sein.

Über Hilfe würde mich super freuen, wenn jemand etwas sieht.

Vielen Dank & Viele Grüße
Bernd

von Random .. (thorstendb) Benutzerseite


Lesenswert?

hab nur kurz überflogen, aber hier was zum starten ohne STLib, 
generiert periodic interrupts:
Auch hier wieder mein Verweis auf die CMSIS von http://www.onArm.com :-)

1
// Timer 1 init
2
void timer1_init(int frequency)
3
{  
4
  bit_on(RCC->APB2ENR, TIM1_EN);          // enable clocks for Timer 2
5
  
6
    TIM1->PSC = 1200-1;                // 72MHz/1200 / 60.000 = 1Hz, enough for a 16Bit-Register :-)
7
8
  TIM1->ARR = ((CPU_CLOCK/1200)/frequency)-1;    // load cont auto reload register with period value
9
  
10
  bit_on(TIM1->CR1, DIR);             // DIR direction down  
11
    
12
  bit_on(TIM1->CR1, URS);             // only over/underflow generates an interrupt  
13
  bit_on(TIM1->DIER, UIE);             // Update Interrupt Enable  
14
  
15
  NVIC->ISER[(TIM1_UP_IRQChannel/32)] |= (1 << TIM1_UP_IRQChannel%32);  // enable interrupt
16
  NVIC->IPR[TIM1_UP_IRQChannel/4] |= ((PRIORITY_TIMER1<<4) << ((TIM1_UP_IRQChannel%4)*8));
17
  
18
  bit_on(TIM1->CR1, CEN);             // Timer1 enable
19
}
20
21
22
// Timer 1 ISR
23
void TIM1_UP_IRQHandler(void)
24
{    
25
  if (TIM1->SR & (1<<UIF))            // check interrupt source for Update Interrupt Flag
26
  {
27
    bit_off(TIM2->SR, UIF);            // clear UIF flag
28
    printf("\n Timer 1");
29
  }
30
}


VG,
/th.

von Bernd (Gast)


Lesenswert?

Hallo Random,

vielen Dank, geht aber um die Input Compare Configuration,
Timer & Output Compare, steht weiter oben und ist schon fertig.

Viele Grüße
Bernd

von Bernd (Gast)


Lesenswert?

Hallo Zusammen,

so, ich habe es jetzt geschafft, das zumindest CCR3 Werte beim
InputCompare erkannt werden. Lag an der GPIO...
1
volatile s32 mySignal1 = 0;
2
volatile s32 mySignal2 = 0;
3
volatile s8  falling = 0; // beginnt mit steigender Flanke
4
5
void TIM4_IRQHandler (void) {
6
7
  mySignal1++;
8
9
  mySignal2 = TIM4->CCR3; // bewirkt Compare Interrupt Flag 3 = 0
10
11
  if (falling) {
12
    falling = 0;
13
14
    // Umstellen auf steigende Flanke (CC1P = 0)
15
    //TIM4->CCER = 0x0000;
16
    //TIM4->CCER = 0x0100;
17
18
    // Counter auf Null setzen
19
    TIM4->CNT = 0;
20
21
  } else {
22
    falling = 1;
23
24
    // Umstellen auf fallende Flanke (CC1P = 1)
25
    //TIM4->CCER = 0x0000;
26
    //TIM4->CCER = 0x0102;
27
  }
28
29
  TIM4->SR = 0; // Reset Status Register Timer 4
30
}
31
32
int main(void) {
33
34
  // ++ Initalisieren
35
36
  // Counter Max.
37
  TIM4->ARR   = 5000;
38
39
  // Counter Mode Register
40
  // - Input Captuere Channel 3 - auf IC2 - TI2
41
  //    (Produziert SR 1 - Capture Compare Interrupt Flag)
42
  //     (Produziert SR 9 - Capture Compare Overflow Flag)
43
  // - No Prescaler, Jedes Event
44
  // - Filter N8 - 8 gleiche Impulse = Capture
45
  TIM4->CCMR2 = 0x0001; // 0031 mit Rauschunterdrückung
46
47
  // Compare Enable Register
48
  // - Enable Capture (Counter in CCR1 Speichern)
49
  // - Rising Edge (Bit1 = 0 High / 1 Low)
50
  TIM4->CCER = 0x0100;
51
52
  // DMA Interrupt Enable Register
53
  // - Compare 3 Interrupt Enable (Produziert SR X - Hier aber nicht, da es nur Compare Interrupts gibt)
54
  // - Update Interrupt Enable (Produziert SR 0)
55
  TIM4->DIER  = 0x0009; //
56
57
  // Controll Register
58
  // - Counter Enabled
59
  // - Update Enabled
60
  // - ..
61
  TIM4->CR1 = 0x0001;
62
63
64
  while (1) {
65
    Delay(250);
66
    LED2_OFF();
67
    Delay(250);
68
    LED2_ON();
69
70
    rprintf("Result: %d \r", mySignal1);
71
    mySignal1 = 0;
72
73
    }
74
}

Auf den InputCompare wird ein solches Signal gelegt
1
_____|...|___________________________________|.....|______________________________
Ein Takt (|...|), alle 20ms.

Nur die steigenden Flanken reichen natürlich nicht, man muß im
Interrupt umschalten. Siehe oben
1
    // Umstellen auf steigende Flanke (CC1P = 0)
2
    //TIM4->CCER = 0x0000;
3
    //TIM4->CCER = 0x0100;
4
5
    // Umstellen auf fallende Flanke (CC1P = 1)
6
    //TIM4->CCER = 0x0000;
7
    //TIM4->CCER = 0x0102;

Eigentlich müßte mit Umschalten der Flanken der Interrupt
doppelt so oft aufgerufen werden. Bleibt aber gleich.

Weiterhin, der Interrupt wird immer ca. 13.000x pro Sekunde
aufgerufen, auch wenn kein Signal anliegt.

Man sieht aber deutlich, das sich mit Signal die Werte
verändern, und nicht gleich bleiben. (Signal ist da...)

Kann mir jemand helfen, was hier nicht stimmt?
Würde ich mich sehr freuen.

Danke
Bernd

von Bernd (Gast)


Lesenswert?

Kleiner Nachtrag, das der Timer durchläuft - Immer, liegt
an
1
   TIM4->SR = 0; // Reset Status Register Timer 4

Mag er nicht. Mit dem Flankenwechsel hat sich aber leider noch nichts
getan....hmmm

von (prx) A. K. (prx)


Lesenswert?

Ich sehe oben bloss Kommentare, keinen Code.

PWM Messung geht übrigens auch direkt: 13.3.6.

Aber wenn du es schon zu Fuss machen willst: Spricht was dagegen, an 
Stelle der Variable "falling" den Pin direkt zu konsultieren?

Und statt den Counter auf 0 zu setzen und damit die Interrupt-Latenz mit 
drin zu haben würde ich beide Captures einfach subtrahieren.

von (prx) A. K. (prx)


Lesenswert?

Welchen Sinn ergibt hier der Update-Interrupt?

von Bernd (Gast)


Lesenswert?

Hallo A.K,

Vielen Dank.

Update Interrupt Enable, - möchte ja den Interrupt zur Laufzeit ändern,
Steigende/Fallende Flanke... Ohne habe ich auch schon mal probiert,
die Anzahl der Interruptaufrufe bleibt mit und ohne Umschaltung der
Flanken aber gleich, genau wie mit dem Update Flag.
1
volatile s32 mySignal1 = 0;
2
volatile s32 mySignal2 = 0;
3
volatile s8  falling = 0; // beginnt mit steigender Flanke
4
volatile s32 mybackup = 0;
5
6
void TIM4_IRQHandler (void) {
7
8
  mySignal1++;
9
  mySignal2 = TIM4->CCR3; 
10
11
  if (falling) {
12
    falling = 0;
13
    
14
    // Umstellen auf steigende Flanke (CC1P = 0)
15
    TIM4->CCER = 0x0000;
16
    TIM4->CCER = 0x0100;
17
  } else {
18
    falling = 1;
19
20
    // Umstellen auf fallende Flanke (CC1P = 1)
21
    TIM4->CCER = 0x0000;
22
    TIM4->CCER = 0x0102;
23
  }
24
  TIM2->SR &= ~4; 
25
}
26
27
int main(void) {
28
29
  // ++ Initalisieren
30
31
  TIM4->ARR   = 65000;
32
  TIM4->CCMR2 = 0x0031; // 0031 mit Rauschunterdrückung
33
  TIM4->CCER  = 0x0100;
34
  TIM4->DIER  = 0x0008;
35
  TIM4->CR1   = 0x0001;
36
37
  while (1) {
38
    Delay(250);
39
    LED2_OFF();
40
    Delay(250);
41
    LED2_ON();
42
43
    rprintf("Result: %d \r", mySignal1);
44
    mySignal1 = 0;
45
    }

Bzgl. falling, weiß nicht so genau wie das direkt geht,
zum testen reicht es hoffentlich.

Bzgl. dem SignalCounter (mySignal1)
- Ohne angelegtes Signal = 0x, sehr gut
- Mit Signal = 30x pro 500ms, ok
- Mit Flankenwechsel das gleiche, müßte aber rein logisch auf 60x 
kommen..
1
    TIM4->CCER = 0x0000;
2
    TIM4->CCER = 0x0100;

Beispiele im Netz gibt es beim besten Willen nicht, und die
Libary ist nur Interrupts, gar nich so einfach,

Danke & Viele Grüße
Bernd

von (prx) A. K. (prx)


Lesenswert?

Bernd schrieb:

> Bzgl. falling, weiß nicht so genau wie das direkt geht,

Du weisst nicht wie man den Status eines Pins abfragt?

von (prx) A. K. (prx)


Lesenswert?

Wie der Name schon suggeriert steuert CC1P die Flanke von CC1, nicht von 
CC3. Der Rest des Codes verwendet CC3.

Wieso setzt du CCER immer erst auf 0?

von (prx) A. K. (prx)


Lesenswert?

Bernd schrieb:

> Update Interrupt Enable, - möchte ja den Interrupt zur Laufzeit ändern,

Kompletter Blindflug? Der Timer funktioniert deutlich besser, wenn man 
verstanden hat wie er arbeitet.

Der Update-Interrupt schlägt zu, wenn der Counter bei ARR ankommt und 
sich automatisch auf 0 setzt. Mit deiner Capture-Funktion hat er nichts 
zu tun, sorgt aber naturgemäss für eine konstante Interruptrate 
unabhängig vom Eingangssignal.

von Bernd (Gast)


Lesenswert?

Hallo A.K.

vielen vielen Dank. CC3P funktioniert schon besser ... kopfschüttelnd
Wenn ich die Impulslängen messe, liegen die so knapp unter 65.000
und je nach länge gibts dann schon einen Überschlag.

Die Differenz draus zu bilden, ist dann schwierig.

Weißt Du ggf. was man da machen kann?

Danke & Viele Grüße
Bernd
(zum Hintergrund, ich bin von Hause aus PHP Programmierer...)

von (prx) A. K. (prx)


Lesenswert?

Bernd schrieb:

> Weißt Du ggf. was man da machen kann?

Timer langsamer zählen lassen.

Überlauf 65535=>0 wird trotzdem passieren, stört aber nicht wenn die zu 
messende Zeit stets kleiner ist als die Periode vom Timer.

von Bernd (Gast)


Lesenswert?

Hallo A.K,

> Timer langsamer zählen lassen.

Ich finde nur Prescaler (im CCMR Register),
und auf die Counter Geschwindigkeit hat das
irgendwie keinen Effekt, Immer knapp 50.000
Schritte pro 20ms.

Bei jeder zweiten Berechnung habe ich einen
Überschlag....

Könntest Du mir das Register nennen?

Danke & Viele Grüße
Bernd

von Bernd (Gast)


Lesenswert?

ok, doofe frage,
den prescaler gibts ja auch noch :)

[c]
TIM4->PSC
[c]

von (prx) A. K. (prx)


Lesenswert?

Bernd schrieb:

> irgendwie keinen Effekt, Immer knapp 50.000
> Schritte pro 20ms.

Was doch völlig ausreicht. Das würde sogar ausreichen um die 20ms zu 
messen. Aber erst recht für die maximal 2,5ms um die es geht.

Und wenn dabei die ersten Flanke bei 64765 erfolgt und die zweite bei 
124, dann war zwar ein Überlauf dazwischen (für einen Überschlag reichen 
die 3,3V nicht aus, da brauchts Hochspannung), aber die Differenz lässt 
sich trotzdem verwenden. Modulo 65536 eben (ARR=0xFFFF). Bischen denken 
beim rechnen.

von Bernd (Gast)


Lesenswert?

Hallo A.K,

ich haber leider noch ein Problem mit dem Outputcompare.
Ich möchten den Interrupt nur über den Outputcompare aufrufen,
nicht über Overflow.
1
void TIM2_IRQHandler (void) {
2
3
  TIM2->CCR1 = 45000;
4
  TIM2->CNT = 0;
5
  mycounter++;
6
7
  TIM2->SR &= ~2;//(1<<UIF);
8
  TIM2->SR &= ~1;//(1<<CC1IF);
9
}
10
11
  TIM2->PSC   = 14;    
12
  TIM2->CCR1  = 20;
13
  TIM2->CCMR1 = 0x0000;
14
  TIM2->CCER  = 0x0000;
15
  TIM2->DIER  = 0x0002;
16
  TIM2->CR1   = 0x0001;

Ergebnis des Counters (mycounter) ist 711 pro Sekunde.

Meine Rechnung

32 000 000 Mio Takte pro Sekunde
Prescaler 14 = 2 285 714
Läuft bis 45 000 = 50.79 Aufrufe pro Sekunde.

Wenn Du mir helfen kannst, wäre ganz klasse.
Ich sitze schon den ganzen abend dran... sooo
komplex ist es nicht.

Danke & Viele Grüße
Bernd

von (prx) A. K. (prx)


Lesenswert?

Der CC hat rein garnichts damit zu tun wie weit ein Counter läuft. Der
läuft solange bis er AAR erreicht und fängt dann bei 0 wieder an. Hier
sind es deshalb wohl die vollen 65536, die ein 16bit Counter hergibt.
Und damit passt es dann prima in die Rechnung.

Ob der Interrupt per Update oder per CC kommt ändert nur den Zeitpunkt,
nicht die Häufigkeit. Ein Thema das wir schon einmal geklärt(?) hatten.

Kleine Eselsbrücke falls du dir den Unterschied partout nicht merken
kannst: Es gibt pro Timer einen Counter aber 4 Compare-Channels. Wenn
jeder davon den Counter zurück setzen würde, denn käme von den 4 CCs 
immer nur einer dran, der mit dem kleinsten Wert.

Ansonsten kann ich nur wieder empfehlen, die Phase des Blindflugs zu
beenden, indem du dich mal damit befasst was im Timer tatsächlich
abläuft. Mikrocontroller funktionieren besser wenn man versteht was man
tut.

von (prx) A. K. (prx)


Lesenswert?

Korrektur: Rechnung passt auch dann nicht. Denkfehler ist es schon, auf 
50 kommst damit nie, aber woher die 711 kommen ist unklar.

Schlimmer noch: Wenn in ARR wie hier suggeriert der Reset-Wert von 0 
steht, dann läuft der Counter laut Doku garnicht. Es kann also nicht 
alles sein was du hier gepostet hast. Da fehlt was.

von (prx) A. K. (prx)


Lesenswert?

PS: Eine Kleinigkeit: Das SR setzt man anders zurück. So wird CC1IF 
besser mit SR=~TIM_SR_CC1IF zurück gesetzt, da die SR Bits durch 
Schreiben von 1 nicht verändert werden (=> rc_w0).

von H.j.Seifert (Gast)


Lesenswert?

@A.K.:
Wenn man aber zum Compare-Zeitpunkt den Timer per Software wieder auf 0 
setzt (TIM2->CNT = 0;), sollte das schon passen. Unabhängig vom APR, 
zumindest solange dessen Wert grösser als der von CC1R

von (prx) A. K. (prx)


Lesenswert?

Stimmt, das habe ich übersehen. Aber erstens ist das fast immer der 
falsche Ansatz, zweitens sollte der Interrupt überhaupt nicht auftreten, 
weil bei ARR=0 (Reset-Wert) der Zähler nicht zählt.

von Bernd (Gast)


Lesenswert?

Hallo A.K.

soweit verstehe ich das, ich dachte ARR ist Default 0xFFFF.
(Datenblatt -liegt hier- ist 0x0000...).

Fehler liegt allerdings wohl woanders. Mit ARR = 0xFFFF
1
volatile s32 mycounter = 1;
2
3
void TIM2_IRQHandler (void) {
4
  TIM2->CCR1 = 45000;
5
  TIM2->CNT  = 0;
6
  mycounter++;
7
  TIM2->SR=~TIM_SR_CC1IF;
8
}
9
10
int main(void) {
11
12
  // ++ Initalisieren
13
14
  // Servo Out Timer
15
  TIM2->ARR   = 0xFFFF;
16
  TIM2->PSC   = 14;
17
  TIM2->CCR1  = 20; // Compare wird angestoßen
18
  TIM2->CCMR1 = 0x0000;
19
  TIM2->CCER  = 0x0000;
20
  TIM2->DIER  = 0x0002;
21
  TIM2->CR1   = 0x0001;
22
23
  while (1) {
24
    // Zusammen 1 Sekunde...
25
    Delay(500);
26
  
27
    rprintf("Nr. %d \r", mycounter);
28
    mycounter = 0;
29
    }  
30
}

710 Aufrufe pro 500ms. (gestoppt auf eine Sekunde = 1420)
Halbiert man den 'Zählraum'
1
void TIM2_IRQHandler (void) {
2
  TIM2->CCR1 = 45000;
3
  TIM2->CNT  = 22500;
4
  mycounter++;
5
  TIM2->SR=~TIM_SR_CC1IF;
6
}
7
8
int main(void) {
9
10
  // ++ Initalisieren
11
  STM32_Init();
12
13
  // Servo Out Timer
14
  TIM2->ARR   = 0xFFFF;
15
  TIM2->PSC   = 14;
16
  TIM2->CCR1  = 20; // Compare wird angestoßen
17
  TIM2->CCMR1 = 0x0000;
18
  TIM2->CCER  = 0x0000;
19
  TIM2->DIER  = 0x0002;
20
  TIM2->CR1   = 0x0001;
21
22
  while (1) {
23
    // Zusammen 1 Sekunde...
24
    Delay(500);
25
    LED2_OFF();
26
  
27
    rprintf("Nr. %d \r", mycounter);
28
    mycounter = 0;
29
    }
30
}

Ergebnis 1420...

Prescaler 14 stimmt nach meiner Rechung
1
  32000000
2
/ 14
3
----------------------------
4
= 2285714.28571429
5
/ 45000
6
----------------------------
7
= 50.7936507936509

= 50kz, 20ms Takt.

Hast Du eine Idee was der Timer da macht?


Danke & Viele Grüße
Bernd

von (prx) A. K. (prx)


Lesenswert?

Bernd schrieb:

> Hast Du eine Idee was der Timer da macht?

Jedenfalls ignoriert er den Prescaler, denn wenn du den mal aus der 
Rechnung draussen lässt kommt 32000000/45000=711 raus. Was deutlich 
dichter dran ist als bisher.

Apropros Prescaler: Wenn 14 dann teilt der durch 15.

von (prx) A. K. (prx)


Lesenswert?

Wenn das Interrupt-Flag in SR ganz am Ende der ISR gelöscht wird, dann 
ist es aufgrund der internen Verzögerungen ggf. noch seitens des NVIC 
sichtbar wenn die ISR die Interrupts wieder freigibt.

Ist aber nicht das eigentliche Problem.

von (prx) A. K. (prx)


Lesenswert?

Es scheint keine gute Idee zu sein, im ersten Aufruf der ISR das CNT 
Register zu setzen. Sobald das passiert spinnt der Prescaler.

Wenn man das erst später macht ist Ruhe:
1
int flag = 0;
2
3
void TIM2_IRQHandler (void) {
4
  TIM2->SR = ~TIM_SR_CC1IF;
5
  if (flag)
6
    TIM2->CNT = 0;
7
  flag = 1;
8
  mycounter++;
9
}

von (prx) A. K. (prx)


Lesenswert?

Stell das Problem mal ins STM32 Forum rein.

von (prx) A. K. (prx)


Lesenswert?

Habe ich jetzt selbst reingestellt: 
http://www.st.com/mcu/forums-cat-8739-23.html

von Bernd (Gast)


Lesenswert?

Hallo,

Vielen Dank. Ok. (ich versuch mal hier einen Workaround zu basteln...)

Viele Grüße
Bernd

von (prx) A. K. (prx)


Lesenswert?

Einfachster Workaround: Bevor du den Timer Interrupt via NVIC freigibst, 
warte bis SR.UIF gesetzt ist, der Counter des Timers also einmal 
komplett durchlief. Siehe Testcase im STM32 Forum unter 
"EnableIntAfterUpdate". Ist eine einmalige kleine Verzögerung beim Start 
und am Interrupt-Handler brauchst da dann nichts zu ändern.

von (prx) A. K. (prx)


Lesenswert?

Damit der Thread nicht ohne Erklärung offen liegen bleibt:

Wie das Handbuch sehr detailliert beschreibt ;-) interessiert sich der 
Prescaler erst dann für den Wert in PSC, wenn ein "update event" 
erfolgt. Das passiert, sobald der Zähler am Limit ankommt. Wenn man im 
Compare Interrupt das CNT Register auf 0 setzt, dann wird das nie 
passieren, folglich wird der PSC Wert nie aktiv.

Man sollte als Teil der Initialisierung des Timers manuell einen Update 
anstossen (UG-Bit in EGR).

von Matthias (Gast)


Lesenswert?

Hallo Bernd,

möchtest du uns den Code, der fertigen Servo Ansteuerung zur Verfügung 
stellen? Mache gerade die selbe Prozedur wie du durch.

Gruß Matthias

von Ernst B. (puravida)


Lesenswert?

Bernd schrieb:
> Hallo A.K,
>
> vielen Dank. Servoansteuerung läuft bereits auf dem AVR (nur eben
> viel zu langsam - ruckelte ziehmlich - seidenweich ist anders). PWM
> hatte
> ich auch schon gedacht, nur ist die Frage ob das auf 6 Kanälen
> gleichzeitig geht. (???)
>
> Für die Ausgabe von 6 ServoImpulsen z.B.
>
>
1
> servo_out[0] = 1500; // PA8
2
> servo_out[1] = 2000; // PA11
3
> servo_out[2] = 2500; // PA12
4
> servo_out[3] = 3000; // PA13
5
> servo_out[4] = 3500; // PA14
6
> servo_out[5] = 4000; // PA15
7
>
>
> bräuchte man
>
> - Einen Timer mit Output Compare
> - PA8 wird eingeschaltet
> - Compare = servo_out[i];
> - Wenn der Compare abgelaufen ist, wird PA8 ausgeschaltet.
> - Der Timer wird alle 21ms  (jedenfalls > 21ms)  neu gestartet (?) und
> Pin wird erhöht (nächster PA11)
>
> Müßte dann ergeben (ein Zeichen (|,.,_) entspricht 500ms)
>
1
> PA8
2
> |.|_____________________________________|.|_____________________________________
3
> PA11
4
> __|..|____________________________________|..|__________________________________
5
> PA12
6
> _____|...|___________________________________|...|______________________________
7
> PA13
8
> _________|....|__________________________________|....|_________________________
9
> PA14
10
> ______________|.....|_________________________________|.....|___________________
11
> PA15
12
> ____________________|......|________________________________|......|____________
13
> 
14
>

Hi Bernd!

Mag sein, daß das ein bischen OT ist aber eine Servoansteuerung von 6 
Servos, noch dazu seriell, geht doch am AVR ohne Probleme. Und ohne 
Ruckeln oder Zuckeln sondern seidenweich.

Wenn es nicht gerade Neugier oder Freude am STM32 ist, ist das doch für 
diese Aufgabenstellung vollkommen overdressed.

LG
Ernst

von RP6Conrad (Gast)


Lesenswert?

Ein servo-signal generieren mit ein 16 bit Zaehler kann fiel einfacher : 
eine reine PWM-mode  mit periode von 20 ms, und die pulsbreite kan dan 
mit eine Auflosung von 1 µs laufen : Timer lauft an 1 MHz, zaehlt bis 
20.000. 4 kanale pro timer, jedes kanal kan dan eine Wert haben von 1000 
bis 2000. Timer starten und loss geht es. Dafur braucht man keinen 
Interrupt. Mit mein discovery board kan mit die 4 Timer 16 Servo-signale 
generieren.
1
 TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
2
  TIM_OCInitTypeDef  TIM_OCInitStructure;
3
  /* Time base configuration TIM1*/
4
  TIM_TimeBaseStructure.TIM_Period = 19999;//PWM freq. = 1MHz/20000 = 50Hz
5
  TIM_TimeBaseStructure.TIM_Prescaler = 23;// Timer loopt aan 24 MHz/24 = 1MHz
6
  TIM_TimeBaseStructure.TIM_ClockDivision = 0;
7
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
8
  TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
9
  /* PWM1 Mode configuration: TIM 1, Channel1 */
10
  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
11
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
12
  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
13
  TIM_OC1Init(TIM1, &TIM_OCInitStructure);
14
  TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);
15
  /* PWM1 Mode configuration: TIM 1, Channel4 */
16
  TIM_OC4Init(TIM1, &TIM_OCInitStructure);
17
  TIM_OC4PreloadConfig(TIM1, TIM_OCPreload_Enable);
Die Servosignale wirden dan mit TIM1->CCR1=1500; //test 1.5 ms voor 
servo1
  TIM1->CCR4=1825; //test 1.825 ms voor servo 4 eingestellt.

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.