为什么一些命令到串口接受正确响应而其他命令不接受 [英] Why some commands to serial port are accepted with a right respond and other not

查看:105
本文介绍了为什么一些命令到串口接受正确响应而其他命令不接受的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当点击按钮打开端口时,此按钮设置波特率设置,设置端口名称以连接设备,并创建evendhandler发送命令并从端口接收数据响应如下



serialPortForApp.DataReceived + = new SerialDataReceivedEventHandler(SerialPortInApp_DataReceived);



使用这些设置按钮LoggedDataBtn_Click与开放端口按钮设置和datarecieved事件处理程序完美配合。但GLPBtn_Click根本没有wotk和应用程序崩溃。





但是按钮GLPBtn_Click如果OpenPortBtn_Click中的删除事件datarecieved句柄并且我删除完成SerialPortInApp_DataReceived工作正常但我的LoggedDataBtn_Click停止工作



我的问题是:

如何管理任何命令的datarecieved处理程序串口并得到正确的响应。



感谢您的任何建议和解答



我的尝试:



when a click button open port this button set the baud settings, set port name to connect with device, and create evendhandler to send command and receive data responds from port as follow

serialPortForApp.DataReceived += new SerialDataReceivedEventHandler(SerialPortInApp_DataReceived);

with these settings button LoggedDataBtn_Click works perfect with the open port button settings and with datarecieved eventhandler. but GLPBtn_Click doesnt wotk at all and application crashes.


But button GLPBtn_Click if a delete event datarecieved handle in the OpenPortBtn_Click and I delete complete the SerialPortInApp_DataReceived works perfect but my LoggedDataBtn_Click stop working

My Question is:
how to manage the datarecieved handler for any command that goes to the serial port and get responds properly.

Thank you for any suggestions and answers

What I have tried:

private void LoggedDataBtn_Click(object sender, EventArgs e)
{
//Display request command sent to device in the ShowDataInScreenTxtb

ShowDataInScreenTxtb.Clear();
ShowDataInScreenTxtb.Enabled = true;

//When LoggDataBtn is pressed it Send the command LogToShow to the instrument
string LoggedDataTOshow = "?R\r\n";
string commForMeter = string.Format(LoggedDataTOshow);

try
  {
    if (serialPortForApp.IsOpen)
    {
       Thread.Sleep(2000);
       loggedData.serialPortForApp.Write(LoggedDataTOshow);
     }
   }
   catch (Exception)
   {
     ShowDataInScreenTxtb.Text = "TimeOUT Exception: Error for requested command";
   }
 }

 private void GLPBtn_Click(object sender, EventArgs e)
{
//send command to instrument

 string GLPCommand = "?G\r\n";
string GLPToShow = String.Format(GLPCommand);

 try
 {
   if (serialPortForApp.IsOpen)
   {             
    Thread.Sleep(2000);
    //write command to port
    serialPortForApp.Write(GLPToShow);
     }
 catch
     {
    //MessageBox error if data reading is not posible
    ShowDataInScreenTxtb.Text = "TimeOUT Exception";
   }


            while (DataToSetandGet != "ENDS\r")
            {

                serialPortForApp.Write("\r");
                DataToSetandGet = serialPortForApp.ReadExisting();
 ShowDataInScreenTxtb.AppendText(glpShowInMainForm.DataToSetandGet.Replace("\r", "\r\n"));
}
}


 private void OpenPortBtn_Click(object sender, EventArgs e)
        {
            //Connect Port Button allows to connect the current device base on Port and Raud settings
            try
            {
                if (PortIndexCbx.Text == "" || BaudRateIndexCbx.Text == "")
                {
                    ShowDataInScreenTxtb.Text = "Set Up your Port Settings";                    //Error message to user to set up port connection if empty selection
                }
                else
                {
                    //Get Serial Port index settings to display for users 
                    serialPortForApp.PortName = PortIndexCbx.Text;                       //ComboBox1.Text set to PortName

                    //Display to users the settings for the Baud Rate that will be listened by the Port

                    serialPortForApp.BaudRate = Convert.ToInt32(BaudRateIndexCbx.Text);      //Baudrate is set (fix.) in ComboBox2.Text

                    // Meter default setting listened through the port DataBits, Parity, StopBits,Handshake
                    serialPortForApp.DataBits = 8;                                    
                    serialPortForApp.Parity = Parity.None;                            
                    serialPortForApp.StopBits = StopBits.One;                         
                    serialPortForApp.Handshake = Handshake.XOnXOff;                   

                    //Open serial Port to allow communication between PC and device 
                    //DataReceived Implemented to read from Port sending and recieving the buttons command requested
//To receive data, we will need to create an EventHandler for the "SerialDataReceivedEventHandler"
  serialPortForApp.DataReceived += new SerialDataReceivedEventHandler(SerialPortInApp_DataReceived);

                    
                    serialPortForApp.ReadTimeout = 500;
                    serialPortForApp.WriteTimeout = 500;


                    //Once Serial port and baud rate setting are set, connection is ready to use 
                    //Open serial port for communication between device and PC
                    serialPortForApp.Open();

                    //Connect and close connection buttons Behaviour
                    progressBarPortAccess.Value = 100; 
                    OpenPortBtn.Enabled = false;                    
                    ClosePortBtn.Enabled = true;                     
                    ShowDataInScreenTxtb.Enabled = true;
                    ShowDataInScreenTxtb.Text = "";

                    
                    LoggedDataBtn.Enabled = true;                  
                    DataReadingStatusLbl.Text = "Port is Connected";
                }
            }
            catch (UnauthorizedAccessException)
            {
                ShowDataInScreenTxtb.Text = "Unauthorized Port Access";
            }

        }


 private void SerialPortInApp_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
 try
 {

 // SerialPort1_DataReceived handles to recieve data from device connected to Port
Thread.Sleep(500);
 DataToSetandGet = serialPortForApp.ReadExisting();
  if (this.InvokeRequired)
   {
Invoke(new MethodInvoker(delegate { ShowDataInScreenTxtb.AppendText(DataToSetandGet.Replace("\r", "\r\n")); }));
}
 }
  catch (Exception)
{
ShowDataInScreenTxtb.Text = "TimeOUT Exception: Error Port communincation";
}
 }

推荐答案

接收的数据处理程序在另一个线程中执行。这就是为什么你必须使用调用来更新文本框的内容。



类似适用于用于存储接收到的字符串的 DataToSetandGet 变量。你不能在其他线程中使用它(比如你的 GLPBtn_Click()函数中的GUI /主线程)而不确保只有一个线程同时访问它。



你也在上面的函数和收到的处理程序中调用 serialPortForApp.ReadExisting()。这也行不通。问问自己:执行了两个读取调用中的哪一个?



解决方案是只从串行端口读取一个函数。这通常是收到的处理程序。如果必须从另一个线程访问收到的字符串,则必须使用共享变量并使用适当的锁定机制访问它。最简单的解决方案是编写一个函数,将接收到的字符串作为参数,并使用接收到的处理程序中的 Invoke()进行调用。在该函数中,您可以更新文本框并将数据用于其他目的。



处理before send命令特定请求的一种技术是实现有限状态机 - 维基百科 [ ^ ]。一个简单的例子:

  • 状态是空闲的(可以发送命令)

  • 发送命令:状态正在等待响应(可选择特定回复)

  • 收到答复:州有答案

  • 已处理答案:州闲置(可以发送下一个命令)

The data received handler is executed in another thread. That is why you have to use Invoke to update the content of your text box from.

Similar applies to the DataToSetandGet variable used there to store the received string. You can't use it from other threads (like the GUI / main thread as in your GLPBtn_Click() function) without ensuring that only one thread accesses it at the same time.

You are also calling serialPortForApp.ReadExisting() in the above function and the received handler. That won't work too. Ask yourself: Which of the two read calls is executed?

The solution is to have only one function reading from the serial port. That is usually the received handler. If you have to access the received string from another thread, you have to use a shared variable and access it with appropriate locking mechanisms. The simplest solution would be writing a function that gets the received string as parameter and is called using Invoke() from the received handler. From within that function you can then update your text box and use the data for other purposes as well.

One technique to handle requests specific to a before send command is implementing a Finite-state machine - Wikipedia[^]. A simple example:
  • State is idle (can send command)
  • Sending a command: State is waiting for response (optionally a specific response)
  • Answer received: State is has answer
  • Answer processed: State is idle (can send next command)


嗨每一个



我通过使用Enums来解决这个问题



我创建了一个(公共枚举AppButtonState:byte),并在我的SerialPortInApp_DataReceived事件中声明了该调用。因此,每次按钮发送命令时,首先由枚举进行评估。它为我希望SerialPortInApp_DataReceived事件读取和响应的每个命令创建一个独立的调用。



谢谢
hi every one

I solve this by using Enums

I create a (public enum AppButtonState : byte) and I declared that call in my SerialPortInApp_DataReceived event. So every time a button send a command this is first assessed by the enum. It creates an independent call for each command that I want SerialPortInApp_DataReceived event to read and respond.

thank you


这篇关于为什么一些命令到串口接受正确响应而其他命令不接受的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆