Forum: Compiler & IDEs Globale Variable wird im ISR nicht hochgezählt??


von Georg T. (microschorsch)


Lesenswert?

Hallo zusammen,

merkwürdiges Problem. Ich habe meinen Source-Code aufgeräumt und jetzt 
mehrere Source-Files gemacht, wo ich früher nur einen hatte - war 
dringend erforderlich. Auffällig ist, das meine UART-Routine nicht mehr 
läuft, ich habe versucht das Problem zu simplifizieren:
1
ISR(USART0_RX_vect) {
2
  unsigned char buffer;
3
  //Reading data from UDR0
4
  buffer = UDR0;
5
  //enable this in order to pass back the character written in terminal
6
  uart_putc(buffer); 
7
  char out[16];
8
  sprintf(out,"%d \n\r",globalCounter);
9
  uart_puts(out);
10
  globalCounter++;
11
12
[...]
13
}

Ausgabe ist dann
1
H0
2
A0
3
L0
4
L0
5
O0

globalCounter ist im Header definiert. Es spielt keine Rolle, ob es im 
Header oder im Sourcefile definiere oder deklariere es bleibt immer 0.

Warum?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Georg T. schrieb:
> globalCounter ist im Header definiert

volatile?

In einem Header sollte man übrigens immer nur deklarieren, nie
definieren.  Also:

Header:
1
extern volatile uint16_t globalCounter;

In einer C-Datei dann:
1
#include "Header"
2
3
volatile uint16_t globalCounter;

: Bearbeitet durch Moderator
von Oliver (Gast)


Lesenswert?

Standardantwort 1: volatile

Wie und wo genau definierst und deklarierst du denn?

Oliver

von Georg T. (microschorsch)


Lesenswert?

Ich hab jetzt alle möglichen Varianten durch.

volatile hatte ich auch schon probiert. Wenn ich es so mache, wie Jörg 
es vorschlägt gehts ebenfalls nicht

Danke

Schorsch

von Karl H. (kbuchegg)


Lesenswert?

Wer oder was vergreift sich sonst noch an globalCounter?

zb. ein einziges, bei der Editoroperation verloren gegangenes '=' kann 
zb den Effekt erklären
1
...
2
3
  while( 1 ) {
4
    if( globalCounter = 0 )
5
      ....

da wo vorher ein Vergleich stand, steht jetzt eine Zuweisung.

Genau das ist das Problem mit globalen Variablen und warum sie ab 
Desktop-Systemen aufwärts verpöhnt sind. Zu Überblicken, wo und warum 
sich welcher Code an welcher globalen Variablen vergreift wird mit 
wachsender Codegröße immer schwieriger. Und zwar exponentiell 
schwieriger.

Daher auch der Rat:
Wenn du dein Programm in Module aufteilst, dann ist das super. Aber mach 
es das nächste mal gleich von Anfang an und nicht erst ganz zum Schluss. 
(Aus demselben Grund sind auch Aussagen wie "Erst mal muss es 
funktionieren, dann kümmere ich mich darum, dass der Code schön wird" 
Unsinn. Schöner Code hat einen Nebeneffekt: Er hilft Fehler zu 
vermeiden)

von Georg T. (microschorsch)


Lesenswert?

Hallo,
Ich habe globalCounter neu angelegt, um dieses Problem zu verstehen, ich 
bin mir 100%ig sicher, dass es sonst nirgens verwendet wird. Es scheint 
fast so als würde er auf dem Stack die Variable neu anlegen.

Was mir noch aufgefallen ist:
Als alles in einem source-code lag, musste ich "sei()" nur einmal am 
Anfang von main() aufrufen. Jetzt musste ich es nochmal in die 
uart_init() routine schreiben, sonst sprint die ISR nicht an.... spielt 
hier der Linker irgenwie nicht mit???

Schorsch

von Karl H. (kbuchegg)


Lesenswert?

Georg T. schrieb:
> Hallo,
> Ich habe globalCounter neu angelegt, um dieses Problem zu verstehen, ich
> bin mir 100%ig sicher, dass es sonst nirgens verwendet wird. Es scheint
> fast so als würde er auf dem Stack die Variable neu anlegen.

Dann zeig den Code.
Und zwar den richtigen Code. Nicht irgendwas, was du für Forum zurecht 
gemacht hast, sondern den Code, der tatsächlich auf dem µC läuft.

> Als alles in einem source-code lag, musste ich "sei()" nur einmal am
> Anfang von main() aufrufen. Jetzt musste ich es nochmal in die
> uart_init() routine schreiben, sonst sprint die ISR nicht an.... spielt
> hier der Linker irgenwie nicht mit???

Deine Symptome werden immer seltsamer.

Um es klar zu sagen:
Das Problem ist mit 99.9% Sicherheit in deinem Code und/oder deinen 
Projektparametern zu finden. Dem Compiler/Linker die Schuld in die 
Schuhe schieben zu wollen, ist zwar verständlich, bringt dich aber mit 
99.9% Sicherheit nicht weiter. Das Problem sitzt praktisch immer vor dem 
Bildschirm und nur ganz, ganz selten im Compiler/Linker. Ehe man in die 
Richtung Compiler/Linker weiterforscht, ist man immer gut beraten, erst 
mal mit der Hypothese "Problem im Code" zu arbeiten.

: Bearbeitet durch User
von Georg T. (microschorsch)


Angehängte Dateien:

Lesenswert?

Hallo,

ich glaube, ich bin gerade ein großes Stück weiter gekommen. Ich habe 
versucht, den Code zu minimalisieren, um ihn Euch besser zuschicken zu 
können.... und auf einmals gings....


ich konnte das alles auf eine einzige Routine extmem_init() 
zurückverfolgen, lasse ich sie weg, gehts. Warum???

Anbei der geforderte Code

Danke für Eure Hilfe

von Mario (Gast)


Lesenswert?

Georg T. schrieb:
> ISR(USART0_RX_vect) {
>   unsigned char buffer;
>   //Reading data from UDR0
>   buffer = UDR0;
>   //enable this in order to pass back the character written in terminal
>   uart_putc(buffer);
>   char out[16];
>   sprintf(out,"%d \n\r",globalCounter);
>   uart_puts(out);
>   globalCounter++;
>
> [...]
> }

Es ist keine Gute Idee die serielle Ausgabe (uart_putc) und 
Stringfunktionen (sprintf) in der ISR zu implementieren. Vermutlich 
gibts du hintereinanderweg "Hallo" in der Konsole ein, dann schlägt der 
Receive ISR sehr kurz hintereinander zu wer weiß was dann passiert...
Versuche mal die Ausgabe in eine Funktion auszulagern und in der ISR nur 
ein flag zu setzen welches in der Hauptschleife abgefragt wird. Mit 
gesetztem flag wird dann die Funktion aufgerufen und das flag 
zurückgesetzt.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Georg T. schrieb:

> ich konnte das alles auf eine einzige Routine extmem_init()
> zurückverfolgen, lasse ich sie weg, gehts. Warum???

Weil pBank_1 zu 0 initialisiert wird, denn es ist eine globale Variable. 
In extmem_init schreibst du dann 0x8000 an Adresse 0. Was soll das?

Insbesondere setzt du R1 (zero_reg) auf 0x80.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

In deiner uart.h sind ein paar Dinge, die da nicht hingehören.

* die globalen Variablen
* die ISR

deine uart.h sollte so aussehen
1
#ifndef UART_H
2
#define UART_H
3
4
#define BAUD 115200UL      
5
6
#define uart_maxstrlen 255
7
 
8
extern uint8_t uart_str_complete;
9
extern uint8_t uart_str_count;
10
extern char uart_string[uart_maxstrlen+1];
11
extern volatile uint8_t globalCounter;
12
13
void UART_init();
14
int uart_putc(unsigned char c);
15
void uart_puts (char *s);
16
uint8_t uart_getc(void);
17
void uart_gets( char* Buffer, uint8_t MaxLen );
18
char USARTReadChar();
19
20
#endif

Dafür wandern ein paar Dinge in die uart.c
* die includes (über stdint.h könnte man diskutieren, schliesslich wird 
uint8_t auch jetzt im Header File noch benötigt)
* die ganzen Berechnungsmakros für die Baudrate (denn nur dort werden 
sie gebraucht)
* die Defintionen für die globalen Variablen - um die 'One Definition 
Rule' erfüllen zu können.

Halte ich an eine Daumenregel: Wenn du ein Headerfile schreibst, was 
exakt muss ein Verwender dieses Moduls wissen und was muss er nicht 
wissen? Muss er wissen, wie sich aus F_CPU und der Baudrate die 
Konstanten für die UART Register berechnen? Nein, muss er nicht wissen! 
Das ist nur in der Funktion UART_init() wichtig. Ein Verwender muss 
wissen, dass es eine Funktion UART_init() gibt, aber nicht wie sie 
arbeitet. Daher gehört das alles nicht ins Header File. Je weniger du 
von dern Internals eines Moduls über das Header File preisgeben musst, 
desto besser.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Edit: Ah, ich seh schon. Johann hat sich um dieses ominöse extmem schon 
angenommen.

von Georg T. (microschorsch)


Lesenswert?

Johann L. schrieb:
> Georg T. schrieb:
>
>> ich konnte das alles auf eine einzige Routine extmem_init()
>> zurückverfolgen, lasse ich sie weg, gehts. Warum???
>
> Weil pBank_1 zu 0 initialisiert wird, denn es ist eine globale Variable.
> In extmem_init schreibst du dann 0x8000 an Adresse 0. Was soll das?
>
> Insbesondere setzt du R1 (zero_reg) auf 0x80.

Ich glaube, dass verstehe ich zu wenig....

früher - vor meinem Umbau :-) - sah das so aus - und funktionierte
1
#ifndef EXTMEM_H
2
#define EXTMEM_H
3
4
extern volatile uint16_t *pBank_1;
5
6
volatile uint16_t *pBank_1 = (unsigned char *) 0x8000;
7
8
void extmem_init(void) __attribute__ ((naked)) __attribute__ ((section (".init1")));
9
10
void extmem_init(void) {
11
  MCUCR = (1 << SRE);
12
}
13
14
#endif

Macht nicht mich für den Code verantwortlich, das hab ich mir 
zusammenkopiert. Wie müsste der Code jetzt aussehen?

Georg

von Karl H. (kbuchegg)


Lesenswert?

Georg T. schrieb:
> Johann L. schrieb:
>> Georg T. schrieb:
>>
>>> ich konnte das alles auf eine einzige Routine extmem_init()
>>> zurückverfolgen, lasse ich sie weg, gehts. Warum???
>>
>> Weil pBank_1 zu 0 initialisiert wird, denn es ist eine globale Variable.
>> In extmem_init schreibst du dann 0x8000 an Adresse 0. Was soll das?
>>
>> Insbesondere setzt du R1 (zero_reg) auf 0x80.
>
> Ich glaube, dass verstehe ich zu wenig....

was gibt es da nicht zu vertsehen?

Du hast einen NULL-Pointer

  uint8_t * ptr = NULL;

und bei

  *ptr = irgendwas;

kann alles mögliche passieren. Du schreibst irgendwo in den Speicher. 
Ok, nicht einfach irgendwo, sondern an Adresse 0. Aber dort liegt nichts 
was du sinnvoll beschreiben könntest.

> extern volatile uint16_t *pBank_1;
>
> volatile uint16_t *pBank_1 = (unsigned char *) 0x8000;

'Das' ist aber eine ganz andere Geschichte! Hier wird der Pointer selbst 
mit einem Wert initialisiert und nicht der Speicherzelle auf die er 
zeigt ein Wert zugewiesen.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

extmem.h
1
#ifndef EXTMEM_H
2
#define EXTMEM_H
3
4
#include <avr/io.h>
5
6
extern volatile uint16_t *pBank_1;
7
8
void extmem_init(void) __attribute__ ((naked)) __attribute__ ((section (".init1")));
9
10
#endif

extmem.c
1
#include "extmem.h"
2
3
volatile uint16_t *pBank_1 = (unsigned char *) 0x8000;
4
5
void extmem_init(void) {
6
  MCUCR = (1 << SRE);
7
}

Ich empfehle DIRINGEND (wie viele tausend male zuvor in diesem Forum), 
die Konsultation und das Durcharbeiten eines C-Buchs! Dann stellt es 
dich auch nicht mehr vor große Probleme, was eine Deklaration ist und 
was eine Definition; wann man extern braucht und wie man es einsetzt; 
wan man es mit einer Initialisierung zutun hat und wann mit einer 
Zuweisung; und last but not least: Pointer!

: Bearbeitet durch User
von Georg T. (microschorsch)


Lesenswert?

Hallo Karl-Heiz et al,

ich habe wirklich viele Zeilen (sicherlich über hunderttausend) C++ 
geschrieben. Hier kenne ich mich recht gut aus, denke ich. Im Regelfall 
konnte ich hier allerdings auch auf "voltile", _attribute_, PROGMEM 
und so weiter verzichten.
Auch wenn das wie selbstverständlich erscheint, an C tue ich mich sehr 
schwer (wie Dir ja auch aufgefallen ist).

Vielen Dank für Deinen Code, ich muss jetzt leider noch was erledigen, 
ich werds heute Abend ausprobieren.

Jetzt noch mal für mich zum mitschreiben
1
volatile uint16_t *pBank_1 = (unsigned char *) 0x8000;

Was ist der Unterschied??? Ich ralls nicht... Ich sage dem Pointer: 
"Pointer, Du zeigst jetzt auf 0x8000", wohin er vorher zeigt ist doch 
wurscht...?

meric

Schorsch

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Georg T. schrieb:

> Jetzt noch mal für mich zum mitschreiben
>
>
1
> volatile uint16_t *pBank_1 = (unsigned char *) 0x8000;
2
>
>
> Was ist der Unterschied??? Ich ralls nicht... Ich sage dem Pointer:
> "Pointer, Du zeigst jetzt auf 0x8000", wohin er vorher zeigt ist doch
> wurscht...?

In deinem Code sieht das aber so aus:
1
*pBank_1 = (unsigned char *) 0x8000;

Da wird nicht der Zeiger geändert, sondern das, auf das er zeigt.

Richtig wäre (wenn man es mit einer Zuweisung statt einer
Initialisierung machen möchte):
1
pBank_1 = (unsigned char *) 0x8000;

von Vn N. (wefwef_s)


Lesenswert?

Georg T. schrieb:
> ich habe wirklich viele Zeilen (sicherlich über hunderttausend) C++
> geschrieben.

Die Probleme, an denen es bei dir scheitert, sind in C++ die gleichen.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

vn nn schrieb:
> sind in C++ die gleichen.

In aller Regel nicht, denn dort hantiert man mit Zeigern nicht in
der Form wie hier.

von nur Gast (Gast)


Lesenswert?

Jörg Wunsch schrieb:
> Georg T. schrieb:
>> globalCounter ist im Header definiert
>
> volatile?
>
> In einem Header sollte man übrigens immer nur deklarieren, nie
> definieren.  Also:
>
> Header:
>
>
1
extern volatile uint16_t globalCounter;
>
> In einer C-Datei dann:
>
>
1
> #include "Header"
2
> 
3
> volatile uint16_t globalCounter;
4
>

Hallo Jörg,

warum ist das besser als z.B.:

Header;

char test;

C-Code:

test = 0;
test ++;

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

nur Gast schrieb:
> warum ist das besser als z.B.:
>
> Header;
>
> char test;
>
> C-Code:
>
> test = 0;
> test ++;
1
$ echo "char test;" > header
2
$ echo '#include "header"' > cfile1.c
3
$ echo 'int foo(void) { test = 0; }' >> cfile1.c
4
$ echo '#include "header"' > cfile2.c
5
$ echo 'int main(void) { extern int foo(void); foo(); test++; }' >> cfile2.c
6
$ cc -O -fno-common -o foo cfile1.c cfile2.c
7
/var/tmp//ccZrWvH3.o(.bss+0x0): multiple definition of `test'
8
/var/tmp//cczRicUe.o(.bss+0x0): first defined here

von nur Gast (Gast)


Lesenswert?

Hallo Jörg,

Danke für die Antwort.

Dein Fall ist natürlich klar.

Was ist aber, wenn mann jede .c (incl. .h) Datei allein
compiliert und dann alles zusammenlinkt, dann dürfte man das
Problem nicht mehr haben, oder ?

Tschuldigung, bin nicht so der Experte im compilieren,
aber so hatte ich trotz mehrfache Einbindung von .h Dateien in
einzelne .c Dateien noch keinen Ärger.
(Includewächter sind natürlich drin)

Mein Beispiel oben ließt man auch in diversen C-Büchern sowie
externen Quellcode.

Sollte man die Benutzung dieses Systems ernsthaft überdenken ?

Danke

Gast (Login liegt zu Hause)

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

nur Gast schrieb:

> Was ist aber, wenn mann jede .c (incl. .h) Datei allein
> compiliert und dann alles zusammenlinkt, dann dürfte man das
> Problem nicht mehr haben, oder ?

Doch, selbstverständlich.  Das passiert ja ohnehin genau so im
Hintergrund.

> Tschuldigung, bin nicht so der Experte im compilieren,
> aber so hatte ich trotz mehrfache Einbindung von .h Dateien in
> einzelne .c Dateien noch keinen Ärger.

Weil beim GCC -fcommon die normale Voreinstellung ist.  Dabei
werden alle gleichnamigen nichtinitialisierten Variablen durch den
Linker überlagert.  Das ist eine (von zwei) Möglichkeiten, die der
C-Standard für diesen Fall zulässt, aber die Vorgehensweise von
-fno-common ist die andere der beiden.

Ein strikt konformes Programm darf sich also nicht darauf
verlassen.  Alle anderen sollten sich lieber nicht darauf verlassen. 
;)

Deine Methode geht übrigens spätestens in dem Moment schief, wo du
eine initialisierte Variable benutzen willst.  Ersetze also das
1
char test;

im Header durch
1
char test = 42;

und es knallt auch ohne -fno-common dann beim Linken.

> Mein Beispiel oben ließt man auch in diversen C-Büchern sowie
> externen Quellcode.

Sofern die C-Bücher nach 1989 verfasst worden sind, sind sie zumindest
sehr schlampig gearbeitet.

von Walter (Gast)


Lesenswert?

nur Gast schrieb:
> Mein Beispiel oben ließt man auch in diversen C-Büchern sowie

schmeiß die Bücher weg

von nur Gast (Gast)


Lesenswert?

Hallo Jörg,

Danke für Deine umfangreiche und vor allem verständliche Erklärung.
Diese Fußangel ist mir noch nie aufgefallen, aber Du hast Recht:

char test = 0; im Header, da mault der Compiler.

Es ist echt gut hier im Forum so einen Experten zu diesem Thema zu haben
wie Dich.

GCC -fcommon ist also standardmäßig aktiviert und der gcc gibt beim
Compilieren keinen Fehler aus, so ist das doch, oder ?
Daher ist mir das noch nie aufgefallen.

Also müßte die richte Vorgehensweise so sein:
(Beispiel !)

<Header>
1
extern uint8_t Test;

<C-Datei>
1
uint8_t Test = 0;
2
Test ++;
3
Test--:

Oder ?

Danke

Gast

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

nur Gast schrieb:
> GCC -fcommon ist also standardmäßig aktiviert und der gcc gibt beim
> Compilieren keinen Fehler aus, so ist das doch, oder ?
> Daher ist mir das noch nie aufgefallen.

So ist es.

> Also müßte die richte Vorgehensweise so sein:

Ja, wobei man eine Initialisierung mit 0 bei globalen und statischen
Variablen immer weglassen kann.

von nur Gast (Gast)


Lesenswert?

Hallo Jörg,

Besten Dank.

Gast

von Georg T. (microschorsch)


Lesenswert?

Hallo,

danke, danke für die vielen Antworten, leider bin ich offensichtlich 
kein C-Experte und habe die Auswirkungen des Ausdrucks "extern" wohl nur 
unzureichend verstanden, aber mein "Gefühl" sagt mir, dass dein Beispiel

> Also müßte die richte Vorgehensweise so sein:
> (Beispiel !)
>
> <Header>
>
>
1
> extern uint8_t Test;
2
>
>
> <C-Datei>
>
>
1
> uint8_t Test = 0;
2
> Test ++;
3
> Test--:
4
>

nun auf dem Stack eine weitere Variable Test anlegt und das 
ursprüngliche Test unangetastet bleibt. Sagt das extern nicht nur dem 
compiler "Du compiler, Du braucht für mich keinen Platz mehr 
freizuhalten, den bringt der Linker schon mit???"

vn nn schrieb:
>> sind in C++ die gleichen.

>In aller Regel nicht, denn dort hantiert man mit Zeigern nicht in
>der Form wie hier.

Wie wahr. Ich denke der wesentliche Unterschied ist die magische KB 
Grenze. Ich vermute ein µC mit 1GB SRAM ließe sich deutlich einfacher 
programmieren, das muß man sich nicht jedes byte zweimal überlegen.

BTW Hat schonmal jemand versucht einen µC in C++ zu programmieren??

> Richtig wäre (wenn man es mit einer Zuweisung statt einer
> Initialisierung machen möchte):
1
 
2
pBank_1 = (unsigned char *) 0x8000;

Ja... wo ich das so sehe, sieht das einleuchtend aus. Merci

Schorsch

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Georg T. schrieb:

>>
1
>> uint8_t Test = 0;
2
>> Test ++;
3
>> Test--:
4
>>
>
> nun auf dem Stack eine weitere Variable Test anlegt und das
> ursprüngliche Test unangetastet bleibt.

Ja, das ist natürlich so etwas unglücklich geschrieben.

Besser:
1
uint8_t Test;
2
3
void foo(void)
4
{
5
   Test++;
6
}

> Sagt das extern nicht nur dem
> compiler "Du compiler, Du braucht für mich keinen Platz mehr
> freizuhalten, den bringt der Linker schon mit???"

Im Prinzip.  Es sagt dem Compiler "Es gibt irgendwo eine Variable
Test, die hat den Typ uint8_t, du kannst sie benutzen."  Daher ist
es eine Deklaration.  Es muss dann eben irgendwo auch noch eine
Definition dafür geben.

>>> sind in C++ die gleichen.
>
>>In aller Regel nicht, denn dort hantiert man mit Zeigern nicht in
>>der Form wie hier.
>
> Wie wahr. Ich denke der wesentliche Unterschied ist die magische KB
> Grenze.

Nö.  In einer normalen C++-Applikation muss man relativ wenig
"low-level"-Arbeit mit Zeigern machen.  Eigentlich braucht man sie
nur für die dynamische Speicherverwaltung.

Was du hier machen willst, gehört ja sonst eher zum Handwerkszeug
des unter deiner C++-Applikation liegenden Betriebssystems.

> BTW Hat schonmal jemand versucht einen µC in C++ zu programmieren??

Ja, genügend.  Kein grundlegendes Problem, solange du dir im Klaren
bist, welche C++-Konstrukte "Geld kosten".

von Karl H. (kbuchegg)


Lesenswert?

Georg T. schrieb:


Für 100-tausend Lines Of Code sind deine Kenntnisse aber sehr schwach.


> unzureichend verstanden, aber mein "Gefühl" sagt mir, dass dein Beispiel
Dein Gefühl ist falsch,


> nun auf dem Stack eine weitere Variable Test anlegt und das
> ursprüngliche Test unangetastet bleibt.

welches ursprüngliche.
Auch in diesem Beispiel gibt es nur 1 Test. Das Beispiel ist etwas 
unglücklich formuliert, weil die ausführbaren Anweisungen nicht einfach 
so alleine in der gegend rumstehen können, sondern in einer Funktion 
sein müssten. Aber abgesehen davon, ist alles ok.

> Sagt das extern nicht nur dem
> compiler "Du compiler, Du braucht für mich keinen Platz mehr
> freizuhalten, den bringt der Linker schon mit???"

Nö.
Es sagt dem Compiler:
Irgendwo gibt es eine derartige Variable. Du brauchst dich hier, wenn du 
diesen Code compilierst nicht darum zu kümmern, wo genau. Nimm einfach 
nur zur Kenntnis, dass es ein Test gibt und das es vom Type uint8_t ist.

Das ist die Aussage von
1
extern uint8_t Test;
2
3
int main()
4
{
5
  Test = 5;
6
}

Aber:
Irgendwann muss man auch mal Farbe bekennen. 'Irgendwo' muss dann 
tatsächlich mal die Variable erzeugt werden. Der Compiler muss Platz 
dafür reservieren. Das geschieht dann hier
1
uint8_t Test;
2
3
void foo()
4
{
5
  Test = 8;
6
}


Das erste nennt man eine Deklaration. Eine Deklaration ist alles, was 
dem Compiler zur freundlichen Kenntnisnahme vorgeworfen wird und wofür 
er keine Platzreservierung im fertigen Programm vornehmen muss.
1
extern uint8_t Test;
ist so eine Deklaration.

Eine Definition hingegen ist etwas, was dem Compiler nicht nur die 
Eigenschaften des Gewünschten verklickert, sondern wofür der COmpiler 
dann auch tatsächlich dafür sorgen muss, dass es im fertig gelinkten 
Programm auch existiert.
1
uint8_t Test;
ist eine Definition.


So. Und jetzt gibt es eine C-Regel (die es übrigens auch bei C++ gibt), 
die nennt sich die ODR - die One Definition Rule.
Sie besagt, dass es in einem ferig gelinkten Programm lediglich immer 
nur eine Definition für etwas geben darf. Du kannst beliebig viele 
Deklarationen haben, sofern sie alle (und die Definition) in den 
Datentypen übereinstimmen. Aber Definition darf es nur 1 geben.

Du darfst auch in einer Übersetzungseinheit sowohl eine Deklaration als 
auch eine Definition haben. Das ist kein Problem, solange die beiden 
übereinstimmen.
1
extern uint8_t Test;
2
uint8_t Test;
3
4
void foo()
5
{
6
  Test = 7;
7
}
ist absolut perfekt und zulässig.

So. Jetzt gibt es aber ein potentielles Problem.
Der COmpiler kann nicht überprüfen, ob die Deklaration in
1
extern uint8_t Test;
2
3
int main()
4
{
5
  Test = 3;
6
}

und die Definition in
1
uint8_t Test;
2
3
void foo()
4
{
5
  Test = 7;
6
}
auch tatsächlich zusammenstimmen. Denn der Compiler sieht ja immer nur 
eines der beiden Files. Wenn er das andere compiliert fängt er wieder 
bei 0 an.

Und da kommt jetzt das Header File ins Spiel.
Die Deklaration wird ins Header File gezogen
1
extern uint8_t Test;

damit wird die main.c zu
1
#include "Test.h"
2
3
int main()
4
{
5
  Test = 6;
6
}
Wird das vom Compiler compiliert, dann kriegt es über das Header File 
die Deklaration zu Gesicht.

In der anderen *.C Datei braucht es nach wie vor die Definition (denn: 
mann kann nicht immer sagen "die Variable gibt es", irgendwann muss die 
Variable dann auch definiert werden!)
Diese C-Datei sieht so aus
1
uint8_t Test;
2
3
void foo()
4
{
5
  Test = 7;
6
}

Soweit so gut.
Aber: Nichts und niemand hindert dich daran, auch hier das Header File 
reinzuziehen.
1
#include "Test.h"
2
3
utin8_t Test;
4
5
void foo()
6
{
7
  Test = 7;
8
}
Wenn der Compiler dieses jetzt compiliert, dann sieht er beides. Die 
Deklaration UND die Definition. Wenn die beiden jetzt nicht 
zusammenstimmen, dann kann der Compiler das feststellen und es setzt 
einen Fehler. Und da alle anderen *.c Files ebenfalls genau dieses eine 
Header File benutzen, wenn sie Zugang zu dieser Variablen brauchen, ist 
damit auch sichergestellt, dass alle Deklarationen in allen *.c Files 
übereinstimmen.

Und damit hat man dann auch das Problem aus der Welt, dass Deklaration 
und Definition übereinstimmen und das der Compiler das nicht ünberprüfen 
könnte, wenn du die 'extern' in den einzelnen C-Files jeweils seperat 
machen würdest.

Kauf dir ein C Buch (und ein C++ Buch). Das ist ja sagenhaft was du 
alles nicht weißt.

: Bearbeitet durch User
von Georg T. (microschorsch)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Kauf dir ein C Buch (und ein C++ Buch). Das ist ja sagenhaft was du
> alles nicht weißt.

oh mann

von Georg T. (microschorsch)


Lesenswert?

Georg T. schrieb:
> Vielen Dank für Deinen Code, ich muss jetzt leider noch was erledigen,
> ich werds heute Abend ausprobieren.

Hallo zusammen,

so habs ausprobiert, geht leider immer noch nicht. Lasse ich extmem_init 
raus gehts!

Weiterhin habe ich jetzt aber auch aus meinem gesamten Code sämtliche 
pBank_1 Zuweisungen, Definition und Deklaration auskommentiert und es 
geht immer noch nicht. Ich hab auch probiert das
1
MCUCR = (1 << SRE);
auszukommentieren. Geht auch nicht. Es scheint wirklich das
1
void extmem_init(void) __attribute__ ((naked)) __attribute__ ((section (".init1")));
zu sein, was mir Schwierigkeiten macht.

Zum Ausprobieren habe ich jetzt mal diese Zeilen
1
void extmem_init(void) __attribute__ ((naked)) __attribute__ ((section (".init1")));
2
3
void extmem_init(void) {
4
  MCUCR = (1 << SRE);
5
}
unmittelbar vor die main-routine in main.c gepackt. Das geht. Warum 
nicht, wenn ich Dinger erst später zusammenlinken will

Da brauche ich jetzt nochmal Eure Hilfe

Schorsch

: Bearbeitet durch User
von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Georg T. schrieb:
> void extmem_init(void) _attribute_ ((naked)) _attribute_ ((section
> (".init1")));
>
> void extmem_init(void) {
>   MCUCR = (1 << SRE);
> }

Das kann nicht funktionieren, weil extmem_init mit einer RET-Instruktion 
endet und damit den init-Code verlässt und ins Nirwana springt.

Zudem kann der Code nicht funktionieren, wenn nicht hinreichend gut 
optimiert wird: Der SP wird erst in .init3 initialisiert..

Gedankenlose Copy-and-Paste Programmierung führt wie so oft nicht zum 
Ziel...

Was ist überhaupt der Zweck von pBank_1?

von Georg T. (microschorsch)


Lesenswert?

Johann L. schrieb:
> Gedankenlose Copy-and-Paste Programmierung führt wie so oft nicht zum
> Ziel...
>
> Was ist überhaupt der Zweck von pBank_1?

Hallo,

vielen Dank für Deine Hinweise, Ich bin froh, dass der externe RAM 
überhaupt mal lief...

Ich steuere Einen Prozess, der innerhalb eine Sekunde mehrere KB an 
Daten sammelt, dann können die Daten über den UART ausgegeben werden. in 
pBank_1 speiche ich die Daten zwischen (im externen RAM) und kann sie 
wieder ausgeben.

So oder ähnliche könnte die Aquisitionsroutine die einmal pro ms 
aufgerufen wird aussehen. wie gesagt ich bastele noch an dem Projekt
1
uint16_t ssi = getSSI(0);
2
uint16_t din = getDIN();
3
uint16_t adc0 = ADC_Read(0);
4
uint16_t adc1 = ADC_Read(1);
5
if (state == 1 || ssi > threshold) {
6
  state = 1;
7
  uint8_t nVars = 4;
8
  counter += nVars;
9
  pBank_1[counter + 0] = ssi;
10
  pBank_1[counter + 1] = din;
11
  pBank_1[counter + 2] = adc0;
12
  pBank_1[counter + 3] = adc1;
13
         if (counter > 1000/acq_freq * acq_time || (counter > 50 && ssi < threshold) ) { // End of acquisition
14
      TCCR0 = 0x00;
15
      write_out_from_mem(counter, nVars);
16
[...]

wie müsste das denn Deiner Meinung nach aussehen?? Insbesondere die 
Init-Routine??

merci

Schorsch

von Patrick (Gast)


Lesenswert?

Johann L. schrieb:
> Das kann nicht funktionieren, weil extmem_init mit einer RET-Instruktion
> endet und damit den init-Code verlässt und ins Nirwana springt.

Hm, sicher? GCC 4.3.2 bastelt mir jedenfalls kein RET an eine naked 
function, und hiernach

http://gcc.gnu.org/ml/gcc/2013-05/msg00042.html

scheint sich das auch nicht so schnell zu ändern.

> Zudem kann der Code nicht funktionieren, wenn nicht hinreichend gut
> optimiert wird: Der SP wird erst in .init3 initialisiert..

Ja, das stimmt natürlich; auch der Aufruf von extmem_init() in der 
main() im oben geposteten Code zeigt, dass der Autor offensichtlich 
nicht wirklich weiß, was er hier tut (ist ja nicht böse gemeint).

Georg T. schrieb:
> wie müsste das denn Deiner Meinung nach aussehen?? Insbesondere die
> Init-Routine??

Initialisiere pBank_1 so wie von Jörg Wunsch vorgeschlagen
1
volatile uint16_t *pBank_1 = (unsigned char *) 0x8000;

(und bitte, bitte mit Zucker obendrauf: Die Definition in ein C-File und 
niemals nicht in einen Header), packe das Setzen des SRE-Bits
1
MCUCR = (1 << SRE);

in die main() und schmeiß die extmem_init() raus.

Und gehe systematisch vor: Baue Dir zunächst ein Minimalprogramm und 
teste und erweitere es zyklisch.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

extmem_init() in einer Sektion .init1 braucht man nur dann, wenn man
statische Variablen (vom Compiler/Linker zugewiesen) in den externen
Speicher packen lassen will.  Dann muss der externe Speicher natürlich
aktiv sein, bevor die nachfolgenden .initN-Sektionen versuchen, das
.bss auzunullen und das .data zu füllen.

Wenn man den externen Speicher wirklich nur extern über einen Zeiger
in der Applikation benutzt, dann ist es völlig ausreichend, das SRE
am Anfang von main() zu setzen, bevor man den Speicher das erste Mal
anspricht.

von us73 (Gast)


Lesenswert?

Georg T. schrieb:
> Macht nicht mich für den Code verantwortlich, das hab ich mir
> zusammenkopiert. Wie müsste der Code jetzt aussehen?

Das dir bei dem Satz überhaupt noch jemand hier hilft, wundert mich 
sehr.
Mit solch einer Aussage machst Du dir keine Freunde.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Jörg Wunsch schrieb:
> Ja, wobei man eine Initialisierung mit 0 bei globalen und statischen
> Variablen immer weglassen kann.

Leider nicht immer. Wie ich schmerzhaft beim IAR-Compiler für AVRs 
feststellen musste.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Frank M. schrieb:
> Wie ich schmerzhaft beim IAR-Compiler für AVRs feststellen musste.

Glaub' ich dir nicht.  Der IAR ist mir bekannt als einer, die den
C-Standard sehr ordentlich implementieren.

Wenn, dann hast du damit nur einen latenten anderen Bug kaschiert.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Jörg Wunsch schrieb:
> Glaub' ich dir nicht.  Der IAR ist mir bekannt als einer, die den
> C-Standard sehr ordentlich implementieren.

Sorry, ich muss das zurücknehmen. Es handelte sich um Codevision und 
nicht um IAR. Ich hatte es falsch in Erinnerung.

Konkret ging es um die Portierung von IRMP auf Codevision. Ich habe 
diese nicht selbst durchgeführt, sondern ein µC.net-Leser. Hierzu musste 
er sämtliche innerhalb einer Funktion definierten static-Variablen mit 0 
initialisieren, damit es lief. Ich habe dies dann in den IRMP-Source 
widerwillig übernommen, denn es sieht sehr "peinlich" aus, plötzlich 
globale oder statische Variablen mit 0 zu initialisieren.

Ich habe aber wegen der schlechten anderen "Qualitäten" von Codevision 
dann irgendwann die Portabilität zu Codevision aufgegeben und sämtlichen 
Codevision-spezifischen Rotz wieder rausgeschmissen - bis auf diese 
ominösen Initialisierungen ;-)

> Wenn, dann hast du damit nur einen latenten anderen Bug kaschiert.

Wie kann man durch Ersetzung von

 static uint8_t xyz;

in

 static uint8_t xyz = 0;

einen latententen anderen Bug kaschieren?

Das musst Du mir mal erklären ;-)

Gruß,

Frank

: Bearbeitet durch Moderator
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Frank M. schrieb:

> Sorry, ich muss das zurücknehmen. Es handelte sich um Codevision und
> nicht um IAR. Ich hatte es falsch in Erinnerung.

Dann glaube ich dir das schon viel eher. :-)

Allerdings sollte das eigentlich einen fetten Bugreport an
Codevision wert sein.  Jemand, der es nicht schafft, globale und
statische Variablen gemäß C-Standard zu initialisieren, hat meiner
Meinung nach nicht das Recht, da „C“ draußen draufzuschreiben.  Da
hilft auch keine Ausrede wie „im embedded-Bereich muss man geizig
sein mit dem Code“.

>> Wenn, dann hast du damit nur einen latenten anderen Bug kaschiert.
>
> Wie kann man durch Ersetzung von
>
>  static uint8_t xyz;
>
> in
>
>  static uint8_t xyz = 0;
>
> einen latententen anderen Bug kaschieren?

Indem die Variable xyz beispielsweise dann an einer anderen Stelle
im Speicher landet.  Wenn sie infolge eines latenten Bugs (bspw.
eines verbogenen Zeigers) vorher überschrieben wurde und nachher
nicht mehr, dann würde man es instinktiv auf die nicht vorhandene
Initialisierung schieben, obwohl der eigentliche Fehler ganz
woanders liegt.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Jörg Wunsch schrieb:

> Allerdings sollte das eigentlich einen fetten Bugreport an
> Codevision wert sein.

Da hast Du prinzipiell recht. Nur habe ich persönlich nichts mit 
Codevision zu schaffen und deshalb ist mir dieser offensichtliche Bug 
keine 5 Minuten Arbeit wert. Solche Kinderkrankheiten von C-Compilern 
habe ich zuletzt Mitte der 80er auf SINIX-Systemen erlebt.

>  Jemand, der es nicht schafft, globale und
> statische Variablen gemäß C-Standard zu initialisieren, hat meiner
> Meinung nach nicht das Recht, da „C“ draußen draufzuschreiben.

Ja, das ist wirklich peinlich.

> Indem die Variable xyz beispielsweise dann an einer anderen Stelle
> im Speicher landet.

Warum sollte sie? Für mich sind beide Statements adäquat. Aber Du hast 
recht: ich habe auch in Erinnerung, dass der gcc früher auch mal 
zwischen vom Programmierer initialisierten globalen Variablen und den 
nicht explizit initialisierten unterschied. Mittlerweile wird beides

static uint8_t xyz;
static uint8_t xyz = 0;

aber vom gcc absolut gleich behandelt, d.h. es gibt kein einziges Byte 
Unterschied im Compilat.

>  Wenn sie infolge eines latenten Bugs (bspw.
> eines verbogenen Zeigers) vorher überschrieben wurde und nachher
> nicht mehr, dann würde man es instinktiv auf die nicht vorhandene
> Initialisierung schieben, obwohl der eigentliche Fehler ganz
> woanders liegt.

Korrekt. Das würde ich dann auch denken. Auf den Gedanken, dass eine 
explizit mit 0 initialisierte Variable vielleicht in eine andere Section 
geschoben wird, bin ich dabei nicht gekommen. Schließlich sind für mich 
als Programmierer beide Statements absolut äquivalent.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Frank M. schrieb:
> Mittlerweile wird beides
>
> static uint8_t xyz;
> static uint8_t xyz = 0;
>
> aber vom gcc absolut gleich behandelt, d.h. es gibt kein einziges Byte
> Unterschied im Compilat.

-fno-zero-initialized-in-bss

Damit kann man das frühere (bis GCC 2.95 meiner Erinnerung nach)
Verhalten noch aktivieren.

Einer Applikation sollte das natürlich eigentlich egal sein.

von Gero (Gast)


Lesenswert?

@ Karl Heinz Buchegger schrieb:

>Wer oder was vergreift sich sonst noch an globalCounter?
>
>zb. ein einziges, bei der Editoroperation verloren gegangenes '=' kann
>zb den Effekt erklären
>
>...
>
>  while( 1 ) {
>    if( globalCounter = 0 )
>      ....

>da wo vorher ein Vergleich stand, steht jetzt eine Zuweisung.


gewöhn dir doch einach an, die Konstante links zu schreiben,
schreibs doch einfach anders herum:

>    if( 0 = globalCounter )

dann meckerts der Compiler immer an. So lassen sich Fehler beim Kodieren 
vermeiden

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Gero schrieb:
> gewöhn dir doch einach an, die Konstante links zu schreiben,
> schreibs doch einfach anders herum:

Lässt sich aber bescheuert lesen, und alle Compiler dieses Jahrtausends
sind problemlos in der Lage, das versehentlich vergessene doppelte
Gleichheitszeichen zu warnen.

Warnungen muss man natürlich einschalten und auch beachten.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Jörg Wunsch schrieb:
> Gero schrieb:
>> gewöhn dir doch einach an, die Konstante links zu schreiben,
>> schreibs doch einfach anders herum:
>
> Lässt sich aber bescheuert lesen,

Ist nur Gewohnheit.

von Georg T. (microschorsch)


Lesenswert?

Hallo Leute,

obwohl das urspüngliche Problem gelöst ist hören die Probleme bei mir 
leider noch nicht auf.

Mein XMEM Interface läuft nicht mehr, mittlerweile bin ich mir auch 
nicht mehr sicher, ob es je richtig lief... Ich hab eine einfache 
io-routine geschrieben, die werte in den externen speicher schreibt und 
ließt, das geht manchmal aber leider nicht immer, hängt von der Stelle 
ab.

Also meine Frage, wenn ich mein Interface mit
1
void extmem_init(void) __attribute__ ((naked)) __attribute__ ((section (".init1")));
2
3
void extmem_init(void) {
4
  MCUCR = (1 << SRE);
5
}

initialisiere. Kann ich dann einen Pointer
1
extern volatile uint16_t *pBank_1;
2
3
volatile uint16_t *pBank_1  = (uint16_t *) 0x8100;
als Zeiger auf eine externe Speicheraddresse benutzen, ohne Verwundung 
von Linker-Optionen und allocs??

Oben steht einiges davon, dass man es in the .init3 schreiben müsste, 
weiterhin finde ich neben "naked" auch "used" und "section", oder auch, 
dass man MCUCR direkt in die main() schreibt. Ich denke, ich hab jetzt 
alle Varianten durch. So weit reicht mein C wirklich nicht... jaja ich 
kauf mir ein Buch... könnt ihr eins empfehlen??

Danke
Schorsch

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Johann L. schrieb:
> Ist nur Gewohnheit.

Nö.  Ich formuliere doch den Satz auch:

„Wenn die Variable i den Wert 2 hat, dann …“

und nicht

„Wenn 2 dem Wert der Variablen i gleicht, dann …“

Wie gesagt, für derartige Aktionen gibt's seit vielen Jahren eine
Warnung.  Die Bugs, die mir im realen Leben über den Weg gelaufen
sind, liegen allesamt woanders.  Das fängt damit an, dass man die
andere Seite des Vergleichs sich falsch ausgedacht hat …

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Georg T. schrieb:
> Kann ich dann einen Pointer extern volatile uint16_t *pBank_1;
>
> volatile uint16_t *pBank_1  = (uint16_t *) 0x8100;
>
> als Zeiger auf eine externe Speicheraddresse benutzen, ohne Verwundung
> von Linker-Optionen und allocs??

Verwunden musst du die Linkeroptionen nicht gleich. :-)

Das “volatile” dort muss normalerweise auch nicht sein (sofern nicht
aus verschiedenen Threads drauf zugegriffen wird), denn es stört dich
überhaupt nicht, ob der Compiler die Zugriffe auf den Externspeicher
nun irgendwo cachet oder nicht.

Ja, einen derartigen Zeiger kannst du benutzen, naja, sofern sich
halt auch wirklich RAM dort befindet. ;-)

Dein SRE-Bit kannst du getrost am Anfang von main() setzen, das spart
dir das Gewurschtel mit den nackten init-Funktionen.

Hast du irgendwas wie eine UART dran?  Dann lass dir doch mal den
Inhalt des Externspeichers ausdumpen.  Das sind typischerweise
einigermaßen regelmäßige Muster, die aber einzelne Ausreißer haben.
Wenn da nur 0x00 oder 0xFF rauskommt, dann würde ich stutzig werden.

von Georg T. (microschorsch)


Lesenswert?

Hi, danke für die schnelle Anwort

Jörg Wunsch schrieb:
> Hast du irgendwas wie eine UART dran?  Dann lass dir doch mal den
> Inhalt des Externspeichers ausdumpen.  Das sind typischerweise
> einigermaßen regelmäßige Muster, die aber einzelne Ausreißer haben.
> Wenn da nur 0x00 oder 0xFF rauskommt, dann würde ich stutzig werden.


ja, kann den Speicher dumpen:
1
Reading: 964(0x8888): -8241
2
Reading: 965(0x888a): -8225
3
Reading: 966(0x888c): -8241
4
Reading: 967(0x888e): -8225
5
Reading: 968(0x8890): 0
6
Reading: 969(0x8892): 0
7
Reading: 970(0x8894): 0
8
Reading: 971(0x8896): 0
9
Reading: 972(0x8898): -8241
10
Reading: 973(0x889a): -8241
11
Reading: 974(0x889c): -8369
12
Reading: 975(0x889e): -12449
13
Reading: 976(0x88a0): 0
14
Reading: 977(0x88a2): 0
15
Reading: 978(0x88a4): 0
16
Reading: 979(0x88a6): 0
17
Reading: 980(0x88a8): -8353
18
Reading: 981(0x88aa): -8241
19
Reading: 982(0x88ac): -8241
20
Reading: 983(0x88ae): -8225
21
Reading: 984(0x88b0): 0
22
Reading: 985(0x88b2): 0
23
Reading: 986(0x88b4): 0
24
Reading: 987(0x88b6): 0
25
Reading: 988(0x88b8): -8225
26
Reading: 989(0x88ba): -8225
27
Reading: 990(0x88bc): -12465
28
Reading: 991(0x88be): -8369
29
Reading: 992(0x88c0): 0
30
Reading: 993(0x88c2): 0
31
Reading: 994(0x88c4): 0
32
Reading: 995(0x88c6): 0
33
Reading: 996(0x88c8): -8225
34
Reading: 997(0x88ca): -8241
35
Reading: 998(0x88cc): -8241
36
Reading: 999(0x88ce): -12337


hier lege ich int16_t aus, sieht für meine Begriffe regelmäßig aus.
Eigentlich hatte ich aber vorher Zahlen von 1 bis 255 dort 
hingeschrieben

Weiter oben (leider außerhalb des screens) funktioniert das auch. Hab im 
Augenblick keine Ahnung was da schiefgeht.

Nachtrag:

hier kann man sehen,
1
Reading: 889(0x87f2): 121
2
Reading: 890(0x87f4): 122
3
Reading: 891(0x87f6): 123
4
Reading: 892(0x87f8): 124
5
Reading: 893(0x87fa): 125
6
Reading: 894(0x87fc): 126
7
Reading: 895(0x87fe): 127
8
Reading: 896(0x8800): 0
9
Reading: 897(0x8802): 0
10
Reading: 898(0x8804): 0
11
Reading: 899(0x8806): 0
12
Reading: 900(0x8808): -8241
13
Reading: 901(0x880a): -8241
14
Reading: 902(0x880c): -12337
15
Reading: 903(0x880e): -8241
dass das anscheinend erst ab 0x8800 schiefgeht

Schorsch

: Bearbeitet durch User
von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Georg T. schrieb:

> hier lege ich int16_t aus, sieht für meine Begriffe regelmäßig aus.
> Eigentlich hatte ich aber vorher Zahlen von 1 bis 255 dort
> hingeschrieben

Du hast also 8-Bit-Werte (0-255) (unsigned?) geschrieben? Byteweise oder 
wortweise?

Und jetzt liest Du sie als 16-Bit-Werte (und dann noch signed) wieder 
aus?

Das hört sich ziemlich verworren an. Ein Hex-Dump wäre 
aufschlussreicher.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Hmm, normalerweise macht man Speicherdumps besser hexadezimal und
byteweise ;-), sieht sehr gewöhnungsbedürftig aus so.

Aber ich stimme zu, dass das wie ein typisches Einschaltmuster eines
SRAM aussieht.

Georg T. schrieb:
> Eigentlich hatte ich aber vorher Zahlen von 1 bis 255 dort
> hingeschrieben
>
> Weiter oben (leider außerhalb des screens) funktioniert das auch.

Naja, deine Kenntnisse über Zeiger hatten ja schon ein paar
Bildungslücken gezeigt :), insofern denke ich, dass dort noch was
schief läuft.

Fang mal an, kleine Brötchen zu backen.  Bau dir eine Hexdump-Funktion,
dann schreib ein Stück Speicher voll (vielleicht nicht gerade mit 0,
1, 2, 3 …, sondern entweder negiert oder ver-XOR-t oder sowas, da
erkennt man ggf. die Fehler besser), lass es dir dumpen.

Wenn du dir dann Sicher bist, dass du deinen Speicher lesen und
schreiben kannst, dann mach weiter.

> dass das anscheinend erst ab 0x8800 schiefgeht

Naja, bislang ist wohl noch nicht einmal klar, ob dein SRAM auch
korrekt angeschlossen ist, das Adress-Latch funktioniert etc. pp.

: Bearbeitet durch Moderator
von Georg T. (microschorsch)


Lesenswert?

So....

hier meine routine
1
  int i=0;
2
  uint16_t max = 1000;
3
  for (i = 0; i < max; i++) {
4
    char out[32];
5
    pBank_1[i] = i;
6
    sprintf(out, "Writing to 0x%x: 0x%.4x (%d). Reading: 0x%.4x (%d)\n\r",(unsigned int)&pBank_1[i], i, i, pBank_1[i], pBank_1[i]);
7
    uart_puts(out);
8
  }

Hier der untere Teil der Ausgabe
1
Writing to 0x87f0: 0x0378 (888). Reading: 0x0378 (888)
2
Writing to 0x87f2: 0x0379 (889). Reading: 0x0379 (889)
3
Writing to 0x87f4: 0x037a (890). Reading: 0x037a (890)
4
Writing to 0x87f6: 0x037b (891). Reading: 0x037b (891)
5
Writing to 0x87f8: 0x037c (892). Reading: 0x037c (892)
6
Writing to 0x87fa: 0x037d (893). Reading: 0x037d (893)
7
Writing to 0x87fc: 0x037e (894). Reading: 0x037e (894)
8
Writing to 0x87fe: 0x037f (895). Reading: 0x037f (895)
9
Writing to 0x8800: 0x0380 (896). Reading: 0x0000 (0)
10
Writing to 0x8802: 0x0381 (897). Reading: 0x0000 (0)
11
Writing to 0x8804: 0x0382 (898). Reading: 0x0000 (0)
12
Writing to 0x8806: 0x0383 (899). Reading: 0x0000 (0)
13
Writing to 0x8808: 0x0384 (900). Reading: 0xcfcf (-12337)
14
Writing to 0x880a: 0x0385 (901). Reading: 0xdfcf (-8241)
15
Writing to 0x880c: 0x0386 (902). Reading: 0xcf4f (-12337)
16
Writing to 0x880e: 0x0387 (903). Reading: 0xcfcf (-12321)
17
Writing to 0x8810: 0x0388 (904). Reading: 0x0000 (0)
18
Writing to 0x8812: 0x0389 (905). Reading: 0x0000 (0)
19
Writing to 0x8814: 0x038a (906). Reading: 0x0000 (0)
20
Writing to 0x8816: 0x038b (907). Reading: 0x0000 (0)
21
Writing to 0x8818: 0x038c (908). Reading: 0xcfcf (-8241)
22
Writing to 0x881a: 0x038d (909). Reading: 0xcfdf (-8241)
23
Writing to 0x881c: 0x038e (910). Reading: 0xdfcf (-12337)
24
Writing to 0x881e: 0x038f (911). Reading: 0xcfdf (-8241)
25
Writing to 0x8820: 0x0390 (912). Reading: 0x0000 (0)
26
Writing to 0x8822: 0x0391 (913). Reading: 0x0000 (0)
27
Writing to 0x8824: 0x0392 (914). Reading: 0x0000 (0)
28
Writing to 0x8826: 0x0393 (915). Reading: 0x0000 (0)
29
Writing to 0x8828: 0x0394 (916). Reading: 0xdfcf (-12337)
30
Writing to 0x882a: 0x0395 (917). Reading: 0xdfcf (-8241)
31
Writing to 0x882c: 0x0396 (918). Reading: 0xdfcf (-8241)
32
Writing to 0x882e: 0x0397 (919). Reading: 0xdfcf (-12337)

Danke Schorsch

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Ich würde jetzt die Hardware verdächtigen … du schreibst nicht, welcher
Controller das ist.

Ich würde erstmal vorsichtig rangehen mit
1
   XMCRA = _BV(SRW11) | _BV(SRW10);   // 2 wait states

von Georg T. (microschorsch)


Lesenswert?

Jörg Wunsch schrieb:
> Ich würde jetzt die Hardware verdächtigen … du schreibst nicht, welcher
> Controller das ist.
>
> Ich würde erstmal vorsichtig rangehen mit
>
>
1
>    XMCRA = _BV(SRW11) | _BV(SRW10);   // 2 wait states
2
>

Hi,

hab ich probiert, selbes Fehlerbild.

Es ist ein Atmega64 mit einem 32KB SRAM. Die Verschaltung ist die 
"übliche" mit dem Latch dazwischen um Beinchen zu sparen.

Ich habe auch gerade mal den Pointer des Arrays nach 0x1100 verschoben, 
weil ich glaube, dass hier der externe RAM beginnen müsste, früher 
Funktionierte das mal nicht.... wie auch immer

wie geagt selbes Fehlerbild, hier klappt es nicht ab 0x1800, sieht 
irgendwie systematisch aus

Schorsch

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Wie gesagt, guck dir die Hardware an.  Zinnbrücken, Verdrahtungsfehler.

Das Codeschnipsel oben war für einen ATmega1281.  Beim ATmega64
befindet sich das Bit SRW10 im Register MCUCR (statt in XMCRA).

Hast du aus Versehen was in XMCRB reingeschrieben?  Dort kann man einen
Teil der Adressleitungen von Port C außer Betrieb nehmen, sodass sie
wieder als Portpins verfügbar sind.

Last but not least, man kann das gesamte XMEM-Interface auch mal
spaßeshalber „zu Fuß“ implementieren, also Low-Adresse auf Port A
ausgeben, ALE-Impuls auf PG2, High-Teil Adresse auf Port C ausgeben,
WR- oder RD-Impuls ausgeben, Daten auf Port A behandeln.  Das ist
langsamer, aber dann kann man es auch so langsam machen, dass man
im Einzelschritt mit dem Debugger durchgehen kann und die Pins am
SRAM nachmessen kann.

von Georg T. (microschorsch)


Lesenswert?

Ich konnte die "betroffenen" Bereiche jetzt identifizieren
1
Writing to 0x1100: 0x00ff (255). Reading: 0x0000 (0)
2
Writing to 0x1300: 0x00ff (255). Reading: 0x0000 (0)
3
Writing to 0x1500: 0x00ff (255). Reading: 0x00ff (255)
4
Writing to 0x1700: 0x00ff (255). Reading: 0x00ff (255)
5
Writing to 0x1900: 0x00ff (255). Reading: 0x0000 (0)
6
Writing to 0x1b00: 0x00ff (255). Reading: 0x0000 (0)
7
Writing to 0x1d00: 0x00ff (255). Reading: 0x00ff (255)
8
Writing to 0x1f00: 0x00ff (255). Reading: 0x00ff (255)
9
Writing to 0x2100: 0x00ff (255). Reading: 0x00ff (255)
10
[...]
11
Writing to 0x7d00: 0x00ff (255). Reading: 0x00ff (255)
12
Writing to 0x7f00: 0x00ff (255). Reading: 0x00ff (255)
13
Writing to 0x8100: 0x00ff (255). Reading: 0x0000 (0)
14
Writing to 0x8300: 0x00ff (255). Reading: 0x0000 (0)
15
Writing to 0x8500: 0x00ff (255). Reading: 0x00ff (255)
16
Writing to 0x8700: 0x00ff (255). Reading: 0x00ff (255)
17
Writing to 0x8900: 0x00ff (255). Reading: 0x0000 (0)
18
Writing to 0x8b00: 0x00ff (255). Reading: 0x0000 (0)
19
Writing to 0x8d00: 0x00ff (255). Reading: 0x00ff (255)
20
Writing to 0x8f00: 0x00ff (255). Reading: 0x00ff (255)
21
Writing to 0x9100: 0x00ff (255). Reading: 0x0000 (0)
22
Writing to 0x9300: 0x00ff (255). Reading: 0x0000 (0)
23
Writing to 0x9500: 0x00ff (255). Reading: 0x00ff (255)
24
Writing to 0x9700: 0x00ff (255). Reading: 0x00ff (255)
25
Writing to 0x9900: 0x00ff (255). Reading: 0x0000 (0)
26
Writing to 0x9b00: 0x00ff (255). Reading: 0x0000 (0)
27
Writing to 0x9d00: 0x00ff (255). Reading: 0x00ff (255)
28
Writing to 0x9f00: 0x00ff (255). Reading: 0x00ff (255)
alle anderen Stellen stehen auf 0x00ff
Sieht wirklich systematisch aus.... sind die Beinchen vielleicht 
irgendwie standardmäßig abgeschaltet???

Schorsch

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

@ Georg T. (microschorsch)

>> Ich würde erstmal vorsichtig rangehen mit

>>    XMCRA = _BV(SRW11) | _BV(SRW10);   // 2 wait states

Jo. Vor allem, wenn man nur ein 74HC573 Latch hat und kein AHC.

>Es ist ein Atmega64 mit einem 32KB SRAM. Die Verschaltung ist die
>"übliche" mit dem Latch dazwischen um Beinchen zu sparen.

Hab ich vor einigen Monaten auch gemacht, lief auf Anhieb. Mit einem 
schnellen 74AHC573 und 1 Wait State, 70ns SRAM.

>wie geagt selbes Fehlerbild, hier klappt es nicht ab 0x1800, sieht
>irgendwie systematisch aus

Lötfehler? Adressleitung verbunden mit ???

Ein Speichertest wirkt wunder. Z.B. wenn man in Adressen reinschreibt 
und liest, wo immer nur 1 Bit gesetzt ist. Da erkennt man solche Fehler 
recht schnell.

von Georg T. (microschorsch)


Lesenswert?

Hallo Falk,
Falk Brunner schrieb:
> Hab ich vor einigen Monaten auch gemacht, lief auf Anhieb. Mit einem
> schnellen 74AHC573 und 1 Wait State, 70ns SRAM.


der latch ist n ACT der RAM ist echt schnell seh ich gerade 55ns

Momentan tippe ich auch auf einen Lötfehler, würd auch erklären warum es 
schonmal ging, da wars n anderes Board, ich hab zwei davon....

Schorsch

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Georg T. schrieb:
>     char out[32];
>     sprintf(out, "Writing to 0x%x: 0x%.4x (%d). Reading: 0x%.4x

Kommt die 32 aus dem Zufallsgenerator? Gefühlt lang genug aber definitiv 
zu kurz für die Zeichenkette, die out fassen soll!

von Georg T. (microschorsch)


Lesenswert?

Hi,

ja das mit den 32 ist mir schon aufgefallen, hatte ich alten Code 
weiterverwendet... geht dennoch nicht


Habe mir alle Beinchen angeschaut, sieht super aus. Die Platine ist 
professionell gefertigt.

Habe noch versucht den Pointer von einer anderen Adresse starten zu 
lassen, aber es sind immer die selben Bereiche "blind" Kann der RAM 
vielleicht ne Macke haben?

Schorsch

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Georg T. schrieb:
> Kann der RAM vielleicht ne Macke haben?

Ziemlich unwahrscheinlich.

von Georg T. (microschorsch)


Angehängte Dateien:

Lesenswert?

Hi,

ich hab jetzt mal einen minimalistischen Speichertest geschrieben und 
das gesamte Testresultat Byte für Byte ins angehängte File gedumped. 
demzufolge wären von meinen 32768Byte 2944Byte nicht zu gebrauchen. 
Immerhin hätte ich einen zusammenhängenden Speicherbereich gefunden, der 
für meine Anwendung vermutlich groß genug wäre.

Verstehen möchte ich das aber trotzdem gerne.

Ich hab einen Logik-Analysator an einige Beinchen gehängt, ohne das 
jetzt auswerten zu wollen, es ist definitiv Aktivität auf den Leitungen.

Mir ist aufgefallen, dass ich beim Bestücken vielleicht mit dem 
Flußmittelpaste etwas großzügig war. Meint ihr sowas könnte einen 
Einfluß haben?

Schorsch

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Georg T. schrieb:
> Ich hab einen Logik-Analysator an einige Beinchen gehängt, ohne das
> jetzt auswerten zu wollen, es ist definitiv Aktivität auf den Leitungen.

Dann kannst du ja mal ein paar Schreibvorgänge analysieren, bspw.
auf 0x13ff vs. 0x17ff oder 0x1400 vs. 0x1800.  Du solltest natürlich
unbedingt auch die /WR- und /RD-Signale mit tracen.

> Mir ist aufgefallen, dass ich beim Bestücken vielleicht mit dem
> Flußmittelpaste etwas großzügig war. Meint ihr sowas könnte einen
> Einfluß haben?

Das hat eher Einfluss auf den Reststrom, da das Zeug Feuchtigkeit
aufnimmt.  Damit es aber zum Übersprechen zwischen Digitalleitungen
kommt, müsste schon ziemlich viel Oxid drin gelöst worden sein.

von Falk B. (falk)


Lesenswert?

@ Georg T. (microschorsch)

>ich hab jetzt mal einen minimalistischen Speichertest geschrieben und
>das gesamte Testresultat Byte für Byte ins angehängte File gedumped.

Was mal sinnlos ist, wenn man nicht Soll- und Istwerte hat!

Ausserdem sucht man so einen Adressfehler anders!

>Immerhin hätte ich einen zusammenhängenden Speicherbereich gefunden, der
>für meine Anwendung vermutlich groß genug wäre.

Murks!

>Ich hab einen Logik-Analysator an einige Beinchen gehängt, ohne das
>jetzt auswerten zu wollen, es ist definitiv Aktivität auf den Leitungen.

Ja, aber die Frage ist, ob die Leitungen UNABHÄNGIG schalten können!

>Mir ist aufgefallen, dass ich beim Bestücken vielleicht mit dem
>Flußmittelpaste etwas großzügig war. Meint ihr sowas könnte einen
>Einfluß haben?

Definitiv.

Mach es so wie Jörg vorgeschlagen hat. Betreibe die Ports für den SRAM 
manuell. Wackel mit allen Bits EINZELN!

Datenbus, 0x80, 0x00 im Wechsel mit 1s, da kann man mit dem Multimetetr 
messen! Es darf NUR D7 wackeln! Alle anderen Leitungen müssen LOW sein. 
Das Ganze auch mit dem High Adress Byte und dem Latch, sprich, Daten auf 
dem Datenbus ausgeben, im Latch speichern, messen.

Damit sollte man einen Kurzschluss recht schnell finden.

von Georg T. (microschorsch)


Lesenswert?

Falk Brunner schrieb:
> Betreibe die Ports für den SRAM
> manuell. Wackel mit allen Bits EINZELN!

Hallo,

gute Nachricht und schlechte Nachricht.

Habe das alte Board wieder in Betrieb genommen, leider war es durch 
einen Transportschaden beschädigt worden, hier funktioniert der XMEM 
Test ohne Fehler!!! Das deutet für mich sehr sehr deutlich auf einen 
Hardwaredefekt des neuen Board hin

Das neue Board habe ich nach Falks Methode nun Pin für Pin und Bit für 
Bit durchgetoggelt. Sogar WR, RD und ALE habe ich gemessen. Ich mit 
einem Scope gemessen, bin mir sicher, dass es keine kruzschlüsse gibt. 
Ich würde sagen, entweder RAM oder Latch ist Schrott

Worauf würdet ihr tippen?

Schorsch

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Georg T. schrieb:
> Worauf würdet ihr tippen?

RAM.  Wenn das Latch gar nicht latchen würde, dann würde keine
Speicherzelle funktionieren.  Einzelne tun aber.  Wenn das Latch
nur zu langsam ist, solltest du das mit geringerem Takt oder mehr
wait states zum Funktionieren bekommen.

von Georg T. (microschorsch)


Lesenswert?

Der RAM wars

jetzt gehts !!! freu endlich....


Vielen Dank an alle für Eure vielen Ratschläge und die technische 
Expertise

Gruß und schönes Wochenende

Schorsch

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.