Hallo zusammen!
Dumme Frage zu folgendem Fall:
Ich habe ein Gerät das jetzt Modbus lernen soll. D.h. alle Parameter und
daten sollen per Modbus ausgelesen und beschrieben werden. Die
eigentliche Modbus Kommunikation läuft schon.
Aktuell habe ich z.B. solche Variablen
1
typedefstruct
2
{
3
uint8_tbatt_nr;// ausgewählter Batterie Typ
4
uint8_tbatt_cells;// Batterie Zellen
5
uint16_tcapacity;// in A/h
6
int16_ttemp;
7
}selected_batt_t;
8
9
typedefstruct{
10
ext_output_mode_text_output_mode;
11
uint8_tv_high;
12
uint8_tv_low;
13
uint8_tinverted;
14
uint8_thold_time;
15
}ext_output_config_t;
16
17
uint16_tact_pwm;
18
floatvoltage_batterie;
19
floatcurrent_batterie;
20
selected_batt_tselected_batterie;
21
ext_output_config_toutput_config[4];
Damit die Variablen jetzt immer an eine feste Adresse liegen, und ich
diese einfach per Modbus ausgeben kann, habe ich angefangen alles in ein
großes Array zu machen:
1
#define ADR_ACT_PWM 0
2
#define ADR_VOLTAGE_BATTERIE_H 1
3
#define ADR_VOLTAGE_BATTERIE_L 2
4
#define ADR_CURRENT_BATTERIE_H 3
5
#define ADR_CURRENT_BATTERIE_L 4
6
7
uint16_tsystem_data[5];
Jetzt kann ich einfach die Werte übertragen wenn die Adressen vom Modbus
Master abgefragt werden.
Nur was mache ich mit den Strukturen? z.B. die output config Struktur,
in der die Konfiguration der Ausgänge gespeichert ist. Davon habe ich
vier Stück mit jeweils 5 Parametern. Ich könnte das jetzt aufdrösseln,
ins große Array packen und die 20 Adressen mit #define definieren. Aber
ich denke das wird unübersichtlich. Außerdem habe ich im programm sowas
stehen:
[c]
for (x=0;x <= 3; x++)
{
switch (ext_output_config[x].ext_output_mode)
{
case EXT_OUTPUT_MODE_DISABLED:
break;
case EXT_OUTPUT_MODE_CHARGER_STATE:
if (charger_mode==charger_mode_float_charging)
{
switch_output_on(x);
}
else
{
switch_output_off(x);
}
break;
}
}
Das würde dann ja gar nicht mehr gehen, wenn ich keine Struktur für die
Konfiguration eines Ausgangs habe....
Jetzt steh ich irgendwie auf den Schlauch, wie man das am cleversten
machen kann... irgendwelche Vorschläge?
Gruß
Falko
Falko J. schrieb:> Jetzt steh ich irgendwie auf den Schlauch, wie man das am cleversten> machen kann... irgendwelche Vorschläge?
Ja. Wie du an der Vielzahl der eingegangenen Antworten erkennst, wäre es
vielleicht sinnvoll, die Frage(n) so zu stellen, daß irgend jemand das
Problem überhaupt versteht (dich eingeschlossen ;)
Oliver
:-)
okay ich versuchs mal
Also um alle Daten des Gerätes einfach via Modbus zu übertragen habe ich
ein uint16_t Array angelegt. Jetzt kann ich einfach die angefragte
Adressen zurücksenden, indem ich die angefragte Adresse als index für
das Array verwende.
Die verwendeten Strukturen kann ich aber nicht einfach in das Array
verschieben, da diese ja keine uint16_t ist....
ich hoffe das hat die Sache klarer gemacht...
Gruß
Falko
Falko J. schrieb:> Also um alle Daten des Gerätes einfach via Modbus zu übertragen habe ich> ein uint16_t Array angelegt. Jetzt kann ich einfach die angefragte> Adressen zurücksenden, indem ich die angefragte Adresse als index für> das Array verwende.
Damit löst du ein Problem, was du überhaupt nicht hast.
Was spricht gegen ein Array direkt mit den Strukturdaten? Oder ein Array
mir Pointer auf die Daten, oder eine (linked)Liste, oder, oder...
Die Zuordnung der Modbusadresse zur jeweilige Datenstruktur ist doch
nebensächlich und trivial.
Oliver
Oliver schrieb:> Die Zuordnung der Modbusadresse zur jeweilige Datenstruktur ist doch>> nebensächlich und trivial.
Nicht wenn man auf dem Schlauch steht :-)
> Was spricht gegen ein Array direkt mit den Strukturdaten?
Wie meinst du das...mach mal ein Stück c-code bitte
>Oder ein Array> mir Pointer auf die Daten,
Das würde aber zusätzlich Ram Speicher verbrauchen. oder? Davon ist
nicht mehr viel da...
Gruß
Falko
Du hast eine interne Datenstruktur, die du nach aussen auf
Modbusadressen abbilden willst. Die internen Daten müssen aber doch
nicht so abgelegt werden, daß sie der Mosbusstruktur entsprechen. Die
"Übersetzung" kann ja eine Funktion übernehmen.
Aber um das konkreter zu lösen, braucht es konkreteren Input. Schreib
doch mal ein Beispiel auf, wie die internen Datenstrukturen auf die
Modbusadressierung abgebildet werden sollen.
Oliver
einfach der reihe Modbus adressen bekommen.
act_pwm Adresse 0
voltage_batterie Adresse 1 und 2
current_batterie Adresse 3 und 4
charger_mode Adresse 5
output_mode Adresse 6
output_config dann Adresse 7-11 für die erste Struktur 12 - 17 für die
zweiten usw.
im obigen Beispiel währe es vielleicht noch möglich charger_mode und
output_mode auf eine Adresse zu legen (Highbyte und Lowbyte) für zwei
Byte sparen... aber das währe dann wohl nicht modbus konform....
Die Schreib und lese Funktion sollen auf den gleichen Speicherbereich
sein.
Gruß
Falko
Falko J. schrieb:> einfach der reihe Modbus adressen bekommen.
Wenn ich wikipedia richtig verstehe, kennt Modbus Adressen und
Funktionen. Es scheint naheliegend, jedem Modul eine (1) Adresse und
jedem Konfig-Parameter einen Funktionscode zuzuweisen. Wahrend der
Funktionscode nur 8 bit (RTU Modus) bzw. 2 Dezimalstellen (ASCII Modus)
umfasst, kann das Datum zu jedem Funktionscode beliebig lang sein.
Dein Bus läuft im RTU Mode?
Falko J. schrieb:> Nur was mache ich mit den Strukturen?
Wie wär's mit:
Jeder Struktur bekommt einen Funktionscode.
Im Datum wird das ganze Struct hinterlegt.
Auf deine Daten kannst du auf die 'schöne' Art mit sprechenden Namen
zugreifen
1
storage.data.b=5.0;
und für den Zugriff als uint16_t Array benutzt du
1
Transfer(storage.data_mod[3]);
Warnung:
Eigentlich ist diese Verwendung einer union streng genommen nicht
erlaubt. Allerdings gibt es keinen Compiler, der das falsch (= nicht so
wie erwartet) macht, so dass sich diese Variante als eine Art
'inoffizieller De Facto Standard' etabliert hat.
Warnung 2:
Ein mögliches Struktur Padding kann dir da noch einen Strich durch die
Rechnung machen. Also darauf aufpassen und zur Not in der Compiler-Doku
nachsehen, wie man das Padding für eine einzelne Struktur gezielt
abschalten kann.
Falko J. schrieb:> Eine Frage noch:> Was versteht man den unter "Struktur Padding " ?
Das der Compiler zwischen Strukturmember Dummybytes einfügen darf um
Alignment-Restriktionen des Prozessors zu erfüllen.
Gilt zb die Restriktion, dass alle Zugriffe nur auf geraden
Speicher-Adressen stattfinden dürfen, dann gibt es hier
struct x
{
char a;
char b;
};
ein Problem, wenn ein char 1 Byte gross ist. Es ist einfach, dafür zu
sorgen, dass bei einer struct x Variablen das x.a auf einer geraden
Speicheradresse zu liegen kommt. Aber es ist so erst mal nicht möglich
im selben Aufwasch dann auch x.b auf eine gerade Speicheradresse zu
legen. Der Compiler hat nun 2 Möglichkeiten: entweder er baut etwas
komplizierteren Code um auf x.b zugreifen zu können (in dem er nicht
einen Bytezugriff macht sondern einen Wortzugriff und dann das b
entsprechend mit UND/ODER Operationen rausmaskiert) oder aber er schiebt
einfach ein Dummybyte zwischen a und b rein. Dann liegt auch b wieder an
einer geraden Speicheradresse und alles ist wieder paletti.
Ob Structure Padding notwendig ist oder nicht, hängt im wesentlichen von
der Prozessorarchitektur ab und welche Speicherzugriffsrestriktionen sie
mitbringt.
Man merkt es daran, dass zb
sizeof( struct x )
eine 4 ergibt und keine 2, wie man eigentlich anhand der beiden 1-Byte
Variablen annehmen würde.
Ein kurzer Blick auf die "Endinaness" des Systems und des Modbusses
sollte man aber schon gemacht haben, bevor die Lösung mit der union zum
Einsatz kommt. Ansonsten gibts Datensalat...
Oliver
Hallo Oliver, Hallo Karl Heinz,
Danke für die ausführliche Erklärung zum Structure Padding.
Ich hab jetzt angefangen das Programm umzuschreiben und es funktioniert.
Auch ohne Probleme mit "Structure Padding", musste also nichts am
compiler umstellen (Das System ist übrigens ein avr und gcc).
Der Modbus Master Simulator für den PC "Modbus Poll" hat auch die
nacheinander abgelegten float Werte wieder zu einer richtigen float zahl
zusammengesetzt.
Vielen Dank nochmal!
Gruß
Falko