mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Problem mit Servoansteuerung mit einem PIC!


Autor: Dewey (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Leute,

Ich weiß, dass das hier wahrscheinlich der 500 beitrag zu servos ist, 
aber ich muss da jetzt einfach fragen weil ich ziemlich verzweifelt bin!

Ich bekomm es einfach nicht hin, meinen servo(futaba S3003 
Modellbauservo)
so laufen zu lassen wie ich es möchte.

Also ich weiß wie servos angesteuert werden müssen. also impulse der 
länge zwischen 1ms und 2ms verändern meine position des servos usw.
Also funktionsprinzip vom servo ist mir prinzipell klar!

So ich verwende einen PIC 16F877a mit jetzt mittlerweile der 
Taktfrequenz 20Mhz (vor ein paar wochen habe ich noch 4 MHz verwendet -> 
hat aber leider auch nicht hingehaun). Diesen PIC programmiere ich in C 
mit MPLAB Version 8.10.

folgender pseudocode funktioniert einwand frei:
while(true)
{
   output_high(PIN_C2); 
   delay_ms(2);
   output_low(PIN_c2);
   delay_ms(18);
}
also ich erzeuge mir quasi selbst ein pwm signal, indem ich den ausgang 
C2 auf HIGH setze, dann 2 ms warte , und dann wieder auf LOW setze und 
dann 18 ms warte!
wenn ich diesen code in meinem pic laufen lasse, stellt sich die 
gewünschte servo position ein. (funktioniert auch für diverse andere 
positionen also zum BSP für delay_ms(1.5) stellt sich mein servo in 
mittelstellung!

so jetzt zu meinen eigentlichen problem:
ich möchte per rs232 aber die servo position variieren können!

dazu hab ich folgenden pseudocode:
while(true)
{
  empfangen = getc();
  switch(empfangen)
  {
    case 'a': pos = 1;
              break;  
    case 's': pos = 1.5;
              break;
    case 'd': pos = 2;
              break;
  } 
  output_high(PIN_C2); 
  delay_ms(pos);
  output_low(PIN_c2);
  delay_ms(18);
}

also ich empfange über die rs232 ein zeichen, je nachdem ob dieses 
empfangene zeichen a, b oda c ist sollte dann der sevo sich auf die 
mittelstellung oda ganz rechts oda ganz links einstellen.

leider macht er das nicht. der servo zuckt dann nur!

was mach ich falsch? ich hoffe es kann mir jemand helfen! bin für viele 
ratschläge, anmerkungen und eventuell anderen vorschlägen sehr dankbar!!

mfg dewey

Autor: ... ... (docean) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ohne Timer bekommt man normalerweise nicht ruhig...

Kannst du mit einem Ossi nachmessen was kommt?

Kann es sein das deine getch wartet?

Autor: holger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>  empfangen = getc();

Wenn getc() auf ein Zeichen wartet kann das
auch nicht funktionieren. Du bräuchtest eine
Funktion die mal nachsieht ob ein Zeichen empfangen wurde.
Falls nicht wird einfach im Programm weitergemacht.

Autor: Dewey (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
hallo,

hab leider kein ossi!

nein eigentlich nicht! weil das kbhit() ist zwar dazu da das nur ein 
zeichen gelesen wird, wenn eines empfangen wird! aba sobald das erste 
zeichen gelesen wird. also ich sende das zeichen a zu meinem pic das 
zeichen a wird dann in die variable empfangen gespeichert und dann 
sollte durch die switch funktion die gewünschte pos eingestellt werden! 
wenn jetzt kein zeichen empfangen wird, wird die getc() funktion 
übersprungen und das zeichen das der variable empfanegen steht wird 
wieder an meine switch funktion übergeben!

naja wie würdest du das mit den timer machen! welchen timer muss ich da 
verwenden?? timer1??

also müsste ich folgendermaßen vorgehen:

ich setze mein ausgangspin auf high, in dem moment beginnt mein timer 
hinauf zu zählen und nach 20ms wird mein timer wieder auf null gesetzt.
wenn ich jetz mein timer signal mit dem compare modul mit einem 2ten 
signal vergleiche und die haben den selben wert also sind ident wird 
mein ausgangspin wieder auf low gesetzt!

bin ich auf den richtigen weg oder??
wie würdest du das machen??

Autor: Dewey (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ahhhhh halt hab oben bei meinem ersten beitrag die fkt kbhit vergessen

ich werde es gleich ausbessern

Autor: Dewey (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
also statt der zeile
 empfangen = getc() 

gehört
 if(kbhit()) empfangen = getc(); 

also so hab ichs angewendet

sorry hab vergessen oben zum dazuschreiben!

Autor: holger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>>  empfangen = getc();
>wenn jetzt kein zeichen empfangen wird, wird die getc() funktion
>übersprungen und das zeichen das der variable empfanegen steht wird
>wieder an meine switch funktion übergeben!

Und woher soll getc() wissen was vorher in empfangen war?
empfangen wird einfach überschrieben mit dem Rückgabewert von getc().

Autor: holger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> if(kbhit()) empfangen = getc();
>sorry hab vergessen oben zum dazuschreiben!

Hast du sonst noch was vergessen?

Autor: Dewey (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
nein sonst hab ich nix vergessen!*gg*

also if(kbhit()) wird nur TRUE wenn etwas über die rs232 empfangen wird!

das heißt ich sende ein zeichen vom pc zum pic -> kbhit() liefert 1 
zurück -> empfangen = getc() wird ausgeführt (also das zeichen wird in 
die variable empfangen gespeichert) -> switch anweisung ...

wenn ich jetzt kein zeichen sende -> kbhit() lifert rückgabewert 0  -> 
empfangen = getc() wird nicht ausgeführt (weil ja die if anweisung FALSE 
ist) -> somit wird das zeichen das vorher in der variable empfangen 
gespeichert wurde wieder an die switch anweisung übergeben!

Autor: Dewey (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
hallo leute!

ich habe jetzt mal einen programm code geschrieben der den servo in eine 
bestimmte position bringt und dort die position hält!
das ganze habe ich jetzt mit den timer1 realisiert!
 
#include <16F877a.h>
#include <stdlib.h>
#use delay (clock=20000000)
#fuses HS, NOWDT, NOPROTECT, NOLVP
#use RS232(baud=9600,xmit=PIN_C6,rcv=PIN_C7)

void main (void)
{
   int16 pos = 1875;  // Servo mittelstellung (1,5ms)
   
   setup_timer_1(T1_INTERNAL | T1_DIV_BY_4);
   
   set_timer1(0);         //timer wird auf 0 gesetzt
   output_high(PIN_C2);   //ausgang wird auf high gesetzt
   while(true)
   {   
     if(get_timer1() >= pos)  //wenn pos >= timer wert 
        output_low(PIN_C2);   // ausgang auf low setzen

     if(get_timer1() >= 22500) //22500 entspricht 18ms
     {
         set_timer1(0);
         output_high(PIN_C2);
     }   
   }
} 

so dieser code funktioniert einwandfrei! ich habe es mit verschiedenen 
pos werten ausprobiert und es funktioniert!

nun möchte ich aber per rs232 ein zeichen senden das mir eine bestimmte 
pos bestimmt.
das heißt wenn ich zb ein 'a' sende, soll mein servo ganz nach links 
oder rechts drehen!
also ich habe folgenden code ausprobiert, aber so funktioniert es leider 
nicht und ich weiß nicht warum!
#include <16F877a.h>
#include <stdlib.h>
#use delay (clock=20000000)
#fuses HS, NOWDT, NOPROTECT, NOLVP
#use RS232(baud=9600,xmit=PIN_C6,rcv=PIN_C7)

void main (void)
{
   int16 pos = 1875;  // Servo mittelstellung (1,5ms)
   char empfangen;

   setup_timer_1(T1_INTERNAL | T1_DIV_BY_4);
   
   set_timer1(0);         //timer wird auf 0 gesetzt
   output_high(PIN_C2);   //ausgang wird auf high gesetzt
   while(true)
   {   
     if(get_timer1() >= pos)  //wenn pos >= timer wert 
        output_low(PIN_C2);   // ausgang auf low setzen

     if(get_timer1() >= 22500) //22500 entspricht 18ms
     {
         set_timer1(0);
         output_high(PIN_C2);
     }
     if(kbhit()) empfangen = getc();  //zeichen wird gelesen, in empfangen gespeichert
     if(empfangen == 'a') pos == 2500;  //2ms  
     if(empfangen == 'w') pos == 18750; // 1,5ms-Mittelstellung  
   }
} 

mein timer zählt bis 22500 dann wird er auf null gesetzt (22500 
entsrechen 20 ms)

könnte mir bitte jemand helfen! was hab ich falsch gemacht?
wie würdet ihr das machen?

mfg dewey

Autor: ... ... (docean) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Du mußt das schalten der Ausgänge in eine ISR packen...

Ich hab das dann so gemacht das dien zweiter timer die ausgabe immer 
wieder neu startet (damit man fein genung auflösen kann)

Aber man kann auf folgendes machen...

ISR wird alle 0,1ms ausgeführt...

In der ISR zählst du einen weiter variabele hoch, wenn diese gleich dem 
Wert für den Servo ausgang wird der Ausgang low geschaltet..

High wird der Ausgang wenn deine Zählvar. 0 wird..

Die wird 0 wenn sie das Maxiumum erreicht hat (bei dieser Version 20000)

so zB (so ungefähr)
ISR ()
{
 if(!zaehler)
   pin_high();

 if(zaehler==wertservo)
   pin_low();

 if(zaehler==20000)
   zaehler=0;

  zaehler++;
}

Autor: dewey (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
hallo jan-h. B.!

danke für deine antwort!

das heißt also das mein code folgendermaßen aussieht:
#include <16F877a.h>
#include <stdlib.h>
#use delay (clock=20000000)
#fuses HS, NOWDT, NOPROTECT, NOLVP
#use RS232(baud=9600,xmit=PIN_C6,rcv=PIN_C7)

int16 zaehler = 0;
int16 pos = 1875;  // Servo mittelstellung (1,5ms)

#INT_TIMER1  //wodurch soll diese isr ausgelöst werden
void isr()   // jetzt würde diese isr() ja auch immer nur ausgeführt
{            // wenn der timer1 einen überlauf hat
 
 if(zaehler == 0)
   output_high(PIN_C2);

 if(zaehler == pos)
   output_low(PIN_C2);

 if(zaehler == 20000)
   zaehler=0;

  zaehler++;
}

void main (void)
{
   char empfangen;

   setup_timer_1(T1_INTERNAL | T1_DIV_BY_4);
   
   set_timer1(0);         //timer wird auf 0 gesetzt
   output_high(PIN_C2);   //ausgang wird auf high gesetzt

   while(true)
   {   
     if(kbhit()) empfangen = getc();  //zeichen wird gelesen, in    empfangen gespeichert
     if(empfangen == 'a') pos == 2500;  //2ms  
     if(empfangen == 'w') pos == 1250; // 1ms  
   }
} 

das mit der isr() ist mir noch nicht ganz klar! warum man das braucht!
was macht diese interrupt routine wird einfach alle 0,1ms ausgelöst oder 
wie? und durch welche aktion wird diese ausgelöst? kann man das auch 
einstellen wann diese isr() immer ausgeführt wird?? brauch dann 
überhaupt noch meinen timer1??
danke schon im voraus!!
mfg dewey

Autor: dewey (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ahhh jetzt hab ichs glaub ich kapiert!!

ich stelle meinen timer so ein, damit der immer nach 0,1ms einen 
interrupt auslöst! und wenn mein interrupt ausglöst wird, in dieser isr 
wird ein zähler implementiert der immer um eins hinaufzählt wenn die isr 
ausgeführt wird! und weiters wenn der zähler in der isr den wert 20000 
erreicht hat wird der zähler auf 0 rückgesetzt , wenn der zähler gleich 
der gewünschten servo positionswert ist wird der ausgang auf low gesetzt 
und wenn der zähler == 0 ist wird der ausgang auf high gesetzt!

also ist jetzt zwar nicht gerade übersichtlich aufgeschieben aber ich 
glaub jetzt weiß ich wie das funktionieren soll!

also ich werds einfachmal so ausprobieren!!

falls ich wieder einen denkfehler habe, bitte melden!

danke schon im voraus

mfg dewey

Autor: dewey (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
hallo!

also hab jetzt mal einen code geschrieben der aller 0.1ms einen 
interrupt auslöst.
#include <16F877a.h>
#device ADC=10      //10bit Auflösung
#use delay (clock=20000000)
#fuses HS, NOWDT, NOPROTECT, NOLVP
#use RS232(baud=9600,xmit=PIN_C6,rcv=PIN_C7)
#include <stdlib.h>

int16 pos = 1000;
int16 zaehler = 0;

#INT_TIMER0
void isr()
{
   zaehler++;
   if(zaehler == pos)
     output_low(PIN_C2);   
   if(zaehler == 20000)
     zaehler=0;
   if(zaehler == 0)
     output_high(PIN_C2);
}

void main (void)
{
   char empfangen;

   setup_timer_2(T2_DIV_BY_4, 125, 1);
   set_timer2(0);
   output_high(PIN_C2);
  
   enable_interrupts(INT_TIMER2);
   enable_interrupts(GLOBAL);
   while(true)
   {   
     if(kbhit()) empfangen = getc();
     if(empfangen == 'a') pos == 2000;  //2ms  
     if(empfangen == 'd') pos == 1000; // 1ms  
     if(empfangen == 's') pos == 1500; // 1.5ms  
   }

} 

aber wenn ich jetzt kompiliere sagt mir der compiler 3 warnings
und zwar in der while(true) schleife bei den if(empfangen...) 
anweisungen

der compiler sagt das dieser code keinen effekt hat!!

kann mir jemand erklären warum?? versteh das nicht ganz!

mfg dewey

Autor: ... ... (docean) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
== ist ja keine Zuweisung...
     if(kbhit()) empfangen = getc();
     if(empfangen == 'a') pos == 2000;  //2ms  
     if(empfangen == 'd') pos == 1000; // 1ms  
     if(empfangen == 's') pos == 1500; // 1.5ms  

geht besser:
if(kbhit())
{
 empfangen=getc();

 switch (empfangen)
 {
   case 'a':
    pos=2000;
    break;
   case 'd':
    pos=1000;
    break;
 }
}

Ist das richtig das du Timer2 einrichtest aber Timer0 ISR nimmst?

Autor: dewey (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
hallo

ah hoppala da gehört natürlich timer2, ich habs bei mir im MPLAB eh 
richtig nur da hab ichs im eifer des gefechts vergessen auszubessern!

ja ich hab jetzt die empfangen variable initialisiert aber der compiler 
meckert immer noch!

mfg

Autor: dewey (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
jaaaa natürlich da hast du recht!! das doppelte == ist natürlich 
falsch!!!

danke
mfg

Autor: dewey (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
naja hab jetzt mal diesn code getestet! aber haut leider nicht hin!
der servo ruckelt nur vor sich hin!

mfg

Autor: dewey (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
hallo

wollt euch nur mitteilen das ich das problem gelöst habe
anbei der richtige code! funktioniert einwandfrei!!
#include <16F877a.h>
#use delay (clock=20000000)
#fuses HS, NOWDT, NOPROTECT, NOLVP
#use RS232(baud=9600,xmit=PIN_C6,rcv=PIN_C7)
#include <stdlib.h>

int8 pos;
int8 zaehler = 0;

#INT_TIMER2
void isr()
{
   zaehler++;
   
   if(zaehler == 180)
   {
     zaehler = 0;
     output_high(PIN_C2);
   }
   if(zaehler == pos)
     output_low(PIN_C2);   
}


void main (void)
{
   char empfangen = 'a';

   setup_timer_2(T2_DIV_BY_4, 125, 1);
   set_timer2(0);
     
   enable_interrupts(INT_TIMER2);
   enable_interrupts(GLOBAL);
    
   while(true)
   {   
    if(kbhit())
     {
        empfangen=getc();

        switch (empfangen)
        {
           case 'a': pos=20;
                     break;
           case 'd': pos=10;
                     break;
           case 's': pos=15;
                     break;
        }
      }
   }
} 

@jan.h danke das du mir geholfen hast!

mfg dewey

Autor: dewey (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo

Ich bin gerade dabei, eine Steuerung mit einem PIC16F877a zu bauen. 
Dabei sollen 3 Servos per PC angesteuert werden können. Aber auch 3 
Sensordaten sollen zum PC zurückgesendet werden.
Ich habe jetzt schon mal einen Programmcode geschrieben, mit dem ich 
einen Servo problemlos per PC steuern kann.
Nun möchte ich aber auch noch Daten zu meinem pc senden. Doch leider 
funktioniert das nicht so wie ich es mir vorstelle!
Der Timer2 Interrupt wird alle 0,1ms ausgeführt und in dieser 
Interruptroutine erzeuge ich dann den Impuls von 1-2ms. Jetzt habe ich 
noch den Timer0 so konfiguriert, dass diese isr alle 0,05ms ausgeführt 
wird und mir dann die Messdaten zum PC sendet (Ich habe jetzt nur einen 
einfachen Text zum testen genommen). Dieser Text wird mir zwar zu meine 
PC gesendet, aber ich kann meinen Servo nicht mehr steuern.

Könnte mir jemand erklären warum das so nicht funktioniert.
Wie würdet ihr das machen??
Hoffe mir kann jemand helfen!
#include <16F877a.h>
#use delay (clock=20000000)
#fuses HS, NOWDT, NOPROTECT, NOLVP
#use RS232(baud=9600,xmit=PIN_C6,rcv=PIN_C7)
#include <stdlib.h>

int8 servopos;
int8 zaehler = 0;

#INT_TIMER2
void timer2overflow()
{
   zaehler++;
   if(zaehler == 180)
   {
      zaehler = 0;
      output_high(PIN_B0);    
   }   
   if(zaehler == servopos)
   {
      output_low(PIN_B0);
   }       
}

#INT_TIMER0
void timer0overflow()
{
   printf("\fMessdaten\n");  
}

void main (void)
{
   char empfangen;
   
   setup_timer_2(T2_DIV_BY_4, 125, 1);
   set_timer2(0);

   setup_timer_0(RTCC_INTERNAL);
   set_timer0(0);

   enable_interrupts(INT_TIMER2);
   enable_interrupts(INT_TIMER0);
   enable_interrupts(GLOBAL);
    
   while(true)
   {  
      if(kbhit()) empfangen = getc();
      servopos = empfangen - 60;      
   }
} 

Danke im Voraus

mfg dewey

Autor: ... ... (docean) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ein printf in einer ISR ist bäh...

schalt mal ne LED an oder aus...

Autor: dewey (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
hallo
Habe jetzt eine LED eingeschalten und es funktioniert. LED leutet und 
ich kann meinen Servo auch noch steuern.

Hast du eine Idee wie ich Messdaten zu meinem PC senden könnte??

Autor: ... ... (docean) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
setz nur ne var. in der isr...und das printf machst in main...
#INT_TIMER0
void timer0overflow()
{
   senden=1;  
}

void main (void)
{
   ... 
   while(true)
   {  
      if(kbhit()) empfangen = getc();
      servopos = empfangen - 60;      

      if(senden)
      {
           printf("bla");
           senden=0;
       }
   }
}

Autor: dewey (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
hallo
Erstmal danke!
Ich habe das jetzt ausprobiert, doch leider hängt sich da mein pic auf!
Am Anfang hat es funktioniert und sobald ich einmal ein Zeichen zum PIC 
sende hängt er sich auf!

mfg

Autor: dewey (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
hallo
ich hab mal ne frage! Warum ist das nicht gut, wenn ich in einer isr 
einen printf Befehl verwenden will? Warum funktioniert das nicht??

mfg dewey

Autor: ... ... (docean) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
printf verbraucht massig Rechenzeit...

ISRs sollten möglichst kurz sein...

Daher kann es dann sein das du Interrupts übersiehst/nicht rechtzeitig 
reagierst...

Autor: dewey (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ok verstehe! Aber warum funktioniert es auch nicht, wenn ich in der 
while(true) schleife die printf Anweisung einbaue?

mfg

Autor: gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
weil printf viel länger braucht als 0,05ms !!!

Autor: dewey (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
hallo
Ok hab ich jetzt verstanden! Aber wie kann ich dann dieses Problem 
lösen??
Irgendwelche Vorschläge??
mfg dewey

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.