Forum: Mikrocontroller und Digitale Elektronik CAN Bus fifo


von fifo (Gast)


Lesenswert?

Hallo,

in diesem Thread (Beitrag "Can Interruppt") ging es 
um die Frage, ob man einen std. fifo in c schreibt und für "jeden CAN 
Bus Wert" sprich ID, Nachrichten, Nachrichten Längen einen eigenen fifo 
erstellt oder das ganze in einer einzigen Struktur speichert.

Guido war damals fest davon überzeugt, eine Lösung für alles zusammen 
ist die bessere. Ich habe inzwischen immer mal wieder drüber nachgedacht 
und tendiere weiterhin zu der Variante für jeden Typ einen eigenen fifo 
anzulegen.

Habe mal etwas gegoogelt und entweder ich bin zu doof oder es macht 
sonst keiner in der "eine Struktur" Variante.

Ich denke man spart einiges an Speicherplatz. Konsistent sollte das 
ganze ja bleiben, indem man ein paar Abfragen hinzufügt.

Ich seh mich schon da sitzen und das umschreiben.. aber vorher wollte 
ich mal die Allgemeinheit zu ihrer Meinung fragen :)

von dunno.. (Gast)


Lesenswert?

fifo schrieb:
> für "jeden CAN Bus Wert" sprich ID, Nachrichten, Nachrichten Längen
> einen eigenen fifo

Macht das sinn?
Wie stellst du dann die beziehungen zwischen den daten her, wenn alles 
fragmentiert in irgendwelchen fifos liegt.. Also, welche id gehört zu 
welcher nachricht..?

Denke, du wirst präzisieren müssen..

von ui (Gast)


Lesenswert?

fifo schrieb:
> Ich denke man spart einiges an Speicherplatz. Konsistent sollte das
> ganze ja bleiben, indem man ein paar Abfragen hinzufügt.

Aus was für Gründen sollte man den Speicherplatz sparen, wenn man genau 
das gleiche speichert?

Angenommen 2 Fifos:
in 1) speicher ich x,y,z
in 2) speicher ich a,b

bei einer Fifo speicher ich halt dann
x,y,z,a,b


bei einer Fifo hat man sogar einen gewaltigen Vorteil:
die chronologische Reihenfolge bleibt erhalten...

von Guido (Gast)


Lesenswert?

fifo schrieb:
> Guido war damals fest davon überzeugt, eine Lösung für alles zusammen
> ist die bessere.
ist er auch immernoch ;)

fifo schrieb:
> Ich denke man spart einiges an Speicherplatz.
Das solltest du mal näher erläutern, vielleicht hast du ja eine gute
Idee!

Ich will ja nicht behaupten, die allein seeligmachende Lösung zu kennen, 
aber mit dem CAN Bus, Fifos, Interrupts, Treiberschnittstellen habe ich 
mich jahrelang befassen dürfen. Es gibt verschiedene Ansätze, je nach 
Anforderung.

Aber Informationen, die zusammen im Interrupt aufschlagen und dann auch 
zusammen weiterverarbeitet werden müssen, sollten m.E. nach in eine 
Datenstruktur geschrieben und in einem Ringspeicher verwaltet werden.

Wenn du (ich wiederhole mein Argument) drei Fifos benutzen willst,
musst du die Fifos konsistent halten. D.h. du musst an der Stelle, wo du 
die Fifos liest, dafür sorgen, dass dir kein Interrupt dazwischen haut. 
Das bedeutet, du musst den Interrupt sperren und dann alle Fifos lesen 
und dann den Interrupt wieder freigeben. Aber das wäre widersinnig. Man 
benutzt ja einen Ringspeicher um den Interrupt eben genau nicht sperren 
zu müssen.

...aber es kann auch sein, dass ich deine Argumente einfach nicht 
verstehe. Ich will nicht ausschließen, dass dein Ansatz in deinem 
konkreten Fall die bessere Lösung ist!

von Guido (Gast)


Lesenswert?

ui schrieb:
> Aus was für Gründen sollte man den Speicherplatz sparen, wenn man genau
> das gleiche speichert?

So wie ich das sehe, müssen zumindest die Fifo-Verwaltungsstrukturen
3x angelegt werden - also mind. 3x Read-Pointer, 3x Write-Pointer zzgl. 
weitere Daten je nach Ringbuffer-Implementierung

von Horst (Gast)


Lesenswert?

Was soll denn bitte der Vorteil bei dreifach so großem 
Verwaltungsaufwand und der Schwierigkeit der Synchronität sein? Bisher 
jedenfalls hab ich hier kein Argument (nichtmal nicht überzeugende) 
dafür lesen können.

von fifo (Gast)


Lesenswert?

Guido schrieb:
> fifo schrieb:
>> Ich denke man spart einiges an Speicherplatz.
> Das solltest du mal näher erläutern, vielleicht hast du ja eine gute
> Idee!

Mit Speicherplatz meine ich Flashspeicher des Programms. Wenn man alles 
in einer Struktur verwaltet braucht man mehr pointer, muss alle Fktion 
erweitern usw. Man hat ja max 4x8Bit für die CAN ID, zwischen 0 und 
8x8Bit für die Daten und einmal 8Bit für dlc + ggf. weitere Parameter. 
Das muss ja alles programmiert werden. Legt man für jedes einen FIFO an, 
spart man das und muss dafür immer gucken, wie der aktuelle Stand aller 
FIFOs ist.

von Horst (Gast)


Lesenswert?

fifo schrieb:
> Guido schrieb:
> fifo schrieb:
> Ich denke man spart einiges an Speicherplatz.
>
> Das solltest du mal näher erläutern, vielleicht hast du ja eine gute
> Idee!
>
> Mit Speicherplatz meine ich Flashspeicher des Programms. Wenn man alles
> in einer Struktur verwaltet braucht man mehr pointer, muss alle Fktion
> erweitern usw. Man hat ja max 4x8Bit für die CAN ID, zwischen 0 und
> 8x8Bit für die Daten und einmal 8Bit für dlc + ggf. weitere Parameter.
> Das muss ja alles programmiert werden. Legt man für jedes einen FIFO an,
> spart man das und muss dafür immer gucken, wie der aktuelle Stand aller
> FIFOs ist.

Häh? Warum braucht man mehr Pointer? Du mit deinen tausend Fifos 
brauchst mehr pointer in jedem Fall. Die struct-Lösung braucht je nach 
Fifo-Implementierung genau 2.

von Guido (Gast)


Lesenswert?

fifo schrieb:
> Mit Speicherplatz meine ich Flashspeicher des Programms.
Die Daten müssen doch zur Laufzeit geändert werden. Die
liegen daher im RAM. Oder was meinst du jetzt?

fifo schrieb:
> zwischen 0 und
> 8x8Bit für die Daten und einmal 8Bit für dlc
Ist deine Sorge, dass du immer 8 Bytes für die CAN-Daten verbrauchst, 
auch wenn der DLC bspw. nur 1 ist - also 7 Bytes verschwendet?
Falls ja, nimmst du einfach eine Ringspeicherimplementierung, die mit 
Objekten verschiedener Größe klarkommt.

von fifo (Gast)


Lesenswert?

Ne, das ist nicht meine Sorge.

Aber mal anderes: Warum sollte man eigentlich nicht ein Fifo anlegen mit 
der Lib. aus dem andern Thread und da alles drin speichern. (Sprich ein 
ganz normales fifo wie mans auch für UART nutzt)

Man könnte:
ID | ID | ID | ID | MSG 0 - 8 | DLC

Als erstes liest man den DLC Wert aus, weiß dann wie viele Nachrichten 
man gespeichert hat, liest die Nachrichten aus und dann die 32 Bit CAN 
ID usw.

Man spart bei der Variante auch das Zusammenschieben der CAN ID zu einer 
32 Bit Variable, was man dann ggf. später in der Hauptschleife tun 
könnte.

Hab ich nen Denkfehler?

von Guido (Gast)


Lesenswert?

fifo schrieb:
> Man spart bei der Variante auch das Zusammenschieben der CAN ID zu einer
> 32 Bit Variable, was man dann ggf. später in der Hauptschleife tun
> könnte.
>
> Hab ich nen Denkfehler?

Naja:
1.
DLC vorne ist schonmal ok, aber ich würde dann MSG 8 - 0 reinschreiben, 
um beim Auslesen gleich die richtige Reihenfolge zu haben. Also nach DLC 
erstmal MSG-0, MSG-1 usw

2.
Bevor du im Interrupt in den Fifo schreibst, musst du abfragen,
ob im Fifo genug Platz für die gesamte Botschaft ist.
Wenn du nicht weißt, wie ein Fifo genau funktioniert, wird dir das
Probleme machen. Und wenn du es weißt, kannst du genauso gut einen
Fifo schreiben, der komplette CAN Objekte aufnehmen kann ;)

3.
Wenn du das so umsetzt, machst du dir warscheinlich eine Wrapper 
Funktion, mit der du die CAN-Daten byteweise in den Fifo schreibst bzw. 
liest. Aber dann hast du eigentlich genau das gemacht, was
ich schon die ganze Zeit propagiere ;)

ringBufferWrite(uint len, char* bytes);

CANObj data;
//schreiben: Länge=DLC + CAN_ID + RTR
ringBufferWrite(data.dlc+sizeof(uint32)+sizeof(char), (char*)&data);

von Guido (Gast)


Lesenswert?

Guido schrieb:
> ringBufferWrite(data.dlc+sizeof(uint32)+sizeof(char), (char*)&data);

Korrektur bzw besser:
ringBufferWrite(data.dlc+sizeof(data.CanId)+sizeof(data.rtr),(char*)&dat 
a);

Der macht dann nichts weiter, als ab Adresse &data eine Anzahl
Bytes zu nehmen und in den Ringbuffer zu kopieren.
Man muss aber mit dem Alignment aufpassen! Datenstrukturen am besten
so aufbauen, dass der Compiler keine Alignmentbytes einbaut.

von fifo (Gast)


Lesenswert?

Hallo nochmal,

https://www.mikrocontroller.net/articles/FIFO#FIFO_als_Bibliothek
Mit dieser Bib hat man doch genau die Möglichkeit, die du oben 
beschreibst (also fast) Ich weiß nicht, ob du das Archiv überhaupt 
gesehen hast, oder über die Beispiele schreibst... ich meine auf jeden 
Fall immer das Archiv.

z.B. zum schreiben wird dieses Beispiel angeführt:
1
block_size = 7;
2
    wrap_size = fifo_get_write_wrap(&fifo);
3
    if (fifo_get_free(&fifo) >= block_size) { 
4
        if (block_size > wrap_size) {
5
            // split action into two blocks due to pointer wrap around
6
            memset((uint8_t*)fifo.write_p, 'A', wrap_size * sizeof(fifo_data_t));
7
            fifo_write_bursted(&fifo, wrap_size);
8
            block_size -= wrap_size;
9
        }
10
        // no pointer wrap around in block or second half of block operation
11
        memset((uint8_t*)fifo.write_p, 'A', block_size * sizeof(fifo_data_t));
12
        fifo_write_bursted(&fifo, block_size);
13
    }

Mich würde interessieren, wie das funktioniert. Ich hab z.b. jetzt die 
CAN ID und die Nachrichten sowie die Anzahl der Nachrichten. Wie bekomme 
ich diese mit dem Code oben in den fifo in einem rutsch?

Wenn ich mir memset() angucke:
1
memset   (   void *    dest,
2
    int    val,
3
    size_t    len 
4
  )


Das Ziel ist klar, auch die Länge, aber warum nutz man hier "A" ? A 
sollte doch nur 2 Byte groß sein und nicht 7? Und wie würde ich z.b. 
jetzt ein Array aus CAN ID + MSG + Länge übergeben?

von Guido (Gast)


Lesenswert?

Auf dieses Beispiel hatte ich mich nicht bezogen.

"A" ist hier anscheinend ein char und somit 1Byte groß.
Der Code stellt wohl beispielhaft dar, wie man 7x "A" in den
Fifo schreibt, wenn ich das richtig verstehe.

Du musst also einfach memcpy nehmen und deinen CAN-struct in den
Fifo schreiben.

Kleine Kritik an dieser Implementierung:
Die Behandlung der zwei Fälle
1. Lesepointer vor Schreibpointer
2. Lesepointer hinter Screibpointer
sollte man nicht von Hand bedienen müssen.
Aus meiner Sicht müsste das innnerhalb der Funktion fifo_write_bursted
bzw. fifo_read_bursted behandelt werden.

Für mich müsste das so aussehen:
fifo_write_bursted(&fifo, *byte data, int len);

von fifo (Gast)


Lesenswert?

Mit memcpy() also so: (Unabhänige von CAN)
1
uint8_t array[5];
2
array[0] = 1;
3
array[1] = 2;
4
array[2] = 3;
5
array[3] = 4;
6
7
block_size = 4;
8
9
memcpy((uint8_t*)fifo.write_p, &array, block_size * sizeof(fifo_data_t));
10
fifo_write_bursted(&fifo, block_size);

Theoretisch müsste ja dann als erstes 1, dann 2 usw. aus dem fifo 
"kommen", wenn man einzeln abruft. Ich erhalte allerdings 2,3,4,1 nach 
ein paar Durchläufen.

Mit Durchlauf meine ich array in fifo schreiben und direkt wieder 
ausgeben.

Beim ersten Durchlauf erhalte ich:

1,2 (4,3)

Zweiter Durchlauf:

3,4,1 (6,5,4)

Dritter Druchlauf:

2,3,4,1 (7,6,5,4)

Vierter Durchlauf wie Dritter usw.


Was mache ich hier falsch?

von fifo (Gast)


Lesenswert?

Vergessen zu erwähnen:

Die Werte in Klammern sind das aktuelle level im fifo.
z.b.
1,2 (4,3) heißt, dass wenn er die 1 aus dem fifo holt das level 4 ist. 
Wenn die 2 aus dem fifo genommen wurde ist das level 3

Ich verstehe es nicht...

von Guido (Gast)


Lesenswert?

fifo schrieb:
> Was mache ich hier falsch?

Hast du die Fifo-Implementierung und das von dir gezeigte
Code-Beispiel verstanden?
Speziell wozu fifo_get_free da ist?
Weißt du, was ich mit "Schreibpointer vor Lesepointer" und "Lesepointer 
vor Schreibpointer" meine?

Und noch ein Tip:
Du hast  array[5] nicht komplett initialisiert.
Du hast da was falsch abgeschrieben.

von fifo (Gast)


Lesenswert?

> Hast du die Fifo-Implementierung und das von dir gezeigte
> Code-Beispiel verstanden?
Denke ja

> Speziell wozu fifo_get_free da ist?
Gibt die noch freien Plätze im fifo zurück.


> Weißt du, was ich mit "Schreibpointer vor Lesepointer" und "Lesepointer
> vor Schreibpointer" meine?
Nein. Mein Plan war es erstmal das ganze zum laufen zu bekommen. Die 
fifo_write_bursted Fkt. zu erweitern würde ich dann später machen.

> Und noch ein Tip:
> Du hast  array[5] nicht komplett initialisiert.
> Du hast da was falsch abgeschrieben.

Habe mit verschiedenen Blockgrößen getestet. Im Grunde ists aber egal, 
da ich die Größe in memcpy vorgebe. (Später nat. wichtig)


An der Größe des fifos kanns nicht liegen... oder worauf willst du 
hinaus?
1
fifo_t fifo;
2
fifo_data_t tmp_a[200];
3
fifo_init(&fifo, tmp_a, sizeof(tmp_a) / sizeof (fifo_data_t));

von fifo (Gast)


Lesenswert?

Wenn ich 5 Elemente in den Fifo kopiere, dann die Elemente einzeln 
abrufe erhalte ich wenn das letzte Element noch im fifo ist ein level 
von 5

Wenn ich 4 Elemente in den Fifo kopiere, .... ist das level von 4

OK, linear... das freut mich. verstehe nur nicht, warum das level 
konstant 4 am ende ist. wo werden die ersten 4 Plätze am Anfang belegt?

von Guido (Gast)


Lesenswert?

fifo schrieb:
>> Weißt du, was ich mit "Schreibpointer vor Lesepointer" und "Lesepointer
>> vor Schreibpointer" meine?
> Nein. Mein Plan war es erstmal das ganze zum laufen zu bekommen. Die
> fifo_write_bursted Fkt. zu erweitern würde ich dann später machen.

Entschuldige bitte, aber ab diesem Punkt kann ich dir nicht 
weiterhelfen.

von fifo (Gast)


Lesenswert?

Warum erklärst du es mir nicht einfach?

von fifo (Gast)


Lesenswert?

1
block_size = 7;
2
    wrap_size = fifo_get_write_wrap(&fifo);
3
    if (fifo_get_free(&fifo) >= block_size) { 
4
        if (block_size > wrap_size) {
5
            // split action into two blocks due to pointer wrap around
6
            memset((uint8_t*)fifo.write_p, 'A', wrap_size * sizeof(fifo_data_t));
7
            fifo_write_bursted(&fifo, wrap_size);
8
            block_size -= wrap_size;
9
        }
10
        // no pointer wrap around in block or second half of block operation
11
        memset((uint8_t*)fifo.write_p, 'A', block_size * sizeof(fifo_data_t));
12
        fifo_write_bursted(&fifo, block_size);
13
    }
14
    level = fifo_get_level(&fifo);

Hier wird erst fifo_get_write_wrap() aufgerufen. Dabei wird die 
Differenz zwischen der Größe und dem Schreibtpointer plus eins addiert. 
Ist das fifo 200 groß und der Schreibpointer gerade auf dem Element 198 
erhalte ich also 3.

Nun wird geguckt, ob die freien Plätze im Fifo ausreichend sind um die 
neue Blockgröße dh. X Byte aufzunehmen. Wenn ja, wird geguckt, ob die 
Block Größe  größer ist, als die wrap größe. Ist die Wrap Größe wie oben 
berechnet 3 und die Block Größe 7 springt das Programm in die If um dort 
memset auszuführen. Hierbei wird A also 3x ab der Adresse des 
Schreibpointers geschrieben. Insg. nimmt dies einen Speicher von 3 Byte 
ein. Weiter gehts mit fifo_write_bursted() (sieht unten)

Die restlichen 4 Byte (Dh. 7 - 3 = 4) werden anschließend verarbeitet.
Nun springt man in die Fktion fifo_write_bursted()
Hier wird ein temporärer Pointer angelegt. In diesen Pointer wird die 
Adresse geschrieben an der der Schreibpointer plus die hinzugefügte 
block Größe liegt. Daraufhin wird geguckt, ob der temp Pointer größer 
als der oberste Wert des fifos ist. Ist dem so, wird dem lese Pointer 
der Start des Fifos zugewiesen ansonsten entspricht der lesepointer dem 
tempörären pointer.

Anschließend wird nocheinmal die aktuelle Füllstandsgröße abgerufen. 
Diese sollte 7 sein.


Hierbei wird also nur der Lesepointer verändert, ansonsten versteh ichs 
nicht. Oder meinst du, dass erst der Schreibpointer behandelt wird und 
dann der Lesepointer? Ich wüsste aber keinen Weg es anders zu 
machen...!?

von fifo (Gast)


Lesenswert?

Niemand kann mir weiterhelfen?

von fifo (Gast)


Lesenswert?

Letzter Versuch...

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.