www.mikrocontroller.net

Forum: Compiler & IDEs c code optimierung ev. assembler?


Autor: Patrick B. (p51d)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich habe hier ein kleines Problem:

Die Ansteuerung 2-er DA-Wandler von einem MCU aus sollte sehr rasch 
erfolgen, leider wahrscheinlich zu rasch für normales c.

Zur Info:
control[] ist ein 8-Bit Array, bei dem die Bits 0 bis 4 für einen 
Delay-Wert verwendet werden und die Bits 5 bis 7 für LED-Zustände.
x_achse und y_achse sind je 16 Bit grosse Arrays, von denen eigenltich 
nur 12 Bits gebraucht werden.
SysCount ist eine Variable die von einem HW-Timer alle 10us 
inkrementiert wird. SysCount kann Werte von 3 bis 31 enthalten.

Die Funtkion die die Array-Werte an die Parallel-DAC ausgibt:
void DAC_Output(uint16_t val){    // converts 12Bit to the DA Converter
  if(val & (1<<8)){
    PORTC |= (1<<3);
  }
  else{
    PORTC &= (~(1<<3));
  }
  if(val & (1<<9)){
    PORTC |= (1<<2);
  }
  else{
    PORTC &= (~(1<<2));
  }
  if(val & (1<<10)){
    PORTC |= (1<<1);
  }
  else{
    PORTC &= (~(1<<1));
  }
  if(val & (1<<11)){
    PORTC |= (1<<0);
  }
  else{
    PORTC &= (~(1<<0));
  }
  PORTA = val;
}

und die Steuerung:
void Output(){
  static uint16_t p = 0;
  if((control[p] & (1<<7)) != 0){
    LED_red_on;
  }
  else{
    LED_red_off;
  }
  if((control[p] & (1<<6)) != 0){
    LED_green_on;
  }
  else{
    LED_green_off;
  }
  if((control[p] & (1<<5)) != 0){
    LED_blue_on;
  }
  else{
    LED_blue_off;
  }
  if((control[p] & 0x1F) == SysCount){
    DAC_Output(x_achse[p]);
    X_select;
    no_select;
    DAC_Output(y_achse[p]);
    Y_select;
    no_select;
    if((p == 299) || (p == 599)) RXTX_Reg = ENQ;
    if(p == 574) p = 0;
    else p ++;
    SysCount = 0;
  }  
}
wobei mir die Zeile "if((control[p] & 0x1F) == SysCount)" das ganze 
verunstalltet. Ergibt die Maske 3 so ist der SysCount zu schnell, nehme 
ich anstelle vom "==" ein ">" läufts etwas schneller, aber nicht viel.
Schreibe ich anstelle der Maske eine 3 so läuft das Programm im 
gewohnten Tempo (schnell).

Jetzt ist meine Frage, wie kann ich die Durchlaufszeit des Programms so 
minim wie möglich halten?
Die Funktion DAC_Output() in Assembler schreiben und der Teil mit den 
LEDs auch?
Einziges Problem: Ich habe in der Schule nur etwa 4 Lektionen Assembler 
gehabt, sonst nur C.

Was mir etwas Bedenken macht: Das ganze wird noch mit einem RS232-Teil 
ergänzt, welcher zwar schon im Prinzip "State-machine" aufgebaut ist, 
aber sicherlich noch etwas Rechenzeit in anspurch nimmt.

Habt ihr etwaige Vorschläge?
MFG
Patrick

Autor: Stefan B. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Je nach dem ob die Bits 8-11 in val gesetzt sind, setzt du die Bits 3-0 
in POSTC. Dazu verwendest du rel. umfangreiche if/else Konstruktinen.

Wie wäre es, die Bits iN PORTC geschickter zuzuordnen,damit die 
Reihenfolge identisch ist 8-11 => 0-3? Und dann über Bitmanipulation 
mit einer Bitmaske die 4 Bits auf einen Streich zu setzen? Ähnliches 
geht vielleicht auch beim Setzen der LEDs. Dann DAC_Output() ggf. noch 
als inline Funktion schreiben.

Insgesamt denke ich, dass du nicht "besonders" viel rausholst. Die 
Zeitfresser verstecken sich meiner Meinung nach woanders in deinem 
Programm.

Autor: Patrick B. (p51d)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
um die if/else-Geschichte durch einen einfachen Schiebebefehl wegmachen 
zu können, müsste ich Hardware-Änderungen vornehmen...(Leiterbahnen 
trennen und Drähtchen ziehen.

das Problem ist, dass das ganze Prog nicht länger als 20-30us (besser 
~10) Durchlaufszeit haben darf, und je nach dem, ob die hälfte der 
Arrays ausgegeben wurde, noch die RS232-Funktion und der RX-Interrupt 
dazukommt.
Zum Testen habe ich einfach mal in ner h Datei die Arrays 
hineingeschrieben und dann ausgegeben.

Was mich vorallem mistrauisch macht ist, dass es so extreme Unterschiede 
zwischen diesen beiden Varianten gibt:
if((control[p] & 0x1F) == SysCount)
if(3 == SysCount)
Der Ausmaskierte Wert würde auch 3 ergeben... Variante 1 hat Verzögerung 
und Variante 2 nicht.

Hier ist mal noch der Rest des Codes:
// Einbinden von bestehenden Modulen
#include <avr/io.h>                            // AVR Standart IO Register Definitionen
#include <stdio.h>                            // Standart IO Register
#include <avr/iom644.h>                          // ATMega644 Register Definitionen
#include <avr/iomxx4.h>                              // ATMegaXX4 Register Definitionen
#include <avr/interrupt.h>                        // Interrupt Register Definitionen
#include "arrays.h"

//------------------------------------------------------------------------------
// Definitionen
#define LED_green_on   (PORTD |= (1<<5))
#define LED_green_off (PORTD &= (~(1<<5)))
#define LED_red_on  (PORTB |= (1<<3))
#define LED_red_off  (PORTB &= (~(1<<3)))
#define LED_blue_on  (PORTD |= (1<<7))
#define LED_blue_off  (PORTD &= (~(1<<7)))     
#define X_select    (PORTB &= (~(1<<0)))
#define Y_select    (PORTB &= (~(1<<1)))
#define no_select    ((PORTB |= (1<<0)) & (PORTB |= (1<<1)))

#define search 0
#define waitSOH 1
#define waitCONTROL 2
#define waitAXIS 3
#define waitEOT 4
#define Buf_max 49
#define RXTX_Reg UDR0
#define ENQ 0x05
#define SOH 0x01                            // Start of Header
#define EOT 0x04                            // End of Transmission

#define F_CPU 24000000                          // Quarzfrequenz
#define BAUDRATE 3000000                        // Baudrate für Kommunikation

int uart_putchar(char c, FILE *stream);                  //privat für printf  
static FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL,_FDEV_SETUP_WRITE);

//------------------------------------------------------------------------------
// Variablen
static volatile int SysCount = 0;                    // Nutz-Counter
static volatile int index_rxBuf_R = 0;                  // Index wo zuletzt gelesen wurde
static volatile int index_rxBuf_W = 0;                  // Index wo zuletzt geschrieben wurde
static volatile int rxBuf[Buf_max+1];                  // RX Buffer

//int control[600]={};
//uint16_t x_achse[600]={};
//uint16_t y_achse[600]={};

//------------------------------------------------------------------------------
// Funktionen
void DAC_Output(uint16_t val){                      // converts 12Bit to the DA Converter
  if(val & (1<<8)){
    PORTC |= (1<<3);
  }
  else{
    PORTC &= (~(1<<3));
  }
  if(val & (1<<9)){
    PORTC |= (1<<2);
  }
  else{
    PORTC &= (~(1<<2));
  }
  if(val & (1<<10)){
    PORTC |= (1<<1);
  }
  else{
    PORTC &= (~(1<<1));
  }
  if(val & (1<<11)){
    PORTC |= (1<<0);
  }
  else{
    PORTC &= (~(1<<0));
  }
  PORTA = val;
}
void SysInit(){
  int i = 0; 
  
  DDRA = 0xFF;                            // IO Definieren
  DDRB = 0x1B;
  DDRC = 0x0F;
  PORTC = 0xF0;                            // Pull up einschalten
  DDRD = 0xF0;
  
  UBRR0H = ((F_CPU/(BAUDRATE*8L)-1) >> 8);                  // calc Baud
    UBRR0L = (uint8_t)(F_CPU/(BAUDRATE*8L)-1);               // Double speed
  UCSR0A |= (1<<U2X0);                        // RS232 Settings: double speed
  UCSR0B |= (1<<RXCIE0)|(1<<RXEN0)|(1<<TXEN0);            // Complete Interrupts, RX und TX einschalten
  UCSR0C |= (1<<UCSZ01)|(1<<UCSZ00);                  // Asynchron, 8-Bit  
  
  TCCR2A |= (1<<WGM21);                        // Timer2 löst mit 100kHz Interrupt aus
  TCCR2B |= (1<<CS21);
  TIMSK2 |= (1<<OCIE2A);
  OCR2A = 30;

  for(i=0;i<=Buf_max;i++){
    rxBuf[i] = 0;
  }
  DAC_Output(2048);
  X_select;
  Y_select;
  no_select;
  
  stdout = &mystdout;  
  
  sei();                                // Global Interrupts einschalten
}

int uart_putchar(char c, FILE *stream)      //Nur für Ausgabe mit printf
    {
      if (c == '\n')
        uart_putchar('\r', stream);
      loop_until_bit_is_set(UCSR0A, UDRE0);
      UDR0 = c;
      return 0;
    }
  
  
void RS232(void){
  static int RS232_state = 0;                      // Protokollstatus
  static int index_par = 0;                      // Array-Variable für Parameters
  static uint16_t k = 0;
  if(index_rxBuf_W == index_rxBuf_R){                  // Buffer Error abgleichen
    return;                              // Wenn Error => aus Funktion heraus springen
  }
  if(index_rxBuf_R == Buf_max){                    // Erhöhen
    index_rxBuf_R = 0;
  }
  else{
    index_rxBuf_R ++;
  }
  switch (RS232_state){                        // RS232 Protokoll
    case search:
      if(rxBuf[index_rxBuf_R] == '@'){
        RS232_state = waitSOH;
        printf("dac_board_v1");
      }
      break;
    case waitSOH:
      switch (rxBuf[index_rxBuf_R]){
        case SOH:  
          RS232_state = waitCONTROL;              // In nächsten State wechselnd
          break;
        case EOT:
          RS232_state = waitSOH;
          break;
        default:
          RXTX_Reg = NACK;
          break;
      }
      break;
    case waitCONTROL:
      control[k] = rxBuf[index_rxBuf_R];
      RS232_state = waitAXIS;
      break;
    case waitAXIS:
      switch (index_par){
        case 0:
          x_achse[k] = (rxBuf[index_rxBuf_R] << 4);
          index_par ++;
          break;
        case 1:
          x_achse[k] |= (x_achse[k - 1] | (rxBuf[index_rxBuf_R] >> 4));
          k ++;
          y_achse[k] = ((rxBuf[index_rxBuf_R] & 0x0F) << 8);
          index_par ++;
          break;
        case 2:
          y_achse[k] |= (y_achse[k - 1] | (rxBuf[index_rxBuf_R]));
          index_par = 0;
          RS232_state = waitEOT;
          break;
      }
      break;
    case waitEOT:
      if(rxBuf[index_rxBuf_R] == EOT){
        RS232_state = waitSOH;
      }
      break;
  }
  if(k == 599) k = 0;
  else k ++;
}
void Output(){
  static uint16_t p = 0;
  if((control[p] & (1<<7)) != 0){
    LED_red_on;
  }
  else{
    LED_red_off;
  }
  if((control[p] & (1<<6)) != 0){
    LED_green_on;
  }
  else{
    LASER_green_off;
  }
  if((control[p] & (1<<5)) != 0){
    LED_blue_on;
  }
  else{
    LED_blue_off;
  }
  if((control[p] & 0x1F) == SysCount){
    DAC_Output(x_achse[p]);
    X_select;
    no_select;
    DAC_Output(y_achse[p]);
    Y_select;
    no_select;
    if((p == 299) || (p == 599)) RXTX_Reg = ENQ;
    if(p == 574) p = 0;
    else p ++;
    SysCount = 0;
  }  
}
//------------------------------------------------------------------------------
// INTERRUPS
ISR(TIMER2_COMPA_vect){                          // Mit 100kHz SysCount erhöhen
  SysCount ++;
}
ISR(USART0_RX_vect){                          // Received Complete Interrupt
  if(index_rxBuf_W == Buf_max){                    // Damit Buffer nicht überläuft
    index_rxBuf_W = 0;
  }
  else{
    index_rxBuf_W ++;
  }
  if(index_rxBuf_W == index_rxBuf_R){                  // Wenn Write-Index Read-Index eingeholt hat
    return;
  }
  else{                                // Daten speichern
    rxBuf[index_rxBuf_W] = RXTX_Reg;
  }
}
//------------------------------------------------------------------------------
// Hauptfunktion (wird beim Start ausgeführt) 
int main(void){
    SysInit();                              // Systeminitialisierung
  while(1){
    RS232();
    Output();
  }
return 0;
}
//------------------------------------------------------------------------------

Der MCU ist wegen FTDI und Baudrate schon übertaktet: anstelle 20Mhz 
24Mhz.
Muss ich hier den MCU Wechseln und einen nehmen mit > 40Mhz oder was?

Danke für die rasche Antwort
MFG
P51D

Autor: Stefan B. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich sehe nicht wie dein control[] definiert ist, Ich vermute auch 
volatile? Siehst du Speed-Änderungen, wenn du das zu Beginn von Output() 
in eine lokale, non-volatile Variable kopierst und dann die Abfragen mit 
der lokalen Variablen machst? Das setzt allerdings voraus, dass sich 
control[] während der Laufzeit von Output() nicht ändern kann/soll.

Die Zuordnung der gedrehten Bits könnte man auch über eine 16-elementige 
Lookup-Tabelle versuchen, wenn der Platz im RAM das her gibt.

Autor: Patrick B. (p51d)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
die Arrays sind so definiert:
static int control[600]={};
static uint16_t x_achse[600]={};
static uint16_t y_achse[600]={}

Stefan B. schrieb:
> Die Zuordnung der gedrehten Bits könnte man auch über eine 16-elementige
> Lookup-Tabelle versuchen, wenn der Platz im RAM das her gibt.

durch die sehr grossen Arrays ist der minimale 4k SRAM schnell 
aufgebraucht.
erfolgreich getestet habe ich es bis jetzt aber nur mit 575 Werten 
grossen Arrays.

Das ganze kann man als FIFO ansehen, nur werden die UART-Werte noch 
etwas zerlegt.

also doch andere Controller?

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Patrick B. schrieb:
> Was mich vorallem mistrauisch macht ist, dass es so extreme Unterschiede
> zwischen diesen beiden Varianten gibt:
if((control[p] & 0x1F) == SysCount)
if(3 == SysCount)
> Der Ausmaskierte Wert würde auch 3 ergeben... Variante 1 hat Verzögerung
> und Variante 2 nicht.

Warum wundert dich das? Wenn control und p Variablen sind, muss das 
zur Laufzeit ausgerechnet werden. Das dauert natürlich länger wie der 
Vergleich mit einer simplen Konstante.

Du solltest mit den "Optimierungen" als erstes mal bei deinem 
Programmierstil beginnen. Warum sind z.B. alle Variablen 16 Bit groß, 
obwohl bei den meisten 8 Bit reichen würde?
Damit ist das hier auch wirklich kein Wunder:
> durch die sehr grossen Arrays ist der minimale 4k SRAM schnell
> aufgebraucht.
Ein großer Teil des verbrauchten Platzes ist einfach nur verschwendet. 
Und der Code wird dadurch auch deutlich verlangsamt.

Autor: Patrick B. (p51d)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Stefan Ernst schrieb:
> Du solltest mit den "Optimierungen" als erstes mal bei deinem
> Programmierstil beginnen. Warum sind z.B. alle Variablen 16 Bit groß,
> obwohl bei den meisten 8 Bit reichen würde?
> Damit ist das hier auch wirklich kein Wunder:
Sorry, es ist schon etwas spät und vielleicht habe ich was vorhin beim 
überfliegen übersehen: wo hast du VIELE 16-Bit Variablen gefunden dies 
nicht sein müssten?
1. und 2. Variable:
> uint16_t x_achse[600]={};
> uint16_t y_achse[600]={};beide sind zur abspeicherung von 12-Bit Werten gedacht, 
hatte zwar mal vor, das mit Bitmuster zu lösen, aber da ist der Zugriff auch nicht 
der schnellste
3. Variable
> uint16_t val;
Dient zur übergabe von den Array-Werten (ok, könnte man weglassen, 
verunschöndert aber den Code
4. und 5. Variable
> static uint16_t k = 0;
> static uint16_t p = 0;
Dient zum Zugriff auf die 600-Plätze grossen Arrays. Kannst du mit einer 
8-Bit Variable bis auf 600 zählen? Ich nicht.

>> durch die sehr grossen Arrays ist der minimale 4k SRAM schnell
>> aufgebraucht.
> Ein großer Teil des verbrauchten Platzes ist einfach nur verschwendet.
> Und der Code wird dadurch auch deutlich verlangsamt.
?? Wie meinst du das?
Die Arrays machen den Grossteil des Platzes aus und die sind bewusst so 
gross, damit Windows, welches mir daten Schicken soll, später nicht alle 
30us Werte Verarbeiten muss, sondern zwischendurch auch mal ein paar 
sekunden Pause hat.

Bin immer offen für Kritik und Vorschläge, aber bitte nicht einfach so 
ins Grüne hinaus kritisieren.

MFG
P51D

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Patrick B. schrieb:
> Sorry, es ist schon etwas spät und vielleicht habe ich was vorhin beim
> überfliegen übersehen: wo hast du VIELE 16-Bit Variablen gefunden dies
> nicht sein müssten?

Gegenfrage: zeige mir eine einzige Variable, die nicht 16 Bit groß ist?
(Hinweis: "int" sind 16 Bit)

Autor: Patrick B. (p51d)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Eine int Variable auf einem 8-Bit MCU ist nicht 16Bit gross.
Wenn das so sein würde, wäre ich in den letzten 2 Jahren vergebens zur 
Schule gegangen und hätte zahlreiche Projekte und Programme nicht 
speziell auf 16Bit int Variablen abändern müssen, weil die Berechnungen 
nicht mehr in einer normalen 8Bit Variable Platz gehabt hätten.

wozu dann auch noch uint16_t wenns doch schon 16 Bit gross wäre?
http://www.mikrocontroller.net/articles/AVR-GCC-Tu...

Sobalt die MCU grösse steigt steigt auch der Wertebereich eines Integer
8Bit MCU => 8Bit
16Bit MCU => 16Bit
32Bit MCU (oder PC Programmierung nix mit int32_t) => 32Bit

Also wenn du mir hier nicht zustimmst, dann habe ich wohl wirklich etwas 
falsch gemacht in den letzten 2 Jahren und ich entschuldige mich jetzt 
schon für meine Unwissenheit

Autor: Karl (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Entschuldigung angenommen: int auf AVR-GCC sind 16 Bit! Doku lesen.

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Patrick B. schrieb:
> Eine int Variable auf einem 8-Bit MCU ist nicht 16Bit gross.
> Wenn das so sein würde, wäre ich in den letzten 2 Jahren vergebens zur
> Schule gegangen und hätte zahlreiche Projekte und Programme nicht
> speziell auf 16Bit int Variablen abändern müssen, weil die Berechnungen
> nicht mehr in einer normalen 8Bit Variable Platz gehabt hätten.

Ein int auf AVR ist 16 Bit groß. Warum? Weil der C-Standard 
vorschreibt, dass ein int mindestens 16 Bit haben muss.

Autor: Patrick B. (p51d)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
sorry, finde den Teil nicht wos etwas in der Art heisst wie "int steht 
für einen 16-Bit..."

finde nur das
>  int8_t steht für einen 8-Bit Integer mit einem Wertebereich -128 bis +127.

> uint8_t steht für einen 8-Bit Integer ohne Vorzeichen (unsigned int) mit
> einem Wertebereich von 0 bis 255

> int16_t steht für einen 16-Bit Integer mit einem Wertebereich -32768 bis
> +32767.

> uint16_t steht für einen 16-Bit Integer ohne Vorzeichen (unsigned int) mit
> einem Wertebereich von 0 bis 65535.

geht denn bei einer Initialisierung eines normalen Integer durch "int xy 
= 0;" der Wertebereich von -255 bis +255?

Habs mal rasch auf einem MCU-Board testen müssen, denn ist wirklich 
schwer so etwas zu glauben, wenn jemand einem das 4 Jahre lang anders 
beigebracht hat (hat zu Teil auch wirklich Auswirkungen bei Berechnungen 
gehabt, als hätte man nur einen 8-Bit Wert zur Verfügung). Tja man 
lehrnt nie aus.
Neue Erkenntniss über den Lehrer: er hat noch weniger Ahnung von nichts 
als ich bisher angenommen habe (zuteil auch belegt...) Scheiss 
Berufsschullehrer bringen einem nie etwas brauchbares bei...

OK, entschuldige mich nochmals in aller Form für meine "hartnäckigkeit". 
Dann werde ich die nichtbenötigten "int" in char umwandeln (diese sind 
doch aber 8 Bit? oder hat man uns da wieder was falsches beigebracht?)

MFG
P51D

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Patrick B. schrieb:

> geht denn bei einer Initialisierung eines normalen Integer durch "int xy
> = 0;" der Wertebereich von -255 bis +255?

Nein. "int" ist einfach das Gleiche wie int16_t.

> Dann werde ich die nichtbenötigten "int" in char umwandeln (diese sind
> doch aber 8 Bit? oder hat man uns da wieder was falsches beigebracht?)

Nimm auf keinen Fall "char" (außer es handelt sich tatsächlich um ein 
Zeichen). Nimm entweder "unsigned char" oder "signed char", oder noch 
besser uint8_t oder int8_t.

Autor: Florian W. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
char sind 8 bit, so wurde es zumindest auch mir in Uni beigebracht.
Da war ich anfangs auch etwas verwundert warum ein Datentyp der 
scheinbar für Buchstaben gedacht ist auch gerne für kleine Zahlen 
verwendet wird.
Von VB (jaja, Schande über mich) was ich zu Schulzeiten nebenher 
programmiert hab ist man was anderes gewohnt.

Es gibt glaub ich irgendwo eine Option für den gcc, mit der int zu 8 bit 
wird, aber da das dem C-Standard widerspricht ist die Option inzwischen 
deprecated.

Sauberer ist meiner Meinung nach die Verwendung von uint8_t, uint16_t, 
etc., so hat man die Größe direkt bei der Deklaration im Blick.

Autor: Karl (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Bei char ist AFAIR nicht definiert, ob es signed oder unsigned ist. 
Deshalb char für Zeichen, (u)int8_t für 8 Bit Zahlen. Einfach, oder? ;)

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.