mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik CAN Bus fifo


Autor: fifo (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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 :)

Autor: dunno.. (Gast)
Datum:

Bewertung
1 lesenswert
nicht 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..

Autor: ui (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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...

Autor: Guido (Gast)
Datum:

Bewertung
1 lesenswert
nicht 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!

Autor: Guido (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Horst (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: fifo (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Horst (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Guido (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: fifo (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Guido (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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);

Autor: Guido (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: fifo (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo nochmal,

https://www.mikrocontroller.net/articles/FIFO#FIFO...
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:
block_size = 7;
    wrap_size = fifo_get_write_wrap(&fifo);
    if (fifo_get_free(&fifo) >= block_size) { 
        if (block_size > wrap_size) {
            // split action into two blocks due to pointer wrap around
            memset((uint8_t*)fifo.write_p, 'A', wrap_size * sizeof(fifo_data_t));
            fifo_write_bursted(&fifo, wrap_size);
            block_size -= wrap_size;
        }
        // no pointer wrap around in block or second half of block operation
        memset((uint8_t*)fifo.write_p, 'A', block_size * sizeof(fifo_data_t));
        fifo_write_bursted(&fifo, block_size);
    }

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:
memset   (   void *    dest,
    int    val,
    size_t    len 
  )   


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?

Autor: Guido (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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);

Autor: fifo (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mit memcpy() also so: (Unabhänige von CAN)
uint8_t array[5];
array[0] = 1;
array[1] = 2;
array[2] = 3;
array[3] = 4;

block_size = 4;

memcpy((uint8_t*)fifo.write_p, &array, block_size * sizeof(fifo_data_t));
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?

Autor: fifo (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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...

Autor: Guido (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: fifo (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?
fifo_t fifo;
fifo_data_t tmp_a[200];
fifo_init(&fifo, tmp_a, sizeof(tmp_a) / sizeof (fifo_data_t));

Autor: fifo (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Guido (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: fifo (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Warum erklärst du es mir nicht einfach?

Autor: fifo (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
block_size = 7;
    wrap_size = fifo_get_write_wrap(&fifo);
    if (fifo_get_free(&fifo) >= block_size) { 
        if (block_size > wrap_size) {
            // split action into two blocks due to pointer wrap around
            memset((uint8_t*)fifo.write_p, 'A', wrap_size * sizeof(fifo_data_t));
            fifo_write_bursted(&fifo, wrap_size);
            block_size -= wrap_size;
        }
        // no pointer wrap around in block or second half of block operation
        memset((uint8_t*)fifo.write_p, 'A', block_size * sizeof(fifo_data_t));
        fifo_write_bursted(&fifo, block_size);
    }
    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...!?

Autor: fifo (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Niemand kann mir weiterhelfen?

Autor: fifo (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Letzter Versuch...

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.