SerialDevice.FromIdAsync()产生一个空串行端口

时间:2016-05-29 00:48:52

标签: c# serial-port uwp

尝试连接串口时,我的代码发生了一些奇怪的事情。使用Serial Sample时,一切正常,并在第一次尝试时连接。但是,使用我的代码,串行端口返回null。

我找到了一种解决方法,通过嵌套调用在do循环中写入串口,直到串口不再为空并且它可以工作。平均需要500个环路才能连接,并且它始终连接。我知道这是危险的,因为如果它总是为空,则存在无限循环,所以我尝试将do循环切换为for循环。使用for循环,它总是返回null,即使我迭代它50,000次。有没有其他人遇到这个并找到了更好的解决方案?我正在连接到Arduino Uno。

我不仅仅使用串行示例代码的原因是因为我想使用下拉菜单并使用端口名称而不是设备ID名称。我的代码如下。感谢。

    //lists com ports
    private async void listPorts()
    {
        try
        {
            string aqs = SerialDevice.GetDeviceSelector();
            var dlist = await DeviceInformation.FindAllAsync(aqs);
            status.Text = "Select a device and connect";

            for (int i = 0; i < dlist.Count; i++)
            {
                var port = await SerialDevice.FromIdAsync(dlist[0].Id);
                ConnectDevices.Items.Add(port.PortName);
            }

            comConnect.IsEnabled = true;
            ConnectDevices.SelectedIndex = 0;
        }
        catch (Exception ex)
        {
            status.Text = ex.Message;
        }
    }

    //connects to the selected com port
    private async void comConnect_Click(object sender, RoutedEventArgs e)
    {
        _key = 0;
        status2.Text = "";
        string aqs = SerialDevice.GetDeviceSelector(ConnectDevices.SelectedItem.ToString());
        var dlist = await DeviceInformation.FindAllAsync(aqs);
        if (dlist.Count <= 0)
        {
            status.Text = "No devices found.";
            return;
        }
        try
        {
            do
            {
                serialPort = await SerialDevice.FromIdAsync(dlist[0].Id);
                status.Text = "Connecting to serial port...";
            } while (serialPort == null);

            // Configure serial settings
            serialPort.WriteTimeout = TimeSpan.FromMilliseconds(1000);
            serialPort.BaudRate = 38400;
            serialPort.Parity = SerialParity.None;
            serialPort.StopBits = SerialStopBitCount.One;
            serialPort.DataBits = 8;
            serialPort.Handshake = SerialHandshake.None;
            status.Text = "Serial port configured successfully.";

我已将以下内容添加到package.appmanifest

<Capabilities>
 <DeviceCapability Name="serialcommunication">
  <Device Id="any">
   <Function Type="name:serialPort" />
  </Device>
 </DeviceCapability>
</Capabilities>

3 个答案:

答案 0 :(得分:11)

在我的项目中,将以下代码添加到package.appmanifest后,串口正常工作。

    <Capabilities>
     <DeviceCapability Name="serialcommunication">
      <Device Id="any">
       <Function Type="name:serialPort" />
      </Device>
     </DeviceCapability>
    </Capabilities>

串口变量应为&#34; serialPort&#34;。

答案 1 :(得分:0)

此代码适用于我的com端口和Arduino:

string aqs = SerialDevice.GetDeviceSelector("COM3");
DeviceInformationCollection dlist = await DeviceInformation.FindAllAsync(aqs);

if (dlist.Any())
{
 deviceId = dlist.First().Id;
}

using (SerialDevice serialPort = await SerialDevice.FromIdAsync(deviceId))
{
// .....
}

尝试指定com端口

答案 2 :(得分:0)

这里的问题(您已经正确识别)是您的listPorts保持打开与SerialPort的连接。

  

SerialDevice类为您管理串行端口,并确保只有一个进程可以一次使用任何给定的端口。在这种情况下,多次调用创建一个SerialDevice不会引发异常,直到释放设备为止。

     
    

SerialDevice实现了IDisposable,因此当代码超出范围时,应将对其的调用包装在using中以正确释放连接。

  
//lists com ports
private async void listPorts()
{
    try
    {
        string aqs = SerialDevice.GetDeviceSelector();
        var dlist = await DeviceInformation.FindAllAsync(aqs);
        status.Text = "Select a device and connect";

        for (int i = 0; i < dlist.Count; i++)
        {
            using(var port = await SerialDevice.FromIdAsync(dlist[0].Id))
            {
                ConnectDevices.Items.Add(port.PortName);
            }
        }

        comConnect.IsEnabled = true;
        ConnectDevices.SelectedIndex = 0;
    }
    catch (Exception ex)
    {
        status.Text = ex.Message;
    }
}

通过更改上面的代码,您可以删除comConnect_Click逻辑中的无限循环:

    // NOTE: possible infinite loop!
    //do
    //{
    status.Text = "Connecting to serial port...";
    serialPort = await SerialDevice.FromIdAsync(dlist[0].Id);
    //
    //} while (serialPort == null);

之所以可行,是因为listPorts执行超出范围一段时间后,GarbageCollector将破坏port变量资源并释放物理串行端口连接。

如果您的代码不再占用连接,那么等待可能会占用连接的外部进程实际上可能是无限的,或者肯定是不确定的。

  • 在这种情况下中止连接过程会是一个更好的模式:
        status.Text = "Connecting to serial port...";
        serialPort = await SerialDevice.FromIdAsync(dlist[0].Id);
        if(serialPort == null)
        {
            status.Text = "Port busy, please try again later";
            return;
        }
    

注意:设备选择

除非您已经使用了非常特定的设备选择查询,使得列表中只有一个(或零个)项目,否则应避免对dlist[0].Id这样的设备数组选择进行硬编码。

OP的特定代码确实可以确保唯一选择,如果您在家中跟随,则不要简单地使用列表中的第一项,特别是如果您已使用此类代码枚举了SerialDevices。

string aqs = Windows.Devices.SerialCommunication.SerialDevice.GetDeviceSelector();
var dlist = await Windows.Devices.Enumeration.DeviceInformation.FindAllAsync(aqs, null);

此查询将例行返回本地PC,索引为零,然后返回每个串行连接的设备。

为避免这种陷阱,请在要操作的列表中保存对特定项目的引用,而不是对索引进行硬编码。

var selectedDevice = dlist[0];

这是诊断代码中的空值或数组索引问题的明显点。

关于无限循环

仅在异步环境或应用程序中,其中许多进程(或同一进程的许多实例)可能需要访问SerialPort,或者您要允许同一OS上的外部进程在需要时访问SerialPort,应该您担心创建SerialPort的循环结构。但是,您应该添加一些检查以允许中止该过程

如果确实要允许重试,则应限制重试次数或允许重试过程等待的时间。您甚至可以同时做这两个工作,将所有内容包装在一起,可能会有这样的内容,以及一些要检查的诊断信息:

var timer = Stopwatch.StartNew();
int attempts = 0;
do
{
    serialPort = await SerialDevice.FromIdAsync(dlist[0].Id);
    attempts++;
    if(serialPort != null)
        break;
    await Task.Delay(1000); // pause between retries
} while (serialPort == null && timer.ElapsedMilliseconds < 10000 && attempts < 5); 
// max wait 10 seconds, or 5 total retries for serial device coms to come online.
timer.Stop();
Debug.WriteLine($"Connection to SerialPort {(serialPort == null ? "FAILED" : "Successs")} in: {timer.Elapsed} ({attempts} attempts)");

if(serialPort == null)
{
    status.Text = "Port busy, please try again later";
    return;
}

应用程序的结构是这样的,您希望维持与SerialPort的排他连接,并阻止对可能也希望使用同一设备的其他进程的访问,在这种情况下,您不需要处理使用 Connect 按钮进行连接时,请使用SerialPort,但是请确保在表单的dispose方法中清除serialPort实例变量。

如果要构造所有调用以始终在需要时创建新的SerialPort连接,则需要确保在处理完该对象后将其处置。在这种模式下,如果要实现while循环以连接到设备,则可以使用using-try模式来代替finally语句:

//connects to the selected com port
private async void comConnect_Click(object sender, RoutedEventArgs e)
{
    _key = 0;
    status2.Text = "";
    string aqs = SerialDevice.GetDeviceSelector(ConnectDevices.SelectedItem.ToString());
    var dlist = await DeviceInformation.FindAllAsync(aqs);
    if (dlist.Count <= 0)
    {
        status.Text = "No devices found.";
        return;
    }
    try
    {
        // NOTE: possible infinite loop!
        do
        {
            serialPort = await SerialDevice.FromIdAsync(dlist[0].Id);
            status.Text = "Connecting to serial port...";
        } while (serialPort == null);

        // Configure serial settings
        serialPort.WriteTimeout = TimeSpan.FromMilliseconds(1000);
        serialPort.BaudRate = 38400;
        serialPort.Parity = SerialParity.None;
        serialPort.StopBits = SerialStopBitCount.One;
        serialPort.DataBits = 8;
        serialPort.Handshake = SerialHandshake.None;
        status.Text = "Serial port configured successfully.";

        // Perform other serial logic... 
    }
    catch (Exception ex)
    {
        status.Text = ex.Message;
    }
    finally
    {
        if(serialPort != null)
        {
            serialPort.Dispose();
            serialPort = null;
        }
    }
}