Forum: Mikrocontroller und Digitale Elektronik 3 Servos mit PIC 16F874 ansteuern


von Michael B. (dumbledore)


Lesenswert?

Hallo Zusammen,

Bin ganz frisch hier im Forum, also zuerst mal Grüße an alle!

Ich habe eine kleine Herausforderung zu bewältigen.
Vorweg: Ich habe schon mehrere Tage das Net und das Forum hier 
durchsucht bevor ich den Beitrag aufgemacht habe.

Kurze Beschreibung:

Aufgabe:
Mir steht ein PIC16F874 zur Verfügung, mit dem ich 3 Servos individuell 
ansteuern möchte.

Meine Lösungsvorschlag:
Die PWM - Steuerung auf einen externen PWM - Baustein auslagern.
Mit dem PIC über I2C den Baustein ansprechen und den gewünschten 
Duty-Cycle in dessen entsprechende Register schreiben . Dann den 
gewünschten Servo über den PWM - Baustein ansteuern.
(Rein prinzipiell, kein Anspruch auf Details).

Mein grundsätzliches Problem:

Passenden PWM - Baustein finden.
Bin hier im Forum auf massenhaft Artikel über PWM - Bausteine gestoßen,
sehr oft wurden PCA9634 und PCA9685 genannt.
Dies sind allerdings LED - Treiber. Was meiner Meinung rel. egal ist, da 
ich die benötigte Leistung dann über den Treiber zur Verfügung stelle. 
Beim PWM - Baustein ist mir rein die Pulsung wichtig.

Meine Frage:

Weiß jemand hier im Forum noch weitere, geeignete Bausteine mit denen 
sich über I2C Pulsweiten vorgeben lassen? 3 Ausgangskanäle (eben für die 
3 Servos) würden reichen.

Vielen Dank für Antworten,
Dumbledore

von U.R. Schmitt (Gast)


Lesenswert?

Wo ist das Problem einfach 3 Ausgänge als SoftPWM anzusteuern? Ist 
wahrscheinlich weniger Aufwand als I2C zu programmieren.

von Michael B. (dumbledore)


Lesenswert?

Hallo,

zuerst mal Danke für ihre Antwort.
Ich möchte die Realisierung aber gerne über Hardware - PWM machen, 
zusätzlich bin ich in Sachen Soft - PWM ein Greenhorn :-/

Mfg,
Dumbledore

von Karl H. (kbuchegg)


Lesenswert?

Michael B. schrieb:
> Hallo,
>
> zuerst mal Danke für ihre Antwort.
> Ich möchte die Realisierung aber gerne über Hardware - PWM machen,

Ist doch viel komplizierter als mit Software

> zusätzlich bin ich in Sachen Soft - PWM ein Greenhorn :-/

Dann musst du dich weiterbilden.
Alles was du brauchst ist ein Timer, der über einen Compare-Match 
Interrupt verfügt. Dann kannst du ohne Probleme bis zu 10 bis 12, 
wahrscheinlich auch noch ein paar mehr (Servos sind nicht sehr heikel), 
Servos ansteuern.
Belastung für den µC: irgendwo im Promillebereich.

Wenn du Timergesteuert eine LED blinken lassen kannst und die Blinkzeit 
über den Compare-Match Interrupt durch Verändern des Compare Wertes 
einstellen kannst, bist du schon fast dort. Die 'Blinkzeiten' sind bei 
Servos einfach nur ein wenig schneller als das was bei einer LED 
sinnvoll ist.
Mehr musst du im Grunde nicht können, der Rest ist einfach nur ein wenig 
Organisation im Programm.

von Eddy C. (chrisi)


Lesenswert?

...im übrigen macht es für diese Anwendung keinen Sinn, drei 
Hardware-PWMs aufzubauen: Es reicht ja sogar, wenn man reihum den drei 
Servos die jeweils passende Pulsbreite verabreicht. Servos sind ja für 
den Einsatz im Umfeld RC designt und hier kommen die Werte für die 
Pulsbreite gezwungenermassen nacheinander.

Für die Erzeugung der passenden Pulsbreite könnte man folgendes tun:

1) Simple Warteschleife (Jitter durch Interrupts)
2) Warteschleife mit Timer (per Interrupt oder auch ohne)
3) PWM des Prozessors per Singleshot. Zur Weiterleitung des Pulses an 
den passenden Servo könnte man den Puls über jeweils ein Gate 
(UND-Gatter)leiten.

von Dumbledore (Gast)


Lesenswert?

Danke auch für die weiteren Antworten.

Habe mir nochmal Gedanken über die Sache gemacht.

Ihr habt mich umgestimmt, werde mich jetzt wohl mit Soft - PWM 
versuchen.
Vom Prinzip her hatte ich das schon verstanden, wollte aber an meiner 
Hard - PWM festhalten.

Nachdem aber solche IC´s echt schwer zu bekommen sind
(nach LANGER Suche bei Futureelectronic und Digi-Key für PCA 9685, falls 
sie doch jemand kaufen möchte)
denke ich ist der Aufwand für Software - PWM tatsächlich geringer.

Werde mal meine Ergebnisse und Fortschritte posten.

Danke für die "Beratung" :-)

Dumbledore

von Erhard (Gast)


Lesenswert?

@ dumbledore

Wenn du noch Interesse daran hast: Ich habe mal mit einem PIC16F883 10 
Servos angesteuert.

Könnte dir ja das asm-file mal zukommen lassen.

von MaWin (Gast)


Lesenswert?

> mit dem ich 3 Servos individuell ansteuern möchte.

Das ist spotteinfach und macht ein uC quasi im Leerlauf,
an beliebigen 3 Pins Impulse von 1-2 Millisekunden Länge
auszugeben, die er alle 25 Milisekunden wiederholt.

Das kann man sogar in BASIC programmieren und man braucht
auch keine Hardwaretimer oder PWM-Funktionsgruppen im uC
dafür.

Falls der uC nach was anderes machen soll (z.B.
Lagestabilisierung eines Flugzeugs) kann es sinnvoll sein,
die Zeit mit einem zentralen Hardwaretimer des uC mitzuzählen,
dann muß er nicht bei allen Sachen darauf achten wie lange
sie dauern, sondern kann immer mal nachgucken ob es schon
"Zeit" ist.

von Michael B. (dumbledore)


Lesenswert?

@ Erhard:

Danke für das Angebot, gerne. Schick mir mal ne Nachricht mit deiner 
Mail -Adresse.

@MaWin:

Ja, ich hab mir mal die Sache überlegt, ist wirklich rel. simpel.
Ich kann die meisten Features des PIC im Schlaf, nur über SW - PWM hatte 
ich mir keine Gedanken gemacht. Auf jeden Fall was gelernt.

Schönen Abend,
Dumbledore

von Erhard (Gast)


Lesenswert?

@ dumbledore

Bin leider nicht angemeldet, kann somit deine E-Mail nicht in Erfahrung 
bringen.

von Dumbledore (Gast)


Lesenswert?

Hallo wieder,

muss doch nochmal einhaken:

Wollte zum Test auf 2 Pins 2 verschiedene Pulsweiten - Signale ausgeben:

Der Timer ist so eingestellt, dass er alle 1.02ms überläuft


void main()
{

  init();
  TMR2ON = 1;

  for(;;)
  {
      if (TMR2IF)          // Wenn Timer überläuft, setze beide Kanäle 
auf High
      {
        RA0 = 1;
        RA1 = 1;
        TMR2IF = 0;
      }

      if (TMR2 == Dauer1)      // nach 576us RA0 Low setzen
      {
        TMR2ON = 0;
        RA0 = 0;
        TMR2ON = 1;
      }

      if (TMR2 == Dauer3)      // nach 128us RA1 Low setzen
      {
        TMR2ON = 0;
        RA1 = 0;
        TMR2ON = 1;
      }


  }
}

Hab je einen Pin an einen Oszi - Kanal gehängt, sowohl die Pulsdauern 
als auch die Periodendauer des Timers stimmen optimal (+/- 1us).

Stelle ich jedoch für die Dauern andere Zeiten ein (egal ob kleiner oder 
größer), stimmt gar nichts mehr: Periodendauer falsch, Pulsdauer falsch.

An was kann das liegen? Ist meine ganze Umsetzung falsch?

Das TMR2ON = 0 oder 1 dient zum Starten / Stoppen des Timers, ohne diese 
Befehle funktioniert es nicht.

Meine Vermutungen:
- Code ist in C zu träge, ich muss es in Assembler machen (?)

- Geht nur mit Interrupts (nach Aussage von MaWin oben ja nicht, er sagt 
ja ich bräuchte nicht mal HW - Timer) (?)

- Ich bin zu dumm

Für Hilfe bin ich dankbar,
Dumbledore

von Karl H. (kbuchegg)


Lesenswert?

Dumbledore schrieb:

> An was kann das liegen? Ist meine ganze Umsetzung falsch?

Ich denke schon.
Dein Timer kann doch sicherlich auch Interrupts generieren?

Das Problem ist, dass du hier im Forum nicht sehr viele finden wirst, 
die deinen Prozessor kennen. D.h. deine Zuweisungen an irgendwelche 
Register sind für die meisten hier 'böhmische Dörfer'.

> Das TMR2ON = 0 oder 1 dient zum Starten / Stoppen des Timers,
> ohne diese Befehle funktioniert es nicht.

Das ist nicht logisch. Ob du einen Portpin auf 0 oder 1 setzen kannst, 
kann ja nicht davon abhängen ob ein Timer läuft, solange der Pin nicht 
irgendwie hardwaremässig mit diesem Timer verknüpft wurde (der Timer zb 
eine PWM auf diesem Pin generiert). Selbst wenn ich deinen Prozessor 
nicht kenne, traue ich mich das aus der holen Hand heraus zu behaupten.

> Meine Vermutungen:
> - Code ist in C zu träge, ich muss es in Assembler machen (?)

Quatsch. Wie schnell tickt denn dein Timer? Gibt es da so etwas wie 
einen Prescaler? Wie ist der von dir eingestellte Zusammenhang zwischen 
Taktfrequenz und Frequenz mit der der Timer zählt.

Aber wenn du der Sache nicht traust, kannst du ja mal so ersetzen
1
      if (TMR2 >= Dauer1)
dann bist du nicht darauf angewiesen, dass dein Programm genau dann den 
Timer begutachtet, wenn er exakt den richtigen Wert hat. Ein bischen 
später reicht auch.

> - Geht nur mit Interrupts (nach Aussage von MaWin oben ja nicht, er sagt
> ja ich bräuchte nicht mal HW - Timer) (?)

Interrupts sind nicht unbedingt notwendig. Aber sie vereinfachen die 
Dinge meistens.

von Dumbledore (Gast)


Lesenswert?

Hallo Karl - Heinz,

habe mein Programm nochmal überarbeitet.

Bin auch darauf gestoßen, dass die Abfrage if(TMR2 == Dauer1) der Fehler 
war.
Mit if (TMR2 > Dauer1) funktioniert es.
Damit spare ich mir, wie du schon richtig angemerkt hast, das unnötige 
Starten und stoppen des Timers.

Nun funktioniert es bestens mit Genauigkeit +/- 3us.

Danke,
Dumbledore

von Saimen (Gast)


Lesenswert?

@Erhard

Vielleicht könntest du mir den Code einfach hier rein posten, mir 
reichen zwar keine 10 Servos, aber es wäre cool so was mal Professionell 
gemacht zu sehen, ich hab das schon mal hin bekommen... aber leider nur 
mit zuckeln und ruckeln...

Vielen Dank im Voraus...

@kbuchegg

<quote>
Alles was du brauchst ist ein Timer, der über einen Compare-Match
Interrupt verfügt.

*-was macht ein solches Compare-Match?*

Dann kannst du ohne Probleme bis zu 10 bis 12,
wahrscheinlich auch noch ein paar mehr (Servos sind nicht sehr heikel),
Servos ansteuern.
Belastung für den µC: irgendwo im Promillebereich.

Wenn du Timergesteuert eine LED blinken lassen kannst und die Blinkzeit
über den Compare-Match Interrupt durch Verändern des Compare Wertes
einstellen kannst, bist du schon fast dort. Die 'Blinkzeiten' sind bei
Servos einfach nur ein wenig schneller als das was bei einer LED
sinnvoll ist.
Mehr musst du im Grunde nicht können, der Rest ist einfach nur ein wenig
Organisation im Programm.

*Hat ein PIC von Microchip so ein ding?*

Gruß Saimen

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.