Forum: Mikrocontroller und Digitale Elektronik Servoansteuerung mit Timer (ATmega32)


von Thomas P. (thomas_p52)


Lesenswert?

Hallo

ich möchte mit meinem uC ein Servo ansteuern, per Timer / Interrupt

Der Code ist folgendermaßen:
Alle 20ms wird ein Interrupt ausgelöst der dann das Signal, je nach 
Einstellung zw. 1 und 2ms lang einschaltet
1
/*
2
 * servo_timer.c
3
 *
4
 * Created: 09.04.2012 22:13:40
5
 *  Author: Thomas
6
 */ 
7
8
///////////////////////////////////////////////////////////////////
9
//
10
// Servo Steuerung mit Timer / Interrupts
11
//
12
///////////////////////////////////////////////////////////////////
13
14
#ifndef F_CPU
15
  #define F_CPU 16000000      //CPU Taktfrequenz
16
#endif
17
18
#define PWM_MIN 1000        //Min. PWM Zeit in US
19
#define PWM_NULL 1500        //Neutralposition - PWM Zeit in US
20
#define PWM_MAX  2000        //Max- PWM Zeit in US
21
#define PWM_LOOP 10          //Signal Widerholungen
22
23
#include <avr/io.h>
24
#include <avr/interrupt.h>
25
#include "rncontrol.h"
26
27
int pwm_time=PWM_NULL;              //Globale Variable für PWM HIGH Time
28
29
int main(void)
30
{  
31
  sei();                    //Interrupts aktivieren
32
  
33
  DDRC |= 1 << PINC0;              //Status LED 1 aktivieren          
34
  PORTC &= ~(1<<PINC0);
35
  
36
  TCCR1B |= 1<<CS11 | 1<<WGM12;        //Prescaler: -> 50Hz
37
  OCR1A =  0x9C40;                 //Prescaler Settings
38
  TIMSK |= 1<<OCIE1A;              
39
  
40
  DDRB |= 1<<PINB0;              //Ausgang für Servo aktivieren
41
  
42
43
    while(1)
44
    {
45
        if (button()==1)
46
      pwm_time=PWM_MAX;
47
    else if(button()==2)
48
      pwm_time=PWM_NULL;
49
    else if(button()==3)
50
      pwm_time=PWM_MIN;
51
      
52
    
53
    }
54
}
55
56
ISR(TIMER1_COMPA_vect)      //ISR für Servo PWM - durch Timerinterrupt kann während den ca. 20 ms Wartezeit etwas anderes gemacht werden
57
{
58
  int i;
59
  
60
  PORTC ^= 1<<PINC0;
61
  
62
  PORTB |= 1<<PINB0;      //PWM HIGH Signal
63
      
64
  for(i=0;i<pwm_time;i++)
65
    _delay_us(1);      //PWM Time warten
66
        
67
  PORTB &= ~(1<<PINB0);    //PWM LOW Signal
68
        
69
}

Doch das Servo fährt nur bis zum Endanschlag und will dann noch 
weiterdrehen. Was mache ich falsch?

mfg thomas

von check first (Gast)


Lesenswert?

Probier erstmal ob das Servo funzt, vielleicht ist es hin.

von Christian F. (cmf) Benutzerseite


Lesenswert?

Wo wird util/delay.h eingebunden? Optimierung aktiviert?

von Martin H. (martin_h85)


Lesenswert?

Guten Abend,

Du solltest die Interupts erst nach dem konfigurieren des Timers 
aktivieren.


Gruß Martin

von Thomas P. (thomas_p52)


Lesenswert?

Funktioniert ohne Probleme...habs schon mit folgendem Programm getestet:
1
#include <avr/io.h>
2
#include <util/delay.h>
3
4
#define POS 1500           //1000 <--> 2000 (1500 neutral)
5
6
void main()
7
{
8
DDRB |= 1<<PIND0;
9
10
    while(1)
11
    {
12
        PORTB |= 1<<PIND0;
13
        _delay_us(POS);
14
        PORTB &= ~(1<<PIND0);
15
        _delay_ms(18);
16
    }
17
}



ok, mal versuchen ;)

mfg thomas

von Christian F. (cmf) Benutzerseite


Lesenswert?

Thomas P. schrieb:
> Funktioniert ohne Probleme...habs schon mit folgendem Programm getestet:

Bin ich blind? Oben bindest du <util/delay.h> doch  nicht  ein?

von Wolfgang (Gast)


Lesenswert?

Thomas P. schrieb:
> Was mache ich falsch?

Läuft der Prozessor wirklich mit der Frequenz, die mit F_CPU angegeben 
ist?
Falls du kein Oszi hast, könntest du das testen, indem du einen 
1-Sekunden Takt auf einem Pin erzeugst und per LED anzeigen läßt.

von Karl H. (kbuchegg)


Lesenswert?

Hast du deine 16 Mhz kontrolliert?

von Karl H. (kbuchegg)


Lesenswert?

>   TCCR1B |= 1<<CS11 | 1<<WGM12;        //Prescaler: -> 50Hz

Was ist das für ein Vorteiler

>  OCR1A =  0x9C40;                 //Prescaler Settings

Schreibs doch dezmal hin! Dann braucht man nicht lange umrechnen, damit 
man deine Einstellung kontrollieren kann. Oder bist du so gut im 
Hex-Rechnen?

  OCR1A = 40000;

von Thomas P. (thomas_p52)


Lesenswert?

Ohhhh damt... <util/delay.h> einbinden vergessen.....

aber das war trotzdem nicht der Fehler, weil in "rncontrol.h" wird es 
bereits verwendet, also bereits eingebunden!

Wie kann ich die Optimierung einstellen?

mfg thomas

von Thomas P. (thomas_p52)


Lesenswert?

Ne, hab kein Oszi :(
Aber ja, CPU läuft mit 16 MHz

Prescaler Settings: http://www.et06.dk/atmega_timers/ ;)
Ok, werd ich mir angewöhnen

mfg thomas

von Karl H. (kbuchegg)


Lesenswert?

Thomas P. schrieb:

> Prescaler Settings: http://www.et06.dk/atmega_timers/ ;)

Ist es wirklich so schwer hinzuschreiben, dass CS11 alleine ein 
Vorteiler von 256 oder 128 oder was weiß ich ist?

von Karl H. (kbuchegg)


Lesenswert?

>   for(i=0;i<pwm_time;i++)
>    _delay_us(1);      //PWM Time warten

die For-Schleife wird hier bereits einen nicht unbedeutenden Anteil an 
der Gesamtzeit haben. Die Positionen werden nicht ganz stimmen, aber das 
ist jetzt noch nicht das Problem.

von Karl H. (kbuchegg)


Lesenswert?

Thomas P. schrieb:
> Ne, hab kein Oszi :(
> Aber ja, CPU läuft mit 16 MHz

Woher weißt du das?

Mit dem Programm
Beitrag "Re: Servoansteuerung mit Timer (ATmega32)"

Nö. Wenn das hier funktioniert und du nicht die 16 Mhz in den Project 
Settings angegeben hast (was ich bezweifle, denn dann wüsstest du wo man 
den Optimizer einschaltet, ist gleich darunter), dann nicht. Dann läuft 
dein µC mit 1Mhz

von Thomas P. (thomas_p52)


Lesenswert?

Ja ich weiß, ich bin Anfänger....

Ja, wie könnte man das sonst lösen? Denn _delay_us bzw _ms lässt keine 
variablen zu, nur konstanten und meine PWM Ausgänge am µC kann ich nicht 
verwenden, da bereits ein Motortriber verwendet wird...

mfg thomas

von Karl H. (kbuchegg)


Lesenswert?

Thomas P. schrieb:
> Ja ich weiß, ich bin Anfänger....
>
> Ja, wie könnte man das sonst lösen? Denn _delay_us bzw _ms lässt keine
> variablen zu, nur konstanten und meine PWM Ausgänge am µC kann ich nicht
> verwenden, da bereits ein Motortriber verwendet wird...

Du hast noch einen 2-ten Compare Match zur Verfügung.

Aber erst mal solltest du dich darum kümmern, ob die 16Mhz tatsächlich 
stimmen. Bis jetzt hab ich da aus der Ferne noch kein Vertrauen dazu.

von Thomas P. (thomas_p52)


Lesenswert?

Ich hab ne LED einfach mit 1Hz Blinken lassen....

Also:
1) Wo zum Teufl stellt man die F_CPU in den Project Settings ein?????
2) Wo sind die Project Settings???
3) Ich hab nur die Zeile #define F_CPU 16000000 mitkopieren vergessen...
4) Zur Info: ich verwende AVR Studio 5

mfg thomas

von Thomas P. (thomas_p52)


Lesenswert?

Inwieweit einen zweiten?
Welche Ausgänge sind das?

Ich verwende OC1A, OC1B, TOSC1, TOSC2, T0, T1 und OC für 
Frequenzabhängige Anwendungen, was bleibt denn dann noch über?

mfg thomas

von Karl H. (kbuchegg)


Lesenswert?

Thomas P. schrieb:
> Inwieweit einen zweiten?

OCR1B

> Welche Ausgänge sind das?

erst mal gar keine.
Erst mal sind das nur Interrupts, die bei bestimmten Zählerständen 
ausgelöst werden.

von Karl H. (kbuchegg)


Lesenswert?

> 3) Ich hab nur die Zeile #define F_CPU 16000000 mitkopieren vergessen...

Sowas lieben wir hier :-)
Das erleichtert das kontollieren der Einstellungen ungemein, wenn die 
Hälfte der relevanten Informationen fehlt :-)

von Thomas P. (thomas_p52)


Lesenswert?

Ja, weißt eh, war mit reiner Absicht ;)

Ok, aber die Interrupts kann ich dann ja auf Ausänge legen oder?

mfg thomas

PS: ich bin totaler Interrupt Anfänger :(

von Karl H. (kbuchegg)


Lesenswert?

Thomas P. schrieb:
> Ja, weißt eh, war mit reiner Absicht ;)
>
> Ok, aber die Interrupts kann ich dann ja auf Ausänge legen oder?

In den Interrupts kannst du dann ganz normal die Pins schalten, so wie 
du das jetzt auch machst.

In der Zwischenzeit hab ich mir im Datenblatt die Prescaler 
Einstellungen gesucht. CS11 ist ein Prescaler von 8, mit den 40000 
ergibt das die 20ms (eigentlich sollten das 39999 sein, spielt aber 
keine Rolle, die 20ms sind sowieso nicht wichtig).


Mach doch in deiner ISR erst mal einfach nur ein _delay_ms(1.5) zwischen 
den Port Operationen um zu sehen, ob die for-Schleife dir das Timing so 
weit versaut.

von Thomas P. (thomas_p52)


Lesenswert?

Jep...es ist die for Schleife...
mit _delay_us(1500); fährts wunderbar in die Mitte ;)

Was könntn wir da machen?

mfg thomas

von Werner (Gast)


Lesenswert?

mach doch hier
int pwm_time=PWM_NULL;              //Globale Variable für PWM HIGH Time

mal ein
volatile int pwm_time=PWM_NULL;

draus.

Wer weiß, was dein Compiler da optimiert.

von Werner (Gast)


Lesenswert?

Ein delay in einer ISR ist übrignes nicht so richtig schick.
Auf Dauer solltest Du Dir was anderes angewöhnen.

von Thomas P. (thomas_p52)


Lesenswert?

Ja und wie soll ich das dann lösen?

mfg thomas

von Karl H. (kbuchegg)


Lesenswert?

Thomas P. schrieb:
> Jep...es ist die for Schleife...
> mit _delay_us(1500); fährts wunderbar in die Mitte ;)
>
> Was könntn wir da machen?

anstatt eines 1µs delays erst mal ein 10µs delay.
Dafür die Schleifenwiederholungen entsprechend reduzieren.


(Der ganze Ansatz mit dem _delay in der ISR ist nicht zielführend. Da 
kannst du dir die ganze ISR auch gleich sparen.)

von Karl H. (kbuchegg)


Lesenswert?

Thomas P. schrieb:
> Ja und wie soll ich das dann lösen?

zb mit 2 Compare Matches.
Der Match auf OCR1A schaltet den Pin auf 1, der Match auf OCR1B schaltet 
ihn wieder auf 0

von Thomas P. (thomas_p52)


Lesenswert?

Mit dem 10µs delay gehts nicht -> erhöht auf 50µs zu ungenau :(

Wie wäre da dann der Code?

mfg thomas

von Karl H. (kbuchegg)


Lesenswert?

Hier

  OCR1A =  0x9C40;
  TIMSK |= 1<<OCIE1A;

hast du den Compare Match für den Compare A eingerichtet.
Jetzt schaust du ins Datenblatt, ob das für den Comparematch B genauso 
geht und rechnest dir den Wert aus für 1.5ms

Hier

ISR(TIMER1_COMPA_vect)
{

hast du für den Compare Match A die ISR eingerichtet.
Wie wird das dann wohl für Compare Match B aussehen?

In der A setzt du den Pin auf 1, in der B wieder auf 0.

Einfacher als einem Baby den Schnuller klauen.

von MWS (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Einfacher als einem Baby den Schnuller klauen.
Das ist nicht Schnuller klauen, sondern Pferd von hinten durch's Auge 
erschießen :D

Das ist Schnuller klauen:
1
// ATMega32@16MHz
2
3
#include <avr/io.h>
4
5
int main(void)
6
{
7
  DDRD = (1<<PD5)|(1<<PD4);  // Servo 1 an PD5, Servo 2 an PD4
8
  TCCR1B = (1<<WGM13)|(1<<WGM12)|(1<<CS11);     // Prescaler 8
9
  TCCR1A = (1<<COM1A1)|(1<<COM1B1)|(1<<WGM11);  // Mode 14, ICR = Top
10
  ICR1 = 39999;     // Wiederholrate 50Hz
11
  OCR1A = 2000;     // Servoposition Servo 1, 2000 = 1.0ms
12
  OCR1B = 3000;     // Servoposition Servo 2, 3000 = 1.5ms
13
    while(1)
14
    {
15
       // Steuere Servos
16
    }
17
}

von Karl H. (kbuchegg)


Lesenswert?

MWS schrieb:
> Karl Heinz Buchegger schrieb:
>> Einfacher als einem Baby den Schnuller klauen.
> Das ist nicht Schnuller klauen, sondern Pferd von hinten durch's Auge
> erschießen :D

[ ] Ich hab weiter oben gelesen, dass er die Pins nicht frei hat, ist
    mir aber Wurscht, ich geb meinen Senf trotzdem dazu.
[ ] Ich habs nicht gelesen und muss mich trotzdem einbringen

von Christian F. (cmf) Benutzerseite


Lesenswert?

Karl Heinz Buchegger schrieb:
> MWS schrieb:
>> Karl Heinz Buchegger schrieb:
>>> Einfacher als einem Baby den Schnuller klauen.
>> Das ist nicht Schnuller klauen, sondern Pferd von hinten durch's Auge
>> erschießen :D
>
[ ] Ich hab weiter oben gelesen, dass er die Pins nicht frei hat, ist
    mir aber Wurscht, ich geb meinen Senf trotzdem dazu.
[x] Ich habs nicht gelesen und muss mich trotzdem einbringen

Ist doch klar...

von Thomas P. (thomas_p52)


Lesenswert?

Eben..Pins sind bereits belegt

Also wie soll ich das ganze angehen?
Denn so funktionierts mal nicht ;)
1
/*
2
 * servo_timer.c
3
 *
4
 * Created: 09.04.2012 22:13:40
5
 *  Author: Thomas
6
 */ 
7
8
///////////////////////////////////////////////////////////////////
9
//
10
// Servo Steuerung mit Timer / Interrupts
11
//
12
///////////////////////////////////////////////////////////////////
13
14
#ifndef F_CPU
15
  #define F_CPU 16000000      //CPU Taktfrequenz
16
#endif
17
18
#define PWM_MIN 700        //Min. PWM Zeit in US
19
#define PWM_NULL 1500        //Neutralposition - PWM Zeit in US
20
#define PWM_MAX  2300        //Max- PWM Zeit in US
21
#define PWM_LOOP 10          //Signal Widerholungen
22
23
#include <avr/io.h>
24
#include <avr/interrupt.h>
25
#include <util/delay.h>
26
#include "rncontrol.h"
27
28
volatile int pwm_time=PWM_NULL;          //Globale Variable für PWM HIGH Time
29
30
int main(void)
31
{  
32
 
33
  DDRC |= 1 << PINC0;          //Status LED 1 aktivieren          
34
  PORTC &= ~(1<<PINC0);
35
  DDRC |= 1 << PINC1;          //Status LED 2 aktivieren
36
  PORTC |= 1<< PINC1;
37
  
38
  //Timer 1 - 20ms
39
  TCCR1B |= 1<<CS11 | 1<<WGM12;      //Prescaler: -> 50Hz
40
  OCR1A =  40000;            //Prescaler Settings
41
  TIMSK |= 1<<OCIE1A;    
42
  
43
  //Timer 2 - 1.5 ms
44
  TCCR1B |= 1<<CS10 | 1<<WGM12;      //Prescaler -> 666Hz
45
  OCR1B = 24000;  
46
  TIMSK |= 1<<OCIE1B;
47
  
48
  DDRB |= 1<<PINB0;            //Ausgang für Servo aktivieren
49
  
50
  sei();                //Interrupts aktivieren
51
  
52
    while(1)
53
    {
54
        if (button()==1)
55
      pwm_time=PWM_MAX;
56
    else if(button()==2)
57
      pwm_time=PWM_NULL;
58
    else if(button()==3)
59
      pwm_time=PWM_MIN;
60
      
61
    
62
    }
63
}
64
65
ISR(TIMER1_COMPA_vect)      //ISR für Servo PWM - durch Timerinterrupt kann während den ca. 20 ms Wartezeit etwas anderes gemacht werden
66
{  
67
  PORTB |= 1<<PINB0;      //PWM HIGH Signal
68
  PORTC ^= 1<<PINC0;
69
}
70
71
ISR(TIMER1_COMPB_vect)    //ISR für Timer B
72
{
73
  PORTB &= ~(1<<PINB0);  //PWM LOW Signal
74
    PORTC ^= 1<<PINC1;
75
}

Das Servo tickert nur, bewegt sich aber nicht

mfg thomas

von MWS (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> [ ] Du hast weiter oben gelesen, dass er die Pins nicht frei hat

Nein, war versteckt, tatsächlich nicht gelesen, wäre vom Eingangspost 
her auch nicht davon ausgegangen. Die OC1A..B hängen am LM298 und dienen 
dort zur PWM-Steuerung, Timer1 wäre also nicht verfügbar, sollte man die 
vernünftig nutzen wollen.

> [X] Ich habs nicht gelesen und muss mich trotzdem einbringen

Hab' ich doch gern gemacht :D

von Karl H. (kbuchegg)


Lesenswert?

> //Timer 1 - 20ms
  TCCR1B |= 1<<CS11 | 1<<WGM12;      //Prescaler: -> 50Hz
  OCR1A =  40000;            //Prescaler Settings
  TIMSK |= 1<<OCIE1A;

  //Timer 2 - 1.5 ms
  TCCR1B |= 1<<CS10 | 1<<WGM12;      //Prescaler -> 666Hz
  OCR1B = 24000;



Ähm. Du hast da was falsch verstanden.
Du hast nur EINEN Timer 1.
Der hat aber 2 Stück Compare Match Register (A und B). Wenn der 
Timerzählerstand identisch mit dem jeweiligen Compare Match Register 
ist, dann wird die ISR ausgelöst.

Zusätzlich hast du dann noch den Timer in den CTC Modus geschaltet, 
sodass er beim Compare Match A wieder bei 0 zu zählen anfängt. (Was ja 
auch in Ordnung ist)


Das ist dein Setup mit dem du arbeiten musst.
Mit dem A-Compare Match machst du dir deine 20ms und setzt den Pin auf 
1. Und irgendwann, während der Timer von 0 bis 40000 zählt, schaltet der 
Compare Match B den Pin wieder auf 0. Der Timer zählt aber weiterhin bis 
40000.

von Thomas P. (thomas_p52)


Lesenswert?

Ohhh :D

Also ich hab das jetzt mal so umgesetzt:
Timersettings:
1
  //Timer Compare Match A - 20ms
2
  TCCR1B |= 1<<CS11 | 1<<WGM12;      //Prescaler: -> 50Hz
3
  OCR1A =  40000;              //Prescaler Settings
4
  TIMSK |= 1<<OCIE1A;
5
  
6
  //Timer Compare Match B - 1.5 ms
7
  TCCR1B |= 1<<CS10 | 1<<WGM12;      //Prescaler -> 666Hz
8
  OCR1B = 24000;
9
  TIMSK |= 1<<OCIE1B;

Und die Interrupts:
1
ISR(TIMER1_COMPA_vect)      //ISR für Servo PWM - durch Timerinterrupt kann während den ca. 20 ms Wartezeit etwas anderes gemacht werden
2
{  
3
  PORTB |= 1<<PINB0;      //PWM HIGH Signal
4
  PORTC ^= 1<<PINC0;
5
}
6
7
ISR(TIMER1_COMPB_vect)    //ISR für Timer B
8
{
9
  PORTB &= ~(1<<PINB0);  //PWM LOW Signal
10
    PORTC ^= 1<<PINC1;
11
}
Trotzdem tut sich nix :(
Das Servo zuckt nur herum, bewegt sich aber nicht und die LEDs blinken 
gemächlich (ca 2 - 3 Hz)

mfg thomas

von MWS (Gast)


Lesenswert?

Nachtrag
Der von mir gezeigte Code lässt sich übrigens problemlos umzustellen, 
hab's mal gemacht und das läuft hier mit 2 Servokanälen auf beliebigen 
Ports.

Aber ich denke einmal wenigstens Dir, Karl-Heinz, ist das aufgefallen, 
Du wolltest wohl lediglich nicht von Deinem Konzept ablenken, kein 
Problem für mich.

Karl Heinz Buchegger schrieb:
> In der A setzt du den Pin auf 1, in der B wieder auf 0.

Warum verschwendest Du einen Kanal, wenn Du ICR als Top nehmen kannst ? 
Reicht dem TO ein Kanal ?

von Karl H. (kbuchegg)


Lesenswert?

Thomas P. schrieb:


> Also ich hab das jetzt mal so umgesetzt:
> Timersettings:

>   //Timer Compare Match A - 20ms
>   TCCR1B |= 1<<CS11 | 1<<WGM12;      //Prescaler: -> 50Hz
>   OCR1A =  40000;              //Prescaler Settings
>   TIMSK |= 1<<OCIE1A;
>
>   //Timer Compare Match B - 1.5 ms
>   TCCR1B |= 1<<CS10 | 1<<WGM12;      //Prescaler -> 666Hz

gratuliere.
Damit sind deine 20ms zur Makulatur geworden. Und .... 1.5ms sind das 
auch nicht.

Du hast nur EINEN Timer. Also willst du den Vorteiler auch nur EINMAL 
einstellen! Der Vorteiler gehört zum Timer und nicht zum Compare Match. 
Der Compare Match schreit nur "Hier", wenn der Timer einen bestimmten 
Zählerstand erreicht hat. Aber die Compare Match Register haben nichts 
damit zu tun, wie schnell der Timer zählt.

(Jetzt ist es natürlich blöd, dass du bisher immer deinen Timer-Rechner 
zur Berechnung der Konstanten herangezogen hast. Sorry ... aber jetzt 
musst du selber ran und dir den Wert für OCR1B ausrechnen. Auf Dauer ist 
es immer besser, wenn man versteht was man da tut.)

von Karl H. (kbuchegg)


Lesenswert?

MWS schrieb:

> Aber ich denke einmal wenigstens Dir, Karl-Heinz, ist das aufgefallen,
> Du wolltest wohl lediglich nicht von Deinem Konzept ablenken, kein
> Problem für mich.

Spätestens nachdem ich auf die Frage nach dem Vorteiler als Antwort 
einen Link zu einem Timer-Rechner gekriegt habe, war mir klar, dass der 
TO von Timer, sagen wir mal, wenig Ahnung hat.

> Karl Heinz Buchegger schrieb:
>> In der A setzt du den Pin auf 1, in der B wieder auf 0.
>
> Warum verschwendest Du einen Kanal, wenn Du ICR als Top nehmen kannst ?
> Reicht dem TO ein Kanal ?

Keine Ahnung. Lass uns erst mal das Ding so wie es ist, über die Bühne 
bringen. Er hat schon genug damit zu tun, 2 OCR Register an einem Timer 
zu akzeptieren.

von Thomas P. (thomas_p52)


Lesenswert?

Ok...das heißt...ich komm nicht mit ;)

Also wie muss ich das Ganze jetzt machen?
Also ich erzeug mit OCR1A einen Interrupt alle 20ms mit dem ich den Pin 
auf HIGH ziehe....
Und mit OCR1B einen der alle 1.5ms (1...2 ms) den Pin auf LOW legt...

also muss es so passen?
1
  //Timer Compare Match A - 20ms
2
  TCCR1B |= 1<<CS11 | 1<<WGM12;      //Prescaler: -> 50Hz
3
  OCR1A =  40000;              //Prescaler Settings
4
  TIMSK |= 1<<OCIE1A;
5
  
6
  //Timer Compare Match B - 1.5 ms
7
  OCR1B = 24000;
8
  TIMSK |= 1<<OCIE1B;

mfg thomas

von Karl H. (kbuchegg)


Lesenswert?

Thomas P. schrieb:

> also muss es so passen?

Ohne das ich rechne.

Wenn das ...
>   OCR1A =  40000;              //Prescaler Settings
... 20 Millisekunden sind, dann können ...
>   OCR1B = 24000;
... keine 1.5 Millisekunden sein. 24-tausend ist etwas mehr als die 
Hälfte von 40-tausend. 1.5 ist aber sicher nicht etwas mehr als die 
Hälfte von 20.

Der Timer zählt in 20 Millisekunden von 0 bis 40000. Wie weit kann er 
daher in 1.5 Millisekunden zählen? Bzw. Umgekehrt: wie weit muss er 
zählen, damit er dafür 1.5ms braucht?

von Thomas P. (thomas_p52)


Lesenswert?

Nja einfache Schlussrechnung:
20ms .... 40000
1.5ms ... 3000

Also so läuft das Ganze ab ;)
Und jetzt funktioniert das auch :D

danke euch allen
Ich werde euch noch mein fertiges Programm mit Steuerung posten!

mfg thomas

von MWS (Gast)


Lesenswert?

Thomas P. schrieb:
> also muss es so passen?

Ohne dass ich mein Werk ein weiteres Mal anpreisen möchte, aber fällt 
Dir was auf ? :D

MWS schrieb:
> ICR1 = 39999;     // Wiederholrate 50Hz
>   OCR1A = 2000;     // Servoposition Servo 1, 2000 = 1.0ms
>   OCR1B = 3000;     // Servoposition Servo 2, 3000 = 1.5ms

von Karl H. (kbuchegg)


Lesenswert?

Thomas P. schrieb:
> Nja einfache Schlussrechnung:
> 20ms .... 40000
> 1.5ms ... 3000
>
> Also so läuft das Ganze ab ;)
> Und jetzt funktioniert das auch :D

Jetzt setzt du noch anstelle der 40000 die Zahl 39999 ein, dann stimmts 
auch in diesem Detail noch.

Der Timer zählt (am Beispiel '8')

 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 1, 2,

Zähl nach, wieviele Zählvorgänge wurden gemacht, um einmal von 0 bis 
wieder zur 0 zu kommen? Auch das 'Rücksetzen' von 8 auf 0 ist ein 
Zählvorgang. 9 Stück. Also immer um 1 mehr als die größte Zahl. Der 
Compare Match muss bei 39999 auslösen, damit für einmal 'Rundum' 40000 
Zählvorgänge benötigt werden. OK, der Fehler ist nicht gewaltig groß und 
wie gesagt, dem Servo sind die 20ms ziemlich Wurscht.

> Also so läuft das Ganze ab ;)

Genau so läuft das mit einem Timer. Die nächste Stufe wäre das was MWS 
vorgeschlagen hat. Die Timerhardware kann nämlich 'ihren' Pin auch ganz 
alleine ohne ISR schalten. Aber leider, leider, hast du dir die Pins mit 
denen das möglich wäre bereits anderweitig verbaut.


Nichts desto trotz hab ich das Gefühl, dass du jetzt ein bischen besser 
verstehst, was man mit einem Timer machen kann und wie man das macht.

von Thomas P. (thomas_p52)


Lesenswert?

Nja, ich habs mi nicht verbaut - hab das RN Control Board, und da werden 
diese Pins schon für den Motortreiber verwendet, und den möchte ich auch 
nutzen ;)

Ja, um einiges besser :D

Wie versprochen das Programm mit Links/Mitte/Rechts Steuerung fürs RN 
Control Board:
1
/*
2
 * servo_timer.c
3
 *
4
 * Created: 09.04.2012 22:13:40
5
 *  Author: Thomas
6
 */ 
7
8
///////////////////////////////////////////////////////////////////
9
//
10
// Servo Steuerung mit Timer / Interrupts
11
//
12
///////////////////////////////////////////////////////////////////
13
14
#ifndef F_CPU
15
  #define F_CPU 16000000      //CPU Taktfrequenz
16
#endif
17
18
#define PWM_MIN 700        //Min. PWM Zeit in US
19
#define PWM_NULL 1500        //Neutralposition - PWM Zeit in US
20
#define PWM_MAX  2300        //Max- PWM Zeit in US
21
22
#include <avr/io.h>
23
#include <avr/interrupt.h>
24
#include <util/delay.h>
25
#include "rncontrol.h"
26
27
volatile int pwm_time=PWM_NULL;          //Globale Variable für PWM HIGH Time
28
29
int main(void)
30
{    
31
  //Timer Compare Match A - 20ms
32
  TCCR1B |= 1<<CS11 | 1<<WGM12;      //Prescaler: -> 50Hz
33
  OCR1A =  39999;              //Timer zählt bis da - 0 Setzung und Interrrupt - Größte Zahl - 1 weil Rücksetzung auch Zählvorgang
34
  TIMSK |= 1<<OCIE1A;            //Interrupt durh Timer 1 A
35
  
36
  //Timer Compare Match B - 1.5 ms
37
  OCR1B = 3000;              //Timer zählt bis da - Interrupt - KEINE 0 Setzung
38
  TIMSK |= 1<<OCIE1B;            //Interrupt durch Timer 1 B
39
   
40
  DDRB |= 1<<PINB0;            //Ausgang für Servo aktivieren
41
  
42
  sei();                     //Interrupts aktivieren
43
  
44
    while(1)
45
    {
46
    OCR1B = (39999/20000)*pwm_time;    //Positionsberechnung
47
    
48
        if (button()==1)
49
      pwm_time=PWM_MAX;
50
    else if(button()==2)
51
      pwm_time=PWM_NULL;
52
    else if(button()==3)
53
      pwm_time=PWM_MIN;
54
      
55
    
56
    }
57
}
58
59
ISR(TIMER1_COMPA_vect)      //ISR für Servo PWM - durch Timerinterrupt kann während den ca. 20 ms Wartezeit etwas anderes gemacht werden
60
{  
61
  PORTB |= 1<<PINB0;      //PWM HIGH Signal
62
}
63
64
ISR(TIMER1_COMPB_vect)    //ISR für Timer B
65
{
66
  PORTB &= ~(1<<PINB0);  //PWM LOW Signal
67
}

DANKE noch mal!

mfg thomas

von Karl H. (kbuchegg)


Lesenswert?

>  OCR1B = (39999/20000)*pwm_time;    //Positionsberechnung

Das ist schlecht.
erstens: du hast wirklich 40000 Zählvorgänge. Also musst du auch mit 
40000 rechnen.

zweitens 39999 / 20000 ergibt 1
und nicht 1.99995

d.h. bei der danach folgenden Multiplikation hast du durch die fehlenden 
Kommastellen einen gewaltigen Fehler. Wenn pwm_time den Wert 1500 hat, 
dann kommt bei deiner Rechnung 1500 raus, wo eigentlich 2999 rauskommen 
sollte!

Divisionen immer so weit es geht im Rechenprozess nach rechts 
verschieben. Dabei aber mögliche Overflows im Auge behalten! Und in 
deinem Fall: kürzen!

 ->   4 * pwm_time / 2
 ->   2 * pwm_time

das ist dann sogar insofern Spitze, weil eine Multiplikation mit 2 für 
den µC ein Klacks ist.

Bei deinen Zahlen geht sich das jetzt gut aus, denn 40000/20000 hätte 
auch eine glatte 2 ergeben. Aber bei anderen Zahlen schauts da dann 
schon schlechter aus.

von MWS (Gast)


Lesenswert?

Thomas P. schrieb:
> hab das RN Control Board, und da werden
> diese Pins schon für den Motortreiber verwendet, und den möchte ich auch
> nutzen ;)

Und wie machst Du das ? Denn die OC1A..B gehen an den EN-Eingänge des 
L239, damit wird offenbar die Leistungsregelung der Motoren über HW-PWM 
gemacht.

Dafür würde aber zwingend Timer1 benötigt, den Du ja jetzt anders 
verwurstet hast. Wenn Du die Leistung der Motoren noch regeln willst, 
müsstest Du also eine Soft-PWM über PD4..5 an den Motortreiber 
randengeln. Die dürfte aber anspruchsvoller und vor allem 
Leistungs-hungriger als diese Sache hier sein.

von Thomas P. (thomas_p52)


Lesenswert?

Ja ok, wär noch zu verbessern, aber es funktioniert !!

Ich will das Programm auch sozusagen Robotertauglich machen indem ich es 
auf die 8 Bit Timer umprogrammiere - und somit sind die Pins ja wieder 
frei, oder nicht?

mfg thomas

von MWS (Gast)


Lesenswert?

Thomas P. schrieb:
> Ich will das Programm auch sozusagen Robotertauglich machen indem ich es
> auf die 8 Bit Timer umprogrammiere - und somit sind die Pins ja wieder
> frei, oder nicht?

Das bedeutet jetzt übersetzt was ? Das Programm oben ? Bekommst Du 
direkt übersetzt nicht auf 'nen 8Bit Timer, geht allein von der 
Auflösung nicht.

Du hast 3000 für 1.5ms bei 16Bit, bei 8Bit wären's bei gleichen 
Bedingungen 3000 durch 256 gleich 12, bei 'nem theoretischen Prescaler 
von 2048. Der höchste ist 1024, damit hättest Du ca. 16 Stufen für 180 
Grad Servodrehung, bist aber mit der Wiederholrate zu schnell, die 
meisten Servos packen das. Aber dieser Ansatz wird Dich nicht glücklich 
machen.

von Thomas P. (thomas_p52)


Lesenswert?

Direkt schon klar, aber es wird ja wohl möglich sein die ca 180° auf 128 
bzw am besten wären 256 Schritte auszudehnen, oder?

mfg thomas

von MWS (Gast)


Lesenswert?

Thomas P. schrieb:
> Direkt schon klar, aber es wird ja wohl möglich sein die ca 180° auf 128
> bzw am besten wären 256 Schritte auszudehnen, oder?

Dann versuch doch mal zu rechnen, es gibt Beschränkungen bezüglich der 
möglichen Prescaler und schau auch mal was an Wiederholrate raus kommt 
und ob Deine Servos da noch mitmachen. Das Beste, was Du direkt bekommen 
kannst sind so um die 60 Schritte bei ca. 240Hz Wiederholrate.

von MWS (Gast)


Lesenswert?

Nur so als Anhaltspunkt, das Konzept wie oben lässt sich auf Timer0 
umsetzen, 65 Schritte (93-125) von 1-2ms, Wiederholrate ~49Hz, Prescaler 
dazu immer während des Betriebs zwischen 256 auf 1024 umschalten. Der 
Code dazu ist noch recht einfach, also frisch an's Werk ;-)

von Thomas P. (thomas_p52)


Lesenswert?

Gut, danke für den Tipp
Ich werd mich dann mal an die Arbeit machen und meine Fortschritte 
posten

mfg thomas

von Thomas P. (thomas_p52)


Lesenswert?

Hallo
Ich hab versucht das Ganze in die Tat umzusetzten, ohne Erfolg :(

Wie muss ich den Code denn ungefähr aufbauen?

mfg thomas

von MWS (Gast)


Lesenswert?

Thomas P. schrieb:
> Wie muss ich den Code denn ungefähr aufbauen?

Setz' es halt erstmal um den Timer0 herum so auf, dass die Pulslänge 
zwischen 1-2ms bei maximal möglicher Auflösung verschoben werden kann, 
unabhängig von der Wiederholrate. Kannst ja den Code dann zeigen.

Wenn das geht, dann schaltest Du nach jedem Overflow zwischen dem von 
Dir dafür verwendeten und dem maximalen Prescaler hin und her. Wobei der 
Pin nicht in der langen Prescalerphase gesetzt werden darf.

Hab's mal gezählt, in den ISR's befinden sich gerade 7 Zeilen aktiver 
Code, ist also nix Komplexes.

von STK500-Besitzer (Gast)


Lesenswert?

MWS schrieb:
> Wenn das geht, dann schaltest Du nach jedem Overflow zwischen dem von
> Dir dafür verwendeten und dem maximalen Prescaler hin und her. Wobei der
> Pin nicht in der langen Prescalerphase gesetzt werden darf.

Man könnte auch einfach in einer Variablen die Anzahl der Umläufe 
zählen...

von MWS (Gast)


Lesenswert?

STK500-Besitzer schrieb:
> Man könnte auch einfach in einer Variablen die Anzahl der Umläufe
> zählen...

Klar, es gibt viele Wege nach Rom. Da ich zum Toggeln ein Prescalerbit 
teste, brauch ich aber nicht mal 'ne Variable :D

von Thomas P. (thomas_p52)


Angehängte Dateien:

Lesenswert?

Hallo

Ich habs endlich geschaft :)

Der Code ist im Anhang, funktioniert mit einem Servo. Das ganze reicht 
im Moment mal für meinen kleinen "Ketten-Bot". Brauch ich nur zum hin 
und her Bewegen des US Moduls
Als nächstes möchte ich mir mit Hilfe eines AVR mit vielen Timern ein 
Servo Board realisieren, von dem werdet ihr dann natürlich hören!
Welcher AVR ist dazu am Besten geeignet?

Danke für eure Hilfe!!

mfg thomas

von STK500-Besitzer (Gast)


Lesenswert?

Thomas P. schrieb:
> Als nächstes möchte ich mir mit Hilfe eines AVR mit vielen Timern ein
> Servo Board realisieren, von dem werdet ihr dann natürlich hören!
> Welcher AVR ist dazu am Besten geeignet?

Der gleiche. Nur die Software sieht etwas anders aus...
Du programmierst ein Schieberegister, das bei jedem Überlauf um einen 
Schritt weiter geschaltet wird.
Dann brauchst du auch die Umschaltung der beiden Prescaler-Werte nicht 
mehr zu realisieren...

von Thomas P. (thomas_p52)


Angehängte Dateien:

Lesenswert?

Ja, aber das hat noch Zeit ;)
Zuerst muss mal mein Bot fertig werden....

Ich hab jetzt den Code (Annhang) in eine Routine umgebaut und somit 
leichter zu handhaben!

Aber welcher AVR ist am besten für ein Servo Board für ca 10 Servos 
geeignet?

mfg thomas

von Markus W. (Firma: guloshop.de) (m-w)


Lesenswert?

Thomas P. schrieb:
> Als nächstes möchte ich mir mit Hilfe eines AVR mit vielen Timern ein
> Servo Board realisieren, von dem werdet ihr dann natürlich hören!
> Welcher AVR ist dazu am Besten geeignet?

Es gibt zwar ATmegas mit 3, 4 oder 6 Timern, aber ich bin nicht sicher, 
ob das der richtige Weg ist. Ich glaube, ich würde nur einen Timer 
verwenden und in dessen ISR die Pins für die Servos selbst steuern. 
Also, PWM von Hand. Das ist gar nicht sooo kompliziert und läuft dann 
auch auf den billigsten Mikrocontrollern.

von Thomas P. (thomas_p52)


Lesenswert?

Inwieweit von Hand?
Sozusagen:
if(timer == 0) {alle Pins LOW}
if(timer >= 15000) {PIN1 LOW}
usw...
??

mfg thomas

von Markus W. (Firma: guloshop.de) (m-w)


Lesenswert?

Thomas P. schrieb:
> Inwieweit von Hand?
> Sozusagen:
> if(timer == 0) {alle Pins LOW}
> if(timer >= 15000) {PIN1 LOW}
> usw...
> ??

Im Prinzip ja. Aber natürlich nur im Prinzip, sonst schreibst du dir ja 
einen Wolf. :-)
Man kann sowas über Bitmasken machen, die in einer Tabelle stehen. Oder 
über Einzelabfragen mit Wertvergleichen. Ich kenn leider deine Anwendung 
zu wenig, als dass ich dir zu einer konkreten Strategie raten könnte.

von FastPWM (Gast)


Lesenswert?

Hallo zusammen,

sorry, wenn ich einfach so anonym dazwischen funke.

@ThomasP.: Professionell ist die Lösung mit Deinem Code überhaupt nicht.

Ein atmega32 kann im FastPWM Mode ohne(!) ISR-Routine ein PWM-Signal 
selbständig erzeugen, es frisst keinerlei Rechenzeit vom normalen 
Programmablauf!

Du schaltest im Setup auf FastPWM-Mode für z.B. Timer1 und setzt:

ICR1 --> Maximum Counter(=TOP), bis hierher wird gezählt
OCR1A --> MATCH, Umschalten von high auf low
TOIE --> abschalten, Interrupt wird nicht benötigt!

Prescaling und passende Werte für TOP und MATCH mußt Du Dir natürlich 
ausrechnen.
OC1A verwendet ausschliesslich Pin PD5 (siehe Datenblatt)!

Ablauf dann:
Count von 0 bis MATCH   -> PD5 = HIGH
Count von MATCH bis TOP -> PD5 = LOW
usw.

Um den Servo links/rechts drehen zu lassen, veränderst Du im normalen 
Programmablauf einfach den MATCH in OCR1A - fertig!

Eleganter geht's nicht mehr!

Just my 2 cents ;)

von Modellbauer (Gast)


Lesenswert?

Thomas P. schrieb:
> Aber welcher AVR ist am besten für ein Servo Board für ca 10 Servos
> geeignet?

"Am besten" ist immer so eine Sache. Die Steuerung mit dem 
Schieberegister ist nicht die schlechteste Idee (z.B. ATmega8 + 2x 
74HC595) oder wie wäre es mit einer Softwarelösung:
Beitrag "Servocontroller mit ATmega8 für 20 Servos"
http://www.4finger.net/cms/servomaster.html

von Thomas P. (thomas_p52)


Lesenswert?

Hallo

@FastPWM:
1) Ich bin Anfänger und bin unheimlich stolz drauf gewesen, dass es so 
funktioniert hat ;)
2) Geht das nur für Timer1 odr auch für Timer0?
3) Wie soll ich das ohne ISR aufbauen?
4) Falls du es nicht gelesen, von dem ich ausgehe, bei mir ist Timer1 in 
verwendung für Motorensteuerung => somit ist auch OC1A und OC1B, also 
PD4/5 belegt

Ok, danke für die Tipps, ich werde mal Schaltplan und Grundstruktur 
anfertigen und posten!

mfg thomas

von MWS (Gast)


Lesenswert?

FastPWM schrieb:
> @ThomasP.: Professionell ist die Lösung mit Deinem Code überhaupt nicht.

Die ist für ein einzelnes Servo absolut ok, wenn man keinen Timer1 zur 
Verfügung hat.

> Just my 2 cents ;)

Eher 0 Cents, wenn Du Dir den Thread nur ein klein wenig durchgelesen 
hast.

von STK500-Besitzer (Gast)


Lesenswert?

Thomas P. schrieb:
> 2) Geht das nur für Timer1 odr auch für Timer0?

Ist es so schwer danach mal im Datenblatt zu gucken?
Timer0 hat auch die Möglichkeit eine PWM zu erzeugen.

von STK500-Besitzer (Gast)


Lesenswert?

Du kannst Timer0 im FastPWM-Modus betreiben.
Ihn mit einer Spannweite von 20ms zu betreiben ist natürlich Unfug.
Wie ich oben schon beschrieben habe:
Betreibe den Timer so, dass du die 2ms am Höchsten auflöst.
Ob das Servo alle 20ms oder mehr oder weniger häufig angesprochen wird, 
war meinen Servos bis jetzt egal (je länger die Pause ist, umso 
schwächer wurde es).

von Thomas P. (thomas_p52)


Lesenswert?

Ja natürlich ist es nicht schwer, aber
1) keine Lust :b
2) Mein Programm läuft für ein Servo perfekt ;)
3) Bin ich im Moment nicht neugiereig drauf um auf 400 Seitn 3 Sätze rus 
zu suchen

Aber gut, dass machn wir ein anderes Mal, dann wenn ich das Servo Board 
angehe ;)

mfg thomas

von STK500-Besitzer (Gast)


Lesenswert?

Thomas P. schrieb:
> 1) keine Lust :b
Kenne ich von mir auch. Ich hab aber nicht gefragt...

> 2) Mein Programm läuft für ein Servo perfekt ;)
Bis zur ersten Erweiterung.

> 3) Bin ich im Moment nicht neugiereig drauf um auf 400 Seitn 3 Sätze rus
> zu suchen
Schon mal die Suchfunktion des Acrobat Reader's ausprobiert?
Schon mal das Inhaltverzeichnis angesehen?

Deine Entscheidung.

von Thomas P. (thomas_p52)


Lesenswert?

Wieso solls dann nicht mehr funktionieren?
Mein Roboter soll folgendes können:
fahren und nicht anfahren - aus und fertig :b
so was wie Linienverfolgung, Kamera, Funk usw. soll erst später kommen, 
dannn aber auch miit anderen Funktionen wie Greifarm usw.. Aber dann 
muss sowieso eine komplett andere Elektronik her...aber das hat noch 
Zeit!

Ja die kenn ich ;)

mfg thomas

von FastPWM (Gast)


Lesenswert?

Thomas P. schrieb:
> @FastPWM:
> 1) Ich bin Anfänger und bin unheimlich stolz drauf gewesen, dass es so
> funktioniert hat ;)

Das ist doch auch völlig OK - jeder hat wahrscheinlich so mal 
angefangen.

> 2) Geht das nur für Timer1 odr auch für Timer0?

siehe Datenblatt - alles vorkauen möchte ich hier nicht, vielmehr möchte 
ich nur Tipps geben für eine evtl. in Frage kommende, bessere Lösung.

> 3) Wie soll ich das ohne ISR aufbauen?

Wie gesagt, für einige Servos lässt sich das einfacher und ohne ISR 
bewerkstelligen. Daß es möglich ist, habe ich skizzenhaft angedeutet, 
den Rest müsstest Du (aus meiner Sicht) selbst erarbeiten. Für mehrere, 
z.B. 10 Servos ist die Frage, welchen Microcontroller man verwenden 
will. Natürlich hat dann auch das bereits vorgeschlagene Software-PWM 
seine Berechtigung, wenn es nur ein kleinerer Controller sein soll.

> 4) Falls du es nicht gelesen, von dem ich ausgehe, bei mir ist Timer1 in
> verwendung für Motorensteuerung => somit ist auch OC1A und OC1B, also
> PD4/5 belegt

Es ist eine Designfrage: Muß PD4/5 unbedingt belegt werden oder kann 
dasselbe nicht auch an anderen Pins bewerkstelligt werden? Vor allen 
Dingen gibt es ja auch noch andere Timer (und Counter!). wobei zu prüfen 
wäre, was die jeweils können.

Grundsätzlich wollte ich lediglich auf die Möglichkeit der völlig 
eigenständig laufenden PWM hinweisen. Zu diskutieren gibt es da 
eigentlich nichts - es steht ja auch alles im Datenblatt. :)

... just my 0(!) cents. ;)

von Thomas P. (thomas_p52)


Lesenswert?

Hallo

Ok, ich werd mich in nächster Zeit damit beschäftigen

Also nochmals zu 4)
Die Pins sind am aktuellen RN-Control Board belegt (fix)
Bei einem selbstgebauten Servocontroller dann nicht, aber das ist dann 
ein anderes Thema....

bis dann ;)

mfg thomas

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.