Hallo,
auch wenn es schon zig Beiträge über Servos gibt, hab ich nichts zu
meinem Problem gefunden.
Ich benutze einen AtMega8 mit internem 8MHz Takt. Ich habe das Programm
aus dem Wikibeitrag "Modellbauservo Ansteuerung" übernommen und leicht
angepasst zwecks der anderen Timings aufgrund des schnelleren
Prozessortaktes und des OCR1- Pins.
Das Servo soll zwischen "Neutral", "Linker Anschlag" und "Rechter
Anschlag" sich hin und her bewegen. Das klappt auch ein paar Durchgänge
(ca. 5), danach steht dass Servo jedoch für ca. 7s und läuft dann erst
weiter.
Anbei mein Code:
Der Timer läuft maximal in diesem Intervall über:
1 / (8 Mhz 256 256) = 8,2ms
Das Servo braucht aber einen Zyklius von ca 20mS. Also läuft der Timer
viel zu schnell.
Weiterhin verstehe ich den Interrupthandler nicht. Aber das ist erstmal
wurscht, denn du versucht da einen Wert >255 in ein 8 Bit Register zu
laden. Das kann nicht gehen.
Hi
>Aber das ist erstmal>wurscht, denn du versucht da einen Wert >255 in ein 8 Bit Register zu>laden. Das kann nicht gehen.
Timer1 ist ein 16Bit-Timer.
MfG Spess
Zitat:
It is important to notice that accessing 16-bit registers are atomic
operations. If an interrupt occurs between the two instructions
accessing the 16-bit register, and the interrupt code updates the
temporary register by accessing the same or any other of the 16-bit
Timer Registers, then the result of the access outside the interrupt
will be corrupted.
Therefore, when both the main code and the interrupt code update the
temporary register, the main code must disable the interrupts during the
16-bit access.
Ach so, ok. Also sollten wir doch mal einen Blick auf die
Interruptroutine werfen.
Ein Takt dauert 256*(1/8000000)=0,032ms, passt zu den obigen
Kommentaren.
1)
Der erste Output Compare tritt nach 578 Takten auf. Dann geht der
Ausgang auf High, nehme ich mal an. 578*0,032ms = 18,496ms
2)
Der Interrupthandler stellt das register auf 625-578 (also 47) um. Dann
geht der Ausgang wieder auf Low. Mal nachrechnen, wie lange das dauert:
Um vom momentanen Zählwert 578 zur 47 zu kommen, muss er einmal
überlaufen.
65536-578+47 = 65005 Takte.
65005 * 0,032ms = 2080,16 ms
Also zwei Sekunden !?! Mach da mal eine LED dran, das muss ja leicht
sichtbar sein.
Spielen wir das mal weiter durch.
3)
Der Interrupthandler setzte das Register nun auf 625-47 (also 578) um.
Dann geht der Ausgang wieder auf High (start des zweiten Impulses). Das
dauert (578-47) * 0,032ms = 16,99ms
Danach wiederholt sich der Vorgang bei 2).
Dass heisst, der Ausgang ist immer für 2080,16ms High und 16,99ms Low -
wenn ich mich nicht vertan habe. So kann es nicht funktionieren.
Gib uns mal einen Link auf den Artikel, den du meinst. Ich habe ihn
nämlich nicht gefunden. Ich vermute mal, dass sie dort den Timer so
getaktet hatten, dass er alle 20ms überläuft, was bei Dir jedoch ca 100
mal länger dauert.
stefanus schrieb:> 2)> Der Interrupthandler stellt das register auf 625-578 (also 47) um. Dann> geht der Ausgang wieder auf Low. Mal nachrechnen, wie lange das dauert:>> Um vom momentanen Zählwert 578 zur 47 zu kommen, muss er einmal> überlaufen.>> 65536-578+47 = 65005 Takte.> 65005 * 0,032ms = 2080,16 ms
Nö, der Mode ist CTC, der läuft nicht über, sondern TCNT1 wird auf 0
gesetzt und zählt von dort auf 47.
stefanus schrieb:> @MWS: Darum kümmert sich der C Compiler bereits.
Würde mich wundern, das erzeugt Overhead, wenn das TEMP-Register nicht
im Interrupt genutzt wird.
> Nö, der Mode ist CTC, der läuft nicht über,> sondern TCNT1 wird auf 0 gesetzt und zählt von dort auf 47.
Dann müsste es ja funktionieren. Es sei denn, die Hauptschleife
konkurriert tatsächlich mit den Interrupts. Bei den langen delays würde
ich jedoch davon ausgehen, dass das nur sehr selten passiert. Was
wiederum nicht zur Problembeschreibung passt.
Wie dem auch sei, probier mal den Ratschlag von MWS, Interupts in der
Hauptschleife temporär zu sperren, wenn Du das Compare register änderst.
Die Fuses (vor allem CLKDIV8) hast Du geprüft?
stefanus schrieb:> Bei den langen delays würde> ich jedoch davon ausgehen, dass das nur sehr selten passiert.
Das dachte ich mir auch, jedoch sollte man immer die offensichtlichen
Fehler zuerst beheben.
Allerdings, wenn der Zähler sich im Bereich des Servopulses bis z.B. 47
befindet und genau dann einen neuen Wert in der Main zugewiesen bekommt,
so zählt er weiter bis z.B. 563. Jetzt ist der Servokontrollpuls zu lang
und schlechter noch: er toggelt von nun an falsch, da eine Flanke
ausgelassen wurde. Das geht dann solange, bis sich der Fehler erneut
ereignet und die Flanken wieder richtig kommen.
In dieser Form taugt der Code nichts.
> schlechter noch: er toggelt von nun an falsch
Da hast Du einen gravierenden Fehler erkannt. Das könnte die Erklärung
des Rätsels sein - sehr warscheinlich, denke ich mal.
@Uwe: Schau Dir mal meinen Code an
(http://stefanfrings.de/servocontroller/index.html).
Ich lasse dort einen 8bit Timer im Fast-PWM Modus laufen, nutze jedoch
nicht die physikalischen PWM Ausgänge, sondern setze meine Ausgänge in
Interrupt-Handlern.
Der Timer läuft regelmäßig alle 4,1ms über. Der Interrupt Handler
bedient dabei immer einen von 5 Ausgängen wechselweise. So ergibt sich
ein Intervall von ungefähr 20ms pro Ausgang und ich habe 5 Ausgänge mit
nur einem Timer abgedeckt.
Durch Nutzung des zweiten Compare Registers, komme ich auf weitere 5
Ausgänge.
Im Hauptprogramm musst du nur die gewünschten Positionswerte in das
Array schreiben. Die Interrupt Routine überträgt sie zyklisch in das
Comapre-Register.
Hallo,
erstmal vielen Dank für alle Kommentare. Ich habe gelernt, der Code ist
Schrott, ich soll einen anderen machen....... ;-)
@Stefanus: Ich habe mir deinen Code angeschaut. Ich habe ihn an meine
Verhältnisse angepasst: da ich nen AtMega 8 verwende habe ich den 8bit-
Timer2 verwendet. Dann habe ich noch den Teil für Servo 6-9 gelöscht und
alles was die I2C- Übertragung betrifft. Außerdem habe ich den PORTD für
alle Servos verwendet.
Leider tut sich gar nichts. Ich habe als Wert die Neutralstellung
gewählt, aber es kommt anscheinend nichts an. Frequenz habe ich auf 4
MHz eingestellt.......
1
#include<stdio.h>
2
#include<stdint.h>
3
#include<avr/io.h>
4
#include<avr/interrupt.h>
5
6
7
// Generates PWM signals for up to 5 servos.
8
9
//using internal 4Mhz RC oscillator
10
11
// 2 PD0 servo 0
12
// 3 PD1 servo 1
13
// 4 PD2 servo 2
14
// 5 PD3 servo 3
15
// 6 PD4 servo 4
16
17
// PWM Values for the servos
18
volatileuint8_tservo[10];
19
20
// Timer 0 Compare A Interrupt
21
ISR(TIMER2_COMP_vect){
22
// Sets servo 0-4 pins to low
23
PORTD&=~1;
24
PORTD&=~2;
25
PORTD&=~4;
26
PORTD&=~8;
27
PORTD&=~16;
28
}
29
30
// Timer 2 Overflow Interrupt
31
ISR(TIMER2_OVF_vect){
32
staticuint8_tloop;
33
34
// Set servo pins to high, round-robin, two pins per interrupt
35
if(servo[loop]!=0){
36
switch(loop){
37
case0:PORTD|=1;break;
38
case1:PORTD|=2;break;
39
case2:PORTD|=4;break;
40
case3:PORTD|=8;break;
41
case4:PORTD|=16;break;
42
}
43
}
44
45
// Set timer compare values for the next (not the current) loop
46
if(++loop>4){
47
loop=0;
48
}
49
OCR2=servo[loop];
50
}
51
52
53
intmain(){
54
// Configure pin direction and pull-ups
55
DDRD=1|2|4|8|16;
56
57
58
// Timer 2: fast PWM, prescaler 64, IRQ on overflow, compare match A and compare match B.
59
TCCR2=(1<<WGM20)|(1<<WGM21)|(1<<CS11);
60
TIMSK=(1<<TOIE2)|(1<<OCIE2);
61
62
// Enable interrupts
63
sei();
64
65
66
while(1){
67
68
servo[1]=94;
69
70
}
71
}
Danke mal wieder für die Hilfe im vorraus.
Gruß Uwe
CS11 ist ein Bitname von Timer1, das läuft damit schneller als Prescaler
64 und servo[1] ist der zweite Servoausgang.
Und warum Prescale 64, sollte das nicht 128 sein?
Prescaler 64 ist schon richtig. 1/(4000000 Mhz/64/256) = 4ms. Prescaler
128 würde bei 8 Mhz Taktfrequenz richtig sein.
Es muss aber (1<<CS22) heissen, nicht (1<<CS11).
Nun ein bisschen Kritik an meinem eigenen Code :-)
Das kann man abkürzen:
1
PORTD &= ~1;
2
PORTD &= ~2;
3
PORTD &= ~4;
4
PORTD &= ~8;
5
PORTD &= ~16;
1
PORTD &= ~ (1|2|4|8|16)
Und das auch:
1
switch (loop) {
2
case 0: PORTD |= 1; break;
3
case 1: PORTD |= 2; break;
4
case 2: PORTD |= 4; break;
5
case 3: PORTD |= 8; break;
6
case 4: PORTD |= 16; break;
7
}
1
PORTD |= (1<<loop);
Aber für die Funktion spielt es erstmal keine Rolle.
stefanus schrieb:> Prescaler 64 ist schon richtig. 1/(4000000 Mhz/64/256) = 4ms. Prescaler> 128 würde bei 8 Mhz Taktfrequenz richtig sein.
Er nutzt 8 MHz Clock:
Uwe schrieb:> #define F_CPU 8000000UL> Damei steuerst Du den Zweiten Ausgang (Pin PD1) an
So schrub ich bereits:
MWS schrieb:> servo[1] ist der zweite Servoausgang.
MWS schrieb:> Er nutzt 8 MHz Clock:
Hmm, im Kommentar des zuletzt gezeigten Codes steht dagegen 4MHz,
entweder ein übriggebliebenes Textfragment oder den Tatsachen
entsprechend.
Maßgebend ist die Fuse, also hier noch der Hinweis:
Stimmt der Kommentar mit der Fuseeinstellung überein?
Hallo,
endlich konnte ich mich wieder dem Projekt widmen.
Das mit dem Port war mir klar, aber wie ihr richtig geschrieben habt lag
es an dem falschen CS11. Nachdem ich diesen Fehler korrigiert habe,
funktioniert es einwandfrei.
Vielen Dank für die Unterstützung und den Code.
Gruß Uwe