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


von Dewey (Gast)


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:
1
while(true)
2
{
3
   output_high(PIN_C2); 
4
   delay_ms(2);
5
   output_low(PIN_c2);
6
   delay_ms(18);
7
}
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:
1
while(true)
2
{
3
  empfangen = getc();
4
  switch(empfangen)
5
  {
6
    case 'a': pos = 1;
7
              break;  
8
    case 's': pos = 1.5;
9
              break;
10
    case 'd': pos = 2;
11
              break;
12
  } 
13
  output_high(PIN_C2); 
14
  delay_ms(pos);
15
  output_low(PIN_c2);
16
  delay_ms(18);
17
}

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

von ... .. (docean) Benutzerseite


Lesenswert?

ohne Timer bekommt man normalerweise nicht ruhig...

Kannst du mit einem Ossi nachmessen was kommt?

Kann es sein das deine getch wartet?

von holger (Gast)


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.

von Dewey (Gast)


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??

von Dewey (Gast)


Lesenswert?

ahhhhh halt hab oben bei meinem ersten beitrag die fkt kbhit vergessen

ich werde es gleich ausbessern

von Dewey (Gast)


Lesenswert?

also statt der zeile
1
 empfangen = getc()

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

also so hab ichs angewendet

sorry hab vergessen oben zum dazuschreiben!

von holger (Gast)


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().

von holger (Gast)


Lesenswert?

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

Hast du sonst noch was vergessen?

von Dewey (Gast)


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!

von Dewey (Gast)


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!
1
 
2
#include <16F877a.h>
3
#include <stdlib.h>
4
#use delay (clock=20000000)
5
#fuses HS, NOWDT, NOPROTECT, NOLVP
6
#use RS232(baud=9600,xmit=PIN_C6,rcv=PIN_C7)
7
8
void main (void)
9
{
10
   int16 pos = 1875;  // Servo mittelstellung (1,5ms)
11
   
12
   setup_timer_1(T1_INTERNAL | T1_DIV_BY_4);
13
   
14
   set_timer1(0);         //timer wird auf 0 gesetzt
15
   output_high(PIN_C2);   //ausgang wird auf high gesetzt
16
   while(true)
17
   {   
18
     if(get_timer1() >= pos)  //wenn pos >= timer wert 
19
        output_low(PIN_C2);   // ausgang auf low setzen
20
21
     if(get_timer1() >= 22500) //22500 entspricht 18ms
22
     {
23
         set_timer1(0);
24
         output_high(PIN_C2);
25
     }   
26
   }
27
}

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!
1
#include <16F877a.h>
2
#include <stdlib.h>
3
#use delay (clock=20000000)
4
#fuses HS, NOWDT, NOPROTECT, NOLVP
5
#use RS232(baud=9600,xmit=PIN_C6,rcv=PIN_C7)
6
7
void main (void)
8
{
9
   int16 pos = 1875;  // Servo mittelstellung (1,5ms)
10
   char empfangen;
11
12
   setup_timer_1(T1_INTERNAL | T1_DIV_BY_4);
13
   
14
   set_timer1(0);         //timer wird auf 0 gesetzt
15
   output_high(PIN_C2);   //ausgang wird auf high gesetzt
16
   while(true)
17
   {   
18
     if(get_timer1() >= pos)  //wenn pos >= timer wert 
19
        output_low(PIN_C2);   // ausgang auf low setzen
20
21
     if(get_timer1() >= 22500) //22500 entspricht 18ms
22
     {
23
         set_timer1(0);
24
         output_high(PIN_C2);
25
     }
26
     if(kbhit()) empfangen = getc();  //zeichen wird gelesen, in empfangen gespeichert
27
     if(empfangen == 'a') pos == 2500;  //2ms  
28
     if(empfangen == 'w') pos == 18750; // 1,5ms-Mittelstellung  
29
   }
30
}

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

von ... .. (docean) Benutzerseite


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)
1
ISR ()
2
{
3
 if(!zaehler)
4
   pin_high();
5
6
 if(zaehler==wertservo)
7
   pin_low();
8
9
 if(zaehler==20000)
10
   zaehler=0;
11
12
  zaehler++;
13
}

von dewey (Gast)


Lesenswert?

hallo jan-h. B.!

danke für deine antwort!

das heißt also das mein code folgendermaßen aussieht:
1
#include <16F877a.h>
2
#include <stdlib.h>
3
#use delay (clock=20000000)
4
#fuses HS, NOWDT, NOPROTECT, NOLVP
5
#use RS232(baud=9600,xmit=PIN_C6,rcv=PIN_C7)
6
7
int16 zaehler = 0;
8
int16 pos = 1875;  // Servo mittelstellung (1,5ms)
9
10
#INT_TIMER1  //wodurch soll diese isr ausgelöst werden
11
void isr()   // jetzt würde diese isr() ja auch immer nur ausgeführt
12
{            // wenn der timer1 einen überlauf hat
13
 
14
 if(zaehler == 0)
15
   output_high(PIN_C2);
16
17
 if(zaehler == pos)
18
   output_low(PIN_C2);
19
20
 if(zaehler == 20000)
21
   zaehler=0;
22
23
  zaehler++;
24
}
25
26
void main (void)
27
{
28
   char empfangen;
29
30
   setup_timer_1(T1_INTERNAL | T1_DIV_BY_4);
31
   
32
   set_timer1(0);         //timer wird auf 0 gesetzt
33
   output_high(PIN_C2);   //ausgang wird auf high gesetzt
34
35
   while(true)
36
   {   
37
     if(kbhit()) empfangen = getc();  //zeichen wird gelesen, in    empfangen gespeichert
38
     if(empfangen == 'a') pos == 2500;  //2ms  
39
     if(empfangen == 'w') pos == 1250; // 1ms  
40
   }
41
}

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

von dewey (Gast)


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

von dewey (Gast)


Lesenswert?

hallo!

also hab jetzt mal einen code geschrieben der aller 0.1ms einen 
interrupt auslöst.
1
#include <16F877a.h>
2
#device ADC=10      //10bit Auflösung
3
#use delay (clock=20000000)
4
#fuses HS, NOWDT, NOPROTECT, NOLVP
5
#use RS232(baud=9600,xmit=PIN_C6,rcv=PIN_C7)
6
#include <stdlib.h>
7
8
int16 pos = 1000;
9
int16 zaehler = 0;
10
11
#INT_TIMER0
12
void isr()
13
{
14
   zaehler++;
15
   if(zaehler == pos)
16
     output_low(PIN_C2);   
17
   if(zaehler == 20000)
18
     zaehler=0;
19
   if(zaehler == 0)
20
     output_high(PIN_C2);
21
}
22
23
void main (void)
24
{
25
   char empfangen;
26
27
   setup_timer_2(T2_DIV_BY_4, 125, 1);
28
   set_timer2(0);
29
   output_high(PIN_C2);
30
  
31
   enable_interrupts(INT_TIMER2);
32
   enable_interrupts(GLOBAL);
33
   while(true)
34
   {   
35
     if(kbhit()) empfangen = getc();
36
     if(empfangen == 'a') pos == 2000;  //2ms  
37
     if(empfangen == 'd') pos == 1000; // 1ms  
38
     if(empfangen == 's') pos == 1500; // 1.5ms  
39
   }
40
41
}

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

von ... .. (docean) Benutzerseite


Lesenswert?

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

geht besser:
1
if(kbhit())
2
{
3
 empfangen=getc();
4
5
 switch (empfangen)
6
 {
7
   case 'a':
8
    pos=2000;
9
    break;
10
   case 'd':
11
    pos=1000;
12
    break;
13
 }
14
}

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

von dewey (Gast)


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

von dewey (Gast)


Lesenswert?

jaaaa natürlich da hast du recht!! das doppelte == ist natürlich 
falsch!!!

danke
mfg

von dewey (Gast)


Lesenswert?

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

mfg

von dewey (Gast)


Lesenswert?

hallo

wollt euch nur mitteilen das ich das problem gelöst habe
anbei der richtige code! funktioniert einwandfrei!!
1
#include <16F877a.h>
2
#use delay (clock=20000000)
3
#fuses HS, NOWDT, NOPROTECT, NOLVP
4
#use RS232(baud=9600,xmit=PIN_C6,rcv=PIN_C7)
5
#include <stdlib.h>
6
7
int8 pos;
8
int8 zaehler = 0;
9
10
#INT_TIMER2
11
void isr()
12
{
13
   zaehler++;
14
   
15
   if(zaehler == 180)
16
   {
17
     zaehler = 0;
18
     output_high(PIN_C2);
19
   }
20
   if(zaehler == pos)
21
     output_low(PIN_C2);   
22
}
23
24
25
void main (void)
26
{
27
   char empfangen = 'a';
28
29
   setup_timer_2(T2_DIV_BY_4, 125, 1);
30
   set_timer2(0);
31
     
32
   enable_interrupts(INT_TIMER2);
33
   enable_interrupts(GLOBAL);
34
    
35
   while(true)
36
   {   
37
    if(kbhit())
38
     {
39
        empfangen=getc();
40
41
        switch (empfangen)
42
        {
43
           case 'a': pos=20;
44
                     break;
45
           case 'd': pos=10;
46
                     break;
47
           case 's': pos=15;
48
                     break;
49
        }
50
      }
51
   }
52
}

@jan.h danke das du mir geholfen hast!

mfg dewey

von dewey (Gast)


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!
1
#include <16F877a.h>
2
#use delay (clock=20000000)
3
#fuses HS, NOWDT, NOPROTECT, NOLVP
4
#use RS232(baud=9600,xmit=PIN_C6,rcv=PIN_C7)
5
#include <stdlib.h>
6
7
int8 servopos;
8
int8 zaehler = 0;
9
10
#INT_TIMER2
11
void timer2overflow()
12
{
13
   zaehler++;
14
   if(zaehler == 180)
15
   {
16
      zaehler = 0;
17
      output_high(PIN_B0);    
18
   }   
19
   if(zaehler == servopos)
20
   {
21
      output_low(PIN_B0);
22
   }       
23
}
24
25
#INT_TIMER0
26
void timer0overflow()
27
{
28
   printf("\fMessdaten\n");  
29
}
30
31
void main (void)
32
{
33
   char empfangen;
34
   
35
   setup_timer_2(T2_DIV_BY_4, 125, 1);
36
   set_timer2(0);
37
38
   setup_timer_0(RTCC_INTERNAL);
39
   set_timer0(0);
40
41
   enable_interrupts(INT_TIMER2);
42
   enable_interrupts(INT_TIMER0);
43
   enable_interrupts(GLOBAL);
44
    
45
   while(true)
46
   {  
47
      if(kbhit()) empfangen = getc();
48
      servopos = empfangen - 60;      
49
   }
50
}

Danke im Voraus

mfg dewey

von ... .. (docean) Benutzerseite


Lesenswert?

ein printf in einer ISR ist bäh...

schalt mal ne LED an oder aus...

von dewey (Gast)


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??

von ... .. (docean) Benutzerseite


Lesenswert?

setz nur ne var. in der isr...und das printf machst in main...
1
#INT_TIMER0
2
void timer0overflow()
3
{
4
   senden=1;  
5
}
6
7
void main (void)
8
{
9
   ... 
10
   while(true)
11
   {  
12
      if(kbhit()) empfangen = getc();
13
      servopos = empfangen - 60;      
14
15
      if(senden)
16
      {
17
           printf("bla");
18
           senden=0;
19
       }
20
   }
21
}

von dewey (Gast)


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

von dewey (Gast)


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

von ... .. (docean) Benutzerseite


Lesenswert?

printf verbraucht massig Rechenzeit...

ISRs sollten möglichst kurz sein...

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

von dewey (Gast)


Lesenswert?

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

mfg

von gast (Gast)


Lesenswert?

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

von dewey (Gast)


Lesenswert?

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

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.