Forum: Mikrocontroller und Digitale Elektronik STM32 UART buffer rx


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.
von Mathias G. (motze)


Bewertung
0 lesenswert
nicht lesenswert
Moin liebe Programmierfreunde,

Mein Ziel ist es über den UART Daten von meinem Rechner zum STM32F100 zu 
empfangen.
Der Empfang soll wie folgt von statten gehen:
Der Sender(PC) schickt ein genaue Anzahl an Daten auf dem UART des 
STM32.
Der STM32 empfängt diese Daten und wartet auf das Zeichen "\n",
wenn er das Zeichen empfangen hat soll der UART buffer gelert werden und 
wieder auf anfang gestzt werden.

So ein Code sagte mehr als Tausend Worte:
void Daten_Holen(RS232 *_rs232)
{
  HAL_UART_Receive_IT(_rs232->RS232_HT,_rs232->buffer_uart, sizeof(_rs232->buffer_uart));
  
  for(uint8_t i = 0; i < sizeof(_rs232->buffer_uart); i++)
  {
    if(_rs232->buffer_uart[i] == 0x0A)
    {
      for(uint8_t i = 0; i < sizeof(_rs232->buffer_uart); i++)
      {
        _rs232->buffer[i] = _rs232->buffer_uart[i];
      }
      
      for(uint8_t i = 0; i < sizeof(_rs232->buffer_uart); i++)
      {
        _rs232->buffer_uart[i] = ' ';
      }
    }
  }
}

Leider klappt das mit der Startadresse nicht und das Löschen des buffers 
klappt auch nicht wirklich.

Ich hoffe das ihr mir weiter helfen könnt.

von Nop (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Völlig verkehrter Ansatz. Dein RX-Interrupt kann in den Buffer 
reinschreiben, während er gelöscht wird.

Nimm einen Ringbuffer und lies im Applikationsteil zeichenweise daraus 
aus und in einen Applikationsbuffer, solange selbiger nicht voll ist. 
Mach Deine Verarbeitung, wenn der Applikationsbuffer ein return liest. 
Setz Deinen Zähler für den Applikationsbuffer dann zurück (Buffer 
löschen ist überflüssig).

von Stefan ⛄ F. (stefanus)


Bewertung
0 lesenswert
nicht lesenswert
Gibt Dir jemand Geld für übermäßige Verwendung von Unterstrichen?

Ich würde bei verschachtelten Schleifen unterschiedliche Variablen Namen 
verwenden, damit klar ist, wann welches "i" verwendet werden soll. 
Vielleicht ist das sogar der Knackpunkt.

Um ganze Arrays zu kopieren, nimmt man üblicherweise die memcpy() 
Funktion. Und zum Leeren des Puffers die memset() Funktion.

Ich denke, du hast da einen Logikfehler:

HAL_UART_Receive_IT() empfängt eine fest gelegte Anzahl von Bytes 
asynchron. Dass heißt: In der danach folgenden Zeit wird der Puffer nach 
und nach gefüllt. Dein Programm hält an dieser Stelle aber nicht an!

Danach untersuchst du den Inhalt des Puffers auf den Zeilenumbruch. 
Tatsächlich enthält der Puffer zu diesem Zeitpunkt aber nur zufällige 
Bytes oder was auch immer du vorher bei der Initialisierung hinein 
geschrieben hast.

von Mathias G. (motze)


Bewertung
0 lesenswert
nicht lesenswert
Hallo Stefan,

vielen dank erstmal für den Tipp mit memcpy() und memset(), habe meinen 
Code auch gleich umgeändert. Jetzt wirkt es auch bedeutent 
übesichtlicher.
Das Datenpaket kommt nur alle Skunde einmal.

Ich versuche das mal genauer zu beschreiben.
Das Datenpaket welches von dem PC kommt hatt immer ein "\n" als 
Abschluss.

Das hier soll mal mein Buffer im Idealfall darstellen.

| T | E | S | T | \n |

Jetzt kann es natürlich sein, dass mal ein nicht vollständiges Paket 
ankommt.

| S | T | \n |   |   |

In jedem fall soll der buffer gelert werden und wieder auf Startposition 
gesetzt werden, damit das nächste Paket wieder vollständig Empfangen 
werden kann.

Also immer wenn ich ein \n empfange möchte ich den buffer leeren und 
wieder auf Anfang setzen.

Ich hoffe das ist jetzt verständlicher.

Ach so ja ich bekomme Geld dafür das ich übermäßig viele Unterstriche 
verwende ;-)

von Stefan ⛄ F. (stefanus)


Bewertung
0 lesenswert
nicht lesenswert
Bei deinem Konzept musst du eine blockierende HAL Funktion verwenden, 
also HAL_UART_Receive (ohne _IT Suffix).

Und da du sofort auf den Zeilenumbruch reagieren willst (nicht erst wenn 
der Puffer komplett gefüllt wurde), musst du die Funktion mit einem 
Puffer für nur 1 Byte aufrufen und die einzelnen Bytes einsammeln.

So richtig sinnvoll erscheint mir das so allerdings nicht. Normalerweise 
würde man die Daten mittels Interrupt-Handler in einen Ringpuffer 
empfangen und den dann (außerhalb der ISR) abarbeiten, sobald ein 
bestimmtes Ende-Zeichen (in deinem Fall wohl der Zeilenumbruch) erkannt 
wurde.

Wie man die DMA basierte Funktion HAL_UART_Receive_IT in deinem Fall 
sinnvoll nutzen kann, ist mir unklar. Solche Sachen programmiere ich 
immer "zu Fuß". Gibt es dazu kein Tutorial?

Vielleicht muss man dort den Ringpuffer in zwei Hälften aufteilen, die 
man immer abwechselnd per HAL_UART_Receive_IT füllt. Aber auch dann 
brauchst du einen Interrupt-Handler, nämlich einen, der vom DMA 
Controller aufgerufen wird und den Empfang der anderen Hälfte des 
Puffers auslöst.

: Bearbeitet durch User
von Falk B. (falk)


Bewertung
0 lesenswert
nicht lesenswert
Mathias G. schrieb:

> In jedem fall soll der buffer gelert werden und wieder auf Startposition
> gesetzt werden, damit das nächste Paket wieder vollständig Empfangen
> werden kann.

Das ist das kleinste Problem. Dein Grundkonzept ist unsinnig. So eine 
Empfangsroutine kann man im allgemeinen NICHT als naive Funktion 
gestalten, sondern muss sie als Interruptroutine aufbauen, welche wie 
eine Statemachine arbeitet und erst durch mehrfachen Aufruf ein 
Ergebnis liefert.

https://www.mikrocontroller.net/articles/Interrupt#UART_mit_Interrupts

> Also immer wenn ich ein \n empfange möchte ich den buffer leeren und
> wieder auf Anfang setzen.

Ja und?
if (rx_char == '\n') {
  rx_buffer[0] = 0;
}

in de allermeisten Fällen erübrigt sich ein Löschen des Empfangsbuffers, 
es reicht, das 1. Zeichen auf 0 zu setzen, damit erkennen alle 
C-konformen Stringfunktionen einen leeren String.

von Gerald K. (geku)


Bewertung
0 lesenswert
nicht lesenswert
Man könnte zwei Puffer verwenden (z.B. zweidimensionales Array 
buffer[2,size]).

Die Interruptroutine füllt den Puffer bis das Zeichen \n empfangen wurde 
und schließt den Puffer. Dann wird der Puffer dem Hintergrund zur 
Verfügung gestellt und der andere Puffer zum Empfang verwendet.
Signalisierung über "Buffer Descriptoren".

https://stackoverflow.com/questions/36625892/descriptor-concept-in-nic

von Heinz (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Mache dich mit dem Konzept Ringbuffer vertraut und nutze es.
Mache dich mit dem Konzept Statemachine vertraut.

Um alle möglichen Betriebssituationen abzudecken, setze eine 
Statemachine auf. Betriebssituationen bzw Ereignisse sind aus meiner 
Sicht:

1. noch nie Daten empfangen (Init nach Programmstart)
2. Timeout Sendeinterval (hier war es glaube ich 1s)
3. Timeout während Datenempfang
3. Datenbyte empfangen obwohl \n kommen müsste
4. \n empfangen obwohl Datenbyte kommen müsste
5. Gut-Fall: Daten korrekt empfangen
6. Warte auf Beginn des nächsten Datenpaketes

Wenn ich es umsetzen würde, würde ich es etwa so machen:
Der Ringbuffer wird im Rx-Interrupt befüllt. Ansonsten macht der Rx 
Interrupt nichts. Die Applikations-Schicht liest aus dem Ringbuffer und 
arbeitet den Zustandsautomaten ab.

Der Ringbuffer muss bei korrekter Implementierung übrigens
nicht gesperrt werden, wenn es nur einen Leser und einen Schreiber gibt.

von Olaf (Gast)


Bewertung
0 lesenswert
nicht lesenswert
> Solche Sachen programmiere ich
> immer "zu Fuß". Gibt es dazu kein Tutorial?

Ich benutze bei meinem MP3-Player immer drei Buffer, einer der von Hand 
mit Daten vom MP3_Encoder beschrieben wird, einer vom DMA automatisch 
abgespielt wird und einer als Reserve damit es beim umschalten etwas 
spielraum gibt und man nicht auf ein Byte genau arbeiten muss.
Allerdings scheint mir DMA bei soetwas simplen wie Uart etwas 
uebertrieben wenn man da nicht handfeste Gruende fuer hat (z.B soll 
empfangen wenn MCU im Sleepmode ist).
Das sind aber alles keine Sachen mit denen sich ein Anfaenger 
beschaeftigen will. Da sollte ein einfacher Ringbuffer ausreichen. Der 
ist schon anstrengend genug weil man sich fragen muss wer da wann aus 
dem IRQ den Positionszeiger weitersetzen darf und wann nicht und wie man 
dann die Fuellstandsanzeige abfragen kann.

Olaf

von Falk B. (falk)


Bewertung
0 lesenswert
nicht lesenswert
Wenn das Datenpaket wirklich nur einmal/s ankommt, reicht EIN einfacher 
Empfangspuffer. Man braucht nicht mal 2, geschweige denn einen 
Ringpuffer.

K.I.S.S.!

von Stefan ⛄ F. (stefanus)


Bewertung
0 lesenswert
nicht lesenswert
Olaf schrieb:
> Das sind aber alles keine Sachen mit denen sich ein Anfaenger
> beschaeftigen will. Da sollte ein einfacher Ringbuffer ausreichen.

Ja, nur leider scheint die HAL das nicht bereit zu stellen. Schon sind 
wir an dem Punkt, wo wir um eine eine bereits unnötig komplexe Funktion 
noch mehr drum-herum bauen, damit es tut, was wir brauchen.

: Bearbeitet durch User
von Olaf (Gast)


Bewertung
0 lesenswert
nicht lesenswert
> Ja, nur leider scheint die HAL das nicht bereit zu stellen.

Der lernt der Anfaenger gleich das es sinnvoll ist sich direkt mit der 
Hardware zu beschaeftigen. .-)
Wenn er es eines Tages mal beruflich macht dann muss er das ja sowieso.

Olaf

von Peter D. (peda)


Bewertung
0 lesenswert
nicht lesenswert
Hier mal ein kleiner Code, der Textkommandos empfängt:
void receive_command(void)                              // receive from UART
{
  static char cbuf[CMD_MAX_LEN];                        // command buffer
  static uint8_t idx = 0;
  if (ukbhit0() == 0)
    return;
  char ch = ugetchar0();
  switch (ch)
  {
    default:
      cbuf[idx] = ch;
      if (idx < sizeof(cbuf) - 1)
        idx++;
      return;
    case '\b':
      if (idx)
        idx--;                          // remove last char (interactive mode)
      return;
    case '\n':
    case '\r':
      if (idx == 0)                     // do nothing on empty commands
        return;
      cbuf[idx] = 0;
      idx = 0;
      execute(cbuf);
      uputs0(cbuf);                     // send answer
  }
}

ukbhit0() prüft, ob mindestens ein Zeichen in der UART-FIFO ist.
ugetchar0() liest ein Zeichen aus der UART-FIFO.

Puffer löscht man nicht. Man schreibt einfach die neuen Zeichen rein.
Wird das Endezeichen erkannt, übergibt man den Puffer an die weitere 
Verarbeitung.

von Heinz (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Olaf schrieb:
> Da sollte ein einfacher Ringbuffer ausreichen. Der
> ist schon anstrengend genug weil man sich fragen muss wer da wann aus
> dem IRQ den Positionszeiger weitersetzen darf und wann nicht und wie man
> dann die Fuellstandsanzeige abfragen kann.

Aus meiner Sicht ist das recht einfach. Wie meinst du das? Übersehe ich 
etwas?

von Lothar M. (lkmiller) (Moderator) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
Mathias G. schrieb:
> In jedem fall soll der buffer gelert werden
Lass die Finger vom Schreibpointer des Puffers. In den Puffer hinein 
schreibt nur die UART-ISR.
Puffer werden "geleert", indem der Lesepointer auf den selben Index wie 
der Schreibpointer gesetzt wird. Du solltest also einfach den 
Lesepointer so setzen, dass er das korrupte Kommando igonriert.

Stefan ⛄ F. schrieb:
> nur leider scheint die HAL das nicht bereit zu stellen.
Das ist mir auch schon arg unangenehm aufgefallen. Die dort 
implementierten UART-Funktionen muss ein Praktikant im ersten Lehrjahr 
gebastelt haben...

Heinz schrieb:
> Um alle möglichen Betriebssituationen abzudecken, setze eine
> Statemachine auf. Betriebssituationen bzw Ereignisse sind aus meiner
> Sicht:
7. beliebig gestörte Daten empfangen (1)
8. viele Daten empfangen, ohne dass ein \n kommt (2)


(1) durch Störspikes auf der Empfangsleitung umgekippte Bits oder 
Geisterdaten, die durch ein "falsches" Startbit erzeugt werden
(2) der klassische Buffer-Overflow

von W.S. (Gast)


Bewertung
-1 lesenswert
nicht lesenswert
Mathias G. schrieb:
> Mein Ziel ist es über den UART Daten von meinem Rechner zum STM32F100 zu
> empfangen.
> Der Empfang soll wie folgt von statten gehen:
> Der Sender(PC) schickt ein genaue Anzahl an Daten auf dem UART des
> STM32.
> Der STM32 empfängt diese Daten und wartet auf das Zeichen "\n",
> wenn er das Zeichen empfangen hat soll der UART buffer gelert werden und
> wieder auf anfang gestzt werden.

Eine genaue anzahl von Daten? Und was ist mit den empfangenen Daten, 
wenn da mal etwas dazwischen gekommen ist? Mit sowas muß man doch bei 
einer seriellen Strippe zwischen dem PC und deinem µC auch rechnen.

Normalerweise schreibt man sich einen Treiber für den UART, den man 
abfragen kann, ob er denn Empfangsdaten hat und der die empfangenen dann 
zeichenweise herausgibt.

Dann ist es für die höheren Schichten in der Firmware nur noch 
interessant, daraus das sicher zu erkennen, was gebraucht wird - und das 
macht man zum Beispiel so, daß man nicht eine genaue Anzahl von Zeichen 
abzählt, sondern sozusagen Kommandos empfängt und bei Bedarf auch noch 
eine Prüfsumme dabei hat.

Schau dir mal den gewöhnlichen Intel Hexcode an. Dort wird auch 
blockweise Informationen übertragen und das Ganze ist dank 
Kennbuchstaben und Prüfsumme einigermaßen verläßlich. Man läßt eine 
ganze Zeile in einem Puffer auflaufen, kann die Kennung überprüfen und 
die Prüfsumme abtesten und wenn alles OK ist, den Inhalt verwenden.

Natürlich gibt es auch andere Verfahren - aber so wie du das angegangen 
bist, ist es nicht gut.

W.S.

von Harry L. (mysth)


Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Versuchs mal hiermit!

von Heinz (Gast)


Bewertung
1 lesenswert
nicht lesenswert
Lothar M. schrieb:
> Du solltest also einfach den
> Lesepointer so setzen, dass er das korrupte Kommando igonriert.

Bringst du gerade den Empfang von Bytes und das Bilden von Kommandos 
durcheinander? Das sind eigentlich zwei verschiedene Layer in der 
Software.

Die Uart ISR hat (so mache ich es jedenfalls) nur die Aufgabe, 
empfangene Bytes in den Ringbuffer zu schreiben. Höhere Schichten lesen 
den RIngbuffer aus, bilden Kommandos daraus, berechnen ggf Checksummen 
und geben die Kommandos zru Bearbeitung weiter oder kümmern sich um das 
Fehlerhandling (Timeout, Kommando korrupt etc).

Man könnte auch in der ISR Bytes sammeln und die Kommandos dort direkt 
zusammenbauen, aber ich sehe nur sehr selten. Im Normalfall werden ISRs 
sehr kurz gehalten - aber keine Regel ohne begründete Ausnahme ... :)

von drm (Gast)


Bewertung
0 lesenswert
nicht lesenswert
@falk

kiss:
Knights In Service of Satan

https://www.kissonline.com/

ja,ja
keep it stupid simple

ist nicht offensichtlich welche kiss du so meinen könntest

@motze
>Sender(PC) schickt ein genaue Anzahl an Daten auf dem UART des STM32
Empfang per DMA mit entsprechender Länge und IRQ nach Empfang des 
Datenpakets,
Timeout nicht vergessen wenn Übertragungsfehler
oder so:
https://stm32f4-discovery.net/2017/07/stm32-tutorial-efficiently-receive-uart-data-using-dma/
Tilen Majerle ist mittlerweile FAE bei ST, hat vielleich Ahnung ;)

von Gerald K. (geku)


Bewertung
0 lesenswert
nicht lesenswert
Heinz schrieb:
> Im Normalfall werden ISRs sehr kurz gehalten - aber keine Regel ohne
> begründete Ausnahme

Dem kann man nur voll zustimmen!

Wichtig ist nur die Unterbindung eines Puffer überlaufen in der 
Interruptroutine,  wenn die Applikation die Daten nicht abholt, sonst 
gibt es böse Speicherüberschreiber.

von Falk B. (falk)


Bewertung
0 lesenswert
nicht lesenswert
Gerald K. schrieb:
> sonst
> gibt es böse Speicherüberschreiber.

Na die gehören doch bei C zum guten Ton ;-)

von Mathias G. (motze)


Bewertung
0 lesenswert
nicht lesenswert
Vielen dank für die ganzen Information, leider bin ich gestern nicht 
mehr dazu gekommen eure ganzen Informationen zu verarbeiten. Wie gesagt 
ich verdiene ja mein Geld mit Unterstrichen und gestern waren sehr viele 
Unterstriche.
Ich hoffe das ich heute dazu kommen werde mich ein wenig einzulesen.
Bei weiteren problemen zu dem Thema werde ich mich wieder melden.

Vielen dank erstmal

von Ben S. (bensch123)


Bewertung
-1 lesenswert
nicht lesenswert
Tip: Verwende beim kompilieren das Flag -wShadow und behebe alle 
erzeugten Warnungen!

: Bearbeitet durch User
von Peter D. (peda)


Bewertung
0 lesenswert
nicht lesenswert
Heinz schrieb:
> Die Uart ISR hat (so mache ich es jedenfalls) nur die Aufgabe,
> empfangene Bytes in den Ringbuffer zu schreiben.

So habe ich das auch für mich als optimal ermittelt. Die UART-FIFO 
unterscheidet nichtmal zwischen Binärdaten und Textdaten.

Timeouts als Protokollelement finde ich evil und benutze sie nicht. 
Daten im FIFO werden daher nie verworfen. Wenn die übergeordnete Schicht 
die Daten nicht parsen kann, antwortet sie mit einer Fehlermeldung. In 
der Regel wiederholt dann der Sender das Paket. Das passiert z.B beim 
Stecken des Kabels oder Einschaltreset eines Teilnehmers.

Timeouts gibt es erst in höheren Schichten, d.h. ein Teilnehmer schickt 
ein Kommando und der andere antwortet nicht innerhalb einer bestimmten 
Zeit.

von Mathias G. (motze)


Bewertung
0 lesenswert
nicht lesenswert
So gerade arbeite ich mich in das Thema Statemachine ein, um den 
überblick nicht zu verlieren. Ihr Redet ja auch immer über Statemachine 
und ich will ja mitreden können.

Kennt ihr ein gutes Buch zu diesem Thema? Vielleicht auch eine Software 
wo ich Statemachine Simulieren kann?

Danach werde ich mich in das Thema Ringspeicher einarbeiten und dann 
schaue ich weiter mit dem RS232 problem.

von W.S. (Gast)


Bewertung
-2 lesenswert
nicht lesenswert
Mathias G. schrieb:
> Kennt ihr ein gutes Buch zu diesem Thema? Vielleicht auch eine Software
> wo ich Statemachine Simulieren kann?

Wie bitte?
Na klar gibt es theoretisch genau diese Software: nämlich deine noch zu 
schreibende Firmware für deinen STM32F1xx.

Ich schätze, daß es jetzt allerhöchste Zeit ist, daß du mal die Katze 
aus dem Sack läßt, also mal schreibst, was all dieses Daten-Empfangen zu 
bedeuten hat und wozu es gut sein soll.

Ich glaube auch nicht, daß du auf einem µC in Software eine 
Zustands-Maschine implementieren mußt. Stattdessen vermute ich, daß du 
einfach nur anhand empfangener Daten irgendwelche Portpins wackeln 
lassen willst. Also schreib lieber mal den finalen Zweck, sonst 
entgleist dieser Thread völlig.

Und wenn du nach Beispielen suchst, wie man einen Treiber für den UART 
schreibt, oder ein Kommandoprogramm schreibt, oder so was, dann findest 
du hier im Forum einen Haufen Zeugs dazu.

Für mich ist es schon erstaunlich, wie man so etwas Einfaches wie einen 
UART-Treiber (mit 'innenliegendem' Ringpuffer natürlich!) zu einem 
Riesenproblem machen kann.

W.S.

von Stefan ⛄ F. (stefanus)


Bewertung
-1 lesenswert
nicht lesenswert
W.S. schrieb:
> Ich glaube auch nicht, daß du auf einem µC in Software eine
> Zustands-Maschine implementieren mußt

Komisch, das wird doch allgemein empfohlen, im Warteschleifen zu 
vermeiden und Multi-Tasking zu ermöglichen.

Mathias G. schrieb:
> Kennt ihr ein gutes Buch zu diesem Thema? Vielleicht auch eine Software
> wo ich Statemachine Simulieren kann?

Ich denke, dass ein Simulator wenig Sinn macht. Immerhin hat der Chip 
eine schnelle Debugger Schnittstelle, man kann den Code daher direkt auf 
dem Chip testen und untersuchen.

Ein Buch zum Thema kenne ich nicht. Notfalls kommst du vielleicht mit 
diesem Artikel weiter: 
http://stefanfrings.de/multithreading_arduino/index.html

W.S. schrieb:
> Für mich ist es schon erstaunlich, wie man so etwas Einfaches wie einen
> UART-Treiber (mit 'innenliegendem' Ringpuffer natürlich!) zu einem
> Riesenproblem machen kann.

Das kommt davon, wenn man mit der HAL einsteigt. Gerade an diesem Punkt 
wirft sie einem regelrecht Stöcke zwischen die Füße.

: Bearbeitet durch User

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.

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