mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik STM32 und I2C - Fehler im Code


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
Autor: Viktor B. (coldlogic)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi Leute,

ich versuche gerade ein OLED-Display per I2C anzusteuern. Das Display 
funktionierte in einer anderen Schaltung, ist also in Ordnung. Pull-ups 
sind angeschlossen. Es klappt leider nicht mit dem Code - vermute ich, 
weil beim debuggen 80% aller Programmstopps im Interrupt landen. Könnte 
jemand mal meinen Code ansehen? Von alleine finde ich den Fehler nicht. 
Andere Beispiele wie z.B. der von Driller und offizielle ST-Doku habe 
ich mir schon durchgeschaut, will die aber nicht guttenbergen. Hier ist 
das Programm:

#include "stm32f10x_conf.h"
//#include "stm32f10x.h"

void Init(void){
// Hardware initialiser
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);                // enable I2C;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);                // enable alternate functions (why?);

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);               // GPIO C enable
    GPIO_InitTypeDef gpioInit;
    gpioInit.GPIO_Mode = GPIO_Mode_Out_OD;
    gpioInit.GPIO_Speed = GPIO_Speed_2MHz;
    gpioInit.GPIO_Pin = GPIO_Pin_13;
    GPIO_Init(GPIOC, &gpioInit);
    GPIO_WriteBit(GPIOC, ((uint16_t)0x2000), Bit_SET);

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);               // GPIO B enable
    GPIO_InitTypeDef gpioInit1;
    gpioInit1.GPIO_Mode = GPIO_Mode_AF_OD;
    gpioInit1.GPIO_Speed = GPIO_Speed_50MHz;
    gpioInit1.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;                       // route I2C pins
    GPIO_Init(GPIOB, &gpioInit1);

    NVIC_InitTypeDef NVICinit;                                          // configure NVIC
    NVICinit.NVIC_IRQChannel = I2C1_EV_IRQn;                            // add I2C_event interrupt
    NVICinit.NVIC_IRQChannelPreemptionPriority = 0;
    NVICinit.NVIC_IRQChannelSubPriority = 0;
    NVICinit.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVICinit);

    NVICinit.NVIC_IRQChannel = I2C1_ER_IRQn;                            // add I2C_error interrupt
    NVICinit.NVIC_IRQChannelPreemptionPriority = 0;
    NVICinit.NVIC_IRQChannelSubPriority = 0;
    NVICinit.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVICinit);

    I2C_DeInit(I2C1);                                                   // init I2C module
    I2C_InitTypeDef I2Cinit;
    I2Cinit.I2C_Ack = I2C_Ack_Enable;
    I2Cinit.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
    I2Cinit.I2C_ClockSpeed = 100000;
    I2Cinit.I2C_DutyCycle = I2C_DutyCycle_2;
    I2Cinit.I2C_Mode = I2C_Mode_I2C;
    I2Cinit.I2C_OwnAddress1 = 0;
    I2C_Init(I2C1, &I2Cinit);

    I2C_ITConfig(I2C1, I2C_IT_EVT, ENABLE);                             // enable event, error and buffer interrupts
    I2C_ITConfig(I2C1, I2C_IT_ERR, ENABLE);
    I2C_ITConfig(I2C1, I2C_IT_BUF, ENABLE);

    I2C_Cmd(I2C1, ENABLE);
}

volatile uint8_t I2C_errorcode = 0, I2C_addr = 0, I2C_num_bytes = 0;
volatile uint8_t* I2C_reading_pointer;
static uint8_t OLED_init[29]= {                                          // Initialization Sequence
0x00,                                                                   // Commands will be sent
0xAE,                                                              // Display OFF (sleep mode)
0x20, 0b00,                                                            // Set Memory Addressing Mode
                                                                        // 00=Horizontal Addressing Mode; 01=Vertical Addressing Mode;
                                                                        // 10=Page Addressing Mode (RESET); 11=Invalid
0xB0,                                                              // Set Page Start Address for Page Addressing Mode, 0-7
0xC8,                                                              // Set COM Output Scan Direction
0x00,                                                                // --set low column address
0x10,                                                              // --set high column address
0x40,                                                              // --set start line address
0x81, 0x3F,                                                            // Set contrast control register
0xA1,                                                              // Set Segment Re-map. A0=address mapped; A1=address 127 mapped.
0xA6,                                                              // Set display mode. A6=Normal; A7=Inverse
0xA8, 63,                                                            // Set multiplex ratio(1 to 64)
0xA4,                                                              // Output RAM to Display
                                                                        // 0xA4=Output follows RAM content; 0xA5,Output ignores RAM content
0xD3, 0x00,                                                            // Set display offset. 00 = no offset
0xD5,                                                              // --set display clock divide ratio/oscillator frequency
0xF0,                                                              // --set divide ratio
0xD9, 0x22,                                                            // Set pre-charge period
0xDA, 0x12,                                                            // Set com pins hardware configuration
0xDB,                                                              // --set vcomh
0x20,                                                              // 0x20,0.77xVcc
0x8D, 0x14,                                                            // Set DC-DC enable
0xAF                                                                    //?
};

    void I2C1_EV_IRQHandler(){                                              // Self-containing I2C interrupt
        if(I2C1->SR1 & I2C_SR1_SB){                                         // triggered by start condition ->
            I2C1->DR = I2C_addr;                                            // send I2C adress
        }else if((I2C1->SR1 & I2C_SR1_ADDR)||(I2C1->SR1 & I2C_SR1_TXE)){    // if not, then send data
           if(I2C_num_bytes){                                               // in case its available
                I2C1->DR = *I2C_reading_pointer;
                I2C_reading_pointer++;
                I2C_num_bytes--;
            }
        }                                                                   // if not - send stop condition
        else if((I2C1->SR1 & I2C_SR1_BTF)){
            I2C1->CR1 |= ((uint16_t)0x0200);                                // EmBitz does not recognize CR1_STOP_Set
            I2C_errorcode = 0;
        }
    }

    void I2C1_ER_IRQHandler(){
        if(I2C1->SR1 & I2C_SR1_AF)
            I2C_errorcode = 1;                                              // No devise with that adress
        else{
            I2C_errorcode = 2;                                              // Something strange occured, inspect SR1
        }
    }

    void I2C_initiate_transmission(){                                       // it`s fire and forget
        while(I2C1->SR2 & I2C_SR2_BUSY){}                                   // wait for the bus to become free (maybe not required)
        I2C1->CR1 |= ((uint16_t)0x0100);                                    // send start condition; EmBitz doesnt recognize it also
        I2C_errorcode = -1;                                                 // lock the I2C variables
    }


int main(void)
{
    Init();


    I2C_reading_pointer = OLED_init;
    I2C_num_bytes = 29;
    I2C_addr = 0x78;

    I2C_initiate_transmission();

    while(I2C_errorcode != 0){}

    uint8_t foo[2] = {0x40,0xff};

    I2C_reading_pointer = foo;
    I2C_num_bytes = 2;

    I2C_initiate_transmission();

  while(1)
  {

  }
}

Autor: Rätsel Rater (Gast)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
Viktor B. schrieb:
> I2C_ITConfig(I2C1, I2C_IT_BUF, ENABLE);

... und wo ist dein Interrupt-Handler dafür?

Viktor B. schrieb:
> // enable alternate functions (why?);

Wenn du nicht weisst warum, warum machst du es dann?

Viktor B. schrieb:
> // EmBitz does not recognize CR1_STOP_Set
> // send start condition; EmBitz doesnt recognize it also

Was hat EmBitz damit zu tun? Gar nichts. Der Programmierer
ist dafür verantwortlich dass er dem Compiler die Symbol-
definitionen bekannt gibt.

Viktor B. schrieb:
> // No devise with that adress

<device> schreiben ist die Devise

Autor: Rätsel Rater (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Viktor B. schrieb:
> // EmBitz does not recognize CR1_STOP_Set

Merke: EmBitz ist kein Compiler.

Autor: Stefanus F. (Firma: Äppel) (stefanus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Damit wir hier ein bisschen konkreter auf die Bedürfnisse des Displays 
eingehen können, solltest du es benennen (bzw. dessen Controller).

An Deiner Stelle würde ich die Kommunikation mit einem Logik-Analyzer 
untersuchen. Stimmt sie mit dem Quelltext überein? Wenn nicht, hast du 
wohl die I²C Schnittstelle falsch benutzt. Wenn sie jedoch überein 
stimmt, ist etwas an der Initialisierungs-Sequenz falsch.

Falls es wie ich vermute ein SSD1306 sein sollte, darfst du gerne von 
meinem Code abgucken. 
http://stefanfrings.de/esp8266/WIFI-Kit-8-Test2.zip

Meine Initialisierungssequenz ist etwas anders als deine. Sie beruht auf 
Informationen aus zwei unterschiedlichen Datenblättern, plus diversen 
Tipps die ich im Internet gefunden habe. Ich sehe da einige Unterschiede 
(abgesehen von vermutlich harmlosen Abweichungen bei der Reihenfolge der 
Kommandos).

Falls es kein SSD1306 ist, könnte das Display eventuell Pausen zwischen 
bestimmten Kommandos benötigen.

Autor: Stefanus F. (Firma: Äppel) (stefanus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Noch was: Eventuell magst du zuerst mal die Bit-Banging Methode aus 
meinem Code mit deiner Initialisierungssequenz versuchen. Oder du 
versuchst es mit der Polling Methode wie hier umgesetzt: 
http://stefanfrings.de/stm32/index.html#i2c Beide kann man tadellos 
debuggen.

So kannst du wenigstens herausfinden, ob der Fehler in deiner 
Initialisierungssequenz steckt, oder woanders.

: Bearbeitet durch User
Autor: Viktor B. (coldlogic)
Datum:

Bewertung
-1 lesenswert
nicht lesenswert
Rätsel Rater schrieb:
> Viktor B. schrieb:
>> I2C_ITConfig(I2C1, I2C_IT_BUF, ENABLE);
>
> ... und wo ist dein Interrupt-Handler dafür?

Es gibt nur 2 Interruptvektoren - Event und Error. Quelle: Reference 
Manual für STM32F10x Controller, Seite 772, Abbildung 278, I2C interrupt 
mapping diagram.

Rätsel Rater schrieb:
> Viktor B. schrieb:
>> // enable alternate functions (why?);
>
> Wenn du nicht weisst warum, warum machst du es dann?

Weil es in einem der Beispiele so stand. War mir nicht sicher, was es 
sollte, deswegen hab ich es im ursprünglichem Zustand gelassen.

Rätsel Rater schrieb:
> Viktor B. schrieb:
>> // EmBitz does not recognize CR1_STOP_Set
>> // send start condition; EmBitz doesnt recognize it also
>
> Was hat EmBitz damit zu tun? Gar nichts. Der Programmierer
> ist dafür verantwortlich dass er dem Compiler die Symbol-
> definitionen bekannt gibt.

Rätsel Rater schrieb:
> Viktor B. schrieb:
>> // EmBitz does not recognize CR1_STOP_Set
>
> Merke: EmBitz ist kein Compiler.

Ich habe auch nicht gesagt, EmBitz wäre ein Compiler. Und ja, der 
Compiler hinter EmBitz erkennt zwar andere Definitionen aus der selben 
Datei, aber genau die eine irgendwie nicht. Was soll man da bloß machen?

Rätsel Rater schrieb:
> Viktor B. schrieb:
>> // No devise with that adress
>
> <device> schreiben ist die Devise

Oh bitte, war das nötig?

Stefanus F. schrieb:
> An Deiner Stelle würde ich die Kommunikation mit einem Logik-Analyzer
> untersuchen.

EInen Logik Analyzer habe ich mit vor einigen Tagen bestellt, der ist 
leider noch nicht angekommen. Den I2C-Ausgang werde ich so schnell wie 
möglich damit testen.

Das Display ist - richig geschätzt - ein SSD1306. Die 
Initialisierungssequenz ist aus einem anderen Projekt, wo es zuverlässig 
funktioniert, d.h. an der Sequenz wird es erstmal nicht liegen. Ich bin 
mir sicher, ich hätte den I2C-Code vermurkst, aber wie?.. Interrupt 
flags sollen sich durch Hardware beim Lesen der Status-Register 
rücksetzen. Den I2C-takt habe ich eingeschaltet. Welche weit 
verbreiteten Fehler gibt es noch?

Autor: Rätsel Rater (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Viktor B. schrieb:
> Ich habe auch nicht gesagt, EmBitz wäre ein Compiler.

Sehr wohl hast du das (indirekt) gesagt, denn du hast EmBitz
die Schuld zugeschrieben dass er es nicht findet. Dabei ist
es eben der Compiler der es nicht findet. Und genau deswegen
weil du ihm nicht gesagt hast in welcher Datei er es finden
soll. Dumm wäre es jetzt stm32f10x_i2c.c zu inkludieren.

Der Grund ist vermutlich eine gewisse Absicht von den SPL-Machern,
da sie die entsprechenden Defines "privat" gemacht haben.
Du solltest I2C_GenerateSTOP (..) aus stm32f10x_i2c.c verwenden.
Gleiches gilt für I2C_GenerateSTART (..)


Viktor B. schrieb:
> Es gibt nur 2 Interruptvektoren - Event und Error.

Ok, das war mein Wissensdefizit.

Dann solltest du in den Interrupt-Handlern noch entsprechend
gesetzte Flags mit I2C_ClearITPendingBit (..) löschen.

Autor: Viktor B. (coldlogic)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Rätsel Rater schrieb:
> Dumm wäre es jetzt stm32f10x_i2c.c zu inkludieren.

Sollte schon mit conf.h drin sein.

Rätsel Rater schrieb:
> Du solltest I2C_GenerateSTOP (..) aus stm32f10x_i2c.c verwenden.
> Gleiches gilt für I2C_GenerateSTART (..)

Die Methode I2C_GenerateStart und die andere sind dasselbe wie I2C1->CR1 
|= I2C_CR1_START, nur halt mit einem assert, damit ich bloß nicht sowas 
wie TIM1->OCRA |= I2C_CR1_START compiliere (klappen würde es ja, wäre 
trotzdem eine schlechte Idee.) Für einen kleineren Projekt fügt das nur 
Abstraktionslayer hinzu.

Rätsel Rater schrieb:
> ann solltest du in den Interrupt-Handlern noch entsprechend
> gesetzte Flags mit I2C_ClearITPendingBit (..) löschen.

Diese werden von der Hardware gelöscht, wenn man die Register SR1 und 
SR2 ausliest. Außerdem sind die Bits als "read-only" im Reference Manual 
markiert.

Autor: Viktor B. (coldlogic)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Viel Debugging später hat sich gezeigt, dass der Programmfluss im 
Interrupt gestört wird bzw. dort der Fehler liegt. Irgendwie springt es 
von der Zeile
          if(I2C_num_bytes){                     // in case its available

bei I2C_num_bytes = 0 anstatt zur nächsten schließenden geschweiften 
Klammmer einfach völlig aus dem Interrupt. Ich bin ehrlich gesagt 
ratlos.

UPD.: Außerdem enthält das Register I2C1->SR1 auch nach dem Senden des 
ersten Datenbytes nur den bit ADDR gesetzt. Wieso? sollte es nicht 
automatisch resettet werden und bei der nächsten Übertragung das TXE-bit 
gesetzt werden?

: Bearbeitet durch User
Autor: Thomas E. (picalic)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Viktor B. schrieb:
> Irgendwie springt es
> von der Zeile          if(I2C_num_bytes){                     // in case
> its available
> bei I2C_num_bytes = 0 anstatt zur nächsten schließenden geschweiften
> Klammmer einfach völlig aus dem Interrupt.

Wieso? Ist doch richtig: In dem Interrupt ist ja auch nichts mehr zu 
tun!
Geschweifte Klammern sind keine Sprungmarken, sondern kennzeichnen nur 
logische Programmblöcke. Ob und wie der Compiler dafür Code erzeugt, 
bleibt ihm überlassen.

: Bearbeitet durch User
Autor: Viktor B. (coldlogic)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Thomas E. schrieb:
> In dem Interrupt ist ja auch nichts mehr zu
> tun!

Ich hätte erwartet, dass das Programm den IF-Block, der in die Klammern 
eingeschlossen ist, überspringt und mit
else if((I2C1->SR1 & I2C_SR1_BTF))
weitermacht?..

Autor: Bernd K. (prof7bit)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Viktor B. schrieb:
> Compiler hinter EmBitz erkennt zwar andere Definitionen aus der selben
> Datei, aber genau die eine irgendwie nicht. Was soll man da bloß machen?

Sie ohne Tippfehler zu schreiben wär spontan mein erster Gedanke.

Autor: Frank B. (f-baer)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Viktor B. schrieb:
> Thomas E. schrieb:
>> In dem Interrupt ist ja auch nichts mehr zu
>> tun!
>
> Ich hätte erwartet, dass das Programm den IF-Block, der in die Klammern
> eingeschlossen ist, überspringt und mit
>
else if((I2C1->SR1 & I2C_SR1_BTF))
> weitermacht?..

Dafür solltest du deine geschweiften Klammern mal sortieren.
Du hast hier zwei verschachtelte If-Blöcke...

Autor: Viktor B. (coldlogic)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Mein Logic Analyser ist endlich da. Bringt nur mäßig Licht ins Dunkle.

Der µC weiß nichts mit der Clock anzufangen?...

Noch interessanter: Ich hab den Code umsortiert, nun sieht die Routine 
so aus:
void I2C1_EV_IRQHandler(){                                              // Self-containing I2C interrupt
        if(I2C1->SR1 & I2C_SR1_SB)                                          // triggered by start condition ->
        {
            I2C1->DR = I2C_pointer->I2C_addr;                               // send I2C adress
        }
        else if((I2C1->SR1 & I2C_SR1_ADDR)||(I2C1->SR1 & I2C_SR1_TXE)||(I2C_pointer->I2C_num_bytes > 0))// if not, then send data
        {
            I2C1->DR = *(I2C_pointer->I2C_reading_pointer);
            I2C_pointer->I2C_reading_pointer++;
            I2C_pointer->I2C_num_bytes--;
        }                                                                   // if not - send stop condition
        else if((I2C1->SR1 & I2C_SR1_BTF))
        {
            I2C1->CR1 |= ((uint16_t)0x0200);                                // EmBitz does not recognize CR1_STOP_Set
            I2C_pointer->I2C_errorcode = 0;
            I2C_pointer->I2C_lock = 0;
        }
        else {
            GPIO_WriteBit(GPIOC, ((uint16_t)0x2000), Bit_RESET);
        }
    }

An dem Signalverlauf ändert sich nichts, dafür geht der µC regelmäßig in 
einen Hard Fault.

: Bearbeitet durch User
Autor: Stefanus F. (Firma: Äppel) (stefanus)
Datum:

Bewertung
2 lesenswert
nicht lesenswert
> Bringt nur mäßig Licht ins Dunkle.

Das sehe ich aber ganz anders. Wir können hier sehen, dass ein top 
sauberes Timing vorliegt, dass der Master die Zahl 0x78 sendet (= 7bit 
Adresse 0x3C), und dass der Slave diese mit ACK bestätigt. Dass heisst, 
der Slave hat reagiert, er fühlt sich angesprochen.

Aber die nachfolgenden Bytes und das abschließende das Stop Signal 
(roter Punkt) fehlen. Sieht für mich 100% nach einem Softwarefehler auf 
dem Master aus.

Bau mal in deine vier if/else Abschnitte Trace Meldungen 
(http://stefanfrings.de/stm32/index.html#traceswo) ein um zu sehen, 
welche davon in welcher Reihenfolge ausgeführt werden.

: Bearbeitet durch User
Autor: Viktor B. (coldlogic)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Stefanus F. schrieb:
> Bau mal in deine vier if/else Abschnitte Trace Meldungen
> (http://stefanfrings.de/stm32/index.html#traceswo) ein um zu sehen,
> welche davon in welcher Reihenfolge ausgeführt werden.

Ich hab so schnell keine Möglichkeit gefunden, Trace Meldungen in EmBitz 
zu lesen, deswegen musste das Debugging per LED (und Logic Analyser) 
erfolgen. Der Interrupt schien sich sinnlos zu wiederholen. Beim 
Studieren des Datenblattes ist mir dann eine Zeile aufgefallen:

> As soon as the address byte is sent,the ADDR bit is set by hardware
> and an interrupt is generated if the ITEVFEN bit is set.
> Then the master waits for a read of the SR1 register
> *followed by a read of the SR2 register*

Als ein Experiment wurde also die ISR abgeändert. Folgende Zeile wurde 
hinzugefügt:
else if(((I2C1->SR1 & I2C_SR1_ADDR)||(I2C1->SR1 & I2C_SR1_TXE))&&(I2C_pointer->I2C_num_bytes > 0))// if not, then send data
        {
            if(I2C1->SR2&I2C_SR2_BUSY)                     //seems crucial
                GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_RESET);
            I2C1->DR = *(I2C_pointer->I2C_reading_pointer);
            I2C_pointer->I2C_reading_pointer++;
            I2C_pointer->I2C_num_bytes--;
        }

Und siehe da, die ganze Initialisierungssequenz wird übertragen. Zwar 
hab ich komischerweise Schnee im Display, aber es ist besser als gar 
nichts. LA sagt, die zweite Übertragung endet mit einer Stop Condition 
genau nach der Start Condition. Da muss ich wohl nach anderen Fehlern 
suchen.

Autor: Viktor B. (coldlogic)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Update: der zweite Schreibvorgang (foo) klappt bestens, wenn ich das 
Programm mit dem Debugger Schritt für Schritt durchgehe. Dafür, wenn ich 
das Programm laufen lasse, wird nur ein Start und gleich darauf ein Stop 
gesendet..? WTF?

Autor: Lama (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Schaue dir doch mal die Bits im SR mal ganz genau an :-)

Autor: Viktor B. (coldlogic)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Lama schrieb:
> Schaue dir doch mal die Bits im SR mal ganz genau an :-)

Welches SR von der beiden?
Im Datenblatt oder während der Programmausführung?
Welche Bits müssten denn Ihrer Meinung nach genau angeschaut werden?

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.