mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik AVR, Structs zur Laufzeit reservieren


Autor: Phillip Hommel (philharmony)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Servus,
Ich möchte dem Benutzer die Möglichkeit geben, eine Art Setup zu machen, 
dessen Einstellungen dann im EEPROM gespeichert werden.
Im Program sollen diese Daten dann dazu verwendet werden, verschiedenen 
Struct-Arrays Elemente hinzuzufügen.
In den Structs selbst verwende ich einerseits Zeiger, die teilweise 
wieder auf elemente anderer Struct-Arrays zeigen als auch variablen mit 
einem, zwei oder drei Bit Länge.
Jetzt bin ich mir nicht ganz sicher, wie ich zur Laufzeit malloc / 
realloc auf Struct-Elemente anwende, also welche Größe ich den 
Funktionen übergebe.
Funktioniert da?
xyz = realloc(*array, (sizeof(array) / sizeof(array[0]))+sizeof(array[0]))

Ich würde im Quellcode bereits einen "Prototypen" jedes Arrays 
erstellen, funktioniert dann realloc auch noch? Habe gelesen daß das 
eigentlich nur für Blöcke funktioniert, die per malloc/calloc zugewiesen 
wurden.

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

Bewertung
0 lesenswert
nicht lesenswert
Phillip Hommel schrieb:

> Jetzt bin ich mir nicht ganz sicher, wie ich zur Laufzeit malloc /
> realloc auf Struct-Elemente anwende, also welche Größe ich den
> Funktionen übergebe.

Die Anzahl der Bytes die du haben möchtest.

> Funktioniert da?
>
> xyz = realloc(*array, (sizeof(array) /
> sizeof(array[0]))+sizeof(array[0]))
> 

Das kommt einzige und alleine auf deine Arrays an. Aber so richtig 
sinnvoll sieht das nicht aus.


Zeig ein bischen was von deiner Umgebung.

PS: Gerade wenn man wenig Speicher zur Verfügung hat, ist es oft besser 
einfach eine Obergrenze für den dynamischen Teil einzubauen und alles 
mit dieser Obergrenze statisch zu allokieren.
* malloc und Co benötigen für sich selbst auch Speicher
* Vorhersagen, wann der Speicher voll ist, sind schwierig bis unmöglich
  Dementsprechend steigt die Absturzwahrscheinlichkeit

Mit einer fixen Dimensionierung umgeht man beide Problemkreise.

Autor: Wiesel (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das ist ohnehin ein wenig zur Seite gedacht.
Erstmal ist malloc/free/realloc für Speicher im Ram gedacht.
Lies Dir mal die entsprechenden Docs bzw. Kapitel in einem C Buch durch. 
:-)
Ob struct-Elemente oder nicht ist eigentlich völlig wurst. Aber das 
liest Du dann ja alles.

Theoretisch wäre es möglich Funktionen zu schreiben, die wie malloc/free 
arbeiten aber in Bezug auf das EEPROM.
Das es noch niemand gemacht hat, nehme ich als Hinweis das es keinen 
Wert hat. Aber das kann ja jeder anders sehen.

Schreib Dir zwei Defines in Deinen Code die Anfang und Ende eines 
Bereiches im EEPROM bezeichnen.
Dann nimm die Schreib-Lesefunktionen aus Deinem C-Compiler der für AVR 
angepasst ist.

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

Bewertung
0 lesenswert
nicht lesenswert
Wiesel schrieb:

> Theoretisch wäre es möglich Funktionen zu schreiben, die wie malloc/free
> arbeiten aber in Bezug auf das EEPROM.

so wie ich das verstanden habe, liest er aus dem EEPROM irgendwelche 
Konfigurationsinfo, die dann dazu benutzt wird im SRAM irgendwelche 
Datenstrukturen aufzubauen.

Allerdings macht mich die Bezeichnung "Struct-Array" stutzig. Das alles 
klingt irgendwie nach: Er hat sich verrannt.

Autor: Phillip Hommel (philharmony)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Zeig ein bischen was von deiner Umgebung.
Versuchen wir es mal:
//struktur für variablen
struct variable_memory 
{
  int var_number; //Variablennummer aus header in setup
  int *value; //Zeiger auf Values
  unsigned bit:4; //bit 0-15 für Bool-Variablen
  unsigned is_bool : 1; //variable bool?
  unsigned has_low_part: 1; //variable größer als 16 bit?
};

//Port in
struct port_in_devices
{
  struct variable_memory *mem; //Zeiger auf Variable
  volatile unsigned char *virt_port; //Zeiger auf Virtuellen Port
  unsigned pin:3; //Pin
};

//Port out
struct port_out_devices
{
  struct variable_memory *mem; //Zeiger auf Variable
  volatile unsigned char *virt_port; //Zeiger auf Virtuellen Port
  unsigned pin:3; //Pin
  unsigned is_member_of_lamptest:1; //Soll bei Lamptest angehen
  unsigned is_member_of_dim:1; //Soll bei Dim gedimmt werden
};


//Encoder
struct encoder_devices
{
  struct variable_memory *mem; //Zeiger auf Variable
  volatile unsigned char   *channel_a_port; //Zeiger auf Port Kanal a
  unsigned channel_a_pin:3; //Pin Kanal a
  volatile unsigned char *channel_b_port; //Zeiger auf Port Kanal b
  unsigned channel_b_pin:3; //Pin Kanal b
  unsigned long min_value; //Mindestwert
  unsigned long max_value; //Maximalwert
  unsigned long increment; //um wieviel erhöhen
  unsigned has_modulo:1; //Verhalten bei überlauf
};



/////////////////////////////////////////////
////////*****AB HIER ZUWEISUNGEN*****////////
/////////////////////////////////////////////

//Zentraler Speicher
int values[8];  //Hier ist der Zentrale Speicher

//Variablen Index
#define SYSTEM_VAR 0x0000 //Nummer der Systemvariablen
#define INTERNAL_VAR 0xFFFF //Nummer der Intern verwendeten Variablen

struct variable_memory variables[]=          //max 256 variablen
{
 {01, &values[0], 0, 1, 0}, //Variable 01, Speicherziel Values_0, Bit 0, Länge 01, Kein Lowpart
 {10, &values[1], 0, 1, 0}, //Variable 10, Speicherziel Values_1, Bit 0, Länge 01, Kein Lowpart
 {20, &values[2], 0, 0, 0}, //Variable 20, Speicherziel Values_1, Bit 0, Länge 16,  Hat Lowpart
/*usw...*/
 {SYSTEM_VAR, &values[7], 0, 0, 0}, //Variable 0xFFFF, Speicherziel Values_5, Bit 0, Länge 16, Kein Lowpart 
};

//Port Inputs
struct port_in_devices in_ports[]=
{
 {&variables[4], &virtual_port[OFFSET_PORTA + 0], 2}, //verweist auf Variable2, porta ungruppiert, Pin1
/*...*/
};

//Port Outputs
struct port_out_devices out_ports[]=
{
 {&variables[0], &virtual_port[OFFSET_PORTB + 1], 0, 1}, //verweist auf Variable3, portb Gruppe1, Pin0, Lampentest1
};

//Encoder Inputs
struct encoder_devices encoders[]=
{
 {&variables[10], &virtual_port[OFFSET_PORTC + 0], 0, &virtual_port[OFFSET_PORTC + 0], 1, 0, 60, 1, 0}, 
  /*verweist auf Variable20, portc Ungruppiert, Pin0, portc Ungruppiert Pin1, min 0, max 60, increment 1, kein Modulo*/
};

Im Setup soll jetzt eingestellt werden, wieviele Portin/ Out ("Schalter 
und Lämpchen") sowie Encoder und weitere, aus Übersichtsgründen im o.g. 
Beispiel weggelassenene Geräte am Controller hängen und an welchen Pins 
sie anliegen.
In der Init-Funktion zu Beginn der Programms sollen dann die Arrays
in_pots[] 
,
out_ports[]
 und
encoders[]
Um diese Geräte erweitert werden, entsprechend den Daten im EEPROM.

>Allerdings macht mich die Bezeichnung "Struct-Array" stutzig. Das alles
>klingt irgendwie nach: Er hat sich verrannt.

Da ich nicht direkt aus der IT-Sparte komme, sondern mir das ganze 
gezwungenermaßen selbst beibringe drücke ich mich wohl manchmal nicht 
ganz fachlich Korrekt aus. Ich kenne keine richtigere Bezeichnung für 
eine Sammlung an Structs wie zB encoders[], daher bitte entschuldigt die 
manchmal Amateurhafte Ausdrucksweise.

>so wie ich das verstanden habe, liest er aus dem EEPROM irgendwelche
>Konfigurationsinfo, die dann dazu benutzt wird im SRAM irgendwelche
>Datenstrukturen aufzubauen.

Genauso ist es, allerdings will ich die Strukturen nicht direkt aufbauen 
- habe sie ja schon definiert -, sondern zu den entsprechenden "Arrays" 
eben neue Elemente hinzufügen..

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

Bewertung
0 lesenswert
nicht lesenswert
Phillip Hommel schrieb:

> Genauso ist es, allerdings will ich die Strukturen nicht direkt aufbauen
> - habe sie ja schon definiert -,

:-)

Struktur kann beides bedeuten:
die struct Definition als auch die sich dann tatsächlich im Speicher 
ergebende Datenstruktur in ihrer vollen Pracht.

> sondern zu den entsprechenden "Arrays"
> eben neue Elemente hinzufügen..

Genau das würde ich auf einem AVR nicht machen.
So wie du das machst, macht man das auf einem PC. Na ja fast. Dort würde 
man anstelle der Arrays wahrscheinlich Listen benutzen. Sind einfacher 
zu erweitern ohne dass ständig alles umkopiert werden muss.

Auf einem AVR hast du aber ein Problem. Du hast nur sehr wenig SRAM und 
die Fragmentierung des Speichers kann zu einem Problem werden. Grob 
gesagt: Du hast zwar in Summe zb 200 Bytes frei, aber diese 200 Bytes 
sind nirgend in einem Rutsch verfügbar, sondern alle Speicherlöcher 
zusammen ergeben 200 Bytes. Da wird es dann schwierig ein Array, welches 
200 Bytes benötigt anzulegen.

Das ist das eine. Dem könnte man noch mit einer Liste anstelle von 
Arrays zumindest teilweise entgegen wirken.

Aber das rettet dich nicht vor dem anderen Problem: Abzuschätzen wann 
der Speicher eigentlich voll ist.

Und um dem entgegen zu wirken, ist die einfachste Variante die, die 
überlegst dir sinnvolle Obergrenzen. Zb. Dass es nicht mehr als 20 
Eingänge geben kann und nicht mehr als 30 Ausgänge. Dann hilft dir das 
System zumindest insofern weiter, als dir die WinAvr ToolChain Bescheid 
gibt, ob diese Menge an Devices überhaupt noch in den Speicher passen 
wird.
#include <stdio.h>

#ifndef TRUE
#define TRUE 1
#define FALSE 0
#endif

//struktur für variablen
struct variable_memory 
{
  int *    value; //Zeiger auf Values
  unsigned bit:4; //bit 0-15 für Bool-Variablen
  unsigned is_bool : 1; //variable bool?
  unsigned has_low_part: 1; //variable größer als 16 bit?
};

//Port in
struct port_in_devices
{
  struct variable_memory *mem; //Zeiger auf Variable
  volatile unsigned char *virt_port; //Zeiger auf Virtuellen Port
  unsigned pin:3; //Pin
};

//Port out
struct port_out_devices
{
  struct variable_memory *mem; //Zeiger auf Variable
  volatile unsigned char *virt_port; //Zeiger auf Virtuellen Port
  unsigned pin:3; //Pin
  unsigned is_member_of_lamptest:1; //Soll bei Lamptest angehen
  unsigned is_member_of_dim:1; //Soll bei Dim gedimmt werden
};


//Encoder
struct encoder_devices
{
  struct variable_memory *mem; //Zeiger auf Variable
  volatile unsigned char   *channel_a_port; //Zeiger auf Port Kanal a
  unsigned channel_a_pin:3; //Pin Kanal a
  volatile unsigned char *channel_b_port; //Zeiger auf Port Kanal b
  unsigned channel_b_pin:3; //Pin Kanal b
  unsigned long min_value; //Mindestwert
  unsigned long max_value; //Maximalwert
  unsigned long increment; //um wieviel erhöhen
  unsigned has_modulo:1; //Verhalten bei überlauf
};

//Zentraler Speicher
#define MAX_NR_VALUES       8
#define MAX_NR_VARIABLES  256
#define MAX_NR_IN_PORTS    20
#define MAX_NR_OUT_PORTS   20
#define MAX_NR_ENCODERS    10

int values[ MAX_NR_VALUES ];

struct variable_memory variables[ MAX_NR_VARIABLES ];

struct port_in_devices in_ports[ MAX_NR_IN_PORTS ];
unsigned char nr_in_ports = 0;

struct port_out_devices out_ports[ MAX_NR_OUT_PORTS ];
unsigned char nr_out_ports = 0;

struct encoder_devices encoders[ MAX_NR_ENCODERS ];
unsigned char nr_encoders = 0;

unsigned char AddVariable( int Nr, int *Value, unsigned char isBool, unsigned char hasLow )
{
  if( Nr < MAX_NR_VARIABLES ) {
    variables[Nr].value        = Value;
    variables[Nr].is_bool      = isBool;
    variables[Nr].has_low_part = hasLow;
    return TRUE;
  }
  
  return FALSE;
}

unsigned char AddInPort( struct variable_memory * var, volatile unsigned char * port, unsigned char pin )
{
  if( nr_in_ports < MAX_NR_IN_PORTS - 1 ) {
    in_ports[ nr_in_ports ].mem       = var;
    in_ports[ nr_in_ports ].virt_port = port;
    in_ports[ nr_in_ports ].pin       = pin;
    
    nr_in_ports++;
    
    return TRUE;
  }
  
  return FALSE;
}

unsigned char AddOutPort( struct variable_memory * var, volatile unsigned char * port, unsigned char pin,
                          unsigned char doLampTest, unsigned char doDim )
{
  if( nr_out_ports < MAX_NR_OUT_PORTS - 1 ) {
    out_ports[ nr_out_ports ].mem                    = var;
    out_ports[ nr_out_ports ].virt_port              = port;
    out_ports[ nr_out_ports ].pin                    = pin;
    out_ports[ nr_out_ports ].is_member_of_lamptest  = doLampTest;
    out_ports[ nr_out_ports ].is_member_of_dim       = doDim;
    
    nr_out_ports++;
    
    return TRUE;
  }
  
  return FALSE;
}

int main()
{
  // systemvariablen anlegen
  AddVariable(  1, &values[0], FALSE, FALSE );
  AddVariable( 10, &values[1], TRUE,  FALSE );
  AddVariable( 20, &values[2], FALSE, TRUE );

  // alle systemweiten Eingänge
  AddInPort( &variables[4], &virtual_port[OFFSET_PORTA + 0], 2 );

  // alle systemweiten Ausgänge
  AddOutPort( &variables[0], &virtual_port[OFFSET_PORTB + 1], 0, TRUE, FALSE );
}

So blöd es auch klingt. Mit so einer Strategie wirst du wahrscheinlich 
mehr Devices anlegen können, als wie wenn du das ganze dynamisch mit 
realloc aufbaust. Speicherfragmentierung lässt grüßen.

Autor: Phillip Hommel (philharmony)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Klingt wie immer super vernünftig und logisch, vielen Dank erstmal. Ich 
werd mir das heute Abend mal in Ruhe zu Gemüte führen. Soweit ich das 
bisher verstanden habe sehe ich für alle Geräte eine maximal Anzahl vor 
(die dann unter maximaler Ausnutzung aller den Speicher nicht überlaufen 
lässt) und fülle sie einfach eine nach der Anderen mit Daten, wobei die 
"nr_of_..."-Variable mir angibt, bis wohin überhaupt gefüllt ist.
Das Setup soll sowieso von einem PC-Program aus gesteuert werden, daher 
kann ich dort schon vorab eine Menge abfangen und könnte auch mehr 
speicher vorsehen als maximal reinpassen würde.
(Denn ich weiß nicht, ob der Benutzer alle 144 "virtuellen Pins" für 
Schalter, oder für LEDs oder in mischform benutzen will, oder 48 davon 
außen vor lässt um 8 ADCs zu verwenden).
In dieser Flexibilität gegenüber dem Benutzer liegt ja meine 
Hauptschwierigkeit.
Werde das ganze aber mal nach deiner Idee angehen, die MAX_NUMBER_OF 
können ja auch im EEPROM stehen und vom Setup so eingestellt werden, daß 
die Summe wieder passt...

Autor: Phillip Hommel (philharmony)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
So, hab das ganze mal angeschaut bin dabei es einzubauen, das sieht 
wirklich nach der vernünftigsten Lösung aus.
Ich habe mal noch eine Frage zum Thema Speicherverbrauch: Wieviel Platz 
braucht eigentlich ein Zeiger? Wird der als Variable angelegt oder 
intern nur verlinkt?
Wenn ich jetzt 144 port_in_devices habe (soviel kann die hardware), 
würde es dann sinn machen, die True und False-Eigenschaften nicht als 
Byte reinzuschreiben, sondern einmal True und einmal False zu speichern 
und dann nur noch darauf zu zeigen? Sind immerhin 18 Byte pro 
Eigentschaft...

Autor: P. S. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Statt Arrays fuer die verschiedenen Typen bereit zu halten, duerfte es 
besser sein ein Array fuer alle Typen gemeinsam zu haben. Am einfachsten 
geht das indem du eine Union fuer alle Typen erstellst und um die 
nochmal eine struct legst, die einen Identifier hat, welcher Typ 
enthalten ist.

So ungefaehr:

union AllTypesUnion
{
  struct TypeA a;
  struct TypeB b;
  struct TypeC c;
};

struct AllTypesStruct
{
  uint8_t type;

  union AllTypesUnion content;
};

Auf die Pointer wuerde ich nach Moeglichkeit verzichten, die Ports 
kannst du ja auch einfach durchnummerieren.

Autor: Phillip Hommel (philharmony)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Bringt das Speicherplatz? Die Ports sind durchnummeriert und ich greife 
ja sowieso mit verschiedenen Funktionen darauf zu wodurch die 
verschiedenen typen schon vorteilhaft sind.
for(i=0; i < (sizeof out_ports / sizeof (struct port_out_devices)); i++)
  {
    set_port(&out_ports[i]);
  }

Ich bin grade am rumrechnen, wieviele Variablen und bytes im 
Zentralspeicher ich wirklich brauche, und daher eben meine frage wie ich 
noch Speicherplatz sparen kann...

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

Bewertung
0 lesenswert
nicht lesenswert
Phillip Hommel schrieb:
> Bringt das Speicherplatz?

In deinem Fall leider nicht, weil die diversen structs unterschiedliche 
Speicheranforderungen haben.

> Ich bin grade am rumrechnen, wieviele Variablen und bytes im
> Zentralspeicher ich wirklich brauche, und daher eben meine frage wie ich
> noch Speicherplatz sparen kann...

Viel wird da nicht mehr gehen.

Eines würde noch gehen. Anstatt der Pointer könntest du Indizes 
verwenden. Wenn du zb nur max. 200 values haben kannst, die noch dazu 
alle in einem Array sind.

Anstelle von
struct variable_memory 
{
  int *    value; //Zeiger auf Values
  unsigned bit:4; //bit 0-15 für Bool-Variablen
  unsigned is_bool : 1; //variable bool?
  unsigned has_low_part: 1; //variable größer als 16 bit?
};

hast du dann
struct variable_memory 
{
  unsigned char valueNr;
  unsigned bit:4; //bit 0-15 für Bool-Variablen
  unsigned is_bool : 1; //variable bool?
  unsigned has_low_part: 1; //variable größer als 16 bit?
};

was dir bei jedem struct variable_memory Objekt 1 Byte einbringt. Für 
das einzelne Objekt ist das nicht viel, aber in Summe läppert es sich.
Für die anderen Structs dann sinngemäss genauso: Überall dort wo du 
einen Pointer in ein Array hast UND du weißt das das Array weniger als 
255 Elemente haben wird, kannst du den Pointer durch den Index ersetzen.
255 deshalb, weil du dann den Indexwert 0xFF für 'kein Eintrag' (also 
das was vorher der NULL Pointer war) reservierst.

(Auf der anderen Seite erhebt sich die Frage, warum du überhaupt einen 
Verweis aus einem variable_memory Objekt auf einen value hast. Warum 
nicht den value direkt in die struct variable_memory Objekt einbauen?)

Autor: Phillip Hommel (philharmony)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>>Auf der anderen Seite erhebt sich die Frage, warum du überhaupt einen
>>Verweis aus einem variable_memory Objekt auf einen value hast. Warum
>>nicht den value direkt in die struct variable_memory Objekt einbauen?

Das hat den Hintergrund, daß ich teilweise nur Bits (state von 
Port-Pins) speichere, und dann einfach 16 Pins auf den selben Value 
zeigen lasse plus der Bit-Nummer.
Auf der anderen Seite sind manche Variablen länger als 16 Bit, denen 
gebe ich dann einen Low-Part und nehme den Valuie an der "bezeigten" 
Adresse und den an der nächsten. Damit habe ich dann 32bit.
Das mit den Indizes wäre eine Idee, also anstatt dem Pointer auf Int 
einfach die nummer des Values im Array reinschreiben? Ich dachte immer 
Pointer wären schön klein bzw würde sowieso angelegt werden, 
aberanscheinend fressen die auch ganz schön Speicher...

So lange ich selbst die Hardware in meinem eigenen Projekt konfiguriere 
kann ich das alles wunderbar "Hard-Coded" in den Quelltext aufnehmen und 
brauche die ganze Initialisierung nicht, sobald das aber jemand anderes, 
im besten Fall später ein Kunde nach seinen Wünschen zuweisen soll bin 
ich z.Zt. einfach am Ende mit meinem Wissen.
Ich sollte
Eventuell könnte man noch so etwas wie beim Paparazzi-Projekt (bekannt?) 
machen, wo ein Compiler im Paparazzi-Center mit dabei ist und man darin 
per Code-Generator die Zuweisung macht.

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

Bewertung
0 lesenswert
nicht lesenswert
Phillip Hommel schrieb:

> Das mit den Indizes wäre eine Idee, also anstatt dem Pointer auf Int
> einfach die nummer des Values im Array reinschreiben? Ich dachte immer
> Pointer wären schön klein

Neben double und long sind Pointer normalerweise einer der längsten 
Dtaentypen, die es in C gibt. Schliesslich muss in einem Pointer ja auch 
die auf dem System größte möglcihe Speicheradresse in Zahlenform 
gespeichert werden können. Kann eine Architektur mehr als 256 Bytes 
adressieren, muss ein Pointer schon mal mindestens 2 Bytes groß sein.

> bzw würde sowieso angelegt werden,

Nö, warum sollen sie?
Eine Pointervariable ist auch nur eine Variable in der ein Zahlenwert 
gespeichert wird. Definierst du keine Variable, wird sie auch nicht 
angelegt.
Jedes Objekt hat zwar eine Adresse im Speicher, so wie jedes Haus in 
einer Strasse eine Hausnummer hat. Aber wenn niemand da ist, der sich 
die Hausnummer notiert, muss man auch keinen Zettel reservieren, auf dem 
man sich die Hausnummer aufschreiben kann.

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.