www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Kompiler vergibt Speicheradresse mehrmals?!


Autor: Alex Bürgel (Firma: Ucore Fotografie www.ucore.de) (alex22) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Guten Morgen!

Ich benutze Freescale Codewarrior 6.2 und programmiere einen 
MC9S08-Controller in C.
Für ein Programm möchte ich globale Variablen benutzen.

Mein Vorgehen war folgendes:
volatile int GLOBALE_VARIABLE;

void main(void){
   int LOKALE_VARIABLE;
   //... beliebiger Code
}

Leider funktionierte mein Code nicht wie gewünscht. Mit Hilfe des 
Debuggers stellte ich fest, dass sowohl die lokale Variable als auch die 
globale Variable beide vom Kompiler an Speicheradresse 0x100 abgelegt 
wurden und sich demnach gegenseitig beeinflussten.

Daher:
Wie macht man sowas richtig??

Danke & Grüße,
Alex

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Alex Bürgel wrote:

> Wie macht man sowas richtig??

Erst einmal indem man alle relevante Information postet. Damit man das 
selber nachvollziehen kann, und/oder damit man anhand des Quellcodes und 
des Assembler-Listings erkennen kann was da abläuft. Nachvollziehen 
können es aber nur Anwender dieses Compilers, und das sind hier m.W. 
nicht allzu viele.

Mit der von dir geposteten Info können ausschliesslich jene etwas 
anfangen, die schon einmal über exakt dieses Problem bei exakt diesem 
Compiler gestolpert sind.

Merksatz: 99% aller sogenannten Compilerfehler sind Missverständnisse 
oder Anwenderfehler.

Autor: Alex Bürgel (Firma: Ucore Fotografie www.ucore.de) (alex22) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Merksatz: 99% aller sogenannten Compilerfehler sind Missverständnisse
>oder Anwenderfehler.

Natürlich, dass vermute ich hier auch. Ich denke nicht, dass es sich um 
einen Fehler des Compilers handelt, sondern, dass ich den Compiler 
falsch anwende.

>Erst einmal indem man alle relevante Information postet.
Ich hatte gehofft diese Bedingung erfüllt zu haben. Welche weiteren 
Informationen werden denn, deiner Meinung nach, benötigt?

Schöne Grüße,
Alex

Autor: Peter (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
einfach mal dem erzeugten Assembler code posten.

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Alex Bürgel wrote:

> Ich hatte gehofft diese Bedingung erfüllt zu haben. Welche weiteren
> Informationen werden denn, deiner Meinung nach, benötigt?

Der komplette Quellcode der betreffenden Funktion, ggf. mit zusätzlicher 
Information die notwendig ist um den Code nachzuvollziehen. Und zwar 
soweit minimiert, dass nur ausschliesslich der Code drin ist, der der 
mit dem Fehler zu tun hat und alles Überflüssige entfernt wurde. Dieser 
Code muss aber noch compilierbar und der Fehler darin reproduzierbar 
sein. Nennt man einen "Testcase".

Das Listing des so erzeugten Assembler-Codes.

Ein Map-File mit Adressbelegung der Variablen und der Speicherbereiche.

Und wenn sich dann doch herausstellt, dass es ein echter Compilerfehler 
ist, dann war diese Arbeit erst recht nicht umsonst. Denn jeder Support 
seitens des Herstellers oder Programmierers, ob von Freeware oder 
Löhnware, wird eben genau diese Information benötigen. Die brauchen dann 
aber noch die Information wie compiliert wurde, also Kommandozeile, 
Optionen, ...

Autor: Alex Bürgel (Firma: Ucore Fotografie www.ucore.de) (alex22) Benutzerseite
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Nun gut, vielleicht hilft es ja...

C-Code in der main.c
//included files
#include <hidef.h> /* for EnableInterrupts macro */
#include "derivative.h" /* include peripheral declarations */
#include <string.h>
#include <stdio.h>

//definitions
#define BUS1          PTBD_PTBD6
#define BUS2          PTBD_PTBD7
#define BUS3          PTDD_PTDD0
#define BUS4          PTDD_PTDD1
#define BUS5          PTDD_PTDD2
#define BUS6          PTDD_PTDD3
#define BUS7          PTDD_PTDD4
#define BUS8          PTDD_PTDD5
#define BUS9          PTDD_PTDD6
#define BUS10         PTDD_PTDD7
#define MASTER_READY  PTBD_PTBD4
#define UN252         PTBD_PTBD3
#define LED1          PTAD_PTAD4
#define RESET_LINE    PTED_PTED0

//State-definitions
#define IDLE          0     //permanently measure cell-voltages, stack-voltage and temperatures
#define SET_EQU_RESISTORS 1 //Toggle equalization resistors on/off

//function declarations
void MCU_init(void);                            /* Device initialization function declaration */
int RS232_send_char(char);                      //Function to send one character
int RS232_send_string(char *);                  //Function to send a string
char RS232_rec_char(void);                      //Function to read one character
unsigned int get_adc_value(int);                //Function to read one ADC-value (select channel)
void pause(int);                                //Function to generate a pause for Nx125ns
char send_command_to_slave(char, char, char);   //Function to send one command to the slave 
void reset_slave(char);                         //Function to reset the slave



//global variables
volatile char RS232RXCHAR = 0;
volatile unsigned int STATE = IDLE;
volatile unsigned int EQU_RESISTORS[5] = {0,0,0,0,0};


//main function
void main(void) {

  unsigned int cellVoltages[40];
  int i,j,k,error_counter;
  
  MCU_init();       //call Device Initialization
  EnableInterrupts; //global interrupt enable
  
  for(i=0;i<40;i++)
    cellVoltages[i] = 0;   
  
  for(;;){
     
    if(RS232RXCHAR != 0){
      
      RS232_send_char(RS232RXCHAR);   
      RS232RXCHAR = 0;
      STATE = IDLE;
    }
     //State-Machine
      switch(STATE){
        case IDLE:{
          for(i=1;i<39;i++){
            if(!send_command_to_slave(i, 1, 0)){
              cellVoltages[i] = get_adc_value(0); 
            } 
          }    
          break;  
        }
        case SET_EQU_RESISTORS:{
          error_counter = 0;
          k = 1;
          for(i=0;i<5;i++){
            for(j=1;j<=128;j<<=1){
              if(j&EQU_RESISTORS[i]){                
                if(send_command_to_slave((i+1)*k, 1, 1)){
                  error_counter++;
                }
              } else{
                if(send_command_to_slave((i+1)*k, 0, 1)){
                  error_counter++;
                }
              }
                
            }
            k++;
          }
          STATE = IDLE;
          break;
        }
        default:{
          break;
        }
      }  
               
    __RESET_WATCHDOG(); //reset the watchdog
  }
}

Im Anhang ein Map-File des Projekts.

Leider weis ich nicht, wie ich den Assembler-Code bzw. das 
Memory-Mapping kopieren könnte...

Grüße,
Alex

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

Bewertung
0 lesenswert
nicht lesenswert
Super.
Und wir dürfen jetzt abspecken oder was?

Aber seis drum. Um welche Variablen handelt es sich und an welcher 
Stelle bist du im Debugger an der du denkst, dass der Compiler 2 
Variablen übereinandergelegt hat (was ich mir ehrlich gesagt überhaupt 
nicht vorstellen kann, denn globale Variablen werden komplett anders 
behandelt als lokale Variablen´)

Autor: Alex Bürgel (Firma: Ucore Fotografie www.ucore.de) (alex22) Benutzerseite
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Ganz konkret befinden sich hier die Variablen:

EQU_RESISTORS[1] (global, vor main() definiert)

und

i (lokal, in main() definiert)

beide an Speicheradresse 0x105.

Im Anhang befindet sich ein Screenshot der Debugging-Software.
Der Screenshot ist zusammengeschnitten aus zwei einzelnen, da man 
jeweils nur eine Variable markieren kann.
Das Fenster "Data2" zeigt die in main() lokal angelegten Variablen, das 
Fenster "Data1" zeigt die globalen Variablen.

Schöne Grüße,
Alex

Autor: Alex Bürgel (Firma: Ucore Fotografie www.ucore.de) (alex22) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Und wir dürfen jetzt abspecken oder was?
Um dies zu vermeiden hatte ich zunächst nur den Pseudo-Code gepostet und 
versucht das Problem aufs Wesentliche zu reduzieren. Jedoch hatte dann 
jemand nach dem "echten" Quelltext gefragt...

Autor: Severino R. (severino)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Alex Bürgel wrote:
>>Und wir dürfen jetzt abspecken oder was?
> Um dies zu vermeiden hatte ich zunächst nur den Pseudo-Code gepostet und
> versucht das Problem aufs Wesentliche zu reduzieren. Jedoch hatte dann
> jemand nach dem "echten" Quelltext gefragt...

>>> Der komplette Quellcode der betreffenden Funktion, ggf. mit zusätzlicher
>>> Information die notwendig ist um den Code nachzuvollziehen. Und zwar
>>> soweit minimiert, dass nur ausschliesslich der Code drin ist, der der
>>> mit dem Fehler zu tun hat und alles Überflüssige entfernt wurde. Dieser
>>> Code muss aber noch compilierbar und der Fehler darin reproduzierbar
>>> sein. Nennt man einen "Testcase".

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

Bewertung
0 lesenswert
nicht lesenswert
Alex Bürgel wrote:
>>Und wir dürfen jetzt abspecken oder was?
> Um dies zu vermeiden hatte ich zunächst nur den Pseudo-Code gepostet und
> versucht das Problem aufs Wesentliche zu reduzieren. Jedoch hatte dann
> jemand nach dem "echten" Quelltext gefragt...

'echten Quelltext', den du solange abspecken sollst, bis alles 
Überflüssige raus ist, der Fehler aber noch drinnen ist!

Stell dir einfach vor, wir wären die Hotline deines Compiler-Herstellers 
und du musst ins in die Lage versetzen, das selbst zu kompilieren. Und 
zwar das einfachst mögliche Programm, welches den Fehler gerade noch 
zeigt.

Drum ist Pseudocode ungeeignet.
Drum ist auch dein Programm in seiner Langform ungeeignet. Denn kein 
Entwickler wird sich durch Unmengen von Assembler-Code quälen, der 
überhaupt nichts zum Problem beiträgt.

Und wenn wir schon dabei sind:
Sind irgendwelche Optimierungen eingeschaltet?

Denk immer dran: Du musst uns in die Lage versetzen, zumindest 
theoretisch, dein Program hier bei uns zu compilieren. Mit allen 
Einstellungen! Wenn ich das hier compilieren würde, müsste ich genau das 
gleiche EXE erhalten können wie du!

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

Bewertung
0 lesenswert
nicht lesenswert
Auf welchen Adressen liegen eigentlich j und k
(nur um mal ein Gefühl dafür zu bekommen, welcher Wertebereich für i 
angemessen wäre).

Autor: Mike (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mal gecheckt, ob der RAM-Speicher ausreicht. Meiner Erinnerung nach hat 
der MC9S08 nur 256 byte RAM. Da könnte es knapp werden und der Heap mit 
dem statischen Speicher kollidieren. Die Addresse 0x105 sieht irgendwie 
verdächtig aus. Vielleicht irre ich mich, ich kenne den Controller zu 
wenig.

Gruss
Mike

Autor: Eckhard (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

schau mal in dein PRM File nach STACKSIZE.
Lokale Variablen werden auf dem Stack abgelegt, also auch die aus der 
Main Funktion.

 unsigned int cellVoltages[40];
  int i,j,k,error_counter;

Das sind dann 88 Bytes. die du dafür allein auf dem Stack brauchst.

Also entweder die Stacksize entsprechend anpassen oder STACKSIZE weg und 
den Stackpointer per STACKTOP auf die oberste Speicherstelle setzen.


Eckhard

Autor: Alex Bürgel (Firma: Ucore Fotografie www.ucore.de) (alex22) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ok, reduzierter Code:
//included files
#include <hidef.h> /* for EnableInterrupts macro */
#include "derivative.h" /* include peripheral declarations */
#include <string.h>
#include <stdio.h>


//global variables
volatile char RS232RXCHAR /*@0x80*/ = 0;
volatile unsigned int STATE /*@0x81*/= IDLE;
volatile unsigned int EQU_RESISTORS[5] /*@0x83*/ = {0,0,0,0,0};


//main function
void main(void) {

  unsigned int cellVoltages[40];
  int i,j,k,error_counter;
  
  for(;;);
}

Der Controller MC9S08DZ60 besitzt 4096Byte RAM, von Speicheradresse 0x80 
bis 0x107F.

Die anderen von mir angelegten Variablen befinden sich:

RS232RXCHAR   0x100
STATE         0x101 bis 0x102
EQURESISTORS  0x103 bis 0x10C

i             0x105 (=EQURESISTORS[1])
j             0x107 (=EQURESISTORS[2])
k             0x10B (=EQURESISTORS[4])
error_counter 0x109 (=EQURESISTORS[3])

Autor: Alex Bürgel (Firma: Ucore Fotografie www.ucore.de) (alex22) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Also entweder die Stacksize entsprechend anpassen oder STACKSIZE weg und
>den Stackpointer per STACKTOP auf die oberste Speicherstelle setzen.

Leider kenne ich mich mit der Funktionsweise von Compilern/Linkern und 
dem Stack nicht so gut aus... hier ist die von Eckhard angesprochene 
prm-Datei:
/* This is a linker parameter file for the mc9s08dz60 */

NAMES END /* CodeWarrior will pass all the needed files to the linker by command line. But here you may add your own files too. */

SEGMENTS /* Here all RAM/ROM areas of the device are listed. Used in PLACEMENT below. */
    Z_RAM                    =  READ_WRITE   0x0080 TO 0x00FF;
    RAM                      =  READ_WRITE   0x0100 TO 0x107F;
    ROM                      =  READ_ONLY    0x1900 TO 0xFFAD;
    ROM1                     =  READ_ONLY    0x1080 TO 0x13FF;
    EEPROM                   =  READ_ONLY    0x1400 TO 0x17FF;
 /* INTVECTS                 =  READ_ONLY    0xFFC0 TO 0xFFFF; Reserved for Interrupt Vectors */
END

PLACEMENT /* Here all predefined and user segments are placed into the SEGMENTS defined above. */
    DEFAULT_RAM                         /* non-zero page variables */
                                        INTO  RAM;

    _PRESTART,                          /* startup code */
    STARTUP,                            /* startup data structures */
    ROM_VAR,                            /* constant variables */
    STRINGS,                            /* string literals */
    VIRTUAL_TABLE_SEGMENT,              /* C++ virtual table segment */
    DEFAULT_ROM,
    COPY                                /* copy down information: how to initialize variables */
                                        INTO  ROM; /* ,ROM1: To use "ROM1" as well, pass the option -OnB=b to the compiler */

    _DATA_ZEROPAGE,                     /* zero page variables */
    MY_ZEROPAGE                         INTO  Z_RAM;
END


STACKSIZE 0x50

//VECTOR 0 _Startup     /* Reset vector: this is the default entry point for an application. */


Darf ich nun einfach die STACKSIZE (beliebig) vergrößern?
Oder wäre eine andere Lösung zu bevorzugen?

Autor: Eckhard (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

bei dem Compiler funktioniert das so :

Oberhalb der Variablen wird der Stack in der angegebenen Größe gesetzt.
Hier also Hex 50 was 80 Byte sind. Der Stack kann natürlich nicht größer 
als der RAM Speicher sein. Auf jeden Fall reichen die 80 Bayte nicht 
aus. Du kannst die Größe aber ruhig ein wenig heraufschrauben. Bei 4K 
ist da noch einiges an Luft. Nimm 0x180 als Stacksize und as sollte 
passen.

Du könntest dann deien Globalen Variablen noch in die Zeropage packen.


#pragma DATA_SEG __MY_ZEROPAGE

//global variables
volatile char RS232RXCHAR /*@0x80*/ = 0;
volatile unsigned int STATE /*@0x81*/= IDLE;
volatile unsigned int EQU_RESISTORS[5] /*@0x83*/ = {0,0,0,0,0};

#pragma DATA_SEG DEFAULT


Eckhard

Autor: Alex Bürgel (Firma: Ucore Fotografie www.ucore.de) (alex22) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Eckhard,

Vielen Dank!!
Du hast damit meine Probleme gelöst und es mir so ausführlich erklärt, 
dass ich die Ursache und die Lösung verstanden habe.

Eine kurze frage hätte ich noch:

Du erwähntest weiter oben den Befehl STACKTOP. Könntest du mir kurz 
erläutern wozu er dient und wann es sinnvoll ist ihn anzuwenden?


Danke auch an alle Anderen die an dieser regen Diskussion teilgenommen 
haben!

Schöne Grüße,
Alex

Autor: Eckhard (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,


mit STACKTOP kannst Du den Stackpointer direkt an eine Adresse setzen, 
z.B. an das obere Ende des RAMs. Wann genau das eine oder andere 
vorteilhafter ist weiß ich aber auch nicht genau. Mit STACKTOP hat man 
dann gleich die Maximale Größe des Stacks.



Eckhard

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Alex Bürgel wrote:

> Um dies zu vermeiden hatte ich zunächst nur den Pseudo-Code gepostet und
> versucht das Problem aufs Wesentliche zu reduzieren. Jedoch hatte dann
> jemand nach dem "echten" Quelltext gefragt...

Wenn du zum Arzt gehst, und die Diagnose gleich mitbringst, dann hast du 
exzellente Chancen auf eine falsche Behandlung.

Wenn du deinen Pseudocode oben nochmal ansiehst, dann wirst du 
feststellen, dass alles was man zur Erkennung des Problems benötigte 
darin fehlte.

Autor: SoLaLa (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ich möchte auch noch was lernen :-)
den Fehler oben verstehe ich so: der Compiler legt die globalen 
Variablen im RAM ab und reserviert einen relativ kleinen Stack weil er 
meint "is ja nur n kleines Programm...", wird dann in der main() mit dem 
Feld
unsigned int cellVoltages[40];
überrascht und der Stack kollidiert daraufhin mit dem Bereich der 
globalen Variablen.
Physikalisch liegt das aber alles im RAM des Controllers.
Die cellVoltages[40] werden im main() aber doch dauerhaft benutzt und 
dieser Platz würde zur Laufzeit des Programms sowieso nie freigegeben 
werden (oder hab ich hier nen Denkfehler?).
Wäre es da nicht sinnvoll cellVoltages[40] ebenfalls in die globalen 
Variablen zu übernehmen und müßte damit der Fehler nicht ebenfalls weg 
sein? Hätte das irgendwelche anderen Nachteile (von denen ich 
zugegebenermaßen keine Ahnung habe)?

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
SoLaLa wrote:

> den Fehler oben verstehe ich so: der Compiler legt die globalen
> Variablen im RAM ab und reserviert einen relativ kleinen Stack weil er
> meint "is ja nur n kleines Programm...",

Die Programmgrösse ist ihm egal, den Stackverbrauch abzuschätzen ist 
dein Problem, möglicherweise mit etwas Hilfestellung. Ich bin mir 
ziemlich sicher, dass sich zu diesem Thema ein paar Worte in Handbuch 
des Compilers finden lassen.

> Wäre es da nicht sinnvoll cellVoltages[40] ebenfalls in die globalen
> Variablen zu übernehmen und müßte damit der Fehler nicht ebenfalls weg
> sein?

Korrekt. Beim '08 wäre ich auch kein bischen überrascht, wenn das zu 
besseren Code führt. Die meisten 8bitter tun sich mit global/statisch 
allozierten Arrays ohnehin leichter. Kannst auch "static" davor 
schreiben.

Autor: Eckhard (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

der Nachteil der Globalen Variable wäre eben genau das die Kapselung weg 
ist.
Die Performance wird durch globale Variablen auch nicht besser. Der 
HC(S)08 kann den Stackpointer zur Adressierung als Indexregister 
benutzen und somit sehr effektiv auf die Lokalen Variablen zugreifen.


Eckhard

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Deshalb ja auch der Tip mit "static" davor. Hat die gleiche Adressierung 
wie global, ist aber gekapselt. Und dass main() rekursiv verwendet wird, 
das kann man wohl ausschliessen ;-).

Und er kann zwar das Indexregister benutzen, aber ich glaube kaum, dass 
der '08 eine Adresse aus der Summe zweier Indexregister (plus Konstante) 
im Befehl bilden kann. Was er aber für ein normales lokales Array 
braucht.

Bei Skalaren hast du recht, bei Arrays sieht das anders aus. 
Globale/statische Arrays adressieren sich über die Summe vom Index und 
der statischen Adresse. Und das wiederum kann er wahrscheinlich im 
Befehl.

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.