Forum: Mikrocontroller und Digitale Elektronik Einfaches Nachrichtenprotokoll


von Peter K. (Gast)


Lesenswert?

Hallo alle zusammen,

ich hab mir mal die mühe gemacht und schnell mal ein einfaches 
Nachrichtenprotokoll in C geschrieben, sinn und zweck ist es 
Resourcensparend Daten in eine Nachricht zu packen und am anderen ende 
wieder einfach lesen zu können.

Ich weis es gibt bereits viele Nachrichtenprotokolle UDP TCP usw. aber 
ich hab keines gefunden dass einfach zu bedienen ist, Daten in 
Nachrichten verpackt und am Ende die Korrektheit der Daten über eine 
Prüfsumme bestimmt.

Das Programm ist aufgrund eines Projekts enstanden indem ich zwischen 
einem ATMEGA8 und einem STM32F103 Steuerungsnachrichten übertragen 
musste, da diese Nachrichten aus mehreren Bytes bestanden und es mir 
nicht möglich war das alles in einer einzigen I2C übertragung zu 
übertragen, hab ich das Protokoll geschrieben und auch erfolgreich 
verwendet.

Ich habe das Protokoll bisher auf allen µC und CPUs kompiliert ohne 
änderung und probleme.

Falls jemand von euch vor einem ähnlichen Problem steht könnt ihr den 
Code von meine Github Account runterladen und verwenden, modifizieren 
oder was auch immer ihr damit machen wollt.

https://github.com/Freshdouble/SMP/

von Prüfbit (Gast)


Lesenswert?

Peter Kremsner schrieb:
> da diese Nachrichten aus mehreren Bytes bestanden und es mir
> nicht möglich war das alles in einer einzigen I2C übertragung zu
> übertragen

Wie lang ist denn bei dir ein Byte, wenn du es nicht schaffst mehrere 
davon in eine I2C Übertragung zu packen?

von Andreas S. (Firma: Schweigstill IT) (schweigstill) Benutzerseite


Lesenswert?

Mir fällt bei den Quellen auf:
- inkonsistente Schreibweisen des API
- Rechtschreibfehler
- label/goto statt while-Schleife

von Peter K. (Gast)


Lesenswert?

Prüfbit schrieb:
> Wie lang ist denn bei dir ein Byte, wenn du es nicht schaffst mehrere
> davon in eine I2C Übertragung zu packen?

8Bit
Das Problem war dass die I2C Schnittstelle automatisch (vermutlich war 
sie einfach nur falsch konfiguriert) nach jedem Byte das Stop Signal 
gesendet hat und kein repetet start. Somit war die I2C Übertragung immer 
nur ein Byte lang war, dann kam das nächste Startsignal die Adresse und 
das nächste Byte.
Das Problem war auch dass die Übertragung in einer verrauschten umgebung 
statgefunden hat, viele Motoren die mit PWM gesteuert wurden. Somit 
waren die Daten nicht immer richtig die beim Slave ankamen damit die 
Daten jetzt in Richtiger reihenfolge, die Pakte komplett sind und vor 
allem die Daten richtig waren haben wir dann das Programm geschrieben.

Nebenbei haben wir später das Protokoll auch für UART-Schnittstellen 
angewandt und die können nur ein Byte pro Übertragung durchführen.

von Peter K. (Gast)


Lesenswert?

Andreas Schweigstill schrieb:
> inkonsistente Schreibweisen des API
> - Rechtschreibfehler

ist mir bekannt und werde ich bald ausbessern, das programm war bis 
jetzt auch nur in einem privaten Projekt

Andreas Schweigstill schrieb:
> - label/goto statt while-Schleife

Kann man eventuel noch verbessern, aber bringt die verwendung einer 
while schleife performancevorteile?

von Moritz A. (moritz_a)


Lesenswert?

Peter Kremsner schrieb:
> Kann man eventuel noch verbessern, aber bringt die verwendung einer
> while schleife performancevorteile?

Ja, sie erhöht die Debuggingperformance massiv ;)

von spess53 (Gast)


Lesenswert?

Hi

>Das Problem war dass die I2C Schnittstelle automatisch (vermutlich war
>sie einfach nur falsch konfiguriert) nach jedem Byte das Stop Signal
>gesendet hat und kein repetet start.

So falsch kannst du die TWI-Schnittstelle gar nicht konfigurieren.

MfG Spess

von Rudolph (Gast)


Lesenswert?

Peter Kremsner schrieb:
> aber ich hab keines gefunden dass einfach zu bedienen ist, Daten in
> Nachrichten verpackt und am Ende die Korrektheit der Daten über eine
> Prüfsumme bestimmt.

Zum Beispiel LIN.

von Karl H. (kbuchegg)


Lesenswert?

?
Hilf mir mal. Irgendwo scheine ich da einen Denkfehler zu haben oder du 
hast da einen gewaltigen Bug in der Software

Du unterscheidest zwischen 2 Buffer. So weit so gut. Der in ist der 
SMP_OutBuf und der andere der SMP_InBuf.

Wie ist deren Strukturdefinition? Hier sind die beiden
1
typedef struct __SMP_BUF_T {
2
  unsigned char data[SMP_BUF_SIZE];
3
  unsigned int wrIdx;
4
  unsigned int rdIdx;
5
} SMP_BUF_T;
6
7
//SMP input buffer
8
typedef struct __SMP_TBUF_T{
9
  unsigned char data[BUFFER_SIZE];
10
  unsigned int wrIdx;
11
  unsigned int rdIdx;
12
} SMP_TBUF_T;
13
14
static volatile SMP_BUF_T  SMP_OutBuf;
15
static volatile SMP_TBUF_T SMP_InBuf;

Man beachte, und das ist im weiteren wichtig, dass die beiden Arrays 
nicht potentiell gleich gross sind. Der eine hat eine Arraygrößsse von 
SMP_BUF_SIZE, der andere eine Arraygröße von BUFFER_SIZE. Wie sind da 
die zugehörigen Defitionen.
1
#define BUFFER_SIZE MAX_PAYLOAD * 2 + 4
2
3
...
4
#define MAX_PAYLOAD 64
5
#define SMP_BUF_SIZE 64

So wie das definiert ist, ist BUFFER_SIZE immer um einiges größer als 
SMP_BUF_SIZE. Worauf es mir ankommt: wenn man nicht sorgfältig die 
Definitionen von MAX_PAYLOAD und SMP_BUF_SIZE  aufeinander abstimmt, 
dann ergeben sich da verschiedene Werte.

So weit, so gut.
Jetzt kommts.
1
int smp_send(byte *buffer,int length)
2
{
3
    byte message[BUFFER_SIZE];
4
    int i, offset = 0;
5
    int d;
6
    unsigned short crc = 0;
7
8
    if(length > MAX_PAYLOAD)
9
        return 0;
10
11
    if((length + 4) > BUF_FREE_SPACE(SMP_InBuf))
12
13
....

und
1
#define BUF_COUNT(cdcBuf)      (SMP_BUF_MASK & (cdcBuf.wrIdx - cdcBuf.rdIdx))
2
....
3
#define BUF_FREE_SPACE(cdcBuf)     (SMP_BUF_SIZE - BUF_COUNT(cdcBuf))
Hä?
Du kannst doch nicht die Makros, die du dir zur Verwaltung des 
Ringbuffers im Output Buffer gemacht hast so mir nichts, dir nichts auf 
den Input Buffer anwenden. Wie wir gerade gesehen haben, sind die Arrays 
in den beiden Buffern unterschiedlich gross. D.h. aber auch, das alle 
Berechnungen, die in den Makros letzten Endes auf SMP_BUF_SIZE beruhen 
nicht auf den Input Buffer anwendbar sind. Dessen Array Grösse ist 
BUFFER_SIZE und nicht SMP_BUF_SIZE.

: Bearbeitet durch User
von Peter K. (Gast)


Lesenswert?

spess53 schrieb:
>
> So falsch kannst du die TWI-Schnittstelle gar nicht konfigurieren.
>

Ging nicht um die twi sondern die I2C am stm32

Rudolph schrieb:
> Zum Beispiel LIN.

Danke werd ich mir mal ansehen, wussten wir damals leider noch nicht

von Bitflüsterer (Gast)


Lesenswert?

"Timo danaos et dona ferrentes." :-)

OK. Ich baue hier im Forum manchmal auch ganz schönen Mist.

Aber machen wir doch einen Review-Thread daraus.

von Bitflüsterer (Gast)


Lesenswert?

Ups. "Timeo" ... Ts ts ts. ;-)

von Peter K. (Gast)


Lesenswert?

Danke karl heinz dass ist offensichtlich ein bug.

Hat aber noch nie zu fehlern geführt, der eine buffer wird nur nicht 
voll verwendet.

Aber wenn es da ohnehin schon ein fertiges protokoll mit LIN gibt ist 
das hier doch ohnehin obsolet.

von Karl H. (kbuchegg)


Lesenswert?

Peter Kremsner schrieb:
> Danke karl heinz dass ist offensichtlich ein bug.
>
> Hat aber noch nie zu fehlern geführt, der eine buffer wird nur nicht
> voll verwendet.

Dann überleg dir mal, was wohl passiert, wenn ich im festen Vertrauen 
auf die Konfigurierbarkeit den MAX_PAYLOAD auf, sagen wir mal, 10 setze.

-> und kawumm.
Array Zugriff out of bounds.


Wenn mir wer ein Ringbuffersystem unterjubeln will, und in der 
Strukturdefinition des Ringbuffers kommt die Längenangabe des Arrays 
nicht als Strukturmember vor, dann werd ich hellhörig und seh mir die 
Zugriffsfunktionen bzw. die Strukturdefinitionen etwas genauer an. 
Entweder alle Arrays sind dann gleich gross oder in den Zugriffsmakros 
wird mit sizeof gearbeitet. Ist beides nicht der Fall, haben wir schon 
eine 99% Chance auf einen kapitalen Fehler.

: Bearbeitet durch User
von Peter K. (Gast)


Lesenswert?

Ich werde es bei gelegenheit noch anpassen

Das programm wurde unter zeitdruck geschrieben und ist natürlich weit 
entfernt von perfekt,ich wollte es hier nur mal teilen falls es jemand 
verwenden möchte natürlich ohne garantie
Ich kann nur sagen wir haben es verwendet und es hat super funktioniert

von Wolfgang A. (Gast)


Lesenswert?

Peter Kremsner schrieb:
> Ging nicht um die twi sondern die I2C am stm32

Und was ist bitte der Unterschied zwischen TWI und I2C, außer das Atmel 
aus bis 2006 relevanten lizenzrechtlichen Gründen die Bezeichnung TWI 
eingeführt hat?

von spess53 (Gast)


Lesenswert?

Hi

>Und was ist bitte der Unterschied zwischen TWI und I2C, außer das Atmel
>aus bis 2006 relevanten lizenzrechtlichen Gründen die Bezeichnung TWI
>eingeführt hat?

Es geht ja um die hardwaretechnische Umsetzung. Das Datenblatt des STMs 
zeigt aber, das der sehr ähnlich gestrickt ist. In der Initialisierung 
gibt es auch keine Einstellung die das o.g. Verhalten hervorrufen kann.

MfG Spess

von Peter K. (Gast)


Lesenswert?

spess53 schrieb:
> Das Datenblatt des STMs
> zeigt aber, das der sehr ähnlich gestrickt ist. In der Initialisierung
> gibt es auch keine Einstellung die das o.g. Verhalten hervorrufen kann.

Wir haben uns nicht mit der Hadware direkt beschäft und haben eine 
CooCox Library verwendet, ich dachte nur dass es von der Hardware her 
kommt, kann natürlich auch an der CooCox Library liegen, ich kann 
jedenfalls nur sicher sagen dass es so direkt nicht funktioniert hat und 
wir desshalb dieses Programm verwendet haben

Hab jetzt die GOTO Anweisung entfernt und den bug den karl heinz 
angesprochen hat beseitigt (zumindest denke ich das ^^), hab es aber 
noch nicht getestet

von spess53 (Gast)


Lesenswert?

Hi

>Wir haben uns nicht mit der Hadware direkt beschäft und haben eine
>CooCox Library verwendet, ich dachte nur dass es von der Hardware her
>kommt, ...

Du stocherst bei der Hardware und bei der Software im Dunkeln. Meinst du 
nicht, das es mal langsam Zeit wäre sich mal mit den Grundlagen zu 
beschäftigen?

MfG Spess

von Peter D. (peda)


Lesenswert?

Peter Kremsner schrieb:
> da diese Nachrichten aus mehreren Bytes bestanden und es mir
> nicht möglich war das alles in einer einzigen I2C übertragung zu
> übertragen

Ich kann mich dunkel erinnern.
Bist Du es nicht gewesen, der sich standhaft geweigert hat, mit einem 
I2C-EEPROM im Page Mode zu überprüfen, ob das Problem schon am Master 
oder erst am Slave liegt?

Ein Würg-Around ist niemals eine Lösung und erst recht nichts, was man 
guten Gewissens anderen empfehlen kann.

Gehe systematisch vor, finde den Fehler und mache es richtig.

von Peter K. (Gast)


Lesenswert?

spess53 schrieb:
> Du stocherst bei der Hardware und bei der Software im Dunkeln. Meinst du
> nicht, das es mal langsam Zeit wäre sich mal mit den Grundlagen zu
> beschäftigen?

Ich beschäftige mich seit 4 Jahren mit Mikrocontroler Programmierung 
also ich kenne die Grundlagen. Es war aber das erste mal dass ich einen 
Cortex-M3 Mikrocontroler vor mir hatte und wir mussten innerhalb von 
einer Woche ein fertiges und zumindest ausreichend lauffähiges Programm 
haben, aus diesem Grund haben wir uns nicht auf die Hardware an sich 
konzentriert sondern haben die CooCox Bibliothek für das I2C Interface 
genommen und darauf vertraut dass die Funktioniert, jedenfalls hat es 
aber nicht wirklich funktioniert, also haben wir uns die Übertragung am 
I2C-Bus mal auf einem Oszi angesehen und erkannt dass da immer ein Stop 
Signal gesendet wird.

Wenn wir mehr zeit gehabt hätten dann hätten wir uns mit der Hardware 
natürlich mehr beschäftigt und mal nachgesehen ob da ein Fehler in der 
Library ist oder eben alles selbst Programmiert.

Am Ende war das Programm nicht mal annähernd Resourcenschonend oder 
optimal, denn die CooCox Library hatte einen Buffer für die I2C 
Übertragung und danach haben wir ja noch den Buffer in dem SMP File, 
aber für eine saubere Implementierung war eben nicht genug Zeit

von Peter K. (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Ich kann mich dunkel erinnern.
> Bist Du es nicht gewesen, der sich standhaft geweigert hat, mit einem
> I2C-EEPROM im Page Mode zu überprüfen, ob das Problem schon am Master
> oder erst am Slave liegt?

Hab bis jetzt noch keinen I2C-EEPROM angesteuert also war ich das sicher 
nicht ^^

von Peter D. (peda)


Lesenswert?

Peter Kremsner schrieb:
> Hab bis jetzt noch keinen I2C-EEPROM angesteuert

Eben.
Denn dann hätte man ja rausgekriegt, obs am Master liegt oder am Slave.

von Peter K. (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Eben.
> Denn dann hätte man ja rausgekriegt, obs am Master liegt oder am Slave.

Das Problem war am Master, wir haben die Übertragungen am Oszi geprüft, 
das Stopsignal wird eben vom Master gesendet und nicht vom Slave, zudem 
hatten wir keinen EEPROM zur Verfügung.

Außerdem konnten wir uns sicher sein dass der Slave funktioniert, das 
war nähmlich ein atmega der eine LED-Matrix angesteuert hat und der 
hatte eine I2C Schnittstelle zur Verfügung und wurde davor schon mal 
verwendet.

Ich hab hier im Forum auch noch nichts bezüglich I2C geschrieben glaub 
ich ^^

von Peter K. (Gast)


Lesenswert?

Sorry für den Repost,

ist ja auch ziemlich egal was wir damals hätten machen können, feststeht 
wir habens nicht gemacht.

Das Projekt war es einen einfachen Linear Positionierer zu steuern, wir 
hatten dazu dc motoren für die x und y richtung und sensoren die durch 
eine Analoge Spannung die Koordinaten in x und y Richtung bestimmen.
Als Controler haben wir den STM32 bekommen.
Der Positionierer hat bereits funktioniert und dann hat der Professor 
gemeint wir sollen noch die LED-Matrix anhängen, als kleinen zusatz, die 
soll nur Grobe Informationen über den Positionierer anzeigen, also ein 
Warndreieck wenn er sich bewegt ein Rufzeichen wenn er an der 
Enbegrenzung ist usw.

Die Led Matrix wurde bereits von einer anderen Gruppe mit einem anderen 
Atmega8 angesteuert und hatte solche Zeichen schon vorprogrammiert 
drinnen.
Die erwartete 0xFF als startzeichen, dann die Nummer des Zeichens und 
die Position wo er es anzeigen soll.

Wir haben also das angeschlossen und versucht, hat aber nicht 
funktioniert.
Nachdem wir den Sourcecode des LED-Matrix Moduls hatten, haben wir das 
SMP an der LED Matrix und am STM32 implementiert und dann hats geklappt.

Aber eigentlich sollte der Thread doch um das Programm gehen falls es 
jemand nutzen möchte und nicht warum wir es damals so gemacht haben und 
nicht anders.

von mar IO (Gast)


Lesenswert?

Hab deinen Code nur überflogen, aber was mir aufgefallen ist:

- auf manche Typedefs kannst Du verzichten und stdint.h einbinden

- bin mir nicht sicher, aber die Makros sind sollten eigentlich 
überflüssig sein. Wenn die nur für die Buffers sind, dann schreibe halt 
ein eigenes Modul dafür. Es gibt auch inline...

- Wieso zwei gleiche Buffers beschreiben mit unterschiedlichen Namen.

- die zweite Zeile ist doch Unsinn, oder nicht?
1
bytesToRead = length;
2
bytesToRead = (bytesToRead < (length)) ? bytesToRead : (length);
3
//          = (length      <  length ) ? length      :  length ;

- Bei deiner Beschreibung: Hast Du eigentlich etwas gegen einen Punkt 
bei Satzende.

- ...

von Peter K. (Gast)


Lesenswert?

mar IO schrieb:
> - die zweite Zeile ist doch Unsinn, oder nicht?

Jup isses.

ich hab mir den code selbst nochmal angesehen, ist ohnehin unsinn, ich 
werds einfach verwerfen

Thread kann geschlossen werden

von Peter D. (peda)


Lesenswert?

Peter Kremsner schrieb:
> Ich hab hier im Forum auch noch nichts bezüglich I2C geschrieben glaub
> ich ^^

Dann hat vielleicht der andere auch die selbe I2C-Lib benutzt, wie Du.
Das Problem kam mir jedenfalls bekannt vor.

Ich glaube aber nicht wirklich, daß ein so großer Patzer in einer Lib 
unbemerkt bleibt.
Ich würde nochmal die Doku gründlich lesen, ob es andere Funktionen für 
Blockmode gibt.

In C++ gibt es aber auch die Unsitte, Funktionen zu überlagern. D.h. 
eine Funktion mit gleichem Namen macht was völlig verschiedenes in 
Abhängigkeit von den Aufrufargumenten. Ich finde nicht, daß Überlagern 
die Verständlichkeit von Code erhöht. Ich mag lieber die Unterscheidung 
durch aussagekräftige Namen.

von Peter K. (Gast)


Lesenswert?

Die libs waren in c programmiert war vermutlich wirklich die 
konfiguration. Aber das ist schon ein halbes jahr her und das projekt 
sowieso abgeschlossn, hab auch keinen zugriff mehr darauf.
Bezüglich der überlagerung kann ich nur sagen dass ich die gut finde, 
eine funktion zu schreiben die sich nach den eingabeparametern richtet 
ist schon positiv natürlich sollte sie aber im endeffekt das selbe 
machen

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.