Hallo,
ich wollte einen Servo mit einem attiny45 und den timer zum laufen
bringen. Da habe ich vorher allerdings schon probleme.
Zunächst mal mein Code
1
#define F_CPU 8000000UL
2
#include <avr/interrupt.h>
3
#include <avr/io.h>
4
5
void init();
6
void setup();
7
8
int zaehler;
9
10
11
int main(void) {
12
init();
13
setup();
14
while(1)
15
{
16
}
17
}
18
19
void init(){
20
DDRB |= (1<<PB0); //PB0 -> Ausgang
21
PORTB &= ~(1<<PB0); // PB0 -> low
22
DDRB |= (1 << PB1); // PB1 = Output
23
PORTB &= ~(1<<PB1); // PB1 -> low
24
25
}
26
27
void setup(){
28
TIMSK |= (1<<TOIE0); // Interrupt beim Overflos auslösen
zunächst wollte ich erst einmal testen,ob der den Interrupt macht und
eine LED anschalten. Dazu sollte er alle 0,256ms den Interrupt auslösen
und alle 0,5s ms die LED toggeln. Diese bleibt allerdings aus.
Eine Warnung bekomme ich
../interrupt.c:38:1: warning: 'TIM0_OVF_vect' appears to be a misspelled
signal handler [enabled by default]
damit kann ich aber irgendwie nichts Anfangen.
Wäre cool, wenn da mal jemand rüber gucken könnte, wo genau der Fehler
ist.
Marc
Marc schrieb:> damit kann ich aber irgendwie nichts Anfangen.
der Name TIM0_OVF_vect wird falsch sein. Zur not mal in dem headerfile
suche wir er richtig ist.
Richtig, du hast den falschen Namen genommen.
Noch ein Tipp: Im Moment arbeitest du mit dem Timer Overflow Interrupt.
Schau dir mal den Timer Compare Match Interrupt an. Du kannst hier eine
Variable sparen und dein Code würde kleiner werden:
1
ISR(TIMER0_COMPA_vect){
2
PORTB^=1<<PB0;
3
}
wäre das einzige, was in deiner Interruptroutine steht. Du müsstest nur
das Compare-Register entsprechend vorladen in der Initialisierung ;)
Das mit der falschen Benennung hatte ich ja schon oben gefunden. aber
danke noch mal.
@ Michael. Ich möchte ja später einen Servo-Motor ansteuern.
funktioniert das dann mit dem Timer Compare Match Interrupt immer noch?
Werde ich mir aber mal angucken
Marc
OCR0A = 1; // Timer zählt 78 Takte = 20ms und springt danach wieder auf 0
Im datenblat steht bei der Tabelle für PWM-Mode (bei WGM02 und WGM00)
Update of
OCRx at
heisst das, dass der zähler immer bei den Wert anfängt, den ich da
setze? also hier 1?
leuchten tut aber leider immer noch nichts.
Marc
Marc schrieb:> @ Michael. Ich möchte ja später einen Servo-Motor ansteuern.> funktioniert das dann mit dem Timer Compare Match Interrupt immer noch?
Klar, warum sollte es nicht funktionieren? Ich weiß ja nicht genau wie
du den Motor steuern willst aber denk dran: Du hast zwei Timer und
kannst beide benutzen. Das Timing muss nur stimmen. Wenn dein Tiny mit 8
MHz läuft und du den einen Timer brauchst um PB0 alle 500 ms zu toggeln
wirst du auch den Systemtakt runterteilen müssen um nicht irgendwo die
Auslösungen in der ISR zu zählen. Die Idee mit dem "eigenen" Zähler ist
zwar ganz nett aber kein schöner Stil. Daher sollte man immer schaun ob
es nicht ohne einen solchen Zähler lösbar ist.
Oder, ich sags mal anders: Ich kenne viele Menschen die ihren µC gerne
mit 16 MHz laufen lassen, dabei genügt es in ihrer Anwendung wenn der µC
mit 100 kHz liefe.
Das ist eines der ersten Dinge, die du prüfen solltest: Wie schnell muss
dein µC reagieren können? Das gibt dein Systemtakt vor und nichts
anderes.
> OCR0A = 1; // Timer zählt 78 Takte = 20ms und springt danach wieder
4
> auf 0
WGM00 ist nicht im TCCR0B
>> Im datenblat steht bei der Tabelle für PWM-Mode (bei WGM02 und WGM00)> Update of> OCRx at>> heisst das, dass der zähler immer bei den Wert anfängt, den ich da> setze? also hier 1?
Nein. Es gibt die Tabelle mit den Modes und welche Bits dafür gesetzt
werden müssen. In der Tabelle bedeutet "Update of OCRx ..", daß das
Register (OCR0A, OCR0B)bei entsprechendem TCNT0-Stand aktualisiert wird.
Das Programm kann jederzeit hineinschreiben, effektiv wird das dann aber
erst zu den entsprechenden TCNT Ständen. Wird also gepuffert. Siehe
entsprechende Erklärungen im Datenblatt. Vorteil zB bei Nutzung der
HW-PWM Ausgänge um keine Glitches aufkommen zu lassen.
Ich habe da noch einmal eine Nachfrage.
Ich könnte ja die frequenz des Attiny langsamer machen.
Ist das richtig, dass ich den Internen Oszillator nur mit 8MHz oder
6,4MHz schwingen lassen kann?
http://www.engbedded.com/fusecalc/
wollte das dann wie hier
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/Die_Timer_und_Z%C3%A4hler_des_AVR
in CTC Clear Timer on Compare Match (Auto Reload)
machen.dann würde ich die Frequezn auf 6,4MHz setzen , den Prescaler auf
1024 und OCR0A = 125.
Dann käme ich genau auf 20 ms, die ich dann den overflow aufrufen würde
und den pin auf high stellen würde.
mit den zweiten timer (Den ich mir noch angucken muss) würde ich den pin
je nach sollwinkel des servos nach 1,5 bis 2,5 ms wieder zurücksetzen.
Marc
Direkt noch eine Frage hätte ich,
wie muss ich denn genau die fuses setzen? da sind ja jetzt vier mal
6,4MHz angegeben.
Ist das egal welchen von denen ich nehme?
Eine letzte frage für heute noch.
Habe das Programm nach dem Beispiel nachgebaut. Folgendes ist anders bei
mir:
- mein Attiny läuft mit 8MHz
- damit ich sehe, ob der das funktioniert, lasse ich den zähler in der
ISR- Routine 250 mal hochzählen um die led an und aus zu schalten.
Später möchte ich (wie oben erwähnt) alle 20ms den pin für den servo auf
high schalten
allerdings bleibt auch hier die LED
aus
[code]
#define F_CPU 8000000UL
#include <avr/interrupt.h>
#include <avr/io.h>
void init();
void setup();
int zaehler;
int main(void) {
init();
setup();
while(1)
{
}
}
void init(){
DDRB |= (1<<PB0); //PB0 -> Ausgang
PORTB &= ~(1<<PB0); // PB0 -> low
DDRB |= (1 << PB1); // PB1 = Output
PORTB &= ~(1<<PB1); // PB1 -> low
}
void setup(){
TCCR0A |= (1 << WGM01); // CTC Mode
TCCR0B |= (1 << CS02); // prescale 256
OCR0A = 125-1;
TIMSK |= (1<<OCIE0A);
sei(); // Interrupts freigeben
}
//ISR(TIMER0_OVF_vect){ // alle 4 ms
ISR(TIMER0_COMPA_vect){
zaehler++;
if (zaehler>=250){// -> 1s
zaehler = 0;
PORTB ^= 1<<PB0;
}
}
Was ist denn jetzt noch verkehrt?
Marc schrieb:> Was ist denn jetzt noch verkehrt?
Scheint mir alles richtig zu sein. Auch die Sekunde stimmt.
Wie ist die LED angeschlossen? Vorwiderstand? Wieviel Strom braucht die?
Programmer insbesondere von PB0 abgezogen?
Auf alles gehe ich nicht ein, da musst du dich noch mal genau einlesen.
Schau hier mal in die verschiedenen Artikel auf der Seite.
Zum Systemtakt
Nur mal so angenommen du verwendest den internen RC-Oszilator mit 8 MHz.
Das kann man machen und ist nicht unbedingt verkehrt. Dann hast du einen
Systemtakt-Prescaler, der kann den Takt des Taktgebers runterteilen und
zwar um die Faktoren 2, 4, 8, 16, 32, 64, 256 und 1024. Das steht aber
im Datenblatt. Nehmen wir mal an du nimmst den 256er dann läuft dein
ATTiny mit 8/256 MHz.
Jetzt musst du dir den Timer anschauen. Der kann mit dem Systemtakt
laufen oder auch den Systemtakt nochmal runter teilen um 8, 64, 256 oder
1024. Wählst du da nun den 8er stellst du fest, dass du OCR0A mit 1953
(genau: 1953,125 aber Kommastellen gehen ja nicht, wir bauen uns also
einen Fehler ein) vorladen. Das passt da natürlich nicht rein, OCR0A ist
ja nur 8 bit groß. Nun kannst du entweder den Prescaler für den
Systemtakt ändern oder für den Timer. Der für den Systemtakt ist schon
auf maximum, somit bleibt hier nur der für den Timer. Den auf 64
geändert ergibt, dass du für OCR0A auf 244 (genau: 244,141) vorladen
musst. Das passt rein, würde also gehen.
Für OCR0A gilt: Je größer der Wert desto kleiner der Fehler der durch
die wegfallenden Nachkommastellen entsteht. Du könntest den Prescaler
des Timers ja auch auf 1024 einstellen, OCR0A ist dann nur noch 15
(genauer: 15,259).
Lies dich unbedingt da mal genau ein.
Marc schrieb:> Ich könnte ja die frequenz des Attiny langsamer machen.> Ist das richtig, dass ich den Internen Oszillator nur mit 8MHz oder> 6,4MHz schwingen lassen kann?
Nein. Beim Tiny45 hast du eine reiche Auswahl möglicher interner Takte.
Grundsätzlich stehen erstmal zwei interne Quellen zur Verfügungen, ein
128kHz-Oszillator und ein 8MHz-Oszillator. Der Vorteil des
128kHz-Oszillators ist, daß er selber sehr wenig Strom benötigt, der
Vorteil des 8MHz-Oszillators ist, daß er sich relativ genau auf eine
Sollfrequenz in einem relativ weiten Frequenzbereich abgleichen läßt.
Zusätzlich ist es möglich, eine PLL zur effektiven Taktverdoppelung
des 8MHz-Oszillators zu verwenden.
Zusätzlich kannst du alle bisher genannten Takte mittels CKDIV-Fuse
auch noch durch 8 teilen lassen.
Schon rein nominell hast du also folgende interne Taktoptionen:
16MHz(*)
8MHz(*)
2MHz(*)
1MHz(*)
128kHz
16kHz
Die mit (*) gekennzeichneten Optionen basieren allesamt auf dem
8MHz-Oszillator als Taktquelle und können deshalb darüber hinaus noch
über die Kalibrierung dieses Oszillators "stufenlos" weiter variiert
werden, +-25% des Nominalwertes sind auf jeden Fall ziemlich problemlos
drin, mit Rücksicht auf bestimmte Randbedingungen auch mehr.
> http://www.engbedded.com/fusecalc/
Du mußt da schon den richtigen Tiny wählen, sonst rechnet das Teil
natürlich nach dem bekannten Grundgesetz der Informatik "shit in->shit
out" nur Quatsch aus. Viel besser ist aber, zuerst mal das Datenblatt zu
lesen und zu verstehen, um einen Überblick darüber zu gewinnen, was
eigentlich alles möglich ist. Das erleichtert nicht nur die Benutzung
des Fusecalc ungemein, sondern versetzt dich auch in die Lage, dessen
Ergebnisse zu überprüfen, BEVOR du dich durch eventuell falsche
Fuse-Settings "aussperrst".
> wollte das dann wie hier> http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/Die_Timer_und_Z%C3%A4hler_des_AVR> in CTC Clear Timer on Compare Match (Auto Reload)> machen.
Das ist doch Quatsch. Du wolltest einen Servo ansteuern, dafür ist PWM
die beste Option, nicht CTC. Und du brauchst da auch keine ISR dafür,
das kann der Timer alles ganz alleine. Der einzige Nachteil ist, daß du
ganz bestimmte Pins als Ausgänge verwenden mußt, weil der Timer sein
selbstständig erzeugtes Signal nur an genau diesen speziellen Pins nach
außen absondern kann. (Jedenfalls ist das bei den meisten AVRs noch so,
bei einigen neueren aber schon nicht mehr, da kann man das Signal zu
verschiedenen Pins "routen".)
Das A und O ist in jedem Fall: Lesen und Verstehen des Datenblatts!
> mit den zweiten timer (Den ich mir noch angucken muss) würde ich den pin> je nach sollwinkel des servos nach 1,5 bis 2,5 ms wieder zurücksetzen.
Das ist noch mehr Quatsch. Du brauchst immer nur einen Timer, selbst für
dein nutzloses Software-Gefrickel.
Besser ist, wie gesagt, den Timer in einem PWM-Modus laufen zu lassen
(Fast-PWM ist am einfachsten zu verstehen), dann macht er die ganze
Arbeit alleine.
Den Timer versorgst du mit einem solchen Basistakt, daß der volle
Zählumfang des Timers in ungefähr 20ms durchlaufen ist. Hier kommt es
nicht besonders auf Genauigkeit an, wenn es nur 16 oder auch 24ms sind,
geht das meist ganz genauso gut.
Den eigentlichen Stellwert des Servo erzeugst du dann über die
OCRxn-Register des Timers. Darüber bestimmst du den Anteil der
Gesamtzeit eines Zyklus, in dem das Ausgangssignal Low ist. Und der muß
dann eben so ca. 1..2ms betragen.
Michael Köhler schrieb:> Dann hast du einen> Systemtakt-Prescaler, der kann den Takt des Taktgebers runterteilen und> zwar um die Faktoren 2, 4, 8, 16, 32, 64, 256 und 1024. Das steht aber> im Datenblatt. Nehmen wir mal an du nimmst den 256er dann läuft dein> ATTiny mit 8/256 MHz.
Erst einmal danke c-hater für deine ausführliche erklärung. Habe meinen
Code noch einmal komplett überarbeitet. Jetzt benötige ich auch die
ISR-Funktion nicht mehr. Allerdings habe ich noch eine kleine
verständisschwierigkeit
TIMSK |= (1 << OCIE0A); // Compare Match Interrupt aktiviert
33
sei(); // Interrupts freigeben
34
}
mit (1 << COM0A1) sage ich, dass er (wenn er bei null anfängt zu zählen)
OC0A auf high setzen soll.
mit (1 << WGM02) (in TCCR0B) und (1 << WGM01) | (1 << WGM00) (in TCCR0A)
sage ich, dass es FastPWM sein soll und der counter nur bis OCR0A zählen
soll.
mit OCR0A=157 komme ich auf eine Periode von ziemlich genau 20ms.
mein Problem ist jetz, dass ich nicht weiss, womit ich sagen kann, dass
er bei einem bestimmten Wert OC0A auf Low setzen soll.
Das einzige was ich gefunden habe, war das man dafür OCR0A verwendet.
Wenn ich nur WGM01 und WGM00 setzen würde, hätte ich OCR0A frei.
Allerdings finde ich dann keine gescheite Kombination um die
Periodendauer nur in der nähe von 20 ms zu bekommen.
Gibt es also noch ein Register, mit dem ich sagen kann, wann die PWM auf
wieder auf LOW gesetzt wird?
Ich denke OCR0B ist für OC0B, also für einen andere Pin. So verstehe ich
das zumindestens aus dem Datenblatt.
Ich müsste anstelle von OCR0A -> TCNT0 setzen. Dann würde er TCNT0
wieder auf HIGH springen und bei OCR0A auf LOW
Da ich nicht weiss, ob das mit dem Servo funktioniert, probiere ich es
erst mit der LED aus. Die sollte ja jetzt eigentlich von dunkel nach
hell aufleuchten und anschließend wieder von hell nach dunkel.
Aber auch hier leuchtet nichts.
HAbe es auch mit OCR0B auprobiert gehabt
Du erlaubst Overflow- Und Compare-Interrupts ohne passende ISRs, so wird
das nix, das ergibt zyklische Resets. Außerdem sollte man beim
erstmaligen Beschreiben eines Registers die Werte direkt zuweisen und
nicht verodern.
da stehen und im setup
[cdoe]OCR0A = 0; [/code]
aber auch so tut sich nichts.
wieso darf ich denn im setup nicht
[cdoe]OCR0A = i; [/code]
stehen haben? i habe ich doch vorher auf 0 gesetzt.
Marc schrieb:> Habe jetzt nur noch> da stehen und im setup>
1
TIMSK |= (1 << OCIE0A)
Wozu? Es gibt doch keine ISR dazu.
> [cdoe]OCR0A = 0; [/code]> [cdoe]OCR0A = i; [/code]
Mit Kopieren und probieren wird das nichts. Abgesehen davon verstehe ich
die Frage nicht. OCR0A darf durchaus mit Zuwesisung durch eine Variable
gesetzt werden.
Schau nochmal was MWS geschrieben hat und versuch anhand des Dateblattes
zu verstehen, was da mit dem Timer passiert.
Hat die LED überhaupt schon mal Lebenszeichen von sich gegeben
Stephan schrieb:> mit (1 << COM0A1) sage ich, dass er (wenn er bei null anfängt zu zählen)> OC0A auf high setzen soll.> mit (1 << WGM02) (in TCCR0B) und (1 << WGM01) | (1 << WGM00) (in TCCR0A)> sage ich, dass es FastPWM sein soll und der counter nur bis OCR0A zählen> soll.> mit OCR0A=157 komme ich auf eine Periode von ziemlich genau 20ms.>> mein Problem ist jetz, dass ich nicht weiss, womit ich sagen kann, dass> er bei einem bestimmten Wert OC0A auf Low setzen soll.
Die Antwort ist einfach: garnicht. Da OCR0A im Mode 7 die Zykluszeit
bestimmt, kann man damit KEIN PWM-Signal erzeugen. Dafür steht dann
nur noch OCR0B zur Verfügung. Also mußt du nicht den OC0A-Ausgang
freischalten und benutzen, sondern den OC0B-Ausgang und zur Steuerung
der PWM auch das OCR0B-Register verwenden.
Alternativ kannst du den Timermode 3 verwenden, bei dem die Zykluszeit
allein vom Zählumfang des Timers bestimmt wird. Da im relevanten Bereich
aber nur die Prescaler 1024 und 256 zur Verfügung stehen, mußt du dann
die PLL-Taktoption mit 16MHz benutzen. Dann kommst du mit Prescaler 256
auf eine Zykluszeit von 16ms, was OK ist. Der Vorteil ist hier, daß du
sowohl OCR0A als auch OCR0B als PWM-Kanal verwenden kannst, also zwei
Servos steuern kannst. Außerdem hast du im relevanten Bereich mehr
Schritte zur Verfügung, kannst die Servos also auch noch feiner
ansteuern.
Um bei 8MHz Takt bleiben zu können, wäre eine weitere Alternative der
Timermodus 1, also PhasecorrectPWM ohne Limit. Da bei Phasecorrect hoch-
und runtergezählt wird, halbiert sich die effektiv die Frequenz. Damit
kannst du Prescaler 1024 verwenden und kommst damit bei 8MHz auf
ebenfalls 16ms Zykluszeit. Auch hier stehen dann zwei PWM-Kanäle zur
Verfügung.
c-hater schrieb:> kannst du Prescaler 1024 verwenden und kommst damit bei 8MHz auf> ebenfalls 16ms Zykluszeit.
Und wenn du glaubst, unbedingt auf 20ms Zykluszeit kommen zu müssen
(mußt du nicht), dann kannst du in beiden Varianten den 8MHz-Oszillator
knapp ca. 25% langsamer, also mit gut 6MHz laufen lassen. Um exakt 20ms
zu erreichen, wäre der Wert 6,5536MHz. Der passende Kalibrierwert dafür
läge so ungefähr bei 70.
fuse
lfuse 61
hfuse DF
efuse FF
damit sollte er mit 16MHz sein.
Auch habe ich am anfang vom programm
1
#define F_CPU 16000000UL
geändert.
Eigentlich habe ich jetzt gedacht, dass ich mit 1<<COM0A -> OC0A bei
null auf High gesetzt wird und sobald OCR0A erreicht ist wird OC0A auf
Low gesetzt. Daher habe ich OCR0A auf 255. Also eigentlich sollte jetzt
die ganze zeit an sein. Das teste ich wieder mit der LED. aber diese
Bleibt aus. Auch wenn ich OCR0A=0 setze, passiert nichts. Auch wenn ich
den Servo anschließe tut sich gar nichts.
In der while-schleife in der main-funktion habe ich nichts stehen.
Die LED nicht kaputt. Das habe ich schon getestet
Das ist korrekt, soweit es die Initialisierung des Timers betrifft. Es
fehlt aber noch eine Sache: Du mußt den OC0A-Pin (also PB0) auf Ausgang
schalten.
> fuse> lfuse 61> hfuse DF> efuse FF>> damit sollte er mit 16MHz sein.
Nein, das ergibt 2MHz.
Ich habe noch einmal eine Nachfrage.
Die lfuse habe ich auf 0x6E gesetzt. jetzt habe ich das Problem, dass
ich den code nicht mehr überspielen kann.
im command-window gebe ich ein
avrdude -c STK500v2 -P COM5 -p t45 -B 9 -U flash:w:servo.hex
als fehlermeldung bekomme ich
avrdude: stk500v2_command<>: command failed
ardude: initialization failed, rc=-1
habe gelesen, dass es an der bitclock (-B) liegen kann. diesen habe ich
einmal auf 1 gesetzt und hoch bis auf 250. Aber diese Fehlermeldung
kommt immer wieder.
Davor habe ich die Programme immer mit den oben genannten Befehl
überspielt. Also muss ich wohl mit den lfuse etwas falsch gemacht haben.
Marc schrieb:> Die lfuse habe ich auf 0x6E gesetzt.http://www.engbedded.com/fusecalc
Da sollte also jetzt ein externer Quarz dran sein. Ist das nicht der
Fall, ist kein Takt vorhanden = vorübergehend tot.
Entweder Quarz dranhängen, oder zum Programmieren an CLKI/XTAL1 einen
Takt zuführen.
ach sch.. zum heulen.
dann muss ich das erst mal zur Seite legen und später weiter machen.
Habe auf http://www.klaus-leidinger.de/mp/Mikrocontroller/index.html
auch eine Anleitung gefunden, womit ich das wieder richten kann (hoffe
ich zumindestens).
Trotzdem möchte ich mich hier schon einmal bei euch allen bedanken. Ohne
euch (speziell c-hater aber auch alle anderen) hätte ich es nicht so
weit geschafft.
Besten dank
Marc