www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik STM32 - Servoansteuerung


Autor: Bernd (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Bernd (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

volatile s32 myTimerCounter = 0;

void TIM2_IRQHandler(void) {
  myTimerCounter++;
}

int main(void) {

  // Initalisieren, STM32_Init, GPIO, UART ...

  // TIM2CLK = 32 MHz, Prescaler = 100, TIM2 counter clock = 32 KHz
  TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
  //TIM_TimeBaseStructure.TIM_Period = 0;
  TIM_TimeBaseStructure.TIM_Prescaler = 100;
  TIM_TimeBaseStructure.TIM_ClockDivision = 0;
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
  TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
  TIM_Cmd(TIM2, ENABLE);

  while (1) {
    Delay(250);
    LED2_OFF();
    Delay(250);
    LED2_ON();

    rprintf("Stand2: %d\r", myTimerCounter);
    }
}

// Ergebnis:
// Stand2: 0
// Stand2: 0
// Stand2: 0
// 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

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Bernd (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.
servo_out[0] = 1500; // PA8
servo_out[1] = 2000; // PA11
servo_out[2] = 2500; // PA12
servo_out[3] = 3000; // PA13
servo_out[4] = 3500; // PA14
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)
PA8  |.|_____________________________________|.|_____________________________________
PA11 __|..|____________________________________|..|__________________________________
PA12 _____|...|___________________________________|...|______________________________
PA13 _________|....|__________________________________|....|_________________________
PA14 ______________|.....|_________________________________|.....|___________________
PA15 ____________________|......|________________________________|......|____________


In der Zwischenzeit funktioniert auch mein Timer
  // TIM2CLK = 32 MHz
  TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;

  // Specifies the period value to be loaded into the active
  // Auto-Reload Register at the next update event. This parameter
  // must be a number between 0x0000 and 0xFFFF.
  TIM_TimeBaseStructure.TIM_Period = 65535;
  TIM_TimeBaseStructure.TIM_Prescaler = 0;
  TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
  TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

  TIM_ClearFlag(TIM2, TIM_FLAG_Update);
  TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
  TIM_ClearFlag(TIM2, TIM_FLAG_Update); /* clear int flag */

  TIM_Cmd(TIM2, ENABLE);

  while (1) {
    // Zusammen 1 Sekunde...
    Delay(250);
    LED2_OFF();
    Delay(250);
    LED2_ON();
    Delay(250);
    LED2_OFF();
    Delay(250);
    LED2_ON();

    rprintf("Stand2: %d\r", myTimerCounter);
    }

Allerdings nur mit Periode.... (vom AVR kenn ich das überhaupt nicht).
Das Ergebnis ist jedenfalls
Stand2: 13214    
Stand2: 13703  Differenz 489  
Stand2: 14193  Differenz 490
Stand2: 14682  Differenz 489
Stand2: 15172  Differenz 490
Stand2: 15661  Differenz 489
Stand2: 16151  Differenz 490
Stand2: 16640  Differenz 489
Stand2: 17130  Differenz 490
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

Autor: Bernd (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
achso, noch der zugehörige Interupt
volatile s32 myTimerCounter = 0;

void TIM2_IRQHandler (void) {
  TIM_ClearFlag(TIM2, TIM_FLAG_Update);
  myTimerCounter++;
}

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Bernd (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ok, ich hab's Periode funktioniert nicht anders als ein Vorteile
  TIM_TimeBaseStructure.TIM_Period = 32000;
  TIM_TimeBaseStructure.TIM_Prescaler = 19;
[c]

gibt 50khz oder 20ms.

[c]
32000000 (mhz) / 32000 Period =  1000 / 20 Prescaler = 50

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

Autor: Bernd (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo A.K,
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.

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

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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).

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Bernd (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Bernd (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.
volatile s32 myTimerCounter1 = 0;
void TIM2_IRQHandler(void) {

  // Also cleared the wrong interrupt flag in the ISR
  TIM_ClearFlag(TIM2, TIM_FLAG_Update);

  // TIM_GetITStatus(TIM2, TIM_IT_CC1) != RESET
  TIM_ClearITPendingBit(TIM2, TIM_IT_CC1); // Clear the interrupt flag

  TIM2->CCR1 = 20;

  myTimerCounter1++;

}

int main(void) {

  // ++ Initalisieren

  // Description : Initialize the Timer2 channel-1.
  // TIM2CLK = 36 MHz, counter clock = 20 ms
  TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
  TIM_OCInitTypeDef TIM_OCInitStructure;

  TIM_TimeBaseStructure.TIM_Period = 32000; // auto reaload register
  TIM_TimeBaseStructure.TIM_Prescaler = 0;
  TIM_TimeBaseStructure.TIM_ClockDivision = 0;
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
  TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

  TIM_PrescalerConfig(TIM2, 19, TIM_PSCReloadMode_Immediate);

  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Timing ;
  TIM_OCInitStructure.TIM_OutputState = TIM_Channel_1;
  TIM_OCInitStructure.TIM_Pulse = 0;
  TIM_OC1Init(TIM2, &TIM_OCInitStructure);
  TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Disable);

  TIM_ClearFlag(TIM2, TIM_FLAG_Update);
  TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
  TIM_ClearFlag(TIM2, TIM_FLAG_Update);

  TIM_Cmd(TIM2, ENABLE);

  while (1) {
    // Zusammen 1 Sekunde...
    Delay(250);
    LED2_OFF();
    Delay(250);
    LED2_ON();
    Delay(250);
    LED2_OFF();
    Delay(250);
    LED2_ON();

    rprintf("Result: %d\r", myTimerCounter1);
    }
}


Mit
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

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Bernd (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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
TIM_ARRPreloadConfig(TIM2, ENABLE);

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

Danke & Viele Grüße
Bernd

Autor: Bernd (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jedenfalls, vor lauter Verzweifelung, hab ich schonmal versucht das ohne
die Lib hinzubekommen.


     void TIM2_IRQHandler (void) {
  // Interrupt Flag muß per Software gelöscht werden
  TIM_ClearFlag(TIM2, TIM_FLAG_Update);
  myTimerCounter1++;
     }

     // Counter Max. der ins Auto-Load Register geschrieben wird
     // Läuft bis 24287, dann 0
  TIM2->ARR   = 24287;
  // Prescaler, Clock Teiler, Counter = Clock/Psc+1
  TIM2->PSC   = 255;
  // Vergleichswert für Counter
  TIM2->CCR1  = 4095;
  // Compare Mode Register
  // - Freeze - mit OC1REF (definierter PIN?) nichts anstellen
  // - Auto Preload Reload enable - CCR1 kann verändert werden
  TIM2->CCMR1 = 0x0008;
  // Compare Enable Register
  // - OC1 Low ?
  // - OC1 enabled
  TIM2->CCER  = 0x0003; // 00 geht auch
  // DMA Interrupt Enable Register
  // - Interrupt Enable
  TIM2->DIER  = 0x0002;
  // Controll Register
  // - Counter Enable
  TIM2->CR1   = 0x0001;


  while (1) {
    // Zusammen 1 Sekunde...
    Delay(250);
    LED2_OFF();
    Delay(250);
    LED2_ON();
    Delay(250);
    LED2_OFF();
    Delay(250);
    LED2_ON();

    static s8 toggle = 1;

    if (toggle) {
      toggle = 0;
      TIM2->CCR1 = 10;
    } else {
      toggle = 1;
      TIM2->CCR1 = 20000;
    }

    rprintf("Result: %d\r", myTimerCounter1);
    myTimerCounter1 = 0;
        }

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

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Bernd schrieb:

>   TIM2->DIER  = 0x0002;

Da sind noch mehr Bits drin.

Autor: Michael B. (bubi)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Bernd (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo A.K.
  // DMA Interrupt Enable Register
  // - Interrupt Enable
  // - Update Interrupt Enable
  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

Autor: A. K. (prx)
Datum:

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

Autor: Bernd (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

Ok, super Danke. Läßt sich aber so leider immer noch verändern

void TIM2_IRQHandler (void) {

  // Interrupt Flag' muß per Software gelöscht werden
  TIM2->SR &= ~2;
  TIM2->SR &= ~1;

  myTimerCounter1++;
}

int main(void) {

  // ++ Initalisieren

  // Counter Max. der ins Auto-Load Register geschrieben wird
  // Läuft bis 24287, dann 0
  TIM2->ARR   = 24287;
  // Prescaler, Clock Teiler, Counter = Clock/Psc+1
  TIM2->PSC   = 55;
  // Vergleichswert für Counter
  TIM2->CCR1  = 4095;
  // Compare Mode Register
  // - Freeze - mit OC1REF (definierter PIN?) nichts anstellen
  // - Auto Preload Reload enable - CCR1 kann verändert werden
  TIM2->CCMR1 = 0x0008;
  // Compare Enable Register
  // - OC1 Low ?
  // - OC1 enabled
  TIM2->CCER  = 0x0003; // 00 geht auch
  // DMA Interrupt Enable Register
  // - Interrupt Enable
  // - Update Interrupt Enable
  TIM2->DIER  = 0x0003;
  // Controll Register
  // - Counter Enable
  TIM2->CR1   = 0x0001;


  while (1) {
    // Zusammen 1 Sekunde...
    Delay(250);
    LED2_OFF();
    Delay(250);
    LED2_ON();
    Delay(250);
    LED2_OFF();
    Delay(250);
    LED2_ON();

    static s8 toggle = 1;

    if (toggle) {
      toggle = 0;
      TIM2->CCR1 = 10;
    } else {
      toggle = 1;
      TIM2->CCR1 = 15000;
    }

    rprintf("Result: %d\r", myTimerCounter1);
    myTimerCounter1 = 0;
    }


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

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Bernd (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Bernd (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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
volatile s32 mySignal1 = 0;
volatile s32 mySignal2 = 0;

void TIM4_IRQHandler (void) {

  static s8  falling = 0; // Starte mit einer steigenden Flanke, siehe CCER Config

  mySignal1 = TIM4->CCR1; // bewirkt TIM4->SR &= ~2; Compare Interrupt Flag = 0
  mySignal2 = TIM4->CNT;

  if (falling) {
    falling = 0;
    TIM4->CCER = 0x0001; // Umschalten auf steigende Flanke
  } else {
    falling = 1;
    TIM4->CCER = 0x0003; // Umschalten auf fallende Flanke
  }

  TIM4->SR &= ~2;  // ??
  TIM4->SR &= ~1;  // Update Interrupt Enable zurücksetzen
  TIM4->SR &= ~10; // Ggf. Overcapture zurücksetzen (noch unbehandelt)
}


// Counter Mode Register
// - Input Captuere auf IC2 - TI2
//    (Produziert SR 1 - Capture Compare Interrupt Flag)
//     (Produziert SR 9 - Capture Compare Overflow Flag)
// - No Prescaler, Jedes Event
// - Filter N8 - 8 gleiche Impulse = Capture
TIM4->CCMR1 = 0x0031;

// Compare Enable Register
// - Enable Counter (Counter in CCR1 Speichern)
// - Rising Edge (Bit1 = 0 High / 1 Low)
TIM4->CCER = 0x0001;

// DMA Interrupt Enable Register
// - Interrupt Enable (Produziert SR 1 - Hier aber nicht, da es nur Compare Interrupts gibt)
// - Update Interrupt Enable (Produziert SR 0)
TIM4->DIER  = 0x0003;

// Controll Register
// - Counter Enabled
// - Update Enabled
// - ..
TIM4->CR1   = 0x0001;

while (1) {
  rprintf("Result: %d  %d\r", mySignal1, mySignal2);
}


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

Autor: Random ... (thorstendb) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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 :-)


// Timer 1 init
void timer1_init(int frequency)
{  
  bit_on(RCC->APB2ENR, TIM1_EN);          // enable clocks for Timer 2
  
    TIM1->PSC = 1200-1;                // 72MHz/1200 / 60.000 = 1Hz, enough for a 16Bit-Register :-)

  TIM1->ARR = ((CPU_CLOCK/1200)/frequency)-1;    // load cont auto reload register with period value
  
  bit_on(TIM1->CR1, DIR);             // DIR direction down  
    
  bit_on(TIM1->CR1, URS);             // only over/underflow generates an interrupt  
  bit_on(TIM1->DIER, UIE);             // Update Interrupt Enable  
  
  NVIC->ISER[(TIM1_UP_IRQChannel/32)] |= (1 << TIM1_UP_IRQChannel%32);  // enable interrupt
  NVIC->IPR[TIM1_UP_IRQChannel/4] |= ((PRIORITY_TIMER1<<4) << ((TIM1_UP_IRQChannel%4)*8));
  
  bit_on(TIM1->CR1, CEN);             // Timer1 enable
}


// Timer 1 ISR
void TIM1_UP_IRQHandler(void)
{    
  if (TIM1->SR & (1<<UIF))            // check interrupt source for Update Interrupt Flag
  {
    bit_off(TIM2->SR, UIF);            // clear UIF flag
    printf("\n Timer 1");
  }
}



VG,
/th.

Autor: Bernd (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Bernd (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Zusammen,

so, ich habe es jetzt geschafft, das zumindest CCR3 Werte beim
InputCompare erkannt werden. Lag an der GPIO...


volatile s32 mySignal1 = 0;
volatile s32 mySignal2 = 0;
volatile s8  falling = 0; // beginnt mit steigender Flanke

void TIM4_IRQHandler (void) {

  mySignal1++;

  mySignal2 = TIM4->CCR3; // bewirkt Compare Interrupt Flag 3 = 0

  if (falling) {
    falling = 0;

    // Umstellen auf steigende Flanke (CC1P = 0)
    //TIM4->CCER = 0x0000;
    //TIM4->CCER = 0x0100;

    // Counter auf Null setzen
    TIM4->CNT = 0;

  } else {
    falling = 1;

    // Umstellen auf fallende Flanke (CC1P = 1)
    //TIM4->CCER = 0x0000;
    //TIM4->CCER = 0x0102;
  }

  TIM4->SR = 0; // Reset Status Register Timer 4
}

int main(void) {

  // ++ Initalisieren

  // Counter Max.
  TIM4->ARR   = 5000;

  // Counter Mode Register
  // - Input Captuere Channel 3 - auf IC2 - TI2
  //    (Produziert SR 1 - Capture Compare Interrupt Flag)
  //     (Produziert SR 9 - Capture Compare Overflow Flag)
  // - No Prescaler, Jedes Event
  // - Filter N8 - 8 gleiche Impulse = Capture
  TIM4->CCMR2 = 0x0001; // 0031 mit Rauschunterdrückung

  // Compare Enable Register
  // - Enable Capture (Counter in CCR1 Speichern)
  // - Rising Edge (Bit1 = 0 High / 1 Low)
  TIM4->CCER = 0x0100;

  // DMA Interrupt Enable Register
  // - Compare 3 Interrupt Enable (Produziert SR X - Hier aber nicht, da es nur Compare Interrupts gibt)
  // - Update Interrupt Enable (Produziert SR 0)
  TIM4->DIER  = 0x0009; //

  // Controll Register
  // - Counter Enabled
  // - Update Enabled
  // - ..
  TIM4->CR1 = 0x0001;


  while (1) {
    Delay(250);
    LED2_OFF();
    Delay(250);
    LED2_ON();

    rprintf("Result: %d \r", mySignal1);
    mySignal1 = 0;

    }
}


Auf den InputCompare wird ein solches Signal gelegt
_____|...|___________________________________|.....|______________________________

Ein Takt (|...|), alle 20ms.

Nur die steigenden Flanken reichen natürlich nicht, man muß im
Interrupt umschalten. Siehe oben

    // Umstellen auf steigende Flanke (CC1P = 0)
    //TIM4->CCER = 0x0000;
    //TIM4->CCER = 0x0100;

    // Umstellen auf fallende Flanke (CC1P = 1)
    //TIM4->CCER = 0x0000;
    //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

Autor: Bernd (Gast)
Datum:

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

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

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Welchen Sinn ergibt hier der Update-Interrupt?

Autor: Bernd (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.
volatile s32 mySignal1 = 0;
volatile s32 mySignal2 = 0;
volatile s8  falling = 0; // beginnt mit steigender Flanke
volatile s32 mybackup = 0;

void TIM4_IRQHandler (void) {

  mySignal1++;
  mySignal2 = TIM4->CCR3; 

  if (falling) {
    falling = 0;
    
    // Umstellen auf steigende Flanke (CC1P = 0)
    TIM4->CCER = 0x0000;
    TIM4->CCER = 0x0100;
  } else {
    falling = 1;

    // Umstellen auf fallende Flanke (CC1P = 1)
    TIM4->CCER = 0x0000;
    TIM4->CCER = 0x0102;
  }
  TIM2->SR &= ~4; 
}

int main(void) {

  // ++ Initalisieren

  TIM4->ARR   = 65000;
  TIM4->CCMR2 = 0x0031; // 0031 mit Rauschunterdrückung
  TIM4->CCER  = 0x0100;
  TIM4->DIER  = 0x0008;
  TIM4->CR1   = 0x0001;

  while (1) {
    Delay(250);
    LED2_OFF();
    Delay(250);
    LED2_ON();

    rprintf("Result: %d \r", mySignal1);
    mySignal1 = 0;
    }

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..
    TIM4->CCER = 0x0000;
    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

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Bernd schrieb:

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

Du weisst nicht wie man den Status eines Pins abfragt?

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Bernd (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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...)

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Bernd (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Bernd (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ok, doofe frage,
den prescaler gibts ja auch noch :)

[c]
TIM4->PSC
[c]

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Bernd (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

void TIM2_IRQHandler (void) {

  TIM2->CCR1 = 45000;
  TIM2->CNT = 0;
  mycounter++;

  TIM2->SR &= ~2;//(1<<UIF);
  TIM2->SR &= ~1;//(1<<CC1IF);
}

  TIM2->PSC   = 14;    
  TIM2->CCR1  = 20;
  TIM2->CCMR1 = 0x0000;
  TIM2->CCER  = 0x0000;
  TIM2->DIER  = 0x0002;
  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

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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).

Autor: H.j.Seifert (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Bernd (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

volatile s32 mycounter = 1;

void TIM2_IRQHandler (void) {
  TIM2->CCR1 = 45000;
  TIM2->CNT  = 0;
  mycounter++;
  TIM2->SR=~TIM_SR_CC1IF;
}

int main(void) {

  // ++ Initalisieren

  // Servo Out Timer
  TIM2->ARR   = 0xFFFF;
  TIM2->PSC   = 14;
  TIM2->CCR1  = 20; // Compare wird angestoßen
  TIM2->CCMR1 = 0x0000;
  TIM2->CCER  = 0x0000;
  TIM2->DIER  = 0x0002;
  TIM2->CR1   = 0x0001;

  while (1) {
    // Zusammen 1 Sekunde...
    Delay(500);
  
    rprintf("Nr. %d \r", mycounter);
    mycounter = 0;
    }  
}


710 Aufrufe pro 500ms. (gestoppt auf eine Sekunde = 1420)
Halbiert man den 'Zählraum'

void TIM2_IRQHandler (void) {
  TIM2->CCR1 = 45000;
  TIM2->CNT  = 22500;
  mycounter++;
  TIM2->SR=~TIM_SR_CC1IF;
}

int main(void) {

  // ++ Initalisieren
  STM32_Init();

  // Servo Out Timer
  TIM2->ARR   = 0xFFFF;
  TIM2->PSC   = 14;
  TIM2->CCR1  = 20; // Compare wird angestoßen
  TIM2->CCMR1 = 0x0000;
  TIM2->CCER  = 0x0000;
  TIM2->DIER  = 0x0002;
  TIM2->CR1   = 0x0001;

  while (1) {
    // Zusammen 1 Sekunde...
    Delay(500);
    LED2_OFF();
  
    rprintf("Nr. %d \r", mycounter);
    mycounter = 0;
    }
}



Ergebnis 1420...

Prescaler 14 stimmt nach meiner Rechung
  32000000
/ 14
----------------------------
= 2285714.28571429
/ 45000
----------------------------
= 50.7936507936509

= 50kz, 20ms Takt.

Hast Du eine Idee was der Timer da macht?


Danke & Viele Grüße
Bernd

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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:
int flag = 0;

void TIM2_IRQHandler (void) {
  TIM2->SR = ~TIM_SR_CC1IF;
  if (flag)
    TIM2->CNT = 0;
  flag = 1;
  mycounter++;
}

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Stell das Problem mal ins STM32 Forum rein.

Autor: A. K. (prx)
Datum:

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

Autor: Bernd (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

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

Viele Grüße
Bernd

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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).

Autor: Matthias (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Ernst B. (puravida)
Datum:

Bewertung
0 lesenswert
nicht 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.
>
>
> servo_out[0] = 1500; // PA8
> servo_out[1] = 2000; // PA11
> servo_out[2] = 2500; // PA12
> servo_out[3] = 3000; // PA13
> servo_out[4] = 3500; // PA14
> 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)
>
> PA8
> |.|_____________________________________|.|_____________________________________
> PA11
> __|..|____________________________________|..|__________________________________
> PA12
> _____|...|___________________________________|...|______________________________
> PA13
> _________|....|__________________________________|....|_________________________
> PA14
> ______________|.....|_________________________________|.....|___________________
> PA15
> ____________________|......|________________________________|......|____________
> 
> 

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

Autor: RP6Conrad (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.
 TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
  TIM_OCInitTypeDef  TIM_OCInitStructure;
  /* Time base configuration TIM1*/
  TIM_TimeBaseStructure.TIM_Period = 19999;//PWM freq. = 1MHz/20000 = 50Hz
  TIM_TimeBaseStructure.TIM_Prescaler = 23;// Timer loopt aan 24 MHz/24 = 1MHz
  TIM_TimeBaseStructure.TIM_ClockDivision = 0;
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
  TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
  /* PWM1 Mode configuration: TIM 1, Channel1 */
  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
  TIM_OC1Init(TIM1, &TIM_OCInitStructure);
  TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);
  /* PWM1 Mode configuration: TIM 1, Channel4 */
  TIM_OC4Init(TIM1, &TIM_OCInitStructure);
  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.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.