C#-通过串口从设备获取所有数据,并检测控制字符(ACK、SOH等)

C#-通过串口从设备获取所有数据,并检测控制字符(ACK、SOH等),c#,serial-port,C#,Serial Port,我可以很容易地从设备接收数据的响应,并用Serial.ReadExisting()在文本框中显示。显示数据时,问题是某些控制字符(ACK、SOH、ETX等)无法打印 我试图用下面的代码从串行响应中检测控制字符,但在比较中出现了一些错误 代码是: public bool read_port(ref string rs232data, int timeout) { try { int index_read = 0;

我可以很容易地从设备接收数据的响应,并用Serial.ReadExisting()在文本框中显示。显示数据时,问题是某些控制字符(ACK、SOH、ETX等)无法打印

我试图用下面的代码从串行响应中检测控制字符,但在比较中出现了一些错误

代码是:

public bool read_port(ref string rs232data, int timeout)
    {
        try
        {
            int index_read = 0;
            int total_read = 0;
            int i = 0;
            int delay_ms = 1;
            byte[] buffer = new byte[serialPort1.ReadBufferSize];
            rs232data = "";
            string buffer_str = "";

            if ((serialPort1.IsOpen == false))
            {
                #if DEBUG
                MessageBox.Show("COM port is not opening");
                #endif
                return false;
            }
            do
            {
                Application.DoEvents();
                PauseForMilliSeconds(delay_ms); //System.Threading.Thread.Sleep(delay_ms);
                i++;

                //serial port receive data
                if (serialPort1.BytesToRead <= 0) continue;
                if (i > 1) i--;
                index_read = serialPort1.Read(buffer, total_read, serialPort1.BytesToRead);
                total_read += index_read;
                buffer_str = ToStringLsb(buffer);

                //// Find a Start byte
                int ack_index = buffer_str.LastIndexOf(ToStringLsb(ACK));
                int nak_index = buffer_str.LastIndexOf(ToStringLsb(NAK));

                //Find a Stop byte
                int stop_index_etx = buffer_str.LastIndexOf(ToStringLsb(ETX));
                int stop_index_lf = buffer_str.LastIndexOf(ToStringLsb(LF));

                if ((stop_index_etx < 0) && (stop_index_lf < 0) && (ack_index < 0)) continue; // Can't find a byte Stop


                if (stop_index_etx > 0)
                {
                    if ((stop_index_etx + 1) < total_read)
                    {
                        stop_index_etx++;
                    }
                    else
                    {
                        if (buffer[total_read - 2] == ETX)
                        {
                            stop_index_etx--;
                        }
                        else continue;
                    }
                }

                rs232data = buffer_str.Substring(0, total_read);
                return true;
            }
            while (i < timeout);
            return false;
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
            return false;
        }
    }

    public static string ToStringLsb(byte bytevalue)
    {
        try
        {
            byte[] bytearray = { bytevalue };
            return System.Text.Encoding.GetEncoding("iso-8859-1").GetString(bytearray);
        }
        catch (System.Exception ex)
        {
            MessageBox.Show(ex.Message);
            return "";
        }
    }

    public static string ToStringLsb(byte[] bytearray)
    {
        return System.Text.Encoding.GetEncoding("iso-8859-1").GetString(bytearray);
    }
public bool read_端口(参考字符串rs232data,int超时)
{
尝试
{
int index_read=0;
int total_read=0;
int i=0;
int delay_ms=1;
字节[]缓冲区=新字节[serialPort1.ReadBufferSize];
rs232data=“”;
字符串缓冲区_str=“”;
if((serialPort1.IsOpen==false))
{
#如果调试
MessageBox.Show(“COM端口未打开”);
#恩迪夫
返回false;
}
做
{
Application.DoEvents();
pauseformillisconds(delay_ms);//System.Threading.Thread.Sleep(delay_ms);
i++;
//串口接收数据
if(serialPort1.BytesToRead 1)i--;
index_read=serialPort1.read(缓冲区、总读取、serialPort1.BytesToRead);
总读取次数+=索引读取次数;
buffer_str=ToStringLsb(buffer);
////找到一个起始字节
int ack_index=buffer_str.LastIndexOf(ToStringLsb(ack));
int nak_index=buffer_str.LastIndexOf(ToStringLsb(nak));
//找到一个停止字节
int stop_index_etx=buffer_str.LastIndexOf(ToStringLsb(etx));
int stop_index_lf=buffer_str.LastIndexOf(ToStringLsb(lf));
如果((stop_index_etx<0)和&(stop_index_lf<0)和&(ack_index<0))继续;//找不到字节停止
如果(停止索引etx>0)
{
如果((停止索引etx+1)<总读取)
{
停止索引etx++;
}
其他的
{
if(缓冲区[总读取量-2]==ETX)
{
停止指数etx--;
}
否则继续;
}
}
rs232data=缓冲区字符串(0,总读取);
返回true;
}
而(i
您需要查看ASCII映射并确定要查找的控制值

例如,我将在代码中定义以下控制字符,然后在
DataReceived
事件中查找这些字符

char ACK = (char)6;
char NAK = (char)21;
char SOH = (char)1;
char LF = (char)10;
StringBuilder sb = new StringBuilder();

private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
    string Data = serialPort1.ReadExisting();

    foreach (char c in Data)
    {
        if (c == LF)
        {
            sb.Append(c);

            CurrentLine = sb.ToString();
            sb.Clear();

            //parse CurrentLine here or print it to textbox
        }
        if (c == ACK)
        {
            sb.Append("<ACK>"); //or whatever you want to print
        }
        else
        {
            sb.Append(c);
        }
    }
}
char ACK=(char)6;
char NAK=(char)21;
char-SOH=(char)1;
字符LF=(字符)10;
StringBuilder sb=新的StringBuilder();
private void serialPort1\u DataReceived(对象发送方,System.IO.Ports.SerialDataReceivedEventArgs e)
{
string Data=serialPort1.ReadExisting();
foreach(数据中的字符c)
{
if(c==LF)
{
sb.附加(c);
CurrentLine=sb.ToString();
(某人清楚地);
//在此处解析CurrentLine或将其打印到文本框
}
如果(c==ACK)
{
sb.Append(“”;//或任何你想打印的东西
}
其他的
{
sb.附加(c);
}
}
}
编辑:
在下面的评论中回答您的一些问题。
DataReceived
事件在其缓冲区中获取字符时触发,但可能在仅获取一半消息时触发。因此,当使用此事件时,您必须构建一个字符串,直到您知道您拥有整个消息。在上面的例子中,我假设LF(换行符)表示我拥有整个消息。对于您来说,您可以使用您正在搜索的任何字符来标记消息的结束(在您的情况下可能是ACK?)。您可以选择是否将该字符附加到字符串中,这取决于您的要求。在我的示例中,您可以看到我附加了LF,但您可以轻松地将该行删除。

谢谢。它起作用了。但当我将其显示给textbox时,它仍然包含可打印字符。我们可以在使用foreach循环检测后删除可打印字符吗?它应该只打印您放入
StringBuilder
缓冲区的内容。您只需查找所有这些字符,如果您看到一个字符,请不要将其添加到字符串中。如果我不通过.Append将搜索到的这个字符放入StringBuilder,它将跳过以查找下一个字符?顺便问一下,如果我通过串行端口(.ReadExisting)接收到像“012…789”这样的字符串,我如何才能获得其中的字符串(“3456”)?非常感谢@Baddack。基本上,您正在获取来自串行端口的data received事件的字符串,并生成自己的字符串(您将在文本框或任何地方显示)。在上面的示例中,我不是将0x06字符放在要显示的字符串中,而是将其附加到字符串中,因此当您在文本框中显示它时,它不是一些垃圾字符。然而,根据您的需求,当您看到该字符时,您可以什么也不做,也不向您的stri添加任何内容