PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Servo mit 8-bit Timer (AVRmega32) ?



phk
05.04.2007, 22:32
Hallo,
wie kann ich 50Hz frequent für mein servo mit 8-bit timer (timer0 oder timer2 von mega32) erzeugen? Ich benutze 16mhz Taktfrequent
Timer1 in mein Board ist shon besetzt, ich kann das nicht benutzen.

sorry für mein schlechtes deutsch.
Vielen Dank.

wkrug
06.04.2007, 08:39
Das kommt halt darauf an, mit welcher Präzision das Servo angesteuert werden soll.
Ich würd es mal mit einem preload Wert im TCNT2 versuchen.
Und zu diesem Zeitpunkt den Impulsausgang einschalten.
Dann in der Timer 2 Overflow Routine ein Register hochzählen, bis der Sollwert erreicht ist (zwischen 1 und 2ms).
Ist dieser erreicht den Impulsausgang wieder abschalten.
Die Pausen zwischen den Impulsen (ca. 20ms) könnte man auch so erzeugen.
Lediglich die Berechnung des Preload Wertes stell ich mir ein bischen Trickreich vor.
Ich weiss jetzt blos nicht sicher, ob man den Prescaler für den TCNT 2 einzeln einstellen kann, oder ob der nicht mit einem anderen Zähler gekoppelt ist.

Banzai
06.04.2007, 23:49
Hi Leutz,

ich steh eigentlich vor dem selben Problem:


#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

unsigned int ontime = 0;
unsigned int offtime = 2000;

ISR(TIMER0_COMP_vect)
{
offtime--;

if( offtime == 0 )
{
offtime = 2000;
PORTA &= ~0x80;
}
if(offtime == ontime )
{
PORTA |= 0x80;
}
}

int main(void)
{

DDRC = 0xFF;
PORTC = 0x0F; // alles aus

DDRA = 0x80;
PORTA = 0x00;

// CLK/8 Prescaler CS01
TCCR0 = (1<<CS01);


OCR0 = 20; // 16000000/8 = 1999490Hz/20 = 99974,50Hz => 0,01ms

TIMSK |= (1<<OCIE0);

sei();

int i = 0;

ontime = 100;

for(i=0;i<25;i++)
_delay_ms( 200 );

ontime = 125;

for(i=0;i<25;i++)
_delay_ms( 200 );

ontime = 150;

for(i=0;i<25;i++)
_delay_ms( 200 );

ontime = 175;

for(i=0;i<25;i++)
_delay_ms( 200 );

ontime = 200;

for(i=0;i<25;i++)
_delay_ms( 200 );

ontime = 0;

while(1){ }
}

Ich benutze den Billig-Servo von Conrad (Basetech ES-05). Sollte ich das mit den Timern richtig verstanden haben, so müsste doch die Interrupt-Routine alle 0.01ms aufgerufen werden. Sprich 20ms = 2000 Aufrufe. Damit wäre ein ontime von 100 = 1ms usw.?
Auch die _delay_ms-Funktion sollte bei 25*200ms = 5000ms = 5 Sekunden warten, was sie auch nicht tut (F_CPU ist über AVR Studio mit 16000000 für ATmega32 gesetzt ).

Inzwischen glaube ich, dieses Mikrocontroller-Zeug ist von bösen Zauberern erfunden worden und nur Magiekundigen zugänglich, die ihre Seele bereits dem Satan verkauft haben und durch Zölibat und Selbstgeisselung die schwarze Kunst der Elektronenbeeinflussung beherrschen.

Grütze

Banzai

Hubert.G
07.04.2007, 18:47
@Banzai
Wenn ich die avr-libc richtig gelesen habe dann wäre dein _delay_ms( 200 ) nur bei 1MHz Takt 200msek, bei 16MHz nur 1/16 .

Henry
07.04.2007, 18:55
@Banzai:

Ich möchte allerdings kein Servo ansteuern (jedenfalls noch nicht) sondern das Signal vom Empfänger auswerten (also im Prinziep die Impulslänge messen und dann vergleichen)

Hubert.G
07.04.2007, 19:21
@Henry
Das Signal vom Empfänger auf den INT0 oder INT1 Eingang legen, bei der steigenden oder fallenden Flanke den Timer starten, bei der nächsten Flanke den Timer stoppen, auslesen, neustarten usw. Wenn du den Timer so einstelllst das die max-Zeit bis zum Überlauf etwas mehr als die max. Impulszeit ist kannst du mit dem Overflow auch gleich die Pause auswerten.

Banzai
09.04.2007, 22:31
Hallo Leutz,

ich habe meinen Servo soweit, dass er sich entsprechend seiner Funktion verhält.
Warum weiss ich allerdings nicht.


#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

int ontime = 0;
int offtime = 2000;

ISR(TIMER0_COMP_vect)
{
static int onBuf;

if( offtime - ontime > 0 )
{
onBuf = ontime;
offtime--;
}
else
{
if( onBuf == 0 )
{
offtime = 8000;
PORTA &= ~0x80;
onBuf = ontime;
}
else
{
if( onBuf == ontime )
PORTA |= 0x80;
onBuf--;
}
}
}

int main(void)
{

DDRA = 0x80;
PORTA = 0x00;

// CLK/8 Prescaler CS01
TCCR0 = (1<<CS01);

OCR0 = 20; // 16000000/8 = 1999490/20 = 99974,50Hz => 0,01ms

TIMSK |= (1<<OCIE0);

sei();

int i = 0;

ontime = 3; // links

for(i=0;i<312;i++)
_delay_ms( 16 );

ontime = 11; // mitte

for(i=0;i<312;i++)
_delay_ms( 16 );

ontime = 19; // rechts

for(i=0;i<312;i++)
_delay_ms( 16 );

ontime = 0;

while(1);
}

Kann mir einer meinen Code erklären (klingt lächerlich, ich weiss)?

Warum müsste lt. Theorie die Interrupt-Routine alle 0.01ms aufgerufen werden oder täusche ich mich da?
@Hubert G.: Du hattest recht, auch wenn diese minimalistische Bemerkung, das hätte was mit 1MHz zu tun, in der avr-Libc-Doku irgendwie missverständlich für eine _delay_ms-Funktion ist.
Warum veranlasst ein ontime=4 die linke Position, wenn das theoretisch niemals 1ms sein kann?
Warum diese komischen anderen Werte für Mitte/Links?
Ich benutze kein PWM, generiere ich da unwissentlich ein Software-PWM?
Bewegt sich ein Servo immer in so ruckartigen Bewegungen, oder gibt es eine Möglichkeit von einer zur nächsten Position kontinuierlich zu kommen?

thx4Help

Banzai

Der Gärtner
10.04.2007, 09:38
1) k.a. was du da für einen Servo hast, aber es sind echt 30us Links voll und 200us Rechts voll.
2) Warum stellst du die Offtime in der Interruptroutine plötzlich auf 8000? war nicht 2000 geplant? (beim Programmstart stellst dus auf 2000)
2) Du generierst eine SoftwarePWM.
3) Du kannst die Servos langsamer drehen lassen. Einfach die Position langsam verändern. die Dinger sollen im Modellbau ja SCHNELL ihre Pos. ändern können, das passt schon so.

Ich geb mal dem Servo die schuld an seinem l(i/u)stigem verhalten. hast du ein Oszi oder ein messgerät mit Oszifunktion? Dann kannst dir ja die Signale anzeigen lassen, die der Servo wirklich kriegt.

Banzai
10.04.2007, 15:35
@der Gärtner:
1.) Hoch lebe die Definition, nieder mit der Regel!
2.) Ein Fehler meinerseits. Die 8000 resultierten noch aus einem Versuch mit einem Zähler von 5 statt 20 (also 0.0025ms/Interrupt). Sollte schon 2000 heissen.
2(3).) Ui, dann kann ich sowas, ohne zu wissen dass es ich kann :o)
3(4).) Langsamer => längere Pausen zwischen den Werte-wechseln, oder?

Nein, leider bin ich nicht in Besitz von Oszi (weder real, noch über Bekannte oder Arbeit), obwohl sowas gerade in der Anfangszeit bestimmt nützlich wäre.

Und was das l(i/u)stige Verhalten angeht, würde ich fast behaupten, dass ist sogar hinter* und un* :-|

thx4Hints

Banzai