PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Servoansteuerung mit Software PWM und ATmega8(EDIT: neu 644)



hosti
22.06.2008, 09:44
Hallo,

Ich hab mir ein Programmm geschrieben welches mir in 20ms abstand ein 1.5ms langes Highsignal ausgiebt. Damit möchte ich meine Servos in die mittelstellung bringen. Das funktioniert auch... irgendwie.

Leider zucken meine Servos unregelmässig und mein Signal verändert sich manchmal von schönen graden Flanken zu schwankenden herabfallenden :-k

Ich glaube am Aufbau liegt es nicht also wollt ich mal sehen ob vieleicht ihr verbesserungsvorschläge zum Code, oder gar eine erklärung habt.

Was vermutlich nicht ideal ist, ist meine Frequenz(des Interupts)
da ich eine Kommazahl in die Konstante Timer schreibe.

Hier ist mein Code:



#include <avr/io.h>
#include <avr/interrupt.h>

#define F_CPU 8000000
#define timer (256-F_CPU/64/2000)
int ms;

ISR(TIMER0_OVF_vect) //Timer Interrupt Vector
{
TCNT0 = timer; //Interupt auf 2000Hz einstellen (Alle 0.5ms ein Interupt)
ms++;
}

int main(void)
{

//Ports Init
DDRC |= (1<<PORTC3);

//Timer Init
TIMSK |= (1<<TOIE0); //Timerinterrupt freigeben
TCCR0 |= (1<<CS00) | (1<<CS01) | (!(1<<CS02)); //Timer Prescaler = 64

sei(); //Interrupts global aktivieren

//main Schleife
for (;;)
{
if(ms >=40) //Wenn ms grösser gleich 40 (40*0.5=20ms) -->40 ausgelöste Interupts
{
PORTC |= (1<<PORTC3); //Port auf HIGH.
}

{
if(ms >= 43) //Wenn ms grösser gleich 43 (43-40=3*0.5=1.5ms)
{
PORTC &= ~(1<<PORTC3); ms = 0; //Port auf LOW und ms löschen
}

}

}


}

Achja, ich habe den ATmega8 über die Fusebits intern auf 8Mhz gestellt.
und zwar auf 8 Mhz, 6CK, 64ms.
6CK = 6 Cyklen??, 64ms?? kann mir jemand erklären was das bedeutet?

Ceos
22.06.2008, 12:01
deine formatierung ist etwas unkonventionell, die geschweiften klammern um das 2te if herum sind iwie nutzlos .... EGAL

einen rat, erhöhe auf jeden fall die frequenz, damit du eine bessere auflösung für deinen servo hast, so 20kHz mind. statt 2kHz ...


und prüfe die bedingung
if (ms >= 40)
oben im interrupt, statt in der main(), so wird bei JEDER inkrementierung auch der wert überprüft und zu verpasst keinen interrupt ... ausserdem hast du die main() für andere aufgaben (die steuerung zum beispiel) frei

dasselbe machst du auch mit der
if(ms >= 43)
ab in die ISR damit

jetzt passt du den prescaler an (20kHz) und natürlich die werte bei denen du den pin schaltest, legst dir noch ein paar variablen für das tastverhalten an, also statt
if (ms >= 400)
if (ms >= PortC3ONval) PortC3_AN();

und

if (ms >= PortC3OFFval) PortC3_AUS();

du könntest jetzt statt dem reset bei der 2ten bedingung zu machen auchnoch was anderes versuchen,
du legst dir ne variable an, die deine wiederholrate festlegt (20ms in deinem fall, natürlich umgerechnet auf deinen counter) und jetzt kannst du für mehrere ports mehrere variablen anlegen, die jeweils einen offset bilden von 0 (so dass alle 3ms ein anderer port angesteuert wird) bis 20ms und eine variable die die pulslänge für jeden port bestimmt, wenn dein counter dann denr resetwert errreicht, wird er zurückgesetzt ... damit kannst du "beliebig" viele pins für servos benutzen .... zumal das ja sicher dein ziel war .... ich hoffe ich habe dir jetzt nicht zu viel vorweggenommen :p

EDIT: das erklärt jetzt nicht unbedingt die "schwachen" flanken, ich hatte ein leicht ähnliches verhalten, wenn ich bei mir die impulslänge inkrementiere, hatte ich durch den spannungseinbruch durch den verbrauch des servomotor einen minimalen nachlaufeffekt .... kaum wahrnehmbar aber existent ... versuche mal ein paar pufferkondensatoren an die versorgungsspannung deines µC anzuschliessen und eventuell einen transistor zwischen µC und impulsleitung des servo hängen, die haben die angewohnheit ihren strom aus der impulsleitung zu ziehen, wenn mal der versorgungspin "abrutschen" sollte und das mag dein µC sicher nicht

hosti
22.06.2008, 13:25
Danke für deine Ausführlichen Erklärungen.

Ist es den kein Problem das ich dem Timer eine Kommazahl übergebe?
#define timer (256-F_CPU/64/2000)
256-8000000/64/2000 = 193.5 oder auch
256-8000000/64/20000 = 249.75

Für das Tast verhältniss variablen oder gleich Konstanten definieren im Präprozessor?

Kann es sein das nicht alle Servo's absolut indentisch sind?
wen ich alle Servos in die mitte fahren lasse und diese dann auf die Servohörner aufstecke dann sind alle leicht zu einander verschoben... ist das normal?

Ceos
22.06.2008, 14:32
1.nein, alles hinterm komma wird total ignoriert, es wird abgeschnitten
2.natürlich variablen, du möchtest die servos ja steuern
3.ja, manche sind auch temperaturempfindlich, die verstellen sich immer weiter wenn sie dauerbelastet werden
4.tja da hilft nur mechanisch nullstellen, also die hörner leicht drehen(wenn das so n geriffelte welle ist) oder eben software nachstellen ... also ne art korrekturwert für jeden port .... ich bevorzuge die mechanische lösung ...

hosti
28.07.2008, 19:03
ärgerlich, es klappt leider nicht so wie ich es möchte.
Folgendes Signal möchte ich ja gerne:
http://www.8ung.at/hosti/bilder/9.jpg
Dieses Signal hat folgender Code erstellt:

#include <avr/io.h>
#include <avr/interrupt.h>

#define F_CPU 8000000
#define timer (256-F_CPU/64/20000)

int ms; //Interuptzählvariable

ISR(TIMER0_OVF_vect) //Timer Interrupt Vector
{
TCNT0 = timer; //Interupt auf 20000Hz einstellen (Alle 0.05ms ein Interupt)
ms++;
if(ms>=370)
{
PORTB |= (1<<PORTB0);
}
if(ms>=400)
{
PORTB &= ~(1<<PORTB0); ms = 0;
}
}

int main(void)
{

//Ports Init
DDRC |= (1<<PORTB0);

//Timer Init
TIMSK |= (1<<TOIE0); //Timerinterrupt freigeben
TCCR0 |= (1<<CS00) | (1<<CS01) | (!(1<<CS02)); //Timer Prescaler = 64

sei(); //Interrupts global aktivieren

//main Schleife
for (;;)
{

}

}


Genau dieser Code erzeugt aber manchmal... wie z.B. jetzt dieses Signal:
http://www.8ung.at/hosti/bilder/15.jpg

Mir ist es absolut unverständlich. Ich habe auch schon den Controller gewechselt.

Das mein Board kaputt ist kann fast nicht sein, resp. es gibt keinen Grund dazu. Ich verwende ein MysmartUSB v2.

Plötzlich wird aus 2.6...2.7V --->500mV. Da stimmt doch was nicht.
Und die starkabfallenden flanken.... :-k

radbruch
28.07.2008, 19:25
Hallo hosti

Es gibt natürlich zig Möglichkeiten ein Signal für ein Servo zu erzeugen. Warum quälst du dich so? Schau dir mal den Beispiel-Code im RN-Wissen zu diesem Thema an:
https://www.roboternetz.de/wissen/index.php/Servo

Wenn du nicht unbedingt an "deinem Weg" festhalten willst wäre das eine mögliche Lösung. 2 bzw. 20kHz als Taktfrequenz sind eher unüblich. Ohne zwingende Gründe würde ich mich an die Standards halten und einen 100kHz-Takt verwenden.

Gruß

mic

hosti
28.08.2008, 14:46
Ich versuche mich jetzt neu am Rn-wissen Beispiel.
So sieht der Code im Moment aus:


#include <avr/io.h> // I/O Port definitions
#include <avr/interrupt.h> // Interrupt macros

#define F_CPU 16000000
#define SERVOPIN 7
#define SERVOPORT PORTA
#define DDRSERVO DDRA

volatile unsigned char servopos;

void servo_init()
{

TIMSK2 |=(1<<OCIE2);
TCCR2 |= (1<<WGM21) | (1<<CS20); //Prescale=1, CTC mode
OCR2 = F_CPU/100000; //alle 10µS ein IRQ
DDRSERVO|=(1<<SERVOPIN);
};

ISR(TIMER2_COMP_vect)
{

static int count;
if(count>servopos)
SERVOPORT&=~(1<<SERVOPIN);
else
SERVOPORT|=(1<<SERVOPIN);
if(count<2000+servopos)
count++;
else
count=0;
};

int main(void)
{

DDRA = 0xFF;
PORTA = 0x00;

sei();
servo_init();

servopos=100;

while(1)
{

}
return 0;
}



Jetzt kriege ich aber folgende Fehlermeldung beim compilieren:

compilieren ... Servos neutral664.cc: In function `void servo_init()':
Servos neutral664.cc:14: error: `OCIE2' was not declared in this scope
Servos neutral664.cc:15: error: `TCCR2' was not declared in this scope
Servos neutral664.cc:16: error: `OCR2' was not declared in this scope
Servos neutral664.cc: At global scope:
Servos neutral664.cc:20: warning: `TIMER2_COMP_vect' appears to be a misspelled signal handler


Mir ist irgendwie nicht klar was er mir damit sagen möchte.
Die berrechnung der PWM ist falsch, das weis ich. Ich wollt erst mal den Timer zum laufen bringen.

hosti
29.08.2008, 13:35
Keiner ne Idee?

Klingt für mich irgendwie als ob diese Register/Funktionen nicht bekannt wären. Sollten sie meiner Meinung nach (Oder dem Datenblatt nach) sein.
Verzweiflung ](*,)

radbruch
29.08.2008, 14:35
Hallo

Ohne ein paar kleine Fehler (TIMSK2 und servo_init() ) kann man dein Programm fast übersetzen:

#include <avr/io.h> // I/O Port definitions
#include <avr/interrupt.h> // Interrupt macros

#define F_CPU 16000000
#define SERVOPIN 7
#define SERVOPORT PORTA
#define DDRSERVO DDRA

volatile unsigned char servopos;

void servo_init(void)
{

TIMSK |=(1<<OCIE2);
TCCR2 |= (1<<WGM21) | (1<<CS20); //Prescale=1, CTC mode
OCR2 = F_CPU/100000; //alle 10µS ein IRQ
DDRSERVO|=(1<<SERVOPIN);
};

ISR(TIMER2_COMP_vect)
{

static int count;
if(count>servopos)
SERVOPORT&=~(1<<SERVOPIN);
else
SERVOPORT|=(1<<SERVOPIN);
if(count<2000+servopos)
count++;
else
count=0;
};

int main(void)
{

DDRA = 0xFF;
PORTA = 0x00;

sei();
servo_init();

servopos=100;

while(1)
{

}
return 0;
}



Allerdings funktionieren die Zugriffe auf den Port A überhaupt nicht:

avr-gcc -mmcu=atmega8 -Os -mno-interrupts -funsigned-char -funsigned-bitfields -Wall -Wstrict-prototypes -ggdb -c -DF_CPU=8000000UL -Wa,-acdhlmns=temp.lst temp.c -o temp.o
temp.c: In function `servo_init':
temp.c:17: error: `DDRA' undeclared (first use in this function)
temp.c:17: error: (Each undeclared identifier is reported only once
temp.c:17: error: for each function it appears in.)
temp.c: In function `__vector_3':
temp.c:25: error: `PORTA' undeclared (first use in this function)
temp.c: In function `main':
temp.c:37: error: `DDRA' undeclared (first use in this function)
temp.c:38: error: `PORTA' undeclared (first use in this function)
make: *** [temp.o] Error 1

In der iom8.h (die über io.h eingebunden wird) sind die Register für Port A nicht definiert:

...
/* Port D */
#define PIND _SFR_IO8(0x10)
#define DDRD _SFR_IO8(0x11)
#define PORTD _SFR_IO8(0x12)

/* Port C */
#define PINC _SFR_IO8(0x13)
#define DDRC _SFR_IO8(0x14)
#define PORTC _SFR_IO8(0x15)

/* Port B */
#define PINB _SFR_IO8(0x16)
#define DDRB _SFR_IO8(0x17)
#define PORTB _SFR_IO8(0x18)
...
Was schlicht daran liegt, dass der Mega8 keinen Port A besitzt. Versuche mal 'nen anderen Port.

Gruß

mic