Moin moin Allseits,
ich habe ein frage die zwar nicht ganz zu GCC passt, aber dem am
nächsten kommt da es eine allgemeine C-Frage ist, denke ich.
Folgendes möchte ich machen und bekommen es nicht ganz gebacken. Ein
CANopen Verzeichniss soll umgesetzt werden. Dazu habe ich eine Typedef
Struct angelegt mit Index, SubIndex, Attribute (read, Write, etc.) Typ
(Uint8, uint16, etc.) und einem pointer (der soll sowohl auf Variable,
als auch auf Funktion zeigen können, falls das überhaupt geht). Hier
erst mal der entsprechende Code:
1
typedefstruct
2
{
3
unsignedshortidx;// index des eintrags
4
unsignedcharsub;// subindex des eintrags
5
enumattributesattr;// attribute
6
enumtypestype;// variablentyp
7
constuchar*pROM;
8
9
}_CANopen_DICT_Template;
10
11
constuchar__dummy[4]={0,0,0,0};
12
13
const_CANopen_DICT_Template_db_object[]={
14
//idx sub attributen typ ptr auf variable/funktion
Die Mitglieder des Structs werden nun ganz normal mit Beispielsweise:
1
if(_db_object[k].type==u8){/* tu dies und das*/}
angesprochen.
Nun möchte ich aber auf meine Variable über den Pointer zugreifen, evtl.
so:
1
if(_db_object[k].idx==0x1018){
2
_db_object[k].*pROM=0;// variable nullen
3
}
Oder eben auf meine Funktion, ungefähr so:
1
if(_db_object[k].idx==0x100D){
2
_db_object[k].pROM();// funktion aufrufen
3
}
aber bekomme ich nicht gebacken, da ich nicht ganz verstehe wie ich nun
auf die variable zugreifen kann, bzw. meine Funktion aufrufen kann. Hat
jemand einen Tipp für mich?
Offlineuser schrieb:> Nun möchte ich aber auf meine Variable über den Pointer zugreifen, evtl.> so:if (_db_object[k].idx == 0x1018) {> _db_object[k].*pROM = 0; // variable nullen> }
Vielleicht besser so:
Offlineuser schrieb:> Oder eben auf meine Funktion, ungefähr so:
Welche Funktion?
Ich sehe hier keine.
Wenn es wirklich auf eine Funktion zeigt, muß aber gecastet werden
(da der Zeiger nicht als Zeiger auf eine Funktion deklariert ist,
sondern als Zeiger auf const uchar).
Um den cast zu zeigen, bräuchte man die genaue Deklaration der Funktion.
Keine Ahnung was damit gemeint ist.
@Klaus Wachtler:
Die Funktion
1
void_CO_COMM_NMTE_LifeFactorAccessEvent(void)
muss man sich im Moment noch dazu denken, die ist noch nicht
implementiert, da ich zuerst die Variablenaufrufe in den Griff bekommen
wollte.
Mit casten meinst du in etwa Folgendes?:
muss
> man sich im Moment noch dazu denken, die ist noch nicht implementiert,
macht nichts.
Trotzdem muss sie ja eine Funktionssignatur haben.
Welchen Returntype hat sie?
Welche und wieviele Argumente nimmt sie?
Welchen Datentyp haben die Argumente?
Das alles musst du wissen wenn du eine Funktion aufrufen willst.
Das ist auch nicht anders, wenn man die Funktion per Pointer aufruft.
> Mit casten meinst du in etwa Folgendes?:>
Nein.
Funktionspointer sind anders.
http://www.mikrocontroller.net/articles/FAQ#Funktionszeiger> Wäre es übrigens eine Alternative die Tabelle zu erweitern mit separatem> Pointer auf Funktion, evtl. so:
Ich würde ehrlich gesagt, die ganzen Funktionspointer von den
Datenpointern komplett trennen. Komplett eigene Struktur. Schon alleine
die Tatsache, dass du hier wie ein Wilder rumcasten musst, zeigt schon
dass da was nicht stimmt.
Exzessives Casten ist meistens ein deutliches Indiz, dass der Aufbau der
Datenstruktur nicht stimmt.
> Ich muss zugeben, dass ich bei den ganzen Pointern im Moment ziemlich> verwirrt bin.
Das Gefühl habe ich auch. Du übernimmst dich. Du bist noch nicht soweit
um das in den Griff zu kriegen.
Karl heinz Buchegger schrieb:> macht nichts.>> Trotzdem muss sie ja eine Funktionssignatur haben.>> Welchen Returntype hat sie?>> Welche und wieviele Argumente nimmt sie?>> Welchen Datentyp haben die Argumente?>>>> Das alles musst du wissen wenn du eine Funktion aufrufen willst.>> Das ist auch nicht anders, wenn man die Funktion per Pointer aufruft.
Ist das nicht alles mit:
Sieht das plausibel aus?
> Ich würde ehrlich gesagt, die ganzen Funktionspointer von den>> Datenpointern komplett trennen. Komplett eigene Struktur. Schon alleine>> die Tatsache, dass du hier wie ein Wilder rumcasten musst, zeigt schon>> dass da was nicht stimmt.>> Exzessives Casten ist meistens ein deutliches Indiz, dass der Aufbau der>> Datenstruktur nicht stimmt.
Komplett eigene Struktur für Funktionen und Variablen möchte ich
vermeiden. Die Tabelle soll flexibel sein und je nach Eintrag mal das
eine mal das andere ausführen können.
Aber der Kompromis mit eigenem Eintrag für Variable und Funktion ist ein
guter Mittelweg, denke ich. Den Versuch der Umsetzung ist ein Absatz
höher aufgeführt....
>> Ich muss zugeben, dass ich bei den ganzen Pointern im Moment ziemlich>>> verwirrt bin.>> Das Gefühl habe ich auch. Du übernimmst dich. Du bist noch nicht soweit>> um das in den Griff zu kriegen.
Genau deswegen wende ich mich ja an euch und versuche zu lernen und das
in den Griff zu bekommen.
Danke schon mal an alle an dieser Stelle für ihre Unterstützung!
Offlineuser schrieb:> Mit solchem Aufruf:> if (_db_object[k].idx == 0x1002) {> _db_object[k].Function(); // funktion aufrufen> }> Sieht das plausibel aus?
So in etwa, ja.
Dabei muß aber noch __dummyFKT vor der ersten Verwendung deklariert
werden.
In deiner ersten Version hattest du jeweils einen Eintrag in
der struct weniger, indem du pROM mal als Zeiger auf Daten und mal
als Zeiger auf eine Funktion genutzt hattest.
Wenn sichergestellt ist, daß wirklich immer nur der eine oder
der andere Wert genutzt wird und du am Zusammenhang erkennen kannst,
welches der beiden jeweils drin steht, kann man das auch mit einem
Element erledigen. Dazu sollte man dann eine union aus dem
Funktionszeiger und dem Zeiger auf die Daten machen.
Solange der Platz aber nicht zu knapp ist im Programm, würde
ich aber nicht dazu raten. Solche Mehrfachnutzungen machen
das Programm nicht gerade übersichtlicher und dadurch
fehleranfälliger.
Abgesehen davon kommt mir das Programm auch nicht so ganz
elegant vor (das kann aber auch daran liegen, daß mnan nur
wenig davon sieht).
Z.B. würde ich (soweit man den Wert nicht braucht) nicht
ein Dummy-Feld oder eine Dummy-Funktion nehmen, sondern einfach
NULL. Dann darf man natürlich auch nicht darauf zugreifen, aber
es ist offenkundig, daß man den Wert nicht nutzen will.
Will man wirklich den Wert haben und nutzen, ist irgendwas
mit dummy im Namen eher eine schlechte Wahl.
Naja, nicht die einzige schlechte Wahl zur Zeit.
Klaus Wachtler schrieb:> So in etwa, ja.> Dabei muß aber noch __dummyFKT vor der ersten Verwendung deklariert> werden.
Jo, das ist klar.
> In deiner ersten Version hattest du jeweils einen Eintrag in> der struct weniger, indem du pROM mal als Zeiger auf Daten und mal> als Zeiger auf eine Funktion genutzt hattest.> Wenn sichergestellt ist, daß wirklich immer nur der eine oder> der andere Wert genutzt wird und du am Zusammenhang erkennen kannst,> welches der beiden jeweils drin steht, kann man das auch mit einem> Element erledigen. Dazu sollte man dann eine union aus dem> Funktionszeiger und dem Zeiger auf die Daten machen.> Solange der Platz aber nicht zu knapp ist im Programm, würde> ich aber nicht dazu raten. Solche Mehrfachnutzungen machen> das Programm nicht gerade übersichtlicher und dadurch> fehleranfälliger.
Ja, stimmt, das sehe ich mittlerweile genau so und habe die Tabelle nun
um getrennte Variablen und Funktionszeiger erweitert.
> Abgesehen davon kommt mir das Programm auch nicht so ganz> elegant vor (das kann aber auch daran liegen, daß mnan nur> wenig davon sieht).> Z.B. würde ich (soweit man den Wert nicht braucht) nicht> ein Dummy-Feld oder eine Dummy-Funktion nehmen, sondern einfach> NULL. Dann darf man natürlich auch nicht darauf zugreifen, aber> es ist offenkundig, daß man den Wert nicht nutzen will.> Will man wirklich den Wert haben und nutzen, ist irgendwas> mit dummy im Namen eher eine schlechte Wahl.> Naja, nicht die einzige schlechte Wahl zur Zeit.
Das mit Null hatte ich mir auch überlegt und man könnte natürlich auch
den Pointer vor dem Zugriff auch auf NULL abfragen, außerdem hat die
tabelle auch einen Eintrag der besagt was auszuführen ist. Naja, mal
sehen.
Vielen Dank nochmals für eure Unterstützung!
Ich denke ich habe das nun soweit ausgeknobbelt, dass es einwandfrei
funktioniert (zumindest die hier im Thread angesprochenen Funktionen).
Und ich habe entsprechend ein kleines Testprogramm geschrieben, das man
im Simulator laufen lassen kann um dies gegenzuchecken (im MPLAB IDE mit
C30 Compiler funktioniert alles exact wie gewünscht). Für die
Interessierten hier noch das Testprogramm:
main.c
1
#include<p30Fxxxx.h>
2
#include<stdio.h>
3
#include"canopen_defs.h"
4
5
6
intmain(void)
7
{
8
unsignedcharucTest=0;
9
unsignedshortusTest=0;
10
unsignedlongulTest=0,ulTest2=0;
11
12
unsignedchar*pChar;
13
14
_CANopen_DICT_TemplatelocalObject;
15
16
for(;;)
17
{
18
//__asm__("NOP");
19
20
// test, wie viele einträge hat die tabelle?
21
ucTest=sizeof(_db_object)/sizeof(localObject);
22
23
//---------------------------
24
// aus dem speicher in can buffer schreiben
25
//---------------------------
26
// unsigned char
27
ucTest=(unsignedchar)(*_db_object[1].pVar);
28
29
// unsigned short, 2 byte
30
pChar=_db_object[2].pVar;
31
usTest=(unsignedshort)(*pChar++);
32
usTest+=((unsignedshort)(*pChar))<<8;
33
34
// unsigned long, 4 byte
35
pChar=_db_object[0].pVar;
36
ulTest=(unsignedlong)(*pChar++);
37
ulTest+=((unsignedlong)(*pChar++))<<8;
38
ulTest+=((unsignedlong)(*pChar++))<<16;
39
ulTest+=((unsignedlong)(*pChar))<<24;
40
41
42
//---------------------------
43
// aus dem Buffer etwas in den speicher schreiben
44
//---------------------------
45
// fiktiven CAN-Buffer füllen
46
ulTest2=0x12345678;
47
// vom CAN-Buffer in speicher holen, unsigned long
48
pChar=_db_object[2].pVar;
49
*pChar++=ulTest2;
50
*pChar++=ulTest2>>8;
51
*pChar++=ulTest2>>16;
52
*pChar=ulTest2>>24;
53
54
55
//---------------------------
56
// ein paar funktionen ausführen
57
//---------------------------
58
_db_object[0].pFkt();
59
_db_object[3].pFkt();
60
_db_object[7].pFkt();
61
}
62
63
}
64
65
66
voidCO_COBIDAccessEvent(void){
67
}
68
69
voidCO_GuardTimeAccessEvent(void){
70
}
71
72
voidCO_LifeFactorAccessEvent(void){
73
}
74
75
voidCO_HeartBeatAccessEvent(void){
76
}
77
78
79
80
void__dummyFKT(void){
81
}
canopen_defs.h
1
enumattributes// Memory access type
2
{// ls
3
NA=0x00,//0b00000000, // Default, non-existant
4
CONST=0x40,//0b01000000, // Default, read only from ROM
5
RW=0x60,//0b01100000, // Default, read/write
6
RO=0x40,//0b01000000, // Default, read only
7
WO=0x20//0b00100000, // Default, write only
8
};
9
10
enumtypes// Variable type
11
{
12
u8=0x0F,//0b00001111,
13
u16=0x0B,//0b00001011,
14
u32=0x03,//0b00000011,
15
fkt=0x80//0b10000000 // funktionsaufruf
16
};
17
18
19
typedefvoid(*VoidFnct)(void);
20
21
typedefstruct
22
{
23
unsignedshortidx;// index des eintrags
24
unsignedcharsub;// subindex des eintrags
25
enumattributesattr;// attribute
26
enumtypestype;// variablentyp
27
unsignedchar*pVar;
28
VoidFnctpFkt;//Function of Entry
29
30
}_CANopen_DICT_Template;
31
32
33
34
// CANopen object 0x1000
35
constunsignedlongrCO_DevType=0x8934AL;
36
37
// CANopen object 0x1008
38
constunsignedcharrCO_DevName[]="Sens";
39
40
// CANopen object 0x1009
41
constunsignedcharrCO_DevHardwareVer[]="V1.0";
42
43
// CANopen object 0x100A
44
constunsignedcharrCO_DevSoftwareVer[]="V1.0";
45
46
// CANopen object 0x1018
47
constunsignedcharrCO_DevIdentityIndx=0x4;
48
constunsignedlongrCO_DevVendorID=0x12345678L;
49
constunsignedlongrCO_DevProductCode=0x555AAA11L;
50
constunsignedlongrCO_DevRevNo=0x10305070L;
51
constunsignedlongrCO_DevSerialNo=0x87654321L;
52
53
// CANopen object 0x1001
54
unsignedcharuCO_DevErrReg;
55
56
// CANopen object 0x1002
57
unsignedlonguCO_DevManufacturerStatReg;
58
59
60
unsignedchar__dummy[4]={0,0,0,0};
61
void__dummyFKT(void);
62
voidCO_COBIDAccessEvent(void);
63
voidCO_GuardTimeAccessEvent(void);
64
voidCO_LifeFactorAccessEvent(void);
65
voidCO_HeartBeatAccessEvent(void);
66
67
68
_CANopen_DICT_Template_db_object[]={
69
//idx sub attributen typ ptr auf variable funktion