Forum: Mikrocontroller und Digitale Elektronik Advance Serial Buffer


von A. S. (Gast)


Angehängte Dateien:

Lesenswert?

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.

von Falk B. (falk)


Lesenswert?

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.

von A. S. (Gast)


Lesenswert?

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).

von Falk B. (falk)


Lesenswert?

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
ASBufType ASBCreate(BYTE *mem, int size)
2
{
3
ASBufType b = (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
void InitUart(void)
2
{
3
static BYTE mem_rx[1000];
4
static BYTE mem_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.

von Falk B. (falk)


Lesenswert?

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?

von A. S. (Gast)


Lesenswert?

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.

von A. S. (Gast)


Lesenswert?

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
1
#define ASBUF_MEM_SIZE_ADD (20)
2
#define ASBUF_MEM_SIZE(tel, size) (2UL + MEM_SIZE_ADD + tel*(size+2))
3
 ....
4
BYTE mem[ ASBUF_MEM_SIZE(20, 100)];
5
BYTE mem2[ASBUF_MEM_ADD + 10000];

von Falk B. (falk)


Lesenswert?

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.

von Falk B. (falk)


Lesenswert?

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?!?

von A. S. (Gast)


Angehängte Dateien:

Lesenswert?

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.

von A. S. (Gast)


Lesenswert?

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).

von Heinz (Gast)


Lesenswert?

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.

von A. S. (Gast)


Lesenswert?

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.

von Heinz (Gast)


Lesenswert?

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.

von foobar (Gast)


Lesenswert?

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?

von foobar (Gast)


Lesenswert?

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.

von foobar (Gast)


Lesenswert?

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 ;-)

von A. S. (Gast)


Lesenswert?

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).

von foobar (Gast)


Lesenswert?

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
struct ASBuf;
2
struct ASBuf *ASBCreate(uint8_t *mem, size_t size);
3
uint8_t *ASBAllocate(struct ASBuf *b, size_t n);
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.

von Heinz (Gast)


Lesenswert?

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?

von Peter D. (peda)


Lesenswert?

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.

von A. S. (Gast)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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.

von A. S. (Gast)


Lesenswert?

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
void rx_interrupt(BYTE c)
2
{
3
static BYTE *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
    else if(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.

von Peter D. (peda)


Lesenswert?

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"

von A. S. (Gast)


Lesenswert?

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.

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.