mikrocontroller.net

Forum: Compiler & IDEs Schreiben in SRAM per pointer


Autor: Steffffan (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,
habe folgenden c-Code für den ATMega16:
// Global pointer
unsigned char* happyPtr = 0;

...

// Write on stack
happyPtr = SP;
*happyPtr = (unsigned char)0x01;
happyPtr -= 1;
*happyPtr = (unsigned char)0x2D;
happyPtr -= 1;
SP -= 2;

Ich will eine Rücksprungadresse auf den Stack schieben (wahrscheinlich 
habe ich noch die Byteorder falsch, aber das ist gerade nicht so 
spannend), danach folgt ein reti. Das Problem ist, der erste Wert landet 
nicht im SRAM, der zweite schon. Noch lustiger wirds, wenn man die 0x01 
durch z.B. 0xCC ersetzt, das funktioniert dann.

Mehrere Fragen:
1. Warum funktioniert die zweite Zuweisung und die erste nicht?
2. Warum funktioniert die erste mit einem anderen Wert?
3. Warum muss man wie in dem Code oben den SP klonen, bevor man 
dereferenzieren kann (bei letzterer Frage habe ich das böse Gefühl, dass 
sich die Antwort auf alle Fragen findet)

Vielen Dank!

Autor: Rufus Τ. Firefly (rufus) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Ich will eine Rücksprungadresse auf den Stack schieben

Bist Du Dir wirklich ganz sicher, daß Du das in C machen willst?

Autor: Steffffan (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wie funktioniert das denn für nicht konstante Werte in asm? Letztlich 
will ich eine Variable auf den Stack schreiben. Ich wüsste nicht, wie 
das inline asm geht...

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Ich will eine Rücksprungadresse auf den Stack schieben , danach folgt
> ein reti.

Warum springst du nicht direkt?

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

Bewertung
0 lesenswert
nicht lesenswert
In C würde man für sowas einen Funktionszeiger benutzen und die
Dreckarbeit dem Compiler überlassen.  Ein RETI ist ja weiter nichts
als ein Sprung, bei dem das globale Interruptflag gesetzt wird, da
kann man auch vor dem Sprung gleich ein sei() machen (das in seiner
Wirkung ja immer einen Befehl verzögert ist).

Autor: Steffffan (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also nochmal in Ruhe:
Das Ganze gehört zu einem Preemtive Multi-Tasking-Scheduler. Der 
Scheduler (Timer-Interrupt) springt zwischen verschiedenen 
Laufzeitkontexten. D.h. der SP wird auf einen anderen Stack verschoben 
(beim Wechseln des Tasks), auf dem es noch keine Rücksprungadresse gibt. 
Ergo muss man bei bisher noch nicht ausgeführten Tasks die Startadresse 
des entsprechenden Programms unten auf den entsprechenen Stack legen.
Alternativvorschläge zur Implementierung eines RTOS nehme ich zwar 
prinzipiell auch gerne entgegen, aber in dem aktuellen Zusammenhang kann 
ich nicht einfach die C-Funktionalität verwenden (vielleicht aus 
Unwissenheit, deswegen poste ich ja hier).
MfG Steffffan

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Ergo muss man bei bisher noch nicht ausgeführten Tasks die Startadresse
> des entsprechenden Programms unten auf den entsprechenen Stack legen.

Warum arbeitest du dazu im Stackkontext des Prozesses, dessen Stack du 
erst initialisierst? Lege erst ein Array an, fülle es mit den Daten, die 
der Scheduler erwarten würde und löse dann den Kontextwechsel aus bzw. 
warte auf den Timerinterrupt. Der AVR-Port des FreeRTOS-Kernels könnte 
dir da als Inspiration dienen.

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

Bewertung
0 lesenswert
nicht lesenswert
Außerdem denke ich, dass dieser Teil eines jeden RTOS wirklich
besser/einfacher in Assembler implementierbar ist.

Autor: Steffffan (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Lege erst ein Array an, fülle es mit den Daten, die
> der Scheduler erwarten würde und löse dann den Kontextwechsel aus bzw.
> warte auf den Timerinterrupt.

Ich bin mir nicht sicher, ob ich dich richtig verstehe, deshalb 
folgendes zur Klärung.

1. Erst lege ich ein globales Array an, in dem für jeden Task der 
entsprechende Stackpointer, Zustand (Running, Ready etc.) und 
dergelichen gespeichert wird.

2. Jedes mal, wenn ein Task initialisiert wird, lege ich die 
Rücksprungadresse (Anfangsadresse im Program Memory) und eine 
entsprechende Anzahl Nullen (als initialisierter Laufzeitkontext) auf 
den Stack des jeweiligen Tasks.

3. Danach starte ich den Idle-Task, der in der Zeile vor den betreten 
der Endlosschleife die Interrupts einschaltet.

Zum FreeRTOS kenne ich 
http://www.freertos.org/implementation/index.html, habe ich mich auch 
weitestgehend dran gehalten (glaub ich). Allerdings wird ja inzwischen 
die "ISR"-Schreibweise für Interrupts empfohlen, deswegen sieht der 
Interrupt bei mir auch entsprechend anders aus.

Inzwischen habe ich einen Work-Around: Erst 0x02 reinschreiben, dann 
einmal dekrementieren :-)

Dafür gibt es ein neues Problem, nämlich lässt sich ein Pointer nicht in 
eine unsigned int casten. Dazu lasse ich mich natürlich auch gerne 
beraten.

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

Bewertung
0 lesenswert
nicht lesenswert
> 1. Erst lege ich ein globales Array an, in dem für jeden Task der
> entsprechende Stackpointer, Zustand (Running, Ready etc.) und
> dergelichen gespeichert wird.

Ja, und die Register.

> 2. Jedes mal, wenn ein Task initialisiert wird, lege ich die
> Rücksprungadresse (Anfangsadresse im Program Memory) und eine
> entsprechende Anzahl Nullen (als initialisierter Laufzeitkontext) auf
> den Stack des jeweiligen Tasks.

Genau, aber auf den kannst du ja auch über einen ganz normalen Pointer 
zugreifen. Das muß nicht der SP sein. Ich hänge mal die Datei mit 
Taskinitialisierung und Kontextwechsel von FreeRTOS an (hab eh grad drin 
nachgeschaut und es daher offen). Da ist es so gemacht, wie ich es 
meine.

> Allerdings wird ja inzwischen die "ISR"-Schreibweise für Interrupts
> empfohlen, deswegen sieht der Interrupt bei mir auch entsprechend
> anders aus.

Es empfielt sich schon, die mit __attribute__((naked)) zu definieren und 
den Taskwechsel oder zumindest das Speichern und Laden des Kontexts dann 
in inline-Assembler zu implementieren.

> Dafür gibt es ein neues Problem, nämlich lässt sich ein Pointer nicht
> in eine unsigned int casten. Dazu lasse ich mich natürlich auch gerne
> beraten.

Was heißt "läßt sich ... nicht casten"? Was passiert, wenn du es 
versuchst?

Autor: Steffffan (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vielen Dank schon mal soweit!

> Was heißt "läßt sich ... nicht casten"? Was passiert, wenn du es
> versuchst?

Es kommt sinnloses Zeug raus, meistens Null, selten auch 0x0E (was 
nichts mit der Pointeradresse 0x01D2 zutun hat). Habe schon ein Dutzend 
verschiedene Klammerungen etc. versucht, immer umständlicher, immer 
idiotensicherer, will aber nicht.

Kann es sein das gcc es nicht so mit dem Lesen aus Struct-Arrays hat? 
Bastele hier gerade an einer if-Abfrage:
#define VIRGIN 0x03

...

if(taskStates[currentTask].state == VIRGIN)
{
...
}

Die Alternative wird ignoriert. Der Debugger sagt, das in 
taskStates[1].state eine 3 drinsteht. Wenn ich
volatile unsigned char temp = taskStates[currentTask].state;

if(temp == VIRGIN)
{
...
}

mache, zeigt die Watchlist nach der Alternative eine 0 in temp.

Den Pointer ziehe ich auch aus dem gleichen Array und es kommt nur Null 
raus. Besteht da ein Zusammenhang?

Antwort schreiben

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

Wichtige Regeln - erst lesen, dann posten!

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

Formatierung (mehr Informationen...)

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




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

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