Hallo,
ich habe eine Frage zum Timer0 beim Attiny85.
Ich möchte um eine Periode von 20ms erzeugen und eine Pulsweite von 1ms
bis 2ms. Damit möchte ich meinen Servomotor für meine Eisenbahn
ansteuern.
Habe ich mir die Tutorials angeguckt. Vor allem die mit dem Timern
http://www.mikrocontroller.net/articles/AVR-Tutorial:_Timer
Aber ich bin mir nicht ganz sicher, ob ich es richtig verstanden habe
und ob ich das so machen kann, wie ich es mir überlegt habe.
Folgendes würde ich in meinem Code machen
CPU-Frequenz = 4MHz
TCCR = CS01 -> Vorteiler = 8
TIMSK = TOIE0 -> Interrupt beim Overflow auslösen
sei() -> Interrupts freigeben
Damit würde ich eine Periode von
t=(2^8*8)/4000000 = 0.000512s (~0.5ms) bekommen
Bei einem Interrupt würde ich folgendes machen
1
ISR(TIM0_OVF_vect){
2
zähler_Periode=zähler_Periode+1;
3
if(zähler_Periode>=40){// 20ms sind erreicht (40*0.5ms=20ms)
4
Zähler_Pulsweite=Zähler_Pulsweite+1;
5
if(Zähler_Pulsweite<=3){//Pulsweite ist 1,5ms
6
PB1HIGH
7
}
8
elseif(Zähler_Pulsweite<3){
9
PB1LOW
10
Zähler_Pulsweite=0;
11
Zähler_Periode=3;// eigentlich wieder auf 0 setzen, da ich aber schon wieder 3 Schritte (Zähler_Pulsweite) weitergezählt habe, springe ich auf 3.
12
}
13
}
14
}
In diesem Beispiel würde der Servo jetzt eine Position halten.
Meine Frage wäre, ob ich das so richtig verstanden habe und es so
umsetzen könnte? Mir geht es jetzt nicht um die richtige Schreibweise,
sondern mehr um die Logik (die ich versucht habe so gut wie möglich zu
beschreiben).
Bis dahin,
guten Abend und gute Nacht
Mark
Ein bisschen eleganter geht das schon:
Prescaler = 1024
20ms = 1/50Hz
4Mhz/1024/50Hz = 78
Timer0, Mode7, Fast PWM Top = OCR0A.
OCR0A = 78 - 1;
Damit zählt der Timer 78 Takte = 20ms und geht wieder auf 0.
78 / 20ms * 1,5ms = 5,85 gerundet 6
OCR0B = 6;
Das ist dein Puls von ca. 1,5ms.
Bei TCNT0 = 0 geht PB1 auf H, bei 6 auf L.
1
TCCR0A|=(1<<COM0B1)|(1<<WGM01)|(1<<WGM00);
2
TCCR0B|=(1<<WGM02)|(1<<CS02)|(1<<CS00);
3
OCR0A=77;
4
OCR0B=6;
5
DDRB|=(1<<1);
Damit hast du deinen Impuls von ~1,5ms mit 20ms Periode an PB1.
Macht der Timer alles ganz alleine in Hardware.
Zum Abschalten einfach den Pin auf Eingang schalten: DDRB &= ~(1 << 1);
Den OVF-Interrupt kannst du noch als 50Hz-Ticker verwenden, falls du das
brauchst.
mfg.
Thomas hat ja schon geschrieben, wie man das ganze dem Timer ganz
alleine aufhalsen könnte
Aber um auf das Ursprungs-Programm zurückzukommen.
Deine ISR ist falsch. Du hast dich da mit zu vielen Variablen selbst ins
Knie geschossen.
Worum gehts denn.
Du brauchst einen Zeitraum von 20 Millisekunden, von denen in den ersten
1.5ms ein Pin auf High sein soll und die restliche Zeit soll er auf Low
sein.
Durch deine (korrekte) Zeitberechnung, weisst du, dass die 20ms 40
Aufrufen der ISR entsprechen. Die ersten 3 Aufrufe der ISR soll der Pin
auf High gehalten werden, die restlichen 37 auf Low
d.h. du baust dir da jetzt erst mal einen Zähler zusammen, der ständig
immer nur von 0 bis 39 zählt.
1
ISR(TIM0_OVF_vect)
2
{
3
zähler_Periode++;
4
if(zähler_Periode==40)
5
zähler_Periode=0;
Die ersten 3 ISR Aufrufe sind aber leicht identifiziert. Denn die liegen
genau dann vor, wenn diese Variable einen der Werte 0, 1 oder 2 hat
Es gibt beim compilieren keinen Fehler, allerdings dreht sich der
servomotor gar nicht. Eigentlich müsste er sich jetzt mittig
positionieren.
hatte es auch versucht, indem ich in dem Code
1
intzaehler;
2
3
ISR(TIM0_OVF_vect)
4
{
5
zaehler++;
6
if(zaehler==77)
7
zaehler=0;
8
9
if(zaehler<6)
10
PORTB|=(1<<PB0);
11
else
12
PORTB&=~(1<<PB0);
13
}
einfüge.
und den Code wie ich es am anfang vorhatte, habe ich auch ausprobiert
1
#define F_CPU 4000000;
2
#include<avr/interrupt.h>
3
4
intzaehler;
5
6
ISR(TIM0_OVF_vect)
7
{
8
zaehler++;
9
if(zaehler==40)
10
zaehler=0;
11
12
if(zaehler<3)
13
PORTB|=(1<<PB0);
14
else
15
PORTB&=~(1<<PB0);
16
}
17
18
intmain(void){
19
DDRB|=(1<<PB0);// PB0 = Output
20
TIMSK|=(1<<TOIE0);// Interrupt beim Overflos auslösen
21
TCCR0B|=(1<<CS01);
22
sei();// Interrupts freigeben
23
while(1){
24
25
}
26
}
Aber diese sachen ergaben keinen Unterschied. Was habe ich noch falsch?
Kann mir da noch jemand einen tip geben?
Schon einmal vielen dank
Mark
Mark schrieb:> Aber diese sachen ergaben keinen Unterschied. Was habe ich noch falsch?> Kann mir da noch jemand einen tip geben?
Der Ausgang ist PB1(OC0B)!
Den Overflow-Int brauchst du für die PWM nicht. Den kannst du optional
verwenden, falls du z.B. noch etwas zum Blinken hast.
1
#define F_CPU 4000000UL
2
#include<avr/interrupt.h>
3
4
intmain(void){
5
DDRB|=(1<<PB1);// OC0B = PB1 Output
6
TIMSK|=(1<<TOIE0);// Interrupt beim Overflos auslösen
7
TCCR0A|=(1<<COM0B1)|(1<<WGM01)|(1<<WGM00);// COM0B1 -> Clear OC0A/OC0B on Compare Match,
Ah, jetzt funktioniert es. Ich hatte den Kontroller und den Servomotor
mit der selben Batterie betrieben. Da hat sich auch nichts bewegt. Jetzt
habe ich den Servomotor an einer externen Batterie angeschlossen und er
bewegt sich.
Jetzt gucke ich, dass ich die Winkel durch Tastendruck ändern kann und
evtl wie Thomas gefragt hat auch eine LED anzusteuern.
Aber bis hier hin, schon einmal ein sehr großes Dankeschön.
Ich habe noch einmal eine Nachfrage bezüglich des Parameters des OCR0B.
In diesem steckt ja im Prinzip die Pulsweite drinn.
Irgendwie passt die Rechnung aber bei mir hier nicht.
wenn ich OCR0B=6 -> 6*20ms/78~1,5ms (Servo steht in der Mitte)
OCR0B=4-> 1ms (Servo ist Links)
OCR0B=8-> 2ms (Servo ist rechts)
Allerdings durch ausprobieren fährt der Servo nur zwischen 6 und 16.9
und dabei keine kompletten 180° (eher 160 oder so ähnlich)
Meine Frage wäre, ob oben die Berechnungen dann falsch wären, oder wieso
ich so abweichende Werte eingeben muss?
Mark schrieb:> Allerdings durch ausprobieren fährt der Servo nur zwischen 6 und 16.9
6 und vor allem 16.9 was?
> und dabei keine kompletten 180° (eher 160 oder so ähnlich)
Das kann schon sein. Längst nicht jeder Servo fährt 180° und die
allermeisten Servos sind auch nicht so genau, daß sie exakt bei 1 bzw.
2ms duty ihre jeweilige Endposition erreichen. Da gibt es teils kräftige
Toleranzen.
Mark schrieb:> mit 6 und 6.9 meinte ich> OCR0B=6> und> OCR0B=16.9> aber danke für die Info
Das ist Integer. Da kannst du 16 oder 17 eingeben. Aber nicht 16.9 oder
irgendeine andere Fließkommazahl.
Hinterm Komma wird einfach abgeschnitten. D.h. aus 16.9 wird 16. Mit 17
sollte er ein Stück weiter laufen.
mfg.
Mark schrieb:> ich für OCR0B ja jetzt werte zwischen 6 und 16 ein. Wenn der Servo in> der endposition ist, summt der aber immer noch weiter. Wenn der Servo> keinen Strom bekommt, kann ich diesen von Hannd noch ein stück weiter> drehen (also er ist noch nicht in der Endposition).
Gut, schon mal wichtig.
Es gibt 3 Möglichkeiten.
Entweder steckt die Mechanik bereits im Endanschlag, so dass der Motor
zwar versuchen würde, noch weiter zu drehen, was aber rein mechanisch
nicht mehr möglich ist (den Fall hast du offenbar nicht)
oder aber die Position ist insofern 'schlecht', das das Poti an dieser
Stelle ein bischen 'wackelt' und die Servoelektronik daher ständig ein
klein wenig hin und her fährt. Sowas kommt vor. Lässt man das Servo ein
klein wenig weiter fahren, ist das Motorbrummen wieder weg, weil hier
das Poti stabil immer den gleichen Wert meldet.
oder dein Pulse schwanken ganz leicht. Das kann zb passieren, wenn dein
CPU-Taktgeber nicht sehr zuverlässig ist und in der Frequenz schwankt.
D.h. das Servo bekommt tatsächlich Pulse, die laufend ein klein wenig
unterschiedlich sind und befolgt die selbstredend.
Ich glaube je nach Servo gibt es eine elektrische und eine mechanische
Endposition.
Die müssen nicht übereinstimmen. Wäre auch doof wenn das Servo gegen den
mechanischen Anschlag fährt.