locked
Is there a way simple method of doing Half Duplex Comms in the Serial Port Class? RRS feed

  • Question

  • Hi All,

    I have been asking about having to send data out of the Serial port twice to get a responce from a device, the serial port latching the data etc. I have found this is due to the processor being Half Duplex (one wire) and I am now trying to get my program to handle the Half Duplex Comms like HyperTerminal.  I can if I send the commands twice get the unit to reply and then clear with the following:

     myComPort.DiscardInBuffer();
                myComPort.DiscardOutBuffer();
                rtbIncomingData.Text = "";
                rtbIdeaDisplay.Text = "";

    While this works having to send the data twice is asking for trouble and I would like a way to do it properly using C#(VS 2008) I tried reading

    http://www.innovatic.dk/knowledg/SerialCOM/SerialCOM.htm 

    This is more geared toward VB and is a little vague on the duplex situation (but covers most aspects well), can any one help, please!

    Glenn

       

    • Moved by Mike Feng Tuesday, December 18, 2012 2:43 AM (From:.NET Base Class Library)
    Friday, December 14, 2012 1:11 PM

All replies

  • Read the sections on RTS, DTR, and Flow control.   RTS and CTS are the hardware controls and should be disables They are archaic.  Setting them high I would recommend like in the book.  Also Software Flow control should be set to none.

    Yo can manually change these settings using the Device Driver Properties for each com port.  Yo umay want to look at the default options in the Device Driver so you will know the different setting that software can use.  Any of the Device Driver settings can also be set using the Net Library


    jdweng

    Friday, December 14, 2012 1:37 PM
  • Ah, OK, at the moment the Handshake is set to none as is the parity.  The other thing is I am connecting to the board via an FTDI cable that appears as a virtual com port, the device is connected to the end of it via a mono jack plug.

    Glenn  

    Friday, December 14, 2012 2:34 PM
  • If it works with a hyperterminal by typing once it should also work in software provided the Hyperterminal settings and the software settings are identical.  The problem could be in your software.  The Net library does nothing except may add 100msec delay because it runs off the timer tick interupt.  The two reads may just be adding a little bit delay. 

    You must test that a byte is available before reading the data.  You may have an exception handler that giving masking a problem like reading data when there is no data.  Make sure you uaren't getting into any exception handlers.  You should send a command and then wait for the response from the instrument before sending another command.  Either check for an end of line character or make sure you read the proper number of bytes from the instrument before allowing your software to send additional commands.


    jdweng

    Friday, December 14, 2012 3:11 PM
  • I am sorry about this,   but I am using ReadExisting() to read the data at the serial port as it seemed to be the most reliable.  So from your description I think I should be using the Read() method,  I have tried this in the past but run into problems compiling it. Another method I have used is:

       Int32 recievedByte;
                if (myComPort.BytesToRead > 0)
                {
                    recievedByte = myComPort.ReadByte();
                    rtbIncoming.Text = recievedByte.ToString();
                }
                else MessageBox.Show("No Data Available");
            }

    With little success is the Read() the correct way to go?

    Glenn

      

    Friday, December 14, 2012 5:53 PM
  • "I have been asking about having to send data out of the Serial port twice to get a responce from a device"

    Does the SerialPort time out before the DataReceived event is fired after sending the first data? 

    Friday, December 14, 2012 6:50 PM
  • Hi Glenn,

    Welcome to the MSDN Forum.

    Here is a forum dedicated for network issues, I have moved this thread to that forum.

    Thank you for your understanding and support.

    Best regards,


    Mike Feng
    MSDN Community Support | Feedback to us
    Develop and promote your apps in Windows Store
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Tuesday, December 18, 2012 2:42 AM
  • Thats the thing I am expecting the DataReceived event to fire. As soon as the board replies, by spying on the unit I can see it replying however the DataRecieved event does not fire.  I take it that using ReadExisting() is not causing the problems I ask this as I was using it as a non blocking method. 

     try
     {
              InputData = myComPort.ReadExisting();
               if (InputData != String.Empty)
             {
      this.BeginInvoke(new SetTextCallback(SetText), new object[] { InputData });
              ProcessInput();
              }
              else if (InputData == String.Empty)
              {
                   return (null);
              }
        }
         catch (UnauthorizedAccessException)
         {
               MessageBox.Show("Error Caught!!");
         }
    Above is the means I have used many times with no problems. Should I use an access marshal as the DataRecieved is executed on a separate thread? 
        
    Tuesday, December 18, 2012 10:11 AM
  • "Thats the thing I am expecting the DataReceived event to fire. As soon as the board replies, by spying on the unit I can see it replying however the DataRecieved event does not fire."

    If the DataRecieved event doesn't fire in this case, you don't have it wired up.

    Tuesday, December 18, 2012 10:20 AM
  • The BeginInvoke() method is executed when a connection occurs.  In a serial port there isn't yo udon't get a start of a connection unless some control lines are beig used.  I would eliminate the BeginInvoke() method.

    jdweng

    Tuesday, December 18, 2012 10:24 AM
  • I would first check tthat you can see the recieved data in a HyperTerminal Application.  Use Device manager to make sure you are looking at the correct port number.  Also check the properties in the the Device Manager for the device and in Hyperterminal.  Experiment with the different setting and make sure you r program is settting the properties exactly the same as when you get the HyperTerminal to work.  Once you get HyperTerminal working it should be simple to modify your code and get your software working.


    jdweng

    Tuesday, December 18, 2012 10:28 AM
  • Tried it, got comms with Hyperterminal, the issue is when I send a command from my Program the data does not appear at the comm port the way I was anticipating.  It's almost as if I send the command wait a couple of cycles and send a second command (a blank space) I get the data to appear.   This is odd as in the past I have as you suggested run HyperTerm (or similar) up sent commands an got responces and then started to automated the process.  The only thing thats different this time is the system is half duplex.

    Glenn

      
    Tuesday, December 18, 2012 10:48 AM
  •      Hmm,

    If you mean the below I do:

          myComPort.DataReceived +=

    new System.IO.Ports.SerialDataReceivedEventHandler(port_DataReceived);

    This was one the first check points!

    Glenn

    Tuesday, December 18, 2012 10:50 AM
  • You added the event, but did you add the function or are you using the default port_DataReceived() function?  the "+=" just adds the event.

    jdweng

    Tuesday, December 18, 2012 12:10 PM
  • You may have a race condition.  You must wait for all the data to get received before you send another command.  Either you need to terminate all commands with a terminating character like "\n" and then wiat until the "\n" is received befofore sending a 2nd command.  Or send a byte count at the beginning of the message and wait until you receivved all the bytes before you send another command.  Data may not get received in one chunk.  You have to append the individual chunks together using mystr = mystr + bytes[];  So normally when before I send a command I clear the receive buffer (to eliminate the "\n" from previous message) send a command.  Then in the recieve event append all the bytes into a string until the "\n" is received.

    jdweng

    Tuesday, December 18, 2012 12:19 PM
  • 
    

    Hi,

    The event handler is:

        private void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
            {
                Reply_Status = (int)REPLY.YES_REPLY;
             //   InputData = myComPort.ReadExisting();
             ////   rtbIdeaDisplay.SelectionStart = rtbIdeaDisplay.Text.Length;
             // //  rtbIdeaDisplay.ScrollToCaret();
             //   if (InputData != String.Empty)
             //   {
             //       this.BeginInvoke(new SetTextCallback(SetText), new object[] { InputData });
             //   }
             //    //Reply_Status = (int)REPLY.YES_REPLY;
             //    //rtbIdeaDisplay += "\n"+    
            }

    This sets the Enum Reply to yes meaning there is data there, this handled by the Write_Board() function is:

            private string Write_Board(string Data_To_Board)
            {
    
                Pause.Start();
    
                myComPort.Write(Data_To_Board + (char)10 + (char)13);// + (char)13);
                {
    
                    while (Reply_Status == (int)REPLY.NO_REPLY)
                    {
                        NoDataAtPort.Enabled = true;
                    }
                    NoDataAtPort.Enabled = false;
    
                    if (Reply_Status == (int)REPLY.TIMEOUT_REPLY)
                    {
                        Data_From_Board = "TIMEOUT\n";
                        Pause.Stop();
                    }
                    else if (Reply_Status == (int)REPLY.YES_REPLY)
                    {
                        Pause.Stop();
                        //add try catch
                        try
                        {
                            InputData = myComPort.ReadExisting();
                            //myForm1.AccessFormMarshalDelegate1(InputData);
                            //myForm1.AccessFormMarshallDelegate1(InputData);
                            if (InputData != String.Empty)
                            {
                                this.BeginInvoke(new SetTextCallback(SetText), new object[] { InputData });
                                ProcessInput();
                            }
                            else if (InputData == String.Empty)
                            {
                                return (null);
                            }
    
                        }
                        catch (UnauthorizedAccessException)
                        {
                            MessageBox.Show("Error Caught!!");
                        }
    
                    }
                    else
                    {
    
                        Cursor.Current = Cursors.No;
                        Reply_Status = (int)REPLY.NO_REPLY;
                    }
                }
                //    ProcessInput();
                //       Data_From_Board = Data_From_Board + "|||";
    
                //    return (Data_From_Board);
                return ("");
    
            }

    The Board_Write function is called by:

      Reply_Status = (int)REPLY.NO_REPLY;
                Write_Board("/p");

    I was middle of typing this when your email about thread races arrived this method was originally cooked up to avoid race conditions,  by having a single function the you sent data from and it would return with the reply.

    Glenn

    Tuesday, December 18, 2012 12:26 PM
  • I see a couple problems with your code.   I like using waitone instead of a pause.

    1) You should read data in the Read Event so you don't have problems.  I believve the reading data and the event are synchronized

    2) You should be mixing AsyncRead and SyncRead together.  You have an Asynchronous Rad event but you are using ReadExisting which is synchronous.


    jdweng

    Tuesday, December 18, 2012 2:48 PM
  • Umm,

    Can I have clarification on a couple of points, waitone  & Pause(), Pause is the name for

    System.Timers.Timer Pause = new System.Timers.Timer(100);

    is waitone a timer like Pause as the only reference I can seem to find for it is at the MSDN page

    http://msdn.microsoft.com/en-us/library/aa332439(v=VS.71).aspx  I have added it to my Project as a seperate class with the Main renames to MainWait(); I am little unsure of how to call it?

    Sorry!

    Tuesday, December 18, 2012 3:26 PM
  • WaitOne is a semiphore that is part of Threading Class() that doesn't use a timers.  It simply blocks a thread from contiuing until another thread does an unblock.  See webpage below

    http://msdn.microsoft.com/en-us/library/system.threading.waithandle.waitone(v=vs.90).aspx


    jdweng

    Tuesday, December 18, 2012 3:48 PM
  • Yeah, I got that from the description.  It is just I am a little unsure of how to use it. 

    I have it as a serperate Class added to the project in the solution window, (I had to change the name from Main (static void Main()) to  static void MainWait() and tried to use it as follows:

    WaitOne(500);

    Error 1 An object reference is required for the non-static field, method, or property 'object.Equals(object)' C:\Users\glennp\Dropbox\PSION Emmulation\PSION Emmulation\PSION_EmmualtionA\TestBoardComms\WindowsFormsApplication152\WindowsFormsApplication152\Form1.cs 478 13 WindowsFormsApplication152  

    WaitOne.Equal(500);

    gives Error 1 An object reference is required for the non-static field, method, or property 'object.Equals(object)' C:\Users\glennp\Dropbox\PSION Emmulation\PSION Emmulation\PSION_EmmualtionA\TestBoardComms\WindowsFormsApplication152\WindowsFormsApplication152\Form1.cs 477 13 WindowsFormsApplication152

    WaitOne.Equal(1,2); compiles runs but doesn't seem to do very much, neither does increasing the variables by 100 (WaitOne.Equal(100,200); ) The system still needs the Thread Sleep() and other commands (blank spaces sent). 

    Glenn

      

    Tuesday, December 18, 2012 4:14 PM