首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >串口通信

串口通信
EN

Code Review用户
提问于 2016-12-12 14:21:57
回答 2查看 9.5K关注 0票数 4

对于下面的代码来说,这是一个效率问题,它是用C#中的MS编写的。如果没有任何解释,请告诉我,我会补充。该项目的这一部分所做的是:

  1. 按下按钮
  2. 马达开始移动(通过发送带有命令的9字节数组)
  3. 等待传感器注册值更改
  4. 电动机停止
  5. 保存motor(命令获取位置的命令被发送,9字节的数组响应由电机发送),以便稍后访问它并返回到那里。

rotate()stop()这样的调用是将数据写入9字节数组并将字节数组写入串口的命令。

现在,我的主要问题是步骤5,我在堆栈溢出上读到“如果您必须使用wait(),并且没有使用DataReceived实现的意义”。那么,我真的应该摆脱这个问题,还是有一个很好的方法来处理这个问题?如果我不使用wait(),它就会保存在停止命令之后发送的答案(每个命令也会发送一个答案)。

在第三步中使用DataReceived会更有效吗?在开始时,我将一个值保存为新0,并将这里读出的当前传感器与这个0值进行比较。

代码语言:javascript
复制
private void button13_Click(object sender, EventArgs e)
{
    //rotate right speed
    string Test = textBox4.Text;
    byte[] A = new byte[9];
    rotate(A, Test);
    //read sensor
    serialPort2.WriteLine("SR,00,002\r\n");
    string b = serialPort2.ReadLine();
    decimal caliber = decimal.Parse(Regex.Split(b, "SR,00,002,")[1]);
    decimal b1 = 0;
    do
    {
        serialPort2.WriteLine("SR,00,002\r\n");
        string z = serialPort2.ReadLine();
        b1 = decimal.Parse(Regex.Split(z, "SR,00,002,")[1]);
    }
    while (b1 <= caliber);
    // stop motor
    byte[] B = new byte[9];
    stop(B);
    //save position
    System.Threading.Thread.Sleep(5000);
    byte[] C = new byte[9];
    getPosi(C);
    System.Threading.Thread.Sleep(5000);
    M = buf;
    //move to 0
    byte[] D = new byte[9];
    MoveTo0(D);
}

如果需要了解上面的代码:

代码语言:javascript
复制
//global variables
byte[] buf = new byte[9];
byte[] M = new byte[9];
private void button1_Click(object sender, EventArgs e)      //Ports öffnen
{
    serialPort3 = new SerialPort();
    serialPort3.PortName = "COM6";
    serialPort3.Handshake = Handshake.RequestToSend;
    serialPort3.ReceivedBytesThreshold = 8;
    serialPort3.DataReceived += new SerialDataReceivedEventHandler(DataRecievedHandler);
    serialPort3.Open();
}
private void DataRecievedHandler(object sender, SerialDataReceivedEventArgs e)
{ 
    SerialPort sp = (SerialPort)sender;
    int bytes = serialPort3.BytesToRead;
    byte[] buffer = new byte[bytes];
    sp.Read(buffer, 0, bytes);
    string hex = BitConverter.ToString(buffer);
    hex.Replace("-", "");
    MessageBox.Show(hex);
    buf = buffer;         
}

其他方法:

代码语言:javascript
复制
private void rotate(byte[] A, string Test) //Rotate Motor Right
    {
        A = MB.TMCL_RMR(Test);
        serialPort3.Write(A, 0, A.Length);
    }

    private void stop(byte[] B) // Stop Motor
    {
        B = MB.TMCL_MST();
        serialPort3.Write(B, 0, B.Length);
    }

    private void getPosi(byte[] C) //Get Position
    {
        C = MB.GET_POSI();
        serialPort3.Write(C, 0, C.Length);
    }

    private void MoveTo0(byte[] D) //Move to 0
    {
        D = MB.MoveTo0();
        serialPort3.Write(D, 0, D.Length);
    }

例如,关于字节数组是如何创建的,它是从我的单独的马达控制类中得到的。每个十六进制引用一个特定的信息,最后一个字节是校验和.如果这个数组被发送到串口,它将请求电机位置作为回答。

代码语言:javascript
复制
public byte[] GET_POSI()
    {
        byte[] E = new byte[9];
        E[0] = 0x1;
        E[1] = 0x6;
        E[2] = 0x1;
        E[3] = 0x0;
        E[4] = 0x0;
        E[5] = 0x0;
        E[6] = 0x0;
        E[7] = 0x0;
        E[8] = 0x08;
        return E;
    }

代码按照我希望的那样工作,用于几个输入测试。我只是觉得可能有比Thread.Sleep()更好的方法。

EN

回答 2

Code Review用户

回答已采纳

发布于 2016-12-13 00:21:28

我猜您考虑过避免使用Thread.Sleep()方法,而不是您在标题中提到的Wait()。我无法测试您的代码,因为我既没有COM端口,也没有马达。我想给你一些关于如何改进你的代码的一般建议。让我们实现一些坚实而枯燥的原则。我将从类马达开始,它将代表您的设备。例如,您可以为此目的实现单例类。这有助于您将电机逻辑与其他逻辑分离开来,例如用户界面。我还可以想象您的端口设置是应用程序配置的一部分。

Motor.cs

代码语言:javascript
复制
public sealed class Motor : IDisposable
{
    public static readonly Motor Instance = new Motor();

    private Motor()
    {
        MotorPort = new SerialPort();
        MotorPort.PortName = "COM6";
        MotorPort.Handshake = Handshake.RequestToSend;
        MotorPort.ReceivedBytesThreshold = 8;
        MotorPort.DataReceived += new SerialDataReceivedEventHandler(dataRecievedHandler);
        MotorPort.Open();
    }

    public SerialPort MotorPort { get; private set; }
    public decimal Position { get; private set; }
    public void StopRotate()    { }
    public void StartRotate(string speed) { }
    public void MoveToPosition(int position) { }
    private void dataRecievedHandler(object sender, SerialDataReceivedEventArgs e)
    {
    }
    public void Dispose()
    {
        if (MotorPort.IsOpen)
        {
            MotorPort.Close();
        }
    }
}

而不是使用全局字段(M,buf),将它们封装到对象中,并设置适当的访问修饰符或更好地创建属性。使用有意义的名称您的字段和属性。使用资本化公约资本化公约

您可以将重复字符串隔离为单独的静态类,以使字符串常量存储库更容易维护,不需要直接使用特定的字符串。

StringConstants.cs

代码语言:javascript
复制
static class StringConstants
{
    public const string Prefix = "SR,00,002,";
    public const string PrefixCrLf = "SR,00,002\r\n";
}

扩展方法可以简化代码并使其更具可读性。

Extensions.cs

代码语言:javascript
复制
static class Extensions
{
    public static decimal GetDecimalOutput(this SerialPort port)
    {
        return decimal.Parse(Regex.Split(port.ReadLine(), StringConstants.Prefix)[1]);
    }
    public static void SendCommand(this SerialPort port)
    {
        port.WriteLine(StringConstants.PrefixCrLf);
    }
}

如果您已经实现了Motor.cs方法,则可以使用它或扩展它。

Form1.cs

代码语言:javascript
复制
private void btnTest_Click(object sender, EventArgs e)
{
    Motor.Instance.StartRotate(speed);
    Thread.Sleep(5000);
    Motor.Instance.StopRotate();
    Motor.Instance.MoveToPosition(0);

    //read sensor
    serialPort2.SendCommand();
    decimal caliber = serialPort2.GetDecimalOutput();

    do
    {
        serialPort2.SendCommand();
        actualValue = serialPort2.GetDecimalOutput();
    }
    while (actualValue <= caliber);
}

我没有串口编程方面的经验,但我确信事件SerialPort.DataReceived应该像调用serialPort3那样用于调用订阅的方法。我不知道为什么要使用Thread.Sleep(),因为我看不到getPosi(C);方法。

票数 3
EN

Code Review用户

发布于 2016-12-13 09:53:18

只是有件小事从我身上跳了出来:

代码语言:javascript
复制
private void rotate(byte[] A, string Test) //Rotate Motor Right
{
    A = MB.TMCL_RMR(Test);
    serialPort3.Write(A, 0, A.Length);
}

为什么要传入一个字节数组,以便用第一行中的其他东西替换它?

代码语言:javascript
复制
private void SendRotateCommand(string speed)
{
    var commandBytes = MB.TMCL_RMR(speed);
    motorPort.Write(commandBytes, 0, commandBytes.Length); 
}

我猜到Test参数实际上是调用它的方法中的注释中的旋转速度。

你需要改进你的命名贯穿始终。您的代码非常简单,但是很难理解,因为命名太差了。

另一件小事:

代码语言:javascript
复制
public byte[] GET_POSI()
{
    byte[] E = new byte[9];
    E[0] = 0x1;
    E[1] = 0x6;
    E[2] = 0x1;
    E[3] = 0x0;
    E[4] = 0x0;
    E[5] = 0x0;
    E[6] = 0x0;
    E[7] = 0x0;
    E[8] = 0x08;
    return E;
}

可以更简单地写成:

代码语言:javascript
复制
public byte[] GET_POSI()
{
    return new byte[] { 1, 6, 1, 0, 0, 0, 0, 0, 8 };
}

我真不明白你想把这个职位留给什么。或者你是怎么做的。你在给马达发信息,它把东西送回来了。你收到一条信息,你就会明白这意味着什么,然后行动起来。

您还将DataReceivedSerialPort.ReadLine混合在一起--它们都使用相同的底层内存流,您应该使用其中一种--而不是两者都使用。

在等待操作完成时,您还会冻结大量的UI。可以接受来自用户的命令,然后随着操作的进展更新UI。当电机旋转时显示当前位置,当它到达正确的位置时,停止它。

票数 3
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/149640

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档