Forum: Mikrocontroller und Digitale Elektronik Servo ansteuern mit ATMega16


von PowerProgrammer (Gast)


Lesenswert?

Hallo Leute!
Ich würde gerne einen/mehrere Servo(s) ansteuern. Also habe ich mir 
einen kleinen Code zusammengebastelt:
1
#define F_CPU 16000000L
2
3
#include <avr/io.h>
4
#include <util/delay.h>
5
6
unsigned char i;
7
8
int main()
9
{
10
  DDRA = 0xFF;
11
  PORTA = 0x00;
12
  while(1)
13
  {
14
    for(i=0; i<150; i++)
15
    {
16
      PORTA = 0xFF;
17
      _delay_us(2000);
18
      PORTA = 0x00;
19
      _delay_ms(18);
20
    }
21
    for(i=0; i<150; i++)
22
    {
23
      PORTA = 0xFF;
24
      _delay_us(1000);
25
      PORTA = 0x00;
26
      _delay_ms(19);
27
    }
28
  }
29
30
  while(1);
31
  return 0;
32
}
Dazu habe ich den Servo dann angeschlossen, also das schwarze Kabel an 
Minus, das Rote an Plus und das letzte an irgendeinen Pin von Port A.

Eigentlich sollte das Servovieh immer schön hin und her drehen, oder?

Und wie ihr euch schon denken könnt: Es funktioniert nicht. Ein Servo 
dreht sturr nach links und ein anderes Modell tut garnichts. Ich habe 
absolut keine Ahnung, wo der Fehler liegt? Ist was in meinem Code 
falsch?

Also mit der Frequenz sollte eigentlich alles stimmen...
Habt ihr eine Antwort?
Vielen Dank schonmal, PowerProgrammer

von Thorsten F. (thorsten)


Lesenswert?

Aus der avr-libc Doku:

_delay_ms:
The maximal possible delay is 262.14 ms / F_CPU in MHz.

_delay_us:
The maximal possible delay is 768 us / F_CPU in MHz.

da musste mal deinen code anpassen

von PowerProgrammer (Gast)


Lesenswert?

Oh, vielen Dank!
Vorher hatte ich das aber auch mit _delay_ms(1); und _delay_ms(2); 
gemacht, es hatte trotzdem nicht geklappt. Naja, das wäre hier jetzt ein 
zusätzlicher Bug, danke für den Hinweis!

Weiß denn jemand, was noch falsch sein könnte?

von Marcus (Gast)


Lesenswert?

Hi PowerProgrammer,

die Servos erwarten folgendes "Protokoll":
 [Stellimpuls][Warteimpuls]

 Von Impuls zu Impuls 22,5ms
 Stellimpuls + Warteimpuls = 22,5ms
 Für die Position 0 ist der Stellimpuls 0,7ms
 für die Position voll Ausschlag 2ms


1
    while(1)
2
    {
3
      PORTA = 0xFF;
4
      _delay_us(2000);
5
      PORTA = 0x00;
6
      _delay_ms(20);
7
      PORTA = 0xFF;
8
      _delay_us(700);
9
      PORTA = 0x00;
10
      _delay_ms(21);
11
12
    }

Der Code hat zumindest bei mir funktioniert.
Habs nacher mit einem Timer gelöst. Das ist nicht ganz so CPU lastig

Gruß Marcus

von PowerProgrammer (Gast)


Lesenswert?

Hmm, ich hatte gelesen, dass die Summe aus Stellimpuls und Wartezeit 
nicht exakt sein muss und zwischen 10 und 20 ms liegen soll.
Jedenfalls kann dein Code nach der Anmerkung thorstens nicht 
funktinieren, wenn er das bei dir dennoch tat, dann ist das höchst 
seltsam. Nach deinem Code wechselt das Teil ja alle 22,5 ms die 
Stellung, das schafft kein Servo, also, ich werde den Code bei 
gelegenheit mal ausprobieren, wenn er klappt, fresse ich nen Besen. 
Danke.

von Marcus (Gast)


Lesenswert?

Hallo PowerProgrammer,

ups, da ist mir wirklich ein kleiner Fehler unterlaufen. Hab aus meinem 
Code das ein oder andere rausgeschnippelt und leider auch eine falsche 
Zeile!
1
    while(1)
2
    {
3
      int i = 0;
4
      while (i < 200) {
5
         PORTA = 0xFF;
6
         _delay_us(2000);
7
         PORTA = 0x00;
8
         _delay_ms(20);
9
         i++;
10
      }    
11
      while (i < 200) {
12
         PORTA = 0xFF;
13
         _delay_us(700);
14
         PORTA = 0x00;
15
         _delay_ms(21);
16
      }
17
18
    }

Das Servo sollte so alle 4 Sekunden einal von ganz rechts nach ganz 
links wandern. Die Standartservos brauchen da so ihre Zeit.

Gruß Marcus

von Marcus (Gast)


Lesenswert?

Hallo PowerProgrammer,

ups, da ist mir wirklich ein kleiner Fehler unterlaufen. Hab aus meinem
Code das ein oder andere rausgeschnippelt und leider auch eine falsche
Zeile!

    while(1)
    {
      int i = 0;
      while (i < 200) {
         PORTA = 0xFF;
         _delay_us(2000);
         PORTA = 0x00;
         _delay_ms(20);
         i++;
      }
      i = 0;
      while (i < 200) {
         PORTA = 0xFF;
         _delay_us(700);
         PORTA = 0x00;
         _delay_ms(21);
         i++
      }
      i = 0;
    }

Das Servo sollte so alle 4 Sekunden einal von ganz rechts nach ganz
links wandern. Die Standartservos brauchen da so ihre Zeit.

Gruß Marcus

von Johannes M. (johnny-m)


Lesenswert?

Trotzdem werden weder _delay_us(2000) noch _delay_us(700) das tun, was 
Du von ihnen erwartest. Wenn das funktioniert, dann zufällig! Thorstens 
Beitrag entspricht Tatsachen...

von H.joachim S. (crazy_horse)


Lesenswert?

mein Gott, Timer sind doch wirklich keine Mangelware. Noch dazu, da man 
sie selbst bei nur halbwegs geschickter Programmierung mehrfach nutzen 
kann.

von PowerProgrammer (Gast)


Lesenswert?

Danke, es geht mir halt drum, erstmal einen verfluchten Servo irgendwie 
zu bewegen, egal wie dreckig der Code ist;) Ich bin halt neu in der 
Branche und habe so Sachen wie Timer und Interrupts und alles beim AVR 
noch nie genutzt, ich kenne bis jetzt halt nur die Intel-x86-Prozessoren 
genauer...
Ich verzweifel an diesen Teilen sowieso, ich weiß nicht, wieso die Teile 
mich nicht mögen. Wüsste nur zu gerne, wie ich am besten mal die Signale 
meines AVRs unter die Lupe nehmen könnte...

PowerProgrammer

von Rahul D. (rahul)


Lesenswert?

>wie ich am besten mal die Signale meines AVRs unter die Lupe nehmen >könnte...

Für sowas benutzt man in der Regel ein Oszilloskop.

Würdestr du die Suche des Forums bemühen, würdest du auch diverse 
Lösungen mit Timern finden.

Fluchen bringt übrigens bei Servos (und anderen Dingen) überhaupt 
nichts, sowas hilft höchstens bei anderen Menschen...

von Peter B. (pbuenger)


Lesenswert?

Hai PowerP,

guck' Dir doch mal meinen Code an. Eine brauchbaren Servopuls kriegst Du 
mit einer Handvoll Codezeilen hin.

Beitrag "Sequenzer für Einziehfahrwerke"

Gruß,
Peter

von PowerProgrammer (Gast)


Lesenswert?

Ein wirklich gut gemeinter Ratschlag, allerdings doch etwas 
unverständlich für einen Anfänger, der schon bei 2 Delay-Anweisungen 
verzweifelt;) Ich dachte mir halt wirklich, Timer erst einzubauen, wenn 
der Servo überhaupt erstmal irgendiwie funktioniert!
Trotzdem nochmal vielen Dank an all die Leute, die mir geholfen haben!
PowerProgrammer

von Ben (Gast)


Lesenswert?

ok Jungs, jetzt sag ich euch mal wie das geht! mit folgender methode 
kannst du ganz elegeant 2 servos ansteuern!
du benutzt fast PWM und setzt ICR als TOP! das machst du so WGM1n=1110, 
siehe dazu datenblatt S.112! den timer musst du noch ein wenig 
prescalen, damit du genau nach 22.5ms den wert in ICR erreichst! nun 
stellst du noch OCR1n so ein dass bei einem compare match das pwm signal 
gelöscht wird! jetzt gibt es ja aber ein minimum und  maximum deines pwm 
signals, d.h das minimum ist ja glaub ich bei 1ms und das maximum bei 
2ms oder so! dazu berechnest du einfach den zählerstand des timers der 
bei diesen werten! um OCR1n zu setzen nimmst du dann 
OCR1n=Minimum+(Maximum-Minimum)/Auflösung*Wert
alles klar?

von Ben (Gast)


Lesenswert?

hier noch der code (für einen ATmega128),hoffe der stimmt so noch, aber 
sonst werden es nur kleinigkeiten sein:

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

#define F_CPU 14745600 // clock
#define PRESCALE_FACTOR 8 // prescale factor 8
#define PRESCALE_FACT (1<<CS11)
#define PULSE_PERIOD 50 //50Hz->20ms
#define MAXPULS_FREQ 500 // 500Hz -> 2ms
#define MINPULS_PRESC 2 //MinPuls ist 2 mal kleiner als MaxPuls
#define MAXTIMER_VALUE F_CPU/(MAXPULS_FREQ*PRESCALE_FACTOR)
#define MINTIMER_VALUE 
F_CPU/(MAXPULS_FREQ*MINPULS_PRESC*PRESCALE_FACTOR)
#define TIMER_VALUE  F_CPU/(PRESCALE_FACTOR*PULSE_PERIOD)

void init(){
DDRB|=(1<<PB5); // set PB5 (OC1A) to output
/***fast pwm mode***/
TCCR1A|=(1<<COM1A1); // clear OCnx on compare match
TCCR1A|=(1<<WGM11);
TCCR1B|=((1<<WGM13)|(1<<WGM12));
ICR1H|=TIMER_VALUE;
TCCR1B|= PRESCALE_FACT;// timer prescaling
}

//speed Werte 0-255 erlaubt
void setMotorSpeed(unsigned char speed){
OCR1A=MINTIMER_VALUE+(MAXTIMER_VALUE-MINTIMER_VALUE)/255*speed;
}

int main(void){
init();
setMotorSpeed(128);
while(1);
return 0;
}

von Jan W. (Gast)


Lesenswert?

Hi,

ich werd den alten Thread ma vorkramen, da ich ein ähnliches bzw. das 
gleiche problem hab. Ich hab hier zwei Servos, der eine ohne 
Potirückmeldung und der andere mit Rückmeldung.

Dazu hab ich den folgenden Quellcode, denn ich aus dem Tutorial von hier 
habe. Allerdings laufen beide servos immer nur nach links, auch wenn ich 
die Delay-Zeit ändere!

Kann mir jemand helfen!!
Danke!
1
#include <avr/io.h>
2
#define F_CPU 1000000L
3
#include <util/delay.h>
4
5
6
int main (void)
7
{
8
9
  DDRB |= (1<<PB2);
10
  
11
12
    while(1)
13
    {
14
  PORTB |= (1<<PB2);
15
    _delay_ms(2);
16
    PORTB &= ~(1<<PB2);
17
    _delay_ms(18);
18
    }
19
    
20
21
22
return 0;
23
}

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.