Laden...

usb2.dll in C# Projekt einbinden

Erstellt von dengel vor 18 Jahren Letzter Beitrag vor 18 Jahren 4.178 Views
D
dengel Themenstarter:in
101 Beiträge seit 2005
vor 18 Jahren
usb2.dll in C# Projekt einbinden

Hallo alle miteinander!

ich hab folgendes Problem:

ich habe in mein c# Projekt die usb2.dll (Version 1.7 von www.braintechnology.de) eingebunden! Die Funktionen habe ich auch extern deklariert! Es funktioniedren alle Funktionen bis auf die Funktion um I2C Telegramme zu senden.

allgemeine Funktionsbeschreibuntg:

function UsbI2CWriteBytes(DevNum, SlaveAddr, Length : Word; P : Pointer) : boolean;

mein DllImport:


[DllImport("usb2.dll", CharSet = CharSet.Auto)]
public extern static bool UsbI2CWriteBytes(byte DevNum, byte SlaveAddr, short Length, [MarshalAs(UnmanagedType.LPTStr)] StringBuilder P);

mein Funktionsaufruf:


bool result = UsbI2CWriteBytes(DevNum, adr, datalength, data[0]);

Das Senden der Adresse funktioniert. Nur die Datenbytes werden nicht versendet! Ich denke das hängt irgendwie mit dem Pointer zusammen!

Vielleicht kann mir jemand helfen! Bin für jeden vorschlag dankbar!

Wünsch euch ein schönes Wochenende!

Gruß dengel

1.373 Beiträge seit 2004
vor 18 Jahren

Ich weiß ja nicht, was die Methode beim Parameter P erwartet ("Pointer" kann ja mal alles sein), aber ich vermute doch mal den zu versendenden Datenpuffer. Versuch also mal


[DllImport("usb2.dll", CharSet = CharSet.Auto)]
public extern static bool UsbI2CWriteBytes(byte DevNum, byte SlaveAddr, short Length, byte[] P);

Entsprechend musst du dann für P ein byte[] übergeben.
Wenn du direkt strings übergeben willst, würde ich das hier probieren:


[DllImport("usb2.dll", CharSet = CharSet.Auto)]
public extern static bool UsbI2CWriteBytes(byte DevNum, byte SlaveAddr, short Length, [MarshalAs(UnmanagedType.LPTStr)]string P);

Der Stringbuilder wird eigentlich nur verwendet, wenn eine Funktion einen String zurückgibt. Du willst aber offensichtlich etwas übergeben (und versenden).

MfG VizOne

D
dengel Themenstarter:in
101 Beiträge seit 2005
vor 18 Jahren

Hallo VizOne,

danke für deine Antwort! Hab beide möglichkeiten ausprobiert! Nur leider verschickt mir die Funktion nur die SlaveAddr! Danach wird abgebrochen!

mein Funktionsaufruf:

bool result = UsbI2CWriteBytes(DevNum, adr, datalength, data[0]);

mfg

dengel

1.373 Beiträge seit 2004
vor 18 Jahren

Welchen Datentyp hat data[0] denn? Was willst du genau verschicken?

MfG VizOne

D
dengel Themenstarter:in
101 Beiträge seit 2005
vor 18 Jahren

Hallo,

hab gerade herausgefunden, das der Pointer vom Typ char* ist! Hilft dir das weiter? Bei der Dll war eine headerdatei für c++ dabei! (im anhang)!

data[0] ist vom Typ string

string[] data = new string[9];

data[0] = 0x00; //Adresse 2
data[1] = 0x00; //Adresse 3
data[2] = 0x22; //Control (schreiben)
data[3] = 0x00; //Index
data[4] = 0x00; //Subindex
data[5] = 0x00; //Data1
data[6] = 0x00; //Data2
data[7] = 0x00; //Data3
data[8] = 0x00; //Data4

Das Telegramm besteht dann aus der SlaveAdr und den Datenbytes!

mfg

dengel

1.373 Beiträge seit 2004
vor 18 Jahren

Hm, warum ein string-array? Wenn du ein String-Array verwendest und nur das erste element versendest (data[0]), wird das bestimmt nicht das gewünschte Ergebnis haben.

Mach doch mal folgendes:



[DllImport("usb2.dll", CharSet = CharSet.Auto)]
public extern static bool UsbI2CWriteBytes(byte DevNum, byte SlaveAddr, short Length, byte[] P);


//...


byte[] data = new byte[9]

data[0] = 0x00; //Adresse 2
data[1] = 0x00; //Adresse 3
data[2] = 0x22; //Control (schreiben)
data[3] = 0x00; //Index
data[4] = 0x00; //Subindex
data[5] = 0x00; //Data1
data[6] = 0x00; //Data2
data[7] = 0x00; //Data3
data[8] = 0x00; //Data4


UsbI2CWriteBytes(DevNum, adr, data.Length, data);


Das müsste gehen.

MfG VizOne

D
dengel Themenstarter:in
101 Beiträge seit 2005
vor 18 Jahren

Hab ich auch schon ausprobiert! Bekomm aber den gleichen Effect! Es wird auch nur die Adresse geschickt! Die Datenbytes fehlen!

Hab eben die gleiche Funktion in C++ ausprobiert! Dort funktioniert es! Hab die .h Datei eingebunden, Funktion aufgerufen und es hat funktioniert! Das Telegramm wurde gesendet!
?????????? ?( ?(
Nur in C# funktionierts nicht!

1.373 Beiträge seit 2004
vor 18 Jahren

Welche Adresse? SlaveAddr oder data[0]?

MfG VizOne

4.506 Beiträge seit 2004
vor 18 Jahren

Hallo dengel!

Also ich kann mir sehr gut vorstellen, dass das Problem hier die Stringverarbeitung bei C++ ist.

C++ erwartet in dem Fall ein Startpunkt (Zeiger) auf einen Stringarray, soweit so gut. Bei C++ ist hier das Problem, dass er dann so lange liest, bis er dann ein "\0" liest.

In C# gibt es solch einen Datentyp nicht mehr (wegen der Typsicherheit), was mich auf folgenden Hinweis bringt:

Versuche mal alle Bytes die Du schicken möchtest in einen normalen String einzulesen (am Besten ANSI Format), also KEIN Array!

Und übergib den kompletten String an die Funktion.

C++ sollte dann das character für character einlesen, bis zum "\0".

Meiner Meinung nach sollte das das Problem sein.

Ciao
Norman-Timo

A: “Wie ist denn das Wetter bei euch?”
B: “Caps Lock.”
A: “Hä?”
B: “Na ja, Shift ohne Ende!”

D
dengel Themenstarter:in
101 Beiträge seit 2005
vor 18 Jahren

VizOne:

Die SlaveAdr wird geschickt!

norman_toímo:

Das werd ich gleich mal auspobieren! Dank dir aber schon mal!

mfg

dengel

1.373 Beiträge seit 2004
vor 18 Jahren

Es ist sehr unwahrscheinlich, dass die Funktion einen NULL-Terminierten String erwartet. Erstes gibt es denn vorletzten Parameter Length, zweitens wurde doch offenbar in C++ erfolgreich gesendet und drittens wäre der String doch bereits beim ersten byte terminiert (0x00). Aber Probieren kostet nichts 😉

MfG VizOne

P.S.: http://www.braintechnology.de/braintechnology/usb2dll_help17_en.html#Usbi%3Csup%3E2%3C/sup%3EcWriteBytes

1.373 Beiträge seit 2004
vor 18 Jahren

Zwei Dinge noch:

a) Änder mal Spaßeshalber CharSet.Auto in CharSet.Ansi oder lass es ganz weg
b) Was gibt die Funktion beim Aufruf zurück? true? false?

MfG VizOne

4.506 Beiträge seit 2004
vor 18 Jahren

Ja, stimmt, die Length Angabe ist hier dann auch etwas überflüssig.

Ich denke nur an meine C, bzw. C++ Zeit zurück, und da war ein char* immer ein null-terminierter String.

Warum aber der String nach dem ersten Byte terminiert sein soll, kann ich nicht nachvollziehen, denn so etwas wie:



string tmpString = "";

for (int i = 0; i < data.Length(); i++)
{
   tmpString += data[i].toString();
}


versetzt bei jedem Hinzufügen eines bytes die null-terminierung nach hinten.

Daher dachte ich, das könnte so funktionieren.

Die Length angabe in der DLL-Methode könnte ein Art Überlaufschutz sein, oder aber zur Abgrenzung für das Versenden von einem gewissen Teil (nicht komplett) einer Nachricht. [Dafür kenne ich mich in der Syntax der Nachricht nicht genug aus!]

Ciao
Norman-Timo

A: “Wie ist denn das Wetter bei euch?”
B: “Caps Lock.”
A: “Hä?”
B: “Na ja, Shift ohne Ende!”

1.373 Beiträge seit 2004
vor 18 Jahren

Original von norman_timo
Warum aber der String nach dem ersten Byte terminiert sein soll, kann ich nicht nachvollziehen, denn so etwas wie:

Ich bezog mich auf diesen code:


data[0] = 0x00; //Adresse 2
data[1] = 0x00; //Adresse 3
... usw.

Bereits das erste byte ist das (potenziell terminierende) null-byte. Aber dengel sagt ja selbst, dass es in C++ funktioniert; vermutlich so:


char data[9];
data[0] = 0x00; //Adresse 2
data[1] = 0x00; //Adresse 3
data[2] = 0x22; //Control (schreiben)
data[3] = 0x00; //Index
data[4] = 0x00; //Subindex
data[5] = 0x00; //Data1
data[6] = 0x00; //Data2
data[7] = 0x00; //Data3
data[8] = 0x00; //Data4

MfG VizOne

4.506 Beiträge seit 2004
vor 18 Jahren

Ja, das könnte aber auch daran liegen, dass im C++ Teil das Ganze hintereinander fortgeschrieben wird, dass das aber trotzdem zu Probleme führt, wenn beim Schreiben des Array's im Speicher auf schon besetzten Platz stösst!

Das sind so typische "Fehler" in C++ Programmen, und da sind die String-Operationen sowas von gemeingefährlich, weil sie von 100 Fällen bei 99 funktionieren!

Nur weil das jetzt so funktioniert hat, heißt das nicht, das das korrekt war! Ich behaupte sogar dass das Zufall war!

Egal, das ist erst mal unwichtig, dengel will das ja in .NET ausführen können.

Ciao
Norman-Timo

A: “Wie ist denn das Wetter bei euch?”
B: “Caps Lock.”
A: “Hä?”
B: “Na ja, Shift ohne Ende!”

D
dengel Themenstarter:in
101 Beiträge seit 2005
vor 18 Jahren

Hab CharSet.Ansi ausprobiert! Doch es funktioniert leider auch net!
Als Ergebnis der Funktion bekomme ich false zurück! In dem Fall wurde die Funktion nicht richtig ausgeführt! Als Fehlercode bekomme ich 0x10! Das lt. Beschreibung "The i2c slave did not acknowledge" bedeutet! Doch der Slave wird ja nur angesprochen(SlaveAdr) und bekommt gar keine Telegramme! Also kann er auch nicht antworten!!! Ich überwache die Ausgabe mit einem OSZI um zu überprüfen ob auch wirklich was geschickt wird! Es wird SlaveAdr geschickt und nicht mehr!

Kann ich irgendwie die C++ Headerdatei und den Funktionsaufruf von VC++ in meinem C# Projekt verwenden???

1.373 Beiträge seit 2004
vor 18 Jahren

Nicht direkt. Aber du kannst eine Managed C++ bzw. C++/CLI DLL schreiben und in dein C# Projekt einbinden. Aber es müsste auch mit C# funktionieren. Zeig noch mal bitte deinen aktuellen DllImport und deinen Aufruf der Funktion. Kontrolliere zudem mit dem Debugger, was wirklich an die Methode übergeben wird. Es muss ja irgend einen Unterschied geben, was du bei C# machst und was bei C++ passiert. Hast du alles initialisiert (Geräte oder so)?

MfG VizOne

D
dengel Themenstarter:in
101 Beiträge seit 2005
vor 18 Jahren

DllImport:

[DllImport("usb2.dll",CharSet = CharSet.Ansi)]
public extern static bool UsbI2CWriteBytes(byte DevNum, byte SlaveAddr, short Length, byte[] P);

Funktionsaufruf:

bool result = UsbI2CWriteBytes(DevNum, adr, datalength, data);

1.373 Beiträge seit 2004
vor 18 Jahren

Wo und wie wird datalength initialisiert? Nimm zur Sicherheit data.Length, das passt dann auf jeden Fall.

MfG VizOne

D
dengel Themenstarter:in
101 Beiträge seit 2005
vor 18 Jahren

Ich hab eine Funktion, in der die Telegramme erstellt werden, und aus der dann wiederum eine Funktion aufgerufen wird, die diese Telegramme dann versendet.

Hier die Funktion die für das Senden verantwortlich ist:


private bool WriteMessage(byte DevNum, byte adr, short datalength, byte[] data)
{
	bool result = UsbI2CWriteBytes(DevNum, adr, datalength, data);	
			
	if(result == true)
	{
		form3.progressbar();
		//evtl Wartezeit einfügen
		result = UsbI2CReadBytes(DevNum, adr, datalength, rdata);
		if((result == true) && (rdata[2] == 0x60) 
		   && (rdata[3] == data[3]) && (rdata[4] == data[4]))
		{
		   result = true;
		}
	}
	return result;
}

Und hier ein teil der FUnktion die die Telegramme erstellt:


private bool IOtransmit(byte adr)
{
               //Variable deklarieren
	bool result = false;					//Rückgabewert
	short datalength = 0x09;				//Datenlänge

	//Telegramme zusammenstellen
                //0x07 Manufacturer SW Version
	data[3] = 0x07;	//Index
	data[4] = 0x00;	//Subindex
	data[5] = Convert.ToByte(IO.Manufacture_SW_Version);
	result = WriteMessage(DevNum, adr, datalength, data);
	if (result == false)
		return false;
 	//0x0A Storrage Parameter
	for( int i = 0 ; i < IO.Store_parameters.Length; i++)
	{
		data[3] = 0x0A;	//Index
		data[4] = Convert.ToByte(i);
		data[5] = Convert.ToByte(IO.Store_parameters[i]);
		result = WriteMessage(DevNum, adr, datalength, data);
		if (result == false)
			return false;
	}

4.506 Beiträge seit 2004
vor 18 Jahren

Häh?

Verzeihung, aber ich versteh das mal überhaupt nicht, was Du da machst.

Deshalb frage ich mal ganz vorsichtig:

  • Wieso deklarierst Du in der IOtransmit()-Methode zunächst data[3] bis data[5], dann in der for-Schleife nochmal?

  • Wieso rufst Du für jedes data[x] die Methode WriteMessage() auf (in der for-Schleife)

  • Müsste man nicht zunächst das data komplett initialisieren und dann nur EINMAL die Methode WriteMessage() aufrufen. (hier auch data[0] bis data[2] initialisieren!!!)?

Also vielleicht hab ich das ganze Prinzip jetzt nicht verstanden, oder aber Du hast Dich da in etwas verrant.

Ciao
Norman-Timo

A: “Wie ist denn das Wetter bei euch?”
B: “Caps Lock.”
A: “Hä?”
B: “Na ja, Shift ohne Ende!”

D
dengel Themenstarter:in
101 Beiträge seit 2005
vor 18 Jahren

Es ist so gedacht:

Ich hab ein Objektverzeichnis, das ich als Klasse/Struktur erstellt habe. In dem sind Variable, die auch in dem I²C Slave vorhanden sind. Nun möchte ich jede Variable des OBV als telegramm zum Slave schicken. Deshalb baue ich für jede Variable ein Telegramm oder wenns ein Array ist natürlich mehrere! Wenn ein Telegramm erstelt ist, ruf ich die Funktion WriteMessage auf um dieses zu verschicken. Danach bau ich das nächste Telegramm und verschick dieses wieder, bis ich am ede des OBV bin!
data[0], data[1] und data[2] sind nur dummywerte, die nicht verändert werden. Die werden Später evtl noch benötigt! darum werden die nicht jedesmal neu initialisiert.

4.506 Beiträge seit 2004
vor 18 Jahren

Ich glaub Du verwechselst jetzt das "data[]" für deinen Funktionsaufruf, und das "data[]" was Deine OBV (was auch immer das ist?) Daten enthält.

Wenn ich die C++ Methode richtig verstanden hab, dann erwartet das "data[]" dort das komplette Telegramm mit Header und Content.

Also man könnte das doch auch so machen, oder irre ich mich da:



    // Build telegram content
    ArrayList myCompleteData = new ArrayList();

    for( int i = 0 ; i < IO.Store_parameters.Length; i++)
    {
        myCompleteData.Add(Convert.ToByte(IO.Store_parameters[i]));
    }

    // add header to content in new byte[]
    byte[] toSend = new toSend[myCompleteData.Count + 5];

    // header    
    toSend[0] = // ?? , [1], [2] = ?
    toSend[3] = 0x0A;    //Index
    toSend[4] = Convert.ToByte(i); // Subindex weiß nicht wie gesetzt...

    // content
    myCompleteData.CopyTo(toSend, 6);


    // send telegram
    result = WriteMessage(DevNum, adr, datalength, data);
    
    if (result == false)
        return false;


Warum Du für wirklich JEDES Byte ein Telegramm verschickst weiß ich nicht!

Aber so sollte es doch besser funktionieren, oder?

Ciao
Norman-Timo

A: “Wie ist denn das Wetter bei euch?”
B: “Caps Lock.”
A: “Hä?”
B: “Na ja, Shift ohne Ende!”

D
dengel Themenstarter:in
101 Beiträge seit 2005
vor 18 Jahren

public class InputOutput
{
	//Variablen deklarieren
	public int Error_Register;				//01
	public int Manufacture_Device_Name;			//05
	public int Manufacture_HW_Version;                      		//06
	public int Manufacture_SW_Version;			//07
	public int[] Store_parameters = new int[9];			//0A
	public int[] Restore_default_parameters = new int[9];	                //0B
	public int Ring;					//0C
	public int[] Operating_time_counet  = new int[3];		//10
	public int[] Identity_Object = new int[6];			//11
	public int Modul_Position;				//12
	public int Communication_State;				//29
	public int[] I2C_Rank_Definition = new int[4];		//2A
	public int[] Data_In_Interface = new int[4];		                //2B
	public int Transceive_State;				//2D
	public int[] Data_Out_Interface = new int[4];		//2F
	public int[] I2C_SchedulleList = new int[126];		//31
	public int[] Input_Parameter_1 = new int[255];		//41
	public int[] Input_Parameter_3 = new int[255];		//43
	public int[] Output_Parameter_1 = new int[255];		//46
	public int[] Output_Parameter_4 = new int[255];		//49
	public int[,]Function_Data_List = new int[255,2];		//50
	public int[] Function_Adress_List = new int[255];		//51
	public int[] Modul_State = new int[255];			//53
	public int[] PMD_Modul_List = new int[255];			//54
	public int PMD_Update_List;				//55
	public int[] IO_Control =  new int[2];			//68
	public int[,] IO_Assigned_List =  new int[9,4];		//69
		
	//Konstructor
	public InputOutput()
	{
                         ..................................


Das ist das OBV!

Um die Informationen auf dem Slave zu speichern, muss ich die Werte teilweise einzeln übertragen. Ich kann in einem Telegramm nur 4 Datenbyte versenden!
Ind data[] schreibe ich die werte aus den einzelnen variablen des OBV.

Das Protokoll:
Adresse A,B und C
A w B w C w Control index Sub-index Data 1 Data 2 Data 3 Data 4

bsp:

Input_Parameter_List[0] = 0x22;

d.h. subindex = 0x00 und data1 = 0x22

Input_Parameter_List[1] = 0x34;

d.h. subindex = 0x01 und data1 = 0x34

In der Art muss ich die Daten übertragen!

4.506 Beiträge seit 2004
vor 18 Jahren

Ok, dann würde ich wohl die gleichen Probleme wie Du haben, denn ab hier sehe zumindest ich auch nicht weiter!

Sorry, dass ich doch nicht helfen konnte.

Ciao
Norman-Timo

P.S: Welches Telegramm kann denn nur 4 Bytes übertragen, so was von fehlkonstruiert 🙂

A: “Wie ist denn das Wetter bei euch?”
B: “Caps Lock.”
A: “Hä?”
B: “Na ja, Shift ohne Ende!”

D
dengel Themenstarter:in
101 Beiträge seit 2005
vor 18 Jahren

Ich dank dir aber trotzdem für deine Zeit und Hilfe!!

mfg

dengel

1.373 Beiträge seit 2004
vor 18 Jahren

Ein int besteht aber aus vier bytes. Convert.ToByte ist daher nicht ausreichend. Statt dessen solltest du BitConverter.GetBytes() benutzen, diese Methode gibt - wenn mit einem int-Parameter aufgerufen - ein array mit den vier bytes des übergebenen Ints zurück. Diese musst du dann entsprechend in dein Dataarray hineinkopieren.
Außerdem musst du wissen, in welcher Reihenfolge der Slave diese bytes erwartet (LittleEndian vs. BigEndian).

Wie sähe denn der funktionierende äquivalente C++ code aus?

MfG VizOne

D
dengel Themenstarter:in
101 Beiträge seit 2005
vor 18 Jahren

Guten Morgen VizOne!

Ich habs hinbekommen! Ich hab gestern den ganzen Tag rumprobiert und auf einmal hat es funktioniert! Ich weiss aber nicht genau warum! Ich vermute mal es lag daran, dass der Slave manchmal kein acknowledge zurückschickt und dadurch hat die Funktion immer nen Fehler gemeldet! Naja hauptsache es funktioniert jetzt!

Ich dank Dir aber trotzdem mal für deine Hilfe und deine investierte Zeit!

mfg

dengel