Forum: Compiler & IDEs Atmega32 kleines Problem mit Modellservoansteuerung
Moin moin,
ich weiß, dass es ein oft Diskutiertes Thema ist, aber ich hab trotzdem
ein kleines Problem mit meinem Code.
An sich denke ich, dass ich es verstanden habe wie man den Servo
ansteuert:
- 20ms Signal generieren
- die ersten 2ms sind das Servosignal und der Rest ist für nix quasi.
Das hab ich mal probiert mit nem Atmega32 mit dem internen 8MHz Quarz.
Währe cool wenn jemand vlt. Mal drüber gucken könnte und mir nen kleinen
Denkanstoß geben könnte?
Danke im Voraus.
Gruß Dennis
hier mein C Code:
1 | #include <avr/io.h>
| 2 | #include <avr/interrupt.h>
| 3 |
| 4 | #define servo_anzahl 1
| 5 | uint16_t isvalue=1000;
| 6 | uint16_t value[1]={1500};
| 7 | uint16_t a=0,b=0;
| 8 |
| 9 |
| 10 | ISR (TIMER2_COMP_vect) // µs Takt
| 11 | {
| 12 | uint8_t i;
| 13 |
| 14 | isvalue++;
| 15 |
| 16 | if (isvalue>=20000) isvalue=0; //gucken ob die 20ms um sind
| 17 |
| 18 | if(isvalue==0){//wenn sie um sind die Servoports auf High schalten
| 19 | for(i=0;i<servo_anzahl;i++) PORTA|=(1<<i);
| 20 | }
| 21 |
| 22 | for(i=0;i<servo_anzahl;i++){//wenn gewünschte zeit rum ist servosignal auf low schalten
| 23 | if(value[i]==isvalue) PORTA &=~(1<<i);
| 24 | }// end for(i=0
| 25 |
| 26 | if(isvalue>=2000) for(i=0;i<servo_anzahl;i++) PORTA &=~(1<<i); // wenn man über die 2ms kommt, alle servos auf null schalten
| 27 |
| 28 | } // end isr timer2comp
| 29 |
| 30 |
| 31 |
| 32 |
| 33 |
| 34 | int main(void){
| 35 | sei();
| 36 | DDRA=0xFF;
| 37 | DDRB=0xFF;
| 38 | PORTB=0x00;
| 39 | // timer2 im µsekundentakt output compare betrieb
| 40 | TCCR2 =(1<<WGM21) |(1<<CS20); //Prescale von 1 und CTC mode
| 41 | OCR2=8; //Vergleich bei 4 --> (8MHz/1)/8= Interruptabstand von einer µs
| 42 | TIMSK|=(1<<OCIE2);
| 43 | while(1);
| 44 | }
|
Dennis R. schrieb:
> - 20ms Signal generieren
> - die ersten 2ms sind das Servosignal und der Rest ist für nix quasi.
die 2ms sind wichtig.
die 20ms sind nicht so wichtig.
> Das hab ich mal probiert mit nem Atmega32 mit dem internen 8MHz Quarz.
Es gibt keinen 8Mhz internen Quarz.
Es gibt einen RC-Schwingkreis, der in etwa mit 8Mhz schwingt.
> Währe cool wenn jemand vlt. Mal drüber gucken könnte und mir nen kleinen
> Denkanstoß geben könnte?
In welche Richtung sollen wir suchen?
Du denkst also, du kannst alle 8 Taktzyklen einen Interrupt auslösen
lassen, und dann in der Interrupt Routine massig Taktzyklen für
Berechnungen zur Verfügung zu haben.
Das wird nicht gehen.
Wenn du für eine Tätigkeit 5 Minuten brauchst, dann hat es keinen Sinn
wenn dein Chef alle 10 Sekunden da steht und dir noch einen Auftrag gibt
und noch einen und noch einen ad infinitum.
Du wirst mit der Arbeit nicht hinterherkommen.
Wie kommst Du auf die Idee, Deine Interruptroutine könne in weniger als
1 us abgearbeitet sein? Das müsste sie nämlich, wenn Obiges
funktionieren soll. Dein Programm kommt aus dem Interrupt nicht raus
und hängt trotzdem weit hinterher.
Edit: 1 Bemerkung gestrichen, weil sie nicht zutraf.
8 Taktzyklen sind zu wenig Zeit, die du der ISR lässt.
80 Taktzyklen könnte sich so lala ausgehen. Müsste man im Simulator
durchspielen.
Also: schraub deine Erwartungen runter. Kein Servo muss man auf 1000
unterschiedliche Positionen fahren können. Das gibt die Mechanik sowieso
nicht her. 100 reichen auch.
So kannst du die ISR noch ein wenig kürzer gestalten
1 | ISR (TIMER2_COMP_vect) // µs Takt
| 2 | {
| 3 | uint8_t i, servoMask;
| 4 |
| 5 | isvalue++;
| 6 | if (isvalue >= 20000)
| 7 | isvalue=0; //gucken ob die 20ms um sind
| 8 |
| 9 | servoMask = 0x01;
| 10 | for( i = 0; i < servo_anzahl; i++) {
| 11 | if( isvalue < value[i] )
| 12 | PORTA |= servoMask;
| 13 | else
| 14 | PORTA &= ~servoMask;
| 15 |
| 16 | servoMask <<= 1;
| 17 | }
| 18 | }
|
Das ändert aber nichts daran, dass du die ISR Aufruffrequenz absenken
musst.
Wenn Du mehrere Servos am gleichen Port hast, kannst Du das ganze auch
anders lösen. Die Servos werden dabei nicht gleichzeitig, sondern
nacheinander angesteuert.
Es gibt dann eine Variable 'servonummer'. Zu beginn wird die Nummer auf
0 gesetzt. Der Timer wird mit einem Prescaler von 64 im CTC Modus
betrieben. Der Servoport.0 wird auf 1 gesetzt und OCR des Timers mit dem
Wert des ersten Servowertes geladen:
1 | // Pseudocode
| 2 |
| 3 | void InitServo()
| 4 | {
| 5 | servonummer = 0;
| 6 | SERVOPORT = 1;
| 7 | Timer = PRESCALE 64;
| 8 | OCR = value[0];
| 9 | }
|
In der Compare Match ISR wird dann der nächste Pin des Ports auf 1
gesetzt und der aktuelle gelöscht. Die Servonummer wird hochgezählt und
OCR mit dem Wert des nächsten Servos geladen. Sobald alle Servos durch
sind, wird wieder von vorne angefangen:
1 | // Pseudocode
| 2 |
| 3 | ISR(TIMER_COMP_VECT)
| 4 | {
| 5 | if(++servonummer == ANZAHL_DER_SERVOS) // alle Servos durch
| 6 | {
| 7 | servonummer = 0; // -> vorne anfangen
| 8 | SERVOPORT = 1;
| 9 | }
| 10 | else
| 11 | SERVOPORT <<= 1; // ansonsten nächstes Servo ansteuern
| 12 |
| 13 | OCR = value[servonummer]; // Timer mit nächstem Servowert laden
| 14 | }
|
Bei einem OCR-Wert von 128 ergibt das etwa 1ms, bei 255 etwa 2ms
Impulslänge.
Die Zeit zwischen 2 Impulsen, die ein Servo kriegt, ist dabei zwar nicht
konstant 20ms, was in der Praxis jedoch nichts ausmacht. Ein großer
Vorteil ist die sehr geringe Prozessorauslastung, da die ISR höchstens
1x pro Millisekunde ausgeführt wird.
Ich hatte auf diese Weise 8 Servos an einem Controller hängen, hat immer
wunderbar funktioniert. Hab nur leider den Originalcode nicht mehr.
MfG Mark
Ui danke für die vielen und vorallem schnellen antworten.
Hatte es ebend mal versucht die ISR frequenz herab zu senken um den
faktor 10, dann ging es. aber ich konnte den servo nicht bewegen.
D.h. wenn ich value[0] verändert habe dann hat sich am servo aber nix
verändert. nur wenn ich den vorgeladenen wert verändert habe dann hat er
sich bewegt.
also nur ein kleienr erfolg.
Zu dem code von mark ..
hab ich so umgesetzt
der code fährt zwar den server an die richtige position, aber hält ihn
da dann nicht fest! 1 | #include <avr/io.h>
| 2 | #include <avr/interrupt.h>
| 3 |
| 4 | #define SERVO_ANZAHL 8
| 5 | #define SERVOPORT PORTA
| 6 |
| 7 | uint8_t value[8]={250};
| 8 | uint8_t servonummer;
| 9 |
| 10 |
| 11 | ISR (TIMER2_COMP_vect)
| 12 | {
| 13 | if(++servonummer == SERVO_ANZAHL) // alle Servos durch
| 14 | {
| 15 | servonummer = 0; // -> vorne anfangen
| 16 | SERVOPORT = 1;
| 17 | }
| 18 | else
| 19 | SERVOPORT <<= 1; // ansonsten nächstes Servo ansteuern
| 20 |
| 21 | OCR2 = value[servonummer]; // Timer mit nächstem Servowert laden
| 22 |
| 23 |
| 24 | } // end isr timer2comp
| 25 |
| 26 |
| 27 | void InitServo()
| 28 | {
| 29 | servonummer = 0;
| 30 | SERVOPORT = 1;
| 31 |
| 32 | // timer2 im µsekundentakt output compare betrieb
| 33 | TCCR2 =(1<<WGM21) |(1<<CS22); //Prescale von 8 und CTC mode
| 34 | OCR2=value[0]; //Vergleich bei 4 --> (8MHz/8)/1= Interruptabstand von einer µs
| 35 | TIMSK|=(1<<OCIE2);
| 36 | }
| 37 |
| 38 |
| 39 |
| 40 |
| 41 | int main(void){
| 42 | sei();
| 43 | DDRA=0xFF;
| 44 | DDRB=0xFF;
| 45 | PORTB=0x00;
| 46 |
| 47 | InitServo();
| 48 |
| 49 |
| 50 | while(1){
| 51 |
| 52 | }
| 53 | }
|
Dennis R. schrieb:
> D.h. wenn ich value[0] verändert habe dann hat sich am servo aber nix
> verändert. nur wenn ich den vorgeladenen wert verändert habe dann hat er
> sich bewegt.
könnte ein volatile Problem sein.
1 | volatile uint16_t value[1]={1500};
|
> der code fährt zwar den server an die richtige position, aber hält ihn
> da dann nicht fest!
Was heißt 'hält ihn nicht fest'?
> uint8_t value[8]={250};
gib den anderen Servos wenigstens eine kleine Zeit aber nicht 0, damit
das Servo nicht Puls an Puls bekommt. Auch wenn die 20ms nicht kritisch
sind, würde ich meine Hand da nicht dafür ins Feuer legen, was wohl
passiert, wenn die 2ms Stellimpulse da dicht an dicht kommen. Ein paar
Millisekunden dürfen schon Pause dazwischen sein. Und auch hier wieder:
volatile!
1 | volatile uint8_t value[8] = { 250, 200, 200, 200, 200, 200, 200, 200 };
|
Die Pause die jedes Servo zwischen den Pulsen zugestanden bekommt, ist
ja die Summe der Pulslängen aller anderen Servos.
Und bitte:
Bemüh dich wenigstens ein bischen, ein optisch einigermassen akzeptables
Programm zu produzieren. Auch jetzt in der Entwicklungsphase!
Alles einfach nur an den linken Rand zu quetschen ist ein ziemlich
sicherer Weg irgendwann den Überblick zu verlieren, welche Schleife in
welches if geschachtelt wurde und welcher Block von welcher Bedingung
abhängt.
Das bischen korrekte Einrückung kostet keine Zeit während man tippt,
kann aber oft den Unterschied zwischen 'Ich sehs' oder 'Ich sehs nicht'
ausmachen.
na mit nicht festhalten mein ich das man, den servo nach dem stellen mit
der hand bewegen kann.
Normal ist ja eignetlich(so kenne ich es aus dem modellbau bereich) das
er an der Position feststehen tut?
so mit dem code haste ja recht
hab ihn nochmal etwas verschönert ;)
1 | #include <avr/io.h>
| 2 | #include <avr/interrupt.h>
| 3 |
| 4 | #define SERVO_ANZAHL 8
| 5 | #define SERVOPORT PORTA
| 6 |
| 7 | volatile uint8_t value[8]={128};
| 8 | volatile uint8_t servonummer;
| 9 |
| 10 |
| 11 | ISR (TIMER2_COMP_vect)
| 12 | {
| 13 | if(++servonummer == SERVO_ANZAHL) // alle Servos durch
| 14 | {
| 15 | servonummer = 0; // -> vorne anfangen
| 16 | SERVOPORT = 1;
| 17 | }
| 18 | else SERVOPORT <<= 1; // ansonsten nächstes Servo ansteuern
| 19 |
| 20 | OCR2 = value[servonummer]; // Timer mit nächstem Servowert laden
| 21 | } // end isr timer2comp
| 22 |
| 23 |
| 24 | void InitServo()
| 25 | {
| 26 | servonummer = 0;
| 27 | SERVOPORT = 1;
| 28 | TCCR2 =(1<<WGM21) |(1<<CS22); //Prescale von 64 und CTC mode
| 29 | OCR2=value[0];
| 30 | TIMSK|=(1<<OCIE2);
| 31 | }
| 32 |
| 33 | int main(void){
| 34 |
| 35 | sei();
| 36 | DDRA=0xFF;
| 37 | DDRB=0xFF;
| 38 | PORTB=0x00;
| 39 |
| 40 | InitServo();
| 41 |
| 42 | while(1);
| 43 | }// end main
|
aso zu der pause zwischen den pulsen für den servo.
wenn ich jetzt zum Beispiel 4 Servos ansteuern will, hab ich ja
mindestens 4 ms pause, wenn ich sage das Minimum für ein servo ist 1ms
und das max. ist 2ms?
hmm aber leider funktioniert der code leider doch nett.
ich komme irgedwie nicht mehr aus der ISR raus. das hab ich mit dem
simulator auch nachstellen können.
Aber warum?
Dennis R. schrieb:
> ich komme irgedwie nicht mehr aus der ISR raus. das hab ich mit dem
> simulator auch nachstellen können.
> Aber warum?
Das ist an dieser Stelle schon korrekt.
Da in deiner Hauptschleife nichts mehr passiert, macht der Simulator
dieses 'da passiert nichts weiter' im schnellen Vorlauf, bis eben der
nächste Compare Match eintritt.
Dennis R. schrieb:
> aso zu der pause zwischen den pulsen für den servo.
> wenn ich jetzt zum Beispiel 4 Servos ansteuern will, hab ich ja
> mindestens 4 ms pause, wenn ich sage das Minimum für ein servo ist 1ms
> und das max. ist 2ms?
Ja.
Bedenke: Normalerweise ist diese Pause ca. 19ms lang
PS: Bei dir ist sie jetzt 0 (ok, nahezu 0) ms lang.
Aber is schon komisch hab jetzt den Code mal um das in einen Sekenduen
takt invertieren des PortB ergänzed.
Damit müsste die LED an meinen Controller, ja im 1 Sec. Takt blinken.
Was sie ja mit anderen Programmen tut, aber in diesem fall nicht!
Und nochmal zur pause, ok vlt. versteh ich den code falsch:
Wenn ich nur einen Servo hätte, dann leuchtet es mir ein das ich eine
Pause von fast 0 habe. aber wenn ich jetzt hier in diesem Bsp. 8 servos
habe dann müsste ich doch eine Pause von ca. 8 bis 16ms haben oder?
Ich verschiebe doch mein bit an PortA solange bios ich wieder beim
ersten anfange. Somit ist ja der PortA 0 z.B. solange bis er weider dran
ist?
1 | #include <avr/io.h>
| 2 | #include <avr/interrupt.h>
| 3 | #ifndef F_CPU
| 4 | #define F_CPU 8000000ul
| 5 | #endif
| 6 | #include <util/delay.h>
| 7 | #define SERVO_ANZAHL 8
| 8 | #define SERVOPORT PORTA
| 9 |
| 10 | volatile uint8_t value[8]={128};
| 11 | volatile uint8_t servonummer;
| 12 |
| 13 |
| 14 | ISR (TIMER2_COMP_vect)
| 15 | {
| 16 | if(++servonummer == SERVO_ANZAHL) // alle Servos durch
| 17 | {
| 18 | servonummer = 0; // -> vorne anfangen
| 19 | SERVOPORT = 1;
| 20 | }
| 21 | else SERVOPORT <<= 1; // ansonsten nächstes Servo ansteuern
| 22 |
| 23 | OCR2 = value[servonummer]; // Timer mit nächstem Servowert laden
| 24 | } // end isr timer2comp
| 25 |
| 26 |
| 27 | void InitServo()
| 28 | {
| 29 | servonummer = 0;
| 30 | SERVOPORT = 1;
| 31 | TCCR2 =(1<<WGM21) |(1<<CS22); //Prescale von 64 und CTC mode
| 32 | OCR2=value[0];
| 33 | TIMSK|=(1<<OCIE2);
| 34 | }
| 35 |
| 36 | int main(void){
| 37 |
| 38 | sei();
| 39 | DDRA=0xFF;
| 40 | DDRB=0xFF;
| 41 | PORTB=0x00;
| 42 |
| 43 | InitServo();
| 44 |
| 45 | while(1){
| 46 | _delay_ms(1000);
| 47 | PORTB=~PORTB;
| 48 |
| 49 |
| 50 | }
| 51 | }// end main
|
Dennis R. schrieb:
> Aber is schon komisch hab jetzt den Code mal um das in einen Sekenduen
> takt invertieren des PortB ergänzed.
> Damit müsste die LED an meinen Controller, ja im 1 Sec. Takt blinken.
> Was sie ja mit anderen Programmen tut, aber in diesem fall nicht!
>
> Und nochmal zur pause, ok vlt. versteh ich den code falsch:
> Wenn ich nur einen Servo hätte, dann leuchtet es mir ein das ich eine
> Pause von fast 0 habe. aber wenn ich jetzt hier in diesem Bsp. 8 servos
> habe dann müsste ich doch eine Pause von ca. 8 bis 16ms haben oder?
> Ich verschiebe doch mein bit an PortA solange bios ich wieder beim
> ersten anfange.
Ja.
Aber du lädst einen OCR Wert von 0, wodurch beim Verlassen der ISR
gleich wieder der nächste Compare Match vorliegt
Das hier
1 | volatile uint8_t value[8]={128};
|
ist gleichbedeutend mit 1 | volatile uint8_t value[8]={128, 0, 0, 0, 0, 0, 0, 0};
|
d.h. deine restlichen Servos stehen alle auf 0!
Ich hab jetzt auch noch nicht das Timing nachgerechnet, was ein
Prescaler von 64 mit 8Mhz und einem OCR Wert von 128 für eine Zeitdauer
ergibt.
aso hmm ok dachte bis ebend das 1 | volatile uint8_t value[8]={128};
|
das gleiche ist wie 1 | volatile uint8_t value[8]={128,128,128,128,128,128,128,128};
|
deswegen hatte das kbuchegg schon vorhin hervorgehoben.
also mit dem code geht es jetzt.
die ISR für den timer0 hab ich nur reingemacht zum testen. Damit der
servo hin und her fährt.
und das mit der Zeit haut schon hin.
1/((8MHz / 64)/128)=1,024mS
1/((8MHz / 64)/250)=2mS
ok nach oben ist da nicht mehr viel spielraum aber nach unten, falls der
servo mehr kann!
Danke nochmals für die nett hilfe hier, DANKE!
1 | #include <avr/io.h>
| 2 | #include <avr/interrupt.h>
| 3 | #ifndef F_CPU
| 4 | #define F_CPU 8000000ul
| 5 | #endif
| 6 | #include <util/delay.h>
| 7 | #define SERVO_ANZAHL 8
| 8 | #define SERVOPORT PORTA
| 9 |
| 10 | volatile uint8_t value[SERVO_ANZAHL]={128,128,128,128,128,128,128,128};
| 11 | volatile uint8_t servonummer;
| 12 |
| 13 |
| 14 | ISR (TIMER2_COMP_vect)
| 15 | {
| 16 | if(++servonummer == SERVO_ANZAHL) // alle Servos durch
| 17 | {
| 18 | servonummer = 0; // -> vorne anfangen
| 19 | SERVOPORT = 1;
| 20 | }
| 21 | else SERVOPORT <<= 1; // ansonsten nächstes Servo ansteuern
| 22 |
| 23 | OCR2 = value[servonummer]; // Timer mit nächstem Servowert laden
| 24 | } // end isr timer2comp
| 25 |
| 26 | ISR (TIMER0_COMP_vect) {
| 27 |
| 28 | static uint16_t i=0;
| 29 | static uint8_t asd=0;
| 30 | i++;
| 31 | if(i>=50){
| 32 | if(!asd){
| 33 | i=0;
| 34 | value[0]+=1;
| 35 | if(value[0]>=250) asd=1;
| 36 | }else{
| 37 | i=0;
| 38 | value[0]-=1;
| 39 | if(value[0]<=128) asd=0;
| 40 | }
| 41 | }
| 42 | }
| 43 |
| 44 | void InitServo()
| 45 | {
| 46 | servonummer = 0;
| 47 | SERVOPORT = 1;
| 48 | TCCR2 =(1<<WGM21) |(1<<CS22); //Prescale von 64 und CTC mode
| 49 | OCR2=value[0];
| 50 | TIMSK|=(1<<OCIE2);
| 51 | }
| 52 |
| 53 | int main(void){
| 54 |
| 55 | sei();
| 56 | DDRA=0xFF;
| 57 | DDRB=0xFF;
| 58 | PORTB=0x00;
| 59 |
| 60 | TCCR0 =(1<<WGM01) |(1<<CS00)|(1<<CS01);
| 61 | OCR0=125;
| 62 | TIMSK|=(1<<OCIE0);
| 63 |
| 64 |
| 65 | InitServo();
| 66 |
| 67 | while(1);
| 68 | }// end main
|
Aso,
am besten wähere es wenn man natürlich eine pause nach dem servo anfügen
würde oder?
damit man eine bestimmte minimum zeit hat bis der erste servo wieder
dran ist oder?
greetz dennis
Dennis R. schrieb:
> Aso,
> am besten wähere es wenn man natürlich eine pause nach dem servo anfügen
> würde oder?
Das musst du ausprobieren.
Stell deine Servos auf Minimalzeit ein (aber nicht 0)
und sieh nach ob sie mit dieser Pause noch zurecht kommen.
Ich denke mal sie werden es, aber in dem Fall macht Versuch kluch.
> damit man eine bestimmte minimum zeit hat bis der erste servo wieder
> dran ist oder?
Die 0 war deswegen ein Problem, weil dein µC dann nicht mehr hinterher
gekommen ist. Der kann seine ISR nun mal nicht in 0-Zeit abarbeiten.
ja alles kalro jetzt hab ich es gecheckt!
tausend Danke nochmal an alle die geholfen haben.!
gruss
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
|