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
CRLFvalueCRLF
Dabei braucht man nichts machen, da der Lese/Schreibzeiger genau diesen
String umschliesst.
Es können aber auch Listen eintreffen:
1
CRLF
2
listentry1CRLF
3
listentry2CRLF
4
…
5
listentrynCRLF
6
CRLF
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
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.
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.
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.
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.
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.
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.
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
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.
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.
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.
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
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.
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
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.
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.
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.
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.
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).
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.
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.