Archiv verlassen und diese Seite im Standarddesign anzeigen : Servoansteurungsproblem!! Helft mir!!
michaelb
12.12.2005, 14:00
Hi Leutz,
ich da ein Problem mit der Servoansteuerung!
Folgenden Quelltext verwende ich:
#include <avr/io.h>
#include <avr/signal.h>
#include <avr/interrupt.h>
#include <inttypes.h>
#include <math.h>
volatile unsigned char servowert;
volatile unsigned char puls;
volatile unsigned char zaehler;
int set_servo(volatile uint8_t pos)
{
servowert = (pos/180) + 1;
}
int main(void)
{
zaehler = 0;
sei();
DDRB = (1<<PB0);
PORTB = (1<<PB0);
//Timer0 übernimmt die Highphase
//muss 1ms sein also 25 Takte bei einem Prescaler von 64
puls = 25;
TCCR0 = (1<<CS01)|(1<<CS00);
TCNT0 = (255-puls);
for(;;)
{
}
}
SIGNAL(SIG_OVERFLOW0)
{
if(zaehler == 0)
{
PORTB = (0<<PB0);
zaehler++;
}
else if(0 < zaehler < 20)
{
zaehler++;
}
else
{
zaehler = 0;
PORTB = (1<<PB0);
}
}
die Funktion set_servo hat damit noch nichts zu!
Folgendes macht das Programm:
-> Timer0 mit Prescaler64
-> 25 Takte ergeben 1ms
-> zuerst ist PortB0 an
-> nach der 1ms wird PortB0 ausgeschaltet
-> danach wird zaehler immer erhöht bis 20 ist dann wird PortB0 wieder eingeschaltet!
ein Servo braucht doch erst 1ms high und dann 19ms low und dann wieder 1ms high.....usw!
Sobald ich den µC resette fährt der Servo ein Stück weiter bleibt dort dann aber stehen!
Könnt ihr mir da weiterhelfen? Was ist an dem Code falsch?
Gruß Michi
Also, wenn ich das übersetze und mir die .lst anschau, bleibt von dem
if(0 < zaehler < 20) { zaehler++; } else ....
nur der { zaehler++; } übrig
Probier mal statt
if(0 < zaehler < 20)
if ( (zaehler > 0 ) && (zaehler < 20 ) )
Zumindest mir kommt das vertrauter vor, will aber nix heissen.
michaelb
12.12.2005, 14:36
Hi Picnick,
danke für die Antwort!
aber leider ändert sich nichts!!
kann jemand mir weiterhelfen? Oder einen funktionierenden Servocode posten?
Gruß Michi
Source hab ich jetzt auch nicht, aber viel mehr ist da ja eh nicht
das muß auch in die ISR Routine (jedesmal)
TCNT0 = (255-puls);
und das ist wirkungslos
PORTB = (0<<PB0);
bitte:
PORTB = (1<<PB0);
Wenn du sagst, welchen quartz du hast, kann ich auch mitrechnen
michaelb
12.12.2005, 15:03
Hi Picnick,
mein Quartz ist 16Mhz
Es muss doch PORTB = (0<<PB0); heißen da ich ja das Servosignal löschen will!
mein aktueller Code:
#include <avr/io.h>
#include <avr/signal.h>
#include <avr/interrupt.h>
#include <inttypes.h>
#include <math.h>
volatile unsigned char servowert;
volatile unsigned char puls;
volatile unsigned char zaehler;
int set_servo(volatile uint8_t pos)
{
servowert = (pos/180) + 1;
}
int main(void)
{
zaehler = 0;
sei();
DDRB = (1<<PB0);
PORTB = (1<<PB0);
//Timer0 übernimmt die Highphase
//muss 1ms sein also 25 Takte bei einem Prescaler von 64
puls = 25;
TCCR0 = (1<<CS01)|(1<<CS00);
TCNT0 = (255-puls);
for(;;)
{
}
}
SIGNAL(SIG_OVERFLOW0)
{
if(zaehler == 0)
{
PORTB = (0<<PB0);
TCNT0 = (255-puls);
zaehler++;
}
else if ( (zaehler > 0 ) && (zaehler < 20 ) )
{
TCNT0 = (255-puls);
zaehler++;
}
else
{
TCNT0 = (255-puls);
zaehler = 0;
PORTB = (1<<PB0);
}
}
funktioniert immer noch net! :(
Gruß Michi
Löschen is anders:
PORTB &= ~(1<<PB0);
michaelb
12.12.2005, 15:22
hmmmm....bringt auch nichts!!
Gruß Michi
Gut, mit dem Timing dampft es etwas.
Für 1 mS prescale 64 Preload 6
zähler = 0 -->Pulse on
zähler = 20 ----> zähler reset
else ---->pulse off
d.h
1. mS PULSE ON
2. mS ---> 19. mS PULSE ON
20 ms zähler reset
#include <avr/io.h>
#include <avr/signal.h>
#include <avr/interrupt.h>
#include <inttypes.h>
#include <math.h>
volatile unsigned char servowert;
volatile unsigned char puls;
volatile unsigned char zaehler;
int set_servo(volatile uint8_t pos)
{
servowert = (pos/180) + 1;
}
int main(void)
{
zaehler = 0;
sei();
DDRB = (1<<PB0);
PORTB = (1<<PB0);
//Timer0 übernimmt die Highphase
//muss 1ms sein also 25 Takte bei einem Prescaler von 64
puls = 25;
TCCR0 = (1<<CS01)|(1<<CS00);
TCNT0 = 6;
for(;;)
{
}
}
SIGNAL(SIG_OVERFLOW0)
{
switch(zaehler)
{
case 0:
// case 1: das da aktivieren für 2 mS Servo
PORTB |= (1<<PB0);
zaehler++;
break;
case 20:
zähler = 0;
break;
default:
PORTB &= ~(1<<PB0);
zaehler++;
break;
}
TCNT0 = 6;
}
Aber viel kannst du mit dem mS Timing nicht machen, ausser ganz links und ganz rechts
Beim initialisieren fehlt noch was:
TIMSK |= (1 << TOIE0); enablen timer
michaelb
12.12.2005, 15:54
Hi Picnick,
inzwischen läuft mein Servo! wenigstens in eine Richtung!!
denn ich hab was entscheidendes vergessen:
TIMSK = (1<<TOIE0);
aktueller Code:
#include <avr/io.h>
#include <avr/signal.h>
#include <avr/interrupt.h>
#include <inttypes.h>
#include <math.h>
volatile unsigned char servowert;
volatile unsigned char puls;
volatile unsigned char zaehler;
int set_servo(volatile uint8_t pos)
{
servowert = (pos/180) + 1;
}
int main(void)
{
zaehler = 0;
sei();
DDRB = (1<<PB0);
PORTB = (1<<PB0);
//Timer0 übernimmt die Highphase
//muss 1ms sein also 25 Takte bei einem Prescaler von 64
puls = 25;
TCCR0 = (1<<CS01)|(1<<CS00);
TCNT0 = (255-puls);
TIMSK = (1<<TOIE0);
for(;;)
{
}
}
SIGNAL(SIG_OVERFLOW0)
{
if(zaehler == 0)
{
PORTB = (0<<PB0);
TCNT0 = (255-puls);
zaehler++;
}
else if ( (zaehler > 0 ) && (zaehler < 20 ) )
{
TCNT0 = (255-puls);
zaehler++;
}
else
{
TCNT0 = (255-puls);
zaehler = 0;
PORTB = (1<<PB0);
}
}
warum Preload 6 ich hab doch 25 ausgerechnet?
Wie mach ich das wenn ich in die andere Richtung will? Preload verdoppeln?
Gruß Michi
michaelb
12.12.2005, 15:59
Beim initialisieren fehlt noch was:
TIMSK |= (1 << TOIE0); enablen timer
mist du warst schneller! :cry:
das mit dem 6 Preload hab ich noch net verstanden!!
Dein Programm hab ich auch gecheckt! egal in welcher Position er ist fährt er zu einer bestimmten Stelle!
Gruß Michi
Du must diese Zeile aktivieren:
// case 1: das da aktivieren für 2 mS Servo
sollte er in die andere Ecke
michaelb
12.12.2005, 16:05
Hi Picnick,
ok in die 2ms Ecke fährt er ganz nur in die andere nicht ganz!!
Nochmal die Frage:
Wie kommst du auf 6 Preload?
Gruß Michi
das mit den mS is nicht so genau, je nach servo anders.
anbei eine XLS (Excel) zum Prescale /preload ausrechnen.
ist nur für privatgebrauch, mußt ein wenig mitdenken.
michaelb
12.12.2005, 16:21
Hi,
danke!
ich kapier es trotzdem noch net!!
wie kann man den Preloadwert ausrechnen?
Gruß Michi
michaelb
12.12.2005, 16:27
Hi,
mir ist es gerade eingefallen:
1600000Mhz hat der µC
0.0000000625s zischen den Takten
mit Prescaler: 0.000004s zischen den Takten
0.001s/0.000004s = 250
also 256 - 250 = 6!
warum bin ich da auf 25 gekommen?? weil ich hab das auch so gerechnet!! naja jetzt stimmt es jedenfalls! ich muss da meine Rechnung nochmal angucken!
8-[ 8-[ 8-[
ich muss wohl mit den Zeiten noch ein bisschen rumspielen damit es zu dem Servo exakt stimmt!! Die Exceltabelle hab ich nicht so wirklich verstanden!
Gruß Michi
Clock / Prescaler => Zählfrequenz des Timer0 Spalte B
Zählfrequenz des Timer0 / Wunsch-frequenz --> gibt eine Zahl Spalte C
die muß aber kleiner als 256 sein (Timer0) oder 65536 (timer1)
Wenn sich das ausgeht, ist die Differenz 256 od. 65536 zu der Zahl der preload wert.
gibts's meist zwei oder mehr Lösungen.
btw: ich muß jetzt weg, bis morgen !
michaelb
12.12.2005, 16:37
Hi,
thanks a lot!!
passt gerade! ich muss auch weg!
Gruß Michi
Hier gibt's noch einen Link für einen 10-fach Servocontroller:
https://www.roboternetz.de/phpBB2/zeigebeitrag.php?t=14220
Gruss,
stochri
michaelb
13.12.2005, 18:48
Hi,
@stochri: Danke werd ich mir mal ansehen!!
ich glaub ich muss mal versuchen 0,1ms hinzubekommen da ich dann ne bessere Auflösung hab als nur links und rechts! bisher weiß ich noch nicht wie ich das machen soll! aber da muss ich wohl nen kleineren Prescaler nehmen!!!
Gruß Michi
michaelb
17.12.2005, 12:42
Hi,
inzwischen hab ich es hingebekommen mit den 0.1ms!
Um den Servo(den von Robotikhardware) ganz nach links zu bewegen brauch ich 6 Takte = 0.6ms undnach rechts die 20 Takte = 20ms!
ist das normal??
Gruß Michi
Das ist auch etwa das, was ich brauche. Man müßte natürlich die Zeitdifferenzen durch die Maschinenbefehle berücksichtigen, die vom Timer --> Pulseoff noch verstreichen. Aber was soll's. irgendeine Zahl ist links und eine andere ist rechts. Von der objektiven Zeit hab ich ja nix.
michaelb
17.12.2005, 13:50
Hi Picnick,
mit den Takten mein ich die Interrupts also nach 6 Interrupts 6ms usw.!
Ja die Befehle verbrauchen sxchon ein paar Takte aber die will ich jetzt mal hinterrücklings lassen!
Mein aktueller Code:
#include <avr/io.h>
#include <avr/signal.h>
#include <avr/interrupt.h>
#include <inttypes.h>
#include <math.h>
volatile unsigned char servowert;
volatile unsigned char puls;
volatile unsigned char zaehler;
int set_servo(volatile uint8_t pos)
{
servowert = (pos/180) + 1;
}
int main(void)
{
zaehler = 0;
sei();
DDRB = (1<<PB0);
PORTB = (1<<PB0);
//Timer0 Prescaler 64
//1600000Mhz hat der µC
//0.0000000625s zischen den Takten
//mit Prescaler: 0.000004s zischen den Takten
//0.0001s/0.000004s = 25
//Preloadwert: 231 <= 256-25
//Zeit zwischen Interrupt 0.1ms
//1ms = 10 Takte
//1.5ms = 15 Takte
//2ms = 20 Takte
//20ms = 200Takte
//da erster Takt = 0
//nach 9 Takten low
//nach 199 Takten high!
TCCR0 = (1<<CS01)|(1<<CS00);
TCNT0 = 231;
TIMSK |= (1 << TOIE0);
for(;;)
{
}
}
SIGNAL(SIG_OVERFLOW0)
{
switch(zaehler)
{
case 0:
case 1:
case 2:
case 3:
case 4:
case 5: //Links
case 6:
case 7:
case 8:
case 9: //1ms
case 10:
case 11: //Mitte
case 12:
case 13:
case 14:
case 15://1.5ms
case 16:
case 17:
case 18:
case 19: //2ms //rechts
PORTB |= (1<<PB0);
zaehler++;
break;
case 199:
zaehler = 0;
break;
default:
PORTB &= ~(1<<PB0);
zaehler++;
break;
}
TCNT0 = 231;
}
ich kann jetzt zwar wenn ich jetzt den Servo nach links stellen will einfach vor dem alles was nach case 5: kommt ein // setzen damit er nach linkns fährt, aber will jetzt dass ich einfach sag z.B. Servo 90 und er fährt zur Mittelstellung!! da muss ich wohl noch ein paar Funktionen einsetzen!!
Gruß Michi
batti112
26.12.2005, 18:22
Hi Leute!
Ich bin auch gerade dabei eine Servoansteuerung zu proggen.
Komme bei einer Ansteurung von 1-2ms allerdings bei weitem nicht an die Endanschläge.
Habe kein Oszi kann also nicht die exakten Zeiten messen.
Wie sind eure Erfahrungen so?
(Ich nutze billig Servos)
Von der Hardware her nutze ich:
Atmega8 mit 16Mhz.
Prescaler 64 -> TCNT0 mit 6 initialisiert = 1ms
Um den rechten Rand zu erreichen muss ich TCNT0 mit 255-160 initialisieren.
Um den linken Rand zu erreichen müsste ich TCNT0 mit 255 - 580 inititalisieren, was ich durch mehrfaches aufrufen erreiche.
Ist bei meiner Rechnung etwas falsch, oder ist das normal?
mfG
Batti
Allen noch einen frohen Feiertag!
michaelb
26.12.2005, 19:12
Hi,
als bei mir variieren die Zeiten auch! bei dem Servo von www.robotikhardware.de bentötige ich um den Servo nach ganz links zu drehen 0,6ms und nach rechts 20ms!
Ich denk mal sowas ist normal wegen den Fertigungstoleranzen!
Gruß Michi
batti112
27.12.2005, 16:44
Danke Michi!
Hab mir schon sowas gedacht...
Trotzdem wundert es micht, da ja auch die Fernsteuerungsanlagen mit den billig Dingern zurecht kommen müssen...
mfg
Batti
traedamatic
11.01.2006, 22:27
Hallo leute,
ich habe diesen Thread genau gelesen und versucht auch alles so zu machen wie du (michaelb)! Aber leider passiert bei mir gar nix!
Vielleicht noch was zu meinem Aufbau: ich hab meinem Servo an einem Mega32 und nicht am PortB sonder am PORTD.7 (Pin21)! Ich hab euch auch mal meinen Code mitgepostet, wo ich alles geändert habe wovon ich denke, dass ich es ändern muss! Ich hoffe ihr habt eine Idee warum es bei mir nicht geht!
#include <avr/io.h>
#include <avr/signal.h>
#include <avr/interrupt.h>
#include <inttypes.h>
#include <math.h>
volatile unsigned char servowert;
volatile unsigned char puls;
volatile unsigned char zaehler;
int set_servo(volatile uint8_t pos)
{
servowert = (pos/180) + 1;
}
int main(void)
{
zaehler = 0;
sei();
DDRD = (1<<PD7);
PORTD = (1<<PD7);
//Timer0 übernimmt die Highphase
//muss 1ms sein also 25 Takte bei einem Prescaler von 64
puls = 6;
TCCR0 = (1<<CS21) | (1<<CS20);
TCNT2 = (255-puls);
TIMSK = (1<<TOIE2);
for(;;)
{
}
}
SIGNAL(SIG_OVERFLOW2)
{
if(zaehler == 0)
{
PORTD &= ~(0<<PD7);
TCNT2 = (255-puls);
zaehler++;
}
else if ( (zaehler > 0 ) && (zaehler < 20 ) )
{
TCNT2 = (255-puls);
zaehler++;
}
else
{
TCNT2 = (255-puls);
zaehler = 0;
PORTD = (1<<PD7);
}
}
Danke für eure Antworten
Matic
michaelb
12.01.2006, 14:58
Hi,
ich finde in deinem Code folgende Fehler:
>TCNT2 durch TCNT0 erstetzen da du den Timer0 benutzen tust!
>puls = 6 wie kommt du darauf? Ersetz das mal durch puls = 25
>warum setzt du das Bit TOIE2? Für Timer0 brauchst du doch TOIE2!
ok wenn du den Timer2 nutzen willst dann brauchst du schon TCNT2 und TOIE0 aber dann muss du die entsprechenden Bits im TCCR2 setzen statt im register TCCR0! wenn du nämlich diese Bits nicht setzen tust dann arbeitet der Timer gar nicht!!
Gruß Michi
traedamatic
14.01.2006, 20:08
Hi,
so jetzt habe ich endlich zeit gehabt um deine Fehler zu korrigieren:
#include <avr/io.h>
#include <avr/signal.h>
#include <avr/interrupt.h>
#include <inttypes.h>
#include <math.h>
volatile unsigned char servowert;
volatile unsigned char puls;
volatile unsigned char zaehler;
int set_servo(volatile uint8_t pos)
{
servowert = (pos/180) + 1;
}
int main(void)
{
zaehler = 0;
sei();
DDRD = (1<<PD7);
PORTD = (1<<PD7);
//Timer0 übernimmt die Highphase
//muss 1ms sein also 25 Takte bei einem Prescaler von 64
puls = 25;
TCCR2 = (1<<CS21) | (1<<CS20);
TCNT2 = (255-puls);
TIMSK = (1<<TOIE0);
for(;;)
{
}
}
SIGNAL(SIG_OVERFLOW2)
{
if(zaehler == 0)
{
PORTD &= ~(0<<PD7);
TCNT2 = (255-puls);
zaehler++;
//PORTB = (0<<PB0);
}
else if ( (zaehler > 0 ) && (zaehler < 20 ) )
{
TCNT2 = (255-puls);
zaehler++;
}
else
{
TCNT2 = (255-puls);
zaehler = 0;
PORTD = (1<<PD7);
}
}
Aber leider tut sich immer noch nichts! Ich weiß nicht woran es liegen kann! Naja vielleicht könnt ihr ja was im Quellcode finden! Danke für eure hilfe!
matic
michaelb
14.01.2006, 20:22
Hi matic,
also ich hab folgende Fehler gefunden:
>ändere das:
TCCR2 = (1<<CS21) | (1<<CS20);
in das
TCCR2 = (1<<CS22);
um damit du den Prescaler von 64 hast!!! Datenblatt lesen!
>das
TIMSK = (1<<TOIE0);
kann nicht stimmen!!
das muss:
TIMSK = (1<<TOIE2);
heißen den du verwendest ja den Timer2!
TOIE2 = Timer Overflow Interrupt Enable für den Timer2!
Gruß Michi
traedamatic
14.01.2006, 21:24
Hi,
Ok letzter versuch. Servo bewegt sich überhaupt nicht! Vielleicht liegt es ja nicht am Quellcode sondern am löten! Aber ich hab das auch schon 1000 mal gemessen!
Hier noch mal der veränderte Code:
#include <avr/io.h>
#include <avr/signal.h>
#include <avr/interrupt.h>
#include <inttypes.h>
#include <math.h>
volatile unsigned char servowert;
volatile unsigned char puls;
volatile unsigned char zaehler;
int set_servo(volatile uint8_t pos)
{
servowert = (pos/180) + 1;
}
int main(void)
{
zaehler = 0;
sei();
DDRD = (1<<PD7);
PORTD = (1<<PD7);
//Timer0 übernimmt die Highphase
//muss 1ms sein also 25 Takte bei einem Prescaler von 64
puls = 25;
TCCR2 = (1<<CS22);
TCNT2 = (255-puls);
TIMSK = (1<<TOIE2);
for(;;)
{
}
}
SIGNAL(SIG_OVERFLOW2)
{
if(zaehler == 0)
{
PORTD &= ~(0<<PD7);
TCNT2 = (255-puls);
zaehler++;
//PORTB = (0<<PB0);
}
else if ( (zaehler > 0 ) && (zaehler < 20 ) )
{
TCNT2 = (255-puls);
zaehler++;
}
else
{
TCNT2 = (255-puls);
zaehler = 0;
PORTD = (1<<PD7);
}
}
Was macht eigentlich die funktion "set_servo" ? Ich sehe gar nicht wo die auf gerufen wird! Oder übersehe ich was?
matic
FartingWeasel
09.02.2006, 21:21
Hallo
also ich hab ein mavr board mit nem atmega8 und nem 3,6864 Mhz quarz drauf...
Ich möcht probieren damit nen Servo anzusteuern so billig dinger vom conrad. hab jetz mal den Code von michaelelb und mein Servo bewegt sich nach auch nach rechts. Bloß ich versteh den Code noch nicht ganz. Ich hab auch nen prscaler von 64 benutzt.
aber mir sagt des ganze tcnt0 und so zeugs ist und so ist. kann mir das mal einer erklären? wär echt super.
und was muss ich machen damit ich den servo zum beispiel in seine mittelposition fahr oder bzw nach links??
wär echt super wenn ihr mir helfen könntet.
Hier das Programm:
weiß leider nicht wie man dass in so nem weißen fenster postet...
#include <avr/io.h>
#include <avr/signal.h>
#include <avr/interrupt.h>
#include <inttypes.h>
#include <math.h>
#ifndef F_CPU
#define F_CPU 3686400UL /* Quarz mit 3.6864 Mhz */
#endif
volatile unsigned char servowert;
volatile unsigned char puls;
volatile unsigned char zaehler;
int set_servo(volatile uint8_t pos)
{
servowert = (pos/180) + 1;
}
int main(void)
{
zaehler = 0;
sei();
DDRB = (1<<PB0);
PORTB = (1<<PB0);
//Timer0 Prescaler 64
//3686400 hz hat der µC
//0.000000271s zischen den Takten
//mit Prescaler: 0.00001736 zischen den Takten
//0.0001s/0.00001736s = 57,6
//Preloadwert: 198,4 <= 256-57,6
//Zeit zwischen Interrupt 0.1ms
//1ms = 10 Takte
//1.5ms = 15 Takte
//2ms = 20 Takte
//20ms = 200Takte
//da erster Takt = 0
//nach 9 Takten low
//nach 199 Takten high!
TCCR0 = (1<<CS01)|(1<<CS00);
TCNT0 = 198,4;
TIMSK |= (1 << TOIE0);
for(;;)
{
}
}
SIGNAL(SIG_OVERFLOW0)
{
switch(zaehler)
{
case 0:
case 1:
case 2:
case 3:
case 4:
case 5: //Links
case 6:
case 7:
case 8:
case 9: //1ms
case 10:
case 11: //Mitte
case 12:
case 13:
case 14:
case 15://1.5ms
case 16:
case 17:
case 18:
case 19: //2ms //rechts
PORTB |= (1<<PB0);
zaehler++;
break;
case 199:
zaehler = 0;
break;
default:
PORTB &= ~(1<<PB0);
zaehler++;
break;
}
TCNT0 = 198.4;
}
super_castle
10.02.2006, 13:10
20 Takte = 20ms!
stimmt nicht..................
FartingWeasel
10.02.2006, 19:52
ist mir nicht ganz klar wieso das nicht stimmt,deshalb wärs super wenn mir das einer erklären könnte.
hab den code nur kopiert und den prescaler geändert und den cpu clock aber ich hab das problem dass mir der rest nichts sagt und in den tutorials find ich auch nichts...
kann mir jemand helfen.
will einfach nur meinen servo entweder nach recht bzw nach links drehen...
michaelb
10.02.2006, 21:33
Hi Leutz,
@FartingWeasel:
//0.0001s/0.00001736s = 57,6
stimmt nicht!!
wenn ich das rechne bekomme ich 5,76 raus!!
TCNT0 = 198,4;
Ich glaube du kannst das Timercounter Register nicht mit einem nicht ganzen Wert füllen!
Gruß Michi
Powered by vBulletin® Version 4.2.5 Copyright ©2024 Adduco Digital e.K. und vBulletin Solutions, Inc. Alle Rechte vorbehalten.