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
if(!strcmp(ucTemp,"version"))main_token.befehl=VERSION;//Erster Befehl entschlüsseln und sichern
2
elseif(!strcmp(ucTemp,"output_on"))main_token.befehl=OUTPUTON;//Erster Befehl entschlüsseln und sichern
3
elseif(!strcmp(ucTemp,"output_off"))main_token.befehl=OUTPUTOFF;//Erster Befehl entschlüsseln und sichern
4
elseif(!strcmp(ucTemp,"pause"))main_token.befehl=PAUSE;//Erster Befehl entschlüsseln und sichern
5
elseif(!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
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.
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
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 :)
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...
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...)
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?
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...
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?
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
typedefvoidFunktion(constchar*parameter[]);
2
3
voidfunktion_end(constchar*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
intmain(){
16
// Parameter hier ermitteln...
17
constchar*test[]={"42","ein string"};
18
// Beispielaufruf einer Funktion aus dem Array oben.
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 :)
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 ;) .
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?
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
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... :)
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.
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...
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:
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? :)
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...
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 :)
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
externvoidOSFunktion();
2
3
intmain(){
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...
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...
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 %) .
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:nearMarkus 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.
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.
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.
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
unsignedcharucTest=0;
9
10
voidinit_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...
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".
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
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 :) .
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?
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...
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?
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 :) .
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
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.
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.
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?
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...
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 :-)
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
charistZahlGerade(intzahl){
2
returnzahl%2;
3
}
4
5
voidtuSonstwas(){
6
return;
7
}
8
9
structAPI{
10
char(*istZahlGerade)(intzahl);
11
void(*tuSonstwas)();
12
}api={
13
istZahlGerade,
14
tuSonstwas
15
};
16
17
typedefvoidUserProgramm(structAPI*api);
18
19
intmain(){
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
structAPI{
5
char(*istZahlGerade)(intzahl);
6
void(*tuSonstwas)();
7
};
8
9
voidProgramm(structAPI*api){
10
charr=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...
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 :)
> 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.
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...
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?
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.
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
// 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
unsignedcharfile_name[9]="up.bin";
53
unsignedcharstr[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
unsignedlongintseek=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
structAPI{
5
char(*istZahlGerade)(intzahl);
6
void(*LED)(unsignedcharucState);
7
void(*wait_clk)(unsignedintuiClocks);
8
};
9
10
voidProgramm(structAPI*api)
11
{
12
unsignedcharucTemp=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
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?
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);
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 :) ...
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?
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?
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=bosGHoULcAMMarkus E. schrieb:> Kannst du die> Binär-Datei mal hochladen?
Aber selbstverständlich :)
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?
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
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?
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
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:
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.
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...
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...
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 ;) .