Hallo zusammen,
seit ein paar Wochen versuche ich mit einem Atmega32 8 Servos anzusteuern, jedoch kamen einige Probleme auf mich zu.
Ich denke, dass ich alle Hardware-Fehler beseitigt habe, jedoch funktioniert noch nichts.
Zur Ansteuerung der Servomotoren wollte ich mich am Quellcode von der Seite
http://www.mikrocontroller.net/artic...vo_Ansteuerung
orientieren.
Ich habe alle Einstellungen mit dem Datenblatt des Atmega 32 verglichen und konnte keine Fehler oder Änderungen feststellen.
Des Weiteren arbeite ich mit 16MHz, nicht mit 11Mhz wie im Beispiel. Dies habe ich im Quellcode geändert.
Alle 8 Servomotoren sollen am PortC angeschlossen werden.
Kann mir jemand meine Fehler aufzeigen? Bzw. mir einen Tipp geben?Code:// // Programm fuer einen ATmega32 // #define F_CPU 16000000UL #include <avr/io.h> #include <avr/interrupt.h> #include <util/delay.h> // // Der Prescaler muss so gewählt werden, dass der Ausdruck // für MILLISEC_BASE einen Wert kleiner als 128 ergibt // MILLISEC_BASE ist der Timerwert, der 1 Millisekunde Zeitdauer ergeben // soll. // #define PRESCALER 128 #define PRESCALER_BITS (1<<CS22) | ( 1 << CS20 ) #define MILLISEC_BASE ( F_CPU / PRESCALER / 1000 ) #define CENTER ( MILLISEC_BASE /2 ) // // Konfiguration der Servoleitungen // #define NR_SERVOS 8 #define SERVO_DDR DDRC #define SERVO_PORT PORTC uint8_t ServoPuls[NR_SERVOS] = { 1<<PC0, 1<<PC1, 1<<PC2, 1<<PC3, 1<<PC4, 1<<PC5, 1<<PC6, 1<<PC7 }; // // Werte für die Servoposition // Gültige Werte laufen von 0 bis 2 * CENTER // 0 ... ganz links // CENTER ... Mittelstellung // 2 * CENTER ... ganz rechts // volatile uint16_t ServoValue[NR_SERVOS]; ISR (TIMER2_COMP_vect) { static uint8_t ServoId = 0; // // den Puls des aktuellen Servos beenden // SERVO_PORT &= ~ServoPuls[ServoId]; // // welches ist das nächste aktuelle Servo? // if( ++ServoId >= NR_SERVOS ) ServoId = 0; // // die Ausgangsleitung fuer dieses Servo auf 1; den Puls beginnen // SERVO_PORT |= ServoPuls[ServoId]; // // den Timer so einstellen, dass bei Pulsende, die ISR erneut aufgerufen wird // OCR2 = MILLISEC_BASE + ServoValue[ServoId]; } void InitServo() { uint8_t i; // // Die Servoleitungen auf Ausgang stellen // SERVO_DDR = ServoPuls[0] | ServoPuls[1] | ServoPuls[2] | ServoPuls[3] | ServoPuls[4] | ServoPuls[5] | ServoPuls[6] | ServoPuls[7]; // // Alle Servos in Mittelstellung // for( i = 0; i < NR_SERVOS; ++i ) ServoValue[i] = CENTER; // // Timer auf CTC Modus konfigurieren // OCR2 = MILLISEC_BASE + ServoValue[0]; TIMSK |= (1<<OCIE2); TCCR2 = (1<<WGM21) | PRESCALER_BITS; // CTC mode } int main(void) { InitServo(); sei(); _delay_ms( 1000 ); // // testweise einfach alle 8 Servos ansteuern // jedes Servo soll sich unterschiedlich schnell bewegen // while( 1 ) { ServoValue[0] += 2; if( ServoValue[0] > 2*CENTER ) ServoValue[0] -= 2*CENTER; ServoValue[1] += 1; if( ServoValue[1] > 2*CENTER ) ServoValue[1] -= 2*CENTER; ServoValue[2] += 2; if( ServoValue[2] > 2*CENTER ) ServoValue[2] -= 2*CENTER; ServoValue[3] += 3; if( ServoValue[3] > 2*CENTER ) ServoValue[3] -= 2*CENTER; ServoValue[4] += 1; if( ServoValue[4] > 2*CENTER ) ServoValue[4] -= 2*CENTER; ServoValue[5] += 3; if( ServoValue[5] > 2*CENTER ) ServoValue[5] -= 2*CENTER; ServoValue[6] += 2; if( ServoValue[6] > 2*CENTER ) ServoValue[6] -= 2*CENTER; ServoValue[7] += 1; if( ServoValue[7] > 2*CENTER ) ServoValue[7] -= 2*CENTER; _delay_ms( 400 ); } }
Grüße High Light
__________________________________________________ _____________
http://pe-products.jimdo.com/
__________________________________________________ _____________
http://pe-products.jimdo.com/
Für (m)einen Roboterkopf habe ich zehn Servos anzusteuern (zum Video runterscrollen). Die Eigenbauplatine bedient die Servos mit besser als 10 Bit Auflösung bei einer Servoperiode von rund 25 ms; es läuft darauf ein mega1284 mit 20 MHz. Mit knapp weniger als 20 ms könnten es dann immer noch acht Servos sein. Das Prinzip ist theoretisch in der Lage auch doppelt so viel Servos anzusteuern. Die Geschichte müsste in ähnlicher Weise auch mit nem mega32 gehen. Da der nur 16 MHz macht (oder?) muss man eben die Anzahl der bedienten Servos entsprechend niedriger ansetzen. Vermutlich sechs bei 20 ms Periodendauer und knapp acht (*gg*) bei 25 ms.... Wie steuert ihr mehrere Servos mit einem Atmega an?
Das Timingprinzip ist hier in einem Codeauszug gezeigt: ein Timer mit einer Periodendauer von 2,5 ms startet jeweils einen umlaufend adressierten Servopuls (den Pin auf high) und einen zweiten Timer(kanal) dessen Laufzeit OCR1B den entsprechenden Servopin wieder zurücksetzt. Aus den zehn Perioden wird dann die Gesamtperiode für einen der zehn Servos. Servos die nicht adressiert werden laufen dann leer mit. Die Lösung ist also auch für nur einen oder zwei Servos geeignet, dann laufen die restlichen Teilperioden einfach leer.
Ciao sagt der JoeamBerg
Vom Prinzip her schaut die Ansteuerung bei mir gleich aus wie bei dir. Also 1 timer im CTC Modus. Deine Werte sollten auch passen wenn ich sie mit meinen vergleiche. (ATmega8 @16MHz)
Tut sich gar nix?
EDIT: Du verwendest keinen Synchronisationszyklus. Am Anfang hast du eine Periode von nur knapp 8ms. Vielleicht ist das für die Servos schon zu wenig.
Geändert von Siad (16.04.2014 um 08:08 Uhr)
Sorry, hab ich erst gerade jetzt gelesen. Dieser Satz, aber "dachte" - tempus perfectum, nicht praesens, wird häufig vor längeren Fehlersuchodysseen gesagt. So wie viele Unfallberichte beginnen mit "Nach Meinung aller Fachleute waren alle denkbaren Sicherheitsmaßnahmen getroffen worden ...".... Ich denke, dass ich alle Hardware-Fehler beseitigt habe, jedoch funktioniert noch nichts ...
WAS hast Du alles getan? GND von allen Baugruppen verbunden (siehe SchaltSCHEMA) - im Link mal runterscrollen. GND eher sternförmig, keine Ringschleife? Separate Servoversorgung, getrennt vom Controller, aber GND!! verbunden, welche Servos, ausreichend Energie für die Servos - manche ziehen im Stillstand satte zig MilliAmpere - PRO Stück, usf. ??? Zum Code, der ja wohl ähnlich angesetzt ist wie meiner, wäre ja auch ein Schaltbild hilfreich - wenn wir dann alle sicher sind, dass das Schaltbild den ISTzustand genau trifft *gg*.
Ciao sagt der JoeamBerg
Danke, für die Antworten.
In der Tat waren ein paar kleine Fehler auf der Platine, jedoch war das größte Problem, eine defekte Platine zur Programmierung...
Wenn ich heute Abend daheim bin, werde ich einen Schaltplan posten.
Getrennte Spannungsquellen und gemeinsame Masse ist umgesetzt.
Ich vermute, dass es an den Einstellungen des Timers 2 liegt....
__________________________________________________ _____________
http://pe-products.jimdo.com/
Also ich rechne mal (und denk dabei nicht weiter nach):Code:#define F_CPU 16000000UL ... // für MILLISEC_BASE einen Wert kleiner als 128 ergibt // MILLISEC_BASE ist der Timerwert, der 1 Millisekunde Zeitdauer ergeben // soll. // #define PRESCALER 128 #define PRESCALER_BITS (1<<CS22) | ( 1 << CS20 ) #define MILLISEC_BASE ( F_CPU / PRESCALER / 1000 ) #define CENTER ( MILLISEC_BASE /2 ) ...
( F_CPU / PRESCALER / 1000 )
16 000 000 [Hz] / 128 / 1000 = 125 [Hz]
1 ms sind - mal locker gesagt: ... 1000 Hz
Irre ich mich ??? (Hab grad den Kopf voll mit ungelösten Themen)
Ciao sagt der JoeamBerg
Die 1000Hz sind ja der zweite Divisor bei 16 000 000[Hz] / 128 / 1000 [Hz] = 125.
Die 125 sind der Wert für OCR2 um auf 1ms zukommen. 16 000 000[Hz] / 128 / 125 = 1000 [Hz].
Hast Recht, siad - ich muss mir das aber noch genauer durchsehen. Pingelig: ich denke dass die theoretische Mitte bei OCR2 124 ist (das sind dann 125 Schritte ab 0). Ich muss mir das Ganze mal in Ruhe durcharbeiten, denn nu kann doch OCR2 nicht mehr kleiner als 125 werden in OCR2 = MILLISEC_BASE + ServoValue[ServoId]; wegen uint16_t ServoValue[NR_SERVOS]. Also mehr bis später.... Die 125 sind der Wert für OCR2 um auf 1ms zukommen ...
Ciao sagt der JoeamBerg
Hallo,
wurde auch die JTAGen-Fuse deaktiviert? Sonst gehen am PORTC beim ATmega32 einige Pins nicht richtig (PC2 bis 5).
Grüße, Bernhard
"Im Leben geht es nicht darum, gute Karten zu haben, sondern auch mit einem schlechten Blatt gut zu spielen." R.L. Stevenson
Lesezeichen