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.
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?
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.
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 :-)
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.
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.
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?
Auch wenn es nichts zum eigentlichen Thema beiträgt: short i=0; Globale Variable mit Namen i ist eine grandiose Idee;)
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).
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.
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.
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.
> 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.
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).
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.
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.
....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.
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.
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?
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.
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.
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.
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.
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?
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.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.