Forum: Compiler & IDEs Schrittmotor - Programmierung einer linearen Rampe


von Chritian (Gast)


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

von Klaus F. (kfalser)


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.

von Reinhard R. (reinhardr)


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

von Christian (Gast)


Lesenswert?

.. danke für die Infos.

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

Servus
Christian

von Gast (Gast)


Angehängte Dateien:

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.
1
// ********************* Variablen ***********************************
2
unsigned int f0=303; 
3
volatile float f, fc, Match;
4
unsigned char Fahrt_Flag;
5
unsigned long Pos_x=0, Pos_P1=0, a, fm=18000;
6
7
// ********************* Prototypen ***********************************
8
void ATOUL(unsigned char *DFeld, int feldlaenge);  // Prototype Arry to unsigned long
9
void ULTOA(unsigned long Value);
10
static unsigned long wurzel(unsigned long x);
11
12
// ***********************ISR zum Zählen der Schritte *******************
13
ISR(TIMER1_COMPB_vect)         // ISR Zählen der Matches
14
{  
15
  PORTB |= (1<<PB2);         // Motor-Takt AN 
16
  
17
  OCR1A=Match;
18
// ----------- Zählen der Schritte in beiden Richtungen  -------------  
19
  Pos_ist++;
20
  if(Rechtsfahrt_Flag==0)
21
  { 
22
    Pos_akt--;          
23
  }  
24
  else 
25
    Pos_akt++;    
26
27
// ----------- Timer ausschalten  ---------------------------------  
28
  if((Pos_soll == Pos_ist) || (MotorAN_ADC == 0) || (MotorAN_LS == 0))
29
  {
30
    TIMSK1=0;                  // Timer Interrupt COMPARE MATCH B aus
31
  }
32
  
33
  PORTB &=~ (1<<PB2);        // Motor-Takt AUS
34
}
35
36
static unsigned long wurzel(unsigned long x)
37
{
38
  register unsigned long xr;  // result register
39
  register unsigned long q2;  // scan-bit register
40
  register unsigned char f;   // flag (one bit)
41
42
  xr = 0;                     // clear result
43
  q2 = 0x40000000L;           // higest possible result bit
44
  do
45
  {
46
      if((xr + q2) <= x)
47
      {
48
        x -= xr + q2;
49
        f = 1;                  // set flag
50
      }
51
      else{
52
        f = 0;                  // clear flag
53
      }
54
      xr >>= 1;
55
      if(f){
56
        xr += q2;               // test flag
57
      }
58
  } while(q2 >>= 2);          // shift twice
59
  if(xr < x){
60
    return xr +1;             // add for rounding
61
  }
62
  else{
63
    return xr;
64
  }
65
}
66
67
void schrittmotor(unsigned char *DFeld,int feldlaenge)    // SUB-Befehl Schrittmotor
68
{
69
  MotorAN_ADC=1;          // Flag 
70
  MotorAN_LS=1;          // Flag 
71
  Fahrt_Flag=1;          // Flag
72
73
  f=f0;
74
  a=10000;  
75
  Pos_P1=10200;
76
77
  Match=0;
78
  OCR1A=0;
79
80
// *********************** Konvertierung der Daten in long int ***************
81
  if(Schrittmotor_flag == 0) 
82
  {
83
    ATOUL(DFeld,feldlaenge);
84
  }  
85
86
// -------- Timer Init -------------------------------------------------------------------
87
  if(Schrittmotor_flag==1)
88
  {
89
    TIMSK1 = 0b00000100;      // Timer Interrupts COMPARE MATCH B = bit2
90
    TCCR1A = 0b00000000;      // Pin disconnect  
91
    TCCR1B = 0b00001001;      // Timer Start: Mode 4 = bit3, Teiler 1 = bit1
92
    PORTD &= ~(1<<PD0);       // Motor an ( low aktiv )     
93
  }
94
95
  while(Schrittmotor_flag==1)          
96
  {    
97
// -------- Aufsteigend -----------------
98
    if(Fahrt_Flag==1)    // 1 = Aufsteigend
99
    {      
100
      f = wurzel(2*Pos_ist*a);
101
      Match= F_CPU/f;      
102
        
103
      if(Pos_ist >= Pos_P1)
104
      {  
105
        fc=f;        
106
        Fahrt_Flag=2;                     
107
      }
108
    }
109
  
110
// -------- Konstant -----------------
111
    if(Fahrt_Flag==2)    // 2 = Konstant
112
    {    
113
      Match= F_CPU/fc;
114
      PORTB |=(1<<PB4);         // LED gelb AN            
115
116
      if(Pos_ist>=(Pos_soll-Pos_P1))
117
      {              
118
        Fahrt_Flag=3;        
119
      }
120
    }  
121
  
122
// -------- Absteigend -----------------
123
    if(Fahrt_Flag==3)    // 3 = Absteigend
124
    {  
125
      PORTB &= ~(1<<PB4);         // LED gelb AUS  
126
127
      f = wurzel(2*a*(Pos_soll-Pos_ist));      
128
      Match= F_CPU/f;
129
130
      if(Pos_ist >= Pos_soll)  
131
      {        
132
        PORTD |= (1<<PD0);         // Motor aus
133
      }
134
    }
135
  }                  // Ende while
136
  Schrittmotor_flag=0;
137
}                  // 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

von Klaus F. (kfalser)


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
1
  OCR1A=Match;
auf unsigned int (16 bit) ?

Zum Programmierstil gibts sicher viele Tips, z.B.
- verwenden von #defines für die PINS z.B.
1
#define     MSTEP_PIN      PB2
2
#define     MSTEP_PORT     PORTB
3
4
  MSTEP_PORT |= _BV(MSTEP_PIN);         // Motor-Takt AN
- ich liebe switch anweisungen, z.B :
1
    switch(Fahrt_Flag) {
2
    case 1 : // Ansteigend 
3
        ....
4
        break;
5
    case 2 : // konstant 
6
        .... 
7
        break;
8
    default: 
9
    }
oder noch besser
1
#define RAMP_UP    (1)
2
#define RAMP_DOWN  (3)
3
#define CONST_MOTION (2) 
4
5
    switch(Fahrt_Flag) {
6
    case RAMP_UP : // Ansteigend 
7
        ....
8
        break;
9
    case CONST_MOTION : // konstant 
10
        .... 
11
        break;
12
    default: 
13
    }

: Bearbeitet durch User
von franck (Gast)


Lesenswert?

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

von m.n. (Gast)


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_impulse.htm#bsp7 ff.

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.