mikrocontroller.net

Forum: Compiler & IDEs static char *pointer;


Autor: gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich sehe gerade den Wald vor lauter Bäume nicht mehr.

Warum ist es ein riesen Unterschied ob ich

volatile char *pointer;

meine_ISR()
{
    bla bla bla;
}

oder aber

meine_ISR()
{
    static char *pointer;
    bla bla bla;
}


schreibe???
Im zweiten Fall stürzt der µC nach einer Weile ab.
Oder ist das eigentlich egal und der Fehler muss doch woanders liegen?

Autor: gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
im oberen beispiel ist dein pointer global und kann überall im programm 
benutzt werden im unteren legst du ihn lokal an und kann im restlichen 
programm nicht genutzt werden.

Autor: gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich möchte ihn auch nur lokal nutzen.
Nur wie gesagt, der µC stürzt nach ein paar Durchläufen ab.
Möglicherweise ist das untere Beispiel ja irgendwie Resourcenfressend 
oder sowas???
Das obere Beispiel läuft perfekt.

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hast du pointer zweimal? Der lokale ist dann ein anderer als der 
globale.

Johann

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
gast schrieb:
> Ich möchte ihn auch nur lokal nutzen.
> Nur wie gesagt, der µC stürzt nach ein paar Durchläufen ab.
> Möglicherweise ist das untere Beispiel ja irgendwie Resourcenfressend
> oder sowas???

Nein, ist es nicht.

> Das obere Beispiel läuft perfekt.

Mit Testen kann man immer nur das Vorhandensein von Fehlern 
heruasfinden, nicht das Fehlen.

Soll heissen: Weil es im Moment so aussieht, als ob alles funktioniert, 
heisst das noch lange nicht, dass keine Fehler enthalten sind.

Deine 2-te Version hat ein etwas anderes Memory-Layout. Das kann schon 
ausreichen, dass sich in einem Fall ein (anderer) Fehler zeigt, im 
anderen Fall aber nicht zeigt.

Autor: gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke, sehr gut geschrieben.
Mist, ich hab es ja geahnt :-(

Ok, dann hole ich mal weiter aus.

LPC2138, WinArm, wir befinden uns in der SPI1_ISR;

Dort drin ist eine Statemachine, die den Empfang regelt. Startbyte, 
Prüfsumme etc...
Recht komplex, daher hier nur mal gekürzt/schematisch dargestellt.
In case 0 wird die Adresse vom daten-struct übernommen (wenn Startbyte 
erkannt wurde).
Im nächsten Durchlauf dann werden die Empfangsbytes in mein Struct 
geschrieben.
Wenn ich die Zeile "*rx_buffer++ = rx_byte;" auskommentiere stürzt der 
µC auch nicht ab.
Er schreibt aber auch scheinbar an die richtige Adresse, denn die paar 
Durchläufe die funktionieren, sind die Daten später korrekt im 
daten-struct enthalten.

Ich habe auch schon

rx_buffer_end = (U8*)(&daten + sizeof(daten_t));

gegen

rx_buffer_end = (U8*)(&daten + 2); getauscht, dass er auf keinen Fall zu 
weit schreibt.
Stürzt trotzdem irgendwann ab.

Spurious-Interrupt kann ich auch ausschließen, der ist definiert und 
wird nicht angesprungen.

Wenn ich hingegen anstelle von

static U8 *rx_buffer, *rx_buffer_end;

innerhalb der ISR,

volatile U8 *rx_buffer, *rx_buffer_end;

außerhalb der ISR deklariere, dann rennt alles super…

Da ich damit aber nicht weiß, ob der Fehler damit wirklich behoben ist, 
habe ich dabei ein ungutes Gefühl :-(


void __attribute__ ((interrupt("IRQ"))) spi1_int(void)
{

  static U8 rx_state = 0;
  static U8 *rx_buffer, *rx_buffer_end;
  
  U8 rx_byte;
  
  rx_byte = SSPDR;

  switch(rx_state)
  {

    case 0:
      if(rx_byte) == 0x55;
      {
        rx_state = 1;
        rx_buffer = (U8*)&daten;
        rx_buffer_end = (U8*)(&daten + sizeof(daten_t));
      }
    break;
    
    case 1:
      if(rx_buffer < rx_buffer_end)
      {
        *rx_buffer++ = rx_byte; // hier vermute ich den Fehler
      }
      else
        rx_state = 2;
    break;
    
    case 2:
      // hier wird noch einiges geprüft und verarbeitet
      rx_state = 0;
    break;

  }

}

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
gast schrieb:

> Recht komplex, daher hier nur mal gekürzt/schematisch dargestellt.

Schon mal schlecht.
Da dein Fehler, so es einen gibt, überall stecken kann, reicht eine 
schamtische Darstellung nicht.


>     case 0:
>       if(rx_byte) == 0x55;
>       {
>         rx_state = 1;
>         rx_buffer = (U8*)&daten;
>         rx_buffer_end = (U8*)(&daten + sizeof(daten_t));

Wie ist 'daten' definiert?
Das daten_t da hinten ist seltsam. Normal findet man an sochen Stellen

          rx_buffer_end = (U8*)(daten + sizeof(daten));

und daten ist irgend ein Array.
Könnte bei dir aber auch ein struct sein. Dann aber eher so

          rx_buffer_end = (U8*)(&daten + sizeof(*daten));

>     case 1:
>       if(rx_buffer < rx_buffer_end)

Der Fall wird hoffentlich nie auftreten bzw. erst dann, wenn das 
Datenpaket wirklich vollständig ist.

>       {
>         *rx_buffer++ = rx_byte; // hier vermute ich den Fehler

Der einzige Fehler, der hier möglich ist, ist der, dass rx_buffer 
irgendwo in den Speicher zeigt. Der Fall ist aber so gut wie 
ausgeschlossen, wenn rc_buffer im restlichen, nicht gezeigten, Code 
nicht verändert wird oder über einen anderen Out-Of-Bounds Zugriff 
irrtümlich verändert wird.

Denk immer drann: Was du siehst sind nur die Symptome. Dein eigentliches 
Problem kann auch ganz woanders sitzen.

Autor: Werner B. (werner-b)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Was mir auffällt...

1.
*rx_buffer und *rx_buffer_end sind beim Programmstart nicht 
initialisiert.
Du geht offenbar davon aus dass das erste empfangene Zeichen ein 0x55 
ist.
Das ist sehr optimistisch.

2. Davon ausgehen dass 'daten' ein Array eines beliebigen Typs ist
(typ daten[array_length]).

2a. Offenbar muss dort stehen
rx_buffer_end = ((U8*)daten) + sizeof(daten);

denn
2b. Da wir den Typ von daten nicht kennen, muss vor dem Berechnen der 
Endaddresse der Datentyp auf U8* gecastet werden.
 Für den Typ U8 und U16 von daten ergibt  (U8*)(&daten + sizeof(daten)); 
jeweils etwas anderes, denn
  daten + sizeof(daten) == &daten[sizeof(daten)]

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Werner B. schrieb:

> *rx_buffer und *rx_buffer_end sind beim Programmstart nicht
> initialisiert.

Doch, sind sie, mit 0.

> Du geht offenbar davon aus dass das erste empfangene Zeichen ein 0x55
> ist.
> Das ist sehr optimistisch.

Warum? Wenn das erste Zeichen nicht 0x55 ist, schadet es doch nicht.
Er wartet doch einfach nur auf das nächste 0x55.

Autor: Arne (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Werner B. schrieb:

> *rx_buffer und *rx_buffer_end sind beim Programmstart nicht
> initialisiert.

Alle uninitialisierten static Daten landen doch im BSS. Und das muss der 
Startcode ausnullen.

Autor: Werner B. (werner-b)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Stefan Ernst und Arne,

ihr habt mich erwischt 8-|

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ich dachte auch erst, es würde nicht initialisiert, und hätte es fast
geschrieben.
Der Einschlag war knapp daneben :-)

Autor: gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke für eure Beteiligung an der Problemlösung.

'daten' ist ein Struct.

Global definiert.

In meiner main.h steht:
typedef struct
{
  U32 dummy1;
  U32 dummy2;
  U32 dummy3;
  U32 dummy4;
  U8 test[256];
} daten_t;

extern daten_t daten;

und in der main.c noch mal
daten_t daten;

Ich komme jetzt ins Schleudern,
rx_buffer_end = (U8*)(&daten + sizeof(daten_t));

oder
rx_buffer_end = (U8*)(&daten) + sizeof(daten_t);

was ist nun richtiger? :-)

Des Weiteren ist mir noch folgendes aufgefallen: Wenn ich innerhalb 
einer beliebigen ISR eine Variable als volatile deklariere, dann ist der 
Hang des µC zum Absturz viel größer.
Aber nur wenn sie wirklich innerhalb als volatile deklariert und 
verwendet wird. Global deklarieren und in der ISR verwenden ist kein 
Problem.
Ich vermute ja, dass das einfach wieder mit verändertem Speicherlayout 
zu tun hat, aber ich frag zur Sicherheit trotzdem mal.
Man darf doch in einer ISR eine Variable als volatile deklarieren? Oder 
ist das eine goldene „no go Regel“ die ich nicht kenne? :-)

Ich arbeite mich gerade in das Thema debuggen mit JTAG ein. Wie könnte 
man da am besten vorgehen um solche fiesen Fehler zu finden?

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
gast schrieb:

> Ich komme jetzt ins Schleudern,
>
> rx_buffer_end = (U8*)(&daten + sizeof(daten_t));
> 
>
> oder
>
>
> rx_buffer_end = (U8*)(&daten) + sizeof(daten_t);
> 
>
> was ist nun richtiger? :-)

Da haben wir alle bisher geschlafen.
  rx_buffer_end = (U8*)(&daten) + sizeof(daten_t);

ist korrekt.

sizeof liefert die Länge in Bytes. Pointer Arithmetik ist aber so 
definiert, dass der Compiler bei einer Addition immer automatisch mit 
der Datentyplänge des Datentyps auf den der Pointer zeigt, implizit 
multipliziert.

Du hättest daher auch
  rx_buffer_end = (U8*)(&daten + 1);

schreiben können, wäre aufs gleiche rausgelaufen (und ist IMHO sogar die 
bessere Lösung).

-> Dein bisheriger End-Pointer zeigt irgendwo in den Speicher, nur nicht 
auf das Ende der Struktur. (Das ist mit ein Grund warum ich solche 
"Empfange bis irgendwelche sizeof Bytes" Programmstrukturen nicht mag. 
Der andere Grund ist der, dass ich mich damit einem eventuellen Padding 
des Compilers ausliefere, bzw. darauf achten muss. Ganz schlimm wirds 
dann, wenn Sender und Empfänger unterschiedliche Vorstellungen vom 
Padding haben und keiner die alles entscheidende Compilerswitches 
findet, wie man das Padding für diese eine Struktur abdrehen kann)

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Karl heinz Buchegger schrieb:

> Du hättest daher auch
>
>
>   rx_buffer_end = (U8*)(&daten + 1);
> 
>
> schreiben können,

Äquivalent ist
rx_buffer_end = (U8*) &daten[1];

Ja, ich halte das auch für besser als die sizeof-Lösung.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jörg Wunsch schrieb:

> Äquivalent ist
>
>
rx_buffer_end = (U8*) &daten[1];

LOL.
Sieht allerdings etwas pervers aus
  struct irgendwas
  {
    ...
  } daten;

  ...

  &daten[1];

Aber keine Frage: ist zulässig
(Dank des 'flexiblen' Arraygedankens in C)

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sorry, so genau hatte ich mir den Code dann doch nicht angeguckt.
Nee, wenn das kein Array ist, geht das natürlich nicht so (``subscripted
value is neither array nor pointer''), dann geht nur deine Variante.

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
mit einem Klammerpaar mehr geht das schon in Ordnung:
   rx_buffer_end = (U8*)(&daten)[1];

Nachtrag: das war Unfug; so stimmt es:
   rx_buffer_end = (U8*)&(&daten)[1];

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jörg Wunsch schrieb:
> Sorry, so genau hatte ich mir den Code dann doch nicht angeguckt.
> Nee, wenn das kein Array ist, geht das natürlich nicht so (``subscripted
> value is neither array nor pointer''),

Ja, da hab ich zu kurz gedacht. Klaus hat aufgepasst. Ich hab mich nur 
auf die Index-Poiner_Addition Äquivalenz konzentriert.

rx_buffer_end = (U8*)&(&daten)[1];
schmack. Das hat doch was :-)
Das ist ein klassischer Arbeitsplatzsicherer :-)
Gleiche Liga, wie
  for( int i = 0; i < 5; ++i )
    char c = i["Hallo World"];

Autor: gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wow, hüstel, grübel...
Ich werde das mal alles probieren.

Kann noch jemand was hierzu sagen? Denn das tritt unabhängig vom 
derzeitigen Problem auf.
Des Weiteren ist mir noch folgendes aufgefallen: Wenn ich INNERHALB
einer beliebigen ISR eine Variable als volatile deklariere, dann ist der
Hang des µC zum Absturz viel größer.
Aber nur wenn sie wirklich innerhalb als volatile deklariert und
verwendet wird.
Global deklarieren und in der ISR verwenden ist kein
Problem.
Ich vermute ja, dass das einfach wieder mit verändertem Speicherlayout
zu tun hat, aber ich frag zur Sicherheit trotzdem mal.

Man darf doch in einer ISR eine Variable als volatile deklarieren? Oder
ist das eine goldene „no go Regel“ die ich nicht kenne? :-)

und
Ich arbeite mich gerade in das Thema debuggen mit JTAG ein. Wie könnte
man da am besten vorgehen um solche fiesen Fehler zu finden? Stack überwachen etc?

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
volatile ist halt eine ,,Pessimierung'', d. h. deine ISR wird dadurch
dann langsamer.  Überholen sich dann eventuell zwei Interrupts?

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
gast schrieb:

> Des Weiteren ist mir noch folgendes aufgefallen: Wenn ich INNERHALB
> einer beliebigen ISR eine Variable als volatile deklariere, dann ist der
> Hang des µC zum Absturz viel größer.
> Aber nur wenn sie wirklich innerhalb als volatile deklariert und
> verwendet wird.
> Global deklarieren und in der ISR verwenden ist kein
> Problem.
> Ich vermute ja, dass das einfach wieder mit verändertem Speicherlayout
> zu tun hat, aber ich frag zur Sicherheit trotzdem mal.

Es klingt zumindest stark danach.

> Man darf doch in einer ISR eine Variable als volatile deklarieren? Oder
> ist das eine goldene „no go Regel“ die ich nicht kenne? :-)

Nein.
Du kannst alles volatile machen, wenn du willst. Du hebelst nur so gut 
wie sämtliche Optimierungsbemühungen des Compilers damit aus. :-)

>
> Ich arbeite mich gerade in das Thema debuggen mit JTAG ein. Wie könnte
> man da am besten vorgehen um solche fiesen Fehler zu finden? Stack
> überwachen etc?
> 

Das hängt schwer vom Debugger ab.
Solche Fehler sind wahnsinnig schwer zu finden, weil jede kleine 
Veränderung des Programms den Fehler an eine andere Stelle verschieben 
kann.

Ausgangspunkt ist immer: das Problem reproduzierbar machen. Das heist du 
brauchst eine exakte Abfolge von Input-Schritten, die du in das Programm 
einspeist und die zuverlässig das Problem zum Vorschein bringen. Solange 
du das nicht hast, ist alles andere ein Stochern im Nebel.

Angenommen, du hast das.
Dann crasht dein Pgm irgendwo. Jetzt heist es rausfinden warum es das 
tut. Wahrscheinlich weil ein Pointer oder irgendeine sonstige Variable 
urplötzlich auf einem Wert steht, den sie nicht haben sollte. Und das 
fiese daran: Du setzt den Wert gar nicht. Laut deinem Programm passiert 
eigentlich ganz was anderes, und nachdem eine Sequenz abgelaufen ist, 
hat eine Variable wie von Geisterhand ihren Wert gewechselt (natürlich 
gibts dafür eine Erklärung, aber die versteht man erst dann, wenn man 
die tatsächliche Problemstelle identifiziert hat. Das ist der fiese Teil 
daran)

Jetzt musst du diese Variable im Speicher suchen und wenn dein Debugger 
dir das erlaubt einen Watch-Point auf die Speicherzelle setzen. Wichtig: 
Nicht auf die Variable! Du musst direkt im Speicher das Byte überwachen.

Und dann geht das Spielchen wieder von vorne los. Irgendwann wirst du 
eine Codezeile identifizieren, die mit deiner Variablen nicht das 
geringste zu tun hat und trotzdem ändert sie genau den Bytewert im 
Speicher. Meistens ist die betreffende Codezeile dann irgendein Pointer, 
der genau auf diese Speicherzelle zeigt und eigentlich woanders 
hinzeigen sollte. Dann heist es herausfinden warum dem so ist. Und so 
hantelt man sich vom Symptom zur Ursache durch.
Und wenn man dann die Ursache kennt, ist alles plötzlich sonnenklar.


Wie sowas zb im Code aussehen könnte

int main()
{
  int i = 0;
  int k[4];
  int m = 0;

  k[4] = 8;
}

Nach der Zuweisung an k[4] stehen die Chancen nicht schlecht, dass 
entweder i oder m nicht mehr den Wert 0 hat. Wenn nachfolgender Code 
aber zb darauf abfrägt und bei nicht 0 einen Motor startet, der bei 0 
nicht gestartet hätte werden dürfte, dann hast du ein mächtiges Problem

Veränderst du aber das Speicherlayout ein klein wenig

int main()
{
  int i = 0;
  int j = 0;
  int k[4];
  int l = 0;
  int m = 0;

  k[4] = 8;
}


dann behalten m (oder i) ihren Wert, die Abfrage auf 0 stimmt wieder und 
alles geht gut. Das Problem ist nicht, dass mit m (oder i) irgendetwas 
nicht stimmt (das ist nur das Symptom), sondern dass der Zugriff k[4] 
ausserhalb des Arrays ist (= die Ursache). Du siehst aber nur, dass sich 
der Motor dreht obwohl er das nicht sollte und suchst dir die Augen 
wund, wo du denn etwas seltsames mit m (oder i) anstellst.

Autor: Peter (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi,

ich würde den kram mit dem rx_buffer_end ganz weglassen und stattdessen 
eine einfaches int mit der länge benutzen und runterzählen.

also in case 0: remain=sizeof(daten)
     in case 1: if(remain-->0) ...

falls du unbedingt den endezeiger so lassen willst würde ich zumindest 
das < durch ein != ersetzen um ein evtl. warp-around zu vermeiden (gab 
es zumindest auf dem z80)

Peter

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Peter schrieb:
> Hi,
>
> ich würde den kram mit dem rx_buffer_end ganz weglassen und stattdessen
> eine einfaches int mit der länge benutzen und runterzählen.

Yep.
Die Länge würde ich mir allerdings vom Sender geben lassen (wenn ich 
Einfluss auf das Protokoll habe) und nicht blindlings annehmen, dass das 
schon passen wird. Sowas hat sich in der Vergangeheit bei Erweiterungen 
immer wieder als Boomerang erwiesen.

Autor: hydravliska (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
case 0:
      if(rx_byte) == 0x55;
     ...

Ist das ein Tippfehler oder ich verstehe der Syntax nicht... Ich glaube 
dass die "if abfrage" so nicht funktionieren wuerde? Wenn "rx_byte" = 1 
klappt, wenn 0 dann nicht. Das sollte aber gegen 0x55 verglichen werden, 
oder?

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
hydravliska schrieb:
> case 0:
>       if(rx_byte) == 0x55;
>      ...
>
> Ist das ein Tippfehler

Das ist ein Tippfehler. Das wär so nie durch den Compiler gegangen.
Sowas kommt vor, wenn nicht Originalcode gepostet wird, sondern der Code 
fürs Forum noch mal umformatiert wird.

Und ich hoffe auch, dass der ; da am Zeilenende nur ein Tippfehler ist. 
Den nämlich würde der Compiler akzeptieren.

Autor: Hans-jürgen Herbert (hjherbert) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
struct daten_t  {
  char x[8] ;
  } ;

struct daten_t daten ;



        rx_buffer_end = (U8*)(&daten + sizeof(daten_t));
= 64 mehr als rx_buffer
ohne fehler, weil
daten = eine struct
&daten = Pointer auf diese struct

So wie dieses einfachere Beispiel zeigt:
int *p ;
int *n ;
n = p+1 ;

durch das +1 wird dann sizeof(int) hinzuzählt zum Pointer.

Richtig wären die folgenden Variationen:

        rx_buffer_end = ((U8*)(&daten)) + sizeof(daten_t);

        rx_buffer_end = ((U8*)(&daten)) + 8;

        rx_buffer_end = (U8*)(&daten + 1);

        rx_buffer_end = (U8*)(&(&daten)[1]);

Autor: Gast (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

vielen Dank für die zahlreichen Tipps. Mein Problem scheint jedoch viel 
tiefer zu liegen.
Ich habe mein Projekt auf ein kleines simples Beispiel für ein Olimex 
Board (LPC-P2148) runterbrechen können und komme aus dem Staunen nicht 
mehr raus.

Das angehängte WinArm Projekt soll eigentlich folgendes machen:

- Timer 1 wird initialisiert
- nach einer kurzen Wartezeit wird in eine Endlosschleife gesprungen in 
der "main_update_flag" abgefragt wird
- ist "main_update_flag" = "true" dann toggle "PAD1" und setze 
"main_update_flag" auf "0"
- im Timerinterrupt wird "timer_counter" um "1" erhöht und "PAD2" 
getoggled
- wenn "timer_counter" glatt durch 20 teilbar ist, dann wird 
"main_update_flag" auf "1" gesetzt

PAD1 entspricht auf einem Olimex 2148 Board LED1 und PAD2 = LED2

Eigentlich keine große Sache sollte man meinen. Mein Miniprojekt benimmt 
sich da aber etwas störrisch.
Es gibt viele kleine Dinge die man tun kann, damit das Programm 
scheinbar läuft. Doch diese erklären nicht wirklich den Fehler.
Beschränken wir uns auf folgendes Phänomen.

Lasse ich "timer_counter" auf teilbar durch 20 checken also:
if(!(timer_counter % 20))

stolpert das Programm nur so rum. LED1 blinkt total unregelmäßig.

Lasse ich "timer_counter" auf teilbar durch 32 checken also:
if(!(timer_counter % 32))

läuft das Programm scheinbar.

GCC 4.1.1 mit Optimierung s, ohne Optmierung gibts keinen Fehler.

Findet einer den Fehler? Was ist da los?

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Gast schrieb:

> Lasse ich "timer_counter" auf teilbar durch 20 checken also:
>
>
if(!(timer_counter % 20))
>
> stolpert das Programm nur so rum. LED1 blinkt total unregelmäßig.
>
> Lasse ich "timer_counter" auf teilbar durch 32 checken also:
>
>
if(!(timer_counter % 32))
>
> läuft das Programm scheinbar.

Hab' mir das Programm angeguckt, nur so als Idee: der Modulus mit
32 wird durch den Compiler durch eine Bitmaskierung ersetzt, während
der durch 20 die vergleichsweise langsame Divisionsroutine aufrufen
muss.

Autor: Gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ja, das scheint in der Tat damit etwas zu tun zu haben. Denn mit 16 
geht's dann auch wieder. Aber Schein trügt denke ich.
Das bisschen Rechnerei sollte doch den guten guten nicht außer Tritt 
bringen.
Er hat doch jede Menge Zeit. Der nächste Interrupt kommt doch erst nach 
50ms.
Ich hatte in meinen inzwischen nicht mehr zählbaren Test's auch 
Versionen, in denen sich der Prozessor in der Art weghängt, dass ISR gar 
nicht mehr angesprungen wird.
Wenn ich timer_counter nicht mehr volatile hab und die ISR wie folgt 
umbaue:
void __attribute__ ((interrupt("IRQ"))) tc1_isr(void)
{
  T1IR = (1<<0);
  PAD2_ON;
  
  volatile unsigned char test;
  timer_counter++;
  test++;


  if(!(timer_counter % 32)) // 32 = i.O.  20 = seltsam
  {
    main_update_flag=1;
  }

  PAD2_OFF;
  VICVectAddr = 0;

}

Dann kommt er auch mit 32 aus dem Tritt. LED1 blinkt SEHR unregelmäßig.

Wie  Karl heinz Buchegger vermute ich, der Fehler liegt an ganz anderer 
Stelle und äußert sich durch Codeverschiebung nur jedes Mal anders.

Nur ist in diesem Beispiel ja kaum noch Platz für Fehler.
Keine Pointer keine Arrays, nichts!

Autor: Marcus Harnisch (mharnisch) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jörg Wunsch schrieb:
> Hab' mir das Programm angeguckt, nur so als Idee: der Modulus mit
> 32 wird durch den Compiler durch eine Bitmaskierung ersetzt, während
> der durch 20 die vergleichsweise langsame Divisionsroutine aufrufen
> muss.

Bei höheren Optimierungen wird "% 20" auf dem ARM7 übrigens sogar vom 
GCC in eine sehr effiziente Inline Sequenz übersetzt. Zugegeben, immer 
noch langsamer als das Maskieren von Bits.

Wie wäre es mit einem Zähler der überhaupt nur bis 20/32/etc. zählt. Das 
ist in jedem Fall schnell und unabhängig vom tatsächlichen Divisor.

Gruß
Marcus
http://www.doulos.com/arm/

Autor: Gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Marcus Harnisch schrieb:
> Wie wäre es mit einem Zähler der überhaupt nur bis 20/32/etc. zählt. Das
> ist in jedem Fall schnell und unabhängig vom tatsächlichen Divisor.

Dann hätte ich ja nur die Symptome beseitigt, nicht aber den Fehler, der 
mir dann später sicher wieder auf den Fuß fällt.

Autor: Marcus Harnisch (mharnisch) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Gast schrieb:
> Dann hätte ich ja nur die Symptome beseitigt, nicht aber den Fehler, der
> mir dann später sicher wieder auf den Fuß fällt.

Wenn Du dir da so sicher bist... Diese Änderung ist in jedem Fall 
sinnvoll und würde Deine Theorie bestätigen, dass der Fehler woanders 
liegt.

Lass doch einfach mal die LED aus tcl_isr heraus blinken. Dort wo Du 
main_update_flag auf 1 setzt. Wenn das genauso flackert, ist es mit 
Sicherheit keine race condition bei der Kommunikation über die Variable.
Dann kann es eigentlich nur noch die Division/Modulo sein.

Mach Dir mal das Vergnügen und sieh Dir die Routine __aeabi_uidivmod 
(und von dieser aufgerufene Funktionen) in libgcc.a an.

Interessanterweise löst GCC (CodeSourcery, 4.3.3) bei -O0(!) die 
Division in eine sehr effiziente Routine auf, in der Art wie ich es 
weiter oben beschrieben hatte. Bei -Os hingegen wird die langsame 
Hilfsfunktion __aeabi_uidivmod bemüht.

Gruß
Marcus
http://www.doulos.com/arm/

Autor: Gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sicher bin ich mir inzwischen über gar nichts mehr :-)
Aaaaaber, wenn ich z.B. die Zeile:

test++;

aus der ISR entferne. Dann togglen die LEDs wie gewünscht.
Die Variable test hat jedoch im Codeverlauf rein gar nichts mit den LEDs 
zu tun. Sie ist nämlich eigentlich ungenutzt.
Daher meine Annahme, das der Fehler nur durch Codeverschiebung auftritt 
oder eben nicht zu sehen ist.

Tja, und an der Stelle bleibt fast nur noch die Vermutung, dass der GCC 
4.1.1 hier selber die Fehlerursache darstellt oder?
Mit GCC 4.3.3 geht es übrigens, habe ich inzwischen getestet.

Autor: Marcus Harnisch (mharnisch) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Theoretisch denkbar wäre natürlich immer noch, dass der GCC 4.3.3 durch 
andere Optimierung eine race condition (die ich allerdings nicht 
erkennen kann) verdeckt. Es empfiehlt sich, einen Blick auf den 
erzeugten Code zu werfen, und diesen nachzuvollziehen.

Einstweilen hier kann ich mich nur selbst wiederholen:

GCC & ARM => CodeSourcery

Während der Zeit in der ich aktiv an diesem Forum teilgenommen habe, 
haben wir schon einige Merkwürdigkeiten auf GCC zurückführen können. In 
keinem Fall konnte das mit dem CodeSourcery GCC reproduziert werden.

Ich habe im Übrigen keine Anteile an dieser Firma und verwende GCC 
ohnehin selten im Zusammenhang mit ARM.

Gruß
Marcus
http://www.doulos.com/arm/

Autor: Gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke für den Tipp! Werde ich sicher mal mit der Evaluation Version 
durchspielen.
Was ich eben noch getestet habe, wenn ich nicht die Ports P0:10 und 
P0:11 sondern die Ports P1:16 und P1:17 verwende und dann mit dem Oszi 
drauf schaue, dann geht's auch...

So macht das doch keinen Spaß... /&%(/&"%§ :-)

Jemand einen Tipp was das nun am Ende sein kann?

Autor: Mark Brandis (markbrandis)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Karl heinz Buchegger schrieb:
> Das ist ein klassischer Arbeitsplatzsicherer :-)
> Gleiche Liga, wie
>
>   for( int i = 0; i < 5; ++i )
>     char c = i["Hallo World"];
> 

test.c: In function `main':
test.c:7: error: syntax error before "char"

**nur mal so anmerk**

Autor: Gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Gast schrieb:
> Was ich eben noch getestet habe, wenn ich nicht die Ports P0:10 und
> P0:11 sondern die Ports P1:16 und P1:17 verwende und dann mit dem Oszi
> drauf schaue, dann geht's auch...

Hab mich geirrt, auch mit P1 geht es nicht wirklich. Hatte nur die 
Definition von PAD2_FLASH etwas umgestellt und da ist wohl wieder nur 
Code verschoben worden.
Derzeit wundere ich mich wie das eigentliche Projekt in dem mir das 
aufgefallen ist, und welches doch um einiges grösser war, bislang so 
störungsfrei lief...
Nun ist jedenfalls das Vertrauen erst mal weg.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.