Forum: Mikrocontroller und Digitale Elektronik Teil-String Suche in Byte FIFO


von Adam P. (adamap)


Lesenswert?

Morgen zusammen,

ich verwende ein Telit Bluemod +S50 Modul.
Eintreffende Daten werden im USART Interrupt in ein FIFO gepusht.

Nun nehmen wir den Fall, dass ich mehrere AT Befehle absätze und darauf 
Antworten erhalte, soweit so gut.
Irgendwann sind die Lese/Schreibzeiger meines FIFO in Bereichen die 
schon mal verwendet wurden.

Einzelne Antworten sehen so aus:
1
CR LF value CR LF

Dabei braucht man nichts machen, da der Lese/Schreibzeiger genau diesen 
String umschliesst.

Es können aber auch Listen eintreffen:
1
CR LF
2
list entry 1 CR LF
3
list entry 2 CR LF
4

5
list entry n CR LF
6
CR LF

Wenn nun eine Antwort eintrifft und im FIFO landet, möchte ich diese 
herausfiltern:
1) das erste CR LF suchen
2) dann das CR LF danach suchen
3) dazwischen herausfiltern

Nun die Frage:
String Suchfunktionen kann ich ja nicht nutzen, da ja hinter dem 
Schreibzeiger bestimmt keine '\0' kommt und er gar nicht wüsste wo er 
aufhören soll. Weiterhin muss ich die Suche aufteilen bzgl. des 
"Umbruchs"
am Ende des Speicherbereichs vom FIFO.

Gibts da eine geeignete Suchfunktion, evtl. was mit mem* oder strstr 
aber mit Angabe der maximalen Suchlänge.

Find grade nichts passendes, oder ich sehe es viel zu komplex.

Gruß Adam

von Adam P. (adamap)


Lesenswert?

Vielleicht ?
1
void * memmem (const void *haystack, size_t haystack-len, const void *needle, size_t needle-len)

Wobei ich grad sehe, "memmem" gibts nur im GNU und nicht bei 
"arm-none-eabi"?

Sowas wie "strnstr" gibts wohl auch nicht als "fertige" Implementierung?
Habe nur Nachbauten gefunden.

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

Adam P. schrieb:

> Irgendwann sind die Lese/Schreibzeiger meines FIFO in Bereichen die
> schon mal verwendet wurden.

Das ist normal und sollte nicht stören. Denn es werden ja die alten 
Daten überschrieben.

> Wenn nun eine Antwort eintrifft und im FIFO landet, möchte ich diese
> herausfiltern:

Du musst einfach den FIFO von der Befehlsverarbeitung trennen. Im FIFO 
sucht man nicht rum, u.a. weil dort ja irgendwo ein Pointer bzw. 
Indexrücksprung nötig ist.

> Find grade nichts passendes, oder ich sehe es viel zu komplex.

Weil dein Ansatz nix taugt. Lies ein Kommando aus dem FIFO und bearbeite 
es. Das ist der Sinn der Sache.

von Adam P. (adamap)


Lesenswert?

Falk B. schrieb:
>> Find grade nichts passendes, oder ich sehe es viel zu komplex.
>
> Weil dein Ansatz nix taugt. Lies ein Kommando aus dem FIFO und bearbeite
> es. Das ist der Sinn der Sache.

Und genau das ist ja das Problem an der ganzen Sache.
Wenn z.B. 15 Byte eintreffen, dann heißt das nicht, das der Datensatz 
(Kommando) schon vollständig empfangen wurde.
Es kann sein, dass erst kurz später der Rest kommt.

Sprich müsste ich den FIFO in ein extra Buffer auslesen und dort dann 
suchen ob das Ende eingetroffen ist.
Das bedeutet aber RAM verbrauch, den ich nicht wirklich übrig habe.

Ich habe versucht eine fifo_mem_search() zu basteln, dauert aber in der 
Ausführung zu lange "für mich", lag für 1536 Byte bei 0,2 - 0,6 ms, je 
nach Verfahren.

Ich werde jetzt wohl entweder nur mit einem Buffer das lösen und den vor 
Neuverwendung 0x00 setzen und dann mit strstr arbeiten, das braucht nur 
ca. 68µs.
Oder eine Kombi aus kleinem FIFO und großen Buffer.

Ja ich hab mir schon gedacht, dass das nicht so das Wahre ist.
Danke trotzdem.

: Bearbeitet durch User
von FOp (Gast)


Lesenswert?

Wenn die Antworten relativ konstant ausfallen, reicht ja schon ein 
Zähler zusätzlich im RAM. Der zählt dann, wieviel Buchstaben von 
"\r\nOK\r\n" schon angekommen sind. Wenn der Zähler bis auf 6 hochzählen 
konnte, hast Du einen Treffer. Musst halt für jedes Zeichen, das Du aus 
dem FIFO holst prüfen, ob es an die durch den Zähler indizierte Stelle 
des Suchstrings passt. Dementsprechend wird der Zähler inkrementiert, 
auf 0 oder 1 gesetzt.

von Peter D. (peda)


Lesenswert?

Adam P. schrieb:
> Sprich müsste ich den FIFO in ein extra Buffer auslesen und dort dann
> suchen ob das Ende eingetroffen ist.
> Das bedeutet aber RAM verbrauch, den ich nicht wirklich übrig habe.

Ja, so macht man das. Keiner parst direkt im FIFO.
Der FIFO muß so groß sein, wie maximal neue Zeichen reinkommen können, 
während das vorherige Kommando geparst wird. Und der Puffer muß so groß 
sein, wie das längste gültige Kommando.
Man muß ja nicht gerade alles in einen ATtiny13 mit 64Byte RAM 
reinquetschen.

von W.S. (Gast)


Lesenswert?

Adam P. schrieb:
> Nun die Frage:
> String Suchfunktionen kann ich ja nicht nutzen, da ja hinter dem
> Schreibzeiger bestimmt keine '\0' kommt

Merke dir mal, daß ein FIFO eine treiberinterne Struktur ist, wo du von 
außen nicht hineinzugrabschen hast. Also laß den FIFO in Ruhe und 
beschrönke dich auf die üblichen Einzelzeichen-Funktionen, die der 
Treiber bietet. Als da vermutlich wären:
char GetChar()
bool IsCharVorhanden()

Und bau dir in deinem Kommandoprogramm eine Kommandozeile damit auf, die 
du später in aller Ruhe auswerten kannst.

W.S.

von Adam P. (adamap)


Lesenswert?

FOp schrieb:
> Wenn die Antworten relativ konstant ausfallen, reicht ja schon ein
> Zähler zusätzlich im RAM. Der zählt dann, wieviel Buchstaben von
> "\r\nOK\r\n" schon angekommen sind.

Das ist es ja, leider ist es nicht immer konstant.

Zusätzlich kommen im Daten-Modus binär Werte die damit gar nichts zu tun 
haben und trotzdem erkannt werden müssen um diese an eine höhere Schicht 
weiterzugeben.

Peter D. schrieb:
> Ja, so macht man das. Keiner parst direkt im FIFO.

Hab ich so auch noch nie gemacht, war nur ne Idee, die ich aber schnell 
verworfen habe.

Peter D. schrieb:
> Der FIFO muß so groß sein, wie maximal neue Zeichen reinkommen können,
> während das vorherige Kommando geparst wird. Und der Puffer muß so groß
> sein, wie das längste gültige Kommando.

Ja irgendwie werde ich da wohl ne Kombination draus machen, muss mal 
schauen wie es läuft.

Peter D. schrieb:
> Man muß ja nicht gerade alles in einen ATtiny13 mit 64Byte RAM
> reinquetschen.

Ne, ist nen SAM4E16E...aber mit OLED (Framebuffer) + SD + USB Buffern + 
weitere. Deshalb bin ich grad schon bei "125816 bytes   96,0 % Full".

Aber danke euch, dann weiß ich zumindest, dass meine erste Idee richtig 
war, bevor ich diesen komischen Gedanken mit dem FIFO Search hattte :-D

von Adam P. (adamap)


Lesenswert?

W.S. schrieb:
> Merke dir mal, daß ein FIFO eine treiberinterne Struktur ist, wo du von
> außen nicht hineinzugrabschen hast. Also laß den FIFO in Ruhe

Meine Grundidee war, das der Treiber die Verarbeitung der AT Befehle 
übernimmt und nur Nutzerdaten weiterleitet.

Sprich von oben sag ich dem Treiber, verarbeite mir z.B. AT+X Befehl.
Der Treiber weiß, ah ok, so sieht der aus, soviele Versuche mit dem 
Timeout.
Und würde nur OK, ERR, oder TIMEOUT zurückgeben, evtl. Daten bei OK.

von W.S. (Gast)


Angehängte Dateien:

Lesenswert?

Adam P. schrieb:
> Find grade nichts passendes, oder ich sehe es viel zu komplex.

Nein, du hast für sowas bloß noch nix parat. Schau dir mal die 
angehängte Datei an. Das ist nur ein Ausriß, aber er sollte dir zeigen, 
wie man sowas machen kann.

W.S.

von W.S. (Gast)


Lesenswert?

Adam P. schrieb:
> Meine Grundidee war, das der Treiber die Verarbeitung der AT Befehle
> übernimmt und nur Nutzerdaten weiterleitet.

Laß die verschiedenen Schichten einer Übertragungskette ihre jeweiligen 
Aufgaben machen ohne daß eine einzige Schicht die Arbeit von mehreren 
Schichten miteinander verquirlen muß. Wenn du das nicht beachtest, 
machst du dir nur selber das Leben schwer.

W.S.

von A. S. (Gast)


Lesenswert?

Beim Byte-Fifo wird jeweils nur 1 Byte gelesen oder geschrieben. Und 
beim Lesen aus dem Puffer gelöscht.

Meist dient er der Synchronisation zwischen Interrupt und Main.

Wenn Du Nachrichten auswerten willst, gibt es mehrere Möglichkeiten, die 
davon abhängen, wie deine Nachrichten aussehen. Meist werden Nachrichten 
zyklisch in eigenen buffern zusammengebaut, mit einzelnen Bytes aus dem 
Byte-Fifo. Und je Zyklus nur teilweise, d.h. die Nachrichten-Dekodierung 
hat eigene Zustände.

Wenn es nur darum geht, ob eine Nachricht vollständig ist, erfolgt oft 
eine Spar-Dekodierung im Interrupt. Z.b. wird bei jedem Endezeichen ein 
flag gesetzt, damit Main eine ganze Nachricht auf einmal dekodieren 
kann.

Man kann auch Nachrichten im Interrupt direkt dekodieren und ganze 
Nachrichten weitergeben. Dann braucht man aber in der Regel 2 oder mehr 
Nachrichten-Speicher. Einem für die Verarbeitung in Main und einen um 
das nächste schon zu verarbeiten.

Genauso gut kann man das auf n Ebenen aufteilen:

Ein Byte-HW-Fifo im uart

Ein Raw-Byte-Fifo für den Interrupt-Handler

Ein Byte-fifo für die msg Dekodierung (Byte-stuffing, start/Stop)

Ein msg-fifo für eine ganze Antwort (mehrere mag)

Und dann eine Antwort-Fifo, die mehrere Antworten enthält, die je aus 
mehreren msg bestehen, die je X Bytes haben

von W.S. (Gast)


Lesenswert?

Adam P. schrieb:
> Ne, ist nen SAM4E16E...aber mit OLED (Framebuffer) + SD + USB Buffern +
> weitere. Deshalb bin ich grad schon bei "125816 bytes   96,0 % Full".

Und das - über 120K Byte an Code - für eine Firmware, die lediglich GLCD 
(o.ä.), SDIO und USB (vermutlich CDC) enthält? Das scheint mir doch 
etwas viel zu sein. Ich hätte dafür deutlich unter 32K Byte veranschlagt 
- es sei denn, daß da noch einige recht umfängliche Grafiken in 
unkomprimiertem Format hinzukommen.

W.S.

von Adam P. (adamap)


Lesenswert?

W.S. schrieb:
> Schau dir mal die angehängte Datei an.

Das sieht mir nach CLI aus.
Mein CLI hab ich anders gelöst und ist auf mein Fall hier nicht ganz 
anwendbar.

W.S. schrieb:
> Laß die verschiedenen Schichten einer Übertragungskette ihre jeweiligen
> Aufgaben machen ohne daß eine einzige Schicht die Arbeit von mehreren
> Schichten miteinander verquirlen muß.

So war ja meine Idee.

A. S. schrieb:
> Beim Byte-Fifo wird jeweils nur 1 Byte gelesen oder geschrieben. Und
> beim Lesen aus dem Puffer gelöscht.

Ja hast recht, hab mich unglücklich ausgedrückt.

Mein Aufbau ist wie folgt:

BLE Schicht (Ablauf für Verbindung und Datenverarbeitung)
-------------------------------------------------------------
BM+S50 Treiberschicht (Funktionalität AT Befehle und weiters)
-------------------------------------------------------------
COM USART Treiber (2x 64Byte Buffer mit PDC und Timeout)
-------------------------------------------------------------
HAL

von Adam P. (adamap)


Lesenswert?

W.S. schrieb:
> über 120K Byte an Code

RAM nicht Flash.

Es sit USB Vendor...aber das ist hier nicht das Thema.
Flash hab ich 167K Byte.

Ja ist etwas komplexer das Gerät.

von Adam P. (adamap)


Lesenswert?

Adam P. schrieb:
> Mein Aufbau ist wie folgt:

Somit erhalte ich in meiner BM+S50 Treiberschicht entweder 64Byte Pakete 
oder weniger bei USART Timeout.

Edit:
Also ich werde dann jetzt mal zwei Wege testen.

1) Nur ein Buffer ohne FIFO.
2) Ein FIFO mit 3x 64Byte + ein 1536Byte Buffer für Verarbeitung (Größe 
laut Protokoll Definition)

Alles andere scheint wohl zu langsam oder unnötig kompliziert zu sein.

W.S. schrieb:
> Nein, du hast für sowas bloß noch nix parat.

In der größeren Version von diesem Gerät habe ich ein AMW106 Modul.
Da ist der Schichtenaufbau ähnlich, jedoch war die Implementierung 
einfacher, da es keine AT Befehle verwendet und es dort keine Protokoll 
Absprache in dieser Art und Weise gab.
Nun muss ich auf jede Situation reagieren und das Konfigurieren war 
ebenfalls einfacher. Wollte ich nur zur Erläuterung erwähnt haben.

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

Adam P. schrieb:
> Falk B. schrieb:
>>> Find grade nichts passendes, oder ich sehe es viel zu komplex.
>>
>> Weil dein Ansatz nix taugt. Lies ein Kommando aus dem FIFO und bearbeite
>> es. Das ist der Sinn der Sache.
>
> Und genau das ist ja das Problem an der ganzen Sache.
> Wenn z.B. 15 Byte eintreffen, dann heißt das nicht, das der Datensatz
> (Kommando) schon vollständig empfangen wurde.
> Es kann sein, dass erst kurz später der Rest kommt.
>
> Sprich müsste ich den FIFO in ein extra Buffer auslesen und dort dann
> suchen ob das Ende eingetroffen ist.
> Das bedeutet aber RAM verbrauch, den ich nicht wirklich übrig habe.

Das geht auch anders. Deine UART-Empfangsroutine mit soviel Intelligenz 
ausstatten, daß sie zumindest das Ende eines Strings erkennen kann. Wenn 
das der Fall ist, wird eine globale Variable hochgezählt, welche die 
Anzahl der vollständigen Strings im FIFO angibt. Die Lesefunktion kann 
dort drauf zugreifen. Wenn der Zähler auf 0 steht, gibt es keinen 
vollständigen Datensatz im FIFO und man muss im Moment was anderes tun 
Multitasking. Der Zugriff auf den Zähler muss natürlich atomar sein.

von Peter D. (peda)


Lesenswert?

Adam P. schrieb:
> Deshalb bin ich grad schon bei "125816 bytes   96,0 % Full".

Also Du hast 128kB RAM fast voll und willst aber an 1% (1,5kB) noch was 
einsparen, das wird nichts.
Du mußt die großen Brocken (>10%) optimieren, um wieder RAM frei zu 
kriegen.

Vermutlich sind die 96% auch nur statischer Speicher, d.h. Dein Stack 
läuft schon längst über.
Bei 80% würde mir schon unwohl werden.

von Adam P. (adamap)


Lesenswert?

Peter D. schrieb:
> Du mußt die großen Brocken (>10%) optimieren, um wieder RAM frei zu
> kriegen.

Das geht leider nicht (so einfach). An gewissen Stellen benötige ich 
double/triple Buffering, bzgl. Zeitverzögerung von extern um die Daten 
halten zu können.

Peter D. schrieb:
> Dein Stack läuft schon längst über.

Die Angabe ist inkl. Stack. (Habe es grad noch mal verifiziert).

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

Adam P. schrieb:
> Die Angabe ist inkl. Stack. (Habe es grad noch mal verifiziert).

D.h. das Analysetool muß einen kompletten Call-Tree erstellen und 
abklappern. Und es dürfen keine Rekursionen verwendet werden. Auch 
müssen alle Interrupthandler einbezogen werden.

Mein Compiler (AVR-GCC) hat leider kein so ein Tool, was brauchbare 
Ergebnisse liefert. Ich hab mir daher eine Routine geschrieben, die zur 
Laufzeit die Stackreserve ermittelt. Dazu fülle ich bss_end bis SP mit 
einem Pattern (0x77) und prüfe später, bis wohin das Pattern noch 
vorhanden ist.

Adam P. schrieb:
> Das geht leider nicht (so einfach). An gewissen Stellen benötige ich
> double/triple Buffering, bzgl. Zeitverzögerung von extern um die Daten
> halten zu können.

Wenn sich die großen Brocken nicht besser optimieren lassen, dann ist 
der MC zu klein und Du mußt einen mit mehr RAM benutzen.
An Programmteilen mit 1% Verbrauch zu werkeln, ist nur Mikrooptimierung 
mit kaum Effekt.

von Adam P. (adamap)


Lesenswert?

Peter D. schrieb:
> Dazu fülle ich bss_end bis SP mit
> einem Pattern (0x77) und prüfe später, bis wohin das Pattern noch
> vorhanden ist.

Das habe ich auch mal gemacht, mit 0xAA gefüllt und dann laufen gelassen 
und geschaut bis wohin er kommt.

Peter D. schrieb:
> Wenn sich die großen Brocken nicht besser optimieren lassen, dann ist
> der MC zu klein und Du mußt einen mit mehr RAM benutzen.

Das war früher alles so nicht gedacht, nun ist es schon so.
Geräte sind ja schon seit 2018 im Umlauf.
Nun muss aber die Bluetooth Funktionalität nachimplementiert werden, da 
die Spezifikationen erst seit 3 Monaten fertig sind und das 
Partnerunternehmen auch erst jetzt damit anfängt.

Ich werde mal KISS probieren, sonst muss ich doch irgendwo mal 
aufräumen.
Werde euch aber bescheid geben.

: Bearbeitet durch User
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.