【问题标题】:Windows IoT BackgroundTask Async/AwaitWindows IoT BackgroundTask 异步/等待
【发布时间】:2017-12-07 04:14:09
【问题描述】:

我正在玩运行 Windows IoT 的 Raspberry Pi 3。我已经连接了一个 DS18B20 传感器,我可以通过 UWP 应用程序与之通信。

我现在想把这个应用程序变成一个 BackgroundTask 应用程序。 我正在将此代码用于 OneWire coms

class WireSearchResult
{
    public byte[] id = new byte[8];
    public int lastForkPoint = 0;
}
public class OneWire
{
    private SerialDevice serialPort = null;
    DataWriter dataWriteObject = null;
    DataReader dataReaderObject = null;

    public async Task<string> GetFirstSerialPort()
    {
        try
        {
            string aqs = SerialDevice.GetDeviceSelector("UART0");
            var dis = await DeviceInformation.FindAllAsync(aqs);
            if (dis.Count > 0)
            {
                var deviceInfo = dis.First();
                return deviceInfo.Id;
            }
        }
        catch (Exception ex)
        {
            Debug.WriteLine("Unable to get serial device: " + ex.Message);
        }

        return null;
    }

    public void shutdown()
    {
        if (serialPort != null)
        {
            serialPort.Dispose();
            serialPort = null;
        }
    }

    async Task<bool> onewireReset(string deviceId)
    {
        try
        {
            if (serialPort != null)
                serialPort.Dispose();

            serialPort = await SerialDevice.FromIdAsync(deviceId);

            // Configure serial settings
            serialPort.WriteTimeout = TimeSpan.FromMilliseconds(1000);
            serialPort.ReadTimeout = TimeSpan.FromMilliseconds(1000);
            serialPort.BaudRate = 9600;
            serialPort.Parity = SerialParity.None;
            serialPort.StopBits = SerialStopBitCount.One;
            serialPort.DataBits = 8;
            serialPort.Handshake = SerialHandshake.None;

            dataWriteObject = new DataWriter(serialPort.OutputStream);
            dataWriteObject.WriteByte(0xF0);
            await dataWriteObject.StoreAsync();

            dataReaderObject = new DataReader(serialPort.InputStream);
            await dataReaderObject.LoadAsync(1);
            byte resp = dataReaderObject.ReadByte();
            if (resp == 0xFF)
            {
                System.Diagnostics.Debug.WriteLine("Nothing connected to UART");
                return false;
            }
            else if (resp == 0xF0)
            {
                System.Diagnostics.Debug.WriteLine("No 1-wire devices are present");
                return false;
            }
            else
            {
                //System.Diagnostics.Debug.WriteLine("Response: " + resp);
                serialPort.Dispose();
                serialPort = await SerialDevice.FromIdAsync(deviceId);

                // Configure serial settings
                serialPort.WriteTimeout = TimeSpan.FromMilliseconds(1000);
                serialPort.ReadTimeout = TimeSpan.FromMilliseconds(1000);
                serialPort.BaudRate = 115200;
                serialPort.Parity = SerialParity.None;
                serialPort.StopBits = SerialStopBitCount.One;
                serialPort.DataBits = 8;
                serialPort.Handshake = SerialHandshake.None;
                dataWriteObject = new DataWriter(serialPort.OutputStream);
                dataReaderObject = new DataReader(serialPort.InputStream);
                return true;
            }
        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine("Exception: " + ex.Message);
            return false;
        }
    }

    public async Task onewireWriteByte(byte b)
    {
        for (byte i = 0; i < 8; i++, b = (byte)(b >> 1))
        {
            // Run through the bits in the byte, extracting the
            // LSB (bit 0) and sending it to the bus
            await onewireBit((byte)(b & 0x01));
        }
    }

    async Task<byte> onewireBit(byte b)
    {
        var bit = b > 0 ? 0xFF : 0x00;
        dataWriteObject.WriteByte((byte)bit);
        await dataWriteObject.StoreAsync();
        await dataReaderObject.LoadAsync(1);
        var data = dataReaderObject.ReadByte();
        return (byte)(data & 0xFF);
    }

    async Task<byte> onewireReadByte()
    {
        byte b = 0;
        for (byte i = 0; i < 8; i++)
        {
            // Build up byte bit by bit, LSB first
            b = (byte)((b >> 1) + 0x80 * await onewireBit(1));
        }
       // System.Diagnostics.Debug.WriteLine("onewireReadByte result: " + b);
        return b;
    }

    public async Task<double> getTemperature(string deviceId)
    {
        double tempCelsius = -200;

        if (await onewireReset(deviceId))
        {
            await onewireWriteByte(0xCC); //1-Wire SKIP ROM command (ignore device id)
            await onewireWriteByte(0x44); //DS18B20 convert T command 
                                          // (initiate single temperature conversion)
                                          // thermal data is stored in 2-byte temperature 
                                          // register in scratchpad memory

            // Wait for at least 750ms for data to be collated
            await Task.Delay(750);

            // Get the data
            await onewireReset(deviceId);
            await onewireWriteByte(0xCC); //1-Wire Skip ROM command (ignore device id)
            await onewireWriteByte(0xBE); //DS18B20 read scratchpad command
                                          // DS18B20 will transmit 9 bytes to master (us)
                                          // starting with the LSB

            byte tempLSB = await onewireReadByte(); //read lsb
            byte tempMSB = await onewireReadByte(); //read msb

            // Reset bus to stop sensor sending unwanted data
            await onewireReset(deviceId);

            // Log the Celsius temperature
            tempCelsius = ((tempMSB * 256) + tempLSB) / 16.0;
            var temp2 = ((tempMSB << 8) + tempLSB) * 0.0625; //just another way of calculating it

            System.Diagnostics.Debug.WriteLine("Temperature: " + tempCelsius + " degrees C " + temp2);
        }
        return tempCelsius;
    }
}

最后是 StartupTask

public sealed class StartupTask : IBackgroundTask
{
    private BackgroundTaskDeferral deferral;

    private OneWire onewire;
    private string deviceId = string.Empty;
    private bool inprog = false;
    private Timer timer;

    public void Run(IBackgroundTaskInstance taskInstance)
    {
        deferral = taskInstance.GetDeferral(); 

        onewire = new OneWire();
        deviceId = await onewire.GetFirstSerialPort();

        if(deviceId != null)
          await onewire.getTemperature(deviceId));

        BackgroundTaskDeferral.Complete();
    }

}

我遇到的问题是,当我运行此代码时,它会挂在处理 OneWire 类中的 SerialDevice 的行之一上。

我在一些地方读到它与 BackgroundTask 和使用 Async/Await 相关

【问题讨论】:

  • 您是否真的阅读了代码示例中显示的链接中的文档? IE。 aka.ms/backgroundtaskdeferral。在我看来,它似乎提供了一个非常清晰、易于理解的解释,以准确说明如何处理您所询问的问题。
  • 确保在每个serialPort.Dispose(); 之后设置serialPort = null; 并再次测试。
  • @PeterDuniho 请详细说明。我已阅读文档,但没有看到对我的问题的明确解释。我获得了一个延迟,我执行了代码,唯一缺少的是在代码完成后设置 Defferral.Complete()。我的问题是代码没有完成,它挂起,所以我永远无法设置 Defferral.Complete()。
  • @RitaHan-MSFT 我尝试将串口设置为空,但没有成功。
  • "唯一缺少的部分是设置 Deferral.Complete()" -- 是的,缺少那部分。为什么不从尽可能正确的代码开始,而不是从已知不正确的代码开始。您声称代码挂在对Dispose() 的调用上,但是a)您的非minimal reproducible example 显示没有对Dispose() 的调用,并且b)处置完全有可能没有完成,因为您从不调用@ 987654331@方法。

标签: c# asynchronous windows-10-iot-core


【解决方案1】:

此问题与 BackgroundTask 无关。因为您的代码在非 BackgroundTask(app) 中产生了同样的问题。

它的原因看起来像SerialPort is somewhat prone to deadlock。

我发现有太多的onewireReset 方法调用会关闭并重新打开 SerialPort。我不知道为什么要这样做,但这会导致问题。

所以有一个解决方法:重写相关部分逻辑,并确保在程序开始时打开 SerialPort 并在不再需要时将其丢弃。

【讨论】:

  • 该代码在非背景任务应用程序中工作。我在帖子顶部提到这适用于 UWP 应用程序。我可以每秒获得 10 分钟的温度,并且没有发现任何问题。我尝试更改逻辑,因此只有在开始时创建序列并在结束时处理,但传感器不再正确响应。当它执行这个 tempCelsius = ((tempMSB * 256) + tempLSB) / 16.0;我得到了 4096.something(不记得确切的数字了)。
【解决方案2】:

我在后台任务中使用相同的单线代码与 DS18B20 传感器通信,并且遇到的行为与您完全相同。

我发现,如果我在调用串行端口 dispose 方法之前延迟 100 毫秒,它就可以工作

await Task.Delay(100)

我尝试了不到 100 毫秒,但它一直挂起。

这个 stackoverflow questions first answer解释了 .Net Framework Why Thread.Sleep() before SerialPort.Open and Close?中的串口问题

【讨论】:

  • 有趣,我稍后再试试。昨晚我进行了一些实验并重新编写了一些代码。我设法删除了串行端口的所有创建和处置。我能做到的最好的事情是我可以创建一个串行连接,读取温度,然后关闭连接 4 次,然后它会挂在第 5 个循环上。我确实尝试过创建连接的想法,然后以 5 秒的间隔在无限循环中进行读取,但在循环 15 左右出现异常。有点谷歌搜索并切换到本机调试,现在看起来像可视化工作室是问题。
  • 我在 Visual Studio 2017 中运行了 1 个多小时,返回温度,然后才将其关闭。
  • 这对我来说听起来是个好消息 :-) 当我尝试了 100 毫秒的延迟后会报告。
  • 是的,这看起来已经解决了问题,谢谢@DazFahy
【解决方案3】:

我自己昨天也遇到了完全相同的问题(在 onewireReset(...) 方法中处理 SerialDevice 对象时程序突然挂起),我设法解决了它。

解决原则: 不要不断地配置/重新获取串口。相反,获取端口一次 并根据需要即时重新配置(=更改波特率)。这样可以完全避免挂起的 SerialDevice.Dispose() 调用。

注意:为了能够改变波特率,您必须首先从端口中分离 DataReader 和 DataWriter 对象流,否则你会得到一个例外。更改后,重新附加新的 DataReader 和 DataWriter 对象(记得妥善处理旧对象)。

修改后的 OneWire 类:

    public class OneWire
{
    private SerialDevice serialPort = null;
    DataWriter dataWriteObject = null;
    DataReader dataReaderObject = null;

    public void shutdown()
    {
        if (serialPort != null)
        {
            serialPort.Dispose();
            serialPort = null;
        }
    }

    private async Task ReconfigurePort(uint baudRate, string deviceId)
    {
        if (serialPort == null)
        {
            serialPort = await SerialDevice.FromIdAsync(deviceId);
            serialPort.WriteTimeout = TimeSpan.FromMilliseconds(1000);
            serialPort.ReadTimeout = TimeSpan.FromMilliseconds(1000);
            serialPort.BaudRate = baudRate;
            serialPort.Parity = SerialParity.None;
            serialPort.StopBits = SerialStopBitCount.One;
            serialPort.DataBits = 8;
            serialPort.Handshake = SerialHandshake.None;
            dataWriteObject = new DataWriter(serialPort.OutputStream);
        }
        else
        {
            dataWriteObject.DetachStream();
            dataWriteObject.DetachBuffer();
            dataWriteObject.Dispose();

            dataReaderObject.DetachStream();
            dataReaderObject.Dispose();

            serialPort.BaudRate = baudRate;

            dataWriteObject = new DataWriter(serialPort.OutputStream);
        }
    }

    async Task<bool> onewireReset(string deviceId)
    {
        try
        {
            await ReconfigurePort(9600, deviceId);

            dataWriteObject.WriteByte(0xF0);
            await dataWriteObject.StoreAsync();

            dataReaderObject = new DataReader(serialPort.InputStream);
            await dataReaderObject.LoadAsync(1);

            byte resp = dataReaderObject.ReadByte();
            if (resp == 0xFF)
            {
                //System.Diagnostics.Debug.WriteLine("Nothing connected to UART");
                return false;
            }
            else if (resp == 0xF0)
            {
                //System.Diagnostics.Debug.WriteLine("No 1-wire devices are present");
                return false;
            }
            else
            {
                //System.Diagnostics.Debug.WriteLine("Response: " + resp);
                await ReconfigurePort(115200, deviceId);
                dataReaderObject = new DataReader(serialPort.InputStream);
                return true;
            }
        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine("Exception: " + ex.Message);
            return false;
        }
    }

    public async Task onewireWriteByte(byte b)
    {
        for (byte i = 0; i < 8; i++, b = (byte)(b >> 1))
        {
            // Run through the bits in the byte, extracting the
            // LSB (bit 0) and sending it to the bus
            await onewireBit((byte)(b & 0x01));
        }
    }

    async Task<byte> onewireBit(byte b)
    {
        var bit = b > 0 ? 0xFF : 0x00;
        dataWriteObject.WriteByte((byte)bit);
        await dataWriteObject.StoreAsync();
        await dataReaderObject.LoadAsync(1);
        var data = dataReaderObject.ReadByte();
        return (byte)(data & 0xFF);
    }

    async Task<byte> onewireReadByte()
    {
        byte b = 0;
        for (byte i = 0; i < 8; i++)
        {
            // Build up byte bit by bit, LSB first
            b = (byte)((b >> 1) + 0x80 * await onewireBit(1));
        }
        //System.Diagnostics.Debug.WriteLine("onewireReadByte result: " + b);
        return b;
    }

    public async Task<double> getTemperature(string deviceId)
    {
        double tempCelsius = -200;

        if (await onewireReset(deviceId))
        {
            await onewireWriteByte(0xCC); //1-Wire SKIP ROM command (ignore device id)
            await onewireWriteByte(0x44); //DS18B20 convert T command 
                                          // (initiate single temperature conversion)
                                          // thermal data is stored in 2-byte temperature 
                                          // register in scratchpad memory

            // Wait for at least 750ms for data to be collated
            //await Task.Delay(250);

            // Get the data
            await onewireReset(deviceId);
            await onewireWriteByte(0xCC); //1-Wire Skip ROM command (ignore device id)
            await onewireWriteByte(0xBE); //DS18B20 read scratchpad command
                                          // DS18B20 will transmit 9 bytes to master (us)
                                          // starting with the LSB

            byte tempLSB = await onewireReadByte(); //read lsb
            byte tempMSB = await onewireReadByte(); //read msb

            // Reset bus to stop sensor sending unwanted data
            await onewireReset(deviceId);

            // Log the Celsius temperature
            tempCelsius = ((tempMSB * 256) + tempLSB) / 16.0;
            var temp2 = ((tempMSB << 8) + tempLSB) * 0.0625; //just another way of calculating it

            //System.Diagnostics.Debug.WriteLine("Temperature: " + tempCelsius + " degrees C " + temp2);
        }
        return tempCelsius;
    }
}

【讨论】:

    猜你喜欢
    • 2022-01-19
    • 1970-01-01
    • 2023-03-12
    • 2016-07-07
    • 2016-03-25
    • 2017-10-09
    • 1970-01-01
    相关资源
    最近更新 更多