Ich habe einen Atmega8 in Arbeit.
Leider hängt er sich (ausschließlich) innerhalb folgender Schleife auf:
1
while(!btn_enter&&!btn_menue){
2
3
if(btn_up&&*constant<i)*constant=*constant+1;
4
if(btn_down&&*constant>0)*constant=*constant-1;
5
6
btn_down=btn_up=0;
7
8
set_cursor(x,1);
9
10
// Zeit oder Datum anzeigen?
11
if(time)show_time();
12
else{
13
14
show_wkday();
15
show_date();
16
17
}
18
19
20
21
if(btn_menue)return;
22
23
_delay_ms(100);
24
25
}
Das passiert rein zufällig.
btn_... wird über einen Interrupt von einem Portexpander gesetzt.
Ansonsten wird nur ein Wert erhöht, bzw. erniedrigt und dann wird er
angezeigt.
Aktiviert sind zudem beide Externe-Interrupts, 3 Timer für SoftPWM etc.
Fällt jemanden ein Grund ein, warum der µC sich an dieser Stelle
aufhängen kann oderliegt es an einem anderen Codeteil?
Leider kann man bei diesem Codefetzen nur raten. Es fehlen die
Definitionen aller Variablen und die Definitionen der aufgerufenen
Funktionen.
> btn_... wird über einen Interrupt von einem Portexpander gesetzt.
Und sind deshalb volatile gekennzeichnet?
Hängt sich die nackische Variante, d,h. keine Funktionsaufrufe auch auf?
Um das zu erkennen, kannst du z.B. vor dem while eine Debug-LED
anschalten und dannach ausschalten. Wenn du ein Oszi oder ein LA hast,
reicht das Schalten eines freien Portpins natürlich auch.
> Und sind deshalb volatile gekennzeichnet?
Jup,ich brauche Sie später noch in der main();
Die Variablen sind im übrigen alle int.
*constant zeigt auf ein Element einer Struktur.
Durch das ständige auftreten von Interrupts (habe alle 3 Timer an)
könnte der Programmablauf bei entsprechend langen Interruptroutinen ja
blockiert werden.
Könnte das ein Grund sein. Dann müsste das aber jedes mal passieren und
nicht rein zufällig.
Oft lässt sich der Wert auch ein paar ma ändern und dann hängt der AVR
sich auf.
Könnte es sein, dass constant urplötzlich in den Stack zeigt?
Falls ja, ist Aufhängen vorprogrammiert.
Es wäre ansonsten viel besser, wenn man den kompletten Code sieht.
Du glaubst gar nicht, wie viele Leute schon behaupteten, dass die
anderen Funktionen ihres Programms garantiert fehlerfrei seien. Meist
steckte der Fehler dann in diesen Funktionen.
Btw:
Lange Interrupts sollte es nicht geben. In einem INT wird nur das
nötigste gemacht (kein delay!); idealerweise nur Flags gesetzt, die dann
in der Hauptschleife ausgewertet werden.
> Wie hast du das denn herausgefunden?
Die Funktion ändert einen Wert (z.B. eine Stelle der Uhrzeit). Die
Gesamte Uhrzeit ist in einer Struktur gespeichert, welche dann mittels
show_time() ausgegeben wird. Drück man jetzt also eine Taste, erhöht
sich der Wert und der neue Wert wird angezeigt. Manchmalpassiert aber
gar nix, obwohl der Interrupt ausgelöst wird.
Der rest funktioniert eigendlich so wie er soll, ich poste aber mal
alles nötige:
Die Interrupts (bis auf Tastatur-Interrupt) habe ich jetzt so geändert,
dass sie nur noch Flags setzen.
Wenn ich die Funktionsaufrufe zum Anzeigen auslasse, konnte den Fehler
bisher nicht reproduzieren. Diese Funktionen werden aber auch regülärzum
Anzeigen der Uhrzeit im Hauptprogramm genutzt.Dort gab es damit noch nie
Probleme.
*constant ist doch im Funktionskopf definiert
> [...] (int *constant, [...]
Reicht das nicht, oder habe ich deine Frage falsch verstanden?
Die Struktur für die Zeit ist folgende:
1
// Zeit-Struktur
2
structtime_struc{
3
intthours;
4
intnhours;
5
6
inttmin;
7
intnmin;
8
9
inttsec;
10
intnsec;
11
12
inttday;
13
intnday;
14
15
inttmon;
16
intnmon;
17
18
intwkday;
19
20
intyear;
21
};
An *constant wird nur die Adresse eines Elements der Struktur übergeben.
Hannes Eilers schrieb:
> Die Variablen sind im übrigen alle int.
Und alle Zugriffe im gezeigten Code nicht nicht atomar... s.
Interrupt> char ASCII[ASCII_L];
Ich sehe keine Definition von ASCII_L. Reicht der Platz für das spätere
itoa()?
Diese lokale Variable belastet den Stack. Wenn der Stack bereits eng ist
und eine ISR angesprungen wird, kann die ISR der Todesstoss sein. Ich
würde die versuchsweise global anlegen. Wieviel RAM braucht dein
Programm ungefähr (=> AVR-GCC => avr-size)?
> Und alle Zugriffe im gezeigten Code nicht nicht atomar...
Daran habe ich gar nicht gedacht. Werde den Interrupt während des
Zugriffs auf die Variabeln mal ausschalten.
1
#define ASCII_L 9
Ich brauche aber ind er Regel weniger Platz. Könnte das also nich
optimieren. Ist noch so hoch, weil ich beim "anlegen" der Def. nicht
wusste,wie viel Platz ich brauche.
Das Programm benötigt inzwischen 86,3% (7072 Byte) des
Programmspeichers.
Die Sache mit den Einzelvariablen für die Buttons finde ich keine so
gute Idee. Die würde ich alle beisammen in einem uint8_t lassen (du bist
überhaupt mit int ziemlich freigiebig!)
Wenn dir zb hier
btn_menue_key = 1;
btn_menue = 0;
btn_enter = 0;
btn_up = 0;
btn_down = 0;
ein Interrupt dazwischenknallt, der einzelne btn_xxx Variablen setzt,
dann können, je nachdem wo der Interrupt auftritt, Tastendrücke
verlorengehen.
Hannes Eilers schrieb:
> Das Programm benötigt inzwischen 86,3% (7072 Byte) des> Programmspeichers.
Das ist die Größe im Flash also .text plus .data (plus
Spezialsektionen)? diese Zahl ist weniger interessant.
Wie groß ist der statische Bedarf an RAM, also .data plus .bss?
RAM-Größe minus statischer Bedarf ist ca. max. Stackgröße. Wenn der
statische Bedarf in Richtung >3/4 des RAM geht, fange ich an, die Sache
kritisch zu beäugen.
Hannes Eilers schrieb:
> Das Programm benötigt inzwischen 86,3% (7072 Byte) des> Programmspeichers.
Das kriegt man schon noch runter.
Du bist da an einigen gezeigten Stellen ziemlich suboptimal unterwegs.
Du hast zb einige Zahlenausgaben und benutzt dafür jedesmal die Sequenz
lcd_string( itoa(edit_timestamp.thours, ASCII, 10) );
Warum nicht für Zahlenausgabe eine eigene Funktion machen
void lcd_number( int num )
{
char buf[10];
itoa( num, buf, 10 );
lcd_string( buf );
}
und dann an den verwendenden Stellen
lcd_number( edit_timestamp.thours );
die Funktion braucht zwar auch etwas Platz, aber dafür sparst du an der
Stelle der Verwendung Code ein. Und da du die Funktion oft verwendest
spart das dann in Summe mehr Code ein als durch die zusätzliche Funktion
aufgebraucht wird.
Aber im Grunde ist die Menge des verwendeten Flash uninteressat. Die
Data Section ist das interessante.
> du bist überhaupt mit int ziemlich freigiebig!
Das stimmt,ich finde esso übersichtlicher.DerDatenspeicher ist
gerademalbei 35% da geht also nich was. Aber man könnte das sicherlich
ändern und optimieren.
>je nachdem wo der Interrupt auftritt, Tastendrücke verlorengehen
Wäre ja nicht so schlimm,man müsste halt nochmal auf die Taste drücken
:-)
Was meinst du denn,könnte es Probleme mit dem Stack und der ASCII_L
geben?
Ich werde mal versuchen den Code weiterb zu optimieren. Vielleicht leigt
es daran. Ich werde aber trotzdem noch etwas Platz für dasProg brauchen,
da noch einige Funktionen fehlen.
Hannes Eilers schrieb:
>> du bist überhaupt mit int ziemlich freigiebig!> Das stimmt,ich finde esso übersichtlicher.
Was ist da dran übersichtlicher? gar nichts!
1
structtime_struc{
2
uint8_tthours;
3
uint8_tnhours;
4
5
uint8_ttmin;
6
uint8_tnmin;
7
8
uint8_ttsec;
9
uint8_tnsec;
10
11
uint8_ttday;
12
uint8_tnday;
13
14
uint8_ttmon;
15
uint8_tnmon;
16
17
uint8_twkday;
18
19
intyear;
20
};
ist genauso übersichtlich wie deine Version. In der verwendenden
Programmierung gibt es keinen Unterschied, ausser das du dir um atomaren
Zugriff auf einzelne Member der Struktur keine Gedanken mehr zu machen
brauchst.
Lass es dir noch einmal eindringlich gesagt sein:
Auf einem 8-Bit µC, wie einem Mega8, ist der Datentyp uint8_t oder
int8_t immer deine erste Wahl!
* das ist der Datentyp mit dem der µC am besten umgehen kann
* alles andere erfordert mehr Aufwand als notwendig
* du hast bei allen anderen Typen immer das Problem, dass du
Zugriff unter Umständen atomar absichern musst.
* du blockierst unnötigerweise Speicher, der dir an anderer Stelle
manchmal schmerzlich fehlen kann
(Warum gibt es eigentlich in einem Datum jeweils einen t und einen n
Eintrag? Das erscheint mir nicht wirklich logisch)
>>je nachdem wo der Interrupt auftritt, Tastendrücke verlorengehen> Wäre ja nicht so schlimm,man müsste halt nochmal auf die Taste drücken> :-)
Doch das ist schlimm.
>> Was meinst du denn,könnte es Probleme mit dem Stack und der ASCII_L> geben?
Bei 35% RAM Verbrauch: unwahrscheinlich.
Da wird wohl irgendwo ein Pointer im Wald stehen.
Hannes Eilers schrieb:
> Ich werde mal versuchen den Code weiterb zu optimieren.
Schlechte Strategie.
Durch optimieren wird es selten besser, solange das Problem nicht 'Out
of memory' lautet.
Hannes Eilers schrieb:
> Die Variablen sind im übrigen alle int.
Damit verschenkst Du schonmal ein Großteil des Speicherplatzes, oder hat
bei Dir die Stunde 65535 Minuten?
Nimm besser uint8_t, wos ausreicht.
Peter
Also ich habe jetzt alle Variabeln angepasst, was mir mächtig Platz
spart.
Hierfür erstmal danke!
>(Warum gibt es eigentlich in einem Datum jeweils einen t und einen n Eintrag? Das
erscheint mir nicht wirklich logisch)
Das t ist für die Zehnerstelle das n für die Einerstelle.Ich brauche die
einzeln um die Dtaen hinterher in den Uhrenbaustein zu schieben, will
die Stellen leider einzeln haben.
Meine Tastur habe ich jetzt wie folgt realisiert:
Hannes Eilers schrieb:
>>(Warum gibt es eigentlich in einem Datum jeweils einen t und einen n Eintrag?
Das
> erscheint mir nicht wirklich logisch)>> Das t ist für die Zehnerstelle das n für die Einerstelle.Ich brauche die> einzeln um die Dtaen hinterher in den Uhrenbaustein zu schieben, will> die Stellen leider einzeln haben.
Das ist jetzt aber nicht dein Ernst, oder?
(Und dann noch für Zahlen die nur von 0 bis 9 laufen können einen
eigenen int :-)
Du zwirbelst dir hier eine Menge 'Ungemach' auf, nur damit du die
Zerlegung einer Zahl in Zehner und Einer direkt beim Verschicken an den
Uhrenbaustein einsparst?
Hast du Zehner und Einer, dann errechnet sich die dadurch dargestellte
Zahl durch:
Zahl = 10 * Zehner + Einer
und die Umkehrung, die Zerlegung ist
Zehner = Zahl / 10
Einer = Zahl - 10 * Zehner;
Problem gelöst und du brauchst beim Erhöhen/Erniedrigen der Uhrezeit,
sowie bei Vergleichen oder sonstigen Rechnereien keine 'Durch die Brust
von hintern ins Auge' Aktionen mit Zehnern und Einern machen.
Einzig bei der Ausgabe auf LCD muss man ein wenig darauf achten, dass
führende Nullen mit ausgegeben werden. Aber da die Ausgabe in einer
einzigen Funktion gesammelt ist, ist auch das kein wirkliches Problem
:-)
Jetzt versteh ich auch, warum da so viele lcd_string( itoa ...
Konstruktionen sind
Hannes Eilers schrieb:
> Bisher hebt das aber das Problem mit dem Aufhängen nicht auf.
Das Problem wird auch nicht in dieser Funktion sein.
Was du siehst, sind die Symptome. Die Ursache kann ganz woanders
stecken.
Hannes Eilers schrieb:
> Bisher hebt das aber das Problem mit dem Aufhängen nicht auf.
Nur Stochern im Nebel bringts eben nicht.
Poste endlich mal was compilierbares als Anhang!
Aber mach erstmal die tausende Leerzeilen weg, ich hab schon Brandblasen
am Zeigefinger vom vielen Scrollen.
Peter
Es sind mehrere Dateien.
Der AVR soll am Ende folgendes können(damit ihr versteht was ich mit dem
Code eig. bezwekcne will):
- Anzeige von Uhrzeit und Datum (ohne Jahr) im Wechsel (4sec.)
- Alarmfunktion
- Ansteuerung von RGB-LEDs über RGB-Werte (inkl-. Helligkeit)
- Anzeige der Daten auf einem LCD
- programmierbarer Alarm (best.Wochentage, Tägl. oder aus)
- Steuerung eines MP3-Player (über Simulation eines Tastendrucks)
Dazu habe ich einen PCF8583 und einen PCA9555 am I2C.
Letzterer übernimmt die Ports für den MP3-Player und ide Tastatur. Beide
sind über Interrupts angeschlossen.
Ich bin euch dankbar für die vielen Tipps, wie man etwas besser oder
einfacher realisieren kann.
Aber jetzt alles zu überarbeiten ist nicht Zweck dieses Forum, das muss
ich schon selber machen *freu, seufz* ;-)
Wenn jemand also einen Fehler findet, der dazu führen könnte, dass sich
der AVR aufhängt wäre ich ihm sehr dankbar.
p.s.: Ja,Wecker wird ander geschrieben :-))
Kurzer Zwischenbericht:
Du arbeitest mit malloc. Damit sind alle Aussagen bezüglich
Speicherverbrauch und den 35% zu Makulatur verkommen. Die Schlacht um
einen möglichen Stacküberlauf ist damit wieder eröffnet.
-> Versuch ohne malloc auszukommen.
Ein Menüsystem muss nicht die Menüs zur Laufzeit zusammenbauen. Das kann
man auch statisch machen.
Und dann stimmen die vom Compiler ausgeworfenen Zahlen bezgl.
Speicherverbrauch wieder und sind aussagekräftig.
> Das kann man auch statisch machen.
Ich möchte jedoch gerne die Menüstruktur als Liste machen, dann bietet
sich dieses Verfahrne doch an.
Wie würdest du es denn machen, dammit die Menüeinträge als Liste nicht
erst zur Laufzeit angelegt werden? Hab schon drüber nachgedacht sie ins
EEPROM zu legen, aber das ist auch nicht gerade so komfortabel.
Das Menü lässt sich auch prima aufrufen und durchblättern. Nur, wenn ich
die Funktion zum Verändern von Werten aufrufe hackts manchmal. manchmal
funktioniert es aber auch.
Hannes Eilers schrieb:
>> Das kann man auch statisch machen.> Ich möchte jedoch gerne die Menüstruktur als Liste machen, dann bietet> sich dieses Verfahrne doch an.
Kannst du doch.
Leg meinetwegen die einzelnen Knoten statisch an und verkette sie zur
Programmlaufzeit.
Aber verzichte auf malloc!
Der Aufruf des ersten Elements funktioneirt auch. Das nächste in der
Liste wird aber irgendwie nicht richtig gefunden, es tauchen dann als
bezeichnung auf dem Display nur Zeichenfragmente auf (da scheint der
Pointer irgendwie nicht richtig zu sein).
Hier
while( !keybrd ){
set_cursor(7,2);
lcd_string(">");
// ggf.Funktion aufrufen
if( keybrd == 0x08 && *function ){
lcd_clear();
(*function)(tmp -> fdata);
keybrd = 0x20;
}
// Warten auf Benutzereingabe
if( keybrd == 0x10 ) tmp = tmp -> parent;
if( keybrd == 0x08 && !function ) tmp = tmp -> child;
if( keybrd == 0x04 ) tmp = tmp -> prev;
if( keybrd == 0x02 ) tmp = tmp -> next;
solltest du unbedingt noch 'function' nachziehen. Sonst kann es dir
passieren, dass du die falsche Funktion aufrufst. Am besten jedoch,
eliminerst du die Variable function und greifst immer über tmp zu
(und bitte gewöhn dir an dieser Stelle die Leerzeichen wieder ab. Der
Pointer und der Member auf den über den Pointer zugegriffen werden soll,
gehören zusammen. Der -> ist Trenner genug.
Ich bin auch versucht zu sagen: Gewöhn dir auch das weiterschreiben in
einer Zeile nach einem if wieder ab. Die abhängige Anweisung kommt
eingerückt eine Zeile tiefer. Dann sieht man auch aus 3m Entfernung, das
da irgendetwas besonderes passiert)
Auch wimmeln mir da ein bischen zuviele HEX-Konstanten rum. Mach dir
dafür ein paar #define und lesbare Namen.
1
// Funktionen für die Menünavigation
2
voidmenue(){
3
4
structmenueP*tmp=start;
5
6
while(tmp){
7
8
keybrd=0x00;
9
10
lcd_bg(1);// Displaybeleuchtung an
11
lcd_clear();
12
lcd_home();
13
lcd_string(tmp->name);
14
15
while(!keybrd){
16
set_cursor(7,2);
17
lcd_string(">");
18
19
if((keybrd&KEY_ENTER)){
20
if(tmp->function){
21
lcd_clear();
22
(*tmp->function)(tmp->fdata);
23
keybrd=0x20;// was ist 0x20?
24
}
25
else
26
tmp=tmp->child;
27
}
28
29
elseif(keybrd&KEY_PARENT)
30
tmp=tmp->parent;
31
32
elseif(keybrd&KEY_PREV)
33
tmp=tmp->prev;
34
35
elseif(keybrd&KEY_NEXT)
36
tmp=tmp->next;
37
}
38
}
39
}
Muss das wirklich sein, dass da bei jedem Durchlauf durch die äussere
while-Schleife das Display gelöscht und neu beschrieben wird? Das wird
ganz schön flackern :-)
Hannes Eilers schrieb:
> Also z.B. so?:
Genau
> Der Aufruf des ersten Elements funktioneirt auch. Das nächste in der> Liste wird aber irgendwie nicht richtig gefunden, es tauchen dann als> bezeichnung auf dem Display nur Zeichenfragmente auf (da scheint der> Pointer irgendwie nicht richtig zu sein).
Da hast du irgendeinen Fehler gemacht.
Allerdings ist der ganze 'start' Pointer suspekt. Den braucht kein
Mensch.
Übergib der menue Funktion die Adresse des ersten Menüknotens und gut
ists.
Du scheinst höllische Angst davor zu haben, Argumente an Funktionen zu
übergeben. Statt dessen hast du dann eine Menge globale Variablen, die
den Sacheverhalt auch nicht besser machen.
OK, habe jetzt alles geändert.
Navigation durch die Menüs funktioniert auch.
.data + .bss +.noinit = 79,6%
.text + .data +.bootloader = 61,1%
Ich habe zwar nciht so viel Ahnung wie viele Daten man maximal haben
sollte, damit der Stack nicht voll ist aber ~80% sind wohl zu viel oder?
[edit]
Achso, lasse ich die Untermenüs weg, sind es statt ~80% nur ~15%.
Ev. könnte man die Hauptnavigation statisch machen und die Unterpunkte
dynamisch generieren, dass kostet mehr Programmzeilen aber erheblich
weniger Platz auf dem Stack oder?
[/edit]
Hannes Eilers schrieb:
> Ich habe zwar nciht so viel Ahnung wie viele Daten man maximal haben> sollte, damit der Stack nicht voll ist aber ~80% sind wohl zu viel oder?
Welcher Prozessor?
80% ist schon eine Menge Holz.
> [edit]> Achso, lasse ich die Untermenüs weg, sind es statt ~80% nur ~15%.> Ev. könnte man die Hauptnavigation statisch machen und die Unterpunkte> dynamisch generieren, dass kostet mehr Programmzeilen aber erheblich> weniger Platz auf dem Stack oder?> [/edit]
Den meisten Platz werden die Texte brauchen.
Jeweils ein 17-er char Array für einen Text ist schon ziemlich viel.
Mach da mal einen char* Pointer rein und setze den auf den in der
Erzeugungsroutine auf den konstanten Text den du bekommst. Das sollte
den Speicher schon mal drücken. Wenn das immer noch nicht reicht könnte
man die Texte ins Flash auslagern. Das geht immer noch relativ billig
ohen allzuviele Änderungen im Programm.
so würde ich das Menüsystem aufbauen. Wenn du den parent noch haben
willst, dann mach in die Menüstruktur noch den Pointer dafür rein und
füll den Parent Pointer dynamisch in der Menü Funktion aus. Wenn in ein
Childmenü gewechselt wird, wird der parent Pointer in diesem Childmenü
auf den Meneüpointer gesetzt, der für den Aufruf gesorgt hat.
Diese Darstellung oben hat auch den Vorteil gegenüber einer dynamischen
Allokierung, dass man einen besseren Überblick über den Menüaufbau hat,
als wie wenn sich der Aufbau in 40 Zeilen Code verbergen.
> Mach da mal einen char* Pointer rein und setze den auf den in der> Erzeugungsroutine auf den konstanten Text den du bekommst.
Damit komme ich auf 53,2%
µC ist ein ATmega8-16
Fehler tritt aber immer noch auf.
Hannes Eilers schrieb:
>> Mach da mal einen char* Pointer rein und setze den auf den in der> Erzeugungsroutine auf den konstanten Text den du bekommst.>> Damit komme ich auf 53,2%> µC ist ein ATmega8-16
OK.
Damit sollte man auf der sicheren Seite sein.
> Fehler tritt aber immer noch auf.
Es wäre einfacher, wenn ich hier das Zeug nachstellen könnte :-)
So bleibt nur: Alle Pointer Zugriffe absuchen. Sicherheitshalber immer
auf NULL Pointer testen.
Solche Abfragen
if( keybrd == 0x04 && *constant < i ){
**************
sind nicht sehr schlau. Du hast deine Tasten bitcodiert. Jede Taste hat
ihr eigenes Bit. Das kannst du benutzen um defensiv zu programmieren
if( ( keybrd & 0x04 ) && *constant < i )
Der Unterschied:
Bei deiner Version MUSS keybrd exakt 0x04 sein. Ist irgendein anderes
Bit aus irgendwelchem Grund gesetzt, dann geht deine Version nicht mehr.
Bei meiner Version wird getestet, ob das zur Taste gehörende Bit gesetzt
ist. Diese Version funktioniert also auch dann noch, wenn in keybrd
irgendein anderes Bit zusätzlich gesetzt wurde. Bei den erhöh und
erniedrig-Tasten ist das wahrscheinlich egal, aber die Tasten für
'Aussteigen' können davon profitieren, weil dein Programm dann auch aus
einer 'kleinen' Fehlersituation heraus noch sinnvoll arbeiten kann.
Hannes Eilers schrieb:
> Na dann mal ran.(Das wird ein Spaß!) *seufz*
Nochmal:
Was musst du exakt machen, damit es zum Fehler kommt?
Und wie sieht der Fehler aus?
(und poste nochmal deinen jetzigen Code)
Hannes Eilers schrieb:
> Na dann mal ran.(Das wird ein Spaß!) *seufz*
Na ja.
So schlimm auch wieder nicht.
Du hast ein LCD.
Das kannst du benutzen um dir aus dem Programm heraus, Statusausgaben
machen zu lassen. Wenn dein Programm nicht mehr auf Tastendrücke
reagiert, aber in einer Schleife sein müsste, ist es zb eine gute Idee
innerhalb dieser Schleife sich die keybrd Variable an einer Stelle im
LCD ausgeben zu lassen.
usw. Wenn du nicht weißt was dein Programm tut, dann verpass ihm
Ausgaben, so dass dir das Programm erzählt was es gerade tut :-)
Also der Fehler muss irgendwo in der Schleife liegen.
Außerhalb tritt nie ein Fehler auf.
Wenn ich aber innerhalb einfach mal wild auf die Tasten drücke, bleibt
der AVR stehen und erhöht den Wert nicht mehr. Der Interrupt wird auch
nicht mehr aufgerufen.
verwende ich jetzt aber Statusmeldungen um die Fehlerquelle zu
lokalisieren, hängt er sich nicht mehr auf.
Ich habe daher einmal das delay am Ende auf 250ms gesetzt (ist ja eine
kaum spürbare Zeit) und siehe da: Kein Fehler mehr.
Wenn ich gar kein delay rein mache hängt er sich im übrigen sofort auf.
Auf jeden Fall habe ich es heute geschafft, dass mein Code viel weniger
Platz weg nimmt (jetzt ~53%).
Oh oh oh !
Wenn dann im Mainprogramm auch I2C gemacht wird, krachts.
I2C ist quasi ein Device, wie z.B. das LCD. Ein Transfer darf nicht
durch eine anderen unterbrochen werden.
Wenn Du z.B. in Word eine Datei öffnest, kann da auch kein anderer
schreiben. Es würde krachen, daher blockiert der PC Devices und Dateien,
bis der anfordernde Prozess fertig ist.
Man sollte für I2C auch immer ein Timeout aufsetzen (1-10ms). Falls mal
ne Störung erfolgt und der Bus sich verklemmt, würde sonst Dein Program
für immer hängen.
Peter
> Oh oh oh !
Das wird nur vor der Aktivierung der Interrupts aufgerufen, da der
PCA9555 nach dem Start bereits einen Interrupt erzeugt. Er geht nämlich
davon aus, dass an den Pins ein High liegen soll, tut's aber nciht >>
Interrupt.
Dann beschreibe ich ihn, dass Low normal ist udn nur bei high ein
interrupt kommen soll.
Der erste Interrupt verschwindet aber erst, nachdem sich der Status des
Pins ändert oder man ihn einmal ausliest.
Da sollte es also keine Probleme geben.
Habe jetzt einmalmit dem Eintritt in's Menü die Timer ausgeschaltet
(bzw. die Interrupts dafür deaktiviert), dann lässt sich das delay noch
ein wenig nach unten korrigieren.
Hannes Eilers schrieb:
> Habe jetzt einmalmit dem Eintritt in's Menü die Timer ausgeschaltet> (bzw. die Interrupts dafür deaktiviert), dann lässt sich das delay noch> ein wenig nach unten korrigieren.
Dann scheine ich ja richtig zu liegen.
Ich weiß zwar nicht, welches Delay Du meinst, aber für ein sicheres
Programmkonzept darf davon nicht die Funktion abhängen.
Ich würde dieses gefährliche Konzept in jedem Fall ändern.
Die Verwendung einer nicht reentranten Funktion in Main und Interrupt
ist pfui-bäh und wird Dir auch immer wieder auf die Füße fallen.
Setze einfach im Interrupt ein Bit und im Main wird dieses getestet und
dann an sicherer Stelle das I2C aufgerufen.
Tastendrücke sind nichts eiliges, da ist kein Interrupt nötig.
Peter
> Die Verwendung einer nicht reentranten Funktion in Main und Interrupt ist >
pfui-bäh
Da muss ich zustimmen. Ich habe das jetzt geändert, was den Programmcode
etwas größer macht, da ja jede Funktion die laufend auf Tastatureingaben
wartet eine Zeile mehr braucht, aber dafür läuft jetzt alles stabil und
ohne Fehler (auch ohne delays).
Manchmal vergisst man eben diese elementaren (wichtigen!) Dinge.
Danke für eure Hilfe.