Hallo,
ich habe den Cube von Leyanda nachgebaut. Allerdings habe ich einen
Atmega644P statt einem Atmega32.
In der main.c habe ich
1
#define F_CPU 16000000UL
hinzugefügt. Jetzt muss ich noch die Timer in ledcube.c anpassen. Damit
sollte es das dann ja gewesen sein oder?
aus
1
voidcube_show_init(void)
2
{
3
TCCR0|=(1<<CS02)|(0<<CS00);
4
TIMSK|=(1<<TOIE0);
5
}
6
7
8
ISR(TIMER0_OVF_vect)
9
{
10
TCNT0=0xB0;/*at least 0xA0 */
11
/* cli() not neccessary atm */
12
PORTC=0x0;
13
staticuint8_tcube_show_layer=0;
14
....
habe ich in meinem Unwissen
1
voidcube_show_init(void)
2
{
3
TCCR0A|=(1<<CS02)|(0<<CS00);
4
TCCR0B|=(1<<CS02)|(0<<CS00);
5
TIMSK0|=(1<<TOIE0);
6
}
7
8
9
ISR(TIMER0_OVF_vect)
10
{
11
TCNT0=0xB0;/*at least 0xA0 */
12
/* cli() not neccessary atm */
13
PORTC=0x0;
14
staticuint8_tcube_show_layer=0;
gemacht. Allerdings läuft irgendwie alles viel viel zu schnell ab. Was
muss ich noch ändern? Oder kann das noch etwas mit den Fuses zu tun
haben?
Danke für eure Hilfe.
Sven schrieb:> habe ich in meinem Unwissen>> void cube_show_init( void )> {> TCCR0A |= (1<<CS02)|(0<<CS00);> TCCR0B |= (1<<CS02)|(0<<CS00);> TIMSK0 |= (1 << TOIE0);> }
Und was soll das sein?
Die CS02, CS00 Bits werden ja wohl nicht in beiden Registern vorliegen.
Das wäre ein bischen dämlich von Atmel. Denn: welches Register gilt denn
jetzt? Welcher Satz von Bits steuert denn den Vorteiler, wenn die sowohl
in TCCR0A als auch in TCCR0B vorliegen?
Du musst schon auch in die Datenblätter schauen und nicht einfach aufs
Geratewohl die Dinge so schreiben wie du dir denkst dass es sein könnte.
Du musst begreifen, dass es da keinen Schutz gibt. Letzten Endes sind
diese Bitnamen einfach nur andere Namen für Bitmuster. Der Compiler kann
nicht überprüfen ob es in diesem Register ein Bit mit diesem Namen gibt.
Der schreibt einfach nur das sich durch die Konstanten ergebenden
Bitmuster ins Register. Was immer das dann auch bewirkt!
Es ist dein Bier, dass die Kombination aus Registernamen und Bitname
'gültig' ist und das gewünschte bewirkt. Und wenn du dich da vertust,
dann setzt du eben irgendein anderes Bit, welches in diesem Register
'zufällig' an genau dieser Bitposition ist. Und das schaltet dann eben
irgendwas ein oder aus. Was genau ... das steht im Datenblatt bei der
Beschreibung des Register. Dort sieht man, was ein gesetztes 0-tes Bit
wirklich macht.
Um Karl Heinz Bucheggers Ausführungen konkreter zu machen: Die
Prescalereinstellungen beim ATmega644 verstecken sich laut Datenblatt im
Register TCCR0B. Das eigentliche Bitmuster "(1<<CS02)|(0<<CS00)" kannst
du aber übernehmen, um den gleichen Prescaler (= 1024) zu erhalten.
Du schreibst dieses Bitmuster aber auch in TCCR0A, wodurch du den Modus
des Timer/Counters veränderst.
Sven schrieb:> Jetzt muss ich noch die Timer in ledcube.c anpassen. Damit> sollte es das dann ja gewesen sein oder?
Das wird dir kaum jemand beantworten können, da wohl niemand den
Quellcode (und die Unterschiede) zwischen den beiden Controllern
"auswendig" kennt. Application Note AVR505
(http://www.atmel.com/Images/doc8001.pdf) könnte hier hilfreich sein und
listet die Unterschiede zwischen den beiden Controllertypen auf.
Ich weiß natürlich nicht was für Programmier-Kenntnisse du so mitbringst
und inwiefern du Lust zum Basteln hast, aber ich persönlich finde das
Software-Konzept von o.g. Projekt nicht besonders schön. Bzw. um es
anders zu sagen: Es gibt bessere Konzepte. So z.B. dieses
(Beitrag "LED cube 8x8x8"). Hier wird mit einem
Doppelpufferung gearbeitet, wodurch die Bildfrequenz stabil ist und es
zu keinerlei Flackereffekten kommt, welche der Autor von o.g. Projekt ja
im Quellcode selbst bemängelt. Überhaupt kommt mir persönlich der
Quellcode sauberer und überschaubarer vor.
Allerdings unterscheidet sich der Hardwareaufbau und insofern gäbe es da
schon einiges anzupassen. Das Konzept allerdings (sowie die Animationen)
würden sich wunderbar übernehmen lassen.
Sven schrieb:> Oder kann das noch etwas mit den Fuses zu tun> haben?
Es schadet sicherlich nicht sich die Fuses mal anzusehen und eventuell
auftretende Unterschiede zu untersuchen. O.g. Application Note
beinhaltet auch ein Kapitel bzgl. der Fuses.
vielen Dank für die Hinweise. Das mit den mangelhaften
Programmierkenntnissen ist wohl das größte Problem :-D Deshalb ist mir
auch nicht so ganz klar was das Datenblatt mir vermitteln möchte.
Das Thema mit den Timern kommentieren wir einmal kurz aus. Ich habe
momentan eher den Eindruck, dass die delays zu schnell laufen, bzw von
einer falschen Frequenz ausgegangen wird. Allerdings verstehe ich nicht
warum. Anschließendd widme ich mich gerne nochmal dem Timerproblem und
damit der Interruptroutine.
das #define F_CPU habe ich wieder gelöscht. Hat keine Auswirkungen. Aber
wie stelle ich dann die Freuenz ein? Sonst kann das mit den delays ja
nie was werden wenn ich das richtig verstanden habe?
Paul Baumann schrieb:> Merkt das der C-Kompiler nicht, wenn die Bitnamen in diesem Register> nicht existieren?
Und welcher "C-Kompiler" kann das? Siehe oben gemachte Ausführungen von
"Karl Heinz Buchegger".
Ich hätte mir wohl einmal diese Output-meldungen vom Compiler genauer
ansehen sollen. Ich bin bisher immer davon ausgegangen, dass bei "1
succeeded, 0 failed, 0 skipped" schon alles in Ordnung wäre. Von den
ganzen Meldungen über den Compilerverlauf verstehe ich im Normalfall
keine, die gemacht werden. Deshalb achte ich immer nur auf die
Fehlermeldungen wie "; expected" etc..
Allerdings war diesmal im Text die Warnung versteckt, dass F_CPU nicht
definiert ist. Diese Warnungen bekomme ich nur weg, wenn ich in alle
Dateien "#define F_CPU" hinzufüge. Sollte das in der main.c nicht
übergeordnet gehen? Oder erstellt man hierfür noch eine .h die überall
includiert wird?
Die Compiler Optimization habe ich auch noch von -O1 auf -Os gestellt.
Leider kann ich erst wieder am Montag testen.
Ich hoffe damit habe ich die meisten Anfängerfehler erschlagen.
Die Timereinstellungen für die Interruptroutine habe ich jetzt einfach
mal aus dem AVR-GCC Tutorial: Timer/Zähler kopiert.
Karol Babioch schrob:
>Und welcher "C-Kompiler" kann das?
Das weiß ich nicht, deshalb frage ich ja. Ich kenne "C" nicht und unter
solchen Umständen möchte ich es dann auch gar nicht kennen lernen.
>Siehe oben gemachte Ausführungen von "Karl Heinz Buchegger".
Wenn das daraus hervorgegangen wäre, hätte ich die Frage nicht
gestellt...
MfG Paul
Sven schrieb:> Allerdings war diesmal im Text die Warnung versteckt, dass F_CPU nicht> definiert ist. Diese Warnungen bekomme ich nur weg, wenn ich in alle> Dateien "#define F_CPU" hinzufüge. Sollte das in der main.c nicht> übergeordnet gehen? Oder erstellt man hierfür noch eine .h die überall> includiert wird?
Solche #define, die quer über das ganze Projekt gelten sollen, macht man
am besten in den Projekteinstellungen.
Denn auch dafür hat man den Überbau eines Projektes, auch wenn es so
etwas in C in der Form nicht gibt.
Praktisch jeder Compiler hat eine Möglichkeit, wie man ihm beim Aufruf
einige #define mitgeben kann. Hat man also in seinen
Projekteinstellungen eingetragen, dass dem Compiler beim starten des
Compiliervorganges mitgegeben werden soll, dass er das Makro-Symbol
F_CPU mit dem Text "8000000" vorbelegen soll, dann ist dadurch
garantiert, dass alle C-Files mit genau diesem #define compiliert
werden.
Im AVR-Studio-4 gab es dazu in den Projektoptionen sogar ein eigenes
Feld, wo man für F_CPU den Wert eintragen kann. Im 6-er Studio ist
dieses spezielle Feld aus mir unverständlichen Gründen rausgeflogen
(meiner Meinung nach ein Schritt in die falsche Richtung), aber die
Möglichkeit gibt es natürlich immer noch, weil man den Eintrag natürlich
bei den Symbolen machen kann, die dem Compiler mitgegeben werden.
> Die Timereinstellungen für die Interruptroutine habe ich jetzt einfach> mal aus dem AVR-GCC Tutorial: Timer/Zähler kopiert.
Das ist KEINE gute Idee.
Also, das "Ich kopiere das einfach"
Gewöhne dir an, dass das Datenblatt deines µC deine Bibel ist. Das ist
die einzige Autorität, die du akzeptierst! Es ist wirklich nicht schwer,
sich im jeweiligen Timer-Kapitel im Abschnitt "Register Summary" die
Dinge zusqammenzuholen, die man beabsichtigt. In der FAQ
FAQ: Timer
hab ich in diesem Abschnitt versucht, den Prozess des "wie gehe ich vor"
zu skizzieren.
Aber mit dem "Ich übernehm das eigentlich und kontrolliere nichts mit
dem Datenblatt" wirst du auf die Schnauze fallen! Und zwar immer und
immer wieder! Und dann suchst du dir einen Wolf. Die Einstellungen zu
kontrollieren bzw. sich aus dem Datenblatt rauszuholen, dauert (wenn man
erst mal weiß was man eigentlich einstellen will) keine 2 Minuten! Diese
2 Minuten können dir aber Stunden der Fehlersuche ersparen.
Paul Baumann schrieb:> Karol Babioch schrob:>>Und welcher "C-Kompiler" kann das?>> Das weiß ich nicht, deshalb frage ich ja. Ich kenne "C" nicht und unter> solchen Umständen möchte ich es dann auch gar nicht kennen lernen.>>>Siehe oben gemachte Ausführungen von "Karl Heinz Buchegger".>> Wenn das daraus hervorgegangen wäre, hätte ich die Frage nicht> gestellt...
Um das weiter auszuführen:
Nein, das kann er so erst mal nicht.
Denn
TCCR0B |= (1<<CS02)|(1<<CS00); // Prescaler 1024
ist für den Compiler erst mal nur eine Zuweisung eines Zahlenwertes an
eine Variable.
TCCR0B |= 0b00000101;
ist genau dasselbe: Ein Zahlenwert, diesmal ausgedrückt in Form von
Bits, wird an das Register zugewiesen
TCCR0B |= 0x05;
ist genau das gleiche, nur das der Zahlenwert in einer anderen Form
(diesmal als Hex-Schreibweise) angegeben ist.
Und genauso ist in
TCCR0B |= (1<<CS02)|(1<<CS00); // Prescaler 1024
der Teil rechts vom |= einfach nur eine andere Schreibweise für diesen
Zahlenwert. Man könnte auch schreiben
TCCR0B |= 4 + 1;
oder
TCCR0B |= 2*2 + 1;
denn letzten Endes ist es ja wurscht was auf der rechten Seite steht,
solange nur aus diesem Ausdruck sich das Bitmuster ergibt, welches man
in das Register verodern will. Kurz und gut: die rechte Seite der
Zuweisung hängt so erst mal nicht mit der linken Seite zusammen. Die
rechte Seite der Zuweisung kommt mit einem Bitmuster hoch, die linke
Seite bestimmt was damit geschehen soll. Zwischen den beiden gibt es
erst mal keinen weiteren logischen Zusammenhang.
Aber:
* das ganze klingt jetzt schlimmer als es ist. Mit ein wenig Sorgfalt
ist das in der Praxis kein Problem. Man muss einfach nur die Attitüde:
"Mir wurscht was im Datenblatt steht, ich übernehm den Code einfach so
wie ich ihn sehe" aufgeben
* man kann sich natürlich das ganze auch so zurecht legen, dass der
Compiler die Überprüfung machen kann. Hey - wir reden hier von C! Wenn
man seine Sprache kennt, kann man vieles erreichen, was einem
Nicht-Kenner erst mal graue Haare bereitet. C bringt die Mittel und die
Möglichkeiten mit. Man muss sie nur benutzen. Es hat schon seinen Grund
warum es C-Compiler vom kleinsten 8-Bit µC bis hinauf zum stärksten
128-Bit Supercomputer gibt! Und die Sprache C ist in dieser ganzen
Bandbreite von Rechnern immer die gleiche. Ab und zu angereichert mit
Spezialitäten, die nur für eine bestimmte Plattform relevant sind, aber
wer einmal C von der Pieke auf gelernt hat (nicht so wie hier im Forum,
wo immer wieder Leute aufschlagen, die gerade mal 15% des Sprachumfangs
beherrschen), der kommt in kürzester Zeit auf all diesen Plattformen
zurecht.
@Karl-Heinz
Danke für diese ausführliche Erklärung. Ich dachte zunächst, daß man
wie z.B. in Assembler oder auch in Bascom auch in C eine Datei in
das Programm einbinden muß, die die Registerdefinitionen beinhaltet.
So wie das hier:
$regfile = "m32def.dat"
oder in Assembler:
.include "4433def.inc"
Wenn ich dann dort aus Versehen ein Bit in einem falschen Register
"vermute", dann gibt es eben eine Fehlermeldung in der Art:
"Dieses Bit existiert im Register "Trallalla" nicht.
Das finde ich besser, als in "C" wo offenbar dann kein Fehler erkannt
wird und das Programm sich trotzdem kompilieren läßt, aber dadurch
natürlich die "lustigsten" Sachen macht...
MfG Paul
Hi
>Wenn ich dann dort aus Versehen ein Bit in einem falschen Register>"vermute", dann gibt es eben eine Fehlermeldung in der Art:>"Dieses Bit existiert im Register "Trallalla" nicht.
Von Assembler kenne ich das nicht so. Da wird nur gemeckert wenn es
diesen Bitbezeichner überhaupt nicht gibt.
MfG Spess
Im Moment bin ich mit der "Bibel" noch etwas überfordert. Dafür habe ich
einfach zu wenig Ahnung von der ganzen Materie. Ich verfolge momentan
das Motto: learning by doing. Vieles klappt dann aber doch ohne, dass
ich dabei etwas gelernt oder verstanden habe...
Im Nachhinein hätte ich mir statt Maschinenbau ein Studium mit deutlich
stärkerem Aspekt auf der Elektrotechnik suchen sollen. Wenn man sich ein
bisschen damit beschäftigt kann das ja fast schon Spaß machen. :-D
Ich habe viele Artikel von dieser Seite schon mehrfach gelesen und finde
das meiste viel verständlicher als diverse Büchern, die ich ausgeliehen
habe. Über die FAQ bin ich aber irgendwie noch nie gestolpert. Hilft auf
jeden Fall für das Gesamtverständnis.
Paul Baumann schrieb:> So wie das hier:> $regfile = "m32def.dat"
Bascom
> oder in Assembler:> .include "4433def.inc"
Assembler
>> Wenn ich dann dort aus Versehen ein Bit in einem falschen Register> "vermute", dann gibt es eben eine Fehlermeldung in der Art:> "Dieses Bit existiert im Register "Trallalla" nicht.
In Bascom.
Weil Bascom speziell an die AVR angepasst wurde.
Aber C ist ja eine Universalsprache. Und Atmel hat sich die Dinge da ein
wenig einfach gemacht und die simpelst mögliche Alternative gewählt: Das
Problem auf den Benutzer abwälzen. Es wär in C auch anders gegangen.
>> Das finde ich besser, als in "C" wo offenbar dann kein Fehler erkannt> wird und das Programm sich trotzdem kompilieren läßt, aber dadurch> natürlich die "lustigsten" Sachen macht...
struct TCCR0_
{
unsigned char CS00 : 1;
unsigned char CS01 : 1;
unsigned char CS02 : 1;
unsigned char WGM01 : 1;
unsigned char COM00 : 1;
unsigned char COM01 : 1;
unsigned char WGM00 : 1;
unsigned char FOC0 : 1;
};
#define TCCR0 (*(volatile struct TCCR0_*)0x0053)
....
TCCR0.CS00 = 1; // Ist ok, CS00 gibt es im TCCR0
TCCR0.WGM02 = 1; // Möööp, WGM02 existiert nicht in TCCR0
und das ist jetzt nur EINE Möglichkeit. Man kann das Prinzip noch weiter
treiben und landet im Endeffekt bei einem ähnlichen Komfort wie in
BASCOM.
Alles nur eine Frage dessen, ob man als 'Hersteller' den Aufwand treiben
will oder nicht. Das hat aber mit 'C' (als Sprache) an sich überhaupt
nichts zu tun. C bietet alle Zutaten, die man braucht. C ist nur ein
Werkzeug. Ob ein Benutzer dann dieses Werkzeug auch in allen seinen
Fasetten einsetzen kann, darfst du nicht dem Werkzeug anlasten (und
genau das tust du momentan. Du verwechselst die Fähigkeiten des
Programmierers bzw. das was Atmel vorgefertigt hat, mit dem was sein
Werkzeug eigentlich leisten könnte)
Sven schrieb:> Im Moment bin ich mit der "Bibel" noch etwas überfordert.
Du bist damit überfordert, dir im Datenblatt die 5 Seiten rauszusuchen,
auf denen die Register in all ihren Bits mit der genauen Beschreibung
jedes einzelnen Bits erklärt werden? Bzw. mit zwischengestreuten
Tabellen in denen die Bits zusammengefasst werden die Tabelle
rauszusuchen, die für dich zutrifft?
Du bist damit überfordert, dir aus
(siehe Bild)
die für deinen gewünschten Modus notwendigen WGM Bits rauszulesen und
dann bei 2 Registerbeschreibungen nachzusehen, in welchem Register
welches Bit beheimatet ist?
(Selbiges für die Vorteiler-Einstellung)
Das ist jetzt aber nicht dein Ernst!
In deinem eigenen Interesse fasse ich das jetzt mal als billige Ausrede
auf. Denn ansonsten müsste ich dir sagen: such dir ein anderes Hobby,
wenn du damit überfordert bist, dir aus 2 Tabellen und 3 Absätzen die
Information zusammenzusuchen.
@Karl-Heinz
Danke auch für den 2. Text von Dir. Dann weiß ich jetzt, daß man sich
zumindest als Ersteller des Programmes selbst eine Kontrollmöglichkeit
auf Fehler einbauen könnte, um sich nicht ungewollt zum Affen zu machen.
Natürlich guckt man erst mal im Datenblatt, was man überhaupt an
"Verhaltes-Steuer-Bits" braucht und wo die sich herumsielen, aber wie
schnell hat man mal einen falschen Buchstaben drin und schon ist der
Spaß vorbei, wenn das nicht abgefangen wird.
MfG Paul
ja ich bin überfordert, wäre aber erleichtert, wenn du mir sagst dass
das in die richtige Richtung geht:
1
TCCR0A|=(0<<COM0A1)|(0<<COM0A0)|(1<<WGM01)//Normal port operation, OC0A disconnected, CTC-Modus
2
TCCR0B|=(1<<CS02)|(0<<CS00);//Prescaler 1024
3
OCR0A=250;// OCR0A wird mit TCNT0 verglichen
4
TIMSK0|=(1<<OCIE0A);//Interrupt bei Match A (wenn OCF0A in TIFR0 gesetzt ist)
5
6
ISR(TIMER0_COMPA_vect)
7
{
8
...
irgendwie ist die Tabelle aus deinem Datenblatt übersichtlicher als die,
die ich gerade verwende. Mit deiner Tabelle würde ich behaupten meine
WGM sind falsch :-\
aber da seh ich gerade, dass das Bild zum Mega16 gehört. Liege ich dann
vielleicht doch nicht so ganz daneben?
Wieso versetzt du den Timer denn in den CTC Modus? In o.g. Projekt wird
der Timer im ganz "normalen" Modus verwendet und von der Overflow ISR
Gebrauch gemacht.
aber im Obigen Projekt wird der Timer auch manipuliert, um die
Interruptintervalle anzupassen. Im Kommentar ist der alternative Weg CTC
ja schon erwähnt. Da ich mich mit dem Thema jetzt schon auseinadersetze
habe ich mich dann gleich dafür entschieden.
Gibt es Vor- und Nachteile der Varianten? Ich denke höchstens wenn ein
Timer mehrere Funktionen erfüllen soll?
Sven schrieb:> Gibt es Vor- und Nachteile der Varianten? Ich denke höchstens wenn ein> Timer mehrere Funktionen erfüllen soll?
In erster Linie sind die verschiedenen Modi halt für unterschiedliche
Zwecke gedacht. In deinem Fall dürfte der CTC Modus schon mehr Sinn
machen, um die Multiplexfrequenz besser bestimmen zu können. Allerdings
musst du halt prüfen was in der "alten" Overflow-Routine so alles
passiert. Es kann durchaus sein, dass du da mehr ändern musst, als den
Namen des Interrupt Vektors.