Hallo!
Ich habe ein sehr schwierig einzuordnendes Problem mit einem PIC18F252
(MPLAB X IDE v2.00, XC8 v1.34). Folgende Routine gibt mir 16Bit-Zahlen
zu Debugging-Zwecken auf der UART-Schnittstelle aus. Nach einer
unbestimmten Zeit (ca. 20 sek, kann nicht genau sagen, ob regelmäßig)
fängt der Output an kryptisch zu werden (siehe konsole.png). Durch ewige
Sucherei habe ich endlich herausgefunden, woher der Datenmüll kommt,
kann mir aber absolut nicht erklären, warum dies so ist. Datenmüll tritt
auf, wenn der Interrupt in die Routine zur Ausgabe der Zeichen zurück
springt. Dann haben sich aus mir unerklärlichen Gründen die Werte der
lokalen Variablen geändert (siehe buf.png, number.png, dez.png). Dass in
"dez[4]" jetzt komplett andere Werte stehen als bei der Initialisierung
der Variablen geht mir einfach nicht in den Kopf. Ebenfalls, dass in buf
(angelegt zu Debugging-Zwecken) noch der Wert steht, welchen "number"
zum Funktionsaufruf hatte, jetzt aber in "number" ein kompletter
Mondwert (ja, verändert sich, dürfte aber nur kleiner werden) steht.
Wenn ich die Funktion mit deaktivierten Interrupts laufen lasse und
immer einen um Eins erhöhten uint16 füttere, läuft alles problemlos.
Hat jemand eine Anregung für mich, woher das Problem kommen könnte? Ich
bin kurz davor das Programm anders aufzubauen, dies befriedigt aber
meiner Neugier nicht.
Schonmal vielen Dank im Vorraus!
Problematische Funktion:
1
voidUARTputUint16(uint16_tnumber)
2
{
3
/* description: send a decimal number over the serial port
4
* parameters: -
5
* returns: -
6
*/
7
8
uint16_tdez[4]={10000,1000,100,10};
9
uint8_tcnt,i;
10
uint16_tbuf=number;
11
12
// int16 <= 65535 -> maximum 5 decades
13
for(i=0;i<4;i++)
14
{
15
cnt='0';
16
17
// subtract decade as long as it fits
18
while(number>=dez[i])
19
{
20
number-=dez[i];
21
cnt++;
22
}
23
24
// output the digit
25
UARTputChar(cnt);
26
}
27
28
if(number>9)
29
{
30
NOP();
31
buf+=0;
32
}
33
34
UARTputChar(number+'0');
35
}
UARTputChar:
1
voidUARTputChar(charc)
2
{
3
/* description: send a character over UART and wait for buffer to clear
4
* parameters: c - character to be sent
5
* returns: -
6
*/
7
TXREG=c;
8
9
// wait untin sending buffer is empty
10
while(PIR1bits.TXIF==0)
11
{
12
NOP();
13
}
14
}
Im Interrupt wird ein Flag gesetzt (_ACflag), welches dann in der Main
gepollt wird und die problematische Ausgabe aufruft.
ISR:
Enrico Koeck schrieb:> Hat jemand eine Anregung für mich, woher das Problem kommen könnte?
vermutlich ein Speicherüberschreiber irgendwo im code. Oder wenn sich
die Funktionen gegenseitig aufrufen, ein Stack überlauf.
Lass dir mal das Disassembly deiner ISR und deiner Uart Ausgabe
anzeigen. Vieleicht siehst du dann, welcher Speicher tatsächlich von den
jeweiligen Funktionen verwendet wird.
Mich verwirrt dieses Phänomen, da du in der ISR defacto keine Stack
Variablen hast.
Disassembly zu finden unter Window->Debug->Disassembly oder so...
Enrico Koeck schrieb:> INTCONbits.GIE = 1;
Du gibst den Interrupt innerhalb der ISR frei, das könnte das Problem
sein.
Und warum machst du das:
Enrico Koeck schrieb:> // wait untin sending buffer is empty> while(PIR1bits.TXIF == 0)> {> NOP();> }
Das UART macht das Senden auch ohne daß du wartest. Erst wenn ein neues
Byte gesendet werden soll braucht man zu schauen, ob dafür Platz ist.
Little Basdart schrieb:> Lass dir mal das Disassembly ...
Der C-Debugger zeigt doch die Adressen direkt an
MfG Klaus
Enrico Koeck schrieb:> Das Problem hat sich jedenfalls in Luft aufgelöst, wenn ich das Array> dez[4] als globalesconst uint16_t dez[4] anlege, was auch sinnvoller> ist. So ein wenig fader Beigeschmack bliebt> aber trotzdem zurück ^^
richtig, aus dem Grund würde ich es fehlerhaft lassen und lieber den
Fehler suchen.
Enrico Koeck schrieb:> Das Problem hat sich jedenfalls in Luft aufgelöst
Nö.
Globale und lokale Variablen werden einfach nur an verschiedene Stellen
gelinkt.
D.h. der Fehler verändert jetzt ne andere Variable oder mit etwas Glück
noch ungenutzten RAM.
Klaus schrieb:> Little Basdart schrieb:>> Lass dir mal das Disassembly ...>> Der C-Debugger zeigt doch die Adressen direkt an
Das ist auch so weit richtig, jedoch nur die Adressen der Variablen. Da
der PIC aber nur ein Work Register hat, müssen Zwischenergebnisse ins
RAM geschrieben werden. Diese Adressen werden nicht angezeigt und sieht
man nur im Disassembly.
Also das Problem hat in irgendeiner Art und Weise damit zu tun, dass der
Portchange-Interrupt und der Capture-Interrupt sich gegenseitig
verhaken.. Ich habe jetzt meinen Algorithmus angepasst und den Code
komplett umgeschrieben, sodass dies jetzt nicht mehr vorkommen kann.
schulterzuck
Vielen Dank für die Anregungen!
In der ISR vermisse ich das Rücksetzen der Inetrrupt-Flags!
Sobald GIE wieder gesetzt wird, wird der Interrupt erneut aufgerufen.
Durch die vielfache Verschachtelung der Aufrufe läuft der Stack über,
was vermutlich zum Reset führt. Das explizite Setzen des GIE ist ohnehin
unnötig, da der PIC das am Ende der ISR über RETFIE automatisch macht!
Gruss
Mike
Okay, das GIE hab ich mal rausgenommen, wusste nicht genau, ob das
automatisch gesetzt wird. Die Interruptflags werden immer gleich nach
der Abfrage, welcher Interrupt kam, gelöscht.