Forum: Mikrocontroller und Digitale Elektronik ATmega8 mit Timer1 Servo steuern


von Daniel P. (callmed)


Lesenswert?

Hallo, hab wieder mal eine Frage - und JA auch ich habe die vielen Servo 
Einträge bereits gelesen!

Ich verwende ATMEGA8 mit 8MHz intern. Die Servo-Signalleitung hängt an 
PD3 und ist als Ausgang konfiguriert. Als Servo-Versorgung benutze ich 
4x1.5V Batterien, sollte doch genügen. Wenn ich eine LED an PD3 hänge 
sehe ich das flackern, kann auch die Werte per Tastendruck ändern und 
die LED flashed dementsprechend anders. Nur leider bekomme ich bei 
selben Programm und angeschlossenen Servo NULL Veränderung der 
Servostellung.

Timer1 mit 16bit verwende ich prescale=8. Timer beginnt 22ms vor 
Überlauf und PD3=H. Bei CompareA wird PD3=L, dies geschieht 1.5ms nach 
dem Startwert.

Lt. meinem ServoSignalverständnis muss zwischen 1. und 2. ms ein High 
auf Low Pegelwechsel geschehen. Danach 20 ms gleichen Pegel bis wieder 
von vorne beginnt -> Periodendauer von 22ms! (lt. Roboternetz Zeitplan)

fclk=8MHZ/8 -> 0,001ms
2ms/0,001ms = 2000 zyk -> 20ms = 20 000 zyk

Timer läuft bis 2^16 = 0...65535 -> 65535 - 20 000 = 45535
45535 - 2000 (2ms) = 43535, was der Startwert ist.

43 535 = AA0F = Startwert
44 535 = B1DF = Startwert + 1ms
45 035 = = Startwer + 1.5ms = Servomitte
45 535 = ADF7 = Startwert + 2ms
65 535 = FFFF = Startwert + 22ms

Zu den Registereinträgen:
/* timer1 for PWM (16bit) */
/*8 MHz Chip clock, appr. 1° -> 0,004ms per clock  */
  TCNT1H  =0xAA;  // start @ 43535
  TCNT1L  =0x0F;
  TIMSK  |=(1<<OCIE1A) | (1<<TOIE1);  // compare A + overflow interrupt 
enable
  OCR1AH  =0xAF;  // 1,5ms = 45035
  OCR1AL  =0xEB;
  TCCR1A = 0x00;
  TCCR1B  |=(1<<CS11);  // clk/8
  SERVO_ON;


Danke

von Peter B. (pbuenger)


Lesenswert?

Hai Daniel,

mal unabhängig von der Frage, wer denn Deinen Timer nach einem Überlauf 
wieder auf 0xAA0F setzt: Warum machst Du Dir die Sache so kompliziert?

Einfacher Weg: "Fast PWM Mode", "Bottom" ist hier automatisch immer 
Null, "Top" kannst Du in ICR1 setzen, in Deinem Fall also auf 22000. Die 
Werte für OCR1 kannst Du dann einfach in usec einsetzen, also 1500 für 
die Mittelstellung. Feddisch!

Gruß,
Peter

von STK500-Besitzer (Gast)


Lesenswert?

>Timer beginnt 22ms vor Überlauf

Warum? Es gibt Timer-Modi, die dafür besser geeignet sind, als ein 
manuelles Nachladen des Timers, da sie das ganz alleine machen.
Der einzige Nachteil bei diesen Modi ist, dass man an bestimmte Portpins 
gebunden ist, wenn man die Servo-PWM komplett in Hardware ausgeben will.

von Daniel P. (callmed)


Lesenswert?

STK500-Besitzer schrieb:
>>Timer beginnt 22ms vor Überlauf
>
> Warum? Es gibt Timer-Modi, die dafür besser geeignet sind, als ein
> manuelles Nachladen des Timers, da sie das ganz alleine machen.
> Der einzige Nachteil bei diesen Modi ist, dass man an bestimmte Portpins
> gebunden ist, wenn man die Servo-PWM komplett in Hardware ausgeben will.

Danke, für den Tipp und leider schreibst du auch schon das Problem dass 
bei der Verwendung der speziellen Modi kommt - die vorgegebenen PINS. 
PD3 ist der letzte Pin und vorgesehene für  SERVO.

Zum Nachladen der Werte: Werden in der ISR overflow wieder auf Startwert 
gesetzt und PD3 wieder HIGH.
Bei ISR compareA wird PD3 LOW.

von Karl H. (kbuchegg)


Lesenswert?

Trotzdem sollte ein CTC-Modus mit ISR für Overflow und einem Compare 
Match einfacher sein. Zumindest brauchst du dich nicht ums Nachladen 
kümmern und musst auch nicht vom 'oberen' Ende des Zahlenbereichs 
wegrechnen.

> Danach 20 ms gleichen Pegel bis wieder
> von vorne beginnt -> Periodendauer von 22ms!

Das ist ein Missverständnis. Ein kompletter Zyklus dauert 20ms. Wobei 
diese Zeit bei vielen alten Servos keine Tragik ist. Die 20ms haben sich 
aus dem Impulstelegram auf der Funkstrecke ergeben. Den Servos sind die 
20ms meistens egal, die interessieren sich nur für die Pulsbreite.

von Hannes L. (hannes)


Lesenswert?

> Den Servos sind die
> 20ms meistens egal, die interessieren sich nur für die Pulsbreite.

Das kann ich bestätigen. Ich steuere z.B. die Servos eines 
Modellbahn-Schrankenantriebs alle 64ms an, um die Stromaufnahme der 
Servos klein zu halten.

Einen schon etwas älteren, aber gut funktionierenden 
Servoimpuls-Generator mit Mega48 findest Du hier:
http://www.hanneslux.de/avr/mobau/7ksend/7ksend02.html

...

von Daniel P. (callmed)


Lesenswert?

Danke für die vielen Beiträge.

Vielleicht kann mir der eine oder andere noch mehr helfen.

Wills jetzt mit dem CTC Modus versuchen, jedoch mit folgenden 
unveränderbaren Vorgaben!
1.) Servo-Steuerleitung ist PD3
2.) Atmega läuft mit 8 MHZ und Timer1 prescale = 8
3.) Servo soll alle 20ms neuen Impuls bekommen

Hab nun im Manual nachgelesen und folgende Registerwerte festgelegt.

TIMSK  |=(1<<OCIE1A) | (1<<TOIE1);
OCR1A = Impulsdauer;
TCCR1A |=(1<<WGM11) | (1<<WGM10);
TCCR1B |=(1<<CS11) | (1<<WGM13) | (1<<WGM12);
PD3_EIN;

TCNT1 müsste doch bei 0 beginnen und PD3 HIGH. Bei TCNT1 = OCR1A wird 
PD3 LOW. Dies sollte nach 1 - 2 ms der Fall sein. Danach soll der TCNT1 
noch für weitere 18ms weiterlaufen eher TCNT1 = TOP ist und PD3 wieder 
HIGH wird.
Ich check nicht wie ich TOP festlege und ob da dann im CTC Mode PD3 
automatisch wieder gesetzt wird oder manuell gemacht werden muss.

Danke

von STK500-Besitzer (Gast)


Lesenswert?

>Danke, für den Tipp und leider schreibst du auch schon das Problem dass
>bei der Verwendung der speziellen Modi kommt - die vorgegebenen PINS.
>PD3 ist der letzte Pin und vorgesehene für  SERVO.

Dann kann man die Sache halt nicht komplett in Hardware machen, sondern 
muss auf die ISR der jeweiligen Interrupts zurückgreifen

>TCNT1 müsste doch bei 0 beginnen und PD3 HIGH.
richtig

>Bei TCNT1 = OCR1A wird PD3 LOW.
richtig.

>Dies sollte nach 1 - 2 ms der Fall sein.
>Danach soll der TCNT1 noch für weitere 18ms weiterlaufen eher TCNT1 = TOP ist 
>und PD3 wieder HIGH wird.
Auch richtig.

>Ich check nicht wie ich TOP festlege und ob da dann im CTC Mode PD3
>automatisch wieder gesetzt wird oder manuell gemacht werden muss.

Es gibt zwei CTC-Modi: Mode 4 und 12
Im Mode 4 wird OCR1A als TOP verwendet und im Mode 12 ICR1.
Wenn diese Vergeichswerte erreicht werden, wird der Timer auf 0 
zurückgesetzt und ein Timer-Overflow-Interrupt ausgelöst.
In der ISR zu diesem Interrupt kann man dann PD3 auf 1 setzen.
Die Pulsdauer legt man durch den Wert von OCR1B fest.
Auch hierzu gibt es einen Interrupt-Vektor. In der ISR zu OCR1B wird PD3 
= 0 gesetzt.
Hab ich irgendwann schon mal lauffähig (in C) hinbekommen.
Wie Hannes schon anmerkte: Servos stört eine größere Pause nicht 
unbedingt.
Die Ansteuer-Periode kann auch kürzer sein. Wichtig ist nur die 
Pulslänge. Weder die Periodendauer, noch der Tastgrad sind wirklich 
relevant.

von spess53 (Gast)


Lesenswert?

Hi

>Wenn diese Vergeichswerte erreicht werden, wird der Timer auf 0
>zurückgesetzt und ein Timer-Overflow-Interrupt ausgelöst.

Nein. In den CTC-Modes wird wird der Overflow-Interrupt bei MAX (65535) 
ausgelöst. Hier muss anstelle des Overflow-Ints der OCR1A- bzw. der 
ICR-Interropt benutzt werden.

MfG Spess

von Daniel P. (callmed)


Lesenswert?

Hi,

zu deiner Antwort:

> Es gibt zwei CTC-Modi: Mode 4 und 12
> Im Mode 4 wird OCR1A als TOP verwendet und im Mode 12 ICR1.
> Wenn diese Vergeichswerte erreicht werden, wird der Timer auf 0
> zurückgesetzt und ein Timer-Overflow-Interrupt ausgelöst.
> In der ISR zu diesem Interrupt kann man dann PD3 auf 1 setzen.

JA soweit komm ich auch, wann bzw. was nehm ich dann aber als 
Signalende?
Mit deiner vorgeschlagenen Variante bekomme ich ja nur den Impuls von 
1-2ms hin.
     1-2ms   10-65ms
       --              --
... __|  |____________|  |_______ ...

      1  2      3     1  2    3

1... Start in Mode 4 od. 12 ist egal, da nur andere Register TOP sind
2... Timer erreicht TOP, hier setz ich mit ISR_TOP PD3 = LOW
     Leider fängt aber hier der Timer auch wieder mit NULL an und ich 
wäre  wieder bei 1 statt bei 3!

Trennt ihr etwa das Steuersignal in zwei Teile auf - Impulsdauer + 
Wartezeit?
Sprich Timer mit zwei konfigurationen während eines ganzen Impuls laufen 
lassen?

von Hannes L. (hannes)


Lesenswert?

> Trennt ihr etwa das Steuersignal in zwei Teile auf - Impulsdauer +
> Wartezeit?

Das wäre die eine Möglichkeit. Einfacher ist es, Impulsdauer und 
Periodendauer separat zu behandeln.

Die Periodendauer (Impulszeit + Impulspause) ergibt sich aus dem 
Zählumfang des Timers und kann in einem der CTC-Modi mittels der 
ICR1-Register gesetzt werden.

Die Impulsdauer kann dann mit einem der OCR1-Register festgelegt werden.

Man kann die Periodendauer (Top) aber auch mit OCR1A festlegen und mit 
OCR1B die Impulsdauer.

Es kann der zugehörige OC-Pin eingebunden werden (Clear by Compare, Set 
by Bottom). Man kann aber auch ISRs aufrufen lassen und in diesen einen 
beliebigen Pin per Programm setzen und löschen.

Das Ganze funktioniert aber auch mit dem freilaufenden Timer, indem man 
in der Compare-ISR das Compare-Register ausliest, das Intervall dazu 
addiert und als nächsten Interrupt-Termin ins Compare-Register zurück 
schreibt. Dies hat den Vorteil, dass sich die andere Compare-Einheit um 
weitere zeitkritische Dinge kümmern kann und die ICP-Einheit nebenher 
auch noch exakt von außen kommende Impulse messen kann.

Das letzte Verfahren (Addieren des Intervalls auf den aktuellen 
Compare-Wert) hat den weiteren Vorteil, dass man damit auch mehrere 
Servos mit nur einer Compare-Einheit ansteuern kann. Man zählt in der 
ISR einen Zähler hoch, den man als Index (auf RAM-Array) für die Zeit 
bis zum nächsten Interrupt und das Bitmuster für die Servoimpulsausgänge 
nutzt. Man richtet (im Array und Zählumfang des Index) einen Datensatz 
mehr ein als man Servos hat und gibt dem Dummy-Datensatz die Restzeit 
des Servo-Zyklusses. So werden im Hintergrund (Interrupt nur einer 
Compare-Einheit) sämtliche Servoimpulse generiert, während sich die 
Mainloop ganz gemütlich neben anderen Aufgaben um die Bereitstellung der 
Positionsdaten (im SRAM-Array) kümmert. Wer es ganz genau haben will, 
korregiert bei jeder Impulsdauer-Änderung einfach die Restzeit mit 
(Subtraktion der Impulsdauer aller Kanäle vom Fixwert für 20ms). Wer es 
noch genauer haben will, schreibt die neuen Daten in einen Puffer 
(zweites Array) und kopiert sie dann in der ISR im Dummy-Takt, was auch 
nebenbei das Problem mit dem atomaren Zugriff auf die 16-Bit-Werte 
erschlägt. Wie bereits gesagt, stehen dabei der andere 
Compare-Interrupt, der Input-Capture-Interrupt und notfalls auch der 
Überlauf Interrupt (ohne Timer-Preload!!) für andere Zwecke zur 
Verfügung.

Viele Wege führen zum Ziel, der optimale Weg richtet sich nach den 
Randbedingungen, einen "ultimativen einzig richtigen Weg" gibt es nicht.

...

von STK500-Besitzer (Gast)


Lesenswert?

Hab ich mich falsch ausgedrückt?
PD3 wird bei OCR1B gelöscht und bei TOP gesetzt.

     1-2ms   10-65ms
       --              --
... __|  |____________|  |_______ ...

      1  2      3     1  2    3

1: TOP      ==> PD3 setzen
2: OCR1B    ==> PD3 löschen
3: hat keinen Namen, braucht auch keinen

von Daniel P. (callmed)


Angehängte Dateien:

Lesenswert?

Hallo zusammen, ich nochmal.

Hab anscheinend das Problem teilweise gelöst. Ich fahr nun mit Timer1 
und COMPA und COMPB. Wobei COMPA = TOP und COMPB die Impulsdauer 
darstellen.
Mit Teiler 8 komme ich auf 1us / Timertakt -> Impulsdauer zwischen 1000 
und 2000 sowie Periodendauer von 10 000 bis 65 000.

Mit einer angeschlossenen LED funktioniert das toll, flashed schön und 
schaut richtig aus (Oszi keines da!).

Wenn ich aber nun den Servo, marke billigshofer anschließe bewegt sich 
nichts! Die Servoversorgung kommt aus 4x 1.5V AAA Batterien, die auf 
Braun und Rot angeschlossen sind. Das Signal sollte laut Herrsteller auf 
das Orange Kabel gehen - so geschieht auch aber nix.

Was mit noch aufgefallen ist ist dass bei Versorgen, der Servo immer in 
eine Endlagenrichtung geht, egal ob Steuerleitung angeschlossen ist oder 
nicht.
Dachte schon das vielleicht eine andere Ltg die Steuerleitung ist, aber 
dann macht der Servo recht komische Geräusche ;-)

Hab euch mal die main.c und Aufbau-Foto angehängt vielleicht hab ja doch 
noch einen fehler,
Danke

von Hannes L. (hannes)


Lesenswert?

> Mit einer angeschlossenen LED funktioniert das toll, flashed schön und
> schaut richtig aus (Oszi keines da!).

Naja, bei 2 ms alle 20 ms "flasht" da eigentlich nix, 50 Hz erkennt das 
Auge fast noch als Dauerlicht. Ich vermute, dass Deine Impulsbreite 
nicht stimmt, weil Dein Controller nicht mit dem Takt läuft, den Du 
wünscht bzw. vermutest.

Geh' mal davon aus, dass ein neuer (unumgefuster) Mega8 mit 1 MHz 
internem Takt arbeitet. Nimm den Timer-Vorteiler versuchsweise mal raus 
und schau, ob das Servo immernoch auf Anschlag fährt. Das wäre dann ein 
Beweis, dass/ob der Mega8 mit 1MHz tuckert.

Weiterhin besteht die Möglichkeit, dass Dir aufgrund von Störungen der 
Mega8 abstürzt, bzw. einen Reset ausführt.

- Sind die Betriebsspannungen von AVR und Servo sauber entkoppelt?
- Hast Du den obligatorischen 1k-Widerstand in Reihe in der
  Impulsleitung?
- Sind am AVR alle 100n-Abblock-Kerkos (direkt an den Anschlusspins)
  vorhanden?
- Welche Aufgabe hat der SMD-AVR auf dem Aufsteck-Board des STK500?
  könnte der stören? (es soll nur Einen geben)

...

von Paul Baumann (Gast)


Lesenswert?

>(es soll nur Einen geben)

Nein, es kann nur Einen geben.

http://de.wikipedia.org/wiki/Highlander_%E2%80%93_Es_kann_nur_einen_geben

;-)
MfG Paul

von STK500-Besitzer (Gast)


Lesenswert?

1
#define SERVO_ON  ( PORTD &= ~(1<<SERVO) )
2
#define  SERVO_OFF  ( PORTD |= (1<<SERVO) )

ist fürs Servo ungeeignet.

sollte wohl eher so aussehen:
1
#define  SERVO_OFF  ( PORTD &= ~(1<<SERVO) )
2
#define SERVO_ON  ( PORTD |= (1<<SERVO) )

Umgekehrt halt...

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.