Forum: Mikrocontroller und Digitale Elektronik Abfrage effizienter gestallten


von C. H. (hedie)


Lesenswert?

Hallo liebe Community

ich habe derzeit folgende Code Zeilen
1
if(!strcmp(ucTemp,"version"))     main_token.befehl = VERSION;  //Erster Befehl entschlüsseln und sichern
2
  if(!strcmp(ucTemp,"output_on"))   main_token.befehl = OUTPUTON;  //Erster Befehl entschlüsseln und sichern
3
  if(!strcmp(ucTemp,"output_off"))   main_token.befehl = OUTPUTOFF;  //Erster Befehl entschlüsseln und sichern
4
  if(!strcmp(ucTemp,"pause"))     main_token.befehl = PAUSE;  //Erster Befehl entschlüsseln und sichern
5
  if(!strcmp(ucTemp,"end"))       main_token.befehl = END;  //Erster Befehl entschlüsseln und sichern

Ich wollte fragen, ob es eine möglichkeit gibt diese effizienter zu 
gestallten, so das 1. der Code schneller abgearbeitet wird und 2. nicht 
jede bedingung geprüft werden muss...

Switch fällt meines wissens ja aus, da ich damit keine Strings 
vergleichen kann oder?

Danke schonmal

von Falk B. (falk)


Lesenswert?

@Claudio Hediger (hedie)

Einfach mit Tante else
1
if(!strcmp(ucTemp,"version")) main_token.befehl = VERSION;  //Erster Befehl entschlüsseln und sichern
2
  else if(!strcmp(ucTemp,"output_on"))   main_token.befehl = OUTPUTON;  //Erster Befehl entschlüsseln und sichern
3
  else if(!strcmp(ucTemp,"output_off"))   main_token.befehl = OUTPUTOFF;  //Erster Befehl entschlüsseln und sichern
4
  else if(!strcmp(ucTemp,"pause"))     main_token.befehl = PAUSE;  //Erster Befehl entschlüsseln und sichern
5
  else if(!strcmp(ucTemp,"end"))       main_token.befehl = END;  //Erster Befehl entschlüsseln und sichern

>Ich wollte fragen, ob es eine möglichkeit gibt diese effizienter zu
>gestallten, so das 1. der Code schneller abgearbeitet

Man könnte noch so arbeiten, dass man zuerst den 1. Buchstaben prüft und 
nur im Erfolgsfall weiter vergleicht. Bringt hier aber eher nicht viel, 
ist ja für ein lahmes Userinterface, wo sowieso nicht so viele Befehle 
reinkommen.

> wird und 2. nicht
>jede bedingung geprüft werden muss...

Siehe oben.

>Switch fällt meines wissens ja aus, da ich damit keine Strings
>vergleichen kann oder?

Stimmt.

MFG
Falk

von Peter (Gast)


Lesenswert?

Claudio Hediger schrieb:

ein wenig besser geht es auf jeden fall
1
if(!strcmp(ucTemp,"version")) {  
2
   main_token.befehl = VERSION;  
3
} else if(!strcmp(ucTemp,"output_on")) {
4
   main_token.befehl = OUTPUTON;
5
} else if (!strcmp(ucTemp,"output_off")) {
6
   main_token.befehl = OUTPUTOFF;
7
} else if(!strcmp(ucTemp,"pause")) {
8
   main_token.befehl = PAUSE; 
9
} else if(!strcmp(ucTemp,"end")) {
10
   main_token.befehl = END; 
11
}

damit wird gleich abgebrochen wenn ein vergleich passt.

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Prüfen auf ersten Buchstaben, bei output_on off den letzen... und 
sinnvolle Kommentare... Oder elseif kette..

von C. H. (hedie)


Lesenswert?

Vielen Dank für eure Antworten...

von Tim T. (tim_taylor) Benutzerseite


Lesenswert?

Müssen es unbedingt Strings sein?

Integer kann man viel schneller vergleichen und sind dazu noch switch 
kompatibel.

In dem von dir geposteten Beispiel könnte man auch die Stringlänge mit 
einem Switch auswerten.

von David M. (md2k7)


Lesenswert?

Du musst beim else-if bedenken, dass im Worst-Case, z.B. bei "end", 
trotzdem vorher alle anderen Vergleiche laufen.

Liegt der Code in einer Interrupt-Routine, oder warum muss der unbedingt 
schneller laufen? Soo langsam ist das Vergleichen der paar Zeichen ja 
nicht.

Gruß
David

von C. H. (hedie)


Lesenswert?

Tim T. schrieb:
> Integer kann man viel schneller vergleichen und sind dazu noch switch
> kompatibel.

Da hast du recht... Eventuell werde ich später noch zu Integer 
wechseln...
Ist ja schnell erledigt...


David Madl schrieb:
> Liegt der Code in einer Interrupt-Routine, oder warum muss der unbedingt
> schneller laufen? Soo langsam ist das Vergleichen der paar Zeichen ja
> nicht.

Hmmm naja... sagen wir mal sooo... Das gehört zu einem kleinen 
Interpreter...
Und je schneller das Teil Interpretiert desto besser :)

Bitte nicht gleich über mich her ziehn... Der code ist noch nicht 
fertig...
Es ist noch nicht mal alpha version :)

von Torsten K. (ago)


Lesenswert?

Claudio Hediger schrieb:
> Switch fällt meines wissens ja aus, da ich damit keine Strings
> vergleichen kann oder?

Der Aufwand lohnt sich aber nicht, vor allem bei nur ein paar Befehlen 
würde ich bei if... else if... else if... und strcmp bleiben.

int tmplen = strlen(ucTemp);
switch (ucTemp[0])
{
  case 'v': // v)ersion
    main_token.befehl=VERSION;
    break;
  case 'o': //output_oXX
    if (tmplen==9 && ucTemp[8]=='n')
      main_token.befehl=OUTPUTON;
    else if (tmplen==10 && ucTemp[9]=='f')
      main_token.befehl=OUTPUTOFF;
    break;
  ...
}

Bei einer größeren Anzahl Befehle lohnt sich dann schon mal eine 
Schleife über ein Array, und die Funktionen dann direkt anspringen

struct
{
        char *cfgword;
        void (*func)(char *);
} token_cmds[] =
{
        { "version",    cfg_tok_version,  },
        { "output_on",  cfg_tok_outputon, },
        { "output_off", cfg_tok_outputoff,},
        { "pause",      cfg_tok_pause,    },
        { "end",        cfg_tok_end,      },
        { NULL,         NULL,             }
};
...
for (i = 0; token_cmds[i].cfgword; i++)
  if (!strcmp(ucTemp, token_cmds[i].cfgword))
    {
      token_cmds[i].func(line);
      return; // oder break

Oder alternativ statt die Funktionszeiger zu speichern, speicherst du 
die Tokencodes...

von Torsten K. (ago)


Lesenswert?

Claudio Hediger schrieb:
> Hmmm naja... sagen wir mal sooo... Das gehört zu einem kleinen
> Interpreter...
> Und je schneller das Teil Interpretiert desto besser :)

Ahhh ja, ok.. Dannn mach es so, daß du deine Befehlstabelle (ich nehme 
mal an, da kommen ein paar mehr rein...) alphabetisch sortiert in ein 
Array legst, dann kannst Du eine binäre Suche nutzen:

struct
{
   char *name;
   void (*function)(xxx funktionsargumente); // anpassen
   ...
} myKnownToken[]
{
   { "end",        cfg_tok_end,      },
   { "output_on",  cfg_tok_outputon, },
   { "output_off", cfg_tok_outputoff,},
   { "pause",      cfg_tok_pause,    },
   { "version",    cfg_tok_version,  },
   { NULL,         NULL,             }
};

/* find token code in table and return index, or -1 if not found */
int find_mytoken(char *token)
{
  int low, med, high, weight;

  low = 0;
  high = XXX; // number of tokens in table (number of last token)
  while (high > low)
  {
    med = low + (high - low) / 2;
    weight = strcmp(command, myKnownToken[med].name); // strcasecmp ?
    if (weight < 0)
      high = med;
    else if (weight > 0)
      low = med + 1;
    else
      return med;     /* if weight is 0 -> command found! */
  }
  return -1; // not found
}

Dann liefert "i = find_mytoken(ucTemp);" dir also den Index in der 
Tokentabelle zurück, oder -1 wenn es den Befehl nicht gibt.
Nun kannst du mit "myKnownToken[i].function(...);" Die entsprechende 
Funktion aufrufen wenn i positiv ist.

Das da oben ist zwar so Pseudocode, aber ich habe den selber so ähnlich 
in einem irc-bot eingesetzt (damals...)

von C. H. (hedie)


Lesenswert?

Vielen Dank für deine Bemühung...

Das klingt sehr interessant... Viel besser als das was ich derzeit habe 
:)

Wie sieht den eine beispiel funktion aus?

würde

void cfg_tok_end(void)
{
blabla

}

funktionieren?

von Markus E. (engelmarkus)


Lesenswert?

Natürlich würde die funktionieren... aber du wirst deinen einzelnen 
Befehle ja Parameter mitgeben wollen. Wahrscheinlich wäre es besser, 
wenn du jeder einzelnen Funktion ein Array auf die Eingabe-Parameter 
übergibst und die Methode sucht sich die einzelnen Sachen dann zusammen.
Wie die Methoden aussehen müssen, hängt ja von dem zweiten Eintrag in 
dem Struct oben ab...

von C. H. (hedie)


Lesenswert?

Markus E. schrieb:
> Natürlich würde die funktionieren... aber du wirst deinen einzelnen
> Befehle ja Parameter mitgeben wollen. Wahrscheinlich wäre es besser,
> wenn du jeder einzelnen Funktion ein Array auf die Eingabe-Parameter
> übergibst und die Methode sucht sich die einzelnen Sachen dann zusammen.
> Wie die Methoden aussehen müssen, hängt ja von dem zweiten Eintrag in
> dem Struct oben ab...

Das ist allerdings eine gute idee... Ich werde dem mal nachgehen...


Eine andere Möglichkeit Benutzer Code auszuführen, nebst einem 
Interpreter ist ja, den Code in den Externen Speicher des Controllers zu 
laden...

Ich gehe davon aus, das dies wesentlich Komplexer und aufwändiger 
umzusetzen ist als ein Interpreter oder?

von Markus E. (engelmarkus)


Lesenswert?

Also wenn ich dich richtig verstehe, dann wird das nicht gehen. Welchen 
µC benutzt du? AVRs basieren auf der Havard-Architektur, das heißt, sie 
haben getrennte Programm- und Datenräume. Du kannst auf so einem µC kein 
Programm aus irgendwelchem externen Speicher (meinst du RAM oder 
EEPROM?) ausführen, sondern nur aus dem Flash. Die anderen beiden sind 
reine Datenspeicher.
Hiern noch das obige Beispiel etwas ausgebaut:
1
typedef void Funktion(const char* parameter[]);
2
3
void funktion_end(const char* p[]) {
4
  // Hier das Array prüfen, ob genug und vor allem richtige Parameter drin sind.
5
}
6
7
struct {
8
   char *name;
9
   Funktion* f;
10
} myKnownToken[] = {
11
   { "end",        funktion_end,      },
12
   { NULL,         NULL,             }
13
};
14
15
int main() {
16
  // Parameter hier ermitteln...
17
  const char* test[] = {"42", "ein string"};
18
  // Beispielaufruf einer Funktion aus dem Array oben.
19
  myKnownToken[0].f(test);
20
...

von C. H. (hedie)


Lesenswert?

Markus E. schrieb:
> Also wenn ich dich richtig verstehe, dann wird das nicht gehen. Welchen
> µC benutzt du?

Ich verwende einen STM32 mit Neumann Architektur...
Leider befürchte ich, das ich das ohne Hilfe niemals schaffen würde...

Markus E. schrieb:
> Hiern noch das obige Beispiel etwas ausgebaut:

Vielen Dank!
Ich werde es morgen bzw. heute testen :)

von Markus E. (engelmarkus)


Lesenswert?

Alternativ könntest du natürlich auch eine virtuelle Maschine wie die 
von Java schreiben, sodass auf deinem PC bereits eine Syntaxprüfung und 
das alles durchgeführt und ein Bytecode erzeugt wird, den du nur noch 
auf dem µC ausführen musst.
Das andere, was du vorhin gemeint hast, würde schon auch gehen. Dazu 
schreibst du sozusagen im "Betriebssystem" auf dem µC ein paar 
Funktionen, die das Programm später benutzen darf, und legst am Anfang 
vom Flash in einer festgelegten Reihenfolge Zeiger darauf ab. Jetzt 
schreibst du ein Programm, das diese Reihenfolge kennt und somit diese 
Funktionen benutzen kann. Das kompilierst du entweder 
position-independent oder mit einer festen Basisadresse. Dieses Programm 
muss dein Code auf dem µC dann nur noch zur Laufzeit laden und 
hinspringen. Oder so... du weißt schon, was ich mein ;) .

von C. H. (hedie)


Lesenswert?

Markus E. schrieb:
> sodass auf deinem PC bereits eine Syntaxprüfung und
> das alles durchgeführt und ein Bytecode erzeugt wird, den du nur noch
> auf dem µC ausführen musst.


Spannender Ansatz...

Meinst du mit Bytecode ein Code welcher ich noch durch einen Interpreter 
schicken muss oder ByteCode der direkt vom Mikrocontroller verstanden 
wird?

von Torsten K. (ago)


Lesenswert?

Bei einem AVR-Controller sehe ich allerdings auch schwarz... vergiss 
meine Beiträge dann am besten ganz schnell wieder ;-) sorry hab nicht 
auf das "Microcontroller" im Unterforum geachtet.

Du wirst auf jeden Fall Speicher sparen wollen/müssen. Ich würde die 
Befehle kürzen, und nur die ersten zwei und letzten zwei Zeichen 
prüfen/vergleichen:

"veon" = version
"ouon" = output_on
"ouff" = output_off
"pase" = pause
"ennd" = end

Die suchst du dann (notfalls auch mit binärer Suche) in der 
Tabelle/Array, und mit dem Index kannst du dann mit switch/case arbeiten

von C. H. (hedie)


Lesenswert?

Markus E. schrieb:
> Das andere, was du vorhin gemeint hast, würde schon auch gehen. Dazu
> schreibst du sozusagen im "Betriebssystem" auf dem µC ein paar
> Funktionen, die das Programm später benutzen darf, und legst am Anfang
> vom Flash in einer festgelegten Reihenfolge Zeiger darauf ab. Jetzt
> schreibst du ein Programm, das diese Reihenfolge kennt und somit diese
> Funktionen benutzen kann. Das kompilierst du entweder
> position-independent oder mit einer festen Basisadresse. Dieses Programm
> muss dein Code auf dem µC dann nur noch zur Laufzeit laden und
> hinspringen. Oder so... du weißt schon, was ich mein ;) .

Hehe... Klingt eigentlich ziemlich einfach...

Eigentlich im UserProgramm defines setzen welche die Adresse der 
Funktion im HauptSpeicher beinhalten...

Durch den Sprung wird die Funktion aufgerufen...

Gibt es dazu irgendwelche Tutorials oder vorlagen oder sonstwas?
Ich würde sehr gerne ein paar Experimente damit anstellen... :)

von Markus E. (engelmarkus)


Lesenswert?

Mit Bytecode mein ich folgendes: http://de.wikipedia.org/wiki/Bytecode
Zu dem anderen weiß ich jetzt kein Tutorial, aber du könntest mit einem 
alten Turbo C++ und einem PC-Emulator, vorzugsweise Bochs, rumprobieren. 
Da hast du einen tollen Debugger dabei.

von C. H. (hedie)


Lesenswert?

Markus E. schrieb:
> Zu dem anderen weiß ich jetzt kein Tutorial, aber du könntest mit einem
> alten Turbo C++ und einem PC-Emulator, vorzugsweise Bochs, rumprobieren.
> Da hast du einen tollen Debugger dabei.

Ok vielen Dank...


Tutorial ist vielleicht ein wenig zuviel gesagt... Ein code Schnipsel 
würde reichen...

Es geht mir eigentlich vorallem darum wie ich die Adresse einer Funktion 
abspeichern kann, diese vielleicht auch festlegen kann

und wie ich an eine bestimmte adresse Springen kann. (Am besten ohne 
Assembler zu benützen)

Für letzteres werde ich wahrscheinlich mit google etwas finden.. Aber 
bei den oberen bin ich mir noch nicht sicher...

von Markus E. (engelmarkus)


Angehängte Dateien:

Lesenswert?

Also das mit dem Speichern einer Funktionsadresse musst du 
wahrscheinlich einem Linker überlassen, weil ja erst der alle 
Objekt-Dateien zusammensetzt. Mit LD sollte das aber ohne weiteres 
möglich sein. Welchen C-Compiler benutzt du denn?
Hier hab ich dir mal was angehängt, da kannst du mal reinschauen. Das 
ist jetzt aber für den x86... aber das wird dir nur was nützen, wenn du 
mit dem auch rumprobieren willst...
Eine Adresse springst du so an:
1
((void (*)(void))0x120)();
Oder schöner:
1
typedef void Funktion();
2
...
3
  Funktion* f = (Funktion*)0x100;
4
  f();

von C. H. (hedie)


Lesenswert?

Markus E. schrieb:
> Welchen C-Compiler benutzt du denn?
> Hier hab ich dir mal was angehängt, da kannst du mal reinschauen.

Vielen Dank für deine Zeit :)

Ich verwende das Atollic True Studio bzw. den gcc

Du scheinst dich ziemlich gut mit der Materie auszukennen...
Also ich fasse mal zusammen...


Für ein User Programm würde ich jetzt ein neues Projekt für den STM32 
erstellen... Dabei würde ich jedoch alles System bezogene rausschmeissen 
und nur standard zeugs drinn lassen...

Danach würde ich mir innerhalb dieses Projektes bzw. C File die 
Funktionen aus dem "Betriebssystem", bzw. deren Adressen definieren.

Möchte ich nun eine Funktion aufrufen, so springt das Programm an die 
entsprechende Stelle (Der Stack wird dabei nehme ich an wie üblich durch 
den Compiler verwaltet. Also hat die aufgerufene Funktion auch die 
rücksprung adresse?)

Ich würde nun also das C File compilieren und die erzeugte HEX datei 
z.b. auf einer SD Karte ablegen...


Nun zum Betriebssyste... Darin lade ich die HEX Datei ohne Formatheader 
also nur den Bytecode in den Ram des Controllers...

Danach Springe ich zum ersten Byte des im Ram befindlichen codes...


Würde das so funktionieren?


Kann ich dir als Dankeschön die drei letsten Positionen aus meinem 
Thread
Beitrag "[V] VERSCHENKE die 2te :) Diverse IC's mit z.T. sehr speziellen Funktionen"
Portofrei anbieten? :)

von Markus E. (engelmarkus)


Lesenswert?

Es müsste reichen, wenn man im User-Programm einfach die Funktionen aus 
dem "OS" als extern deklariert, die Datei dann zu einer Objektdatei 
kompilieren lässt und den Linker dann für die externs die passenden 
Werte einsetzen lässt. Man muss nur aufpassen, dass er eine echte 
Binärdatei erstellt. Eine solche Hex-Datei besteht doch aus den 
einzelnen Bytes als ASCII-Zeichen notiert, oder? Die kann man dann aber 
nicht einfach so ausführen.
Ich hab damit schon so lange nichts mehr gemacht... %)
Na, wenn du die Teile nicht mehr brauchst, wieso nicht? :) Ich schick 
dir eine PN...

von C. H. (hedie)


Lesenswert?

Wow das ging ja schnell

Markus E. schrieb:
> die Datei dann zu einer Objektdatei
> kompilieren lässt und den Linker dann für die externs die passenden
> Werte einsetzen lässt.

Also... Wie kann ich eine Datei als Objektdatei erzeugen lassen?

Das der Linker den externs die passenden Werte zuweist, klappt aber nur 
dann, wenn man auch den Code des Betriebssystems im gleichen Projekt hat 
oder?

Markus E. schrieb:
> Man muss nur aufpassen, dass er eine echte
> Binärdatei erstellt. Eine solche Hex-Datei besteht doch aus den
> einzelnen Bytes als ASCII-Zeichen notiert, oder?

Hmmm... da bin ich mir jetzt nicht sicher... Ich meinte wenn man die im 
Texteditor öffnet steht nur Müll drin und keine hex Zahlen als 0x75 oder 
so...

Markus E. schrieb:
> Ich hab damit schon so lange nichts mehr gemacht... %)

Dafür weisst du aber sehr gut bescheid darüber :)

von Markus E. (engelmarkus)


Lesenswert?

Also meine AVR-HEX-Dateien sehen so aus:
>:1000000019C033C032C031C030C02FC02EC02DC087
>:100010002CC02BC02AC029C028C027C026C025C09C
>:100020004CC023C022C021C020C01FC01EC01DC0A4
>:100030001CC01BC011241FBECFEFD4E0DEBFCDBF5C

Mit dem GCC kann man Objektdateien erstellen, indem man schreibt:
[quote]gcc -c main.c[/quote]
Die main.o beinhaltet dann noch einen Platzhalter für z. B.
1
extern void OSFunktion();
2
3
int main() {
4
  OSFunktion();
5
}

In der Objektdatei steht dann:
>_main           proc near
>.text:00000000                 push    ebp
>.text:00000001                 mov     ebp, esp
>.text:00000003                 and     esp, 0FFFFFFF0h
>.text:00000006                 call    ___main
>.text:0000000B                 call    _OSFunktion
>.text:00000010                 leave
>.text:00000011                 retn
>.text:00000011 _main           endp
>
>UNDEF:00000020                 extrn ___main:near
>UNDEF:00000024                 extrn _OSFunktion:near

Den Linker lässt man jetzt den passenden Wert einsetzen, wie muss ich 
noch kurz nachschauen...

von C. H. (hedie)


Angehängte Dateien:

Lesenswert?

Markus E. schrieb:
> Mit dem GCC kann man Objektdateien erstellen, indem man schreibt:
> [quote]gcc -c main.c[/quote]
> Die main.o beinhaltet dann noch einen Platzhalter für z. B.

Sehr gut...

Ich habe gerade nachgeschaut... Die Objectfiles werden bei mir 
automatisch durch klicken auf den Build Button erstellt....

Jedoch sehen bei mir diese Objectfiles ein wenig anders aus...
(Siehe Screenshot)

Das ist die Ansicht des Objectfiles der Release Konfiguration...
Bei der Debugkonfiguration hat es einfach wesentlich mehr Zeichen 
drin...

von Markus E. (engelmarkus)


Lesenswert?

Ah... ich hab die Objektdateien mit einem Disassembler geöffnet ;).

Das mit dem Linker müsste mit folgender Datei gehen:
>OUTPUT_FORMAT("binary")
>INPUT(main.o)
>ENTRY(main)
>
>OSFunktion = 0x100;
>
>SECTIONS
>{
>  .text  0x40000 :
>  {
>    *(.text)
>  }
>  .data  :
>  {
>    *(.data)
>  }
>  .bss  :
>  {
>    *(.bss)
>  }
>}

Damit rufst du den Linker auf:
>ld -T link.ld main.o

Der Wert hinter ".text" ist (hoffentlich) sozusagen die Basis-Adresse, 
an die du die Binärdatei, die rauskommt, laden musst, damit der Code 
korrekt durchläuft.
Ich kann das hier grad im Moment aus mit nicht nachvollziehbaren Gründen 
grad nicht ausprobieren %) .

von C. H. (hedie)


Lesenswert?

Ich kann merkwürdigerweise nicht editieren...

Deshalb doppelpost sorry...


So ruft Atollig den GCC auf
1
arm-atollic-eabi-gcc -c -mthumb -mcpu=cortex-m3 -DSTM32F10X_CL -DUSE_STDPERIPH_DRIVER -I../firmware/CMSIS/CM3/CoreSupport -I../firmware/CMSIS/CM3/DeviceSupport/ST/STM32F10x -I../firmware/STM32F10x_StdPeriph_Driver/inc -I../src -I../STM32_EVAL -I../STM32_EVAL/Common -I../STM32_EVAL/Board -Os -ffunction-sections -fdata-sections -Wall -osrc\main.o

von C. H. (hedie)


Lesenswert?

Markus E. schrieb:
> Die main.o beinhaltet dann noch einen Platzhalter für z. B.extern void 
OSFunktion();
>
> int main() {
>   OSFunktion();
> }

Das bedeutet ich kann diesen Code Teil in der Main.c verankern und 
dadurch wird im Objectfile das daraus?

>UNDEF:00000020                 extrn ___main:near
>UNDEF:00000024                 extrn _OSFunktion:near

Markus E. schrieb:
> Das mit dem Linker müsste mit folgender Datei gehen:
>>OUTPUT_FORMAT("binary")
>>INPUT(main.o)
>>...

Diesen Code als file speichern und dem Aufruf des GCC übergeben oder?
>ld -T link.ld main.o <--- Das hier in der CMD eingeben?

Markus E. schrieb:
> Der Wert hinter ".text" ist (hoffentlich) sozusagen die Basis-Adresse,
> an die du die Binärdatei, die rauskommt, laden musst, damit der Code
> korrekt durchläuft.

Eigentlich müsste ich ja den Code an eine x-beliebige stelle im Ram 
laden können oder sehe ich hier einen zusammenhang nicht?

Beim Binary kommt ja nur der Code der main.c heraus und dieser ist ohne 
"OS" ja nicht lauffähig.

von Markus E. (engelmarkus)


Lesenswert?

Ja, die eine Zeile musst du in der CMD eingeben. Aber nimm aus dieser 
Linker-Datei die "INPUT..."-Zeile raus, sonst kriegst du einen Fehler, 
weil du die Datei ja in der Befehlszeile auch schon angibst.
Ob man das mit der Basis-Adresse braucht, weiß ich nicht genau. Wenn der 
Compiler in den Code absolute Sprünge einbaut, dann muss der Linker 
natürlich die Adresse wissen zum korrekten Berechnen. Wenn im Code aber 
nur relative Sprünge drin sind, ist die Adresse egal.

von Klaus W. (mfgkw)


Lesenswert?

Torsten K. schrieb:
> Bei einem AVR-Controller sehe ich allerdings auch schwarz... vergiss
> meine Beiträge dann am besten ganz schnell wieder ;-) sorry hab nicht
> auf das "Microcontroller" im Unterforum geachtet.

Du meinst, dein bsearch-Vorschlag wäre zuviel für einen MC?
Das glaube ich kaum; bsearch hat sehr wenig Code und sucht
auch fix.

von C. H. (hedie)


Lesenswert?

Ok...

Jetzt habe ich noch eine Frage...


Ich habe jetzt probehalber mal im AtollicStudio einfach ein Projekt 
erstellt ohne STM32 Includes... Also nur ein leeres C projekt..

Ist das überhaupt korrekt? Oder besteht nun die Möglichkeit das 
Maschinencode erzeugt wird, mit Befehlen welche der STM32 gar nicht 
verstehen kann? (Nicht jeder Controller versteht ja die gleichen ASM 
Befehle)

Meine Test Datei sieht nun so aus:
1
/*
2
 * main.c
3
 *
4
 *  Created on: Jan 9, 2011
5
 *      Author: darkhedie
6
 */
7
8
unsigned char ucTest = 0;
9
10
void init_user_prog(void)
11
{
12
  ucTest++;
13
}

Nun mekkert er das es kein main gibt aber das ObjectFile wird dennoch 
erstellt...
Ich nehme an ich kann das soweit ignorieren oder?

Angenommen ich würde diesen Code ins Ram des uC laden an die Adresse 
0x5000
und die Funktion init_user_prog würde innerhalb meiner main.c an adresse 
0x100 liegen. Dann könnte ich aus dem "OS" heraus durch folgenden code
1
((void (*)(void))0x5100)();
 Die Funktion Starten oder?

Achsoo jetzt weiss ich auch wozu die 0x4000 nötig waren.
Damit der Linker innerhalb des UserProgrammes die richtigen Adressen 
verlinken kann...

Jetzt muss man nur noch wissen an welcher Adresse dann z.B. 
init_user_prog liegt...

von Markus E. (engelmarkus)


Lesenswert?

Also ich würde sowohl das "OS", als auch die User-Programme als 
komplette C-Programme erstellen mit main() drin.
Wenn deine Funktion an 0x100 liegt, dann musst du beim Aufrufen auch 
0x100 anspringen, an 0x5100 kann ja dann sonstwas sein, oder versteh ich 
das falsch?
Die Adresse einer Funktion stehen in der Listing-Datei. Ich weiß nicht, 
ob deine IDE so eine standardmäßig erzeugt. Man müsste aber eine 
erzeugen können mit "objdump -r main.o".

von C. H. (hedie)


Lesenswert?

Markus E. schrieb:
> Wenn deine Funktion an 0x100 liegt, dann musst du beim Aufrufen auch
> 0x100 anspringen, an 0x5100 kann ja dann sonstwas sein, oder versteh ich
> das falsch?

ich habe damit gemeint, das ich das binary vom userprogram in dem Ram 
Lade..

und zwar beginnend ab adresse 0x5000

Innerhalb dieses userpogrammes liegt die funktion xy an adresse 0x100 
bezogen auf das userprogramm

Aus sicht des OS liegt diese funktion xy aus meiner sicht jedoch an 
adresse 0x5100 und deshalb müsste ich zu dieser springen wenn ich die 
funktion xy aufrufen möchte (aus dem OS)

Markus E. schrieb:
> Die Adresse einer Funktion stehen in der Listing-Datei. Ich weiß nicht,
> ob deine IDE so eine standardmäßig erzeugt. Man müsste aber eine
> erzeugen können mit "objdump -r main.o".

Ah sehr gut :)

Wird ausprobiert... aber wahscheinlich erst nach dem schlafen :)
Habe überigens noch keine PN erhalten bezüglich den Teilen

von Markus E. (engelmarkus)


Lesenswert?

Achso, dann hab ich das missverstanden.
Ich habs hier grad für einen AVR hinbekommen, wobei das natürlich 
ziemlch sinnlos ist, weils eh nicht geht. Aber rein theoretisch lässt 
sich das wunderbar so kompilieren :) .

von C. H. (hedie)


Lesenswert?

Markus E. schrieb:
> Achso, dann hab ich das missverstanden.
> Ich habs hier grad für einen AVR hinbekommen, wobei das natürlich
> ziemlch sinnlos ist, weils eh nicht geht. Aber rein theoretisch lässt
> sich das wunderbar so kompilieren :) .

Cool :)

Ich nehme an, du hast einfach in den Internen Ram geschrieben oder wie 
sehe ich das?

von Markus E. (engelmarkus)


Lesenswert?

Ich habs gar nicht in echt ausgeführt. Ich hab einfach nur die OS-Datei 
und die Programm-Datei disassembliert und das im Kopf durchgespielt. 
Aber die Adressen scheinen alle richtig zu sein. Man muss nur aufpassen, 
wenn das User-Programm beendet werden will. Dann ruft es eine Funktion 
exit() auf oder so, und die muss dann den Stack noch saubermachen, weil 
ja dann nicht zurückgesprungen werden darf...
Das Ganze hat so einen MS-DOS-Touch, da konnte ja auch ein Programm den 
PC lahmlegen, wenn es irgendwas überschrieben hat im Speicher...

von C. H. (hedie)


Lesenswert?

Markus E. schrieb:
> Dann ruft es eine Funktion
> exit() auf oder so, und die muss dann den Stack noch saubermachen, weil
> ja dann nicht zurückgesprungen werden darf...

Ich nehme an, das es für die Reinigungsarbeiten am Stack Assembler 
braucht oder?

von Markus E. (engelmarkus)


Lesenswert?

Ja ich denk schon. Aber das ist ja eigentlich nur eine Zeile. Wobei ich 
überhaupt keine Ahnung von diesen STM32s habe :) .
Die Zeile müsste ja irgendwie so aussehen:
1
__asm__("add sp, 2");
Aber da wird sich schon jemand finden, der das kann :) .

von C. H. (hedie)


Lesenswert?

Markus E. schrieb:
> Aber da wird sich schon jemand finden, der das kann :) .

hehe Vielen Dank für deine riesengrosse Hilfe...

Ich denke ich werde nun mal schlafen gehen...
Morgen gibt es vielleicht noch mal ein paar Fragen :)

Gruss

von Markus E. (engelmarkus)


Lesenswert?

Ah... ganz so einfach wirds doch nicht, weil ja auf dem Stack noch ganz 
andere Sachen liegen können. Erst ganz am Ende wird der ja wieder 
bereinigt, das heißt, man müsste den Linker noch dazu zwingen, nach der 
Main-Methode diesen exit-Sprung zu machen. Wie das geht muss ich aber 
auch erstmal nachschauen...
Das was wir hier zu basteln versuchen, ist im Endeffekt genau das, was 
auf unseren großen PCs passiert, wenn gegen DLLs gelinkt wird... nur, 
dass da Windows beim Laden die Adressen der externen Funktionen aus den 
DLLs rausliest und in eine Tabelle im User-Programm einträgt.

von C. H. (hedie)


Lesenswert?

Markus E. schrieb:
> Ah... ganz so einfach wirds doch nicht, weil ja auf dem Stack noch ganz
> andere Sachen liegen können. Erst ganz am Ende wird der ja wieder
> bereinigt, das heißt, man müsste den Linker noch dazu zwingen, nach der
> Main-Methode diesen exit-Sprung zu machen. Wie das geht muss ich aber
> auch erstmal nachschauen.

Hmmm... Ich denke du hast noch keine Neuigkeiten oder? :)

Ich werde mich jetzt mal an das zusammenbasteln von 2 Modulen machen...

Also "OS" und UserProg... Mal sehen ob ich alles umsetzen kann was du 
geschrieben hast.

von C. H. (hedie)


Lesenswert?

So ich hab mal ein wenig experimentiert...

Objdump.exe war nicht vorhanden also hab ich die avr-objdump.exe 
genommen...
Hat soweit geklappt, jedoch weiss ich nicht welche adresse ich für die 
funktion change verwenden muss (siehe Anhang)

Wenn ich ein Binary erstellen möchte mit dem GCC von Atollic erhalte ich 
folgenden Fehler:

J:\Programme\Atollic\TrueSTUDIO STM32 Lite 
1.4.0\ARMTools\bin>arm-atollic-eabi-ld -T ld.txt main.o

arm-atollic-eabi-ld: BFD (Built by Atollic AB. Distributed with Atollic 
TrueSTUDIO(r). Build 10.1 Lite.)
2.19.1 assertion fail 
C:/msys/1.0/build/tools/obj/binutils-2.19.1/bfd/elf32-arm.c:5286

Weshalb benötigt der Linker ein so merkwürdiges C File?

von C. H. (hedie)


Angehängte Dateien:

Lesenswert?

Anhang vergessen


Hier noch der Inhalt des Linker Script
1
OUTPUT_FORMAT("binary")
2
ENTRY(main)
3
4
OSFunktion = 0x100;
5
6
SECTIONS
7
{
8
  .text  0x20001000 :
9
  {
10
    *(.text)
11
  }
12
  .data  :
13
  {
14
    *(.data)
15
  }
16
  .bss  :
17
  {
18
    *(.bss)  }
19
}


////////////EDIT//////////

Rufe ich den Linker ohne -T auf, so klappt es so wies aussieht..

Danach erhalte ich die im nächsten Post angehängte Datei...
Aber leider beinhaltet die noch normale Zeichen besonders am ende...

von C. H. (hedie)


Angehängte Dateien:

Lesenswert?

Hier der Output des Linkers ohne -T

von Torsten K. (ago)


Lesenswert?

Klaus Wachtler schrieb:
> Torsten K. schrieb:
>> Bei einem AVR-Controller sehe ich allerdings auch schwarz... vergiss
>> meine Beiträge dann am besten ganz schnell wieder ;-) sorry hab nicht
>> auf das "Microcontroller" im Unterforum geachtet.
>
> Du meinst, dein bsearch-Vorschlag wäre zuviel für einen MC?
> Das glaube ich kaum; bsearch hat sehr wenig Code und sucht
> auch fix.

Nein, bsearch selber meinte ich nicht, sondern das ganze drumherum 
(arrays mit Funktionszeigern). Markus hat es ja schon sehr gut erklärt.

Mir war auch noch nicht ganz klar, ob nun auf dem µC interpretiert 
werden soll, oder ob ein externes (= auf einem PC laufendes) Tool den 
Code interpretieren und tokenisieren soll, welcher dann auf den µC 
übertragen und dort von einer VM/Token-Interpreter (g) ausgeführt 
wird.

Ich spreche hier nicht von dem Aufwand einen Tokenizer zu bauen (habe 
das selber mal für 6502 gemacht), sondern davon, ihn auf einem µC zu 
realisieren :-)

von Markus E. (engelmarkus)


Lesenswert?

Ich hab da anscheinend gestern ein paar falsche Sachen erzählt. Wenn man 
das User-Programm über einen Funktionszeiger aufruft, kümmert sich der 
Compiler natürlich nach dem Ablauf der Methode um das Aufräumen. Also 
hier müssen wir und schon mal keine Sorgen machen.
Außerdem hab ich noch mal über das mit den externen Methoden 
nachgedacht. Es geht auf alle Fälle wesentlich einfacher, als die Werte 
über das Linkerskript eintragen zu lassen.
Im Endeffekt kann man es genauso machen, wie ago es vorgeschlagen hat...
Hier mal ein einfaches Beispiel:
1
char istZahlGerade(int zahl) {
2
    return zahl % 2;
3
}
4
5
void tuSonstwas() {
6
    return;
7
}
8
9
struct API {
10
    char (*istZahlGerade)(int zahl);
11
    void (*tuSonstwas)();
12
} api = {
13
    istZahlGerade,
14
    tuSonstwas
15
};
16
17
typedef void UserProgramm(struct API* api);
18
19
int main() {
20
    // Laden einer Binärdatei an Adresse 0x100.
21
    // ...
22
    
23
    // Aufrufen des User-Programms. Der Linker hat dafür gesorgt, dass die
24
    // Haupt-Methode des Programms ganz vorne steht.
25
    UserProgramm* up = (UserProgramm*)0x100;
26
    up(&api);
27
28
    // Hier landen wir, sobald die Methode abgearbeitet ist. Wir können sofort das nächste
29
    // Programm laden oder was anderes machen.
30
    // Interrupte werden an das OS durchgereicht. Solange ist das User-Programm unterbrochen!
31
    
32
    while (1) ;
33
}
Das ist der OS-Teil. Den kompilierst du ganz normal mit deiner IDE, so 
wie jedes andere Projekt auch.
Der nächste Teil ist ein User-Programm. Dabei ist jetzt wichtig, dass du 
es per Hand kompilierst, damit er nicht das komische Linkerskript 
verwendet, das da dabei ist.
1
// Das hier ist das User-Programm.
2
3
/* Dieses Struct muss identisch sein mit dem im OS */
4
struct API {
5
    char (*istZahlGerade)(int zahl);
6
    void (*tuSonstwas)();
7
};
8
9
void Programm(struct API* api) {
10
    char r = api->istZahlGerade(5);
11
    
12
    if (r == 0) {
13
        // ...
14
    }
15
16
    // Sobald die Methode hier zurückspringt, landen wir wieder im OS.
17
    // Das Aufräumen übernimmt der Compiler.
18
}

> avr-gcc -c up.c -o up.o
> avr-ld up.o -o up.elf
> avr-objcopy -O binary up.elf up.bin
Zum Beispiel so. Damit verhindert man, dass der Linker beispielsweise 
vornedran eine IVT hinbastelt oder ähnliches.
Diese Bin-Datei lädst du von deiner SD-Karte an die Adresse 0x100, dann 
müsste es gehen.

Wie gesagt, ich hab leider keinen solchen µC, deswegen kann ich es nicht 
darauf anpassen :) .

Der nächste Schritt wäre jetzt, dass du einen dynamischen Linker 
einbaust, sodass du komplette ELF-Dateien einlesen kannst...

von C. H. (hedie)


Lesenswert?

Guten Abend Markus

Schön von dir zu hören :)
Deine Bauteile liegen schon im Briefkasten, welcher morgen geleert 
wird..

Markus E. schrieb:
> Wenn man
> das User-Programm über einen Funktionszeiger aufruft, kümmert sich der
> Compiler natürlich nach dem Ablauf der Methode um das Aufräumen.

Das habe ich mir fast gedacht... Wäre ja merkwürdig wenn nicht :)

Markus E. schrieb:
>> avr-gcc -c up.c -o up.o
>> avr-ld up.o -o up.elf
>> avr-objcopy -O binary up.elf up.bin
> Zum Beispiel so.

Ich nehme an, ich muss den Compiler für den STM32 verwenden oder?
Wenn ich den AVR-GCC Verwenden würde, würden unter Umständen befehle 
übersetzt welche nur der AVR kennt. Habe ich das so richtig verstanden?

Markus E. schrieb:
> Wie gesagt, ich hab leider keinen solchen µC, deswegen kann ich es nicht
> darauf anpassen :) .

Kein Problem... du gibst dir schon genug mühe :) Ein bisschen etwas muss 
ich ja schon noch selbst machen :)

Ich nehme an, der Compiler verwendet das RAM von beginn an oder?
Gibt es eine Möglichkeit, in C auszulesen, ab wo das RAM frei ist?

Markus E. schrieb:
> Der nächste Schritt wäre jetzt, dass du einen dynamischen Linker
> einbaust, sodass du komplette ELF-Dateien einlesen kannst...

Was ist den der Vorteil oder der unterschied von ELF Dateien gegenüber 
den Binarys?

So wie ich Wikipedia verstanden habe, steht in der ELF Datei zusätzlich 
noch wo sich innerhalb des User Programmes die einzelnen Funktionen 
befinden (Adresse), richtig?

Markus E. schrieb:
> Damit verhindert man, dass der Linker beispielsweise
> vornedran eine IVT hinbastelt oder ähnliches.

Bedeutet das, das der Code somit an eine Beliebige stelle kopiert und 
ausgeführt werden kann? Also entfällt somit die .text Zuweisung mit der 
Basisadresse?
Arbeitet dann der Linker mit Offset werten, oder woher weiss er wo sich 
eine Funktion im Speicher befindet nachdem ich das Userprogramm an eine 
x-beliebige stelle kopiert habe?


Vielen Dank schon jetzt für deine Antwort :)

von Markus E. (engelmarkus)


Lesenswert?

> Ich nehme an, ich muss den Compiler für den STM32 verwenden oder?
Genau, aber den hab ich hier ja nicht, und die anderen erlauben mir das 
so nicht.

> Ich nehme an, der Compiler verwendet das RAM von beginn an oder?
> Gibt es eine Möglichkeit, in C auszulesen, ab wo das RAM frei ist?
Schau hier: http://www.st.com/stonline/products/literature/pm/15491.pdf
Kapitel 2.2 Memory Model, Seite 25
Da steht genau, welche Speicher wohin in den Adressraum gemappt sind.
Schauen, wo Speicher frei ist, kannst du nicht, aber du kannst natürlich 
über malloc einen Speicherblock reservieren...

> Was ist den der Vorteil oder der unterschied von ELF Dateien gegenüber
> den Binarys?
> So wie ich Wikipedia verstanden habe, steht in der ELF Datei zusätzlich
> noch wo sich innerhalb des User Programmes die einzelnen Funktionen
> befinden (Adresse), richtig?
Ja, zum Beispiel. So könntest du eine Art DLL bzw. shared lib bauen... 
Außerdem ist es bei ELF-Dateien so, dass sie verschiedene Segmente 
enthalten können und eben unter anderem auch eine Tabelle für die 
externen Funktionen, die der dynamische Linker dann ausfüllen kann. 
Damit könntest du dir dieses komische Struct in den User-Programmen 
sparen.
Aber bleib lieber erst mal bei dem Binär-Format, bis das funktioniert :) 
.

> Bedeutet das, das der Code somit an eine Beliebige stelle kopiert und
> ausgeführt werden kann? Also entfällt somit die .text Zuweisung mit der
> Basisadresse?
> Arbeitet dann der Linker mit Offset werten, oder woher weiss er wo sich
> eine Funktion im Speicher befindet nachdem ich das Userprogramm an eine
> x-beliebige stelle kopiert habe?
So wie ich das oben geschrieben hab, brauchst du überhaupt kein 
Linkerskript mehr. Ich weiß leider nicht, wie der Compiler standardmäßig 
Code erstellt für den STM32, aber ich gehe mal davon aus, dass er immer 
relative Sprünge verwendet. Deswegen sollte das alles so passen und du 
kannst die Binär-Datei, wie oben schon gesagt, an eine beliebige Stelle 
im RAM laden, vorzugsweise an die Adresse, die malloc dir zugeteilt hat. 
Im Zweifelsfall musst du die Binärdatei halt disassemblieren und 
schauen, welche Art von Sprung drin vorkommt. Aber zumindest ein 
Minimal-User-Programm zum LED anschalten oder so sollte funktionieren.

von C. H. (hedie)


Lesenswert?

Markus E. schrieb:
> So wie ich das oben geschrieben hab, brauchst du überhaupt kein
> Linkerskript mehr. Ich weiß leider nicht, wie der Compiler standardmäßig
> Code erstellt für den STM32, aber ich gehe mal davon aus, dass er immer
> relative Sprünge verwendet. Deswegen sollte das alles so passen und du
> kannst die Binär-Datei, wie oben schon gesagt, an eine beliebige Stelle
> im RAM laden, vorzugsweise an die Adresse, die malloc dir zugeteilt hat.
> Im Zweifelsfall musst du die Binärdatei halt disassemblieren und
> schauen, welche Art von Sprung drin vorkommt. Aber zumindest ein
> Minimal-User-Programm zum LED anschalten oder so sollte funktionieren.

Vielen Dank für deine Hilfe...

Ich denke mit diesen vielen Infos sollte das klappen :)
Morgen haben wir grossen Prüfungs Tag... aber am Abend werde ich das 
wahrscheinlich Testen :)

Ich meld mich wenn ich News habe...

von C. H. (hedie)


Lesenswert?

Also ich habe neuigkeiten...


1:  Der verwendete Linker des STM32 Package motzt das er im object file 
kein entry symbol _start; findet... deshalb setzt er es auf default: 
0x00008000
was ja schlecht ist, weil es so wieder eine referenz hat bzw eine basis 
adresse... also muss ich den code so wie er jetzt wäre an 0x00008000 
kopieren...


2: Wenn ich OBJCOPY aufrufe, dann meldet mir die Konsole das der Flash 
Converter nur in Atollic Professional enthalten ist...

Es sieht so aus, als ob ich mir die Professional edition kaufen muss...

Denn wenn ich das File mit dem AVR linker versuche zu Linken meldet er 
mir ein unbekanntes Format... Das selbe ist es wenn ich versuche mit dem 
avr objcopy das file in ein Binary umzuwandeln...

Oder kennst du dazu einen Trick?

von Markus E. (engelmarkus)


Lesenswert?

Hm... also das mit dem Linker könnte man evtl. beheben, indem man 
entweder auf der Kommandozeile ein "-e 0x100" mit angibt, wobei der Wert 
die Entry-Adresse ist, oder indem man ein Linkerskript erstellt mit dem 
Eintrag "ENTRY(methodenname)".
Das mit dem objcopy kommt mir komisch vor. Benutzt Atollic wirklich die 
GNU-Programme?

Dass das mit den AVR-Tools nicht geht ist klar, weil es zwei 
unterschiedliche Architekturen sind für die die Programme jeweils was 
ausspucken.

von C. H. (hedie)


Lesenswert?

Markus E. schrieb:
> Das mit dem objcopy kommt mir komisch vor. Benutzt Atollic wirklich die
> GNU-Programme?

Dieses problem konnte ich lösen!

Ich verwende einfach die objcopy datei von codesourcery für arm...
Damit gehts einwandfrei... (Also das umwandeln :) )

Markus E. schrieb:
> Hm... also das mit dem Linker könnte man evtl. beheben, indem man
> entweder auf der Kommandozeile ein "-e 0x100" mit angibt, wobei der Wert
> die Entry-Adresse ist,

Entry Adresse bedeutet das ich aus dem OS an die adresse wo ich das bin 
hinkopoert habe + entry adresse hinspringen muss?

Also ich meine:

Adresse = Adresse wo ich das bin hin kopiert habe (Start)

Beginn meines userprogrammes = Adresse + EntryAdresse

von Markus E. (engelmarkus)


Lesenswert?

Hm... gute Frage... was hindert dich, es auszuprobieren? ;)

von C. H. (hedie)


Lesenswert?

Hehe... ja da hast du recht...

das habe ich auch gemacht...

Mein jetztiger code des OS:
1
char istZahlGerade(int zahl) {
2
    return zahl % 2;
3
}
4
5
void LED(unsigned char ucState)
6
{
7
  if(ucState == 1) GPIO_SetBits(GPIOA,GPIO_Pin_8);
8
  if(ucState == 0) GPIO_ResetBits(GPIOA,GPIO_Pin_8);
9
    return;
10
}
11
12
void wait_clk(unsigned int uiClocks)
13
{
14
  while(uiClocks) uiClocks--;
15
  return; //Ist jetzt ergänzt :)
16
}
17
18
struct API {
19
    char (*istZahlGerade)(int zahl);
20
    void (*LED)(unsigned char ucState);
21
    void (*wait_clk)(unsigned int uiClocks);
22
} api = {
23
    istZahlGerade,
24
    LED,
25
    wait_clk
26
};
27
28
typedef void UserProgramm(struct API* api);
29
30
31
32
33
34
int main(void)
35
{
36
  unsigned char userprog[250]; //Das program im bin ist 214byte gross
37
  unsigned int uiCounter = 0;
38
  RCC_Configuration();
39
  GPIO_Configuration();
40
41
42
43
44
    delay_value = 255;  //Low Speed
45
    while (FALSE==mmc_init());  //SD_Karte Initialisieren
46
    delay_value = 1;  //Full Speed
47
48
    // Fat initialisieren. Nur wenn das klappt sind weitere Aktionen sinnvoll, sonst endet das Programm !
49
    if(TRUE == fat_loadFatData()){
50
      // Dateinamen muessen in diesem Format sein !
51
      // Man beachte die Größe des Arrays und die Großbuchstaben!
52
    unsigned char file_name[9]="up.bin";
53
    unsigned char str[13]="Hallo Datei!";
54
55
56
57
    // Datei existiert nicht, also anlegen !
58
        if(MMC_FILE_NEW == ffopen(file_name)){
59
60
           // Schreibt String auf Karte !
61
           // Nur richtige Strings koennen mit ffwrites geschrieben werden !
62
           ffwrites(str);
63
64
           // Neue Zeile in der Datei.
65
                 // Schreibt Zeilenumbruch in die Text Datei.
66
           ffwrite(0x0D);
67
           ffwrite(0x0A);
68
69
           // Schließt Datei.
70
           ffclose();
71
         }
72
73
74
       // Datei existiert also lesen.
75
       // Gerade angelegt und beschrieben, oder war schon vorhanden und es wurde was angehaengt.
76
77
       if(MMC_FILE_EXISTS == ffopen(file_name)){
78
79
        // Setzen einer Variable und dann runterzaehlen geht am schnellsten !
80
        unsigned long int seek=file.length;
81
82
83
        // Lesen eines chars und Ausgabe des chars.
84
        // Solange bis komplette Datei gelesen wurde.
85
        //Datei Kopieren
86
        uiCounter = 0;
87
        do{
88
          userprog[uiCounter] = ffread(); //Byte um byte in den Ram laden
89
          uiCounter++;
90
        }while(--seek);
91
        ffclose();
92
93
       }
94
95
    }
96
97
98
    ((void (*)(void))&userprog[0] + 0x100)();  //User Programm starten!
99
100
101
  while(1)
102
  {
103
104
  }
105
}

so sieht mein userprog aus
1
// Das hier ist das User-Programm.
2
3
/* Dieses Struct muss identisch sein mit dem im OS */
4
struct API {
5
    char (*istZahlGerade)(int zahl);
6
    void (*LED)(unsigned char ucState);
7
    void (*wait_clk)(unsigned int uiClocks);
8
};
9
10
void Programm(struct API* api) 
11
{
12
    unsigned char ucTemp = 10;
13
14
    while(ucTemp)
15
{
16
    api->LED(1);
17
    api->wait_clk(60000);
18
    api->wait_clk(60000);
19
    api->LED(0);
20
    api->wait_clk(60000);
21
    api->wait_clk(60000);
22
    ucTemp--;
23
}
24
    
25
26
    // Sobald die Methode hier zurückspringt, landen wir wieder im OS.
27
    // Das Aufräumen übernimmt der Compiler.
28
}


Doch leider lande ich nach diese Zeila
1
((void (*)(void))&userprog[0] + 0x100)();

Im hard fault  handler des Mikrocontrollers...

Debuggen kann ich diesen Sprung ja leider nicht...


////// Edit /////

Ich sehe gerade, das ich beim delay_clk return vergessen habe...

Gleich nochmal mit return versuchen...


Edit2

Es lag nicht am return...

gibt immer noch hard fault

von Claudio (Gast)


Lesenswert?

@Markus

Ich denke mal du siehst im Code auch keinen Fehler oder?

Gruss

von Markus E. (engelmarkus)


Lesenswert?

Ich seh sogar einen gewaltigen Fehler...
>     ((void (*)(void))&userprog[0] + 0x100)();  //User Programm starten!
Wie soll das gehen?
Das typedef oben ist nicht umsonst da ;) .
Du musst dem User Programm beim Aufruf aus dem OS schon den Zeiger auf 
das Array mit den Funktionen übergeben...
1
UserProgramm* up = (UserProgramm*)(&aha[0] + 0x100);
2
up(&api);

Außerdem steht da oben beim Laden der Datei was von Großbuchstaben? Aber 
den Code kenn ich natürlich nicht...
Wie wird denn auf dem STM32 debuggt? Kannst du da nicht den Speicher 
anschauen? Dann würdest du doch sehen was da abgeht, oder?

von C. H. (hedie)


Lesenswert?

Markus E. schrieb:
> UserProgramm* up = (UserProgramm*)(&aha[0] + 0x100);
> up(&api);

Habe das nun so versucht...

Jedoch bekomme ich bei der Zeile up(&api); einen HardFault...

Markus E. schrieb:
> Wie wird denn auf dem STM32 debuggt? Kannst du da nicht den Speicher
> anschauen? Dann würdest du doch sehen was da abgeht, oder?

Ja ich kann den Speicher anschauen... ich sehe das die Daten aus dem Bin 
korrekt kopiert wurden... Auch die Adresse wird korrekt übergeben...

Was den Hardfault auslöst konnte ich einwenig eingrenzen...
1
    UserProgramm* up = (UserProgramm*)(&userprog[0] + 0x100);
2
0x0800083a <main+294>: add.w   r3, r7, #32
3
0x0800083e <main+298>: add.w   r3, r3, #256  ; 0x100
4
0x08000842 <main+302>: str.w   r3, [r7, #288]  ; 0x120
5
    up(&api);
6
0x08000846 <main+306>: ldr.w   r3, [r7, #288]  ; 0x120
7
0x0800084a <main+310>: movw    r0, #0 <---- Hier gibts den Hardfault...
8
0x0800084e <main+314>: movt    r0, #8192  ; 0x2000
9
0x08000852 <main+318>: blx     r3

von Markus E. (engelmarkus)


Lesenswert?

Ich mein, ich versteh ja nichts von ARM-Assembler, aber so wie ich das 
verstehe, liegt ja offensichtlich das Struct direkt am Anfang vom SRAM 
an 0x20000000. Diesen Wert lädt er in der von dir markierten Zeile und 
der nächsten in das Register r0. Da kann gar kein Hard Fault 
auftreten...
Bist du sicher, dass der nicht erst beim Sprung "blx r3" kommt? 
Interessant wäre der Wert zu wissen, der da in r3 steht und vor allem, 
was an der Stelle im Speicher ist. Also ich meine das Sprungziel :) ...

von C. H. (hedie)


Lesenswert?

Markus E. schrieb:
> Bist du sicher, dass der nicht erst beim Sprung "blx r3" kommt?
> Interessant wäre der Wert zu wissen, der da in r3 steht und vor allem,
> was an der Stelle im Speicher ist. Also ich meine das Sprungziel :) ...

Du hast absolut recht!

Tut mir echt Leid...

In R3 steht zum Zeitpunkt des Sprunges: 536903656
oder in HEX 0x20007fe8

Das userprog[0] array mit dem Binärcode liegt an 0x20007ee8

Somit springt er genau um 0x100 höher also eigentlich richtig...

Der Speicher zeigt auch korrekt ab 0x20007ee8 die Binärdaten an bis zum 
letzten byte!

Also kann es jetzt eigentlich nur noch am Userprog liegen oder?

von Markus E. (engelmarkus)


Lesenswert?

Ja bist du ganz sicher, dass die Haupt-Methode des User-Programms 
wirklich erst 0x100 nach dem Anfang der Binärdatei kommt? Wenn du das 
einfach mal weglässt, gehts dann nicht zufällig? Kannst du die 
Binär-Datei mal hochladen?

von C. H. (hedie)


Angehängte Dateien:

Lesenswert?

Markus E. schrieb:
> Ja bist du ganz sicher, dass die Haupt-Methode des User-Programms
> wirklich erst 0x100 nach dem Anfang der Binärdatei kommt?

Also ich habe beim Linker -e 0x100 mit angegeben...

Markus E. schrieb:
> Wenn du das
> einfach mal weglässt, gehts dann nicht zufällig?

Nein es geht leider nicht...

Zudem tritt der hard fault nun (nachdem ich die 0x100 entfernt habe) 
wieder beim Laden des Registers auf...

Ich habe dir dazu ein Video des Debuggens gemacht... Hier der Link

http://www.youtube.com/watch?v=bosGHoULcAM

Markus E. schrieb:
> Kannst du die
> Binär-Datei mal hochladen?

Aber selbstverständlich :)

von Markus E. (engelmarkus)


Lesenswert?

Also irgendwas ist da faul. Aber am User Programm liegt es definitiv 
nicht, das sieht wunderbar aus. Allerdings bringt deine Linker-Option 
"-e 0x100" absolut nichts. Das Programm beginnt direkt bei 0x20007ee8, 
also direkt am Anfang der Binärdaten! Das heißt, du musst beim Aufruf 
das "+ 0x100" weglassen.
Du kannst doch bestimmt im Debugger einzelne Codezeilen überspringen?
Dann probier mal, was passiert, wenn du diese beiden, die im Bild 
durchgestrichen sind, auslässt. Dann müsstest du zumindest bis zur 
ersten Anweisung des User Programms kommen. Vielleicht musst du dazu 
auch per Hand einfach zum IP 0x10 dazuzählen?
Ich frag mich eh, was diese beiden Zeilen da sollen, das macht dein 
Compiler nämlich öfter, dass er den Wert aus R3 an der Adresse R7 + x 
speichert und dann gleicht wieder reinlädt. Aber dazu kenn ich mich mit 
ARMs zu wenig aus. Würde es dir was ausmachen, auch die OS-Datei 
hochzuladen, also als Hex oder was das halt ist?

von Markus E. (engelmarkus)


Angehängte Dateien:

Lesenswert?

Wunderbar, das Bild ist nicht da... ich versuchs nochmal.

von C. H. (hedie)


Angehängte Dateien:

Lesenswert?

Markus E. schrieb:
> Vielleicht musst du dazu
> auch per Hand einfach zum IP 0x10 dazuzählen?

Was genau meinst du damit? Ich habe die Vermutung, IP dürfte de 
ProgrammAdress Counter sein... Doch wo kann ich den verändern...

Markus E. schrieb:
> Allerdings bringt deine Linker-Option
> "-e 0x100" absolut nichts. Das Programm beginnt direkt bei 0x20007ee8,
> also direkt am Anfang der Binärdaten!

Das ist soweit ja nicht schlimm :)

Markus E. schrieb:
> u kannst doch bestimmt im Debugger einzelne Codezeilen überspringen?
> Dann probier mal, was passiert, wenn du diese beiden, die im Bild
> durchgestrichen sind, auslässt.

Ich glaube diese Funktionalität hat mein Debugger nicht... Oder ich 
kenne sie noch nicht...

Markus E. schrieb:
> Würde es dir was ausmachen, auch die OS-Datei
> hochzuladen, also als Hex oder was das halt ist?

Hehe du musst doch nicht so höflich fragen :) Wenn hier einer bitten 
muss dann wohl ich, denn so wie du mir hier hilfst ist das nicht 
selbstverständlich... :)

Dafür hast du dir noch mehr Bauteile verdient :P

Anbei ein ZIP mit allen möglichen OS dateien...


/////EDIT////

Ich habe vergessen zu schreiben, das ich manchmal durchkomme bis zum 
entscheidenden Sprung Befehl.. jedoch kommt danach der HardFault...

Aber sieh selbst

http://www.youtube.com/watch?v=5_DgPDx2euE
Edit--> Ich habe keine Ahnung woher die komischen Linien im Bild sind :)

Gruss und 10000 Dank für deine so tolle Hilfe

von Markus E. (engelmarkus)


Lesenswert?

Scheinbar ist es so, dass es ein Statusregister "SCB_HFSR" gibt... schau 
doch mal, ob da das "Bit 30 FORCED" gesetzt ist. Wenn ja, dann ist 
ursprünglich eine andere Exception aufgetreten, deren Handling aber 
deaktiviert wurde. Dann wird automatisch zum Hard Fault Handler 
umgeleitet.
Du kannst dann ins Register "SCB_CFSR" schauen, welches Bit da gesetzt 
ist, und mit der Tabelle in der PDF-Datei Kapitel 4.4.9, Seite 141 
vergleichen. Dann siehst du, was konkret das Problem ist. Ich kann 
nämlich beim besten Willen keinen Fehler sehen.
Vor allem irritiert mich, dass das manchmal durchläuft. Am Debugger kann 
das aber nicht liegen, oder? Was passiert, wenn du das Programm einfach 
durchlaufen lässt, ohne Breakpoints und solche Sachen?

von C. H. (hedie)


Lesenswert?

Markus E. schrieb:
> Am Debugger kann
> das aber nicht liegen, oder? Was passiert, wenn du das Programm einfach
> durchlaufen lässt, ohne Breakpoints und solche Sachen?

Wenn ich keine Breakpoints setze, dann gehts trozdem nicht...

Markus E. schrieb:
> Du kannst dann ins Register "SCB_CFSR" schauen, welches Bit da gesetzt
> ist, und mit der Tabelle in der PDF-Datei Kapitel 4.4.9, Seite 141
> vergleichen.

Du gibst dir ja meeega viel mühe :) Vielen Dank!

Markus E. schrieb:
> Scheinbar ist es so, dass es ein Statusregister "SCB_HFSR" gibt... schau
> doch mal, ob da das "Bit 30 FORCED" gesetzt ist. Wenn ja, dann ist
> ursprünglich eine andere Exception aufgetreten, deren Handling aber
> deaktiviert wurde. Dann wird automatisch zum Hard Fault Handler
> umgeleitet.

Ich habe nun also den Speicher an der Adresse 0xE000ED28 ausgelesen...
Hier das Ergebnis was nach einem HardFault da drinn steht...

0xE000ED28 : 0xE000ED28 <Hex>
  Address   0 - 3     4 - 7     8 - B     C - F
  E000ED20  00000000  00000000  00000200  00000040

0xE000ED28 = 0000 0000
0xE000ED29 = 0000 0000
0xE000ED2A = 0000 0010
0xE000ED2B = 0000 0000

Also ist das Bit 17 des UFSR Register gesetzt...
Dazu steht

Bit 17 INVSTATE: Invalid state usage fault:
When this bit is set to 1, the PC value stacked for the exception return 
points to the instruction
that attempted the illegal use of the EPSR.
This bit is not set to 1 if an undefined instruction uses the EPSR.
0: No invalid state usage fault
1: The processor has attempted to execute an instruction that makes 
illegal use of the
EPSR.

Auf Seite 20 Steht was das EPSR ist... Das Execution program status 
register

Mir sagt das leider nicht sehr viel... Dir schon?

gruss

von Markus E. (engelmarkus)


Lesenswert?

Also ich bin ja grad sehr verwirrt. Auf Seite 42 steht:
> Attempt to enter an invalid instruction set state(1)
> 1. Attempting to use an instruction set other than the Thumb instruction set.
Das würde ja heißen, dass der Compiler falsch eingestellt ist? 
Andererseits gehts ja manchmal...
Ändert das irgendwas, wenn du das userprog-Array nicht als lokale 
Variable, sondern global deklarierst?
Irgendwie werd ich aus dieser PDF-Datei nicht schlau. Probier doch mal 
folgendes: Erstell aus dem Userprog ein Array, folgendermaßen:
1
unsigned char rawData[949] = {
2
  0x7F, 0x45, 0x4C, 0x46, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, ...
3
};
Und versuch dann mal, das direkt anzuspringen. Dann legt der Compiler 
das ja mit dem OS praktisch zusammen ab.
An irgendwas muss das ja liegen :) .
Dein Paket ist übrigens heil angekommen. Mal schauen, was ich damit 
anstell.

EDIT: Ja moment mal... Das liegt wahrscheinlich an der Adresse, die der 
Compiler aussucht! Schau mal da:
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka12545.html
Im Bit 0 einer jeden Anweisung wird gespeichert, ob das Thumb- oder das 
32-bit-Instruction Set verwendet wird...
Bei 0x20007ee8 ist das Bit 0 gelöscht.
> However, if the programmer created an address manually,
> he must take care of ensuring that bit[0] is correctly
> set to represent the opcode type of that branch target.

> The Cortex-M3 processor supports only the Thumb-2
> instruction set (a superset of traditional Thumb). Therefore
> all branch targets should be indicated as odd numbers,
> having bit[0] SET for interworking, indicating Thumb-style opcodes.

von C. H. (hedie)


Lesenswert?

Markus E. schrieb:
> Dein Paket ist übrigens heil angekommen.

Freut mich :)

Markus E. schrieb:
> Andererseits gehts ja manchmal...

Also gehen tuts nicht wirklich... Lediglich über den entsprechenden 
Assembler Befehl kommen wir hinweg :)... Aber die LED leuchtet nie...
Und ich komme auch nicht in die while(1) schleife des OS

Markus E. schrieb:
> Ja moment mal... Das liegt wahrscheinlich an der Adresse, die der
> Compiler aussucht! Schau mal da:
> http://infocenter.arm.com/help/index.jsp?topic=/co...
> Im Bit 0 einer jeden Anweisung wird gespeichert, ob das Thumb- oder das
> 32-bit-Instruction Set verwendet wird...
> Bei 0x20007ee8 ist das Bit 0 gelöscht.

Ich bin immer wieder aufs neue begeistert wie viel Zeit du investierst 
mir bei diesem Problem zu Helfen :) Vielen Dank! Du hast was gut bei 
mir!

Ich habe dich also richtig verstanden, das wenn ich als Adresse 
0x20007ee8 übergebe er den ARM 32 Befehlssatz verwendet und bei 
0x20007ee8 + 1 den Thumb 2?

Ich bin also mal davon ausgegangen und habe das Programm folgendermassen 
angepasst:
1
 UserProgramm* up = (UserProgramm*)(&userprog[0]+1 );
Derzeit jedoch immer noch von der SD Karte...

Ergebnis -> Funktioniert nicht!


Nun also noch der Versuch Ohne SD Karte...
1
unsigned char userprog[] = {
2
    0x00,0x48,0x2D,0xE9,0x04,0xB0,0x8D,0xE2,0x10,0xD0,0x4D,0xE2,0x10,0x00,0x0B,0xE5,0x0A,0x30,0xA0,0xE3,0x05,0x30,0x4B,0xE5,
3
    0x24,0x00,0x00,0xEA,0x10,0x30,0x1B,0xE5,0x04,0x30,0x93,0xE5,0x01,0x00,0xA0,0xE3,0x0F,0xE0,0xA0,0xE1,0x13,0xFF,0x2F,0xE1,
4
    0x10,0x30,0x1B,0xE5,0x08,0x30,0x93,0xE5,0xEA,0x0C,0xA0,0xE3,0x60,0x00,0x80,0xE2,0x0F,0xE0,0xA0,0xE1,0x13,0xFF,0x2F,0xE1,
5
    0x10,0x30,0x1B,0xE5,0x08,0x30,0x93,0xE5,0xEA,0x0C,0xA0,0xE3,0x60,0x00,0x80,0xE2,0x0F,0xE0,0xA0,0xE1,0x13,0xFF,0x2F,0xE1,
6
    0x10,0x30,0x1B,0xE5,0x04,0x30,0x93,0xE5,0x00,0x00,0xA0,0xE3,0x0F,0xE0,0xA0,0xE1,0x13,0xFF,0x2F,0xE1,0x10,0x30,0x1B,0xE5,
7
    0x08,0x30,0x93,0xE5,0xEA,0x0C,0xA0,0xE3,0x60,0x00,0x80,0xE2,0x0F,0xE0,0xA0,0xE1,0x13,0xFF,0x2F,0xE1,0x10,0x30,0x1B,0xE5,
8
    0x08,0x30,0x93,0xE5,0xEA,0x0C,0xA0,0xE3,0x60,0x00,0x80,0xE2,0x0F,0xE0,0xA0,0xE1,0x13,0xFF,0x2F,0xE1,0x05,0x30,0x5B,0xE5,
9
    0x01,0x30,0x43,0xE2,0x05,0x30,0x4B,0xE5,0x05,0x30,0x5B,0xE5,0x00,0x00,0x53,0xE3,0xD7,0xFF,0xFF,0x1A,0x04,0xD0,0x4B,0xE2,
10
    0x00,0x48,0xBD,0xE8,0x1E,0xFF,0x2F,0xE1,0x48,0x61,0x6C,0x6C,0x6F,0x20,0x44,0x61,0x74,0x65,0x69,0x21,0x0D,0x0A,
11
  };

Einmal so:
1
 UserProgramm* up = (UserProgramm*)(&userprog[0]+1 );

und einmal so:
1
 UserProgramm* up = (UserProgramm*)(&userprog[0] );
Jedoch gibt es beide male einen HardFault...

Interessant ist jedoch das SCB_HFSR Register >MIT< userprog[0]+1

0xe000ed28 : 0xE000ED28 <Hex>
  Address   0 - 3     4 - 7     8 - B     C - F
  E000ED20  00000000  00000000  00820000  00000040
  E000ED30  01000000  28B08DE2  28B08DE2  00000000

0xE000ED28 = 0000 0000
0xE000ED29 = 1000 0010  //Bit9 und Bit15
0xE000ED2A = 0000 0000
0xE000ED2B = 0000 0000

Bit 9 PRECISERR: Precise data bus error
When the processor sets this bit is 1, it writes the faulting address to 
the BFAR.
0: No precise data bus error
1: A data bus error has occurred, and the PC value stacked for the 
exception return points to
the instruction that caused the fault.


Bit 15 BFARVALID: Bus Fault Address Register (BFAR) valid flag:
The processor sets this bit to 1 after a bus fault where the address is 
known. Other faults can
set this bit to 0, such as a memory management fault occurring later.
If a bus fault occurs and is escalated to a hard fault because of 
priority, the hard fault handler
must set this bit to 0. This prevents problems if returning to a stacked 
active bus fault handler
whose BFAR value has been overwritten.
0: Value in BFAR is not a valid fault address
1: BFAR holds a valid fault address.


Mit userprog[0]
ist es das alt bekannte Bit17 das gesetzt ist...

Irgendwie scheint die Adresse faul zu sein...

von Markus E. (engelmarkus)


Lesenswert?

Du kannst ja mal ins Register BFAR (Seite 146, Kapitel 4.4.12) schauen, 
da steht die Adresse der Anweisung drin, die den Bus Fault ausgelöst 
hat.
Zumindest den Sprung sollte er doch erlauben, egal was dann an der 
Zieladresse steht. Es kann natürlich sein, dass ich was überlesen hab... 
wenn wir dann nichts finden, weiß ich auch nicht mehr weiter, dann 
solltest du vielleicht doch lieber einen kleinen Interpreter schreiben 
wie oben vorgeschlagen ;) .

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.