PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : atmega16 3 Servos ansteuern Timer0



Knipser-14
20.06.2009, 10:44
Hallo,

ich habe bereits ein Programm geschrieben mit dem ich einen Servo einwandfrei ansteuern kann.
Nun habe ich versucht es so zu verändern das ich 3 Servos gleichzeitig ansteuern kann.
Eigentlich soll das Programm folgendes tun:
1. Signalpin von Servo1 auf High
2. Warten bis dessen Stellzeit rum
3. Signalpin Servo 1 auf Low
4. Signalpin 2 auf High
...
nach dem 3. Servo soll er nochmal so lange warten, das 20ms für Servo1 voll sind. Und dann wieder mit Servo 1 anfangen.
Aber iwie reagieren die Servos garnicht (außer beim einschalten des Stroms).

Entdeckt iwer den Denkfehler in meinem Programm? Und wie gesagt die Zeiten müssten stimmen.

Danke, Knipser.


#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdint.h>
#include <math.h>

#define F_CPU 16000000UL

#define BAUD 9600UL
#define UBRR_BAUD ((F_CPU/(16UL*BAUD))-1)

#define SERVO_DDR1 DDRA
#define SERVO_PORT1 PORTA
#define SERVO_PORTPIN1 PA0

#define SERVO_DDR2 DDRA
#define SERVO_PORT2 PORTA
#define SERVO_PORTPIN2 PA1

#define SERVO_DDR3 DDRA
#define SERVO_PORT3 PORTA
#define SERVO_PORTPIN3 PA2

#define LED_DDR DDRD
#define LED_PORT PORTD
#define LED_PORTPIN PD7

volatile uint16_t z,q,p;
volatile uint8_t t;
volatile uint16_t servo1,servo2,servo3;

TIMER0_interrupt_init(void){
z = 0;
TCNT0 = 0;
OCR0 = 9;
TCCR0 = 0x0a;
TIMSK |= (1<<OCIE0); //Interrupt aller 1/200ms ->200 je ms
sei();
}

ISR(TIMER0_COMP_vect){
z++;

}


int main(void)
{
// USART initialisieren
uart_init();

TIMER0_interrupt_init();

t = 1;

servo1 = 255;
servo2 = 255;
servo3 = 355;

LED_DDR |= (1<<LED_PORTPIN);

LED_PORT &= ~(1<<LED_PORTPIN);

SERVO_DDR1 |= (1<<SERVO_PORTPIN1);
SERVO_DDR2 |= (1<<SERVO_PORTPIN2);
SERVO_DDR3 |= (1<<SERVO_PORTPIN3);

SERVO_PORT1 |= (1<<SERVO_PORTPIN1);

sei();

while (1){
if (t==1 && z==servo1){
t=2;
z=0;
SERVO_PORT1 &= ~(1<<SERVO_PORTPIN1);
SERVO_PORT2 |= (1<<SERVO_PORTPIN2);
LED_PORT |=(1<<LED_PORTPIN);
}
if (t==2 && z==servo2){
t=3;
z=0;
SERVO_PORT2 &= ~(1<<SERVO_PORTPIN2);
SERVO_PORT3 |= (1<<SERVO_PORTPIN3);
}
if (t==3 && z==servo3){
t=4;
z=0;
SERVO_PORT3 &= ~(1<<SERVO_PORTPIN3);
q = (4000-servo2-servo3);
}
if (t==4 && z==q){
z = 0;
t = 1;
SERVO_PORT1 |= (1<<SERVO_PORTPIN1);
}
if (z==4000){
z = 0;
t = 1;
SERVO_PORT1 |= (1<<SERVO_PORTPIN1);
}
return 0;
}
}

The Man
21.06.2009, 08:50
Hi Knipser,

eine Macke ist in den IF Abfragen:

if (t==1 && z==servo1)

Das muss

if ((t==1) && (z==servo1)) heißen.

Die einzelnen Statements müssen für sich in Klammern stehen, damit daraus eine eigenständige Aussage wird.

Es währe auch gut, wenn du ein paar mehr Worte zu deinem Programm verlieren würdest.
So wie ich das sehe, schaltest du den Kanal für den nächsten Servo ein, wenn du den vorherigen ausschaltest. Das hat natürlich zur Folge, dass sich die Einschaltpunkte aller Servos außer des ersten komultativ (sorry für das Wort^^) in Abhängigkeit ALLER vorrangegangenen Pulsdauern verändern.
Das heißt, du musst nach jeden Servo noch eine Abfrage machen, ob die 2ms voll sind.

Und noch was. Bau die math.h nur ein, wenn es sich nicht verhindern lässt. Mit den Controlern, die wir hier benutzten, frißt das richtig Resourcen. Letztendlich bedeutet alles was über Strichrechnung hinausgeht einen erheblichen Aufwand für die kleinen Dinger. Deswegen sein auch sehr vorsichtig mit Kommarechnung. Aber das währe ein eigener Thread.

mfg,
The Man

radbruch
21.06.2009, 09:43
Hallo


Das muss

if ((t==1) && (z==servo1)) heißen.
Ähnlich wie "Punkt vor Strich" gibt es in C die Rangfolge der Operatoren:
http://www.hs-augsburg.de/~sandman/c_von_a_bis_z/c_030Anhang_000.htm

Da "gleich" (==) vor "logisches Und" (&&) steht und deshalb dessen Bindung höher ist dienen die Klammern nur der Übersichtlichkeit. Der Kompiler übersetzt beides gleich.


TIMER0_interrupt_init(void){
z = 0;
TCNT0 = 0;
OCR0 = 9;
TCCR0 = 0x0a;
TIMSK |= (1<<OCIE0); //Interrupt aller 1/200ms ->200 je ms
sei();
}
Wo wird der CTC-Mode des Timers eingestellt? Wie sieht im Vergleich der funktionierende Code für ein Servo aus?

Gruß

mic

Edit: Möglicherweise ist deine Hauptschleife nun zu lang und z wird pro Durchlauf mehrfach erhöht. Das hätte zur Folge das ein Vergleich auf "gleich" nicht immer trifft. Sicherheitshalber solltest du deshalb so prüfen:

if (t==1 && z>=servo1){

Besser (und noch blockierender) wäre es so:


...
while (1){
if (t==1) while(z<=servo1);
t=2;
z=0;
SERVO_PORT1 &= ~(1<<SERVO_PORTPIN1);
SERVO_PORT2 |= (1<<SERVO_PORTPIN2);
LED_PORT |=(1<<LED_PORTPIN);

if (t==2) while(z<=servo2);
t=3;
z=0;
SERVO_PORT2 &= ~(1<<SERVO_PORTPIN2);
SERVO_PORT3 |= (1<<SERVO_PORTPIN3);

if (t==3) while(z<=servo3);
t=4;
z=0;
SERVO_PORT3 &= ~(1<<SERVO_PORTPIN3);
q = (4000-servo2-servo3);

if (t==4) while(z<=q);
z = 0;
t = 1;
SERVO_PORT1 |= (1<<SERVO_PORTPIN1);

/* if (z==4000){
z = 0;
t = 1;
SERVO_PORT1 |= (1<<SERVO_PORTPIN1);
*/
}
...
(ungetestet!)

Knipser-14
21.06.2009, 11:43
Hallo

erstmal ein großes Danke für eure Antworten :) .

-also der Interrupt ist der gleiche wie bei meinem funktionierenden Servoprogramm
-der CTC-Mode wird mit


TCCR0 = 0x0a;

aktiviert ( die Zeile stellt gleichzeitig noch den Prescaler auf 8 )

@The Man
-ja die Einschaltpunkte aller Servos verändern sich, aber das ändert ja nicht die Zeit die der Servo high geschaltet wird
-ich gebe zu das die 20ms eher ein circa Wert sind
-vl. ist auch das der Fehler

Also ich werde mal den Code von Radbruch ausprobieren und auch mal versuchen die Hauptschleife zu kürzen und die 20ms genauer einzuhalten.
Kann es aber leider nicht heute machen, da ich nicht zu Hause bin.

Nochmals Danke,

Knipser

Fast vergessen:
Ich hatte auch schonmal versucht die Servos gleichzeitig anzusteuern. Und zwar wollte ich nach 20ms alle 3 Signalpins auf High schalten und dann halt 3 If-Schleifen die dann nach der jeweiligen Zeit für die einzellnen Servos die Pins wieder auf Low ziehen.
Könnte das auch klappen?
Problem wäre wenn die Servos die gleiche Zeit haben.