C#电源串口调试

发布时间:2023年12月22日

目的
记录串口调试的遇到的一些问题以及相应的解决方法
1.串口定义:串口是计算机与其他硬件传输数据的通道,在计算机与外设通信时起到重要作用
2.串口通信的基础知识

  1. C#中的串口通信类
    C#使用串口通信类是SerialPort(),该类使用方法是
    new 一个 SerialPort对象
    为SerialPort对象准备参数
   _serialPort = new SerialPort(portName);
   _serialPort.BaudRate = bauRate;
   _serialPort.Parity = parity;
   _serialPort.DataBits = dataBits;
   _serialPort.StopBits = stopBits;
   _serialPort.Handshake = Handshake.None;
   _serialPort.ReadTimeout = 500;
   _serialPort.WriteTimeout = 500;

接下来就是打开串口并且绑定事件

  try
  {
      ErrorMessage = "";
      _serialPort.Open();

      if (_serialPort.IsOpen)
      {
          _serialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
          return true;

      }
      else
      {
          Log.Debug($"Truth_Power.SerialPortHelper.Open().Return [{false.ToString()}]");
          
          return false;
      }

  }
  catch(Exception ex)
  {
      
      ErrorMessage = ex.Message;
      Log.Error($"Truth_Power.SerialPortHelper.Open().Error [{ex.Message}]");
      
      return false;
  }

2.从串口读取数据
串口的性质不同于网络,网络发一个HTTP会返回一个完整的数据。例如请求一个网页,服务端就会返回完整的网页代码。
但是串口不同,串口不一定一次返回完整的数据,可能会分批次返回
例如我已知串口返回来的数据是10个字节但是第一次触发数据接收事件时可能返回5个,第二次再触发事件时返回剩下的5个。这样串口需要一个数据合并的代码,实例代码如下:

    private  void DataReceivedHandler(object sender,SerialDataReceivedEventArgs args)
    {
        //端口缓冲区字节数,本次数据到达的字节数
        int n = _serialPort.BytesToRead;
        //  Console.WriteLine(n);
        //将端口缓冲区数据存入字节数组
        byte[] byteRev = new byte[n];
        _serialPort.Read(byteRev, 0, n);
        //将字节数组存入程序缓冲区
        string data = BitConverter.ToString(byteRev);
       // RecieveBuffer += data;         
        if (IsRead)
        {
            ReadBuffer += "-"+data;   //拼接返回的数据

        }
        //if(ReadBuffer.Length == 20)                            //如果数据长度合格,则可以发出事件   这个事件不应该在回调中发出而是应该在writeRead函数中发出
        //{
        //    ReceivedDataFromPort.Invoke(command, byteRev, n);  //读出完整数据后再发出事件
        //}
       
    }
    public bool WriteAndRead(byte[] sendBytes, out string recieved, int timeOut = 1000, int stepTime = 20)
    {
        IsRead = true;
        ReadBuffer = "";//公共变量
        recieved = "";
        //clearInput();      //清空串口
        if (Write(sendBytes))
        {
            int times = timeOut / stepTime;
            for (int i = 0; i < times; i++)
            {
                recieved = ReadBuffer;         //获取截止到当前串口返回的数据
                Thread.Sleep(stepTime);        //这个延时一定要放在recived = ReadBuffer之后,如果20ms后ReadBuffer被更新,那么received!=ReadBuffer,这时就不会跳出循环
                if (recieved == "")           //如果这次读到是空先跳过,等下次再读
                {
                    continue;
                }
                if (recieved == ReadBuffer)    //当某次达到满数据后可以直接跳出循。跳出后判断i的值,如果i达到times那么说明超时;如果小于times那么没有超时
                {
                    break;
                }
            }

            clearOutput();
            IsRead = false;
            return true;

        }
        IsRead = false;
        Log.Debug($"Truth_Power.SerialPortHelper.WriteAndRead().Return [{false.ToString()}]");
        return false;
    }

3.C#跨线程访问
C#跨线程访问需要特殊的处理

  private void sendDataThread()
  {
      string fileName = "testdata.txt";
      StreamWriter sr;
      sr = new StreamWriter(fileName);
      
      const  int length = 10; ;//发送与返回的字节长度
      int timeOut = 1000;      //超时时间
      int stepTime = 20;       //时间间隔
      int n = 0;            //当前接收的数据长度

      string hexString;
      //现在的问题就是数据没有写入
      string dataRev;
      

      byte[] dataRecv = new byte[length];
      
      while (true)
      {
          if (killed)                      //如果需要结束线程,那么用break跳出循环
          {
              sr.Close();
              break;
          }
          lock (Lock_Port)
          {

              this.Dispatcher.Invoke(new Action(delegate
              {
                  //你想要做的操作    

                  ControlMode mode = ((MainWindowViewModel)this.DataContext).CurrentSelectedMode;
                  int electric = ((MainWindowViewModel)this.DataContext).Electricity;
                  int magetic = ((MainWindowViewModel)this.DataContext).Magnetic;
                  int voltage = ((MainWindowViewModel)this.DataContext).Voltage;
                  byte[] command;
                  List<byte> data = new List<byte>();
                  switch (mode)
                  {
                      case ControlMode.Voltage:
                          SerialPortCommand.SetVoltage(voltage, Sign.PositiveSign, out command);
                          //这里可能要先清空
                          _serialPortControl.command = command;
                          _serialPortControl.Write(command.ToArray());

                          _serialPortControl.WriteAndRead(command, out dataRev);

                          timestamp = DateTime.Now.ToString();


                          hexString = BitConverter.ToString(command);
                          timestamp = DateTime.Now.ToString();
                          sr.WriteLine("接收时间");
                          sr.WriteLine(timestamp);
                          sr.WriteLine("发送数据");
                          sr.WriteLine(hexString);
                          // hexString = BitConverter.ToString(receive);
                          sr.WriteLine("接收数据");
                          sr.WriteLine(dataRev);
                          sr.WriteLine("接收数据长度");
                          sr.WriteLine(dataRev.Length.ToString());
                          sr.WriteLine("\n");

                          break;
                      case ControlMode.Magnetic:
                          SerialPortCommand.SetMagetic(magetic);
                          break;
                      case ControlMode.Electricity:
                          SerialPortCommand.SetElectric(electric, Sign.PositiveSign, out command);
                          _serialPortControl.command = command;

                          _serialPortControl.WriteAndRead(command, out dataRev);

                          timestamp = DateTime.Now.ToString();


                          hexString = BitConverter.ToString(command);
                          timestamp = DateTime.Now.ToString();
                          sr.WriteLine("接收时间");
                          sr.WriteLine(timestamp);
                          sr.WriteLine("发送数据");
                          sr.WriteLine(hexString);
                          // hexString = BitConverter.ToString(receive);
                          sr.WriteLine("接收数据");
                          sr.WriteLine(dataRev);
                          sr.WriteLine("接收数据长度");
                          sr.WriteLine(dataRev.Length.ToString());
                          sr.WriteLine("\n");
                          break;
                  }
              }));




         
          }

          Thread.Sleep(1000);                                         //延时1秒,保证电源有时间响应
 
      }

4.串口指令生成
写串口实质上是向串口写入数据
数据本质上一串字节型数据,一般有固定的格式。帧头-命令字-数据部分-帧尾,值得关注就是数据部分。
在这里插入图片描述

以这个图片为例,取出32位Int型数据的某八位可以用>>(移位)和&(且)
如果取出高第二个字节

(byte)((dataLength >> 16) & 0xFF)

如果取出Int型数据的低8位

(byte)(dataLength & 0xFF)

5.关于combox selectedChange事件 运行程序立即执行的问题
解决这个bug需要在切换事件中判断串口是否打开,如果未打开则事件立即返回。代码如下:

   //选中的改变之后,根据当前的选中值更新
   //核心获取改变之后的值
   if (!_isOpenForPort)
   {
       MessageBox.Show("串口未打开");
       return;
   }

6.返回重复数据的bug的原因
换成新的WriteAndRead函数后没有把原来的函数Write删掉
在这里插入图片描述
删掉这行代码之后串口接收到的数据就正常了

文章来源:https://blog.csdn.net/m0_52364694/article/details/135144437
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。