JRinfo Geschrieben 28. April 2009 Geschrieben 28. April 2009 Hallo Forum, ich bin das erste Mal in einem Forum. Bei mir geht es um folgendes: ich habe ein Messsystem für Werkzeuge (CNC) mit folgenden Ausgängen entwickelt: RS232, USB, LPT. Eine einfache Software in MS Visual C# 2008 soll die RS232 und USB verwalten. RS232 klappt, aber USB hat noch ein Problem. Ich verwende einen FTDI FT232R USB Controller. Als Virtueller COM-Port funktioniert die USB. Für Visual C# existiert die Datei FTD2XX_NET.dll. Nun möchte ich beim Empfang von Daten einen FT_EVENT_RXCHAR auslösen – und genau da haperts. Wie programmiere ich einen EVENT? Über toolStripButton2_Click öffne ich die USB. Mit einer do – while Schleife konnte ich feststellen, das Daten ankommen. Bis auf ein EVENT fuktioniert alles. Was habe ich bei der Aktivierung das EVENT falsch gemacht ???? Grüße JRinfo using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Windows; using System.IO.Ports; using System.IO; using System.Runtime.InteropServices; using FTD2XX_NET; using System.Threading; namespace WindowsFormsApplication1 { public partial class Form1 : Form { string daten; string readData; private string strPrintText; FTDI.FT_STATUS ftStatus = FTDI.FT_STATUS.FT_OK; FTDI myFtdiDevice = new FTDI(); //einen EVENT erzeugen!!!!!!!! private EventWaitHandle DataReceived = new EventWaitHandle(false, EventResetMode.AutoReset); // Handle for data received events BackgroundWorker DataHandler = new BackgroundWorker(); // Second thread for processing data received public Form1() { InitializeComponent(); DataHandler.DoWork += new DoWorkEventHandler(DataHandler_DoWork); } private void Form1_Load(object sender, EventArgs e) { string[] ports = SerialPort.GetPortNames(); foreach (string comname in ports) { toolStripDropDownButton1.DropDownItems.Add(comname); } } private void toolStripButton2_Click(object sender, EventArgs e) // USB öffnen { toolStripButton1.Checked = false; if (serialPort1.IsOpen) serialPort1.Close(); if (toolStripButton2.Checked == true) { ftStatus = myFtdiDevice.OpenByDescription("WASI-MESSSYSTEM"); if (ftStatus == FTDI.FT_STATUS.FT_OK) { ftStatus = myFtdiDevice.SetBaudRate(115200); if (ftStatus == FTDI.FT_STATUS.FT_OK) { ftStatus = myFtdiDevice.SetDataCharacteristics(FTDI.FT_DATA_BITS.FT_BITS_7, FTDI.FT_STOP_BITS.FT_STOP_BITS_2, FTDI.FT_PARITY.FT_PARITY_EVEN); if (ftStatus == FTDI.FT_STATUS.FT_OK) { if (!DataHandler.IsBusy) DataHandler.RunWorkerAsync(); //einen EVENT erzeugen!!!!!!!! ftStatus = myFtdiDevice.SetEventNotification(FTDI.FT_EVENTS.FT_EVENT_RXCHAR, DataReceived); if (ftStatus != FTDI.FT_STATUS.FT_OK) { } } } } } else { if (myFtdiDevice.IsOpen == true) { myFtdiDevice.Close(); } } } void DataHandler_DoWork(object sender, DoWorkEventArgs e) { uint numBytes = 0; if ((ftStatus = myFtdiDevice.GetRxBytesAvailable(ref numBytes)) == FTDI.FT_STATUS.FT_OK) { if ((ftStatus = myFtdiDevice.Read(out readData, numBytes, ref numBytes)) == FTDI.FT_STATUS.FT_OK) { daten = daten + readData; } } } private void toolStripDropDownButton1_DropDownItemClicked(object sender, ToolStripItemClickedEventArgs e) { toolStripButton1.Text = e.ClickedItem.Text; } private void toolStripButton1_Click(object sender, EventArgs e) { toolStripButton2.Checked = false; if (toolStripButton1.Checked == true) { if (myFtdiDevice.IsOpen == true) { myFtdiDevice.Close(); } if (!serialPort1.IsOpen) { try { serialPort1.BaudRate = 9600; serialPort1.DataBits = 7; serialPort1.StopBits = StopBits.Two; serialPort1.Parity = Parity.Even; serialPort1.Handshake = Handshake.None; serialPort1.Open(); } catch { string message = serialPort1.PortName + " kann nich geöffnet werden!\nWird vermutlich von einer anderen Anwendung benutzt"; MessageBoxIcon icon = MessageBoxIcon.Information; MessageBoxButtons button = MessageBoxButtons.OK; MessageBox.Show(message, "Information", button, icon); toolStripButton1.Checked = false; } } } else { if (serialPort1.IsOpen) serialPort1.Close(); } } private void toolStripButton5_Click(object sender, EventArgs e) { this.richTextBox1.Clear(); daten = ""; } private void DisplayText(object sender, EventArgs e) { richTextBox1.AppendText(daten); } private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e) { int C = serialPort1.BytesToRead; int X; char chr; for (X = 0; X < C; X++) { chr = Convert.ToChar(serialPort1.ReadByte()); if (chr == '%') { this.Invoke(new EventHandler(DisplayText)); } else daten = daten + chr; } } private void toolStripButton7_Click(object sender, EventArgs e) { printDialog1.Document = printDocument1; if (printDialog1.ShowDialog() == DialogResult.OK) { strPrintText = richTextBox1.Text; printDocument1.Print(); } } private void printDocument1_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e) { StringFormat stringFormat = new StringFormat(); Pen blackPen = new Pen(Color.Black, 3); RectangleF rectFPapier, rectFText; rectFPapier = e.MarginBounds; rectFText = RectangleF.Inflate(rectFPapier, 0, -2 * richTextBox1.Font.GetHeight(e.Graphics)); stringFormat.Trimming = StringTrimming.Word; e.Graphics.DrawString(strPrintText, richTextBox1.Font, Brushes.Black, rectFText, stringFormat); } } } Zitieren
SpamBot Geschrieben 8. Mai 2009 Geschrieben 8. Mai 2009 hi, So wie ich das sehe, ist das kein 'regulärer' .NET event sondern eher ein 'Thread event' (keine Ahnung wie das richtig heist...). Ich habe das folgendermaßen gelöst: private AutoResetEvent recvDataEvent; private BackgroundWorker worker; void open() { COM.OpenByIndex(smartBoxIndex); COM.SetBaudRate(115200); COM.SetDataCharacteristics(FTDI.FT_DATA_BITS.FT_BITS_8,FTDI.FT_STOP_BITS.FT_STOP_BITS_1,FTDI.FT_PARITY.FT_PARITY_NONE); COM.SetFlowControl(FTDI.FT_FLOW_CONTROL.FT_FLOW_NONE,0,0); recvDataEvent = new AutoResetEvent(false); COM.SetEventNotification(FTDI.FT_EVENTS.FT_EVENT_RXCHAR,recvDataEvent); worker = new BackgroundWorker(); worker.DoWork += new DoWorkEventHandler(worker_DoWork); worker.RunWorkerAsync(); } void worker_DoWork(object sender, DoWorkEventArgs e) { uint bytesToRead = 0; uint bytesRead = 0; while (true) { // wait until event is fired this.recvDataEvent.WaitOne(); // try to recieve data now if(FTDI.FT_STATUS.FT_OK == this.COM.GetRxBytesAvailable(ref bytesToRead)) { byte[] buffer = new byte[bytesToRead]; if(FTDI.FT_STATUS.FT_OK == this.COM.Read(buffer,bytesToRead,ref bytesRead)) { if (bytesToRead != bytesRead) { Log("Data length error!"); } foreach (byte b in buffer) { recvBuffer.Enqueue(; } Log(ByteArrayToHexString(buffer)); } } } } [/code] evtl nicht der eleganteste Weg, aber immerhin klappt es Eine Frage am Rande: Dein Projekt scheint so ähnlich zu sein wie das, was ich im Moment bearbeite. Nur habe ich ein Problem: Gibt es eine Möglichkeit die Zeit zu bestimmen an der die Daten im Puffer ankommen (Abweichung max. 0,5 ms) ? (bezogen auf die SerialPort klasse meine ich jetzt, da ich auch beides implementiere) Zitieren
kross Geschrieben 13. Mai 2009 Geschrieben 13. Mai 2009 (bearbeitet) Mal ne Frage, die FTDI-Klasse die da verwendet wird ist das zufällig die welche es unter C# Examples direkt oben als managed .NET wrapper gibt? Falls ja, wie geht ihr mit den Mesageboxen um? Keine Ahnung was sich FTDI dabei gedacht hat, aber wenn man sich die einzelnen Funktionen mal im Reflector anschaut, sehen die immer so inetwa aus public FT_STATUS functionname(params) { tests ob ausführung ok ist functionscode if (mögliche fehlerquelle 1) { MessageBox.Show("fehlertext1"); } if (mögliche fehlerquelle 2) { MessageBox.Show("fehlertext 2"); } ... } return ft_status; } und mal ehrlich ich will doch nicht das eine DLL dem User Messageboxen entgegenschmeißt, anstatt mir eine Exception hochzugeben, über deren Behandlung ich mir dann selbst Gedanken machen kann und falls nötig eine messagebox erzeuge. Zum erzeugen von Events in meinem aktuellen Projekt auf Arbeit arbeite ich auf mit nem FTDI-Chip über den Chip läuft ein selbst definiertes Protokoll das auf C# Seite über einen Backgroundworker abgearbeitet wird. Dabei wird nur ein Stream ansich benötigt und dann Streamspezifische funktionen verwendet. So kann man USB und Serial auf einmal erschlagen. Eine FTDI-Streamklasse implementiert read/write. Anschließend liest der Protokollhandler ankommende Daten in einen Puffer und sobald ein gültiges Paket erkannt wurde schmeißt er ein Event. Bearbeitet 13. Mai 2009 von kross Zitieren
SpamBot Geschrieben 14. Mai 2009 Geschrieben 14. Mai 2009 So kann man USB und Serial auf einmal erschlagen. Benutzt du auch die Serial Port Klasse? Kannst evtl. mal ein kleines Schnipsel posten? Zu deinem Problem: Also ich denke da hast du was falsch verstanden, die DLL bietet lediglich keine Exceptions, aber wirft dir natürlich auch keine Messageboxen ins Gesicht Die Fehlerbehandlung erfolgt über so ne Art Statusenum: ftStatus = Mach_Irgendwas(bla); if ( ftStatus != FTDI.FT_STATUS.FT_OK ) { ; } else { Debug.Log("alles klar"); } mfg Zitieren
kross Geschrieben 15. Mai 2009 Geschrieben 15. Mai 2009 Du verstehst da etwas falsch. ftStatus = Mach_Irgendwas(bla); In dem Fall führt ja Mach_Irgendwas code innerhalb der FTDI-Klasse aus, die du als DLL hast, und genau darin sind MessageBoxen für bestimmte Fehler fest im Code... zum code erstelle einfach 2 Klassen FTDI und ComportStream die du von System.IO.Stream ableitest VS bietet dir dann von allein an die abstrakten Funktionen zu implementieren. dann SerialPort m_comport = null; public ComPortStream(SerialPort _comport) { m_comport = _comport; } public override int Read(byte[] buffer, int offset, int count) { return m_comport.Read(buffer, offset, count); } public override void Write(byte[] buffer, int offset, int count) { m_comport.Write(buffer, offset, count); } private static Dictionary<FTDI, FTDIStream> m_instances; public static FTDIStream getInstance(FTDI _ftdi) { if (m_instances == null) m_instances = new Dictionary<FTDI, FTDIStream>(); if(m_instances.ContainsKey(_ftdi)) { return m_instances[_ftdi]; } else { FTDIStream stream = new FTDIStream(_ftdi); m_instances.Add(_ftdi, stream); return m_instances[_ftdi]; } } public static void removeInstance(FTDI _ftdi) { if(m_instances.ContainsKey(_ftdi)) { m_instances.Remove(_ftdi); } } private FTDI m_ftdi; public FTDIStream(FTDI _ftdi) { if (_ftdi == null) throw new NullReferenceException(); m_ftdi = _ftdi; } public override void Flush() { m_ftdi.Purge(FTD2XX_NET.FTDI.FT_PURGE.FT_PURGE_RX); m_ftdi.Purge(FTD2XX_NET.FTDI.FT_PURGE.FT_PURGE_TX); } public override int Read(byte[] buffer, int offset, int count) { if (buffer == null) throw new NullReferenceException(); if (buffer.Length < count) throw new Exception("Buffer to small."); byte[] tempbuffer = new byte[offset + count]; uint bytesread = 0; m_ftdi.Read(tempbuffer, (uint)tempbuffer.Length, ref bytesread); Buffer.BlockCopy(tempbuffer, offset, buffer, 0, (int)((count + offset >= bytesread) ? ((bytesread - offset > 0) ? bytesread - offset : 0) : count)); return (int)((count + offset >= bytesread) ? ((bytesread - offset > 0) ? bytesread - offset : 0) : count); } public override void Write(byte[] buffer, int offset, int count) { if (buffer == null) throw new NullReferenceException(); if (offset > buffer.Length) throw new Exception("Offset greater then Buffersize"); byte[] writebuffer = new byte[buffer.Length - offset]; Buffer.BlockCopy(buffer, offset, writebuffer, 0, writebuffer.Length); uint byteswritten = 0; FTDI.FT_STATUS status = m_ftdi.Write(writebuffer, count, ref byteswritten); } public override long Length { get { uint retval = 0; m_ftdi.GetRxBytesAvailable(ref retval); return retval; } } der fdtistram ist schon deutlich mehr getestet als die comportvariante die hab ich nur mal halbherzig implementiert Zitieren
SpamBot Geschrieben 16. Mai 2009 Geschrieben 16. Mai 2009 ftStatus = Mach_Irgendwas(bla); In dem Fall führt ja Mach_Irgendwas code innerhalb der FTDI-Klasse aus Hm... also ich hab jedenfalls noch keine Messagebox gesehn. Egal welche Funktion ausgeführt wurde. Und wie machst du das mit den Timings? Ich krieg keine Zeitmessung < 10 ms hin. Ist das überhaupt möglich? Ich weiss ja nämlich garnicht wann die Daten in den Buffer geschrieben werden :? Zitieren
kross Geschrieben 18. Mai 2009 Geschrieben 18. Mai 2009 Für mich ist nur wichtig das die Daten überhaupt kommen. Über den USB-Port werden Kundenspezifische Konfigurationsdaten geschrieben oder das Eventlog ausgelesen oder die Firmware upgedated aber nichts timingspezifisches wie ankommende Messwerte ohne eigenes Timestamp. Wenn ich Zeitangaben bräuchte hätte ich das Glück die Firmware gleich mitzuprogrammieren. Wenn das nicht der Fall wäre würde ich dann aber nix anderes als Möglichkeit sehen als in einem Backgroundworker den Puffer permanent schnell auszulesen mir das aktuelle Datetime des ankommens der Daten zu merken mit den Daten zusammen in die Eventargs des Receiveevents packen und dann oberhalb zu schauen ob ich mit den Timespans zwischen meinen Eventargs auf sinnvolle Werte komme. Zitieren
SpamBot Geschrieben 25. Mai 2009 Geschrieben 25. Mai 2009 DateTime ist leider zu ungenau,... habs jetzt mit eine Tickcount für jedes empfangene byte versucht. Hat aber immernoch das gleiche problem, allerdings wesentlich seltener. Messgenauigkeit liegt nun etwa im 3-5 ms bereich. mfg Zitieren
Empfohlene Beiträge
Dein Kommentar
Du kannst jetzt schreiben und Dich später registrieren. Wenn Du ein Konto hast, melde Dich jetzt an, um unter Deinem Benutzernamen zu schreiben.