Forum: Mikrocontroller und Digitale Elektronik Servos und PWM


von Sonke A. (soeni)


Lesenswert?

ich habe ein kleines Programm geschrieben, welches über zwei taster ganz 
simpel einen servo einmal nach links und einmal nach rechts ausschlagen 
läßt. das nacch links ausschlagen funktioniert auch soweit. (ich hatte 
in einem beitrag gelesen, das 2ms voller ausschlag zur einen und 1ms 
voller ausschlag zur anderen seite ist.) nach rechts jedoch bleibt er 
ungefär in mittelstellung stehen. kein problem dachte ich mir, 
verringerst du die zeit unter ein ms. hier trat folgendes problem auf. 
bei 0 ms verzögerung drehte sich der servo hin und her, als ich wenn ich 
anstatt der funktion _delay _ms(); _delay_us(); nehme mechert avr studio 
rum. er will die ganze zeit neu kompilieren und so. diese funktion 
benötige ich aber spätestenz, wenn ich zwischenstellen zwischen 
ausschlag rechts und ausschlag links haben möchte.

hier erstmal mein code:
1
#define __OPTIMIZE__ // Code Optimieren für richtige verzögerungszeit
2
3
#define F_CPU 8000000
4
 
5
6
#include <avr/io.h>
7
#include <util/delay.h>
8
9
10
int8_t flag;
11
12
13
void left(){  // Funktion linksdrehung
14
15
  _delay_ms(20); 
16
17
  PORTB |= (1 << 0);    /* setzt Bit 0 an PortC auf 1 */
18
19
  _delay_ms(2); 
20
21
  PORTB &= ~(1 << 0);   /* loescht Bit 0 an PortC */
22
23
  _delay_ms(20);
24
25
  PORTB |= (1 << 0);    /* setzt Bit 0 an PortC auf 1 */
26
27
  _delay_ms(2); 
28
29
  PORTB &= ~(1 << 0);   /* loescht Bit 0 an PortC */
30
31
}
32
33
void right(){  // funktion rechtsdrehung
34
35
  _delay_ms(20); 
36
37
  PORTB |= (1 << 0);    /* setzt Bit 0 an PortC auf 1 */
38
39
  _delay_us(1000);  // 1 ms warten für rechtsdrehung
40
41
  PORTB &= ~(1 << 0);   /* loescht Bit 0 an PortC */
42
43
  _delay_ms(20);
44
45
  PORTB |= (1 << 0);    /* setzt Bit 0 an PortC auf 1 */
46
47
  _delay_us(1000);   // 1 ms warten für rechtsdrehung
48
49
  PORTB &= ~(1 << 0);   /* loescht Bit 0 an PortC */
50
51
}
52
53
54
55
int main(){
56
57
58
59
60
DDRB = 0xff;
61
DDRD = 0x00;
62
63
while(1==1){
64
65
  while(!(PIND & (1 << 0))){  // bit 1 wird abgefragt gesezt (Taster abfragen)
66
  _delay_ms(100); 
67
68
     left(); //(links drehen)
69
 
70
  }
71
72
73
  while(!(PIND & (1 << 1))){  // bit 2 wird abgefragt gesezt (Taster abfragen)
74
  _delay_ms(100); 
75
 
76
     right();  //(rechts drehen)
77
 
78
  }
79
80
81
82
83
}
84
85
86
}

hier die Fehlermeldung:


avr-gcc.exe  -mmcu=atmega8515 -Wall -gdwarf-2 -O0 -MD -MP -MT servo.o 
-MF dep/servo.o.d  -c  ../servo.c
c:/winavr-20071221/bin/../avr/include/util/delay.h: In function 'right':
c:/winavr-20071221/bin/../avr/include/util/delay.h:143: sorry, 
unimplemented: inlining failed in call to '_delay_ms': function not 
considered for inlining
c:/winavr-20071221/bin/../avr/include/util/delay.h:116: sorry, 
unimplemented: called from here
c:/winavr-20071221/bin/../avr/include/util/delay.h:143: sorry, 
unimplemented: inlining failed in call to '_delay_ms': function not 
considered for inlining
c:/winavr-20071221/bin/../avr/include/util/delay.h:116: sorry, 
unimplemented: called from here

ich benutze das stk500 mit einem 8515 und programmiere in c.



Mach ich villeicht bei der PWM was falsch???

von STK500-Besitzer (Gast)


Lesenswert?

Die Delay-Funktion (kann auch ein Makro sein...) hat einen möglichen 
Wertebereich, der von der Taktfrequenz abhängt.
Möglicherweise hängt dein Problem damit zusammen.
Das vermute ich aber auch nur. Mit der Funktion habe ich noch nie 
gearbeitet,  da es meiner Meinung nach einfacher ist, einen Timer 
entsprechend zu konfigurieren.

von Sonke A. (soeni)


Lesenswert?

gut ich werde das mal probieren. hab ich noch nie gemacht mal sehen 
danke erstmal

von Hannes L. (hannes)


Lesenswert?

http://www.hanneslux.de/avr/mobau/7ksend/7ksend02.html

Ist zwar in Assembler, erklärt aber, wie man Impulse für Servos in 
Software (also ohne Nutzung der Hardware-PWM) erzeugen kann.

...

von Olaf D. (Firma: O.D.I.S.) (dreyero)


Lesenswert?

Hallo,

ich baue auch gerade eine Servosteuerung.
Die PWM muß aber eine Periode von 20ms und ein Pulsbreite von 1 bzw. 2 
ms haben. Also _delay_ms(100) kann nicht funktionieren.

Gruß

Olaf

von STK500-Besitzer (Gast)


Lesenswert?

>Die PWM muß aber eine Periode von 20ms und ein Pulsbreite von 1 bzw. 2
>ms haben.

Nee, muß sie nicht!

von Olaf D. (Firma: O.D.I.S.) (dreyero)


Lesenswert?


von Hannes L. (hannes)


Lesenswert?

STK500-Besitzer wrote:
>>Die PWM muß aber eine Periode von 20ms und ein Pulsbreite von 1 bzw. 2
>>ms haben.
>
> Nee, muß sie nicht!

Stimmt... Es schadet aber trotzdem nicht, so zu tun, als käme der Impuls 
aus einem handelsüblichen RC-Empfänger.

-------------

Und lasst den Blödsinn mit den Warteschleifen, das bisschen 
Servo-Impulstelegramm erzeugt ein AVR ganz nebenbei in einer 
State-machine im Timer-Interrupt, wobei nur wenige Prozent 
Rechenleistung beansprucht werden. Der Mainloop steht also weiterhin 
fast die gesamte Rechenleistung zur Verfügung.

...

von Sonke A. (soeni)


Lesenswert?

gibts dazu ne gute anleitung oder ein tutorial, weil ich weder mit 
timern noch mit externen interrupts bei einem avr gearbeitet habe.

von Hannes L. (hannes)


Lesenswert?

Sönke Paschko wrote:
> gibts dazu ne gute anleitung oder ein tutorial, weil ich weder mit
> timern noch mit externen interrupts bei einem avr gearbeitet habe.

Ist das jetzt Dein Ernst???

Im oben genannten Link wird der Programmablauf mit normalem Deutschen 
Text beschrieben. Der beiliegende Quelltext ist sehr üppig kommentiert, 
den versteht man auch ohne fundierte ASM-Kenntnisse. Ein Blick ins 
Datenblatt des AVRs ist allerdings nötig, denn wenn man die Architektur 
des verwendeten Controllers nicht kennt, kann man auch das beste 
Programm oder Tutorial nicht verstehen.

...

von Peter B. (pbuenger)


Lesenswert?

Um zwei Servos an den OC1A und OC1B Pins eines Tiny 2313 zu betreiben 
reicht schon eine Handvoll Codezeilen:
1
// Servo Frame-Time:
2
#define FRAME_TIME 20 // msec
3
4
// init timer 1:
5
ICR1   = FRAME_TIME * 1000;                      // PWM cycle time in usec
6
TCCR1A = (1<<COM1A1) | (1<<COM1B1) | (1<<WGM11); // OC1A/B clr on match, set on TOP
7
TCCR1B = (1<<WGM13) | (1<<WGM12) | (1<<CS11);    // TOP = ICR1, clk = sysclk/8 (->1us)
8
TCNT1  = 0;                                      // reset Timer
9
10
OCR1A  = 1000;    // set Servo 1 to 1ms-position
11
OCR1B  = 2000;    // set Servo 1 to 2ms-position

Durch die Verwendung der Output-Compare-Funktion des 16bit-Timers 1 
bleiben die Servos gesteuert, ohne dass der Prozessor auch nur einen 
einzigen Befehl dazu abarbeiten müsste. Er muss lediglich die 
OCR-Register passend beschreiben, wenn die Servos bewegt werden sollen.

Gruß,
Peter

von Sonke A. (soeni)


Lesenswert?

so damit hast du jezt den timer gestartet oder was???

was ist OCR1A?
was ist OCR1b?

wan wird ein Pin für pwm beschrieben, oder muss ich das selbst machen 
wenn ja wann?

zum Datenplatt, im datenblatt steht nur, das man einen prescaler 
benutzen kann, jedoch weder wie man ihn benutzt noch wie man den timer 
initialisiert.

von Peter B. (pbuenger)


Lesenswert?

> so damit hast du jezt den timer gestartet oder was???
Mal nebenbei: Wenn Du hilfreiche Antworten haben willst, darfst Du auch 
gerne einen Tick freundlicher sein. Danke.

Zum Timer: Guck Dir mal im Datenblatt den Teil vom Timer 1 an, speziell 
den "Fast PWM Mode". Dort steht genau beschrieben, wie die Ausgangspinne 
OC1A und OC1B gesteuert werden.

Der Timer selbst läuft immer zwischen Null und einer Obergrenze. Hat er 
die erreicht, fängt er wieder bei Null an. Bei meiner Konfiguration gibt 
ICR1 die Obergrenze vor, welche ich auf 20000 gesetzt habe. Programmiert 
man jetzt noch den Vorteiler auf 8, so rennt der Timer bei einem 8MHz 
Quartz in 1us-Schritten von Null bis 20ms, was genau der 
RC-Wiederholungsrate entspricht.

Die Compare Register steuern die Ausgangspins: Beim Sprung des Timers 
auf Null werden sie gesetzt, überschreitet der Timers die Werte in 
OCR1A/OCR1B, wird der entsprechende Pin auf low gesetzt. Du musst halt 
nur in OCR1A/OCR1B eine Zahl zwischen 1000 und 2000 eintragen, das gibt 
dann die gewünschten 1...2ms Pulsbreite mit 20ms Abstand.

Ach ja, die Pins OC1A und OC1B müssen unbedingt als Ausgänge gesetzt 
sein.

Gruß,
Peter

von Sonke A. (soeni)


Lesenswert?

danke, ich meinte das nicht böse oben,dein text ist echt super so hab 
ichs verstanden.

einen nachteil hat das doch aber weil oc1a an ein pin gekoppelt ist und 
sich damit also nur ein servo steuern läßt. kann man das nicht so lösen, 
das eine Interruptrutine aufgerufen wird, die dann die Pins steuert, 
sodass auch mehrere Pins geschaltet werden können?

von Peter B. (pbuenger)


Lesenswert?

Ja, und mit OC1B steuerst Du ein zweites Servo. Wenn's mehr sein soll, 
brauchst Du entweder einen AVR mit mehr 16-Bit PWMs oder eine 
Softwarelösung. Du hattest aber in Deinem Initialposting auch nur von 
einem Servo geschrieben.

Gruß,
Peter

von Gustav K. (hanibal)


Lesenswert?

hallo, ich habe veruscht den code zu verwende, aber irgendwie 
funktiioniert der nicht.

#include <avr/io.h>

int main()
{

  DDRB = (1 << PB1 );
  DDRB = (1 << PB2 );
// Servo Frame-Time:
#define FRAME_TIME 20 // msec

// init timer 1:
ICR1   = FRAME_TIME * 1000;                      // PWM cycle time in 
usec
TCCR1A = (1<<COM1A1) | (1<<COM1B1) | (1<<WGM11); // OC1A/B clr on match, 
set on TOP
TCCR1B = (1<<WGM13) | (1<<WGM12) | (1<<CS11);    // TOP = ICR1, clk = 
sysclk/8 (->1us)
TCNT1  = 0;                                      // reset Timer

OCR1A  = 1000;    // set Servo 1 to 1ms-position
OCR1B  = 2000;    // set Servo 1 to 2ms-position


  while( 1 )
    ;
}


frequenz ist 8 mhz internal. und servo ist an pb1 angeschlossen.

von Sonke A. (soeni)


Lesenswert?

Kannst du mit nem Osziloskop mal gucken, ob die Pulsweiten stimmen? bzw. 
ob überhaupt was rauskommt

von STK500-Besitzer (Gast)


Lesenswert?

>  DDRB = (1 << PB1 );
>  DDRB = (1 << PB2 );

Demnach ist nur noch PB2 ein Ausgang.

Schreib es lieber so:

  DDRB = (1 << PB1 ) | (1 << PB2 );

>Kannst du mit nem Osziloskop mal gucken, ob die Pulsweiten stimmen? bzw.
>ob überhaupt was rauskommt

Einfacher: Häng eine LED an einen Port und lass sie im Sekundentakt 
blinken.

von Gustav K. (hanibal)


Lesenswert?

oh, wie peinlich, das hätte mir auch auffallen solln.

PS: wenn man eine led dranhängt, ans pwm signal kann man auch gucken, ob 
die gedimmt ist.

von Sonke A. (soeni)


Lesenswert?

wobei man das bei meinen LEDs nicht immer so genau sieht finde ich- aber 
Blinken ist natürlich das beste

von Hannes Lux (Gast)


Lesenswert?

Das kleine Hemdentaschenmultimeter von Pollin Best.Nr. 830 096 zum Preis 
von 12,95 EUR hat unter Anderem auch einen Frequenzmessbereich mit dem 
man die Periodendauer messen kann. Dieser lässt sich auf Tastgradmessung 
umschalten, was das Messen der relativen Impulsbreite ermöglicht. Bei 
einem Servoimpuls von 1.0 bis 2,0 ms im Abstand von 20 ms sind das 5 bis 
10 % Tastgrad.

Ich möchte weder gegen ein Oszilloskop diskutieren, noch Werbung für 
Pollin machen, aber die Anschaffung dieses kleinen schnuckligen 
Mini-Multimeters sollte auch als sparsamer Bastler drin sein. Das 
amortisiert sich schnell.

...

von Gustav K. (hanibal)


Lesenswert?

stimmt, mein multimeter hat auch duty cicle messung und frequenzmessung, 
funktioniert super, hab die werte gerade nachgerechnet, stimmt fast 100% 
genau

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.