Forum: Mikrocontroller und Digitale Elektronik AVR Problem mit uint32_t


von muli1329 (Gast)


Lesenswert?

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:
1
Device [26] ByteNr 46 = 47x
2
Device [26] ByteNr 48 = 94x

Der Code für den Timer Interrupt:
1
ISR (TIM0_COMPA_vect)
2
{
3
  adcavgbuffer[adcintch] += 1;
4
5
  adctemp16 = (txbuffer[adcintch*4+46] | (txbuffer[adcintch*4+47] << 8 )) + 1;
6
  txbuffer[adcintch*4+46] = adctemp16;
7
  txbuffer[adcintch*4+47] = adctemp16>>8;
8
9
  adcintch += 1;
10
  if (adcintch == 6) adcintch = 0;
11
}

In der Header-Datei stehen folgende Variablen Definition:
1
volatile uint8_t adcintch;
2
volatile uint32_t adcavgbuffer[6];
3
uint8_t adcpattern;

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

von Codix (Gast)


Lesenswert?

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.

von Codix (Gast)


Lesenswert?

Hier noch ein kleines Beispiel bzgl. unbenutzer IO-Register:
1
// Messung
2
ISR(TIMER1_CAPT_vect)
3
{
4
  
5
  if ( EEAR == 0){    // Signaling only if processing in main is finished!
6
              // main clears that when all is done.
7
    
8
    GPIOR0 = ICR1L;
9
    GPIOR1 = ICR1H;
10
    EEAR=1;        // signal that a new value is available
11
  } else {
12
      GPIOR0 = 0;      // Set null
13
      GPIOR1 = 0;
14
    }
15
  
16
    TCNT1 = 0;
17
    
18
19
}
20
///// Zusammenbauen (hier nur 16 Bit)
21
              temp = GPIOR1;    // High value
22
              temp <<=8;
23
              temp |= GPIOR0;    // Low
24
              
25
              summe += (uint32_t)temp;

von muli1329 (Gast)


Lesenswert?

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).

von Nop (Gast)


Lesenswert?

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.

von Hansi (Gast)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

Wie ist txbuffer deklariert?

von muli1329 (Gast)


Lesenswert?

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).

von Thomas E. (thomase)


Lesenswert?

muli1329 schrieb:
> Mit dem aktuellen Code würde man ja erwarten

Das erwartest du. Ich erwarte, daß dein Speicher überläuft.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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. ;-)

von NurEinGast (Gast)


Lesenswert?

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.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

NurEinGast schrieb:
> volatile uint32_t adcavgbuffer[6];   //  240 Byte (6 * 32)

Unsinn. 6 * 4 = 24 Bytes.

von Peter D. (peda)


Lesenswert?

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.

von Hansi (Gast)


Lesenswert?

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

von NurEinGast (Gast)


Lesenswert?

Ups - das mit dem Rechnen üben wir noch. Sorry

von Hansi (Gast)


Lesenswert?

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.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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.

: Bearbeitet durch Moderator
von muli1329 (Gast)


Lesenswert?

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).

von muli1329 (Gast)


Lesenswert?

Nochmals vielen Dank für den Denkanstoß! :-)

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.