Hallo Leute,
anbei habe ich mein Problemkind gepackt. Die Schaltung, wie sie im PDF
zu sehen ist, habe ich mit Ausnahme des Adapters K5 auf einem Testboard
nachgebaut. Zur optischen Anzeige habe ich noch an den Servoausgängen je
eine LED hängen.
Es ist soll eine Servosteuerung werden, welche recht einfach gehalten
ist. Sie dient für meine Experimentierstrecke zur Weichensteuerung durch
Servos. Je eine LED über den Tasten soll den Umlauf optisch anzeigen
(Blinklicht).
Die Bedienung soll durch einfachen Tastendruck auf S2 für Linkslage,
bzw. S3 für Rechtslage erfolgen.
Ausgangslage der Steuerung ist eine enfache Servoansteuerung, welche ich
mir aus den Videos von Stefan Wintgen auf YouTube entnommen habe.
Wenn ich nun das System starte, werde in der Initialisierung die drei
LED's (PB0 .. PB2) für ca. 1 sec. aktiviert und dann wieder deaktiviert.
Dies dient der Kontrolle. Danach, nach ca. 1. sec. fangen die LED's PB1
und PB2 an zu leuchten. Gleichzeitig habe die Kontroll-LED'S der Servo
ein schnelles Flackern. Eine Taste wurde nicht gedrückt. Auf den
externen Taster (PD3 / INT 1 - siehe unten) wird nicht reagiert. Ein
angeschlossener Servo macht was er will, wer er was macht.
Der Funktionsablauf sollte allerdings so sein:
Nach dem Systemstart sollen die beiden Servos (Servo 1 und Servo 2) in
Linkslage gehen (falls noch nicht geschehen). Wenn ich jetzt die Taste
S3 drücke, sollen die Servos (beide) in Rechtslage wechseln. Während
einer Zeit von ca. 5 sec. soll die LED 2 (PB1) blinken. Wenn die Weiche
dann in Rechtslage ist, soll beim drücken der Taste S2 soll dann die
Servos (wieder beide) in Linkslage wechseln. Gleichzeitigt soll wieder
für ca. 5 sec. die LED 1 (PB0) blinken.
Der Taster S1, welcher an PD2 (INT0) liegt, wird erstmal nicht
verwendet. Der externe Taster 1, welcher an PD3 (INT1) liegt, soll das
System in den Programiermodus setzten. Der Programiermodus dient später
dazu, die Werte für die Servoendlage fein einzustellen und ins EEPROM zu
schreiben. Noch ist diese Funktion nicht implementiert, lediglich die
ISR ( INT1_vect ) ist hierfür vorhanden. Sobald der MC in dem
Programiermodus ist, soll die LED 3 (PB2) blinken und kein Umstellen der
Servos mehr erlauben.
Der Timer0 (8-Bit) dient dazu, die Taktperiode von 25ms zu generieren.
Da es nur ein 8-Bit ist, musste ich mir etwas einfallen lassen, um auf
25000 Impule zu kommen.
Der Timer1 (16-Bit) dient dazu, die Impulsweite zu generieren. Dazu
verwende ich den Timer1_CompA für den Servo 1 und den Timer-CompB für
den Servo 2.
Was ich jetzt noch nicht implementiert habe, ist das Entprellen der
Taster und auch das Speichern der Servoendlagenwerte im EEPROM. Daher
arbeite ich mit festen Werten für die Servoendlage. Zum testen sollte
dies reichen.
Ich weis, das es nicht der Weisheit letzter Schluss ist und auch der
Code sicher noch optimiert werden kann. Und ja, es ist noch Test-Code
drin, welcher später dann entfernt werden wird.
Weis jemand, wo ich den Fehler gemacht habe, bzw. was die Ursache ist,
weshalb es nicht funktioniert? Woher kommt das Dauerleuchten von LED 1
und LED 2 (PB0 und PB1)? Das Flacker der Servo-LED's könnte ich mir
erklären, wenn der angeschlossene Servo beim Start in die Linkslage
gehen würde und dort bleibe. Aber da er teilweis hin und her geht, kann
da auch etwas nicht stimmen.
Liebe grüße,
Sarah
Viel zu kompliziert.
Sieh dir in
Modellbauservo Ansteuerung
den letzten Codeabschnitt an.
Dort findest du Code, der maximal 8 Servos mit einem einzigen Timer
ansteuern kann. Alles was du noch einbauen musst sind deine Taster. Da
du sowieso für jede Endlage eine eigene Taste benutzt, brauchst du
nichts entprellen und du brauchst auch keine Interrupts um die Tasten
abzufragen. Alles was du tun musst, ist bei gedrückter Taste dem
jeweiligen Servo im globalen Array den richtigen Verdrehwert zuzuweisen.
Im Beispiel wird der Timer 2 verwendet, weil sich bei 11Mhz noch
einigermassen vernünftige Servo-Positionsabstufungen ergeben. (*) Wenn
dir die nicht reichen, dann kann man auch den 16 Bit Timer mit einem
anderen Vorteiler benutzen um die Auflösung zu erhöhen. Da aber der 16
Bit Timer ein 'weretvolleres' Gut ist, als der 8-Bit Timer 2, wollte ich
den absichtlich nicht für so etwas Profanes wie die Generierung von
Servopulsen verwenden.
Tip: wenn du weniger als 8 Servos ansteuern willst, dann lass den Code
trotzdem so wie er ist und verwende ganz einfach die entsprechenden
Ausgänge nicht (bzw. steuere die in der ISR erst gar nicht an). Dann
brauchst du dich nämlich nicht um die Wiederholfrequenz der Servopulse
kümmern. Die ergibt sich automatisch im richtigen Zeitrahmen, einfach
durch das hintereinander Abarbeiten von 8 Pulsen, die sich im Zeitraum
von 1 bis 2 ms bewegen und da Servos diesem Detail der Wiederholfrequenz
keine allzugroße Bedeutung beimessen, ist es ihnen egal, ob sich der
Puls im Zeitraster von 8 ms aufwärts bis ca 16ms wiederholt. Stelle die
nicht benötigten Servos auf 1.5ms ein (also Mittelstellung), übergeh sie
in der ISR, wenn der entsprechende zugehörige Pin geschaltet werden soll
und gut ists. 6*1.5 macht 9ms, dann noch im Mittel 3ms für die beiden
übrig gebliebenen Servos mit dazu, macht in Summe 12ms
Pulswiederholzeit, was im Normalfall keinerlei Probleme macht. Und wenn
doch, dann stellst du eben die nicht benötigten Servos auf 2ms um die
Kompletzeit für einen Durchlauf durch alle Servos in die Höhe zu
treiben.
Diese Zeit für die Pulswiederholung ist bei der Generierung von
Servopulsen das Unwichtigste.
(*) gerade nachgerechnet.
Bei 8 Mhz kriegst du immerhin noch 64 unterscheidbare Servo-Positionen.
Wenn dir das reicht, dann brauchst du am angegebenen Code nicht viel
grundsätzliches ändern, sondern nur die Anpassung an den Tiny in den
Timer-Konfigurationsregistern machen. Die Änderungen sollten aber
überschaubar sein, schliesslich basiert alles auf einem ganz
gewöhnlichem Timer-Modus mit aktiviertem Compare-Match Interrupt.
> Servo beim Start in die Linkslage gehen würde und dort bleibe.> Aber da er teilweis hin und her geht, kann da auch etwas nicht stimmen.
Wenn ich mal annehme, dass es sich nicht um einen Programmfehler
handelt:
Servos können Dreckschweine sein, wenn es darum geht die
Versorgungsspannung zu verunstalten. Bei mehreren Servos und einem µC an
derselben Stromversorgung sollte man der Qualität und der Siebung der
Versorgungsspannung besonderes Augenmerk schenken.
Hallo Karl Heinz,
erst einmal vielen dank für deine Antworten. Und auch ein kleines sorry,
das ich mich erst jetzt melde. Auch wenn ich gerade Urlaub habe, so gibt
es doch zu viele Dinge, die ich zu erledigen haben.
Aber nun zu deinen Antworten. Ja, es ist schon ein wenig Aufwändig, was
ich hier entwickelt habe (auch wenn der Wurm drin ist). Für mich selbst
ist es mein erstes wirkliches Porjekt. Und ich finde diese Konfiguration
ganz akzeptabel. Sicherlich werde ich im Laufe der Zeit, bzw. bei den
weiteren Projekten noch sehr viel lernen und auch sicherlich vieles
optimieren. Aktuell aber würde ich mich gerne an diesem Aufwand halten.
Ich habe mir den Code mal angeschaut, den du mir empfohlen hast. Ist
natürlich kein Vergleich zu meinem, zumal auch eine andere Ausgangslage
zugrunde liegt. Aber ich gebe zu, das ich diesen Code zu adaptieren
versuche.
Was ich aber ändern werde, ist der verwendete Timer. Da ich zum einen
nur einen ATtiny habe, stehen mir nur ein 8-Bit und ein 16-Bit zur
Verfügung. Und den 16'er habe ich für nichts anderes vorgesehen, wenn
ich dieses Code zugrunde lege. In meiner Version habe ich ihn ja schon
verwendet, weswegen ich ja auf den 8'er ausweichen musste.
Bei der Frequenz liege ich bei 8 MHz, so das ich hier bei dem 8'er nur
64 Servostufen habe und ich nicht glaube, das diese wirklich ausreichen
werden. Wie schon geschrieben, soll diese Schaltung zur Ansteuerung
einer Weiche via Servo erfolgen und ich denke, das hier eine weitaus
feiner Abstufung notwendig ist. Die Weiche soll ja auch gut anliegen und
nicht zu weit abstehen, bzw. zu weit an die Schiene drücken soll. Ich
möchte weder die Backenschiene überlasten, noch den Servo.
Karl Heinz schrieb:> Wenn ich mal annehme, dass es sich nicht um einen Programmfehler> handelt:
Was ich nicht wirklich ausschließen kann. Ich habe mich bei der
Entwicklung an dem Code von dem Stefan Windgen orientiert und bei der
8-Bit versucht, die einzelnen Register aus dem Datenblatt zu lesen. Da
mein Englisch aber für die Tonne ist, kann es sein, das ich den ein oder
anderen Registereintrag falsch gesetzt habe.
TIMSK|=(1<<OCIE0B);// Timer-Interupt's für Vergleichswerte
Bei dem (1<<WGM02) bin ich nicht sicher, da ich glaube im Datenblatt
etwas von WGM2 gelesen zu haben.
> Servos können Dreckschweine sein, wenn es darum geht die> Versorgungsspannung zu verunstalten. Bei mehreren Servos und einem µC an> derselben Stromversorgung sollte man der Qualität und der Siebung der> Versorgungsspannung besonderes Augenmerk schenken.
Ja, davon habe ich schon gehört. Zumal ich auch recht günstige vom C
verwende. Was allerdings keine pauschale Ausage sein muss.
Ich habe in Sachen Spannung/Versorgung ja auch vorgesehen, diese ein
wenig zu entkoppeln (wenn ich das so nennen kann). Wie in der Schaltung
zu sehen, habe ich einige Kondensatoren vorgesehen, die auch noch in der
Größe variabel sind. Die Stromversorgung des MC ist durch eine Diode und
einem eigenen Spannungsregler abgetrennt. Die eigentliche
Stromversorgung ist eine kleine Platine, welche 5 V liefert, auch hier
sind einige Kondensatoren vorgesehen, die auch noch an Kapazität
vergrößert werden können.
Letztlich wird es der wirklich Einsatz am Modul zeigen, ob es was
bringt.
Sobald ich den Code angepasst habe, werde ich berichten.
Liebe grüße,
Sarah
Sarah S. schrieb:> Sobald ich den Code angepasst habe, werde ich berichten.
Dein Code ist wirklich viel, viel, viel zu kompliziert, wenn Du nur 3
Tasterstellungen auswerten und danach drei Servos ansteuern möchtest.
Im Endeffekt brauchen die Servos nur alle 20 Millisekunden (=20000µs) je
einen Refresh-Impuls mit einer Dauer zwischen ca. 1500 und 2500 µs.
Solange nichts anderes zu steuern ist, brauchst Du kaum mehr als das von
Karl Heinz verlinkte Beispiel "Signalerzeugung für 1 Servo" auf drei
Servos aufzubohren.
Pseudocode für die Hauptschleife:
- int servotime[3];
- Wenn Taster1 unten servotime[0]=1500; else servotime[0]=2500;
- Wenn Taster2 unten servotime[1]=1500; else servotime[1]=2500;
- Wenn Taster3 unten servotime[2]=1500; else servotime[2]=2500;
- Ersten Servo HIGH
- _delay_us(servotime[0]);
- Ersten Servo LOW
- Zweiten Servo HIGH
- _delay_us(servotime[1]);
- Zweiten Servo LOW
- Dritten Servo HIGH
- _delay_us(servotime[2]);
- Dritten Servo LOW
- _delay_us(20000-servotime[0]-servotime[1]-servotime[2]);
Und damit ist ein 20ms Intervall voll und Du fängst von vorne an.
Jürgen S. schrieb:
Servus Jürgen,
> Dein Code ist wirklich viel, viel, viel zu kompliziert, wenn Du nur 3> Tasterstellungen auswerten und danach drei Servos ansteuern möchtest.
Letztlich sind es nur zwei Taster und auch nur 2 Servos. Also noch
weniger Code, als du unten vorgeschlagen hast. Eventuell kommt noch eine
zweite Option dazu, wo jeder Servo mit je einem Tasten gestellt wird.
>> Im Endeffekt brauchen die Servos nur alle 20 Millisekunden (=20000µs) je> einen Refresh-Impuls mit einer Dauer zwischen ca. 1500 und 2500 µs.>> Solange nichts anderes zu steuern ist, brauchst Du kaum mehr als das von> Karl Heinz verlinkte Beispiel "Signalerzeugung für 1 Servo" auf drei> Servos aufzubohren.
Nein, bis auf die beiden Servos und je eine LED pro Seite gibt es nicht
viel, was zu machen ist im Regelbetrieb. Der geplante Programmiermodus
ist davon unabhängig.
Eventuell kommt noch die dritte Tase ins Spiel, welche als "Not-Aus"
angedacht war. Da müsste ich aber noch wissen, wie ich einen Servo in
einem Umlauf, der doch recht kurz ist, sofort anhalten kann. Aber das
ist jetzt nicht wichtig, sondern lediglich eine Option.
>> Pseudocode für die Hauptschleife:> - int servotime[3];> - Wenn Taster1 unten servotime[0]=1500; else servotime[0]=2500;> - Wenn Taster2 unten servotime[1]=1500; else servotime[1]=2500;> - Wenn Taster3 unten servotime[2]=1500; else servotime[2]=2500;> - Ersten Servo HIGH> - _delay_us(servotime[0]);> - Ersten Servo LOW> - Zweiten Servo HIGH> - _delay_us(servotime[1]);> - Zweiten Servo LOW> - Dritten Servo HIGH> - _delay_us(servotime[2]);> - Dritten Servo LOW> - _delay_us(20000-servotime[0]-servotime[1]-servotime[2]);>> Und damit ist ein 20ms Intervall voll und Du fängst von vorne an.
Einfaches Prinzip, was mir sogar gut gefällt. Karl Heinz hatte ja den
Code von den 8 Servos ins Spiel gebracht, was natürlich umfangreicher
wäre, als dein Vorschlag. Wenn man mal von dem Beiwerk wie LED's blinken
lassen, verzögerungen einbauen, gegenläufige Umstellung verhindern, etc.
absieht.
Liebe grüße,
Sarah
Sarah S. schrieb:> Wenn man mal von dem Beiwerk wie LED's blinken> lassen, verzögerungen einbauen, gegenläufige Umstellung verhindern, etc.> absieht.
Der Code dauert pro Umlauf ziemlich genau 20 Millisekunden. Durch
einfaches Aufaddieren von 20 pro Durchlauf auf einen beispielsweise
unsigned long, erhältst Du einen Millisekundenzähler mit einer Auflösung
von 20 Millisekunden.
Solange die Aktionen, die Du zwischendurch noch durchführen möchtest,
z.B. Blinken an LEDs, recht schnell angesteuert werden im Vergleich zu
einem 20ms Intervall, und solange keine höhere zeitliche Auflösung als
diese ca. 20ms verlangt wird, läßt sich das dann wunderbar über den
Stand des Millisekunden-Zählers abstoppen, z.B. eine LED nach Bedarf
jeweils 0,58s ein zu schalten dann wieder für 0,42s aus zu schalten.
Oder wie auch immer.
Da in meinem Pseudocode Eingabe- und Ausgabe bereits getrennt sind,
kannst Du durch Logikbedingungen, die Du nach dem Abfragen der Taster
aber vor dem Ansteuern der Servos einfügst, auch ganz leicht unsinnige
Tasterstellungen ausfiltern.
Mit dem oben skizzierten Pseudocode bekommst Du sowohl die
Servorefresh-Impulse alle (circa) 20 Millisekunden sehr exakt zeitlich
gesteuert, UND darüber bekommst Du einen 20-Millisekundentakt, den Du
einfach nur aufaddieren brauchst, um weitere, langsame Aktionen zu
steuern.