Forum: Mikrocontroller und Digitale Elektronik Hardware PWM Timer1 funktioniert nicht


von terminator (Gast)


Lesenswert?

Hi,

Ich möchte einen Modellbauservo mit hilfe des "phase correct pwm" modus 
des Timer1 eines Atmega 32 ansteuern. Um den Timer zum Laufen zu 
bekommen hab ich das Programm ganz einfach gemacht und den Top 
Wert(ICR1) so wie den Vergleichswert (OCR1A) fest eingestellt. Wenn ich 
alles richtig gemacht hab sollte ICR1 den Wert 20000 haben und OCR1A den 
Wert 18500. Das ergibt bei einem Vorteiler von 8 und dem invertierenden 
Modus eine Frequenz von 50Hz und eine Pulsweite von 1,5ms. Wie gesagt 
wenn ich alles richtig gerechnet habe. Hier mal der Programmcode:

#include <avr/io.h>
#include <avr/interrupt.h>
#include <inttypes.h>

ISR(TIMER1_COMPA_vect) {
}

int main(void) {

  DDRC = 0b1111111;
  DDRD = 0b1111111;
  DDRB = 0b1111111;
  TCCR1A |= (1<<COM1A0)|(1<<COM1A1)|(1<<WGM11);
  TCCR1B |= (1<<WGM13)|(1<<CS11);
  TIMSK |= (1<<OCIE1A);
  ICR1L = 0b00100000;
  ICR1H = 0b01001110;
  OCR1AL = 0b01000100;
  OCR1AH = 0b01001000;
  sei();

  while(1) {

  }
}

Das Problem ist jetzt am Pin 5 von PortD (OC1A) wird nichts ausgegeben. 
Und die ISR wird auch nicht ausgeführt. Wäre schön wenn ihr mir helfen 
könntet.
Danke schon mal im Voraus.

von Karl H. (kbuchegg)


Lesenswert?

terminator schrieb:
> Hi,
>
> Ich möchte einen Modellbauservo mit hilfe des "phase correct pwm" modus
> des Timer1 eines Atmega 32 ansteuern. Um den Timer zum Laufen zu
> bekommen hab ich das Programm ganz einfach gemacht und den Top
> Wert(ICR1) so wie den Vergleichswert (OCR1A) fest eingestellt. Wenn ich
> alles richtig gemacht hab sollte ICR1 den Wert 20000 haben und OCR1A den
> Wert 18500.


Siehs dir halt mal im Simulator an, welche Werte da tatsächlich 
zugewiesen werden.

Mir wäre das ja zu blöd, es so zu schreiben

>   ICR1L = 0b00100000;
>   ICR1H = 0b01001110;
>   OCR1AL = 0b01000100;
>   OCR1AH = 0b01001000;

Ich würde da ganz einfach schreiben

   ICR1 = 20000;
   OCR1A = 18500;

und den COmpiler seinen Job machen lassen. Selbst auf die Gefahr hin, 
dass der Code dann zumindest ein bischen besser lesbar und 
selbsterklärend werden würde.


PS:Wozu braucht man für ein Servo eine 'Phase correct PWM' und einen 
invertiert angesteuerten Pin?

von terminator (Gast)


Lesenswert?

Erst mal danke für die schnelle Antwort. Ok das man da direkt den Wert 
20000 hin schreiben kann wusste ich nicht. Werd ich gleich mal 
probieren. Phase Corect PWM nehm deshalb weil die fast pwm viel zu 
schnell ist.

von H.Joachim S. (crazyhorse)


Lesenswert?

Falls sonst alles korrekt ist, bist du auf einen bekannten Fehler 
hereingefallen. Bei 16bit-Registern: erst high-Register, dann 
low-Register schreiben. Die Reihenfolge ist zwingend nötig.
Und wenn du das dem Compiler überlässt (siehe Karl-Heinz) bekommst du 
gar nicht erst die Möglichkeit, es falsch zu machen :-)

von terminator (Gast)


Lesenswert?

Ich hab des jetzt so gemacht wie es der Karl Heinz geschrieben hat.
Also die Werte direkt rein geschrieben (ICR1=20000) und des 
funktioniert. Der Servo fährt dann genau auf die Mittelposition so wie 
ichs berechnet hatte. Das heißt des Problem is gelöst.^^
Jetzt muss ich nur noch des Fahren in mehrere Zwischenschritte 
unterteilen, damit der Servo langsam fährt. Das war des Ziel der ganzen 
Sache. Weil mit Software PWM kann die Schritte net klein genug machen, 
damit der Servo ruckelfrei fährt. Denn Sonst wird die ISR zu oft 
ausgeführt und das Programm zu sehr belastet.
Also nochmal Dnake für eure schnelle Hilfe.

von Karl H. (kbuchegg)


Lesenswert?

OK
Mit der ISR bist du da eh schon auf einem guten Weg.
Einfach in der ISR den OCR Wert in Schritten einer Vorgabe nachführen.
1
#define STEP 2
2
3
volatile uint16_t servoSoll;
4
5
ISR( ... )
6
{
7
  if( OCR1A < servoSoll )
8
  {
9
    if( OCR1A > servoSoll - STEP )
10
      OCR1A = servoSoll;
11
    else
12
      OCR1A += STEP;
13
  }
14
15
  else if( OCR1A > servoSoll )
16
  {
17
    if( OCR1A < servoSoll + STEP )
18
      OCR1A = servoSoll;
19
    else
20
      OCR1A -= STEP;
21
  }
22
}


WEnn du servoSoll einen Wert zuweist, fährt die ISR das Servo langsam 
und in kleinen Schritten an diese Position heran. Mit STEP legst du die 
Geschwindigkeit fest - je größer der Wert, desto schneller fährt das 
Servo.

von terminator (Gast)


Lesenswert?

Also hab des ganze jetzt mal so geschrieben:

#include <avr/io.h>
#include <avr/interrupt.h>
#include <inttypes.h>

short i=0;

ISR(TIMER1_COMPA_vect) {
  i++;
  if((i==1)&&(!(PINA &(1<<PINA0)))) {    //nach rechts fahren
    OCR1A--;;
  }
  if((i==1)&&(!(PINA &(1<<PINA1)))) {    //nach links fahren
    OCR1A++;
  }
  if(i==1) {
    i=0;
  }
}

int main(void) {

  DDRC = 0b1111111;
  DDRD = 0b1111111;
  DDRB = 0b1111111;
  DDRA = 0b0000000;
  PORTC = 0xFF;
  TCCR1A |= (1<<COM1A0)|(1<<COM1A1)|(1<<WGM11);
  TCCR1B |= (1<<WGM13)|(1<<CS11);
  TIMSK |= (1<<OCIE1A);
  ICR1=10000;
  OCR1A=9450-1;
  sei();

  while(1) {
    if(!(PINA &(1<<PINA0))) {
      PORTC &=~ (1<<PB0);
    }
    if(PINA &(1<<PINA0)) {
      PORTC |= (1<<PB0);
    }

    if(!(PINA &(1<<PINA1))) {
      PORTC &=~ (1<<PB1);
    }
    if(PINA &(1<<PINA1)) {
      PORTC |= (1<<PB1);
    }
  }
}

Zum Testen hab ich mal Taster eingebaut. Ein Taster für jede Richtung.

Der Servo fährt wunderschön. Kann den jetzt richtig langsam fahrn lassen 
ohne das man ein Ruckeln sieht. Wenn man jetzt noch bedenkt das des 
durch die Hardware PWM kaum das Programm belastet.^^
Die ISR wird aber nicht oft genug ausgeführt. Das heißt ich kann den 
Servo mit dieser Ansteuerung nicht schnell fahren lassen. Im Momment 
bestimmt die Variable 'i' wie oft die ISR durchlaufen werden muss bis 
das nächste mal das OCR1A Register erhöht wird. 'i' kann aber nicht 
kleiner sein als 1, also    habe ich mir überlegt den mit dem Timer0 per 
CTC Modus einen Takt zu erzeugen mit dem dann immer das OCR1A Register 
erhöht wird. Ist das eine gute idee oder gibt es da noch eine bessere 
Lösung?

von holger (Gast)


Lesenswert?

Auch wenn es nichts zum eigentlichen Thema beiträgt:

short i=0;

Globale Variable mit Namen i ist eine grandiose Idee;)

von Hans (Gast)


Lesenswert?

Karl Heinz hat doch geschrieben, wie es schneller geht: Indem Du den 
Wert nicht nur um eins erhöhst sondern einen größeren Wert (STEP).

von terminator (Gast)


Lesenswert?

Achso stimmt daran hab ich gar nimme dran gedacht das ich au die 
Schrittgröße verändern kann. Ok danke für den Hinweis Hans.

von Karl H. (kbuchegg)


Lesenswert?

terminator schrieb:
> Achso stimmt daran hab ich gar nimme dran gedacht das ich au die
> Schrittgröße verändern kann. Ok danke für den Hinweis Hans.


Aber schau dir meinen Code nochmal genau an!

Denn im Endeffekt wirst du ja nicht solange fahren wollen, wie du die 
Taste drückst, sondern du wirst ja wahrscheinlich mit dem Taster eine 
der beiden Endpositionen vorgeben, in die das Servo fahren soll.

Und wenn du die SChrittweite so wählst, dass die Endposition kein 
entsprechendes Vielfaches davon ist, dann wird dein Servo entweder
* die Endposition verpassen und weiter fahren
* oder darüber hinausschiessen


Hmm. Von der Tastenabfrage in der ISR bin ich ehrlich gesagt nicht 
begeistert. Kommt drauf an, was du machen willst, aber für meinen 
Geschmack ist da ein Zusammenhang in der ISR fix codiert, den ich dort 
nicht haben will. Die ISR sollte meiner Meinung nach eine Servoposition 
anfahren. WO auch immer die her kommt. Zb indem man einen Taster drückt. 
Aber auch, weil zb bei einem FLugzeug Einziehfahrwerk der Akku langsam 
zur Neige geht und die Elektronik als eine Art 'Achtung' das Fahrwerk 
ausfährt. D.h. es mag im Programm viele Stellen geben, die eine 
bestimmte Servoposition vorgeben. Welche das sind hat aber die ISR nicht 
zu interessieren. Die hat die Vorgabe das Servo an eine bestimmte 
Position zu fahren. Nicht mehr und nicht weniger. Wo die Position her 
kommt, wie die zustande kommt, hat die ISR nicht zu interessieren.

von terminator (Gast)


Lesenswert?

Ja klar möchte ich im fertigen Programm nicht solange fahren wie ich 
einen Taster gedrückt halte. Das ist ja nur ein einfaches Programm um 
die Servo Ansteuerung zu testen.
Das soll irgendwann mal wenn es eben so weit fertig ist einen Hexapod 
steuern (mit 18 Modellbauservos). Denn bisher läuft der Hexapod mit 
einem Atmega32. Dieser erzeugt mithilfe des Timer0 die 18 Servosignale 
Software mäßig(CTC-Modus). Jetzt möchte ich aber das Projekt erweitern 
und dazu will ich die Servos wahlweise schnell und langsam fahrn lassen 
können und auch die Position über den internen Potti der Servos abfragen 
können(über ADC). Bisher kann ich die Servos in 0,1ms Schritten 
anteuern. Das sind natürlich viel zu große Schritte um den Servo 
anständig langsam fahren zu lassen. Also habe ich mir überlegt ein 
Bussystem aufzubauen. Mit 6 atmega8 als Slave und jeder bekommt jeweils 
3 Servos zum ansteuern. Als Master nehm ich einen Atmega16/32 der dann 
den Slaves immer die positionen für die Servos schickt. Dann muss ein 
Chip nicht wie vorher die Signale für 18 Servos sondern nur noch für 3 
Servos erzeugen. Ein weiterer Vorteil des Bussytems ist das ich durch 
die Slaves viel mehr ADC Pins habe. Das Problem ist dann gleich gelöst. 
Meine aktuelle Planung war diese 6 atmega8 ihre drei Servos mit Soft PWM 
ansteuern zu lassen. Da das aber immer noch ein bisschen ruckelt, trotz 
einer Ansteuerung in 0,01ms Schritten, beim langsam fahren möchte ich 
doch auf die Hardware PWM umsteigen.
Problem hierbei ist allerdings das der atmega8 nur zwei Pins besitzt, 
die mit dem Timer1 also sprich mit einem 16bit verbunden sind
(8 bit Timer reicht nicht von der Auflösung). Das heißt im Endeffekt 
mehr Aufwand. Denn ich brauch anstatt 6 chip jetzt 9 (2 Servos pro 
chip). Das heist dann auch mehr Platinen und mehr Arbeit und mehr Geld. 
Darum wollte ich nun prüfen ob es sich lohnt auf die Hardware PWM 
umzusteigen.
Vielleicht habt ihr noch ein paar Ideen wie ich das lösen könnte. Hab 
mir schon überlegt anstatt ein atmega8 ein atmega1284p als Slave zu 
verwenden. Dieser Besitzt 4Pins die mit einem 16bit Timer verbunden 
sind. Der kostet aber beim pollin 6€ das Stück.
Hoffe das ich euch nicht zu sehr zu getextet hab und das mein Problem 
verständlich ist. Danke schon mal im voraus.

von Karl H. (kbuchegg)


Lesenswert?

> Als Master nehm ich einen Atmega16/32 der dann den Slaves immer
> die positionen für die Servos schickt. Dann muss ein Chip nicht
> wie vorher die Signale für 18 Servos sondern nur noch für 3
> Servos erzeugen

Den Teil versteh ich nicht.
Der eine Mega32 langweilt sich mit der Erzeugung der 18 Servosignale.
Und wozu musst der die Position wissen? Der gibt die Position vor! Und 
in dem Tempo wie der die Positionen vorgibt, so schnell bzw. langsam 
fahren dann auch die Servos.

> Da das aber immer noch ein bisschen ruckelt, trotz einer Ansteuerung
> in 0,01ms Schritten, beim langsam fahren möchte ich doch auf die
> Hardware PWM umsteigen.

Das ist völlig wurscht, denn mehr als 50 mal in der Sekunde kriegt ein 
Servo keinen Positionsupdate. Da kannst du in noch so feinen 
Zeitschritten rechnen, das ändert nichts daran, dass von einem 
Servoupdate zum nächsten ca. 20ms vergehen. Zeitlich kannst du diesen 
Update nicht feiner auflösen. Wenn, dann musst du daran arbeiten, dass 
du mehr 'Stufen' in jedem Puls bekommst. Aber das kannst du mit Software 
Erzeugung genausogut machen, wie in Hardware. Das schenkt sich nichts.

Beim Beispiel auf
Modellbauservo Ansteuerung
sind es nur deswegen wenig Stufen, weil da der Timer 2 (ein 8 Bit Timer) 
benutzt wird. Aber das muss ja nicht so bleiben. Mit dem Timer 1 und 
einem kleineren Vorteiler kriegst du dann auch mehr verfügbare 
Servostellungen pro Servo und dadurch dann auch weniger Verfahrweg wenn 
du die jeweilige Servostellung im Programm um +-1 veränderst.

von terminator (Gast)


Lesenswert?

Also hab im Anhang mal des Programm mit der Ansteuerung, die mit 0,1ms 
Schritten arbeitet. Eben das Programm mit dem der Hexapod seither 
betrieben wird(ohne langsam Fahrt der Servos -> die fahren also immer 
Vollgas).
Zu groben Erklärung wie ich in deisem Programm die Signale erzeugt habe:
Also in der ISR wird die Variable i alle 0,1ms erhöt. Bei i=0 werden die 
ersten 6 Pins an denen Servos hängen auf high gesetzt und je nach 
Position einzeln wieder auf low. Das läuft dann bei den anderen Servos 
genauso (die nächsten 6 bei i=25 und die letzten 6 bei i=50) bis i 
wieder 0 ist. i wird immer bei 100 wieder auf 0 gesetzt -> das 
entspricht 100Hz.
Wenn ich alle auf einmal anteuern würde gehts nicht weil der chip zu 
langsam ist(sollte vielleicht erwähnen das der mit 16MHz läuft->also 
maximum). Mit zu langsam mein ich bevor alle if Befehle zum rücksetzten 
der Pins ausgeführt werden wird i wieder erhöht und der Pin bleibt 
solange high bis irgendwann mal zufällig die Zahl erwischt die er zum 
rüclsetzen braucht. Also das habe probiert und nur die ersten paar 
Servos fahren dann richtig und der Rest macht auser zucken nichts. Jetzt 
stell dir mal vor der timer läuft 10mal schneller (0,01ms Schritte).

Die Positionsabfrage der Servos will ich deshalb machen weil irgendwann 
soll des Ding sich ja auch Bewegen. Dazu muss bei einer sehr einfachen 
Bewegung das Bein angehoben werden, danach nach vorne bewegt werden und 
dann wieder abgesetzt werden. Also fang ich an mit Bein anheben. Die 
entsprechenden Servos benötigen jetzt aber Zeit diese neue Position 
anzufahren. Wie lange braucht er? Oder anderst gefragt wann ist er an 
der Soll position angekommen? Sprich wann kann der nächste Servo das 
Bein nach vorne Bewegen. Dies soll ja erst geschehen wenn das Bein oben 
ist und nicht früher aber auch nicht später. Bisher hab ich das so 
gelöst das immer ein Timer Takt den nächsten Schritt eingeleitet hat. 
Das ist aber ungenau und mir zu blöd. Vorallem wenn dann noch schnell 
langsam fahren dazu kommt. Oder mal komplexere Bewegungen realisiert 
werden sollen. Dann komm ich da eigentlich nicht um die Positionsabfrage 
rum wenn ich das anständig machen will.

Als nächstes kann ich noch sagen das die Ansteuerung des Servos mit 
16bit Timer WESENTLICH ruhiger ist als mit der Soft PWM und 0,001 
Schritte. Das liegt wahrscheinlich daran das ich bei so kleinen 
Schritten nicht alle 10 oder 20ms ein update mache sondern halt alle 
50-100ms.(je nach Geschwindigkeit).

von terminator (Gast)


Angehängte Dateien:

Lesenswert?

...ups hab die datei vergessen^^

von Karl H. (kbuchegg)


Lesenswert?

terminator schrieb:
> Also hab im Anhang mal des Programm mit der Ansteuerung,

Sieh dir mal in
Modellbauservo Ansteuerung
an, wie man eine derartige Menge an Servosignalen vernünftig erzeugt.

Das in deiner Variante der µC am Anschlag ist, wundert mich nicht 
wirklich. Und das du mit 100 Verfahrpositionen pro Servo keinen 
Blumentopf gewinnen wirst, ist mir auch klar.

Das Prinzip aus dem Link auf den Timer 1 bei einem kleinern Vorteiler 
übertragen (damit der mögliche 16 Bit Zahlenraum besser ausgenutzt wird) 
und du kannst deine Servos so feinfühlig und langsam fahren wie du 
willst.

von terminator (Gast)


Lesenswert?

OK. Also ich muss ehrlich sagen das ich das Programm nicht so ganz 
verstehe, weil die alles mit so blöden #define dingern da machen. Dann 
muss man immer schauen was des jetzt bedeuten soll.  Wie funktioniert da 
genau die Signal Erzeugung. Vielleicht könnstest du mir das mal erklären 
oder einen stark vereinfachten Programm code für einen Servo schicken 
ohne die ganze #define Befehle.

von terminator (Gast)


Lesenswert?

....Achso. Also wenn ich das richtig verstehe machen die des so das die 
ein Pin von einem Servo auf high setzen und dann den Timer so einstellen 
das die interrupt routine nach der gewünschten Zeit für die Position 
also z.b. 1,5ms (für Mittelposition) wieder ausgeführt wird. Und dann 
wird der Pin gelöscht. Richtig?
Das is ja ne geile Idee. Gefällt mir richtig gut. Aber an den Bus werd 
ich trotzdem nicht vorbei kommen wegen der Positionsabfrage. Aber das 
macht ja auch nichts das war ja icht das Problem.

von Karl H. (kbuchegg)


Lesenswert?

terminator schrieb:
> ....Achso. Also wenn ich das richtig verstehe machen die des so das die
> ein Pin von einem Servo auf high setzen und dann den Timer so einstellen
> das die interrupt routine nach der gewünschten Zeit für die Position
> also z.b. 1,5ms (für Mittelposition) wieder ausgeführt wird. Und dann
> wird der Pin gelöscht. Richtig?

Richtig.
Und dann kommt der nächste Pin drann.
Immer reihum.

Ganz einfach. Und da der Timer die ganze 'Zeitarbeit' macht, hat die CPU 
praktisch damit nichts zu tun. Das bischen Pinwackeln alle 
(durchschnittlich) 1.5ms kostet praktisch so gut wie keine Rechenzeit.

von terminator (Gast)


Lesenswert?

Jetzt hätt ich aber doch noch ein Bedenken und zwar wenn ich jetzt 
langsam fahren will dann warte ich ja nicht zum Beispiel um von Position 
ist (1,0ms)
auf Position soll (2,0ms) zu kommen eine millisekunde sondern ich fahr 
ja in kleinen schritten das bedeutet z.b. wie es jetzt beim 16bit 
Hardware Timer is 0,0005 ms Schritte. Dann würde ja auch die ISR, wenn 
ich es so mach wie auf der Seite von dem Link beschrieben, alle 0,5µs 
ausgeführt werden. Versteh ich das richtig so?
Dann hab ich doch wieder das Problem das die ISR viel zu oft ausgeführt 
wird und der Chip nebenher nichts mehr anderes tun kann oder nicht?

von Karl H. (kbuchegg)


Lesenswert?

terminator schrieb:
> Jetzt hätt ich aber doch noch ein Bedenken und zwar wenn ich jetzt
> langsam fahren will dann warte ich ja nicht zum Beispiel um von Position
> ist (1,0ms)
> auf Position soll (2,0ms) zu kommen eine millisekunde sondern ich fahr
> ja in kleinen schritten das bedeutet z.b. wie es jetzt beim 16bit
> Hardware Timer is 0,0005 ms Schritte.

Bitte hör auf an dieser Stelle in Millisekunden-Schritten zu denken!

AUfgrund der Auslegung der Pulserzeugung hast du für den kompletten 
Servoweg eine bestimmte Anzahl an Schritten verfügbar. Beim Beispiel im 
Link sind das so was um die 100 Schritte. D.h. mehr als 100 verschiedene 
POsitionen kann das Servo nicht anfahren. Das liegt aber am Timer 2 und 
daran, dass das nur ein 8 Bit Timer ist.
Wenn du das auf den Timer 1 umstellst, kannst du aber weit mehr mögliche 
Servopositionen erreichen. Sagen wir mal 2000. D.h. du kannst den Weg 
von ganz links bis ganz rechts in 2000 Zwischenpositionen zerlegen. Und 
so ca. alle 15 Millisekunden kannst du einen anderen dieser 2000 
Zwischenschritte an das Servo ausgeben lassen. Das kann von ganz links 
nach ganz rechts sein, das kann aber auch von einer Position zur 
unmittelbar benachbarten Servoposition sein. Und bei 2000 möglichen 
Servopositionen für den kompletten Verfahrweg, liegen 2 benachbarte 
Servopositionen so dicht beisammen, dass ich ehrlich gesagt meine 
Zweifel habe, ob das Servo das überhaupt genau genug realisieren kann.


> Dann würde ja auch die ISR, wenn
> ich es so mach wie auf der Seite von dem Link beschrieben, alle 0,5µs
> ausgeführt werden. Versteh ich das richtig so?
Nein, das verstehst du falsch.
Denn das Timing ist dir vom Servo aufs Auge gedrückt. Das kannst du 
nicht ändern.

Was dir frei steht ist, wie schnell und welche Zahlenwerte in die 
Servo-Variablen geschrieben werden. Aber schneller als ca 15ms da neue 
Werte reinzuschreiben ist sinnloss, weil es von einem mal ans Servo 
Übertragen bis zum nächsten mal an dasselbe Servo Übertragen eben diese 
ca 15ms dauert. Und diese Zeit kannst du nicht ändern! Servos sind zwar 
meistens nicht besonder heikel, was ihre Updatefrequenz angeht, aber 
weniger als 10ms geht meistens nicht.


Das was der Timer mit den Pins anstellt und welches Timing er 
realisiert, hat nichts mehr damit zu tun, in welchen Zeitabständen du 
neue Servopositionen an die Servos gibt. Sieh diesen Timer als Black Box 
an. Du schreibst deine gewünschten Positionen auf die Variablen und die 
Servos fahren in diese Position. Aus. Ende. Diese Variablen mit den 
Werten die du reinschreiben kannst, SIND deine Servos. Der Mechanismus, 
wie die pyhsikalischen Servos dann ihre Sollposition mitgeteilt bekommen 
hat dich nicht mehr zu inetressieren. Das geschieht 'magisch'. (OK, wir 
wissen, das das nicht magisch passiert, sondern das der Timer mit ein 
wenig ISR Unterstützung das erledigt. Aber für darüberliegende 
Softwareschichten ist das 'magisch', weil sie damit nichts zu tun haben. 
Die liefern ihre Erkentnisse in Form von Werten in diesen Variablen ab 
und mehr braucht sie nicht zu interessieren)
Und wenn du langsam fahren willst, dann stellst du eben nach einem 
Zeitschema sukzessive Zwischenpositionen in diese Variablen. Und damit 
auch nichts ruckelt, brauchst du genügend mögliche Zwischenpositionen.

von terminator (Gast)


Lesenswert?

Natürlich stimmt. War ein Denkfehler von mir. Die Schrittgröße wird ja 
auf die Ist Position des Servos drauf addiert. Dann wäre also bei meinen 
Servos der kürzeste Zeitraum von ISR Aufruf bis zum nächsten ISR Aufruf 
0,6ms(ganz links)+"die schrittgröße(z.b. 0,5µs)" und nicht wie ich 
vorhin gedacht hab nur die Schrittgröße allein.
Und wenn ich in meinem Hexapod Projekt nur drei Servos pro chip zum 
ansteuerun hab, bau ich mir einfach einen "Leerlauf"(also ich mein ein 
Zeitraum in dem nichts angesteuert wird) ein, damit die Frequenz nicht 
zu hoch wird; oder meinst du das macht den servos nichts aus?
Dann kann der chip das ganze sogar so berechnen, dass die Frequenz immer 
die gleiche ist und abhänig von den einzelnen Impulslängen der Servos.
Wenn das jetzt so funktioniert wie ich mir das im Momment vorstell und 
überlegt hab dann ist richtig gut.

Vielen Dank Karl Heinz. Das hat mir sehr geholfen, denn ich glaub ich 
wäre mit den beiden Methoden(1.meine HW PWM, die zu viel Chips 
gebraucht hätte und 2. meine schlechte eigene Soft PWM -> sollte ich 
vielleicht auch noch dazu sagen das diese Programm vor einem Jahr 
entstanden ist, kurz nachdem ich mit C Programmierung und 
Mikrocontroller Programmierung begonnen hatte), die ich mir zu Beginn 
überlegt hatte nicht besonders glücklich geworden.

von terminator (Gast)


Angehängte Dateien:

Lesenswert?

So. Hab jetzt mal ein Testprogramm geschrieben. Die Anteuerung 
funktioniert genauso wie in dem Link beschrieben ist. Nur hab ich noch 
etwas eingefügt das die Frequenz immer gleich halten soll. Weil sie ja 
sonst von den einzelnen Impulsen abhängt. Das macht zwar den Servos 
nichts aus aber wenn ich den Servo langsam fahren lasse, also jedesmal 
wenn die ISR ausgeführt die Positions variable erhöhe dann wird der 
Servo je näher er seinem Ziel kommt immer langsamer. Weil ja durch die 
längeren Impulse auch die Zeit zwischen den ISR Aufrufen erhöht wird und 
damit auch das Positions Update. Allerdings funktioniert das Programm 
nicht so wirklich.
Also die Überlegung ist sobald alle 8 Servo Signale einmal ausgegeben 
sind soll der Timer1 abgeschalten werden und der Timer0 die restliche 
bis 20ms (für 50Hz) so zu sagen füllen oder überbrücken. Also z.B. wenn 
jetzt alle Signale für die 8 Servos 1,5ms betragen, dauert es ja 12ms 
dann würde es wieder von vorne beginnen. Dann hätte ich aber eine 
Frequenz von über 50Hz. Jetzt kommt der Timer0 ins spiel. Der soll nach 
der letzten Signalausgabe gestartet werden und in diesem Falle 8ms 
laufen. In dieser Zeit soll dann kein Servo Singal ausgegeben werden. 
Wenn dann nach den 8ms der Timer1 wieder von vorne beginnt mit der 
Ausgabe der Signale habe ich genau die 50Hz.

Schaut euch mal das Programm an, vielleicht habt ihr ja noch andere 
Verbesserungsvorschläge oder findet noch mehr Fehler. Danke schon mal im 
Voraus.

von Karl H. (kbuchegg)


Lesenswert?

Du kannst natürlich ein fiktives 9. Servo einführen, welches die Zeit 
aufbraucht, um einen kompletten 9-Servo Zyklus auf 20ms zu bringen.

Würd ich allerdings nicht unbedingt tun. Denn wie du schon sagst, den 
Servos ist das egal.
Mein Ansatz wäre, den Timer 1 Timer 1 sein zu lassen und den in Ruhe 
seine Servosignale erzeugen zu lassen. Weitergehende andere Timings, 
greifen dann nicht mehr auf den Timer 1 zurück sondern benutzen eben 
einen anderen Timer,  der sein regelmässiges Taktsignal als 
Basis-Zeittakt erzeugt. Der Timer 0 und der Timer 2 sind ja noch übrig. 
Kein Mensch sagt, dass der Timer 1 das komplette Timing des Programms im 
Alleingang erledigen muss.

Timer 1 macht die Servopulse
Timer 0 ist unter anderem dafür zuständig, dass die Servopositionen 
regelmässig korrekt und in der richtigen Geschwindigkeiten an die 
jeweilige Zielpositionen nachgeführt werden.


Aber was du auch tust: Derartige 'Inter-Timer' Lösungen sind oft viel zu 
kompliziert und funktionieren auch oft nicht besonders gut. Timer möchte 
man durchlaufen lassen. D.h. der wird einmal gestartet und danach läuft 
er. Und läuft und läuft und läuft.

von terminator (Gast)


Lesenswert?

Karl Heinz mal wieder Danke für die schnelle Antwort.  Aber ich versteh 
nicht ganz wie du das meinst. Timer1 macht die Pulse für die Servos und 
Timer 0 ist unter anderem dafür zuständig, dass die Servopositionen 
regelmässig korrekt und in der richtigen Geschwindigkeiten an die 
jeweilige Zielpositionen nachgeführt werden. Könntest du mir das 
vielleicht ein bisschen genauer erklären?

von Karl H. (kbuchegg)


Lesenswert?

Eine ganz normale, einfache Nachführung der aktuellen Position auf die 
vorgegebene Zielposition in diskreten Zeitschritten.
1
uint16_t current_position[Servo_quantity];
2
volatile uint16_t goal_position[Servo_quantity];
3
volatile uint16_t stepSize[Servo_quantity];
4
5
ISR( ... )   Timer 0, zb alle 1ms
6
{
7
  uint8_t i;
8
9
  for( i = 0; i < Servo_quantity; i++ )
10
  {
11
    if( current_position[i] < goal_position[i] )
12
    {
13
      if( current_position[i] < goal_position[i] - stepSize[i] )
14
        current_position[i] += stepSize[i];
15
      else
16
        current_position[i] = goal_position[i];
17
    }
18
19
    else if( current_position[i] > goal_position[i] )
20
    {
21
      if( current_position[i] > goal_position[i] + stepSize[i] )
22
        current_position[i] -= stepSize[i];
23
      else
24
        current_position[i] = goal_position[i];
25
    }
26
  }
27
}

anstatt an servo_position[i] weist du die Ziel Position des Servos an 
goal_position[i] zu. Der Wert in stepSize[i] bestimmt wie schnell das 
Servo fahren soll. Er gibt an, um wieviel sich der Servo Positions Wert 
von einem ISR aufruf zum nächsten verändern kann. Wird diese ISR also 
alle 1ms aufgerufen, dann ist der Wert genau 1/1000 des Verfahrwegs in 1 
Sekunde. Die 1 Millisekunde ist aber auch nicht Pflicht. Du kannst 
selbstverständlich auch andere Zeitschritte benutzen.

(und natürlich wird das Servo nicht jede 1 ms einen Schritt machen. Aber 
wenn das Servo innerhalb vom Timer 1 das nächste mal drann ist, macht es 
dann den kumulierten Schritt aller 1 Millisekunden Schritte, die seit 
dem letzten mal angefallen sind.

Da sich das ganz immer noch im Milli- bzw. Hunderstelsekundenbereich 
abspielt, wird da dann trotzdem immer noch nichts ruckeln.

von Karl H. (kbuchegg)


Lesenswert?

Lass den Timer 1 durchlaufen!

(und tu dir selbst einen Gefallen und vergiss, dass du irgendwann mal 
von int und short gehört hast. Deine Datentypen sind
uint8_t, uint16_t, uint32_t
int8_t, int16_t, int32_t

Denn da hast du dann die saubere Kontrolle über die Bitbreiten. Die 
stehen direkt im Datentyp, ohne dass man lange überlegen muss, was jetzt 
was ist.
uint16_t  ...  unsigned integer mit 16 Bit

wenn immer möglich, möchtest du uint8_t nehmen. Das ist der Leib und 
Magendatentyp vom AVR. Damit gehts am schnellsten. Nicht mit int und 
nicht mit short. Mit uint8_t - einem Ganzzahl-Datentyp mit 8 Bit und 
ohne Vorzeichen.
)

1
ISR(TIMER1_COMPA_vect)
2
{
3
  Servo_Port = 0x00;
4
5
  Servo_ID++;
6
  if( Servo_ID == Servo_quantity )
7
    Servo_ID = 0;
8
9
  Servo_Port = Servo_Bit[Servo_ID];
10
  OCR1A += current_position[Servo_ID];
11
}

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.