Moin,
Ich nutze einen ATMEGA1284P. Es hängt an PB3 eine LED gegen Ground. Über
I2C ist ein LCD angeschlossen. Ich nutze folgenden Code:
1
#ifndef F_CPU
2
#define F_CPU 8000000UL
3
#endif
4
5
#include<util/delay.h>
6
#include<avr/io.h>
7
#include<avr/interrupt.h>
8
#include"lcd-routines.h"
9
10
volatileintzaehler=0;
11
12
intmain(void)
13
{
14
DDRB|=(1<<PB3);// Port OC1A mit angeschlossener LED als Ausgang
15
16
PORTB|=1<<PB3;
17
_delay_ms(1000);
18
PORTB&=~(1<<PB3);
19
_delay_ms(1000);
20
21
22
lcd_init();
23
24
//PWM Signal
25
TCCR0A=(1<<COM0A1)|(1<<WGM10);
26
TCCR0B=(1<<CS01)|(1<<CS00);
27
OCR0A=25;// Duty cycle 50% (Anm. ob 128 oder 127 bitte prüfen)
28
TIMSK0|=1<<OCIE0A;
29
30
//1s-Schleife
31
TCCR1A=(1<<WGM21);
32
TCCR1B=(1<<CS01)|(1<<CS00);// 1/1024
33
OCR1A=255;
34
TIMSK1|=(1<<OCIE1A);//Enable Compare Interrupt
35
36
//Interrupt Steuerung
37
DDRD&=~(1<<PD2);//EIngang
38
PORTD&=~(1<<PD2);//Pull-Up sicher ausschalten
39
EIMSK|=1<<INT0;
40
41
sei();
42
43
while(1);
44
return0;
45
}
46
47
ISR(TIMER1_COMPA_vect)
48
{
49
zaehler++;
50
if(zaehler<10)
51
{
52
charname[16];
53
sprintf(name,"%d",zaehler);
54
lcd_string(name);
55
}
56
}
57
58
ISR(INT0_vect)
59
{
60
}
Zu Beginn blinkt die LED wie erwünscht einmal. Danach wird sie per PWM
gedimmt und leuchtet entsprechend dunkler. Darum kümmert sich der Timer
0. Zusätzlich habe ich nun einen zweiten Timer (1) laufen, der mit die
ISR aufruft, die das LCD updatet. Das Ganze ist soweit nur ein
Testaufbau und so wenig sinnvoll.
Das Problem ist nun, dass auf dem LCD bis 9 hochgezählt wird und sich
der uC anschließend neuzustarten scheint. Um präzise zu sein: Das LCD
wird kurz nicht aktualisiert und fängt anschließend von vorne an zu
zählen. Erhöht man die Grenze in der If-Bedingung wird entsprechend
gezählt. Das interessante dabei ist, dass die LED durchgehend weiter
gedimmt leuchtet und nicht die 2s Blinkung macht! Lasse ich das Display
zum Start einen Begrüßungstext anzeigen, wird dieser allerdings anzeigt.
Drehe ich die If-Bedingung um, also "zaehler > 10" zeigt das LCD nie was
an.
Es wirkt so, als wäre die ISR zu schnell (?), sodass der uC sich dabei
neustartet, wenn es nicht weiter zu tun gibt, als den zaehler
hochzuzählen. Schalte ich die Interrupts vor der Erhöhung ab und danach
wieder an, ändert sich nichts.
Kann es sein, dass ich durch die Initialisierung der Timer einen
weiteren Interrupt initialisiert habe, der auslöst und keine
entsprechende ISR findet und dadurch abstürzt?
Bin aktuell ratlos, wo genau der Fehler liegt :/
Schöne Grüße
Julian
Julian schrieb:> Kann es sein, dass ich durch die Initialisierung der Timer einen> weiteren Interrupt initialisiert habe, der auslöst und keine> entsprechende ISR findet und dadurch abstürzt?
Ja, hast du!
Lorem Ipsum schrieb:> LCD-Routinen, print, etc. in einer ISR sind schon sehr kriminell.> Löse> das erstmal anders.
Wie gesagt, dass ist ein sehr grobes Testprogramm. Der LCD Kram in der
ISR ist in der Tat unschön, aber hier ja auch gar nicht das Problem. Das
Ding ist ja gerade, dass die ISR OHNE dem LCD Arbeiten nicht mehr
arbeitet! Nämlich sobald die If-Bedingung nicht mehr zutrifft stürzt der
uC ab.
Julian schrieb:> Nämlich sobald die If-Bedingung nicht mehr zutrifft stürzt der> uC ab.
Dann ist er fertig und will den nächsten Interrupt ausführen, für den es
keine ISR gibt.
mfg.
>Kann es sein, dass ich durch die Initialisierung der Timer einen>weiteren Interrupt initialisiert habe, der auslöst und keine>entsprechende ISR findet und dadurch abstürzt?
Ja, bei Timer0:
TIMSK0 |= 1 << OCIE0A;
Thomas E. schrieb:> Julian schrieb:>> Bin aktuell ratlos, wo genau der Fehler liegt>>> TIMSK0 |= 1 << OCIE0A;>> TIMSK1 |= (1<<OCIE1A);>> EIMSK |= 1 << INT0;>>> ISR (TIMER1_COMPA_vect)>> ISR (INT0_vect)>> Fällt dir was auf?>> mfg.
-.- Durchaus... Danke für den Tipp! Ist korrigiert und arbeitet nun brav
:)
Julian schrieb:> #ifndef F_CPU> #define F_CPU 8000000UL> #endif
Mutig, mutig.
Wenn du F_CPU nicht explizit angibst, suchst du dich tot nach dem
Fehler, falls der µC nicht zufällig mit 8MHz läuft.
Still und heimlich irgendeine Quarzfrequenz anzunehmen, ist keine gute
Idee.
Tom E. schrieb:> Julian schrieb:>> #ifndef F_CPU>> #define F_CPU 8000000UL>> #endif>> Mutig, mutig.> Wenn du F_CPU nicht explizit angibst, suchst du dich tot nach dem> Fehler, falls der µC nicht zufällig mit 8MHz läuft.>> Still und heimlich irgendeine Quarzfrequenz anzunehmen, ist keine gute> Idee.
Was genau meinst du damit? Theoretisch läuft doch der Controller mit ca.
8Mhz. Wo liegt dann das Problem, dass so zu definieren?
Julian schrieb:> Theoretisch läuft doch der Controller mit ca. 8Mhz.
Wer sagt das? Das hängt doch ganz von der Bestückung deiner jeweiligen
Schaltung und der Einstellung der Fuses ab.
Das sagen die Fuses und das Datenblatt. Ich nutze den internen Oszilator
und der läuft meines Wissens mit 8Mhz. Ich verstehe das Problem nicht?
Sollte ich die Frequenz gar nicht angeben?
Julian schrieb:> Sollte ich die Frequenz gar nicht angeben?
Doch. Aber wenn du einen Mega1284 im Auslieferzustand anwendest, wird er
mit dem internen 8MHz Oszillator und gesetzter CKDIV8 Fuse betrieben -
resultierend in 1Mhz CPU Takt.
Julian schrieb:> Theoretisch läuft doch der Controller mit ca.> 8Mhz.
Allein diese Aussage zeigt, dass du keinerlei Ahnung hast, weil du
niemals ein Datenblatt gelesen hast.
Tatsächlich werden die allermeisten AVR8 (>99.9% des
Gesamtlieferumfangs) mit einem effektiven Takt von ca. *1*MHz
ausgeliefert.
Je nach Generation und Device kann es sich unterscheiden, wie diese ca.
1MHz erreicht werden, und es gibt auch einige (sehr wenige) Ausreißer im
AVR8-Produktspektrum von Atmel, die sehr stark von dieser Regel
abweichen, aber der Normalfall ist und bleibt ~*1*MHz, nicht 8MHz.
Julian schrieb:> Das sagen die Fuses und das Datenblatt. Ich nutze den internen Oszilator> und der läuft meines Wissens mit 8Mhz. Ich verstehe das Problem nicht?>> Sollte ich die Frequenz gar nicht angeben?
Doch sollst du.
Aber du sollst deinen Code nicht so schreiben, dass irgendwelcher Code
(und sei es die UART oder die LCD Routinen) einfach ein F_CPU annehmen
dürfen, wenn keine Vorgabe (zb im makefile oder in den Projekt Optionen)
getroffen wurden.
Sorge dafür, dass es dir nicht passieren kann, das verschiedene Teile
deines Programm mit verschiedenen Werten operieren! Und zwar sorge so
dafür, dass es bei der kleinsten Unregelmässigkeit (und sei es nur weil
du in irgendeinem File auf F_CPU vergessen hast) sofort zu einem Fehler
kommt! Du willst dir keinen Fehler dadurch verschleieren, dass du dann
stillschweigende Annahmen triffst! Das sieht auf den ersten Blick zwar
wie 'Komfort' aus, ist aber auf lange Sicht ein ständiges Ärgernis. Wenn
etwas faul ist, dann willst du, dass der COmpiler dir dafür auf die
Fingern klopft. Und nicht, dass er sich etwas aus den Fingern saugt.
F_CPU willst du am besten nur an einer einzigen Stelle festlegen:
nämlich im makefile oder in den Project-Options wenn du eine IDE
benutzt.
Und du willst, dass solltest du das vergessen, das in einem Fehler
mündet.
Prinzipiell korrekt, allerdings habe ich die Fuse gelöscht ;)
Aber dazu mal ne Frage. Mit der Definition von 8Mhz ist _delay_ms
relativ genau. Nun wollte ich sekündlich ein Interrupt haben. Dazu habe
ich folgenden Timer:
TCCR1A = (1 << WGM21);
TCCR1B = (1 << CS01) | (1 << CS00); // 1/64
OCR1A = 255;
TIMSK1 |= (1<<OCIE1A);
Müsste rein rechnerisch ~488Hz bringen. Allerdings bekomme ich ziemlich
genau die Hälfte! Woran liegt das?
"Gemessen mit":
ISR (TIMER1_COMPA_vect)
{
zaehler++;
if(zaehler > 243)
{
//Sekunde um
}
}
Habe ich mal 10 Minuten laufen lassen und war damit ca. 6 Sekunden
daneben. (Ist jetzt nicht gut, aber ja bei weitem nicht die errechneten
488Hz)
Karl H. schrieb:> [...] F_CPU willst du am besten nur an einer einzigen Stelle festlegen:> nämlich im makefile oder in den Project-Options wenn du eine IDE> benutzt.> Und du willst, dass solltest du das vergessen, das in einem Fehler> mündet.
Ergibt Sinn. Habe ich entsprechend geändert. Ist es denn "schlechter"
Stil, wenn man Projekte antrifft, bei denen das im Code angegeben ist
oder gibt es dafür dann einen anderen Grund?
Julian schrieb:> Stil, wenn man Projekte antrifft, bei denen das im Code angegeben ist> oder gibt es dafür dann einen anderen Grund?
historisch gewachsen.
Solange man nur ein einziges C-File hat, ist das ja auch kein Problem.
Sobald man aber anfängt, sich eine Sammlung von immer wiederkehrenden
FUnktionalitäten zu organisieren, landet man unweigerlich irgendwann an
dem Punkt, an dem zb die UART Funktionen immer noch mit einem F_CPU von
zb 16Mhz operieren, nur weil man 2 Jahre zuvor mal so einen Prozessor
hatte, obwohl für das gerade anstehende Projekt 4Mhz richtig gewesen
wären.
Und nach dem Fehler, warum seit Jahren bewährte Funktionen plötzlich
nicht mehr richtig funktionieren suchst du dir einen Wolf. Denn eben
weil sie schon so lange sauber funktionieren, rechnest du nicht damit,
dass sich dort eine Zeitbombe versteckt hat.
Julian schrieb:> Müsste rein rechnerisch ~488Hz bringen.
488,28125 um genau zu sein
8000000/64/256
> Allerdings bekomme ich ziemlich> genau die Hälfte! Woran liegt das?
Wieso
1
TCCR1A=(1<<WGM21);
das schaltet in den Modus Phase COrrect 9 Bit PWM, mit einem Top Wert
von 0x1FF. Und das ist fast das doppelte der gewünschten 0xFF
>> das schaltet in den Modus Phase COrrect 9 Bit PWM, mit einem Top Wert> von 0x1FF. Und das ist fast das doppelte der gewünschten 0xFF
Du wolltest eigentlich das Bit WGM12 in TCCR1B für den CTC Modus setzen.
Julian schrieb:> Ist es denn "schlechter"> Stil, wenn man Projekte antrifft, bei denen das im Code angegeben ist
Bis auf wenige Ausnahmen: Ja.
> oder gibt es dafür dann einen anderen Grund?
Das sind halt die Ausnahmen von der Regel. Stell dir z.B. ein System
vor, welches vom internen RC-Osziallator befeuert wird. Dieser wird aber
wiederum durch Code via OSCCAL-Register kontrolliert und letztlich z.B.
anhand einer asynchronen RTC-Referenz eingestellt.
Das ist echt heavy, denn hier gibt es de facto überhaupt nix, auf was
man sich jederzeit verlassen könnte.
Allein dieses Beispiel zeigt, wie schwachsinnig u.U. starre Regeln
sind...
Aber für den Normalfall gilt: es gibt nur eine Taktdomain und deren Takt
sollte an genau einer Stelle deklariert werden.
Karl H. schrieb:> Du wolltest eigentlich das Bit WGM12 in TCCR1B für den CTC Modus setzen.Karl H. schrieb:> das schaltet in den Modus Phase COrrect 9 Bit PWM, mit einem Top Wert> von 0x1FF. Und das ist fast das doppelte der gewünschten 0xFF
In der Tat... Im Datenblatt in den 8-Bit-Timer gerutscht. Nun
funktioniert es wie gewollt :)
Danke für eure Hilfe!
@c-hater
Klingt interessant, aber da bleibe ich doch erstmal bei einem Takt :D
Der stiftet (wie man sieht) ja auch schon gerne Verwirrung.
und @Thomas E.
Ist eingefügt ;) danke für den Tipp!