- 12V Akku mit 280 Ah bauen         
Ergebnis 1 bis 9 von 9

Thema: Servoansteuerung mit Software PWM und ATmega8(EDIT: neu 644)

  1. #1
    Erfahrener Benutzer Begeisterter Techniker
    Registriert seit
    02.05.2004
    Alter
    38
    Beiträge
    388

    Servoansteuerung mit Software PWM und ATmega8(EDIT: neu 644)

    Anzeige

    Praxistest und DIY Projekte
    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

    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:
    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?

  2. #2
    Erfahrener Benutzer Robotik Einstein
    Registriert seit
    11.12.2007
    Ort
    weit weg von nahe Bonn
    Alter
    39
    Beiträge
    3.416
    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

    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

  3. #3
    Erfahrener Benutzer Begeisterter Techniker
    Registriert seit
    02.05.2004
    Alter
    38
    Beiträge
    388
    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?

  4. #4
    Erfahrener Benutzer Robotik Einstein
    Registriert seit
    11.12.2007
    Ort
    weit weg von nahe Bonn
    Alter
    39
    Beiträge
    3.416
    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 ...

  5. #5
    Erfahrener Benutzer Begeisterter Techniker
    Registriert seit
    02.05.2004
    Alter
    38
    Beiträge
    388
    ärgerlich, es klappt leider nicht so wie ich es möchte.
    Folgendes Signal möchte ich ja gerne:
    Bild hier  
    Dieses Signal hat folgender Code erstellt:
    Code:
    #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:
    Bild hier  

    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....

  6. #6
    Moderator Robotik Visionär Avatar von radbruch
    Registriert seit
    27.12.2006
    Ort
    Stuttgart
    Alter
    61
    Beiträge
    5.799
    Blog-Einträge
    8
    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
    Bild hier  
    Atmel’s products are not intended, authorized, or warranted for use
    as components in applications intended to support or sustain life!

  7. #7
    Erfahrener Benutzer Begeisterter Techniker
    Registriert seit
    02.05.2004
    Alter
    38
    Beiträge
    388
    Ich versuche mich jetzt neu am RN-Wissen Beispiel.
    So sieht der Code im Moment aus:

    Code:
    #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.

  8. #8
    Erfahrener Benutzer Begeisterter Techniker
    Registriert seit
    02.05.2004
    Alter
    38
    Beiträge
    388
    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

  9. #9
    Moderator Robotik Visionär Avatar von radbruch
    Registriert seit
    27.12.2006
    Ort
    Stuttgart
    Alter
    61
    Beiträge
    5.799
    Blog-Einträge
    8
    Hallo

    Ohne ein paar kleine Fehler (TIMSK2 und servo_init() ) kann man dein Programm fast übersetzen:
    Code:
    #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:
    Code:
    ...
    /* 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
    Bild hier  
    Atmel’s products are not intended, authorized, or warranted for use
    as components in applications intended to support or sustain life!

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •  

Solar Speicher und Akkus Tests