www.mikrocontroller.net

Forum: Compiler & IDEs Atmega32 kleines Problem mit Modellservoansteuerung


Autor: Dennis R. (dennis_r)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Moin moin,
ich weiß, dass es ein oft Diskutiertes Thema ist, aber ich hab trotzdem 
ein kleines Problem mit meinem Code.
An sich denke ich, dass ich es verstanden habe wie man den Servo 
ansteuert:
- 20ms Signal generieren
- die ersten 2ms sind das Servosignal und der Rest ist für nix quasi.


Das hab ich mal probiert mit nem Atmega32 mit dem internen 8MHz Quarz.
Währe cool wenn jemand vlt. Mal drüber gucken könnte und mir nen kleinen 
Denkanstoß geben könnte?

Danke im Voraus.
Gruß Dennis


hier mein C Code:
#include <avr/io.h>
#include <avr/interrupt.h>

#define servo_anzahl 1
uint16_t isvalue=1000;
uint16_t value[1]={1500};
uint16_t a=0,b=0;


ISR (TIMER2_COMP_vect) // µs Takt
{
uint8_t i;

isvalue++;

if (isvalue>=20000) isvalue=0; //gucken ob die 20ms um sind

if(isvalue==0){//wenn sie um sind die Servoports auf High schalten
  for(i=0;i<servo_anzahl;i++) PORTA|=(1<<i);
}

for(i=0;i<servo_anzahl;i++){//wenn gewünschte zeit rum ist servosignal auf low schalten
  if(value[i]==isvalue) PORTA &=~(1<<i);
}// end for(i=0

if(isvalue>=2000) for(i=0;i<servo_anzahl;i++) PORTA &=~(1<<i); // wenn man über die 2ms kommt, alle servos auf null schalten

} // end isr timer2comp



  

int main(void){
sei();
DDRA=0xFF;
DDRB=0xFF;
PORTB=0x00;
// timer2 im µsekundentakt output compare betrieb
TCCR2 =(1<<WGM21) |(1<<CS20);        //Prescale von 1 und CTC mode
OCR2=8;                  //Vergleich bei 4 --> (8MHz/1)/8= Interruptabstand von einer µs
TIMSK|=(1<<OCIE2);
while(1);
}


Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dennis R. schrieb:

> - 20ms Signal generieren
> - die ersten 2ms sind das Servosignal und der Rest ist für nix quasi.

die 2ms sind wichtig.
die 20ms sind nicht so wichtig.


> Das hab ich mal probiert mit nem Atmega32 mit dem internen 8MHz Quarz.

Es gibt keinen 8Mhz internen Quarz.
Es gibt einen RC-Schwingkreis, der in etwa mit 8Mhz schwingt.

> Währe cool wenn jemand vlt. Mal drüber gucken könnte und mir nen kleinen
> Denkanstoß geben könnte?

In welche Richtung sollen wir suchen?

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Du denkst also, du kannst alle 8 Taktzyklen einen Interrupt auslösen 
lassen, und dann in der Interrupt Routine massig Taktzyklen für 
Berechnungen zur Verfügung zu haben.

Das wird nicht gehen.
Wenn du für eine Tätigkeit 5 Minuten brauchst, dann hat es keinen Sinn 
wenn dein Chef alle 10 Sekunden da steht und dir noch einen Auftrag gibt 
und noch einen und noch einen ad infinitum.
Du wirst mit der Arbeit nicht hinterherkommen.

Autor: Hc Zimmerer (mizch)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wie kommst Du auf die Idee, Deine Interruptroutine könne in weniger als 
1 us abgearbeitet sein?  Das müsste sie nämlich, wenn Obiges 
funktionieren soll.  Dein Programm kommt aus dem Interrupt nicht raus 
und hängt trotzdem weit hinterher.

Edit: 1 Bemerkung gestrichen, weil sie nicht zutraf.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
8 Taktzyklen sind zu wenig Zeit, die du der ISR lässt.
80 Taktzyklen könnte sich so lala ausgehen. Müsste man im Simulator 
durchspielen.
Also: schraub deine Erwartungen runter. Kein Servo muss man auf 1000 
unterschiedliche Positionen fahren können. Das gibt die Mechanik sowieso 
nicht her. 100 reichen auch.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
So kannst du die ISR noch ein wenig kürzer gestalten
ISR (TIMER2_COMP_vect) // µs Takt
{
  uint8_t i, servoMask;

  isvalue++;
  if (isvalue >= 20000)
    isvalue=0; //gucken ob die 20ms um sind

  servoMask = 0x01;
  for( i = 0; i < servo_anzahl; i++) {
    if( isvalue < value[i] )
      PORTA |= servoMask;
    else
      PORTA &= ~servoMask;

    servoMask <<= 1;
  }
}

Das ändert aber nichts daran, dass du die ISR Aufruffrequenz absenken 
musst.

Autor: Mark .. (mork)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wenn Du mehrere Servos am gleichen Port hast, kannst Du das ganze auch 
anders lösen. Die Servos werden dabei nicht gleichzeitig, sondern 
nacheinander angesteuert.

Es gibt dann eine Variable 'servonummer'. Zu beginn wird die Nummer auf 
0 gesetzt. Der Timer wird mit einem Prescaler von 64 im CTC Modus 
betrieben. Der Servoport.0 wird auf 1 gesetzt und OCR des Timers mit dem 
Wert des ersten Servowertes geladen:
// Pseudocode

void InitServo()
{
    servonummer = 0;
    SERVOPORT = 1;
    Timer = PRESCALE 64;
    OCR = value[0];
}

In der Compare Match ISR wird dann der nächste Pin des Ports auf 1 
gesetzt und der aktuelle gelöscht. Die Servonummer wird hochgezählt und 
OCR mit dem Wert des nächsten Servos geladen. Sobald alle Servos durch 
sind, wird wieder von vorne angefangen:
// Pseudocode

ISR(TIMER_COMP_VECT)
{
    if(++servonummer == ANZAHL_DER_SERVOS) // alle Servos durch
    {
        servonummer = 0; // -> vorne anfangen
        SERVOPORT = 1;
    }
    else
        SERVOPORT <<= 1; // ansonsten nächstes Servo ansteuern
    
    OCR = value[servonummer]; // Timer mit nächstem Servowert laden
}

Bei einem OCR-Wert von 128 ergibt das etwa 1ms, bei 255 etwa 2ms 
Impulslänge.

Die Zeit zwischen 2 Impulsen, die ein Servo kriegt, ist dabei zwar nicht 
konstant 20ms, was in der Praxis jedoch nichts ausmacht. Ein großer 
Vorteil ist die sehr geringe Prozessorauslastung, da die ISR höchstens 
1x pro Millisekunde ausgeführt wird.

Ich hatte auf diese Weise 8 Servos an einem Controller hängen, hat immer 
wunderbar funktioniert. Hab nur leider den Originalcode nicht mehr.

MfG Mark

Autor: Dennis R. (dennis_r)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ui danke für die vielen und vorallem schnellen antworten.
Hatte es ebend mal versucht die ISR frequenz herab zu senken um den 
faktor 10, dann ging es. aber ich konnte den servo nicht bewegen.
D.h. wenn ich value[0] verändert habe dann hat sich am servo aber nix 
verändert. nur wenn ich den vorgeladenen wert verändert habe dann hat er 
sich bewegt.
also nur ein kleienr erfolg.

Zu dem code von mark ..
hab ich so umgesetzt
der code fährt zwar den server an die richtige position, aber hält ihn 
da dann nicht fest!
#include <avr/io.h>
#include <avr/interrupt.h>

#define SERVO_ANZAHL 8
#define SERVOPORT PORTA

uint8_t value[8]={250};
uint8_t servonummer;


ISR (TIMER2_COMP_vect) 
{
if(++servonummer == SERVO_ANZAHL) // alle Servos durch
    {
        servonummer = 0; // -> vorne anfangen
        SERVOPORT = 1;
    }
    else
        SERVOPORT <<= 1; // ansonsten nächstes Servo ansteuern
    
    OCR2 = value[servonummer]; // Timer mit nächstem Servowert laden


} // end isr timer2comp


void InitServo()
{
    servonummer = 0;
    SERVOPORT = 1;

    // timer2 im µsekundentakt output compare betrieb
TCCR2 =(1<<WGM21) |(1<<CS22);        //Prescale von 8 und CTC mode
OCR2=value[0];                  //Vergleich bei 4 --> (8MHz/8)/1= Interruptabstand von einer µs
TIMSK|=(1<<OCIE2);
}


  

int main(void){
sei();
DDRA=0xFF;
DDRB=0xFF;
PORTB=0x00;

InitServo();


while(1){

}
}


Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dennis R. schrieb:

> D.h. wenn ich value[0] verändert habe dann hat sich am servo aber nix
> verändert. nur wenn ich den vorgeladenen wert verändert habe dann hat er
> sich bewegt.

könnte ein volatile Problem sein.
volatile uint16_t value[1]={1500};


> der code fährt zwar den server an die richtige position, aber hält ihn
> da dann nicht fest!

Was heißt 'hält ihn nicht fest'?

> uint8_t value[8]={250};

gib den anderen Servos wenigstens eine kleine Zeit aber nicht 0, damit 
das Servo nicht Puls an Puls bekommt. Auch wenn die 20ms nicht kritisch 
sind, würde ich meine Hand da nicht dafür ins Feuer legen, was wohl 
passiert, wenn die 2ms Stellimpulse da dicht an dicht kommen. Ein paar 
Millisekunden dürfen schon Pause dazwischen sein. Und auch hier wieder: 
volatile!
volatile uint8_t value[8] = { 250, 200, 200, 200, 200, 200, 200, 200 };

Die Pause die jedes Servo zwischen den Pulsen zugestanden bekommt, ist 
ja die Summe der Pulslängen aller anderen Servos.


Und bitte:
Bemüh dich wenigstens ein bischen, ein optisch einigermassen akzeptables 
Programm zu produzieren. Auch jetzt in der Entwicklungsphase!
Alles einfach nur an den linken Rand zu quetschen ist ein ziemlich 
sicherer Weg irgendwann den Überblick zu verlieren, welche Schleife in 
welches if geschachtelt wurde und welcher Block von welcher Bedingung 
abhängt.

Das bischen korrekte Einrückung kostet keine Zeit während man tippt, 
kann aber oft den Unterschied zwischen 'Ich sehs' oder 'Ich sehs nicht' 
ausmachen.

Autor: Dennis R. (dennis_r)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
na mit nicht festhalten mein ich das man, den servo nach dem stellen mit 
der hand bewegen kann.
Normal ist ja eignetlich(so kenne ich es aus dem modellbau bereich) das 
er an der Position feststehen tut?

so mit dem code haste ja recht
hab ihn nochmal etwas verschönert ;)
#include <avr/io.h>
#include <avr/interrupt.h>

#define SERVO_ANZAHL 8
#define SERVOPORT PORTA

volatile uint8_t value[8]={128};
volatile uint8_t servonummer;


ISR (TIMER2_COMP_vect) 
{
  if(++servonummer == SERVO_ANZAHL)   // alle Servos durch
      {
          servonummer = 0;         // -> vorne anfangen
          SERVOPORT = 1;
       }
    else SERVOPORT <<= 1;         // ansonsten nächstes Servo ansteuern
    
    OCR2 = value[servonummer];       // Timer mit nächstem Servowert laden
} // end isr timer2comp


void InitServo()
{
  servonummer = 0;
  SERVOPORT = 1;
  TCCR2 =(1<<WGM21) |(1<<CS22);  //Prescale von 64 und CTC mode
  OCR2=value[0];
  TIMSK|=(1<<OCIE2);
}

int main(void){

  sei();
  DDRA=0xFF;
  DDRB=0xFF;
  PORTB=0x00;

  InitServo();

  while(1);
}// end main

Autor: Dennis R. (dennis_r)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
aso zu der pause zwischen den pulsen für den servo.
wenn ich jetzt zum Beispiel 4 Servos ansteuern will, hab ich ja 
mindestens 4 ms pause, wenn ich sage das Minimum für ein servo ist 1ms 
und das max. ist 2ms?
hmm aber leider funktioniert der code leider doch nett.
ich komme irgedwie nicht mehr aus der ISR raus. das hab ich mit dem 
simulator auch nachstellen können.
Aber warum?

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dennis R. schrieb:

> ich komme irgedwie nicht mehr aus der ISR raus. das hab ich mit dem
> simulator auch nachstellen können.
> Aber warum?

Das ist an dieser Stelle schon korrekt.
Da in deiner Hauptschleife nichts mehr passiert, macht der Simulator 
dieses 'da passiert nichts weiter' im schnellen Vorlauf, bis eben der 
nächste Compare Match eintritt.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dennis R. schrieb:
> aso zu der pause zwischen den pulsen für den servo.
> wenn ich jetzt zum Beispiel 4 Servos ansteuern will, hab ich ja
> mindestens 4 ms pause, wenn ich sage das Minimum für ein servo ist 1ms
> und das max. ist 2ms?

Ja.
Bedenke: Normalerweise ist diese Pause ca. 19ms lang

PS: Bei dir ist sie jetzt 0 (ok, nahezu 0) ms lang.

Autor: Dennis R. (dennis_r)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Aber is schon komisch hab jetzt den Code mal um das in einen Sekenduen 
takt invertieren des PortB ergänzed.
Damit müsste die LED an meinen Controller, ja im 1 Sec. Takt blinken.
Was sie ja mit anderen Programmen tut, aber in diesem fall nicht!

Und nochmal zur pause, ok vlt. versteh ich den code falsch:
Wenn ich nur einen Servo hätte, dann leuchtet es mir ein das ich eine 
Pause von fast 0 habe. aber wenn ich jetzt hier in diesem Bsp. 8 servos 
habe dann müsste ich doch eine Pause von ca. 8 bis 16ms haben oder?
Ich verschiebe doch mein bit an PortA solange bios ich wieder beim 
ersten anfange. Somit ist ja der PortA 0 z.B. solange bis er weider dran 
ist?
#include <avr/io.h>
#include <avr/interrupt.h>
#ifndef F_CPU
#define F_CPU 8000000ul
#endif
#include <util/delay.h>
#define SERVO_ANZAHL 8
#define SERVOPORT PORTA

volatile uint8_t value[8]={128};
volatile uint8_t servonummer;


ISR (TIMER2_COMP_vect) 
{
  if(++servonummer == SERVO_ANZAHL)   // alle Servos durch
      {
          servonummer = 0;         // -> vorne anfangen
          SERVOPORT = 1;
       }
    else SERVOPORT <<= 1;         // ansonsten nächstes Servo ansteuern
    
    OCR2 = value[servonummer];       // Timer mit nächstem Servowert laden
} // end isr timer2comp


void InitServo()
{
    servonummer = 0;
    SERVOPORT = 1;
  TCCR2 =(1<<WGM21) |(1<<CS22);  //Prescale von 64 und CTC mode
  OCR2=value[0];
  TIMSK|=(1<<OCIE2);
}

int main(void){

  sei();
  DDRA=0xFF;
  DDRB=0xFF;
  PORTB=0x00;

  InitServo();

  while(1){
  _delay_ms(1000);
  PORTB=~PORTB;
    

  }
}// end main

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dennis R. schrieb:
> Aber is schon komisch hab jetzt den Code mal um das in einen Sekenduen
> takt invertieren des PortB ergänzed.
> Damit müsste die LED an meinen Controller, ja im 1 Sec. Takt blinken.
> Was sie ja mit anderen Programmen tut, aber in diesem fall nicht!
>
> Und nochmal zur pause, ok vlt. versteh ich den code falsch:
> Wenn ich nur einen Servo hätte, dann leuchtet es mir ein das ich eine
> Pause von fast 0 habe. aber wenn ich jetzt hier in diesem Bsp. 8 servos
> habe dann müsste ich doch eine Pause von ca. 8 bis 16ms haben oder?
> Ich verschiebe doch mein bit an PortA solange bios ich wieder beim
> ersten anfange.

Ja.
Aber du lädst einen OCR Wert von 0, wodurch beim Verlassen der ISR 
gleich wieder der nächste Compare Match vorliegt

Das hier
volatile uint8_t value[8]={128};

ist gleichbedeutend mit
volatile uint8_t value[8]={128, 0, 0, 0, 0, 0, 0, 0};

d.h. deine restlichen Servos stehen alle auf 0!


Ich hab jetzt auch noch nicht das Timing nachgerechnet, was ein 
Prescaler von 64 mit 8Mhz und einem OCR Wert von 128 für eine Zeitdauer 
ergibt.

Autor: Dennis R. (dennis_r)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
aso hmm ok dachte bis ebend das
volatile uint8_t value[8]={128};

das gleiche ist wie
volatile uint8_t value[8]={128,128,128,128,128,128,128,128};

deswegen hatte das kbuchegg schon vorhin hervorgehoben.

also mit dem code geht es jetzt.

die ISR für den timer0 hab ich nur reingemacht zum testen. Damit der 
servo hin und her fährt.

und das mit der Zeit haut schon hin.

1/((8MHz / 64)/128)=1,024mS
1/((8MHz / 64)/250)=2mS

ok nach oben ist da nicht mehr viel spielraum aber nach unten, falls der 
servo mehr kann!

Danke nochmals für die nett hilfe hier, DANKE!

#include <avr/io.h>
#include <avr/interrupt.h>
#ifndef F_CPU
#define F_CPU 8000000ul
#endif
#include <util/delay.h>
#define SERVO_ANZAHL 8
#define SERVOPORT PORTA

volatile uint8_t value[SERVO_ANZAHL]={128,128,128,128,128,128,128,128};
volatile uint8_t servonummer;


ISR (TIMER2_COMP_vect) 
{
  if(++servonummer == SERVO_ANZAHL)   // alle Servos durch
      {
          servonummer = 0;         // -> vorne anfangen
          SERVOPORT = 1;
       }
    else SERVOPORT <<= 1;         // ansonsten nächstes Servo ansteuern
    
    OCR2 = value[servonummer];       // Timer mit nächstem Servowert laden
} // end isr timer2comp

ISR (TIMER0_COMP_vect) {
  
  static uint16_t i=0;
  static uint8_t asd=0;
  i++;
  if(i>=50){
    if(!asd){
      i=0;
      value[0]+=1;
      if(value[0]>=250) asd=1;
    }else{
      i=0;
      value[0]-=1;
      if(value[0]<=128) asd=0;
    }
  }
}

void InitServo()
{
    servonummer = 0;
    SERVOPORT = 1;
  TCCR2 =(1<<WGM21) |(1<<CS22);  //Prescale von 64 und CTC mode
  OCR2=value[0];
  TIMSK|=(1<<OCIE2);
}

int main(void){

  sei();
  DDRA=0xFF;
  DDRB=0xFF;
  PORTB=0x00;

  TCCR0 =(1<<WGM01) |(1<<CS00)|(1<<CS01);
  OCR0=125;
  TIMSK|=(1<<OCIE0);


  InitServo();

  while(1);
}// end main

Autor: Dennis R. (dennis_r)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Aso,
am besten wähere es wenn man natürlich eine pause nach dem servo anfügen 
würde oder?
damit man eine bestimmte minimum zeit hat bis der erste servo wieder 
dran ist oder?

greetz dennis

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dennis R. schrieb:
> Aso,
> am besten wähere es wenn man natürlich eine pause nach dem servo anfügen
> würde oder?

Das musst du ausprobieren.
Stell deine Servos auf Minimalzeit ein (aber nicht 0)
und sieh nach ob sie mit dieser Pause noch zurecht kommen.

Ich denke mal sie werden es, aber in dem Fall macht Versuch kluch.

> damit man eine bestimmte minimum zeit hat bis der erste servo wieder
> dran ist oder?

Die 0 war deswegen ein Problem, weil dein µC dann nicht mehr hinterher 
gekommen ist. Der kann seine ISR nun mal nicht in 0-Zeit abarbeiten.

Autor: Dennis R. (dennis_r)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ja alles kalro jetzt hab ich es gecheckt!


tausend Danke nochmal an alle die geholfen haben.!

gruss

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.