Hallo,
ich habe ein ganz sonderbares Problem mit einem uint32_t Array (auf
einem AT Tiny 84A):
Ich habe folgenden Timer Interrupt:
- adcavgbuffer ist ein uint32_t Array (und soll eigentlich die
Messwerte aufsummieren, habe ich aber momentan vereinfacht auf +1)
- txbuffer ist der Ausgabepuffer des I2C Client, ab Position 46/47
steht wird die Anzahl der Messwerte gezählt (bei der I2C Abfrage soll
dann der entsprechende Durchschnitt mit dem adcavgbuffer berechnet
werden)
Mit dem aktuellen Code würde man ja erwarten, dass adcavgbuffer[0] und
txbuffer[46] / txbuffer[47] (für die ersten 65535 Timer Ticks)
übereinstimmen...
Was ich aber bekomme, ist das hier... (über Byte 48 gebe ich das lower
Byte von adcavgbuffer[0] aus) - adcavgbuffer[0] scheint bei jedem Tick
2mal inkrementiert zu werden:
In der Header-Datei stehen folgende Variablen Definition:
1
volatileuint8_tadcintch;
2
volatileuint32_tadcavgbuffer[6];
3
uint8_tadcpattern;
Ich bin ziemlich ratlos, was die Ursache sein könnte... habe bereits 3
verschieden AT Tiny Chips getestet, alle das selbe. Die Compiler
(avr-gcc 4.3.3) Optimierung habe ich bereits deaktiviert.
Und noch lustiger, das Problem verschwindet, wenn ich die Zeile
"adcintch += 1;" auskommentiere! Im Disassemly Code sind das gerade mal
diese 3 Zeilen unterschied (das Array adcavgbuffer beginnt bei
Speicheradresse 0x00A0).
1
adcintch += 1;
2
744: 80 91 61 00 lds r24, 0x0061
3
748: 8f 5f subi r24, 0xFF ; 255
4
74a: 80 93 61 00 sts 0x0061, r24
Ich glaube irgendwie nicht, dass das ein Hardware Defekt ist, kann mir
aber auch nicht erklären was an Speicheradresse 0x00A0 so besonders
ist... (wenn ich ein vor dem Array eine Variable lösche und das Array
ein Byte Vorrutscht, dann ist das Problem eben im 2. Byte des uint32).
Hat vielleicht irgendjemand noch eine Idee?
Danke,
muli
Was Du leider nicht geschrieben hast ist, wie die Taktrate des
TIM0_COMPA_vect Interrupt ist.
Dir erscheinen die "paar" Zeilen Code in der Interruptroutine wenig,
aber wenn die Interruptfrequenz sehr hoch ist, kann dies zu solchen
Effekten,
wie Du sie gerade erlebst, führen.
3 Zeilen weniger Code und Dein Problem taucht nicht auf? Klares
Laufzeitproblem in der Interruptroutine.
32 und 16 Bit Zugriffe vermeide ich immer in Interrupts.
Da setze ich mir lieber nur ein Eventflag und verarbeite es in
Hauptschleife.
Wenn es denn sein muss, dann verwende ich unbenutzte IO-Register um die
Werte dort abzuspeichern. Diese schiebe ich dann in der main in einen 32
Bit Wert zusammen.
Deine AVRs sind mit Sicherheit nicht kaputt.
Upps, klar, sorry... Timer-Frequenz soll eigentlich in 1kHz sein (CPU
Takt sind 8MHz), im Laufe meiner Test habe ich diese aber auf 60Hz
reduziert.
Sollte also kein Problem sein, da hinterher zu kommen (einzig anderen
Interrupt habe ich noch für den I2C via USI).
Die uint32_t Addition lässt sich im Interrupt wohl nicht vermeiden,
sonst verliere ich eventuell Messwerte (deswegen habe ich aber die
Berechnung des Durchschnitts schon ausgelagert).
muli1329 schrieb:> ISR (TIM0_COMPA_vect)> {> adcavgbuffer[adcintch] += 1;>> adctemp16 = (txbuffer[adcintch*4+46] | (txbuffer[adcintch*4+47] << 8> )) + 1;
Ich nehme mal an, adctemp16 ist ein int16_t? Den würde ich übrigens in
der ISR als lokale Variable deklarieren.
> txbuffer[adcintch*4+46] = adctemp16;
Was ist der Datentyp von txbuffer? Ich nehme mal an, das ist char? Dann
haust Du da gerade einen 16bit-int in einen char.
Vielleicht hilft es Dir ja weiter wenn Du die Interrupt-Routine zunächst
mal als normale Funktion in einer for-Schleife immer wieder aufrufst und
Dir im Debugger anguckst, was passiert.
Offen gesagt verstehe ich das Problem wahrscheinlich nicht so recht,
aber ich habe so eine Ahnung, dass man möglicherweise einmal die Effekte
von integer-Promotion und impliziter Umwandlung anschauen sollte. Eine
vorher mögliche sinnvolle Vereinfachung (um zunächst mal dem
eigentlichen Problem auf die Spur zu kommen) wäre, nur für den tx-Buffer
ein zwei-elementiges Array zu verwenden und adcavgbuffer durch eine
einfache Variable zu ersetzen.
Ein anderer Gedanke ist der, die ständige Veränderung im tx-buffer ganz
zu unterlassen und einfach eine Zählervariable zu verwenden, die nur bei
Bedarf in den tx-Buffer übertragen wird. Ich halte das ohnehin für
naheliegender, aber das ist Ansichtssache.
Genau, adctemp16 ist ein uint16_t. Und txbuffer ist uint8_t.
Die Zuweisung txbuffer[adcintch*4+46] = adctemp16 ist absichtlich uint16
auf uint 8, in txbuffer[46] soll das lower Byte. txbuffer[adcintch*4+47]
= adctemp16>>8 schreibt dann in der nächsten Zeile das upper Byte in
txbuffer[47].
Die Idee mit der for-Schleife im Hauptprogramm ist gut, das werde ich
mal ausprobieren - muss da nur etwas basteln, weil meine einzige
"Debug-Ausgabe" der I2C-Bus ist...
Statt der Umwandlung im txbuffer eine Zählervariable zu verwenden,
dürfte auf das selbe rauslaufen, die müsste immer uint16 sein (und so
spare ich mir einen Schritt für das Rückwandeln in uint8 beim Auslesen).
Nochmal zum Problem: Ich denke grundsätzlich, dass der Zähler im
txbuffer korrekt ist (die Werte sind auch plausibel, z.B. ~50 [60 Hz
Timer, 6 Kanäle] nach ~5sek). Nur der zweite Zähler im adcavgbuffer
passt nicht - der ist uint32 (weil hier eigentlich eine laufende Summe
rein sollte).
Das eigentlich Problem scheint aber die Speicheradresse 0x00A0 zu
sein... wenn ich das Array im Speicher verschiebe (also vorher eine
andere Variable einfüge bzw. lösche), dann "verschiebt" sich das
Problem, und ein anderes Byte verhält sich komisch.
Da sich aber 3 Chips genau gleich verhalten, und ich in diesem Fall
(lower Byte in 0x00A0) genau eine Doppel-Zählung habe, glaube ich nicht
an einen Hardware-Defekt (dann hätte ich wohl eher zufällige Werte,
nicht immer genau mal 2).
A. K. schrieb:> Wie ist txbuffer deklariert?
Auf die Antwort bin ich auch gespannt. txbuffer müsste mindestens eine
Größe von 68 Bytes haben, sonst gibt es einen Buffer-Overflow.
Definition als unsigned char oder uint8_t wäre auch nicht schlecht. ;-)
Ich tippe auf Speicherüberlauf.
volatile uint32_t adcavgbuffer[6]; // 240 Byte (6 * 32)
txbuffer hat wohl auch ein irgendwas um 240 Byte.
Sag mal wie das definert ist.
AT Tiny 84A hat lauf google 512 Byte RAM.
Jetzt noch der Stack dazu und schon macht Dir der Stack die Speicher
kaput.
muli1329 schrieb:> wenn ich das Array im Speicher verschiebe (also vorher eine> andere Variable einfüge bzw. lösche), dann "verschiebt" sich das> Problem, und ein anderes Byte verhält sich komisch.
Dann überlagern sich Variablen, d.h. Du hast einen Zugriff auf eine
Variable (Array) außerhalb ihrer Größe und zerstörst damit nachfolgende
Variablen.
Hm.
Es ist äusserst unwahrscheinlich, das ein Hardware-Defekt vorliegt.
Soweit sind wir uns einig.
Das sich die "Problemstelle" mit der Veränderung der Adresse des Vektors
verschiebt scheint mir völlig selbstverständlich zu sein. Denn das
Problem wird sicher (meine Meinung) in der Operation liegen und nicht in
der Lage des Vektors im Speicher.
Diese beiden Punkte kannst Du also (vorläufig) ignorieren.
---
>Statt der Umwandlung im txbuffer eine Zählervariable zu verwenden,
dürfte auf das selbe rauslaufen, ...
Das sollte ja auch auf das selbe hinauslaufen :-) Ich wollte ja nicht
den eigentlichen Zweck des Programmes verändern, sonder nur ein Detail
seiner Implementierung.
Allerdings ist das doch eine ziemliche Zeitverschwendung die
Ausgabedaten bei jedem neuen Messwert neu zu bestimmen. Das alleine ist
aber im Moment nicht entscheidend, denke ich.
Du würdest aber doch relativ schnell erkennen können, ob das Problem
nun in der Verwendung verschiedener Datentypen liegt (wie ich im Moment
vermute) oder woanders.
---
Falls Du keinen Debugger hast oder ihn nicht verwenden kannst, kannst Du
auch im Simulator arbeiten. Dann brauchst Du keine Kopfstände mit
Debug-Ausgaben machen (die vielleicht auch neue Probleme herbeiführen).
Viel Erfolg
Peter D. schrieb:> muli1329 schrieb:>> wenn ich das Array im Speicher verschiebe (also vorher eine>> andere Variable einfüge bzw. lösche), dann "verschiebt" sich das>> Problem, und ein anderes Byte verhält sich komisch.>> Dann überlagern sich Variablen, d.h. Du hast einen Zugriff auf eine> Variable (Array) außerhalb ihrer Größe und zerstörst damit nachfolgende> Variablen.
Da muss ich zustimmen. Das ist mir entschlüpft.
Das deutet tatsächlich auf irgendeinen wildgewordenen Pointer oder
Index.
Wenn Du meinem Rat gefolgt wärst, dann wäre an anderer Stelle in Fehler
aufgetreten. Möglicherweise in einer ganz anderen Funktionalität des
Programms.
Peter D. schrieb:> Dann überlagern sich Variablen, d.h. Du hast einen Zugriff auf eine> Variable (Array) außerhalb ihrer Größe und zerstörst damit nachfolgende> Variablen.
Genau. Und txbuffer[] ist dafür ein geeigneter Kandidat. Der TO schrieb
ja, dass, wenn er die Zeile
1
adcintch+=1;
rausnimmt, das Programm fehlerfrei arbeitet. Kein Wunder, dann reicht
als txbuffer die Größe von 0 x 4 + 48 = 48 Bytes.
Wenn er jedoch adcintch von 0 bis 5 einschließlich laufen lässt, braucht
er 5 x 4 + 48 = 68 Bytes.
Leider hat der TO genau diese Information verschwiegen - wie so meist.
Oft liegt der Fehler im nicht-gezeigten Code.
Sorry wegen der fehlenden Array Definition... ist nicht leicht, genau
die richtige Auswahl aus dem Quell-Code zu treffen. Mir war bis eben
nicht klar, dass das txbuffer Array eine Auswirkung auf das adcavgbuffer
hat.
adcavgbuffer was uint8_t [60]... was schlicht verrechnet war, mit [70] =
6x4 + 46 laufen beide Counter jetzt wie sie sollen :-)
Ich nutze übrigend den txbuffer direkt als Zählervariable, um
entsprechend 6*2 Byte für eine dedizierte Zählervariable zu sparen (und
das umkopieren, wenn per I2C darauf zugegriffen wird).