Laden...

Daten seriell von Mikrocontroller auslesen

Erstellt von CSharpEntwickler vor einem Jahr Letzter Beitrag vor einem Jahr 812 Views
C
CSharpEntwickler Themenstarter:in
4 Beiträge seit 2022
vor einem Jahr
Daten seriell von Mikrocontroller auslesen

Hallo,

ich habe eine kleine GUI in C# erstellt. Mit dieser Gui werden/sollen Daten vom Mikrocontroller ausgelesen werden.
Es handelt sich um serielle Daten. Im Programm nutze ich die Bibliothek Syste.IO.Ports.


        private void btn_Init_Click(object sender, EventArgs e)
        {
            mySerialPort = new SerialPort("COM5");

            mySerialPort.BaudRate = 115200;
            mySerialPort.Parity = Parity.None;
            mySerialPort.StopBits = StopBits.One;
            mySerialPort.DataBits = 8;
            mySerialPort.Handshake = Handshake.None;
            mySerialPort.ReadTimeout = 100;

            //mySerialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
            mySerialPort.Open();

            fifo_peekonly = new ConcurrentQueue<byte>();
            fifo_queue = new BlockingCollection<byte>(fifo_peekonly);

            mySerialPort.DataReceived += (sender, e) =>
            {
                byte[] buffer = new byte[mySerialPort.BytesToRead];
                if (!mySerialPort.IsOpen)
                {
                    throw new System.InvalidOperationException("Serial port is closed.");
                }
                mySerialPort.Read(buffer, 0, mySerialPort.BytesToRead);
                foreach (var b in buffer)
                    fifo_queue.Add(b);
            };

            Thread thr = new Thread(new ThreadStart(mythread));
            thr.Start();
        }

        public byte GetByteFromDevice()
        {
            byte b;
            b = fifo_queue.Take();
            return b;
        }

       public void mythread()
        {
            while(true)
            {
                if(recvByteCnt == 0)
                {
                    recvLength = GetByteFromDevice();
                    recvByte = recvLength;
                }
                else
                {
                    recvByte = GetByteFromDevice();
                }

                recvBuffer[recvByteCnt] = recvByte;

                if (recvByteCnt >= recvLength)
                {
                    recvFlag = 1;
                    recvByteCnt = 0;

                    Debug.Write("Complete\n");
                }
                else
                {
                    if (recvByteCnt == 0)
                    {
                        Debug.Write("Start timeout\n");
                    }
                    recvByteCnt++;
                }

                //Debug.Write(recvByte.ToString() + "\n");
            }
        }


Nun fehlt quasi noch eine TimeOut Mechanismus. Dieser soll im thread nach der if Bedingung "if (recvByteCnt == 0)" eingesetzt werden.
Wie würde man solch eine Funktionalität realisieren?

C
CSharpEntwickler Themenstarter:in
4 Beiträge seit 2022
vor einem Jahr

Da hätte ich noch eine weitere Frage.

Wo ist der Unterscheid zwischen ConcurrentQueue und BlockingCollection?


     fifo_peekonly = new ConcurrentQueue<byte>();
     fifo_queue = new BlockingCollection<byte>(fifo_peekonly);

Dies habe ich von folgenden Link herausgezogen für mein Projekt:
c-sharp-serial-port-sometimes-missing-data/45432817#45432817

16.807 Beiträge seit 2008
vor einem Jahr

Wo ist der Unterscheid zwischen ConcurrentQueue und BlockingCollection?

Sowas kannst Du viel schneller beantwortet bekommen, wenn Du einfach in die Docs schaust 🙂

ConcurrentQueue<T> Class (System.Collections.Concurrent)
BlockingCollection<T> Class (System.Collections.Concurrent)

  • ConcurrentQueue ist eine Liste mit Thread-Sicherheit
  • BlockingCollection eine Liste für Blocking und Bounding, zB für den Producer Consumer Pattern relevant

Also zwei vollständig verschiedene Konzepte.

Um die Frage zu beantworten:

Nun fehlt quasi noch eine TimeOut Mechanismus.
Wie würde man solch eine Funktionalität realisieren?

Dazu musst Du Dein Quellcode vollständig umbauen.

Threads sind ein Betriebssystem-Konzept, das Du nicht abbrechen kannst. Ein Thread kann nur abgeschossen werden und hat keinerlei Cancel-Möglichkeit (weil es eben vom Betriebssystem verwaltet wird).
Daher arbeitet man in .NET nur in sehr seltenen Fällen mit Threads, sondern man arbeitet mit Tasks.
Tasks sind eine Abstraktion von Threads; haben volle async/await-Support und können auch Cancel (zB durch ein Timeout) unterstützen.

C
CSharpEntwickler Themenstarter:in
4 Beiträge seit 2022
vor einem Jahr

Hallo Abt, danke für deinen Post.

Ich habe für meine Anwendung BlockingCollection benutzt. Im Thread lese ich dann die bytes mit der Methode GetByteFromDevice aus.

Also wäre in meinem Fall die andere Variante geeigneter?

16.807 Beiträge seit 2008
vor einem Jahr

4 Minuten zwischen meinem Post und Deiner Antwort. Wieso liest Dir denn die beiden Sachen nicht mal wenigstens 15 Minuten durch, um die beiden Dinge zu verstehen?

Man kann beides nutzen; kommt halt drauf an, wie man das umsetzt.
Eine BlockingCollection verwendet man - wie man in der Doku sehen kann - eigentlich mit TryAdd und GetConsumingEnumerable.
Aber um deren Anwendung zu verstehen, sollte man halt die Doku lesen, sonst wirds schwer...

C
CSharpEntwickler Themenstarter:in
4 Beiträge seit 2022
vor einem Jahr

Hallo,

ich habe nun den Empfang der Daten in einer separaten Task realisiert.
Auch das Versenden von Daten läuft in einem anderen Task.
Nun ist es so wenn ich Daten versende, dann läuft das mit dem Empfangen von Daten schief.


           bool SendFlagLedOn = false;
           bool SendFlagLedOff = false;           

           Task recvTask = new Task(new Action(OnRxFrames));
            recvTask.Start();

            Task sendTask = new Task(new Action(OnTxFrames));
            sendTask.Start();


private void OnRxFrames()
        {
            while (true)
            {
                if (recvByteCnt == 0)
                {
                    recvLength = GetByteFromDevice();
                    recvByte = recvLength;
                }
                else
                {
                    recvByte = GetByteFromDevice();
                }

                recvBuffer[recvByteCnt] = recvByte;

                if (recvByteCnt >= recvLength)
                {
                    recvFlag = 1;
                    recvByteCnt = 0;

                    Debug.Write("Complete\n");

                    this.Invoke(new EventHandler(DoUpDate));
                }
                else
                {
                    recvByteCnt++;
                }
            }
        }

        private void OnTxFrames()
        {
            while (true)
            {
                if(SendFlagLedOn == true)
                {
                    SendFlagLedOn = false;

                    byte[] txData = new byte[5];
                    txData[0] = 4;
                    txData[1] = 1;
                    txData[2] = 2;
                    txData[3] = 3;
                    txData[4] = 4;

                    string str = Encoding.Default.GetString(txData);
                    mySerialPort.Write(str);
                }

                if (SendFlagLedOff == true)
                {
                    SendFlagLedOff = false;

                    byte[] txData = new byte[5];
                    txData[0] = 4;
                    txData[1] = 2;
                    txData[2] = 2;
                    txData[3] = 3;
                    txData[4] = 4;

                    string str = Encoding.Default.GetString(txData);
                    mySerialPort.Write(str);
                }
            }
        }

4.931 Beiträge seit 2008
vor einem Jahr

Hallo,

was genau läuft denn schief?

Ein großer Fehler ist jetzt bei dir die erzeugten Endlosschleifen (schau mal im TaskManager auf die Prozessor-/Kernauslastung)!
Du mußt diese wartend oder blockierend machen (oder ist GetByteFromDevice() blockierend, da du ja von ConcurrentQueue und BlockingCollection geschrieben hast?). Zumindestens für die Sendeschleife sollte ein Thread.Sleep(x) hin (bei Benutzerauslösung der Flags reichen einige 100ms).

PS: Sofern du konstante Daten versendest, kann du einfach z.B. byte[] txData = { 4, 1, 2, 3, 4 }; schreiben (und sogar den string str einmalig vor der Schleife erzeugen - bzw. 2 für die beiden Fälle).

16.807 Beiträge seit 2008
vor einem Jahr

Der eigentliche Queue Mechanismus fehlt ja nun komplett, oder übersehe ich etwas?
Weil, das war ja eigentlich schon eine gute Idee, die auch so weit verbreitet ist.

Mit der BlockingCollection, die man hier auch verwenden kann, kann man das blockierende Verhalten der Tasks auch erreichen.
Flow wäre dann:

  • Blocking Collection erzeugen
  • Task erzeugen, der ankommende Daten verarbeitet (weitersenden, oder was auch immer), dabei GetConsumingEnumerable verwenden!
  • Task erzeugen, der ankommende Daten annimmt und in die Blocking Collection schreibt (TryAdd)

Sobald keine Daten mehr kommen sollen, CompleteAdding ausführen, sodass der verarbeitende Task sich, jedoch alle Daten in der Collection noch verarbeitet werden.
Mit einem CancellationToken, der bei der Verarbeitung abgeprüft wird, kann das Canceln erreicht werden - und eben das Enforce durch CompleteAdding.