Servos und PWM

#845806
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???
Gast #845878
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.
#846119
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.

...
#846270
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.

...
#846319
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
#847555
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.
#847820
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
#847885
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?
#1183733
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.
Gast #1184037
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.
Gast #1184871
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.

...

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren