C -> UART -> Übertragen von Strukturen

OP #5984248
Nabend Profis ;)

mal eine generelle Frage an euch..
Ich habe z.B folgende Struktur.
1
typedef struct
2
{
3
   uint8_t uiParameter;
4
   uint8_t pKanal[10];
5
   uint32_t uiAddresse;
6
}Filter_t;

einfach mal als Beispiel jetzt..


Und dann gibt es die "0815" uart Funktion die einen Zeiger und die 
Anzahl der Daten entgegennimmt.
1
void uartPuts( uint8_t *pData , uint8_t uiLength )
2
{
3
   //senden..
4
}

Wie sendet man sinnvoll jetzt solch eine Struktur?
Es gibt ja zig. verschiedene Arten..
Was hat sich bis jetzt so bewährt?
OP #5984270
Es soll erstmal nichts spezielles werden.
Einfach die Vorgehensweise, wie man sowas am besten macht.
Ich hätte es jetzt wie folgt gemacht..
1
// verkürzt
2
BytesSenden( Struktur->Member0 , Anzahl );
3
BytesSenden( Struktur->Member1 , Anzahl );
Hätte jetzt jeden Member einzeln bearbeitet..
#5984275
Jan H. schrieb:
> Was hat sich bis jetzt so bewährt?

Es hat sich bewährt daß man sich stets dessen bewußt sein muß daß es 
verschiedene Endianness und verschiedene Alignmentregeln gibt und das 
nicht dem Zufall überlässt sondern aufs Bit genau festnagelt und 
dokumentiert und sicherstellt daß das dann auch so bleibt. Dazu sind 
dann ein paar mehr Maßnahmen zu beachten als einfach nur blind ein 
Struct byteweise auszugeben und auf das beste zu hoffen.
#5984282
Das erste was mir bei Deinem struct schon mal auffällt ist daß die 
Elemente nicht ordentlich an ihren self-alignment-Grenzen ausgerichtet 
sind und daher Padding erzwingt und noch dazu auf unterschiedlichen 
Plattformen möglicherweise unterschiedlich viel davon.
Gast #5984283
Naj H. schrieb:
> wie man sowas am besten macht.

Das kommt auf den konkreten Anwendungsfall an.

Ich tendiere Dazu, Daten in menschen-lesbarer Form zu übermitteln. CSV, 
JSON, XML oder ähnliche Datenformate nutze ich sehr gerne, weil ich sie 
gut mitlesen und manuell simulieren kann.

Binär ist natürlich kompakter. Vielleicht brauchst du Prüfsummen oder 
Handshake.
OP #5984287
Bernd K. schrieb:
> Jan H. schrieb:
>> Was hat sich bis jetzt so bewährt?
>
> Es hat sich bewährt daß man sich stets dessen bewußt sein muß daß es
> verschiedene Endianness und verschiedene Alignmentregeln gibt und das
> nicht dem Zufall überlässt sondern aufs Bit genau festnagelt und
> dokumentiert und sicherstellt daß das dann auch so bleibt. Dazu sind
> dann ein paar mehr Maßnahmen zu beachten als einfach nur blind ein
> Struct byteweise auszugeben und auf das beste zu hoffen.

Wie wäre es denn Sinnvoller?
Ein Beispiel wäre nett ;)
Gast #5984290
Jan H. schrieb:
> Was hat sich bis jetzt so bewährt?

Es ist wichtig die Grenze der einzelnen Datenpakete sicher zu erkennen.
Das geht entweder mit einer definierte Pause die über einen Timeout 
erkannt wird oder über Framing.

Bei mir bewährt hat sich:
https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing

Immer nur 1 Byte Overhead und sichere Erkennung der Paketgrenzen.

Vorschlag:

- Daten binär verpacken (evtl. _attribute_ ((packed)) benutzen)
- Checksumme anhängen
- COBS-encodieren

Wenn das portabel sein soll und auf anderen als vorher geplanten 
Maschinen laufen soll ist noch die
https://en.wikipedia.org/wiki/Endianness
zu beachten.

Grüße, Brt
OP #5984292
Stefanus F. schrieb:
> Naj H. schrieb:
>> wie man sowas am besten macht.
>
> Das kommt auf den konkreten Anwendungsfall an.
>
> Ich tendiere Dazu, Daten in menschen-lesbarer Form zu übermitteln. CSV,
> JSON, XML oder ähnliche Datenformate nutze ich sehr gerne, weil ich sie
> gut mitlesen und manuell simulieren kann.
>
> Binär ist natürlich kompakter. Vielleicht brauchst du Prüfsummen oder
> Handshake.

Ich meinte spezielle wie ich die Bytes aus der Struktur hole bzw. von 
der Adresse und dem UART übergebe.
Gast #5984299
> Wie sendet man sinnvoll jetzt solch eine Struktur?

So, dass der Empfänger es auch versteht und er außerdem noch die 
Möglichkeit hat, Übertragungsfehler zu erkennen und ggf zu reparieren.

Gern benutzt wird ein ASCII-Format, ein Datensatz pro Zeile, vorne eine 
Kennung um was für einen Typ von Daten es sich handelt, dann die Daten, 
dann ein Zeilenende. Beispiel: "Filter 123 1,2,3,4,5,6,7,8,9,10 
23423424\n". Evtl noch ein paar definierte Timeouts und ne Prüfsumme, 
dann man hat was Solides.

Einfach die Daten binär roh rauszuhauen erscheint erstmal einfach, ist 
aber sehr Fehleranfällig (z.B. geht nur ein Byte verloren oder durch 
eine Störung ein Byte zuviel, kommen beide Seiten nie wieder in Sync) 
und es ist kaum noch erweiterbar.
Gast #5984311
> Ich meinte spezielle wie ich die Bytes aus der Struktur hole bzw. von
> der Adresse und dem UART übergebe.

Beispiel:
1
static void
2
send_filter(Filter_t *f)
3
{
4
    char buffer[128];
5
    int len;
6

7
    len = sprintf(buffer, "Filter %u %u,%u,%u,%u,%u,%u,%u,%u,%u,%u %lu\n",
8
          f->uiParameter,
9
          f->pKanal[0], ..., f->pKanal[9],
10
          f->uiAddresse);
11
    uartPuts(buffer, len);
12
}

Empfänger wird natürlich komplexer, da er sich um die Fehlerbehandlung 
kümmern muß.
Gast #5984327
Naj H. schrieb:
> Wie sendet man sinnvoll jetzt solch eine Struktur?

Aufgrund von Padding nicht sinnvoll auf die dargestellte Weise.

Man kann offsetof() und sizeof() auf die Elemente ansetzen. Beschreibt 
man dann jedes Element als sizeof()/offsetof() Pärchen z.B. in einem 
Array, kann man sich eine generische Funktion bauen, die Strukturen 
"verschicken" kann.
1
typedef struct
2
{
3
   uint8_t offset;
4
   uint8_t bytes;
5
} ITEM;
6

7
typedef struct
8
{
9
   uint8_t count;
10
   const ITEM* items;
11
} MSG;
12

13
enum MSG_TYPES
14
{
15
   MSG_TYPE_1 = 0,
16
   //...
17
   MSG_TYPE_EOL
18
};
19

20
static const ITEM msg_type_1[] =
21
{
22
   {offsetof(Filter_t::uiParameter), sizeof(Filter_t::uiParameter)},
23
   //...
24
};
25

26
static const MSG messages[] =
27
{
28
   { sizeof(msg_type_1)/sizeof(msg_type_1[0]), msg_type_1 },
29
   //...
30
};

Dann kann man eine generische Funktion schreiben, die jeden 
Nachrichtentype versenden kann.
1
void send(enum MSG_TYPES type, const void* data)
2
{
3
   int i, k;
4

5
   if (type < MSG_TYPE_EOL)
6
   {
7
      const uint8_t* ptr = (const uint8_t*)data;
8
      const MSG msg = messages[type];
9

10
      for (i = 0; i < msg.count; i++)
11
      {
12
          for (k = 0; i < msg.items[i].bytes; ++k)
13
              tx_byte(ptr[msg.items[i].offset + k]);
14
      }
15
   }
16
}

Alles nicht compiliert/getestet. Soll nur das Prinzip demonstrieren.
Gast #5984342
Dummerweise gibt's auch noch die Endianness, die in diesem Beispiel 
ignoriert wird. Für eine richtige Lösung, sollte man diese natürlich 
berücksichtigen.

Antwort schreiben

Eine eigene Frage? Dieser Thread ist über 7 Jahre alt. Stell sie in einem eigenen Thread – dort lesen mehr Leute mit, und sie wird öfter und schneller beantwortet.

Neuen Thread starten

Eine Lösung, Ergänzung oder Rückmeldung zu diesem Thread? Dann antworte hier.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.