Morgen,
ich habe hier zum wiederholten Male das selbe Problem.
Mir fällt da keine elegante Lösung ein, aber vielleicht wisst ihr ja
was.
Ich habe eine Sendefunktion die einen Zeiger auf Daten und eine
Längenangabe erwartet.
void senden(uint8_t *src, uint8_t length);
Ob das jetzt UART, Ethernet, SPI oder sonstwas ist spielt erstmal keine
Rolle.
Eine Applikation möchte Nutzdaten senden.
Allerdings gehören diese noch in ein Protokoll verpackt.
Je nach Medium meinetwegen eine Längenangabe, SOF oder Empfängeradresse.
D.h. ich muss die Nutzdaten die ich bekomme um einen Header (und
manchmal auch Trailer) erweitern.
Das heißt aber für mich als "Protokollschicht" dass ich erstmal alle
Daten, die mir die Applikation gibt in einen temporären Buffer kopieren
muss.
Der Buffer ist um den Protokolloverhead größer zu wählen als die
Nutzdaten.
Auf einem uC läuft das wohl auf einen globalen Puffer der Form "uint8_t
tmpBuf[MAX_FRAME_SIZE]" hinaus.
Also kommt zur Kopierarbeit noch ein permanenter RAM-Verbrauch hinzu.
Die Ressourcen (RAM + Rechenzeit) hab ich zwar noch locker über. Aber
mir gehts darum, diese Problemstellung in Zukunft geschickter zu lösen
als bisher immer.
Wie würdet ihr das lösen?
Viele Grüße,
Karlo
Das würde eigentlich nur bei UART funktionieren.
Bei allen Medien, die Paketorientiert arbeiten (oder vielleicht selbst
ein Protokoll implementieren) wie I²C, Ethernet, usw. wird das so
nichts.
Karlo schrieb:> Der Buffer ist um den Protokolloverhead größer zu wählen als die> Nutzdaten.
Nö, der Puffer enthält nur den Header.
Die Daten sendest Du direkt aus deren Quelle.
Und falls gewünscht wird dabei auch gleich die CRC byteweise berechnet
und dann im Footer angehängt.
Karlo schrieb:> Bei allen Medien, die Paketorientiert arbeiten (oder vielleicht selbst> ein Protokoll implementieren) wie I²C, Ethernet, usw. wird das so> nichts.
klar geht das auch bei Ethernet. Der STack packet die Packet eh nach
seinen eingenen Algorithmus
Peter Dannegger schrieb:> Nö, der Puffer enthält nur den Header.> Die Daten sendest Du direkt aus deren Quelle.
Wie soll das gehen?
Hier Beitrag "Re: Header vor die Nutzdaten schreiben" hab ich doch
schon geschrieben, warum ich die Sendefunktion nur einmal aufrufen darf.
Konkret bei I²C würden sonst zwei Botschaften generiert.
Was wenn der Slave erwartet, dass das erste Byte ein Kommando ist?
Bei zwei seperaten Übertragungen würde das erste Byte der Nutzdaten als
Kommando interpretiert.
Oder hab ich dich falsch verstanden und du löst das mit nur einem
Aufruf?
Dann muss ich aber wieder vorher puffern.
> klar geht das auch bei Ethernet. Der STack packet die Packet eh nach> seinen eingenen Algorithmus
Was ist denn "der Stack"?
Vor einiger Zeit hatte ich in einem Projekt mit lwIP und UDP zu tun.
Die udp_send() haut da auch einfach nur die Daten raus die sie von mir
bekommt.
Man könnte zu der Sendefunktion noch eine weitere schreiben, die einen
dazu passenden Puffer bereitstellt, intern jedoch (für den Header) einen
etwas größeren beschafft, wie auch immer. Wenn mehrere solche Puffer im
Umlauf sind, dann wird halt für jeden ein Handle vergeben (das intern
letztlich auch einfach ein Zeiger auf den ganzen Block sein kann).
Der Nutzer sieht dann nur das Handle und komm darüber an die Nutzdaten,
ohne den Header davor sehen zu müssen.
Beispiel:
1
#include<stdlib.h>
2
#include<stddef.h>
3
#include<stdio.h>
4
#include<string.h>
5
6
// gesamtes Paket: Header + Nutzdaten
7
typedefstruct
8
{
9
// Header:
10
size_tlenPayload;
11
// ... und evtl. weitere Headerdaten ...
12
13
// Nutzdaten:
14
charpayload[0];
15
}myProtocolPacket_t;
16
17
// als Handle für ein solches Paket einfach den Zeiger darauf nehmen:
18
typedefmyProtocolPacket_t*myProtocolHandle_t;
19
20
// beschafft einen Puffer für (len) Byte Nutzdaten:
Der Anwender hat nur noch mit dem Datentyp des Handle zu tun, und mit
dem Feld payload in der struct.
Der Header geht ihn nichts an.
Trotzdem muß nie etwas umkopiert werden, weil der Header von Anfang
existiert.
(In C++ würde es natürlich mit private etc. schöner gehen...)
Karlo schrieb:> Peter Dannegger schrieb:>> Nö, der Puffer enthält nur den Header.>> Die Daten sendest Du direkt aus deren Quelle.>> Wie soll das gehen?> Hier Beitrag "Re: Header vor die Nutzdaten schreiben" hab ich doch> schon geschrieben, warum ich die Sendefunktion nur einmal aufrufen darf.
Dann musst du eben deine Sendefunktion etwas überarbeiten.
> Konkret bei I²C würden sonst zwei Botschaften generiert.> Was wenn der Slave erwartet, dass das erste Byte ein Kommando ist?
Dann schick es ihm!
> Bei zwei seperaten Übertragungen würde das erste Byte der Nutzdaten als> Kommando interpretiert.
Moment!
Kein Mensch sagt, dass ein Aufruf deiner Sendefunktion eine 'unteilbare'
Einheit ist. Du musst dann eben das Versenden einer Nachricht selber
wieder in ein paar Einzelteile auflösen
So nach dem Muster
1
voidsendMsg(constchar*Msg)
2
{
3
startMsg();// mach alles was an Vorgeplänkel auf den Leitungen
4
// notwendig ist
5
sendBytes(Header);// dann die Bytes hinten nach, die den Header
6
// ausmachen
7
sendBytes(Msg);// die Nutzdaten
8
9
stopMsg();// und das wars.
10
}
Deine Funktion sendBytes darf eben NICHT eine Message verschicken (oder
was uach immer dein Paket ausmacht) sondern tatsächlich nur das tun, was
der Funktionsname ausmacht: die übergebenen Bytes auf den Weg bringen.
Das ganze möglicherweise notwendige Vorgeplänkel mit Verbindungsaufbau
etc. wird NICHT von dieser Funktion erledigt.
Du übergibst halt deiner Treiberstufe deine 'Nachricht' nicht als
unteilbaren Datenklumpen, in dem schon alles fix fertig aufbereitet sein
muss, sondern in kleineren Dosen, die dann eben vom Sender zu keinem
einzigen Zeitpunk zu einem kompletten Datenklumpen zusammengesetzt
werden, sondern in diesen Einheiten abgesetzt werden. (Oder eben wie im
Fall von Ethernet vom TCP/IP Stack sowieso erst mal gesammelt und
aufbereitet werden. Aber das heißt ja nicht, dass die höherliegenden
SW-Schichten das auch schon vorab machen müssen. Auch einen TCP/IP Stack
darf man mit "Einzelteilen" füttern. Der Empfänger sieht ja sowieso
nicht, ob ich das vorab erst mal alles zusammengesetzt habe oder nicht.
Karlo schrieb:> Konkret bei I²C würden sonst zwei Botschaften generiert.
Man muß natürlich den I2C-Handler entsprechend schreiben. Er kriegt
einen Pointer auf den Header übergeben und an dessem Ende steht ein
Pointer auf die Daten. Man braucht ja eh eine Statemaschine und die hat
dann einen State mehr (sende Daten).