Forum: Mikrocontroller und Digitale Elektronik ATMega16 und 4Servos


von Johannes (Gast)


Lesenswert?

Hallo!

ich wuerde gerne wissen ob ich die Forumsbeitraege bezueglich Servos
richtig verstanden habe! Hier mein Vorhaben (arbeite mit ATMega16
(16MHz), WinAVR-GCC):

Ich will insgesamt 4 Servos ansteuern; das Hauptprogramm hat einiges zu
rechnen (ca. 20 * sin/cos/atan u.a. pro Durchlauf).

Ich opfere einen Timer der mir alle 3 Sekunden einen Interrupt erzeugt
waehrend dem ich nacheinander fuer die 4 Servos einen Puls(5V) von
1ms...2ms erzeuge. Ausserhalb dieser Impulse sind die Servo-Pins
low(0V). Das ergibt eine maximale Periodendauer von 4*(3+2)=20ms und
eine minimale von 4*(3+1)=16ms. In den jeweils 3ms zwischen den
Interrupts lass ich dem uC Zeit fuer meine sin, cos usw...

richtig/falsch gedacht? prinzipielle Verbesserungen?

danke allen Erfahrenen!
mg,
Johannes

von Fritz G. (fritzg)


Lesenswert?

Ich bin mir nicht sicher, aber brauchen die Servos nicht alle 20ms den
Impuls? ich glaub bei mir war es so, sonst drehen die durch.

von Rahul (Gast)


Lesenswert?

Du könntest auch den Timer dauernd einen 1-2ms-Impuls erzeugen lassen -
das würde die Servos nicht stören. Einfach in der ISR einen neuen
OC-Wert setzen und das nächste Servo einschalten. Und das im Kreis
herum...
Die Werte würden dann in einem Feld liegen, dessen Einträge durch die
Berechnungen (vermutlich) verändert werden. Bei 4 Servos hat man dann
sogar eine Simpel-Ringpuffer-taugliche Zahl...

von Wolfram (Gast)


Lesenswert?

ich denke mal deine 3 sekunden sollen Millisekunden sein...
das mit Timer und Interrupt ist die eleganteste Lösung vergiss blos die

volatile für die Servowerte nicht.
Der Stellbereich von Servos liegt zwischen 0,8 und 2,2 ms mit 1 bis
2ms
bekommt man je nach Typ nicht die vollen 180 grad. Danach solltest du
ihnen etwas Zeit lassen.
Etwas besser ist es nur jeweils die High Zeiten für das jeweilige Servo
zu nehmen und dann eine Wartezeit bis 20ms einzulegen.
Also in Timerinterrupt:

1.
Servo1 auf 1
Lade Zeit für Servo1 in Timer
Timer starten
raus aus interrupt

beim nächsten Timerinterrupt
Servo1 auf 0
Servo2 auf 1
Lade Zeit für Servo2 in Timer
Timer starten
raus..

etc. bis zum letzten Servo

dann Servox auf 0
Lade verbleibende Zeit bis 20ms
Timer starten
raus..

danach wieder zu 1.

damit kannst du 10 Servos (bei max 2ms) betreiben und bleibst in der
Spezifikation von 20ms.
Wobei die meisten Servos auch eine längere Ruhepause akzeptieren.
Dein Mikrocontroller hat für seine Berechnungen dabei die ganze Zeit
abzüglich der paar Mikrosekunden für den Timerinterrupt.

von Johannes (Gast)


Lesenswert?

Hallo,

danke fuer eure Beitraege, ganz besonders @Wolfram!!

Ich hab mal mit dem Timer angefangen und nach vielem Probieren und
Ueberlegen nicht herausgefunden, warum mein Timer nicht geht?!
warscheinlich ists ein Trivialikus den ich ueberseh.  Die LEDs an PortC
tun gar nix; eigentlich sollten eines 1s an, und danach 1s aus sein.
Hier der Code:
#include <avr/io.h>
#include <inttypes.h>
#include <avr/interrupt.h>
#include <avr/signal.h>


#ifndef F_CPU
#define F_CPU 16000000           /* Oszillator-Frequenz in Hz */
#endif

#define UART_BAUD_RATE 9600
#define UART_BAUD_CALC(UART_BAUD_RATE,F_OSC)
((F_CPU)/((UART_BAUD_RATE)*16L)-1)

//globals
volatile uint8_t counter;

/*------Interrupt Service Routines---------------*/
/* output compare ISR, executed when TCNT1 = OCR1A*/
SIGNAL(SIG_OUTPUT_COMPARE1A)
{
counter++;
}


/*------------------------------Functions------------------------------- 
--*/
void timer16_load(uint16_t microsec)
{
//Zeitbasis fuer diesen Timer sind 0.5us, deswegen wird microsec mal
zwei genommen (um eine Stelle geshiftet)

// microsec = 2     ->  OCF1A-interrupt in 2 us
// microsec = 1000  ->  OCF1A-interrupt in 1 ms
OCR1A = (uint16_t)(microsec<<1);  //loading timer-interrupt value
}

int main(void)
{

// Timer1 (16bit) config

   //prescaler = 8; -> Zeitbasis =0.47us ~= 0.5us
   CLEARBIT(TCCR2, CS22);
   SETBIT(TCCR2, CS21);
   CLEARBIT(TCCR2, CS20);

   //setting CTC mode = clear timer on compare
   SETBIT(TCCR1B, WGM12);

   //enable output compare interrupt (when TCNT1=OCR1A)
   SETBIT(TIMSK, OCIE1A);

//other initialization stuff
 counter = 0;
 DDRC = 0XFF;
 PORTC = 0x00;

 sei();
 timer16_load(32000);  //load 32ms in den Timer
 for(;;)
 {
  if (counter>=31)   //31*32ms = 992ms ~= 1s
       SETBIT(PORTC,PC3);
  else CLEARBIT(PORTC,PC2);

  if (counter>= 62) counter=0;

 }//for(;;)

}//main()


also wer sieht den Fehler??

von Johannes (Gast)


Lesenswert?

SETBIT und CLEARBIT sollen natuerlich dasselbe bit PC2 verwenden. Das
hatte ich nur aus Testzwecken geaendert...

von Johannes (Gast)


Lesenswert?

Hallo!

also ich hab's. Es ist tatsaechlich ein Trivialikus gewesen:

Ich hab staendig vom Timer1 geredet und gelesen und gesucht und
Register gechecked bis mir ... grgrgrgr

Sollte jemand meinen obigen Code verwenden muessen folgende drei Zeilen
ersetzt werden:

CLEARBIT(TCCR2, CS22);
SETBIT(TCCR2, CS21);
CLEARBIT(TCCR2, CS20);

zu ersetztn durch:

CLEARBIT(TCCR1B, CS22);
SETBIT(TCCR1B, CS21);
CLEARBIT(TCCR1B, CS20);

mg,
Johannes

von Wolfram (Gast)


Lesenswert?

Das dürfte aber noch nicht ganz richtig sein.
Jetzt mal ohne deine Werte nachzurechnen...
Der TimerInterrupt sollte eigentlich nur kommen, wenn im
Statusregister
allgemein Interrupts erlaubt sind.(zusätzlich zu TIMSK Interruptflag)
mit CLEARBIT/SETBIT änderst du die Bits einzeln nacheinander!
d.h. du setzt 3x eine andere Zeitbasis für den Timer ,der beim ersten
setzen losläuft. Dies hat in diesem Fall, da es unmittelbar
hintereinander geschieht wahrscheinlich keinen Einfluß, aber dein Timer
läuft los ,es könnte also ein Interrupt vor deinen 32ms auftreten.
Erst die Werte setzen dann eine Taktquelle an den Timer legen,denn dann
läuft er los.

von AxelR. (Gast)


Lesenswert?

..Zitat
CLEARBIT(TCCR2, CS22);
SETBIT(TCCR2, CS21);
CLEARBIT(TCCR2, CS20);

zu ersetztn durch:

CLEARBIT(TCCR1B, CS22);
SETBIT(TCCR1B, CS21);
CLEARBIT(TCCR1B, CS20);
/Zitat

noch richtiger, obwohl das gleiche:

CLEARBIT(TCCR2, CS22);
SETBIT(TCCR2, CS21);
CLEARBIT(TCCR2, CS20);

zu ersetztn durch:

CLEARBIT(TCCR1B, CS12);
SETBIT(TCCR1B, CS11);
CLEARBIT(TCCR1B, CS10);

Beim Teilerfaktor 8 haut das alles noch hin. Dusseliger wird es, wenn
Du 256 vorteilen willst und alle drei Timer gleich initialisierst,
(weil du zB alle in deiner Applikation brauchst)
Dadurch, dass sich beim Timer2 der Faktor 1/128 "zwischenschiebt",
stimmt dann garnichts mehr.
Siehe Datenblatt
Seite 112 Tabelle 48 (Timer1 Prescaler)
und zum Vergleich
Seite 129 Tabelle 54 (Timer2 Prescaler)

Gruß
Axel

von Johannes (Gast)


Lesenswert?

Hallo,

also die Bemerkung von Wolfram bezueglich Timerinitialisierung [dreimal
SETBIT() ist ungut -> verwende anstelle TCCR1B |=
(1<<CSxx)|(1<<CSxx)|(1<<CSxx) ] leuchtet mir ein! Danke fuer den
Hinweis.

@AxelR
In meiner Applikation verwend ich tatsaechlich manchmal alle Timer, und
zwar mit ganz unterschiedlichen Teilfaktoren. Ich versteh aber leider
nicht auf welches Problem du dich beziehst?
Die Timer sind doch unabhaengig voneinander; ich kann sie doch
initialisieren wo und wann ich will ohne dass sie sich beeinflussen.
Prescaler-Faktoren sind natuerlich teilweise unterschiedlich...
Sitz ich auf der Leitung? bitte um Aufklaerung.

mg,
Johannes

von AxelR. (Gast)


Lesenswert?

Jahaa, war ja nur für die faulen..

ich hatte mir temp1(R16) mit den (für alle drei gleichen) Teilerfaktor
geladen und nacheinander die drei TCCRs damit geladen, da tanzte dann
ein Timer aus der Reihe.
ich weiss, Thema verfehlt, 6, setzen. :-)

von Johannes (Gast)


Lesenswert?

und ich habs nicht gechecked...
also auch setzn, 0b00000110

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.