Hallo Leute,
ich möchte die Serielle Schnittstelle meines Rechners aus C oder C++
ansteuern. Die Bisherigen Lösungsansätze die ich von Google bekommen
habe, haben alle mit Visual C/C++ zu tun. Ich benutze jedoch DevC++,
d.h. ich habe keine microsoftspezifischen Bibliotheken.
Ich habe ein µC mit dem ich über die COM-Schnittstelle kommunizieren
möchte. Mein kleines Protokoll schreibe ich in C/C++, je nach dem wie
die Ansteuerung sich am besten lösen lässt.
Kennt jemand eine Lösung wie ich das ohne Visual C/C++ lösen kann?
Gruß
Konstantin
Wunderbar!!!
Vielen Dank. Habe nach windows.h gegoogelt und folgende Seite gefunden:
http://www.winapi.net/index.php?inhalt=t3
Dort steht eine fertige Klasse zum Download. In mein Projekt
implementiert und es funktioniert auf anhieb.
Nochmals danke.
Gruß
Konstantin
HI Leute,
bin mit meiner Programmierung etwas weiter gekommen. Nun möchte ich die
am PC verfügbaren COM-Ports zur Auswahl stellen.
Wie kann ich die verfügbaren COM-Ports einlesen? Ich meine damit, wenn
der Rechner COM-Ports on board hat, dann sind es höchst wahrscheinlich
COM1 und vtl. COM2. Wenn ich jetzt ein USB-to-COM Adapter anschließe
bekommt er bei einem PC z.B. die Bezeichnung COM9, bei einem anderen
COM8 usw. Deswegen möchte ich bei meinem Programm die möglichen
COM-Ports zur Auswahl stellen.
Wer weiß wie es geht?
Gruß
Konstantin
m_ComPorts ist bei mir eine Drop-Down Box auf der MFC-Oberfläche. Du
müsstest dann die Strings halt woanders abspeichern.
Wie ich schon in anderen Threads schrieb, kann es beim Durchprobieren
aller Ports zu sehr interessnaten effekten an eventuell angeschlossenen
Geräten kommen....
Hab noch ein kleines Problem.
Ich arbeite auf Konsoleebene und mein Compiler kennt den Datentyp
CString nicht.
Was kann ich da machen? Die Datei afx.h finder der auch nicht.
CString ist eine C++-Klasse, die mit den MFC ("Microsoft Foundation
Classes") mitgeliefert wird.
Wenn Du keine MFC-Applikationen schreibst, dann gibt es das natürlich
auch nicht.
(Konsolapplikationen hingegen können sehr wohl MFC-Applikationen sein).
Was für einen Compiler, was für eine C++-Klassenbibliothek setzt Du ein?
GetBufferSetLength liefert einen Pointer auf den vom CString-Objekt
verwalteten Textpuffer.
Der Pointer kann danach mit klassischen C-Funktionen wie sprintf, strcpy
etc. bearbeitet werden; die Anzahl der zur Verfügung stehenden
Speicherstellen wird mit dem Parameter von GetBufferSetLength
festgelegt.
Nachdem der Puffer so bearbeitet wurde, muss er unbedingt mit
ReleaseBuffer wieder freigegeben werden.
Soll ich vtl. ein Array vom Datentyp BYTE übergeben? Also so:
1
BYTEstrData[1024];
2
...
3
res=RegEnumValue(hKey,
4
dwIndex++,
5
chName,
6
&dwName,
7
NULL,
8
NULL,
9
strData,
10
&dwData);
11
...
Ich habe es ausprobiert. Es gibt keine Fehler. Nur wie lese ich es aus?
Wenn ich es mit einer FOR-Schleife mache kommt irgend ein Sch... raus.
Meine FOR-Schleife:
Was ist "irgendein Sch..."?
Du solltest den zweiten NULL-Parameter (also den vor "strData") übrigens
besser durch einen Pointer auf DWORD ersetzen, denn darin wird der
Datentyp des Registryeintrages geliefert:
1
BYTEstrData[1024];
2
DWORDdwType;
3
...
4
res=RegEnumValue(hKey,
5
dwIndex++,
6
chName,
7
&dwName,
8
NULL,
9
&dwType,
10
strData,
11
&dwData);
Das ist sinnvoll, da in der Registry auch durchaus ganz andere Dinge als
Strings (REG_SZ) gespeichert werden können.
Auch solltest Du nach Aufruf der Funktion den Inhalt von dwData genauer
untersuchen - da drin nämlich wird gespeichert, wieviele Bytes überhaupt
aus der Registry gelesen wurden. Ein REG_DWORD braucht nämlich nur 4
Bytes; Deine for-Schleife aber gibt 1024 davon aus.
Da die Funktion iterativ aufgerufen wird, musst Du vor jedem einzelnen
Aufruf dwData wieder auf die Größe des übergebenen Puffers setzen.
Du solltest Dir mal die Dokumentation der Funktion RegEnumValue
durchlesen, so schlecht ist die gar nicht:
http://msdn2.microsoft.com/en-us/library/ms724865.aspx
Du solltest Dir UNBEDINGT die Dokumentation von RegEnumValue durchlesen.
Und von RegOpenKeyEx.
-- Du wertest den Rückgabewert von RegEnumValue nicht aus, bevor Du
lpData[] ausgibst. Das aber darfst Du nur, wenn der Wert ERROR_SUCCESS
zurückgegeben wird.
Bereits beim ersten Durchlauf auf meinem System aber wird der Wert 259
zurückgegeben, und das ist der Fehlercode für
"Das System kann den angegebenen Pfad nicht finden."
Und das liegt hieran:
-- Du übergibst RegEnumValue einen falschen Wert für hKey - der
Rückgabewert von RegOpenKeyEx steht in Result, nicht in hKey.
Denselben falschen Wert übergibst Du auch RegCloseKey.
-- Was hat lpcValueName mit der Anzahl der in lpData[] stehenden Bytes
zu tun? Du solltest hier besser lpcbData verwenden.
-- Du definierst das Array lpData mit 1024 Elementen, weist aber
lpcbData den Wert MAX_PATH (260) zu. Was jetzt?
-- Du musst vor JEDEM EINZELNEN Aufruf von RegEnumValue die Werte
lpcValueName und lpcbData mit der Größe der Arrays lpValueName und
lpData initialisieren.
So funktionierts:
Ich habe mich bei meinen Änderungen an den von Konstantin angehängten
Sourcecode gehalten, da ist "ret" deklariert.
Ersetzt man in Konstantins Sourcecode die do ... while - Schleife durch
den von mit geposteten Code, erhält man ein Programm, das bis auf einen
Fehler (falscher Parameter für RegCloseKey) das tut, was es soll.
@ Rufus t. Firefly:
Ich danke dir vielmals. Ich habe mir die Doku von RegEnumValue gestern
noch ausgedruckt und durchgelesen. Hatte trotzdem noch etwas Probleme
damit.
Nun habe ich die Fehler die du mir genannt hast ausgebessert. Das
Programm läuft soweit. Nur zeigt mir das Programm den letzten Eintrag in
der Regestry an. In der Regestry sieht es bei mir folgender Maßen aus:
1
1. COM3
2
2. COM2
3
3. COM8
COM8 ist ein USBtoCOM-Adapter. Ziehe ich diesen heraus liefert mir das
Programm nur COM2 an den Bildschirm. Schließe ich den Adapter wieder an,
liefert zeigt es mir nur COM8 an. Wie bekomme ich die anderen Einträge
her? In der Doku steht, dass die Einträge in der Regestry nicht geordnet
sind und dass die Einträge einen beliebigen Index haben. Wenn ich doch
mit
1
dwIndwx++
durchlaufe und auf ERROR_NO_MORE_ITEMS überprüfe, komme ich doch nie
(außer zufällig) an die anderen Einträge, oder?
Was kann ich da machen?
Gruß
Konstantin
Du musst natürlich bei jedem Durchlauf den ausgelesenen String irgendwo
speichern, sonst wird ja bei jedem neuen COM-Port der wieder
überschrieben. Ich mach das gleich in die Drop-Down-Box und dann kann
der Nutzer auswählen.
Oder hast du dwIndex nicht auf 0 gesetzt vor dem Registry-Scan?
Das klingt sehr merkwürdig.
Mal in der Registry nachgesehen, wie das zum betreffenden Zeitpunkt
aussieht?
Das Phänomen tritt auf meinem Rechner (XPSP2) nicht auf.
Ich habe zu Testzwecken auch den zugeordneten Gerätenamen ausgegeben:
1
if(ret==ERROR_SUCCESS)
2
{
3
for(i=0;i<lpcValueName;i++)
4
cout<<lpValueName[i];
5
cout<<" ";
6
7
for(i=0;i<lpcbData;i++)
8
cout<<lpData[i];
9
cout<<endl;
10
}
So sieht es mit USB-Seriell-Adapter aus:
1
\Device\Serial0 COM1
2
DiPort1 COM5
3
DiPort2 COM6
4
DiPort3 COM7
5
DiPort4 COM8
6
DiPort5 COM9
7
\Device\VCP1 COM4
und so ohne:
1
\Device\Serial0 COM1
2
DiPort1 COM5
3
DiPort2 COM6
4
DiPort3 COM7
5
DiPort4 COM8
6
DiPort5 COM9
DiPort ist meine ISDN-Karte. Und, ja, mein Rechner hat nur noch eine
serielle Schnittstelle.
PS: Bitte - "Reg_i_stry"
aus der DoWhile-Schleife herausziehe. Und dann bekomme ich nur einen
Port angezeigt.
@ Christian R.:
Nach jedem Aufruf gebe ich den String in der Konsole aus.
Konstantin Ribel wrote:
> Also hier noch mal mein Code.
:-)
1
ret=RegEnumValue(Result,
2
dwIndex++,
3
lpValueName,
4
&lpcValueName,
5
NULL,
6
NULL,
7
lpData,
8
&lpcbData);
9
10
if(ret==ERROR_NO_MORE_ITEMS)
11
{
Warum wertest du die retournierten Dtaen nur dann aus, wenn
RegEnumValue dir mitteilt, dass es keine Werte mehr hat?
Hinweis: Geh solche Sachen im Debugger durch, dort hättest
du das in 20 Sekunden gesehen, dass in der Schleife nichts
ausgegeben wird, obwohl RegEnumValue immer wieder
aufgerufen wird.
Das kann ja auch gar nicht funktionieren - Du gibst die gefundenen Daten
NUR aus, wenn ret == ERROR_NO_MORE_ITEMS ist:
1
do
2
{
3
4
//lpcValueName = MAX_PATH;
5
//lpcbData = MAX_PATH;
6
7
ret=RegEnumValue(Result,
8
dwIndex++,
9
lpValueName,
10
&lpcValueName,
11
NULL,
12
NULL,
13
lpData,
14
&lpcbData);
15
16
if(ret==ERROR_NO_MORE_ITEMS)
17
{
18
cout<<" ";
19
for(inti=0;i<lpcValueName;i++)
20
cout<<lpValueName[i];
21
cout<<" ";
22
23
for(inti=0;i<lpcbData;i++)
24
cout<<lpData[i];
25
cout<<endl<<endl;
26
}
27
}
28
while(ret!=ERROR_NO_MORE_ITEMS);
Und die erneute Initialisierung von lpcValueName / lpcbData ist wichtig,
den solltest Du nicht weglassen.
Die Funktion erwartet in diesen beiden Variablen die Länge des zur
Verfügung stehenden Puffers und schreibt in diese Variablen die Anzahl
der genutzten Bytes hinein.
Damit steht vor dem Aufruf 260 (MAX_PATH) drin, nach dem ersten Aufruf
aber nur noch 4 (willkürlich, soll nur als Beispiel dienen).
Das geht solange gut, wie die Funktion nicht über Werte/Namen mit mehr
als 4 Zeichen Länge stolpert - das aber tut sie spätestens bei COM10 und
liefert "COM1" ...
Also: Lies Dir nochmal genau den von mir geposteten Code durch.
Hab ein neues Problem.
Es scheint, dass die RS232-Schnittstelle ein Buffer hat. Wenn ich mit
meinem µC permannent Zeichen an den Rechner sende und dann das zeichen
ändere dauert es immer etwas bis das neue Zeichen am PC ausgegeben wird.
Wie kann ich dieses Buffer leeren, dass ich gleich die gerade
ankommenden Zeichen lesen kann?
Gruß
Konstantin
Der Pc braucht eben auch etwas , um die ankommenden daten zu verarbeiten
.
dann kommt es automatisch zu einer latenz .
um "bestimmte" zeichen zu versenden , musst du deine nachrichten mit
einen bestimmenden merkmal versehn . und dies gebenen falls abfragen um
sicher zu gehen , das es das richtige zeichen ist .
Mit der Win32-API-Funktion SetupComm kannst Du die Grö0e der Sende- und
Empfangspuffer auf Devicetreiberebene anpassen.
Zusätzlich könnte es -je nach Baudrate- noch sinnvoll sein, den
Empfangs-FIFO des Schnittstellenbausteines zu deaktivieren, das geht mit
dem Gerätemanager.
Anschlüsse->COMx->Eigenschaften->Anschlusseinstellungen->Erweitert
Vielleicht auch mal lieber einen Thread programmieren, der die COM immer
im Hintergrund abfragt....ist sinnvoller.
Und Dtenübertragung sollte grundsätzlich ein Frame-Format haben, mit
Start, Stopp-Kennzeichnung, Checksumme, Längenkennzeichnung usw.
Ich implementiere immer das einfache Format, was auch der Bluetooth-Chip
LMX9820 benutzt, sehr effizient und simpel.
Es geht auch ohne "pollen". Wenn ein Zeichen (oder mehrere) innerhalb
einer festgelegten Zeit empfangen wurden, läßt man sich einen Event
schicken und holt die Daten ab.
SetCommTimeouts und WaitCommEvent helfen da weiter.
Sollte auch bei virtuellen COM-Ports funktionieren.
Blackbird
hallo, ich fange auch mit dec-c++ an und suche nach einem programm für
die serielle schnittstelle. kannst du das mal posten. ich habe mich
schon dämlich gesucht im google-center.
wäre dir sehr dankbar für die hilfe.
mfg
Hallo Leute,
Für die ganze Port Problematik hat Js. Payne ein erstklassiges Aktive X
geschrieben. ( www.jspayne.com ) das sollte alle Probleme beseitigen. Am
schönsten ist das es gleich mit allen Windows Versionen funktioniert.
Hallo,
ich habe nun ein ähnliches Problem:
Ich möchte auch auf die serielle Schnittstelle unter WinXP zugreifen.
Am liebsten würde ich meine Anwendung in Java programmieren, nur scheint
es dort seeehr schwer auf den Com-Port zuzugreifen.
Alternativ würde ich die Anwendung dann mit wxDev-C++ erstellen (wie
auch der Erstautor dieses Beitrags). Leider klappt sein Link zur
"windows.h" nicht mehr.
Brauche ich wirklich nur diese eine Datei zu includen und kann dann
direkt diverse Zugriffsfunktionen nutzen?!
Wo bekomme ich diese Datei? (legal?)
Ich habe dieses jspayne-active-X-Element ausprobiert, bekam es aber
leider unter wxdev-C++ nicht zu laufen... Ich verstehe absolut nicht wie
ich die Datei io.ocx in wxDev-C++ einbinden soll oder was ich dann damit
machen kann etc...
Es wäre toll wenn mich jemand ein bisschen in die richtige Richtung
bringen könnte.
Schöne Grüße,
Alex
Hallo Simon,
das hilft mir leider nicht weiter. Die Suche hatte ich auch schon bemüht
und traf dabei auf diesen Artikel, der sich noch am ehesten mit meinem
Problem beschäftigt.
Also nochmal kurz und knapp:
A) Wer kann mir Hinweise geben, wie die serielle Schnittstelle unter
Windows XP über Java angesprochen werden kann?
oder
B) Wer kann mir Hinweise geben, wie die serielle Schnittstelle mit
WxWidgets bzw. WxDevC++ angesprochen werden kann?
Wie gesagt, über sachdienliche Posts würde ich mich sehr freuen, da alle
Artikel und Webseiten die ich bisher fand nicht funktionierten! (Oder
aber Visual C++ benötigten oder ähnliches nicht frei verfügbares)
Schöne Grüße,
Alex
Alex Bürgel wrote:
> B) Wer kann mir Hinweise geben, wie die serielle Schnittstelle mit> WxWidgets bzw. WxDevC++ angesprochen werden kann?
wxWidgets enthält keine Möglichkeit eine serielle Schnittstelle
anzusteuern. Du musst dafür auf Windows-Systemfunktionen zurückgreifen.
> Wie gesagt, über sachdienliche Posts würde ich mich sehr freuen, da alle> Artikel und Webseiten die ich bisher fand nicht funktionierten! (Oder> aber Visual C++ benötigten oder ähnliches nicht frei verfügbares)
Die benötigen kein Visual C++. Sie benötigen nur ein Satz von
Headerdateien und Libs, mit denen du auf die Windows-API zugreifen
kannst.
http://msdn.microsoft.com/en-us/library/aa363858(VS.85).aspx
Du brauchst das Windows SDK dafür. (Soweit ich weiß)
Hallo Simon K.,
ich glaube ich werde es in C# lösen. Da muss ich mich zwar drin
einarbeiten aber es ist Java (meiner bevorzugten Lösung) auf den ersten
Blick gar nicht so unähnlich...
Zudem gibt es da eine kostenlose grafische IDE die direkt eine Klasse
"SerialPort" bereitstellt. Klappte auf Anhieb :-)
Schöne Grüße,
Alex