Forum: Mikrocontroller und Digitale Elektronik C -> UART -> Übertragen von Strukturen


von Jan H. (janiiix3)


Lesenswert?

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?

von Stefan F. (Gast)


Lesenswert?

Jan H. schrieb:
> Wie sendet man sinnvoll jetzt solch eine Struktur?

So wie es der Empfänger braucht. Im einfachsten Fall einen Zeuger auf 
das erste Byte, und die Länge angeben.

Was soll das denn werden?

von Jan H. (janiiix3)


Lesenswert?

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

von Bernd K. (prof7bit)


Lesenswert?

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.

von Bernd K. (prof7bit)


Lesenswert?

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.

von Stefan F. (Gast)


Lesenswert?

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.

von Jan H. (janiiix3)


Lesenswert?

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

von brt (Gast)


Lesenswert?

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

von Jan H. (janiiix3)


Lesenswert?

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.

von foobar (Gast)


Lesenswert?

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

von foobar (Gast)


Lesenswert?

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

von x^2 (Gast)


Lesenswert?

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.

von x^2 (Gast)


Lesenswert?

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.

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.