Hallo,
ich stehe öfter vor dem Problem, dass ich über Ethernet oder Uart in
einer Zeiteinheit 100 kleine Telegramme oder 10 große Telegramme
reinbekomme. Mit Blockspeichern muss ich den Worst-Case vorhalten muss
(100 Blöcke mit Platz für große Telegramme), bei üblichen Fifos kann ich
nicht mit memcpy rein- oder raus-schreiben und muss die Nachricht zum
Auswerten umkopieren.
Ich habe darum Buffer gebaut, die vom Speicher fast kompakt wie Fifos
sind und wahlfreien Zugriff erlauben.
Wenn ihr sagt, gibts schon besser da und dort, dann danke für den
Hinweis.
Wenn es jemand gut findet oder gar gebrauchen könnte, würde ich es
aufhübschen, Code, Test, Beispiel und Beschreibung trennen und ins Wiki
stellen.
A. S. schrieb:> ich stehe öfter vor dem Problem, dass ich über Ethernet oder Uart in> einer Zeiteinheit 100 kleine Telegramme oder 10 große Telegramme> reinbekomme. Mit Blockspeichern muss ich den Worst-Case vorhalten muss> (100 Blöcke mit Platz für große Telegramme), bei üblichen Fifos kann ich> nicht mit memcpy rein- oder raus-schreiben und muss die Nachricht zum> Auswerten umkopieren.
Doch, kann man, wenn man einen guten FIFO verwendet ;-)
https://www.mikrocontroller.net/articles/FIFO#FIFO_als_Bibliothek> Ich habe darum Buffer gebaut, die vom Speicher fast kompakt wie Fifos> sind und wahlfreien Zugriff erlauben.
Also so richtig verstehe ich dein Konzept und dessen Vorteile nicht so
ganz.
Falk B. schrieb:> Also so richtig verstehe ich dein Konzept und dessen Vorteile nicht so> ganz.
kein xx_wrap() wie in Deinem Code. Und wahlfreier Zugriff beim Lesen und
Schreiben (Random Access Memory).
Außerdem gibt es da ein paar handwerkliche Schnitzer. Die #define und
struct Definitionen gehören in die .h Datei.
Deine Namensgebung ist auch unglücklich, denn ASBufType ist ein Pointer,
kein direktes Datenobjekt. Das irritiert u.a. hier
1
ASBufTypeASBCreate(BYTE*mem,intsize)
2
{
3
ASBufTypeb=(ASBufType)mem;/* Handler = Anfang des Speichers */
Ich würde das nicht so machen und nur mit sASBMsg arbeiten. Wenn jemand
einen Pointer drauf braucht, schreibt er es hin und jeder sieht es.
Typedefinitionen werden von vielen Leuten mit _t gekennzeichnet, z.B. in
stdint, uint8_t etc. Man sollte auch keine unsinnigen neuen Datentypen
erfinden. Wenn man ein ECHTES Byte will, nimmt man heutzutage ein
uint8_t aus stdint.h
1
voidInitUart(void)
2
{
3
staticBYTEmem_rx[1000];
4
staticBYTEmem_tx[1000];
5
6
/* die buffer initialisieren */
7
ASBrx=ASBCreate(mem_rx,sizeof(mem_rx));
8
ASBtx=ASBCreate(mem_tx,sizeof(mem_tx));
9
/* und den Rest */
10
...
11
12
}
Darüber solltest du nochmal nachdenken . . .
Alles in allem sehr nebulös und eher fragwürdig.
A. S. schrieb:> kein xx_wrap() wie in Deinem Code. Und wahlfreier Zugriff beim Lesen und> Schreiben (Random Access Memory).
Und wo kommt die variable Strukturierung zum Tragen?
Hallo Falk,
vielen Dank für Deine Rückmeldung. Ja, ich hätte das direkt schon
aufteilen müssen, so sind es zu viele Dinge auf einmal:
Der eigentliche Treiber sind nur die die Zeilen bis "/* --- ende ---
*/". Danach beginnen einfache Tests und Beispiele. Dazwischen jede Menge
Erklärung, was passiert. Dumm von mir.
Falk B. schrieb:> Außerdem gibt es da ein paar handwerkliche Schnitzer. Die #define und> struct Definitionen gehören in die .h Datei.
Nur soweit woanders verwendet. Das komplette Interface ist die .h-Datei.
eine Applikation sieht nur die .h.
Information Hiding. Die verwaltungsstruktur etc. gehen niemanden was an.
> Deine Namensgebung ist auch unglücklich, denn ASBufType ist ein Pointer,> kein direktes Datenobjekt.
In der Anwendung ist es einfach ein Datentyp, ein Handler, eine Referenz
auf genau einen ASBuffer. Egal ob Ptr oder was auch immer. Du kannst 100
von diesen Buffern anlegen und brauchst dann halt 100 Händler. Man
sollte m.E. nicht mit & Arbeiten, wenn man das nicht auch ohne &
braucht.
Falk B. schrieb:> Und wo kommt die variable Strukturierung zum Tragen?
Beim Auslesen:
- Wenn ich per memcpy umkopieren will
- oder wenn ich gar nicht umkopieren will (sondern direkt auswerte)
Beim Schreiben:
- Wenn ich per memcpy schreiben will
- oder wenn ich wahlfrei schreiben will: Struktur-Ptr und alle Elemente
davon direkt, in beliebiger Reihenfolge.
Beim Beispiel serieller Treiber unten. Bei rx Byte für Byte rein und im
Block raus, beim tx umgekehrt.
Falk B. schrieb:> Darüber solltest du nochmal nachdenken . . .
Ich halte das Konzept, dass der Nutzer/Aufrufer den Speicher
bereitstellt, für gut. Also wenn ich einen Fifo von X Bytes brauche,
dann lege ich X+n Bytes an und sage dem Treiber:
"hier ist Speicherplatz, mache mir daraus einen Fifo und gib ihn mir".
Bei Dir braucht man zusätzlich noch die Verwaltungsstruktur. Das wäre
kein Problem, aber damit muss diese veröffentlicht werden. Bei mir
nicht.
Aber ja, ich hätte noch ein #define hinzu fügen müssen, dass den
Overhead berücksichtigt. Entweder als Größe der Verwaltungsstruktur,
oder das mir für n Telegramme mit m Bytes den Mindestplatz ausrechnet.
So in der Art
A. S. schrieb:> Der eigentliche Treiber sind nur die die Zeilen bis "/* --- ende ---> */". Danach beginnen einfache Tests und Beispiele.
Das hätte man in eine extra Datei packen sollen.
> Information Hiding. Die verwaltungsstruktur etc. gehen niemanden was an.
Schon klar.
> In der Anwendung ist es einfach ein Datentyp, ein Handler, eine Referenz> auf genau einen ASBuffer. Egal ob Ptr oder was auch immer.
Nix egal. Man sollte schon erkennen, ob man mit einem Pointer oder
Datenblock hantiert!
> Du kannst 100> von diesen Buffern anlegen und brauchst dann halt 100 Händler.
Und wo liegt dann die Flexibilität beim Datenempfang? Man kann und will
ja nicht dauern Handler anlegen und löschen. Irgendwie verstehe ich das
Konzept nicht.
In einen FIFO kann man beliebig Daten reinschieben, egal ob klein oder
große Datenpakete. Klar muss die Leseseite das dann entspechend
auseinanderplücken. Aber auch das ist ein lösbares Problem. Z.B. indem
man im FIFO oder in einem 2. FIFO jeweils die Länge der einzelnen
Datenpakete kodiert. Sprich, es gib zu jedem Telegramm eine Blocklänge.
Damit hat man maximale Flexibilität bei minimalem Aufwand. Ja, man kann
bei so einem normalen FIFO auf Ringpufferbasis nicht immer einfach die
Daten linear adressieren, der Überlauf muss entsprechend gehandhabt
werden. Aber das halte ich für vertretbar.
> Man> sollte m.E. nicht mit & Arbeiten, wenn man das nicht auch ohne &> braucht.
Wer sagt das? Oder ist das nur deine subjektive Interpretation?
Der Adressoperator ist das kleine 1x1 von C, es spricht rein GAR NICHTS
gegen seine Nutzung.
> - Wenn ich per memcpy umkopieren will> - oder wenn ich gar nicht umkopieren will (sondern direkt auswerte)
Das mag in einigen Situationen verlockend und günstiger sein, ist aber
auch in einem normalen FIFO möglich. Denn der Überlauf der Daten im
Speicher passiert ja nicht bei jedem Datenpaket, sondern nur bei EINEM!
> Beim Schreiben:> - Wenn ich per memcpy schreiben will> - oder wenn ich wahlfrei schreiben will: Struktur-Ptr und alle Elemente> davon direkt, in beliebiger Reihenfolge.
Dito.
> Beim Beispiel serieller Treiber unten. Bei rx Byte für Byte rein und im> Block raus, beim tx umgekehrt.
Ich kapier's nicht so wirklich.
A. S. schrieb:> Falk B. schrieb:>> Darüber solltest du nochmal nachdenken . . .>> Ich halte das Konzept, dass der Nutzer/Aufrufer den Speicher> bereitstellt, für gut. Also wenn ich einen Fifo von X Bytes brauche,> dann lege ich X+n Bytes an und sage dem Treiber:>> "hier ist Speicherplatz, mache mir daraus einen Fifo und gib ihn mir".
Schon klar, aber nicht als lokale, statische Variablen in einer
Funktion! Das ist Murks!
> Bei Dir braucht man zusätzlich noch die Verwaltungsstruktur. Das wäre> kein Problem, aber damit muss diese veröffentlicht werden. Bei mir> nicht.
Bei mir braucht die auch keiner, denn alle Infos sind per
Funktionsaufruf erhältlich. Aber du hast anscheinen sowieso ein
gespaltenes Verhältnis zu Pointern?!?
vielen Dank Falk für Deine Rückmeldung.
Ich habe das jetzt hier getrennt,
.h --> Interface, nicht geändert
ASBuf2.c: Nur der Treiber
main.c: Tests dazu, lauffähig
example.c: Beispiel für einen Uart (nur runtergeschrieben, nicht
kompilierbar)
Ich hoffe so wird die Kapselung deutlicher. Die Interaktion einer
Applikation mit dem Fifo ist bei Deinem Beispiel viel intensiver, es
sind deutlich mehr Funktionen. Darum bei mir den Handler als Handler, es
interessiert den Anwender einfach nicht, ob es Pointer, Index oder
Struktur ist, und schon gar nicht, wie die Aussieht. Ein & von irgendwas
geht nur, wenn die Struktur bekannt ist. Das soll sie gar nicht sein.
Falk B. schrieb:> Bei mir braucht die auch keiner
nein brauchen nicht. Du musst sie aber veröffentlichen. Und damit kennt
sie jeder. Information Hiding.
Mir wird grad klar, dass es zu viele Schritte auf einmal sind.
Information hiding als Konzept, message-fifo statt einfachem fifo und
dann erst das einzig neue, die Blockzugriffe trotz Fifo.
Falk, von deinen Fifo-Routinen aus wäre der Sprung offensichtlich zu
groß.
Zu messagebuffer gibt's nichts im Wiki, ... warum eigentlich nicht?
Ich glaube, ich sollte da eine eigene Seite Messagebuffer starten, die
üblichen Konzepte mit ihren Eigenschaften und dann den Code hier
aufgehübscht als Archiv.
Das Verfahren scheint wirklich neu zu sein. wer es kennt will kein
anderes mehr, weil es meist sogar performanter ist als Fifos, (wenn man
mit ein paar Bytes Overhead leben kann).
A. S. schrieb:> Das Verfahren scheint wirklich neu zu sein. wer es kennt will kein
Ich weiß nicht genau, ob ich deinen Ansatz und das zugrunde liegende
Problem, welches du lösen willst richtig verstehe:
Soweit ich aber deinen Ansatz verstehe, ist er nicht neu. Es gibt
diverse Ringbuffer-Implementierungen, welche Templates unterstützen
(also beliebige Datenstrukturen speichern können).
Das Problem mit variablen Datenmengen löse ich immer so, dass ich den
besagten template-unterstützenden Ringbuffer nutze. Im Ringbuffer selbst
tauchen bei mir aber nicht die Daten auf sondern nur ein Zeiger auf die
Daten und eine Information über die Länge:
struct ringbufferDataElement
{
uint dataLen;
byte * pData;
};
Wenn ich messages verarbeite, nutze ich oft template-unterstützende
Message-Queues. Um Daten von einem Ringbuffer in eine Messagequeue
umzuhängen, wird dann (und sofern möglich) also nur der Pointer auf die
Daten weitergereicht.
Heinz schrieb:> sondern nur ein Zeiger auf die Daten und eine Information über die> Länge:
Genau. Und für die eigentlichen Daten brauchst Du Speicher, den du per
malloc oder sonstwie reservierst. Und wenn du nicht weißt, ob du 10
Blöcke a 1000 Byte bekommst, oder 1000 a 10 Byte, dann sind diese
Datenblöcke ein Problem. Malloc in der Menge bei embedded sowieso.
Die Buffer hier vermeiden diese Probleme. Du kannst sicher sein, wenn Du
10k+ein bisschen reservierst, läuft ein Uart-empfang mit 115 k in 1s
nicht über und du hast trotzdem Messages, nicht nur Rohdaten.
Jetzt fällt mir auf, das Paradigma des Freigeben ist hier auch mit im
Hintergrund erschlagen, meist muss man daran sonst selber denken. Wir
haben die Buffer jetzt ein paar Jahre, und synchronisieren damit fast
alles.
Ok die Idee habe ich jetzt verstanden. Ich muss aber sagen (nur meine
Meinung und berufliche Erfahrung), dass mir die Verbindung aus
Datenempfang (Interrupt) und Zusammenbau einer Message direkt im
Interrupt nicht gefällt.
Ich halte es so: Bytes einsammeln im Interrupt, diese per Ringbuffer zur
Weiterverarbeitung bereitstellen, Protokolle, Messages zusammenbauen in
jeweils höheren Schichten. Ich bin ein bekennender FAN des OSI
Referenzmodells :)
Ich meine, dass deine Implementierung für genau deinen Fall bestimmt
perfekt funktioniert, aber wer bspw. Fehlererkennung braucht oder Layer4
Protokolle implementieren möchte, kann damit nicht so viel anfangen.
Im Vorraus: Ich hab mir nur die API im .h-File angeschaut. Die Idee
find ich nicht verkehrt. Ein paar Kommentare.
1) typedef BYTE: Komplett groß geschriebene Identifier sind für Makros
reserviert. Bin ja auch kein Fan von stdint, aber evtl hier besser -
packt auch nicht unqualifizierte Symbole in den globalen Namensraum (wie
auch okType). Alternativ gleich void-Pointer, ermöglichen dann auch
direkte Structs. Dann sollte Allocate allerdings auch alignte Pointer
zurückliefern.
2) Pointer-Typedef (BufType): Bin ich kein Fan von - weder von einem
Typedef der Struct und erst recht nicht von einem Pointer darauf. Mit
"struct" liest/parsed sich Sourcecode zig mal besser als mit wilden
Typedefs. Und das "*" zeigt, dass es sich um Pointer handelt. Dann
werden auch "s", "p", "i", "Type" Prefixe/Suffixe unnötig ;-)
3) GetMsg: Da fehlt mir ein "Discard". GetMsg liefert einen Pointer
innerhalb des Buffers zurück. Der darf natürlich noch nicht
wiederverwendet werden, bis der Aufrufer mit den Daten durch ist. Es
fehlt also eine Funktion, den Speicher endgültig freizugeben.
4) Sollen mehrere aufeinanderfolgende Allocates mit Commits in
beliebieger Reihenfolge erlaubt sein? Das gleiche bei Get/Discard?
Hmm... hab mal in die Implementation geschaut.
> 3) GetMsg: Da fehlt mir ein "Discard". [...]
Scheint, dass der Discard implizit beim nächsten GetMsg gemacht wird.
Hat den Nachteil, dass, obwohl der Speicher schon nicht mehr benötigt
wird, der Buffer evtl voll bleibt und keine neuen Daten reinkommen.
Wenn man nicht regelmäßig pollt, könnte sogar ein Deadlock entstehen.
> 4) Sollen mehrere aufeinanderfolgende Allocates [...]
Allocate scheint einen vorherigen Allocate zu verwerfen - ein realloc
ohne copy. Der Pointer beim Commit schein mir dann überflüssig.
Eine Sache ist mir noch aufgefallen (oder übersehen): der Zugriff per
ASB_LEN_ACC scheint unaligned zu sein - geht auf vielen Kisten schief.
Ich sollte mehr lesen bevor ich schreibe :-/
> Der Pointer beim Commit schein mir dann überflüssig.
Im Beispielcode sehe ich, dass der Pointer wohl nicht der ist, den
Allocate geliefert hat, sondern einer auf das Ende des Blockes. Man
kann also weniger committen als man angefordert hat. Nur, warum einen
Pointer statt einer Länge?!?
Ich halt jetzt erstmal die Kappe, bevor ich noch mehr unnötigen Kram
schreibe ;-)
foobar, Danke für die Rückmeldung.
Zu 1): BYTE und okType … ok, geschenkt. Void-Pointer haben den Nachteil,
dass sie immer gecastet werden müssen. Bei ASBCreate und ASBAddMsg gut,
bei Allocate und Commit m.E. nicht. Wenn man da feste Strukturen hat,
kann man besser einfache Msg-Buffer-Systeme nehmen.
Zu 2): Sehe ich genau wie Du, ist im .c auch so. ASBufType ist anders:
Es ist ein typsicherer Handler. Eines der fundamentalen Konzepte:
Information-Hiding im Interface: Was ASBufType ist, braucht niemand
wissen, es wird nicht dereferenziert, keine Internas.
Ein Handler bei WinApi ist (iirc) ein int, und doch kein int.
foobar schrieb:> der Zugriff per> ASB_LEN_ACC scheint unaligned zu sein - geht auf vielen Kisten schief.
ja. Das müsste noch oben drauf.
und ptr statt int: Weil man meist den Pointer eh hat, wenn man mit *p++
was reinschreibt.
Anwendung: Ethernet oder serielle Schnittstellen, mit oder ohne DMA, mit
einzelnen Bytes oder mehreren auf einmal, die mal mit kurzen und mal
langen Telegrammen kommen.
Wenn immer gleich große Blöcke kommen, geht jedes einfache
Msg-Buffer-System (wobei ich hier im Wiki keines gefunden habe, aber das
baut sich bei embedded eh jeder selbst).
sachs schrieb:
> Void-Pointer haben den Nachteil, dass sie immer gecastet werden müssen.
In C++, nicht in C - war aber auch nur ein Vorschlag.
> ASBufType ist anders: Es ist ein typsicherer Handler. Eines der> fundamentalen Konzepte: Information-Hiding im Interface: Was ASBufType> ist, braucht niemand wissen, es wird nicht dereferenziert, keine> Internas.
Klar. Aber dafür braucht man keinen typedef, dafür gibt's die
"incomplete types":
1
structASBuf;
2
structASBuf*ASBCreate(uint8_t*mem,size_tsize);
3
uint8_t*ASBAllocate(structASBuf*b,size_tn);
4
...
Ich halte nichts davon, auch noch den "struct" per typedef zu
verstecken. Und dann ein "Type" anhängen, damit es lesbar wird?
"struct" ist doch ein Standardschlüsselwort, das den gleichen Zweck
erfüllt ...
Aber das sind jetzt schon alles Stilfragen.
Wie sieht es denn bzgl Interrupt-fest aus? Hab da auf die Schnelle
nichts gesehen.
A. S. schrieb:> ich stehe öfter vor dem Problem, dass ich über Ethernet oder Uart in> einer Zeiteinheit 100 kleine Telegramme oder 10 große Telegramme> reinbekomme.
Ich hätte noch eine Frage zu Ethernet: Welches Protokoll? UDP, TCP -
oder tatsächlich bare metal ethernet?
Heinz schrieb:> Ich muss aber sagen (nur meine> Meinung und berufliche Erfahrung), dass mir die Verbindung aus> Datenempfang (Interrupt) und Zusammenbau einer Message direkt im> Interrupt nicht gefällt.
Geht mir auch so.
Ich hab das mit den Messagepuffern früher mal probiert, aber dann
verworfen, da es mir zu Protokoll lastig ist.
Ich hatte das so gemacht, daß der Interrupt beim Endezeichen ein
Flagregister hochzählt und dann das Main direkt im FIFO parsen kann. Am
Ende ruft es dann ein clear_msg() auf, welches alle inzwischen
empfangenen Zeichen wieder an den Anfang kopiert. Somit bleibt der FIFO
immer linear, d.h. ohne wrap around. Das Flagregister hat die
Endezeichen gezählt, d.h. es war keine feste Messagelänge nötig.
Einen Interrupthandler möchte ich möglichst universell haben, dann kann
ich ihn einmal implementieren und muß mich nie wieder darum kümmern
(Race Conditions, Atomic usw.).
Jetzt kopiere ich byteweise aus dem einfachen FIFO in einen
Kommandopuffer und parse dort. Der RAM ist bei heutigen MCs nicht mehr
so knapp und über die UART spielt auch die CPU-Last keine Rolle.
Und bei CAN oder Ethernet hat man eh extra Puffer, die die Hardware
direkt verwaltet.
Peter D. schrieb:> Jetzt kopiere ich byteweise aus dem einfachen FIFO in einen> Kommandopuffer und parse dort.
Genau dafür ist das hier. Mit den ASBuffern würdest Du beim Endezeichen
sagen "commit", beim nächsten "allocate" und bei allen dazwischen nur
"if(p>=pEnd) p=0; if(p) *p++=c;".
Also wie bisher, auch atomar, nur ohne umkopieren. Und die Entnahme
simpel, egal ob 1 oder 100 msg drin sind.
Und nur ein System, egal ob TX oder RX.
Vielleicht kannst Du Dir mal das Beispiel in Example.c anschauen und mit
Deiner typischen Implementierung zu vergleichen. Dort ist der komplette
Code, d.h. es gibt nicht irgendwo noch die Notwendigkeit irgendwas zu
tun.
Peter D. schrieb:> Ich hab das mit den Messagepuffern früher mal probiert, aber dann> verworfen, da es mir zu Protokoll lastig ist.
Genau. Man braucht für jeden Anwendungsfall irgend etwas anders. Darum
wollte ich das auch nicht Messagebuffer nennen, weil das Probleme
assoziiert, die es hier nicht gibt.
A. S. schrieb:> Vielleicht kannst Du Dir mal das Beispiel in Example.c anschauen und mit> Deiner typischen Implementierung zu vergleichen.
Wenn ich das richtig sehe, macht "void rx_interrupt(BYTE c)" genau das,
was ich gerne vermieden hätte. Der Interrupt muß schon auf das Protokoll
prüfen und nicht erst die höheren Schichten in der Mainloop.
Damit geht z.B. nicht, daß ich eine Flash-Schreibefunktion per
Textkommando aufrufe und diese dann Binärdaten einliest. Mit einer
einfachen FIFO geht es.
Peter D. schrieb:> Wenn ich das richtig sehe, macht "void rx_interrupt(BYTE c)" genau das,> was ich gerne vermieden hätte. Der Interrupt muß schon auf das Protokoll> prüfen und nicht erst die höheren Schichten in der Mainloop.
naja, für Dein Protokoll, wenn ich es richtig verstanden habe, sähe der
Code so aus:
> daß der Interrupt beim Endezeichen ein Flagregister hochzählt und dann das Main
direkt im FIFO parsen kann.
1
voidrx_interrupt(BYTEc)
2
{
3
staticBYTE*p=0,*pEnd=0;
4
5
if(c==ENDEZEICHEN)
6
{
7
ASBCommit(ASBrx,p);
8
p=ASBAllocate(ASBrx,MAX_TEL_SIZE);
9
pEnd=p+MAX_TEL_SIZE;
10
}
11
elseif(p&&p<pEnd)*p++=c;/*else p=0; wenn das Tel verworfen werden soll */
12
#endif
(Anmerkung: Durch die lokalen Daten muss rx_interrupt mit "ENDEZEICHEN"
initialisiert werden)
Ist Dein Code kleiner oder einfacher? Die hälfte des Codes ist
Überwachung, ob mehr daten kommen als MAX_TEL_SIZE. Und es braucht
nichts mehr woanders umkopiert werden.
Der Empfang in Main ist dann, so oft wie nötig: ASBGetMsg und direkt auf
den Daten operieren.
A. S. schrieb:>> daß der Interrupt beim Endezeichen ein Flagregister hochzählt und dann das Main> direkt im FIFO parsen kann.
Wie gesagt, das war nur ein erster Versuch. Ich habs dann verworfen
zugunsten der universellen einfachen FIFO. Ich versuche die Interrupts
immer so klein wie möglich zu halten.
Die Paketerkennung habe ich hier mal gepostet:
Beitrag "Re: STM32 UART buffer rx"
Peter D. schrieb:> Die Paketerkennung habe ich hier mal gepostet:> Beitrag "Re: STM32 UART buffer rx"
Mh, die Paketerkennung ist doch eine High-level (zwischen) Auswertung,
die entfällt größtenteils. Meine Buffer sind das Äquivalent zu deinem
Fifo + Paketaufbereitung Daher wäre dein UART-interrupt interessant,
mein Beispiel ist doch auch die Interruptroutine.