Forum: Mikrocontroller und Digitale Elektronik Servo am AtMega8


von Adi A. (mateit)


Lesenswert?

Hallo µC-Freunde,

ich habe mal wieder ein Problem.
Und zwar möchte ich mich an diesem verschneiten Sonntag mit Servos 
beschäftigen. Dazu habe ich einen GWS IQ-200MG neben mir stehen und die 
Impulsleitung an PB1 meines AtMega8 angeschlossen. Den µC fahre ich bei 
1MHz internal Clock.
Soweit so gut.

Folgendes habe ich mir gedacht:
Der Servo erwartet ein PWM Signal mit einer Periodendauer von 20ms und 
darin einen Impuls von 1-2ms.
Um das zu realisieren habe ich das Datenblatt des Mega durchforstet und 
herausgefunden, dass ich den 16bit Timer benötige.
Der Timer zählt also von 0 an zu zählen, dabei soll er high an PB1 
(OC1A) führen. Nach 1000 Takten (also 1ms) soll er low führen - er muss 
demnach einen Compare Match Interrupt ausführen - darf dabei aber nicht 
zurückgesetzt werden.. Dann soll er bis 20.000 (20ms) zählen und erneut 
bei null anfangen - also Overflow-Interrupt.

Hier nun mein nicht funktionstüchtiger Code:
1
/***********************************/
2
/* Servo-Test                      */
3
/* 31.01.10                        */
4
/* F_CPU 1000000                   */
5
/***********************************/
6
7
#include <avr/io.h>
8
#include <avr/interrupt.h>
9
10
11
volatile unsigned char comp, overflow;
12
13
/***********************************/
14
/* init_servo function             */
15
/* sets registers                  */
16
/***********************************/
17
void init_servo()
18
{
19
  //fast pwm 10bit
20
  TCCR1A |= (1 << COM1A1) | (1 << WGM10) | (1 << WGM11) | (1 << WGM12);
21
  // prescaler 1
22
  TCCR1B |= (1 << CS10);
23
24
  //set top
25
  ICR1 = 20000; //20ms
26
27
  //drive to position
28
  OCR1A = 1000; //1ms - left stop
29
30
}
31
32
/***********************************/
33
/* main function                   */
34
/***********************************/
35
int main()
36
{
37
  //set DataDirectionRegisters
38
  DDRB |= (1 << PB1);
39
  
40
  // enable global interrupts
41
  sei();
42
43
  //start
44
  PORTB |= (1 << PB1);
45
46
  //call servo function
47
  init_servo();
48
49
  //endless loop
50
  while(1)
51
  {
52
    if( (comp == 1) || (overflow == 1) )
53
    {
54
      PORTB ^= (1 << PB1);
55
      comp = 0;
56
      overflow = 0;
57
    }
58
59
  }
60
  return 0;
61
}
62
63
64
/***********************************/
65
/* InterruptServiceRoutine         */
66
/***********************************/
67
ISR(TIMER1_COMPA_vect)
68
{
69
  comp = 1;
70
}
71
72
ISR(TIMER1_OVF_vect)
73
{
74
  overflow = 1;
75
}

Vielleicht noch zur Erläuterung:
1
  PORTB |= (1 << PB1);
nutze ich, damit ich das Bit toggeln kann.

Ein Oszilloskop besitze ich leider nicht und kann daher auch nicht 
sagen, was gerade an PB1 anliegt.

Ich verstehe einfach nicht, was daran falsch ist. Stehe ich gerade voll 
auf dem Schlauch oder wo liegt der Fehler?

Ich hoffe Ihr könnt mir helfen.
Danke schon im Voraus.


Grüße
mateit

von spess53 (Gast)


Lesenswert?

Hi

>Dann soll er bis 20.000 (20ms) zählen und erneut bei null anfangen - also 
>Overflow-Interrupt.

Für diese Stelle ist der Capture-Interrupt und nicht der 
Overflow-Interrupt zuständig.

MfG Spess

von Markus M. (Firma: EleLa - www.elela.de) (mmvisual)


Lesenswert?

Um zu wissen was da am PWM Ausgang an kommt kann man sich auch mit einem 
kleinen RC-Glied behelfen.

R 10K an Port-Pin.
Andere Ende des R an C 100nF.
Andere Ande von C an GND.

Jetzt die Spannung von C mit einem Multimeter messen.

Beispiel:
CPU läuft mit 5V
so ist bei einem PWM von 50% Ein/Aus-Dauer die Spannung am C 2,5V
bei einem PWM von 2ms/20ms (10%) ist die Spannung am C 0,5V.

Es braucht also nicht zwingend ein Oszi. Ein einfaches Multimeter reicht 
da schon.

Damit man die Frequenz von 50Hz (20ms) messen kann, kann man da auch 
einfach ein Lautsprecher (über Kondensator) anschließen. Wenn sich der 
gleich anhört wied er Netzbrumm von einem Trafo, dann sind das etwa 
50Hz.

Ein Oszi ist ja schon was feines, wenn Du öfter was machst, dann ist ein 
Oszi eine gute Hilfe, vor allem spart man sich doch viele Stunden mit 
Rätsel Raten.

von spess53 (Gast)


Lesenswert?

Hi

Noch einiges:

>  //fast pwm 10bit
>TCCR1A |= (1 << COM1A1) | (1 << WGM10) | (1 << WGM11) | (1 << WGM12);

WGM12 befindet sich in TCCR1B. Abgesehen davon willst du Timer-Mode 7 
benutzen. Der hat aber als Top $3FF (1023). Damit wird die Anweisung
ICR1 = 20000; //20ms sinnlos, da ICR1 hier überhaupt nicht mitspielt. Du 
hast hier 2 Timermodes durcheinander gebracht. Für dein Vorhaben ist 
Timer-Mode 14 sinnvoll.

MfG Spess

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.