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)


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:
1
void Daten_Holen(RS232 *_rs232)
2
{
3
  HAL_UART_Receive_IT(_rs232->RS232_HT,_rs232->buffer_uart, sizeof(_rs232->buffer_uart));
4
  
5
  for(uint8_t i = 0; i < sizeof(_rs232->buffer_uart); i++)
6
  {
7
    if(_rs232->buffer_uart[i] == 0x0A)
8
    {
9
      for(uint8_t i = 0; i < sizeof(_rs232->buffer_uart); i++)
10
      {
11
        _rs232->buffer[i] = _rs232->buffer_uart[i];
12
      }
13
      
14
      for(uint8_t i = 0; i < sizeof(_rs232->buffer_uart); i++)
15
      {
16
        _rs232->buffer_uart[i] = ' ';
17
      }
18
    }
19
  }
20
}

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)


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)


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)


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)


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)


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?
1
if (rx_char == '\n') {
2
  rx_buffer[0] = 0;
3
}

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)


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)


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)


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)


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)


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)


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)


Lesenswert?

Hier mal ein kleiner Code, der Textkommandos empfängt:
1
void receive_command(void)                              // receive from UART
2
{
3
  static char cbuf[CMD_MAX_LEN];                        // command buffer
4
  static uint8_t idx = 0;
5
  if (ukbhit0() == 0)
6
    return;
7
  char ch = ugetchar0();
8
  switch (ch)
9
  {
10
    default:
11
      cbuf[idx] = ch;
12
      if (idx < sizeof(cbuf) - 1)
13
        idx++;
14
      return;
15
    case '\b':
16
      if (idx)
17
        idx--;                          // remove last char (interactive mode)
18
      return;
19
    case '\n':
20
    case '\r':
21
      if (idx == 0)                     // do nothing on empty commands
22
        return;
23
      cbuf[idx] = 0;
24
      idx = 0;
25
      execute(cbuf);
26
      uputs0(cbuf);                     // send answer
27
  }
28
}

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)


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


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)


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:

Lesenswert?

Versuchs mal hiermit!

von Heinz (Gast)


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)


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)


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)


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)


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)


Lesenswert?

Tip: Verwende beim kompilieren das Flag -wShadow und behebe alle 
erzeugten Warnungen!

: Bearbeitet durch User
von Peter D. (peda)


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)


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)


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)


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
von Mw E. (Firma: fritzler-avr.de) (fritzler)


Lesenswert?

Stefan ⛄ F. schrieb:
> 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.

Es ist doch allgemein bekannt, dass W.S. nunmal keine Ahnung hat.

von Gerald K. (geku)


Lesenswert?

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

Wäre ein Einstieg :

https://de.m.wikipedia.org/wiki/Endlicher_Automat

von Gerald K. (geku)


Lesenswert?

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

Schaden kann das Wissen darüber nicht. Vielleicht hilfts dem TO.

: Bearbeitet durch User
von Gerald K. (geku)


Lesenswert?

Falk B. schrieb:
> Na die gehören doch bei C zum guten Ton ;-)

Zu wissen was man tut ist überall Voraussetzung. Auch bei 
Assemblerprogrammierung ist man vor diesen Fehlern nicht gefeit. Nur 
Codereviews sind schwieriger durchzuführen.

Bei Realisierung über Arrays kann man den Index in dieses eingrenzen. 
Bei Zeiger auf Maximalwerte prüfen.

von W.S. (Gast)


Lesenswert?

Stefan ⛄ F. schrieb:
> Komisch, das wird doch allgemein empfohlen, im Warteschleifen zu
> vermeiden und Multi-Tasking zu ermöglichen.

Hä? Du schreibst in Rätseln. Wozu ist beim UART-Treiber irgend eine 
Warteschleife erforderlich? Der Datentransfer erfolgt aus dem und in den 
jeweiligen Ringpuffer und der eigentliche Transfer wird per Interrupt 
abgewickelt. Nix Warteschleife.

Allenfalls kann man für die Leute, die sowas nicht verstehen wie:
1
 if (RxAvail(toUART1)
2
 { c = GetChar(toUART1);
3
   Behandle_empfangenes_Zeichen(c);
4
 }
eine Routine zum Warten bis Empfang anbieten.

W.S.

von W.S. (Gast)


Lesenswert?

Gerald K. schrieb:
> Schaden kann das Wissen darüber nicht. Vielleicht hilfts dem TO.

Ja, vielleicht.

Schaden kann das Wissen um die Schrödingersche Wellengleichung wohl auch 
nicht - aber hilfreich ist das zum Schreiben zweier 
Ringpuffer-Behandlungsroutinen (eine rein, die andere raus) wohl nicht 
so sehr.

Das Grund-Übel ist eigentlich, daß die Programmieranfänger heutzutage 
sich einfach keine ausreichenden Gedanken über Programmierstrategien 
machen, also wie man für den vorliegendne Fall am zweckmäßigsten 
vorgeht und (soweit möglich) das Ganze so ausarbeitet, daß man es auch 
für spätere anders gelegene Fälle benutzen kann. Stattdessen will man 
sich das, was man eigentlich selbst an Strategie gelernt haben sollte, 
von einem Hersteller-Tool generieren lassen. Nein, so lernt man es eben 
nicht. Dieser Thread zeigt es mal wieder deutlich.

Natürlich kann man auch durch Abgucken dazu lernen - die Chinesen 
haben uns auf diese Weise bereits überholt - und hier im Forum gibt es 
genug Codebeispiele für das hier abgehandelte Thema, selbst bereits 
passend für einen STM32F10x. Hab ja selber sowas vor Zeiten gepostet. 
Siehe Lernbetty, siehe STM32F103C8T6.ZIP und so weiter. Dort überall 
sind fertige UART-Treiber drin, auf die man bloß seine werte Nase 
richten müßte. Aber nein, selbst zum Suchen in diesem Forum sind genau 
DIE Leute nicht bereit, die derart auf dem Schlauch stehen wie der TO.

W.S.

von Gerald K. (geku)


Lesenswert?

W.S. schrieb:
> Das Grund-Übel ist eigentlich, daß die Programmieranfänger heutzutage
> sich einfach keine ausreichenden Gedanken über Programmierstrategien
> machen, also wie man für den vorliegendne Fall am zweckmäßigsten vorgeht
> und (soweit möglich) das Ganze so ausarbeitet, daß man es auch für
> spätere anders gelegene Fälle benutzen kann

Dem kann man nur beipflichten !

Man kann sich die Stratige bei der Heimfahrt mit den Öffsis (bitte nicht 
beim Autolenken) oder Abends statt dem Schäfchenzählen zurecht legen. 
Vielleicht wacht man am Morgen, mit HEUREKA, mit dem Programm im Kopf 
auf ( 
https://www.br.de/fernsehen/ard-alpha/sendungen/entdeckungen-grosser-forscher/kekule-august-102.html).

W.S. schrieb:
> Natürlich kann man auch durch Abgucken dazu lernen - die Chinesen haben
> uns auf diese Weise bereits überholt

Jetzt können wir bei den Chinesen abgucken, oder gleich kaufen.

von Mathias G. (motze)


Lesenswert?

Vielen dank für eure Hilfe, habe es jetzt geschafft und der UART läuft 
wie ich es mir wünsche:

Bei einem \r wird der empfang gelöscht und der text ausgegeben, aber ein 
Code sagt mehr als tausend Worte.
1
void USART1_IRQHandler(void)
2
{
3
  /* USER CODE BEGIN USART1_IRQn 0 */
4
5
  /* USER CODE END USART1_IRQn 0 */
6
  HAL_UART_IRQHandler(&huart1);
7
  /* USER CODE BEGIN USART1_IRQn 1 */
8
  
9
  HAL_UART_Receive_IT(&huart1, it_buffer, 1);
10
  
11
  if(it_buffer[0] == 0x0D)
12
  {
13
    empfangen[count++] = 0x0D;
14
    HAL_UART_Transmit(&huart1, empfangen, count, 10);
15
    
16
    memset(empfangen, 0x00, 10);
17
    count = 0;
18
  }
19
  else
20
  {
21
    empfangen[count] = it_buffer[0];
22
    count++;
23
  }
24
  /* USER CODE END USART1_IRQn 1 */
25
}

von Peter D. (peda)


Lesenswert?

Mathias G. schrieb:
> Bei einem \r wird der empfang gelöscht und der text ausgegeben, aber ein
> Code sagt mehr als tausend Worte.

Nochmal, Löschen ist Unsinn.

Und Gratulation, Du hast den Bufferoverflow erfunden.
Der Code wird Dir bei falschen Daten, Baudrate oder floatendem RXD-Pin 
irgendwann den gesamten RAM zersemmelt haben.

von Mathias G. (motze)


Lesenswert?

Ich hoffe jetzt ohne bufferüberlauf
1
void USART1_IRQHandler(void)
2
{
3
  /* USER CODE BEGIN USART1_IRQn 0 */
4
5
  /* USER CODE END USART1_IRQn 0 */
6
  HAL_UART_IRQHandler(&huart1);
7
  /* USER CODE BEGIN USART1_IRQn 1 */
8
  
9
  HAL_UART_Receive_IT(&huart1, it_buffer, 1);
10
  
11
  if(count >= 10)
12
  {
13
    HAL_UART_Transmit(&huart1, (uint8_t*)"FFFFF\r", 6, 10);
14
    
15
    count = 0;
16
  }
17
  
18
  if(it_buffer[0] == 0x0D && count > 1)
19
  {
20
    empfangen[count++] = 0x0D;
21
    HAL_UART_Transmit(&huart1, empfangen, count, 10);
22
    
23
    count = 0;
24
  }
25
  else
26
  {
27
    empfangen[count] = it_buffer[0];
28
    count++;
29
  }
30
  /* USER CODE END USART1_IRQn 1 */
31
}

von Falk B. (falk)


Lesenswert?

Mathias G. schrieb:

Ganz schön verquer. Man braucht kein Array, wenn man nur einzelne 
Zeichen per HAL liest. Es gibt einen Adressoperator
1
#define BUFFER_SIZE 10
2
uint8_t empfangen[BUFFER_SIZE];
3
4
void USART1_IRQHandler(void) {
5
  char rx;
6
7
  HAL_UART_IRQHandler(&huart1);
8
  HAL_UART_Receive_IT(&huart1, &rx, 1);
9
  
10
  if (rx == '\r') {
11
    empfangen[count] = 0;
12
    HAL_UART_Transmit(&huart1, empfangen, 6, BUFFER_SIZE);    
13
    count = 0;
14
  } else {
15
    empfangen[count++] = rx;
16
    if (count >= BUFFER_SIZE) count = BUFFER_SIZE-1;
17
  }  
18
}

von Peter D. (peda)


Lesenswert?

Falk B. schrieb:
> empfangen[count++] = rx;
>     if (count >= BUFFER_SIZE) count = BUFFER_SIZE-1;

Sicherer ist erst testen und dann schreiben.
Es kann ja ein anderer Programmteil Deinen Index mit Müll überschrieben 
haben.

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]
  • [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.