www.mikrocontroller.net

Forum: Compiler & IDEs Schrittmotor - Programmierung einer linearen Rampe


Autor: Chritian (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Servus miteinander,

es geht um eine Schrittmotoransteuerung. Für den Anfang möchte ich mit 
einer linearen Rampe beginnen. Trapezform. Zum Einsatz kommt ein 
Atmega168.
Meine Programmiererfahrung in C reicht für diesen Zweck nicht ganz aus. 
Deshalb lese ich mich zur Zeit in der Thematik (Interrupt, Timer, usw.) 
einwenig durch. Allerdings habe ich schon mal einige Fragen die zum 
Allgemeinverständnis dienen.

1. Überlegung:
die Funktion einer trapezförmigen  Rampe im Main-Teil in einer While 
aufstellen
und die entsprechenden Werte an dem Ausgangspin, wo der Motor 
angeschlossen ist für die Richtung, Stromabsenkung, Frequenz und 
Ein/Aus, ausgeben.
Welche Probleme sind möglicherweise damit verbunden? Timing = 
Schrittverlust?

2. Überlegung:
Interrupt, hier Timer1. Die Berechnung der entsprechenden Werte für die 
Rampe in der ISR
Welche Probleme sind hierbei damit verbunden? Timing = Schrittverlust? 
Frequenzabhängigkeit? Was passiert in einem Bereicht in dem die Frequenz 
ganz klein ist, unterhalb des 1. Überlaufs?

3. Überlegung:
Interrupt, hier durch zwei Timer. Der eine für die Berechnung, der 
zweite für die Frequenz. Meine Überlegung hier ist die, dass die zwei 
Timer aufeinander  abgestimmt sind und keine Timingprobleme auftreten 
dürften.
Auch hier wieder die Frage zu dem kleinen Bereich aus der 2. Überlegung.

Ich hoffe ihr könnt mir ein wenig dabei helfen die Unklarheiten zu 
beseitigen.

Danke schon mal
Christian

Autor: Klaus Falser (kfalser)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
1. Main Programm
- Das Setzen der Ausgangspins wird möglicherweise nicht so regelmäßig 
bei hohen Frequenzen und Du kannst in deiner Schleife nicht viel anderes 
tun, also kein LCD bedienen oder sonstiges.

2. Timer : Die beste Lösung
Du solltest den Timer aber nicht so verwenden, dass nur ein Interrupt 
ausgelöst wird, sondern der Timer soll beim Erreichen der Schwelle den 
Ausgang setzen. Die ISR setzt dann den Ausgang zurück (es braucht ja nur 
einen kurzen Puls), macht die Berechnungen (nicht in Float !!) und setzt 
den Timer für die nächste Zählung auf.
Ganz kleine Frequenzen werden normalerweise nicht verwendet, der 
Schrittmotor startet schon mit einer Minimal-Frequenz (die Rampe startet 
nicht bei null).
Du kannst mit dem Simulator die Dauer der ISR bestimmen, das ergibt dann 
deine höchste Frequenz, die zu erzeugen kannst.

3) 2 x Timer
Unnötig und kompliziert.

Autor: Reinhard R. (reinhardr)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi,

Die Application Note AVR446 von Atmel beschäftigt sich mit der Thematik. 
Sourcecode gibt es auch dazu.
http://www.atmel.com/products/AVR/mc/?family_id=607


Reinhard

Autor: Christian (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
.. danke für die Infos.

Werd noch mal auf euch zu kommen wenns an die Programmiererei geht.

Servus
Christian

Autor: Gast (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Servus miteinander,

nachdem ich mich einigermaßen in die Schrittmotor und Timerthematik 
eingelesen habe, steht nun Linearrampe.
Allerdings gibt es noch einige Problemstellen deren Lösung ich nicht 
finde.

Zu Beginn der aufsteigenden Rampe kann ich mit dem Oszi eine höhere 
Frequenz, wie über dem restlichen Verlauf der Rampe, sehen. Im Anhang 
kann man in etwa erkennen was ich damit meine.
Nun habe ich momentan keine Ahnung woher diese Ungleichmäßigkeit kommen 
kann.
Auch bin ich mir über die richtige Datentypenwahl nicht ganz sicher.
// ********************* Variablen ***********************************
unsigned int f0=303; 
volatile float f, fc, Match;
unsigned char Fahrt_Flag;
unsigned long Pos_x=0, Pos_P1=0, a, fm=18000;

// ********************* Prototypen ***********************************
void ATOUL(unsigned char *DFeld, int feldlaenge);  // Prototype Arry to unsigned long
void ULTOA(unsigned long Value);
static unsigned long wurzel(unsigned long x);

// ***********************ISR zum Zählen der Schritte *******************
ISR(TIMER1_COMPB_vect)         // ISR Zählen der Matches
{  
  PORTB |= (1<<PB2);         // Motor-Takt AN 
  
  OCR1A=Match;
// ----------- Zählen der Schritte in beiden Richtungen  -------------  
  Pos_ist++;
  if(Rechtsfahrt_Flag==0)
  { 
    Pos_akt--;          
  }  
  else 
    Pos_akt++;    

// ----------- Timer ausschalten  ---------------------------------  
  if((Pos_soll == Pos_ist) || (MotorAN_ADC == 0) || (MotorAN_LS == 0))
  {
    TIMSK1=0;                  // Timer Interrupt COMPARE MATCH B aus
  }
  
  PORTB &=~ (1<<PB2);        // Motor-Takt AUS
}

static unsigned long wurzel(unsigned long x)
{
  register unsigned long xr;  // result register
  register unsigned long q2;  // scan-bit register
  register unsigned char f;   // flag (one bit)

  xr = 0;                     // clear result
  q2 = 0x40000000L;           // higest possible result bit
  do
  {
      if((xr + q2) <= x)
      {
        x -= xr + q2;
        f = 1;                  // set flag
      }
      else{
        f = 0;                  // clear flag
      }
      xr >>= 1;
      if(f){
        xr += q2;               // test flag
      }
  } while(q2 >>= 2);          // shift twice
  if(xr < x){
    return xr +1;             // add for rounding
  }
  else{
    return xr;
  }
}

void schrittmotor(unsigned char *DFeld,int feldlaenge)    // SUB-Befehl Schrittmotor
{
  MotorAN_ADC=1;          // Flag 
  MotorAN_LS=1;          // Flag 
  Fahrt_Flag=1;          // Flag

  f=f0;
  a=10000;  
  Pos_P1=10200;

  Match=0;
  OCR1A=0;

// *********************** Konvertierung der Daten in long int ***************
  if(Schrittmotor_flag == 0) 
  {
    ATOUL(DFeld,feldlaenge);
  }  

// -------- Timer Init -------------------------------------------------------------------
  if(Schrittmotor_flag==1)
  {
    TIMSK1 = 0b00000100;      // Timer Interrupts COMPARE MATCH B = bit2
    TCCR1A = 0b00000000;      // Pin disconnect  
    TCCR1B = 0b00001001;      // Timer Start: Mode 4 = bit3, Teiler 1 = bit1
    PORTD &= ~(1<<PD0);       // Motor an ( low aktiv )     
  }

  while(Schrittmotor_flag==1)          
  {    
// -------- Aufsteigend -----------------
    if(Fahrt_Flag==1)    // 1 = Aufsteigend
    {      
      f = wurzel(2*Pos_ist*a);
      Match= F_CPU/f;      
        
      if(Pos_ist >= Pos_P1)
      {  
        fc=f;        
        Fahrt_Flag=2;                     
      }
    }
  
// -------- Konstant -----------------
    if(Fahrt_Flag==2)    // 2 = Konstant
    {    
      Match= F_CPU/fc;
      PORTB |=(1<<PB4);         // LED gelb AN            

      if(Pos_ist>=(Pos_soll-Pos_P1))
      {              
        Fahrt_Flag=3;        
      }
    }  
  
// -------- Absteigend -----------------
    if(Fahrt_Flag==3)    // 3 = Absteigend
    {  
      PORTB &= ~(1<<PB4);         // LED gelb AUS  

      f = wurzel(2*a*(Pos_soll-Pos_ist));      
      Match= F_CPU/f;

      if(Pos_ist >= Pos_soll)  
      {        
        PORTD |= (1<<PD0);         // Motor aus
      }
    }
  }                  // Ende while
  Schrittmotor_flag=0;
}                  // Ende Schrittmotor

Vielen Dank für die Bemühung.

Ich würde mich auch darüber freuen wenn der eine oder andere ein Wort 
darüber verlieren könnte, was ich evtl am Programmierstil verbessern 
könnte.

Gruß Christian

Autor: Klaus Falser (kfalser)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich vermute, dass in der Berechnung von Match irgendetwas überläuft. Es 
ist auch sicher nicht ideal, diese als float type zu verwenden. Der 
wurzel Algorithmus ergibt long. Warum dann wieder auf float konvertieren 
und dann bei der Zuweisung
  OCR1A=Match;
auf unsigned int (16 bit) ?

Zum Programmierstil gibts sicher viele Tips, z.B.
- verwenden von #defines für die PINS z.B.
#define     MSTEP_PIN      PB2
#define     MSTEP_PORT     PORTB

  MSTEP_PORT |= _BV(MSTEP_PIN);         // Motor-Takt AN 
- ich liebe switch anweisungen, z.B :
    switch(Fahrt_Flag) {
    case 1 : // Ansteigend 
        ....
        break;
    case 2 : // konstant 
        .... 
        break;
    default: 
    }
oder noch besser
#define RAMP_UP    (1)
#define RAMP_DOWN  (3)
#define CONST_MOTION (2) 

    switch(Fahrt_Flag) {
    case RAMP_UP : // Ansteigend 
        ....
        break;
    case CONST_MOTION : // konstant 
        .... 
        break;
    default: 
    }

: Bearbeitet durch Moderator
Autor: franck (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
wo hast du denn bitte post_ist und post_akt her. und hast du bitte das 
programm in ganzer form?

Autor: m.n. (Gast)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
franck schrieb:
> wo hast du denn bitte post_ist und post_akt her. und hast du bitte das
> programm in ganzer form?

Da kann man mal sehen, wie die Zeit vergeht ;-)
Aber wenn Du ein Programm für ATmega88/168/328 suchst: 
http://www.mino-elektronik.de/Generator/takte_impu... ff.

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.