Forum: Mikrocontroller und Digitale Elektronik Servo an Atmega32 (C)


von Marcel H. (marcelhuu)


Lesenswert?

Hallo zusammen. Ich bin neu hier im Forum, also seid bitte nicht zu 
streng zu mir. :-)

Ich habe schon einige Erfahrungen mit Mikrocontrollern gemacht, 
beschäftige mich heute jedoch zum ersten Mal mit Servos - bisher jedoch 
erfolglos.

Als Servo benutze ich den RS-2 Modellbauservo. Als Mikrocontroller 
verwende ich einen Atmega 32 mit einem 12MHZ-Quarz, den ich in C 
programmiere.

Nun zu dem Problem:
Wenn ich folgendes Programm auf den Mikrocontroller übertrage, dann 
sollte sich der Servo eigentlich endlos vom einen Anschlag zum anderen 
Anschlag bewegen, doch er bewegt sich bei mir nur zum einen Anschlag und 
bleibt dann dort, leicht zitternd. Wenn ich nun das Servohorn bewegen 
möchte und dann wieder loslasse, so bewegt sich das Servohorn wieder 
zurück zu dem Anschlag.

Der Code stammt ursprünglich von der Seite 
https://newbiehack.com/MicrocontrollerControlAHobbyServo.aspx , dessen 
Tutorial über Servos ich heute unter anderem durchgearbeitet habe.

Ein mögliches Problem ist der Quarz, der bei mir 12MHz groß ist. Dies 
sorgt dafür, dass der Wert von ICR1 größer als 65535 wird. Auf der 
Internetseite steht, dass man in einem solchen Fall einen sogenannten 
Prescaler verwenden soll. Ich habe jedoch leider nicht verstanden, was 
das genau sein soll. Doch ich habe so das Gefühl, dass dies nicht die 
Ursache des Problems ist.

Ich habe auch einige -  um nicht zusagen viele -  Codes zu Servos aus 
diesem Forum ausprobiert, doch der Servo macht nie was er soll. Bei 
jedem Beispiel, das ich ausprobiert habe, bewegt sich der Servo zu einem 
Anschlag, manchmal zuckt er dann noch willkürlich. Der Servo ist jedoch 
nicht kaputt, da ich auch schon andere, baugleiche Servos ausprobiert, 
die dieselben Bewegungen ausüben.

Ich hoffe, dass ihr mir bei meinem Problem helfen könnt und wünsche euch 
ansonsten noch einen schönen Abend.

Mit freundlichen Grüßen
Marcel

1
#include <avr/io.h>
2
#include <util/delay.h>
3
4
5
int main(void)
6
{
7
  DDRD = (1<<PD5);  // Servo 1 an PD5, Servo 2 an PD4
8
  TCCR1A |= 1<<WGM11 | 1<<COM1A1 | 1<<COM1A0;
9
  TCCR1B |= 1<<WGM13 | 1<<WGM12 | 1<<CS12;
10
  ICR1 = 240000; //(ICR1 = F_CPU / (Servo acceptable Hz value)=12.000.000/50)    
11
  OCR1A = ICR1 - 800;
12
  while(1)
13
  {
14
    OCR1A =  ICR1 - 800;
15
    _delay_ms(1000);
16
    OCR1A = ICR1 - 2200;
17
    _delay_ms(1000);
18
  }
19
}

von Rudolph (Gast)


Lesenswert?

Marcel Hunfeld schrieb:
> ICR1 = 240000;

240000 ist etwas weit raus für einen 16 Bit Timer,
da steht einfach nicht mal ansatzweise drin was Du erwartest.

von Mike A. (Gast)


Lesenswert?

Marcel Hunfeld schrieb:
> Ein mögliches Problem ist der Quarz, der bei mir 12MHz groß ist.

Erstmal solltest du klären, ob dein µC auch wirklich mit diesem 12MHz 
Takt läuft. Dazu kannst du z.B. ein kleines Blinkprogramm für eine LED 
schreiben, so dass du per Augenmaß prüfen kannst, ob der Prozessor mit 
dem von dir angenommenen Takt läuft.

Ein Servo, der zitternd am Anschlag steht, sprich für viel zu lange 
Steuerpulse.

von Thomas M. (zumax) Benutzerseite


Lesenswert?

Probiers mal so in der Art:
1
int main(void)
2
{
3
  DDRD = (1<<PD5);  // Servo 1 an PD5, Servo 2 an PD4
4
5
  TCCR1B |= (1<<WGM13);
6
  TCCR1B |= (1<<WGM12);
7
  TCCR1A |= (1<<WGM11);
8
  TCCR1A &= ~(1<<WGM10);
9
10
  TCCR1A |= (1<<COM1A1);
11
  TCCR1A &= ~(1<<COM1A0);
12
13
  TCCR1B &= ~(1<<CS10 );
14
  TCCR1B |= (1<<CS11 );
15
  TCCR1B &= ~(1<<CS12 );
16
17
  ICR1 = 30000; 
18
  
19
  //OCR1A = 2500;
20
  while(1)
21
  {
22
    OCR1A =  1500 - 100;
23
    _delay_ms(1000);
24
    OCR1A = 1500 + 100;
25
    _delay_ms(1000);
26
  }
27
}

Und am besten nochmal im Forum
das hier 
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/Die_Timer_und_Z%C3%A4hler_des_AVR

und das hier http://www.mikrocontroller.net/articles/AVR-Tutorial:_PWM 
durchlesen.

Grüße Thomas

von Rolf M. (rmagnus)


Lesenswert?

Marcel Hunfeld schrieb:
> Ein mögliches Problem ist der Quarz, der bei mir 12MHz groß ist. Dies
> sorgt dafür, dass der Wert von ICR1 größer als 65535 wird.

Der Wert kann nicht größer als 65535 werden. Einen größeren Wert 
kannst du versuchen reinzuschreiben, aber da werden dann einfach ein 
paar Bits oben abgeschnitten. Das ist ungefähr so, als ob du in einen 
10-Liter-Eimer 35 Liter Wasser reinschüttest.

> Auf der Internetseite steht, dass man in einem solchen Fall einen
> sogenannten Prescaler verwenden soll. Ich habe jedoch leider nicht
> verstanden, was das genau sein soll.

Der Prescaler teilt den Takt vom Timer runter, so daß dieser langsam 
genug ist, damit der Eimer nicht überläuft.

> Doch ich habe so das Gefühl, dass dies nicht die Ursache des Problems
> ist.

Und wie kommst du darauf?

von Marcel H. (marcelhuu)


Lesenswert?

Vielen Dank schon mal für eure starke Mithilfe!

Ich habe gerade das Programm von dir ausprobiert, Thomas. Das 
funktioniert schon deutlich besser. Das Problem ist hier nur, dass das 
Intervall der beiden Punkte zwischen denen sich der Servo hin- und 
herbewegt, man sieht somit, dass sich das Servohorn nur um wenige Grad 
bewegt. Selbst wenn ich das Intervall im Programm auf
1
     OCR1A =  2000;
2
    _delay_ms(2000);
3
    OCR1A = 1000;
4
    _delay_ms(2000);
 vergrößere, ändert sich das Verhaltensmuster des Servos nicht.

Aufällig ist jedoch bei dem Programm noch, dass die Bewegung zur einen 
Seite hin sehr ruckhaft und schnell, während die Bewegung zur anderen 
Seite sehr langsam ( obwohl es sich wie gesagt nur um wenige Grad 
handelt) ausgeführt wird. Weiß jemand, woran das liegen kann?

Mike A. schrieb:
> Dazu kannst du z.B. ein kleines Blinkprogramm für eine LED
> schreiben, so dass du per Augenmaß prüfen kannst, ob der Prozessor mit
> dem von dir angenommenen Takt läuft.

Das werde ich morgen dann mal machen und weiter berichten.

Rolf Magnus schrieb:
>> Doch ich habe so das Gefühl, dass dies nicht die Ursache des Problems
>> ist.
>
> Und wie kommst du darauf?

Weil ich bereits andere Beispielprogramme ausprobiert habe, die 
teilweise für einen 12 oder 16MHz Quarz bestimmt sind und der Servo dort 
das selbe gemacht hat. Deswegen hatte ich das als Ursache schon 
ausgeschlossen, aber ich kann mich auch irren.

Thomas Messmer schrieb:
> am besten nochmal im Forum
> das hier
> 
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/Die_Timer_und_Z%C3%A4hler_des_AVR
>
> und das hier http://www.mikrocontroller.net/articles/AVR-Tutorial:_PWM
> durchlesen.

Das mach ich morgen dann auch noch mal.

Viele Grüße, Marcel

von Rudolph (Gast)


Lesenswert?

Na, der Timer hat doch nur 8 Register. :-)

Die PWM für den Servo braucht so 20ms Perioden-Dauer.
Unterer Anschlag sind so 1ms, Mitte so 1,5ms und oben so 2ms.

12 MHz Takt = 83,33ns pro Zyklus

20ms / 83,33ns = 240000 -> zuviel

Prescaler = 8 -> 1,5MHz -> 666,67ns -> 30000 -> okay
1ms / 666,67ns = 1500
2ms / 666,67ns = 3000
1
#include <avr/io.h>
2
#include <util/delay.h>
3
4
5
int main(void)
6
{
7
  DDRD = (1<<PD5);  // Servo 1 an PD5, Servo 2 an PD4
8
  TCCR1A = (1<<WGM11) | (1<<COM1A1);
9
  ICR1 = 30000; // Top-Wert 
10
  OCR1A = 1500;
11
  TCCR1B = (1<<WGM13) | (1<<WGM12) | (1<<CS11);
12
13
  while(1)
14
  {
15
    OCR1A =  1500;
16
    _delay_ms(1000);
17
    OCR1A = 3000;
18
    _delay_ms(1000);
19
  }
20
}

Sieht doch fast genauso aus.
Geändert habe ich den Output-Mode für den Pin, den Prescaler, die Werte 
für ICR1 und OCR1A, die Art wie die Register geschriebn werden und die 
Reihenfolge in der die Register beschrieben werden.

Der Zähler läuft nämlich los sobald der Prescaler eingestellt ist, 
sobald der Zähler Takt hat, das sollte man also immer als letztes 
machen.

Und wenn man das jetzt vergleicht, das ist im Effekt exakt das gleiche 
was Thomas Messmer schon geschrieben hat, nur weniger kompliziert.

von Marcel H. (marcelhuu)


Lesenswert?

Okay, jetzt weiß ich echt nicht mehr weiter :(
@Rudolph: Mit deinem Programm macht der Servo das selbe, wie bei dem 
Programm von Thomas Messmer. Er bewegt sich nur jeweils in einem sehr 
kleinen Winkel.

Das merkwürdige dabei ist jedoch, dass wenn ich die Endlosschleife leer 
lasse und dem OCR1A nur in dem Hauptprogrammteil einen Wert zuweise, 
dann fährt der Servo auch zu diesem Punkt hin. Damit habe ich 
herausgefunden, dass die linke Grenze des Servos bei OCR1A=1000 und die 
rechte Grenze bei OCR1A=2000 erreicht ist. Wenn ich nun jedoch mit 
diesem Werte in der Endlosschleife weiter arbeiten will, und der Servo 
zwischen diesen beiden Punkten "wandern" soll, dann fährt er zur Mitte 
und macht genau dasselbe wie bei deinem Programm und bewegt sich wieder 
nur ein paar Grad weiter und dann wieder zurück.

von Rudolph (Gast)


Lesenswert?

Sind die 12MHz überhaupt definiert damit das delay() funktioniert?
Das sollte aber eine Compiler-Warnung geben.
Genauso wie eine falsche Optimierungs-Stufe.

Bei 12MHz sind 1000 = 0,66ms und 2000 = 1,33ms, das passt so garnicht zu 
einem Servo.

Hmm, bei 8MHz mit dem Prescaler von 8 sind 1000 genau 1ms und 2000 genau 
2ms.
Dein Quarz scheint überhaupt nicht aktiviert zu sein.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.