PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : "Vereinfachung" von radbruchs Servoansteuerung mög



RCXv.sASURO
10.11.2008, 19:18
hallo asurogemeinde,
ich habe diesen https://www.roboternetz.de/phpBB2/viewtopic.php?t=29902
tread von radbruch gelesen und finde die idee ziemlich genial O:)

Da ich an meinem asuro gerne einen greifer mit zwei servos (fürs erste langt auch einer 8-[ ) anbauen möchte, ich aber keine Fotodiode daheimhabe, wollte ich einfach mal fragen obs auch so gehen würde (siehe anhang) !??
Wenn das ginge, welchen widerstand bräuchte ich dann, bzw. was müsste ich an der schaltung sonst noch ändern ??
ich hab leider keine wirkliche ahnung von elektronic, deshalb bitte nicht wundern wenn die frage dem einen oder anderen profi ein bisschen anfängermässig erscheint 8-[ !!

Könnte man auf eine ähnliche weise evtl. auch eine ansteuerung für einen zweiten servo bauen (mit den beiden V+ pins von con1 und con2 an der basis des transistors ? ) ??

also schon mal danke für die antworten

mfg Felix


P.S.: Warum kann man Bitmaps eigentlich nicht direkt anhängen ??????

radbruch
10.11.2008, 20:03
Hallo

Das war ja eine meiner Jugendsünden. Die galvanische Trennung entstand weil ich nicht auf dem asuro rumbruzeln wollte.

Selbstverständlich kann man die Servos auch an das asuro-Bordnetz anschliesen und das Signal direkt von einem Pin abgreifen. Obwohl ich dabei gewöhnlich keine Schutzwiderstände in den Signalleitungen verwende können ca. 470-1k nicht schaden.

CON1 ist dabei sehr ungünstig weil es ausgerechnet der Anschluss der IR-LED ist. An PB3 liegt möglicherweise immer der 36kHz-IR-Takt den man erst ausschalten müsste. Damit legt man dann aber auch das IR-Senden lahm. Zudem muss man beim Flashen das Servo trennen sonst zuckt es wild.

Die Pins PC2+3 der Liniensensoren würden sich vielleicht besser eignen. Wenn dein geplanter Greiferasuro keiner Linie folgen soll könntest du die Fototransistoren (T9+10) auslöten und durch Buchsenleisten ersetzen. Dann kannst du wahlweise die Liniensensoren oder die Steuerleitungen der Servos anstöpseln. Mit gesockelter D11 wäre Platz für einen dritten Servo. (Mit WireWrap-Buchsen ist das der Umbau für das US-Modul/Snakevision) Eine weitere Möglichkeit wäre die Servos mit PD2/PB0 anzusteuern, das ist die StatusLED...

Viel Spaß und Erfolg

mic

RCXv.sASURO
11.11.2008, 13:35
hi danke für die schnelle antwort :-D

ich hatte die linienfolgeteile nie eingelötet, sondern gleich buchsenleisten ( (kein wirewrap ) eingebaut (ich hab dann die bauteilebeinchen entsprechend gebogen und habab sie dann auf der oberseite eingesteckt) !
funktioniert wunderbar :-D

wenn ich dich richtig vertehe meinst du also ich bräuchte den transistor gar nicht ?
wo soll dann der widerstand hin ? in die pwm leitung ?

kann ich dann wenn ich die signalleitungen an PC 2 und PC 3 hänge also 2 Servos ansteuern ?

Danke für den tipp mit der IR-Sendediode gottseidank hab ichs nicht so gebaut :-p
Ich möchte nämlich deine IR-Hinderniserkennung (die mit dem spiegel ),
die ebenfalls bestens funktioniert, zusammen mit dem greifer verwenden !

Wie ist das dann eigentlich bei deiner lösung ?
da erzeugst du das pwm signal doch auch über den frontled ausgang oder?
ich dachte eigl mal gelesen zu haben dass man leds mit einer so schnellen frequenz gar nicht schalten kann ??!! *verwirrt bin *

danke für die antworten
mfg Felix

radbruch
11.11.2008, 15:25
Hallo


wenn ich dich richtig vertehe meinst du also ich bräuchte den transistor gar nicht?
wo soll dann der widerstand hin ? in die pwm leitung ?Der Transistor ist nicht nötig. Der Widerstand soll in die Signalleitung und schützt den Kontrollerpin bei einem Kurzschluss in der Signalleitung. Der Wert sollte den Strom bei einem Kurzschluss auf <20mA begrenzen.


kann ich dann wenn ich die signalleitungen an PC 2 und PC 3 hänge also 2 Servos ansteuern?Ja.


da erzeugst du das pwm signal doch auch über den frontled ausgang oder?
ich dachte eigl mal gelesen zu haben dass man leds mit einer so schnellen frequenz gar nicht schalten kann ??!!Die optische Ansteuerung mit Fototransistor funktioniert an allen LEDs. Das ist doch nicht schnell! Die IR-LED wird z.B. mit 36kHz angesteuert.

btw: Du plenkst (http://de.wikipedia.org/wiki/Plenk) und ein Satzzeichen reicht !?!!

Gruß

mic

RCXv.sASURO
11.11.2008, 17:41
hi,
danke für deine hilfreichen antworten!

ich werde mir das jetzt dann gleich mal aufbauen und hoffe dass das dann funktioniert :-)

nur zu meinem verständnis: ein PWM signal besteht doch immer aus 1-2ms strom und anschließend 10-20ms pause sehe ich das richtig?
des wären dann max. ca. 90Hz und da dachte ich halt da es ja heißt, dass ne LED ca. 5 sekunden braucht um richtig an bzw. aus zu sein dass des viel zu schnell wär ?!
vieleicht könntest du mir das mal erklären :-)

Plenke ich etz immer noch ? ;-) (danke für den hinweis)
mfg Felix

radbruch
11.11.2008, 18:34
Hallo


ein PWM signal besteht doch immer aus 1-2ms strom und anschließend 10-20ms pause sehe ich das richtig?Fast richtig. Alle 20ms ein Impuls von 1-2ms Dauer wäre richtiger. Es dürfen auch mehr oder weniger als 20ms sein, grössere Werte machen das Servo meist träger.


des wären dann max. ca. 90Hz und da dachte ich halt da es ja heißt, dass ne LED ca. 5 sekunden braucht um richtig an bzw. aus zu seinBei 1 Sek. (1000ms) durch 20ms pro Impuls komme ich auf genau 50Hz. Diesen Takt schaffen sogar noch Glühlampen -> Stroboskopeffekt (http://de.wikipedia.org/wiki/Stroboskopeffekt) bei Plattenspielern mit Strichmarkierungen zur Geschwindigkeitseinstellung.

Die Zeit die eine LED braucht um volle Helligkeit zu erlangen oder wieder ganz dunkel zu sein kann man möglicherweise im Datenblatt der jeweiligen LED finden. Die maximalen Frequenzen liegen vermutlich im 3-stelligen kHz-Bereich! (z.B. sendet eine normale rote 3mm-LED ohne Probleme Daten mit 36kHz-Trägerfrequenz)

Gruß

mic

RCXv.sASURO
11.11.2008, 20:03
ok vielen dank,
man lernt ja nie aus :-D
eine frage hätte ich aber noch:
wie müsste so ungefähr der code aussehn z.B. um beide servos ganz nach links/rechts zu schwenken?

ich habe etz nämlich mal die schaltung gebaut :-)
so wie du sagtest mit je signalleitung einem 470 Ohm wiederstand + und - parallel an OUT + und OUT - und die signalleitungen an PC 2 und PC3.

da ich bisher nur mit den funktionen aus der lib gearbeitet hab es dafür etz aber keine gibt fäll mir des a bissl schwer ;-)

wär also nett wenn mir da jemand auf die sprünge helfen könnte !

ABER: schreibt mir bitte keine fertigen programme ich will ja schliesslich was dabei lernen :-P

vielen dank nochmal an alle die sich so mit mir abmühen, vor allem dir natürlich radbruch !

mfg Felix

radbruch
11.11.2008, 21:46
wie müsste so ungefähr der code aussehn z.B. um beide servos ganz nach links/rechts zu schwenken?
Es gibt einige unterschiedliche Konzepte zur Servoansteuerung. Ich versuche mal die gebräuchlichsten zu erklären (soweit ich es selbst kapiert habe) Grundsätzlich muss man zwischen blockierend und nicht blockierend unterscheiden.

Bei blockierender Ansteuerung kann der Kontroller während der Impulserzeugung keine anderen Tätigkeiten ausführen, selbst Interrupts sollten gesperrt sein um ein Zittern der Servos zu unterdrücken. Diese Art der Ansteuerung ist einfach umzusetzen und hat wenig Seiteneffekte. Die Impuls-und Pausenlängen erzeugt man dabei z.B. mit Zählschleifen. Oder wie im folgenden Beispiel für den asuro (für die BackLEDs!) mit Sleep():

#include "asuro.h"

unsigned char i, servo_stellzeit;

void servo(unsigned char winkel){
unsigned int count=0;
do{
count++;
BackLED(ON,OFF);
FrontLED(ON);
Sleep(winkel);
BackLED(OFF,ON);
FrontLED(OFF);
Sleep(255); Sleep(255); Sleep(255);
}while (count<servo_stellzeit);
}

int main(void) {

Init();
StatusLED(OFF);
do{
servo_stellzeit=35;
servo(51);
servo(90);
servo(51);
servo(15);
servo_stellzeit=2;
for (i=15; i<88; i+=2) servo(i);
for (i=90; i>17; i-=2) servo(i);
}while (1);
return 0;
}
(Ich weiß, oben schreibe ich möglichst ohne Interrupts, aber weil's mit Sleep() so einfach und überschaubar funktioniert wollte ich das auch mal zeigen.)

Anstelle der BackLED()-Funktionen muss man die Ausgänge der gewählten Pins selbst ansteuern. Es müssen zuerst die Pins auf Ausgang geschaltet werden. Dazu schreibt man in das entsprechende Datenrichtungsregister eine 1, am Beispiel von PC2/3 könnte das so aussehen:

DDRC |= 0b00001100; // Die Zählung beginnt mit PC0 ganz rechts!

Das |= ändert nur die beiden in der Maske gesetzten Bits. Bei = würden zusätzlich alle anderen Pins auf Eingang geschaltet werden. Nun kann man mit

PORTC |= 0b00000100; // den Pin PC2 high (5V, 1, ein, aktiv...) schalten

oder mit

PORTC &= ~0b00000100; // wieder ausschalten.

Die Tilde ~ negiert die angegebene Maske (Einerkomplement (http://de.wikipedia.org/wiki/Einerkomplement)), das &= löscht nur die Bits die in der negierten Maske den Wert 0 haben.

/*Nur für Interessierte:

Alternative mit shift-Operatoren

Wie man den Wert der Maske angibt ist übrigens völlig egal und jeder kann die Form wählen die ihm am besten zusagt. Bei den Portzugriffen sorgen shift-Operatoren für besseren Überblick:

PORTC |= (1<<PC3); // setzt z.B. den Ausgang PC3.

Dazu muss man wissen, dass PC3 ein #define ist und vom Präprozessor (vor dem Kopilieren) durch 3 ersetzt wird(->io.h). Und so funktionert es: Die 1 wird in der Maske ganz rechts eingetragen (0b00000001) und anschliessend um die Anzahl der Stellen in <<PC3 nach links verschoben. Das Ergebniss ist also 0b00001000. Wenn man nun noch mehrere Pins zusammenfasst wird es richtig übersichtlich:

PORTC &= ~((1<<PC3) | (1<<PC2));
*/

Bei der Ansteuerung der Servos muss man noch beachten, dass ein Servo nicht schlagartig auf einer neuen Postion steht wenn man die Impulslänge ändert. Vielmehr braucht es abhängig von der Größe der Positionsänderung eine gewisse Zeit während der man die Impulse wiederholen muss. Das erledigt im meinem Beispielprogramm oben die Variable stellzeit.

Noch kurz ein Ausblick auf nichtblockierende Ansteuerungen:

- Eine Interruptserviceroutine wird regelmässig über einen Timerinterrupt aufgerufen. Schöne Werte ergeben sich dabei wenn man die ISR alle 100µs aufrufen läßt, denn wenn man in dieser ISR eine Variable hochzählt vergeht dann genau 1 ms bis der Zähler 100 erreicht, 20ms erreicht man dann nach 2000 Aufrufen. Damit wäre dann auch eine Impuls/Pause-Periode beendet und man kann von vorne beginnen indem man den Zähler wieder mit 0 läd. Diese Methode ist auch im RN-Wiki (Servos) beschrieben. Mit der Anzahl der Servos steigt allerdings der Rechenaufwand beim Vergleich der einzelnen Servopositionen mit dem aktuellen Stand der Zählvariablen. Außerdem werden durch den gleichzeitigen Start der Impulse alle Servos gleichzeitig gestartet was hohe Einschaltsummenströme erzeugt. Dies kann man zwar durch eine pfiffige Programmierung der ISR umgehen, aber dadurch steigt der Rechenaufwand noch mehr.

- Eine weitere, deutlich resourcenschonendere Methode (die ich selbst noch nicht versucht habe): Die Signalleitung des Servos wird eingeschaltet und ein Timer wird so konfiguriert und gestartet dass er (einmalig!) nach der gewünschten Impulslänge eine ISR aufruft. In der ISR wird der Ausgang wieder ausgeschaltet und der Timer dann so umkonfiguriert dass nach ca. 20ms ein zweiter Interrupt ausgelöst wird. Dann wieder Ausgang setzen und Timer auf Impulslänge konfigurieren uswusw. Das funktioniert natürlich auch mit mehreren Servos die dann auch schön nacheinander starten weil die Impulse nacheinander erzeugt werden. Wenn bei mehreren Servos alle Impulse an die Servos gesendet wurden wird die zu 20ms fehlende Zeit mit einem Dummyservo vertrödelt. Ächz.

Gruß

mic

RCXv.sASURO
12.11.2008, 18:46
hallo,
das war jetzt mal sehr viel neues auf einmal!
ich versuche das, was ich glaube zu verstehen, mal zusammenzufassen :-)
wenn ich also einen der beiden servos benutzten möchte schalte ich mit

DDRC |= 0b00001100; pinns pc2 und pc3 an

dann setze ich ihn auf high und schalte den servo ein mit

PORTC |= 0b00000100; bei PC 2 und PORTC |= 0b00001000; bei PC 3

oder ich mach beides in einem schritt : PORTC |=((1<<PC3)|(1<<PC2));
// beide servos an und auf high - richtig ?

wenn ichs wieder ausschalten will ersetze ich einfach das |= durch &=~
oder?

Das war allerdings erstmal alles, was ich glaube wirklich verstanden zu habe :-P

wie mach ich das jetzt genau mit der for-schleife und dem COUNTER??
und was ist eine "Interruptserviceroutine" und ein "Timerinterrupt"?

mit solchen sachen hab ich bisher noch nicht gearbeitet, kenn mich da also kein bisschen aus!

mfg Felix

pinsel120866
14.11.2008, 08:19
Hallo,

es müsste doch auch möglich sein beide Servos auf einen PIN zu nehmen nachdem beide Servos gleichzeitig Synchrone Bewegungen ausführen, oder liege ich da falsch?

radbruch
14.11.2008, 10:31
Ja, zwei Servos (oder mehr) am selben Kontrollerpin funktioniert natürlich auch.

pinsel120866
14.11.2008, 10:42
OK, Hmmm... da fällt mir spontan ein das Port für das SFH5110 zu nehmen, weil die "Buchse" bei mir schon da ist.

Ist diese Doppelbelegung möglich oder muss ich den Teil für die IR-Kommunikation aus der Lib heraustrennen? IR-Komm. brauche ich nicht, weil ich via ISP flashe.

RCXv.sASURO
14.11.2008, 14:19
hallo,
@pinsel120866:
leider geht das in meinem fall mit der synchronen ansteuerung nicht, weil der eine servo einen greifer schließen bzw. öffnen soll und der andere servo den greifer nach oben oder unten bewegen soll!

kann mir keiner mit meinem programm helfen ?

danke für alle antworten
mfg Felix

Slimpson
14.11.2008, 15:43
Aber da sich bei Radbruchs "Walker" die diagonal zueinander stehenden Beine immer gleich bewegen braucht man anstatt 8 Pins nur 4^^

wobei ich mir jetz nicht mehr sicher sind ob sich auch die Servos gleich bewegen... :-k
kommt ja drauf an wie rum man die drehr entweder sie bewegen sich gleich oder genau umgekehrt... ](*,)

Ach vom Prinziep her kann man se mit 4 pins ansteuern^^

€: Wenn er um die Kurve will muss er sich was ausdenken^^

radbruch
14.11.2008, 15:43
Hallo


kann mir keiner mit meinem programm helfen?Ähm, ich wußte nicht, das du weiterhin auf Hilfe wartest. Mit den Erläuterungen von oben solltest du nun deine Servos immerhin blockierend ansteuern können.


leider geht das in meinem fall mit der synchronen ansteuerung nichtDas "Blockieren" bezieht sich auf den Rest des Programms, mehrere Servos funktionieren einfach so:


do
Ausgang Impuls für 1. Servo auf high
Impulslänge für 1.Servo abwarten
Ausgang Impuls für 1. Servo auf low

Ausgang Impuls für 2. Servo auf high
Impulslänge für 2.Servo abwarten
Ausgang Impuls für 2. Servo auf low

Impulspause (20ms - (Impulslänge1+Impulslänge2)) abwarten
while(Servos haben Ziel noch nicht erreicht)

Jetzt alles klar?

Gruß

mic




...vom Prinzip her kann man se mit 4 pins ansteuernJa, im Prinzip schon, aber echtes "Gehen" wird so nicht funktionieren. Das liegt in erster Linie daran das man nur bei einzelner Ansteuerung der Servos die mechanischen und elektrischen Toleranzen korrigieren kann.

wobei ich mir jetz nicht mehr sicher sind ob sich auch die Servos gleich bewegenDoch, genau das machen sie und genau das ist ein großer Sch.... Je nach Einbauort und -lage bewegen sich die Servos bei längeren Impulsen auf/ab bzw. vor/zurück oder bei kürzeren Impulsen ab/auf bzw. zurück/vor. In der nächsten (sechsbeinigen!) Version meines "Walkers" werde ich deshalb die Drehrichtungen der Servos anpassen (Motor und Poti umpolen) um eine Symetrie der Werte entlang der Längs- bzw. der Hochachse zu erhalten. D.h.: kleine Werte=alle Beine vor/hoch, große Werte=alle Beine hinten/unten. Das ist vielleicht kein wissenschaftlicher Ansatz und macht möglicherweise die Ansteuerung mit inverser Kinedingsda schwierig bis unmöglich. Aber weil ich ja noch Beineanfänger bin scheint mir das für den Einstieg leicher umsetzbar.

RCXv.sASURO
14.11.2008, 16:34
hi ,
oh ja vielen dank ;-)
ich habs kapiert jedenfalls fast!
woher weiss ich wie lange ich den vorgang wiederholen muss bis der servo in der stellung ist die ich haben wollte, weil der servo gibt doch keine werte zurück oder doch?
muss ich mich da durch ausprobieren herantasten oder kann ich pauschal einfach mal die stellzeiten die draufstehen hernehmen ?

danke für deine hifreiche antwort
mfg Felix

radbruch
14.11.2008, 16:50
Das Umrechnen der Stellzeiten aus dem Datenblatt ist vermutlich nicht ganz einfach. Ich würde das Senden der Impulse einfach solange wiederholen bis die Servos nicht mehr zucken. Etwas besser wäre vielleicht so:


stellzeit = (pos_neu - pos_old) * WiederholungenproStep
do
Impulse senden
while(stellzeit--)
Nur zur Info und nochmals etwas offtopic:

weil der servo gibt doch keine werte zurück oder doch?
https://www.roboternetz.de/phpBB2/viewtopic.php?t=37927

Dazu benötigt man allerdings einen analogen Eingang zur Ansteuerung des Servos ;) Das ist aber schon sehr fortgeschritten, Anfänger/Einsteiger sollten sich erstmal mit den Grund- und Standartfunktionen der Servos vertraut machen.

RCXv.sASURO
14.11.2008, 17:28
hallo,
ok danke für den hinweis !
des mit dem analogen zeugs lass ich dann erstmal und werde etz malanfangen;-)
wenn ich nimmer weiterkomm bzw. der greifer fertig ist und funktioniert melde ich mich wieder!
bis dann
mfg Felix

pinsel120866
20.11.2008, 10:10
Sag mal RCX,

laufen deine Servos mit den ASURO-Akkus? Ich habe die Erfahrung gemacht dass 5V zum Betreiben der Servos nicht ausreicht. Ausserdem ziehen die Servos so viel Strom dass man einen Transistor braucht.

pinsel120866
22.11.2008, 13:14
Schönen Nachmittag,

Ich habe mir eine Erweiterungsplatine für 3 Servos gebastelt und diese auf meine ATMEGA32 Erweiterungsplatine gesteckt. Für die Servos belegt habe ich die PINS PB5(MOSI), PB6(MISO) und PB7(SCK) der ISP-Schnittstelle.

In der Asuro.c habe ich folgendes eingetragen:


/* function for Servos */
/* example code right Servo On, middle Servo On, left LED Off */
/* Servo(OFF,ON,ON); */
void Servo(unsigned char left, unsigned char middle, unsigned char right)
{
if (left || middle || right) {
DDRB |= (1 << PB5) | (1 << PB6) | (1 << PB7);
PORTB |= (1 << PB5) | (1 << PB6) | (1 << PB7);
}
if (!left) PORTB &= ~(1 << PB7);
if (!middle) PORTB &= ~(1 << PB6);
if (!right) PORTB &= ~(1 << PA5);
}


In die Asuro.h


/* function for Servos */
/* example code right Servo On, middle Servo ON, left LED Off */
/* BackLED(OFF,ON,ON); */
void Servo(unsigned char left, unsigned char middle, unsigned char right);

Und das Programm ist so


#include "asuro.h"

unsigned char i, servo_stellzeit;

void servo(unsigned char winkel){
unsigned int count=0;
do{
count++;
Servo(ON,ON,ON);
FrontLED(ON);
Sleep(winkel);
Servo (OFF,OFF,OFF);
FrontLED(OFF);
Sleep(255); Sleep(255); Sleep(255);
}while (count<servo_stellzeit);
}

int main(void) {

Init();
StatusLED(OFF);
do{
servo_stellzeit=35;
servo(51);
servo(90);
servo(51);
servo(15);
servo_stellzeit=2;
for (i=15; i<88; i+=2) servo(i);
for (i=90; i>17; i-=2) servo(i);
}while (1);
return 0;
}


Aber es bewegt sich nichts...
Könntet ihr mir bitte etwas unter die Arme greifen?

radbruch
22.11.2008, 15:39
Hallo

Blinkt die FrontLED? Wird dein Mega32 auch mit 8MHz getacktet? Funktioniert Sleep()? Funktioniert das:

void servo(unsigned char winkel){
unsigned int count=0;
do{
count++;
//Servo(ON,ON,ON);
if (ON || ON || ON) {
DDRB |= (1 << PB5) | (1 << PB6) | (1 << PB7);
PORTB |= (1 << PB5) | (1 << PB6) | (1 << PB7);
}
FrontLED(ON);
Sleep(winkel);
//Servo (OFF,OFF,OFF);
if (!OFF) PORTB &= ~(1 << PB7);
if (!OFF) PORTB &= ~(1 << PB6);
if (!OFF) PORTB &= ~(1 << PA5);
FrontLED(OFF);
Sleep(255); Sleep(255); Sleep(255);
}while (count<servo_stellzeit);
}
Wenn nicht, funktioniert es ohne ifs?

Ähm, ich seh grad, du hast auch LEDs eingebaut. Liegen die Signalleitungen der Servos direkt am Pin des Kontrollers oder nach dem Vorwiderstand parallel zur LED ?

pinsel120866
22.11.2008, 16:18
Hi radbruch,

die FrontLED blinkt, ich vermute dass der Atmega mit 16MHz getaktet wird, wo kann ich das nachsehen? Dein Code läuft auch nicht, die LEDs habe ich in Serie zwischen Widerstand und Servostecker.

Sieht aus, als ob ich hier einiges verbockt habe,oder?

radbruch
22.11.2008, 16:54
Die LEDs sollten vom Kontrollerpin über den Vorwiderstand gegen GND geschaltet sein (so wie z.b. die FrontLED). Ebenfalls direkt vom Kontrollerpin (sicherheitshalber auch mit Vorwiderstand) sollten die Servosteuerleitungen abgehen.

Wenn deine aktuelle Schaltung vom Kontrollerpin über den Vorwiderstand und über die LED zum Servosteueranschluß geht kannst du für einen schnellen Test die LED überbrücken.

Da die Sleep()-Befehle für den 8MHz-asuro gedacht sind musst du für 16MHz die Werte verdoppeln bzw. die Befehle zweimal ausführen.

pinsel120866
22.11.2008, 18:17
Ich habe nun die LEDs nach deinen Tipps parallel geschalten und den Kontrollerpin via Vorwiderstand an den Servoesteueranschluß angeschlossen. Danach folgendes Programm geflasht:



#include "asuro.h"

unsigned char i, servo_stellzeit;

void servo(unsigned char winkel){
unsigned int count=0;
do{
count++;
//Servo(ON,ON,ON);
if (ON || ON || ON) {
DDRB |= (1 << PB5) | (1 << PB6) | (1 << PB7);
PORTB |= (1 << PB5) | (1 << PB6) | (1 << PB7);
}
FrontLED(ON);
Sleep(winkel);
Sleep(winkel);
//Servo (OFF,OFF,OFF);
if (!OFF) PORTB &= ~(1 << PB7);
if (!OFF) PORTB &= ~(1 << PB6);
if (!OFF) PORTB &= ~(1 << PA5);
FrontLED(OFF);
Sleep(510); Sleep(510); Sleep(510);
}while (count<servo_stellzeit);
}


int main(void) {

Init();
BackLED(OFF,OFF);
do{
servo_stellzeit=35;
servo(51);
servo(90);
servo(51);
servo(15);
servo_stellzeit=2;
for (i=15; i<88; i+=2) servo(i);
for (i=90; i>17; i-=2) servo(i);
}while (1);
return 0;
}

Wenn ich nun einschalte, machen die Servos eine kleine Dehung (ca. 2Grad) gegen den Uhrzeigersinn, das war's. Beim nächsten Einschalten wieder, und das so lange bis die Servos auf Anschlag sind.

Aber das Programm macht doch etwas anderes, FrontLED leuchtet auch nicht...

An den Servosteuerpins messe ich 1,6 Volt. Batteriespannung: 5 Volt

radbruch
22.11.2008, 18:33
Sleep(510); Sleep(510); Sleep(510);

Das müßte massige Fehler ergeben, Sleep() erwartet doch ein Byte als Parameter.

Halte doch mal fliegend eine Steuerleitung an die FrontLED. Und entferne testweise die IFs bzw. ändere nach if(1).... Wo und wie sind denn ON und OFF definiert?

Nanu: if (!OFF) PORTB &= ~(1 << PA5);

pinsel120866
22.11.2008, 18:53
Ich habe nun die Servosteuerleitung nur des Mittleren Servos direkt an den PIN gehängt, also LED und Widerstand überbrückt.

Program:


#include "asuro.h"

unsigned char i, servo_stellzeit;

void servo(unsigned char winkel){
unsigned int count=0;
do{
count++;
if (ON || ON || ON) {
DDRB |= (1 << PB6);
PORTB |= (1 << PB6);
}
FrontLED(ON);
Sleep(winkel);
Sleep(winkel);

if (!OFF) PORTB &= ~(1 << PB6);

FrontLED(OFF);
Sleep(255); Sleep(255); Sleep(255);
Sleep(255); Sleep(255); Sleep(255);
}while (count<servo_stellzeit);
}


int main(void) {

Init();
BackLED(OFF,OFF);
do{
servo_stellzeit=35;
servo(51);
servo(90);
servo(51);
servo(15);
servo_stellzeit=2;
for (i=15; i<88; i+=2) servo(i);
for (i=90; i>17; i-=2) servo(i);
}while (1);
return 0;
}

Servo macht das gleiche, FrontLED blinkt schnell, flackert also. An der Steuerleitung messe ich nun 5V.

radbruch
22.11.2008, 19:26
Vorsichtshalber noch so versuchen:

void servo(unsigned char winkel){
unsigned int count=0;
do{
count++;

DDRB |= (1 << PB6);
PORTB |= (1 << PB6);

FrontLED(ON);
Sleep(winkel);
Sleep(winkel);

PORTB &= ~(1 << PB6);

FrontLED(OFF);
Sleep(255); Sleep(255); Sleep(255);
Sleep(255); Sleep(255); Sleep(255);
}while (count<servo_stellzeit);
}
Hast du eigentlich die Timerprescaler in asuro.c für deine 16MHz angepasst? Die orginalen Sleepwerte passen zu einer 36kHz-ISR wie sie der asuro in den erweiterten Libs verwendet.

pinsel120866
22.11.2008, 19:39
Ich habe es jetzt probiert, keine Änderung. Was die Lib angeht, habe ich meine IR-Entfernungsmessungs-Lib für den Atmega32 verwendet. Wo muss ich ggf. in der asuro.c anpassen?

Ich möchte mich an dieser Stelle für deine Geduld bedanken. Dieser Aufbau soll die Grundlagen für meinen Mehrbeiner sein, wenn er hoffentlich irgenwann einmal läuft.

radbruch
22.11.2008, 20:08
Hallo

Also irgendwie stecken wir fest.

Wichtig ist zuerst mal er richtige Anschluß: Servo auf Vcc und GND, Servo-GND muss mit Kontroller-GND verbunden sein. Servo-Signal geht direkt auf den Kontrollerpin.

Die Sleep-Zeiten müssen halbwegs stimmen. Mit der orginalen asuro-Lib erzeugt man so einen 1Hz-Blinktakt (pro Sekunde muss die FrontLED einmal blinken, also ca. 60 mal pro Minute):


int i;
while(1)
{
FrontLED(ON);
for(i=0; i<500; i++) Sleep(36);
FrontLED(OFF);
for(i=0; i<500; i++) Sleep(36);
}
Wenn das passt ist 36 der Wert für 1ms, 18 wäre dann für 0,5ms und 72 für 2ms usw... wenn es nicht passt musst du die 36 solange ändern bis halbwegs der Sekundentakt hinkommt.


Ich möchte mich an dieser Stelle für deine Geduld bedanken.Kein Problem, allerdings gehen mir so langsam die Ideen aus. Vermutlich übersehen wir irgendwas elementares, wir sollten das Ganze mal "überschlafen"... Ich versuche es morgen mal mit meinem RP6, der hat ja auch einen Mega32.

Gruß

mic

pinsel120866
22.11.2008, 20:21
Das passt, die FrontLED blinkt genau im Sekundentakt kleiner Teilerfolg, morgen sehen wir weiter - du hast Recht.

radbruch
23.11.2008, 10:54
Hallo

Um nun endlich mal zu einem Erfolg zu kommen:

#include <avr/io.h> // I/O Port definitions
//#include <avr/interrupt.h> // Interrupt macros (für cli)

#define servo1_port PORTC
#define servo1_ddr DDRC
#define servo1_pin PC0
#define servo2_port PORTC
#define servo2_ddr DDRC
#define servo2_pin PC1

#define servo1_on servo1_port |= 1<<servo1_pin
#define servo2_on servo2_port |= 1<<servo2_pin
#define servo1_off servo1_port &= ~(1<<servo1_pin)
#define servo2_off servo2_port &= ~(1<<servo2_pin)

uint16_t i, stellzeit, dummy;

int main(void)
{
servo1_ddr |= 1<<servo1_pin; // Pins als Ausgang definieren
servo2_ddr |= 1<<servo2_pin;
servo1_off; // und auf low schalten
servo2_off;
//cli(); // keine Störungen erlauben
while(1) // solange Saft im Akku...
{
stellzeit=100; // 100 mal den Impuls senden
while(stellzeit--)
{
servo1_on; // Impuls für erstes Servo erzeugen
for(i=0; i<500; i++) dummy^=i;
servo1_off;

servo2_on; // Impuls für zweites Servo erzeugen
for(i=0; i<500; i++) dummy^=i;
servo2_off;

for(i=0; i<20000; i++) dummy^=i; // Impulspause
}

stellzeit=100;
while(stellzeit--)
{
servo1_on;
for(i=0; i<2500; i++) dummy^=i;
servo1_off;

servo2_on;
for(i=0; i<2500; i++) dummy^=i;
servo2_off;

for(i=0; i<20000; i++) dummy^=i;
}
}
return(0);
}
Ohne Interrupts und ohne asuro-Lib nur mit Zählschleifen sind nun viele mögliche Störquellen ausgeschaltet. Das läuft auf meinem 8Mhz-Mega32, für 16Mhz sollte es ausreichen die Zählerwerte zu verdoppeln.

Gruß

mic

pinsel120866
23.11.2008, 11:56
Mahlzeit Radbruch,

Danke für den Code - ich habe dein Programm geflasht, leider ohne Erfolg.

Mein angepasster Code:



#include <avr/io.h> // I/O Port definitions
//#include <avr/interrupt.h> // Interrupt macros (für cli)

#define servo1_port PORTB
#define servo1_ddr DDRB
#define servo1_pin PB5
#define servo2_port PORTB
#define servo2_ddr DDRB
#define servo2_pin PB6
#define servo3_port PORTB
#define servo3_ddr DDRB
#define servo3_pin PB7


#define servo1_on servo1_port |= 1<<servo1_pin
#define servo2_on servo2_port |= 1<<servo2_pin
#define servo3_on servo3_port |= 1<<servo3_pin
#define servo1_off servo1_port &= ~(1<<servo1_pin)
#define servo2_off servo2_port &= ~(1<<servo2_pin)
#define servo3_off servo3_port &= ~(1<<servo3_pin)

uint16_t i, stellzeit, dummy;

int main(void)
{
servo1_ddr |= 1<<servo1_pin; // Pins als Ausgang definieren
servo2_ddr |= 1<<servo2_pin;
servo3_ddr |= 1<<servo3_pin;
servo1_off; // und auf low schalten
servo2_off;
servo3_off;
//cli(); // keine Störungen erlauben
while(1) // solange Saft im Akku...
{
stellzeit=100; // 100 mal den Impuls senden
while(stellzeit--)
{
servo1_on; // Impuls für erstes Servo erzeugen
for(i=0; i<500; i++) dummy^=i;
servo1_off;

servo2_on; // Impuls für zweites Servo erzeugen
for(i=0; i<500; i++) dummy^=i;
servo2_off;

servo3_on; // Impuls für drittes Servo erzeugen
for(i=0; i<500; i++) dummy^=i;
servo3_off;

for(i=0; i<20000; i++) dummy^=i; // Impulspause
}

stellzeit=100;
while(stellzeit--)
{
servo1_on;
for(i=0; i<2500; i++) dummy^=i;
servo1_off;

servo2_on;
for(i=0; i<2500; i++) dummy^=i;
servo2_off;

servo3_on;
for(i=0; i<2500; i++) dummy^=i;
servo2_off;

for(i=0; i<20000; i++) dummy^=i;
}
}
return(0);
}


Das Blinkprogramm von gestern hat ja gepasst - damit müsste eigentlich auch der Prozessortakt passen, oder?

pinsel120866
23.11.2008, 12:30
Kommando zurück, ich habe jetzt 3 neue PINs genommen (PB0,PB1,PB2), damit reagieren die Servos. Nach dem Einschalten des Asuros drehen die Servos bis zum Anschlag im Gegenuhrzeigersinn und Ticken dan weiter.

radbruch
23.11.2008, 12:44
Die Zählwerte in den Schleifen müssen noch verdoppelt werden. Jetzt senden wir nur halbe Impulszeiten deshalb gehen die Servos auf Anschlag. Schön dass sich was dreht:)

pinsel120866
23.11.2008, 14:00
Hilf mir bitte mal, wo ich verdoppeln muss:




#include <avr/io.h> // I/O Port definitions
//#include <avr/interrupt.h> // Interrupt macros (für cli)

#define servo1_port PORTB
#define servo1_ddr DDRB
#define servo1_pin PB0
#define servo2_port PORTB
#define servo2_ddr DDRB
#define servo2_pin PB1
#define servo3_port PORTB
#define servo3_ddr DDRB
#define servo3_pin PB2


#define servo1_on servo1_port |= 1<<servo1_pin
#define servo2_on servo2_port |= 1<<servo2_pin
#define servo3_on servo3_port |= 1<<servo3_pin
#define servo1_off servo1_port &= ~(1<<servo1_pin)
#define servo2_off servo2_port &= ~(1<<servo2_pin)
#define servo3_off servo3_port &= ~(1<<servo3_pin)

uint16_t i, stellzeit, dummy;

int main(void)
{
servo1_ddr |= 1<<servo1_pin; // Pins als Ausgang definieren
servo2_ddr |= 1<<servo2_pin;
servo3_ddr |= 1<<servo3_pin;
servo1_off; // und auf low schalten
servo2_off;
servo3_off;
//cli(); // keine Störungen erlauben
while(1) // solange Saft im Akku...
{
stellzeit=100; // 100 mal den Impuls senden
while(stellzeit--)
{
servo1_on; // Impuls für erstes Servo erzeugen
for(i=0; i<1000; i++) dummy^=i;
servo1_off;

servo2_on; // Impuls für zweites Servo erzeugen
for(i=0; i<1000; i++) dummy^=i;
servo2_off;

servo3_on; // Impuls für drittes Servo erzeugen
for(i=0; i<1000; i++) dummy^=i;
servo3_off;

for(i=0; i<40000; i++) dummy^=i; // Impulspause
}

stellzeit=100;
while(stellzeit--)
{
servo1_on;
for(i=0; i<5000; i++) dummy^=i;
servo1_off;

servo2_on;
for(i=0; i<5000; i++) dummy^=i;
servo2_off;

servo3_on;
for(i=0; i<5000; i++) dummy^=i;
servo2_off;

for(i=0; i<40000; i++) dummy^=i;
}
}
return(0);
}


Damit drehen die Servos

radbruch
23.11.2008, 14:25
Na prima. 1000/5000 sind die Werte für die Impulse min/max, 40000 die 20ms-Wiederholung. So könntest du knapp deine 12 Servos ansteuern wenn du pausenlos Impulse senden würdest. Das ist zwar ein Weg der vermutlich in die Sackgasse führt, aber du brauchst erst etwas Übung mit Servos. Das Ansteuern kannst du auch mit deinen drei Servo lernen.

Zuerst solltest du den Wert für die Pause optimieren. Wenn er zu groß ist zuckeln die Servos. Die Impulspause ist nun auch der Platz im Programm zum Berechen neuer Servopositionen. Dazu verwendest du dann Variablen für die Positionen und nach der Pause, noch vor dem Start der nächsten Impulse, berechnest du die neuen Positionen und änderst die entsprechenden Variablen. Die Zeit die das Programm dafür benötigt gewinnst du durch Verkürzung der Pausenzeiten. (Wenn du dich damit beschäftigst: die Schwankungen der Pausezeiten durch unterschiedliche Impulsdauern sollte man auch berücksichtigen. Impulse+Pause sollte ca. 20ms sein)


#include <avr/io.h> // I/O Port definitions
//#include <avr/interrupt.h> // Interrupt macros (für cli)

#define servo1_port PORTB
#define servo1_ddr DDRB
#define servo1_pin PB0
#define servo2_port PORTB
#define servo2_ddr DDRB
#define servo2_pin PB1
#define servo3_port PORTB
#define servo3_ddr DDRB
#define servo3_pin PB2


#define servo1_on servo1_port |= 1<<servo1_pin
#define servo2_on servo2_port |= 1<<servo2_pin
#define servo3_on servo3_port |= 1<<servo3_pin
#define servo1_off servo1_port &= ~(1<<servo1_pin)
#define servo2_off servo2_port &= ~(1<<servo2_pin)
#define servo3_off servo3_port &= ~(1<<servo3_pin)

uint16_t i, stellzeit, dummy;
uint16_t servo1_position, servo2_position, servo3_position;

int main(void)
{
servo1_ddr |= 1<<servo1_pin; // Pins als Ausgang definieren
servo2_ddr |= 1<<servo2_pin;
servo3_ddr |= 1<<servo3_pin;
servo1_off; // und auf low schalten
servo2_off;
servo3_off;
servo1_position=1000;
//cli(); // keine Störungen erlauben
while(1) // solange Saft im Akku...
{
stellzeit=100; // 100 mal den Impuls senden
while(stellzeit--)
{
servo1_on; // Impuls für erstes Servo erzeugen
for(i=0; i<servo1_position; i++) dummy^=i;
servo1_off;

for(i=0; i<40000; i++) dummy^=i; // Impulspause
}
// neue Position berechen
if(servo1_position == 1000) servo1_position=5000; else servo1_position=1000;
}
return(0);
}

Das wird dann auch zum Ziel führen wenn man schrittweise optimiert. Servoansteuerung als Funktion gestalten und zyklisch aufrufen, dann als ISR im Hintergrund. Aber das ist eher ein Ausblick auf die nächsten Hürden.

Gruß

mic

pinsel120866
23.11.2008, 15:02
Nun habe ich dieses Programm gefasht:




#include "asuro.h"

unsigned char i, servo_stellzeit;

void servo(unsigned char winkel)
{
unsigned int count=0;
do{
count++;
if (ON || ON || ON) {
DDRB |= (1 << PB0) | (1 << PB1) | (1 << PB2);
PORTB |= (1 << PB0) | (1 << PB1) | (1 << PB2);
}
FrontLED(ON);
Sleep(winkel);
if (!OFF) PORTB &= ~(1 << PB2);
if (!OFF) PORTB &= ~(1 << PB1);
if (!OFF) PORTB &= ~(1 << PA0);
FrontLED(OFF);
Sleep(255); Sleep(255); Sleep(255);
}while (count<servo_stellzeit);
}


int main(void) {

Init();
BackLED(OFF,OFF);
do{
servo_stellzeit=35;
servo(51);
servo(90);
servo(51);
servo(15);
servo_stellzeit=2;
for (i=15; i<88; i+=2) servo(i);
for (i=90; i>17; i-=2) servo(i);
}while (1);
return 0;
}


Die FrontLED flackert ganz schnell, der Servo dreht: 90° Rechts - 90° Rechts - 90° Links - 90° Links - 180° Rechts - 180° Links

Wenn ich allerdings einen 2. Servo anhänge stimmt der Ablauf nicht mehr, bei 3 angehängten Servos dreht gar keiner mehr.

Wie muss ich vorgehen um alle 3 Parallel betreiben zu können?

pinsel120866
23.11.2008, 15:55
Ich habe den Code auf 3 Servos erweitert:




#include "asuro.h"

unsigned char i, j, k, servo_stellzeit;

void servo1(unsigned char winkel)
{
unsigned int count=0;
do{
count++;
if (ON) {
DDRB |= (1 << PB0);
PORTB |= (1 << PB0);
}
Sleep(winkel);
if (!OFF) PORTB &= ~(1 << PB0);
Sleep(255); Sleep(255); Sleep(255);
}while (count<servo_stellzeit);
}

void servo2(unsigned char winkel)
{
unsigned int count=0;
do{
count++;
if (ON) {
DDRB |= (1 << PB1);
PORTB |= (1 << PB1);
}
Sleep(winkel);
if (!OFF) PORTB &= ~(1 << PB1);
Sleep(255); Sleep(255); Sleep(255);
}while (count<servo_stellzeit);
}

void servo3(unsigned char winkel)
{
unsigned int count=0;
do{
count++;
if (ON) {
DDRB |= (1 << PB2);
PORTB |= (1 << PB2);
}
Sleep(winkel);
if (!OFF) PORTB &= ~(1 << PB2);
Sleep(255); Sleep(255); Sleep(255);
}while (count<servo_stellzeit);
}

int main(void) {

Init();
do{
servo_stellzeit=35;
servo1(51);
servo2(51);
servo3(51);
servo1(90);
servo2(90);
servo3(90);
servo1(51);
servo2(51);
servo3(51);
servo1(15);
servo2(15);
servo3(15);
servo_stellzeit=2;
for (i=15; i<88; i+=2) servo1(i);
for (j=15; j<88; j+=2) servo2(j);
for (k=15; k<88; k+=2) servo3(k);
for (i=90; i>17; i-=2) servo1(i);
for (j=90; j>17; j-=2) servo2(j);
for (k=90; k>17; k-=2) servo3(k);
}while (1);
return 0;
}

Nun arbeiten alle 3 Servos brav nacheinander ihre Drehpostitionen ab. Einen Weg dass z.B. 2 Servos nahezu gleichzeitig drehen habe ich noch keinen gefunden.

pinsel120866
23.11.2008, 17:06
Hallo Radbruch,

bei meinem "Servomultitasking" komme ich nicht weiter, kannst du mir sagen wie ich den Code anpassen muss damit mehrere Servos gleichzeitig arbeiten?

radbruch
23.11.2008, 17:34
Mit asuro.h läuft es nicht mehr auf meinem RP6, deshalb ungetestet:

#include "asuro.h"

unsigned char i, j, k, servo_stellzeit;

void servo(unsigned char winkel0, unsigned char winkel1, unsigned char winkel2)
{
unsigned int count=0;
do{
count++;

if(winkel0){
PORTB |= (1 << PB0);
Sleep(winkel0);
}
PORTB &= ~(1 << PB0);

if(winkel1){
PORTB |= (1 << PB1);
Sleep(winkel1);
}
PORTB &= ~(1 << PB1);

if(winkel1){
PORTB |= (1 << PB2);
Sleep(winkel2);
}
PORTB &= ~(1 << PB2);

Sleep(255-winkel0); Sleep(255-winkel1); Sleep(255-winkel2);
}while (count<servo_stellzeit);
}

int main(void) {

Init();
DDRB |= (1 << PB2) | (1 << PB1) | (1 << PB0);

do{
servo_stellzeit=35;
servo(51, 51, 51);
servo(90, 90, 90);
servo(51, 51, 51);
servo(15, 15, 15);

servo_stellzeit=2;
for (i=15; i<88; i+=2) servo(i, 0, 0);
for (j=15; j<88; j+=2) servo(0, j, 0);
for (k=15; k<88; k+=2) servo(0, 0, k);
for (i=90; i>17; i-=2) servo(i, 0, 0);
for (j=90; j>17; j-=2) servo(0, j, 0);
for (k=90; k>17; k-=2) servo(0, 0, k);
}while (1);
return 0;
}

pinsel120866
23.11.2008, 18:01
Vielen Dank, ich habs mal geflasht. Wenn ich den Code richtig gelesen habe, sollten alle 3 Servos gleichzeitig das Salbe machen. Hmm, 1 Servo alleine arbeitet die Drehpositionen ab, sobald ich einen 2. dazunehme drehen sie zwar parallel, aber nicht auf die vorgegebenen Positionen. Bei 3 Servos zucken sie nur.

radbruch
23.11.2008, 18:14
Vielleicht bricht bei mehreren Servos die Bordspannung zusammen. Oder eine schlechte Lötstelle in der Spannungsversorgung der Servos.

Möglicherweise ziehen auch die Servoeingänge zuviel Strom aus den Kontrollerpins. Vorsichtshalber solltest du besser Vorwiderstände einbauen. Ich habe nur Erfahrungen mit kleinen leistungsschwachen Servos die man ohne Probleme direkt an die Ports klemmen kann.

pinsel120866
23.11.2008, 18:42
Ich habe 5€ Servos vom C*. Vorwiderstände habe ich zwischen ProzessorPIN und SteuerleitungsPIN der Servos drin.

Ich tippe darauf dass die Bordspannung nur einen rotierenden Servo gleichzeitig erlaubt. Möglicherweise bringt der Einbau von Transistoren etwas.

radbruch
23.11.2008, 18:48
Möglicherweise bringt der Einbau von Transistoren etwas.
Eher nicht. Größere Mignon(AA)-Batterien oder gar Babyzellen (wie bei meinem Walker) könnten da schon mehr bringen.

pinsel120866
23.11.2008, 19:08
OK, ich werde es mit AA - Batterien versuchen, 4 Babyzellen sind dem Asuro wohl etwas zu schwer. Ich bin schon zufrieden wenn ich 2 Servos gleichzeitig betreiben kann. Wenn ich die grösseren Batterien eingebaut habe werde ich meine Erfahrungen hier posten.

Herzlichen Dank für deine Hilfe und Unterstützung und dass du dir an diesem Wochenende Zeit für mich genommen hast, ich weiß das sehr zu schätzen.

pinsel120866
24.11.2008, 12:35
Sag mal Radbruch,

wenn ich nun einen gehackten Servo als Motor verwende, reicht es dann wenn ich die Postion anfahre mit z. B. servo(51) um den "Motor" vorwärts drehen zu lassen und danach mit einem kleineren Wert z. B. servo(40) den Servo zurückdrehen zu lassen. Mit PORTB &= ~(1 << PA5); stoppe ich den Servo.

Ist das richtig?