4

从 DLPIO20 设备上的 DS18B20+ 传感器读取实时温度数据时出现问题。我在 Windows 平台上使用 FTD2XX.DLL .NET 包装器(版本 1.0.14)(根据链接: http: //www.ftdichip.com/Support/SoftwareExamples/CodeExamples/CSharp/FTD2XX_NET_v1.0.14.zip) .

演示该问题的测试应用程序如下。

这就是我所说的实时。

2.1 在相当恒定的环境温度(假设为 20°C)下,在调试模式下读取的温度很少,可以看到一致的结果(第 55 行温度读取周期结束时的断点)。

2.2.然后,在断点时,只需在传感器上呼吸或使用其他方式将其加热到高于环境温度几度。

2.3. 运行两个或三个以上的温度读数循环。

传感器加热后我得到的第一个读数是 20° C 的“旧”温度。只有在第二个或第三个读取循环中,我才能得到 28° C 左右的实际结果。

有趣的是,使用测试应用程序 IO20Demo(购买 DLP-IO20 板时提供)如果我在读取之前发出 Convert 命令,我立即得到 28°C 的实时结果。有了这个程序和托管的 .NET 包装类,我还在 Read 之前发送了 Convert 命令,但无济于事。最大的不同是 IO20Demo 不使用托管包装类,而是直接使用 FTD2XX.lib。

你能帮我理解我在这里做错了什么吗?如何使用托管的 .NET 包装类获取实时温度数据?

非常感谢您的帮助!

/// <summary>
/// Program for DLPIO20 device to read temperature data from DS18B20+
/// digital temperature sensor attached to one of its channels. 
/// </summary>
class Program
{
    static void Main(string[] args)
    {
        FTDI FtdiWrapper = null;

        // DLPIO20 channel where the DS18B20+ sensor is attached
        byte sensorChannel = 0x06; 

        try
        {
            // create new instance of the FTDI device class
            FtdiWrapper = ConnectToFirstFtdiDevice();

            if (FtdiWrapper == null || !FtdiWrapper.IsOpen)
            {
                throw new Exception("Error connection to FTDI device.");
            }

            // didn't helped at all for 85dC issue
            //PurgeRxBuffer(FtdiWrapper);

            // helped avoid 85dC issue at first read 
            ConvertSensorData(FtdiWrapper, sensorChannel);

            // send read sensor command
            float? degreesC = null;
            for (int i = 0; i < 100; i++)
            {
                // calling Convert just before ReadTemperatureSensor causes
                // IO20Demo (using FTD2XX.lib) to return real temperature
                // but it doesn't help when using .NET wrapper
                ConvertSensorData(FtdiWrapper, sensorChannel);

                // other failed attempts to get real time sensor data

                // previous value returned:
                //PurgeRxBuffer(FtdiWrapper);

                // read but only initiate conversion on success
                degreesC = ReadTemperatureSensor(FtdiWrapper, sensorChannel);

                if (degreesC == null)
                {
                    throw new Exception("Error converting raw data to Celsius");
                }

                var message = string.Format("Success! {0}° Celsius.", degreesC);
                Console.WriteLine(message);
            }

            Console.WriteLine("Press any key to exit ...");
            Console.ReadKey();
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
            Console.WriteLine("Press any key to exit ...");
            Console.ReadKey();
        }
        finally
        {
            if (FtdiWrapper != null && FtdiWrapper.IsOpen)
            {
                FtdiWrapper.Close();
            }
        }
    }

这是用于连接到第一个 FTDI 设备的代码

    static private FTDI ConnectToFirstFtdiDevice()
    {
        FTDI FtdiWrapper = new FTDI();
        UInt32 ftdiDeviceCount = 0;
        FTDI.FT_STATUS ftStatus = FTDI.FT_STATUS.FT_OTHER_ERROR;

        // Determine the number of FTDI devices connected to the machine
        ftStatus = FtdiWrapper.GetNumberOfDevices(ref ftdiDeviceCount);
        // Check status
        if (ftStatus != FTDI.FT_STATUS.FT_OK)
            throw new Exception(string.Format("Error after GetNumberOfDevices(), ftStatus: {0}", ftStatus));

        if (ftdiDeviceCount == 0)
            throw new Exception("No FTDI device found");

        // Allocate storage for device info list
        var ftdiDeviceList = new FTDI.FT_DEVICE_INFO_NODE[ftdiDeviceCount];

        // Populate our device list
        ftStatus = FtdiWrapper.GetDeviceList(ftdiDeviceList);

        if (ftStatus == FTDI.FT_STATUS.FT_OK)
        {
            // Open first device in our list by serial number
            ftStatus = FtdiWrapper.OpenBySerialNumber(ftdiDeviceList[0].SerialNumber);
            if (ftStatus != FTDI.FT_STATUS.FT_OK)
                throw new Exception(string.Format("Error opening first device, ftStatus: {0}", ftStatus));

            ftStatus = FtdiWrapper.SetBaudRate(9600);
            if (ftStatus != FTDI.FT_STATUS.FT_OK)
                throw new Exception(string.Format("Error to set Baud rate, ftStatus: {0}", ftStatus));

            // Set data characteristics - Data bits, Stop bits, Parity
            ftStatus = FtdiWrapper.SetDataCharacteristics(FTDI.FT_DATA_BITS.FT_BITS_8, FTDI.FT_STOP_BITS.FT_STOP_BITS_1, FTDI.FT_PARITY.FT_PARITY_NONE);
            if (ftStatus != FTDI.FT_STATUS.FT_OK)
                throw new Exception(string.Format("Error to set data characteristics , ftStatus: {0}", ftStatus));

            // Set flow control - set RTS/CTS flow control
            ftStatus = FtdiWrapper.SetFlowControl(FTDI.FT_FLOW_CONTROL.FT_FLOW_RTS_CTS, 0x11, 0x13);
            if (ftStatus != FTDI.FT_STATUS.FT_OK)
                throw new Exception(string.Format("Error to set flow control , ftStatus: {0}", ftStatus));

            // Set read timeout to 5 seconds, write timeout to infinite
            ftStatus = FtdiWrapper.SetTimeouts(5000, 0);
            if (ftStatus != FTDI.FT_STATUS.FT_OK)
                throw new Exception(string.Format("Error to set timeouts, ftStatus: {0}", ftStatus));

        }

        return FtdiWrapper;
    }

转换原始传感器数据的方法

    static private void ConvertSensorData(FTDI FtdiWrapper, byte sensorChannel)
    {
        FTDI.FT_STATUS ftStatus = FTDI.FT_STATUS.FT_OTHER_ERROR;
        UInt32 numBytesWritten = 0;

        byte[] convertCommand = new byte[] { 0x03, 0x40, sensorChannel };
        ftStatus = FtdiWrapper.Write(convertCommand, convertCommand.Length, ref numBytesWritten);

        bool isAllBytesWritten = numBytesWritten == convertCommand.Length;
        if (ftStatus != FTDI.FT_STATUS.FT_OK && isAllBytesWritten)
            throw new Exception(string.Format("Failed to write Convert command to device; Status: {0},  isAllBytesWritten: {1}", ftStatus.ToString(), isAllBytesWritten));
    }

读取传感器温度的方法

    static private float? ReadTemperatureSensor(FTDI FtdiWrapper, byte sensorChannel)
    {
        float? degreesC = null;
        FTDI.FT_STATUS ftStatus = FTDI.FT_STATUS.FT_OTHER_ERROR;
        UInt32 numBytesWritten = 0;

        byte[] readSensorCommand = new byte[] { 0x03, 0x41, sensorChannel };
        ftStatus = FtdiWrapper.Write(readSensorCommand, readSensorCommand.Length, ref numBytesWritten);

        bool isAllBytesWritten = numBytesWritten == readSensorCommand.Length;
        if (ftStatus != FTDI.FT_STATUS.FT_OK && isAllBytesWritten)
            throw new Exception(string.Format("Failed to write readSensorCommand to device; Status: {0},  isAllBytesWritten: {1}", ftStatus.ToString(), isAllBytesWritten));

        // Read back response
        UInt32 numBytesAvailable = 0;
        UInt32 numBytesExpected = 2;    // read sensor command expected to return 2 bytes
        while (numBytesAvailable == 0)
        {
            Thread.Sleep(40); // value of 40 taken from DLP IO20 demo solution
            ftStatus = FtdiWrapper.GetRxBytesAvailable(ref numBytesAvailable);
            if (ftStatus != FTDI.FT_STATUS.FT_OK)
                throw new Exception("Failed to get number of bytes available to read; error: " + ftStatus);

        } //while (numBytesAvailable < numBytesExpected);

        if (numBytesAvailable != numBytesExpected)
            throw new Exception("Error: Invalid data in buffer. (1350)");

        UInt32 numBytesRead = 0;
        byte[] rawData = new byte[numBytesExpected];
        ftStatus = FtdiWrapper.Read(rawData, numBytesAvailable, ref numBytesRead);
        if (ftStatus != FTDI.FT_STATUS.FT_OK)
            throw new Exception("Failed to read data from device after command has been sent; error: " + ftStatus);

        //convert raw response data to degrees Celsius
        degreesC = ConvertTemperature(rawData);
        return degreesC;
    }

清除发送缓冲区的方法

    static private void PurgeRxBuffer(FTDI FtdiWrapper)
    {
        UInt32 numBytesAvailable = 0;
        UInt32 numBytesRead = 0;
        FTDI.FT_STATUS ftStatus = FTDI.FT_STATUS.FT_OTHER_ERROR;
        byte[] rx = new byte[1001]; //allocate large enough space to read from device

        Thread.Sleep(5);

        ftStatus = FtdiWrapper.GetRxBytesAvailable(ref numBytesAvailable);
        if (ftStatus != FTDI.FT_STATUS.FT_OK)
            throw new Exception("Failed to get number of bytes available to read; error: " + ftStatus);

        if (numBytesAvailable > 1000)
            numBytesAvailable = 1000;

        while (numBytesAvailable > 0)
        {
            //read the data from the buffer
            numBytesRead = 0;
            ftStatus = FtdiWrapper.Read(rx, numBytesAvailable, ref numBytesRead);


            ftStatus = FtdiWrapper.GetRxBytesAvailable(ref numBytesAvailable);
            if (ftStatus != FTDI.FT_STATUS.FT_OK)
                throw new Exception("Failed to get number of bytes available to read; error: " + ftStatus);

            if (numBytesAvailable > 1000)
                numBytesAvailable = 1000;

            Thread.Sleep(5);
        }
    }

将温度从原始数据转换为摄氏度的方法

    static private float? ConvertTemperature(byte[] rawData)
    {
        float? tempCelsius = null;
        bool isnegative = false;

        //check input
        if (rawData.Length < 2)
            throw new Exception(string.Format("Input parameter rawData for temperature conversion must be 2 bytes, actual length is: {0}", rawData.Length));

        int temp = rawData[0] | (rawData[1] << 8);
        if ((temp & 0x8000) == 0x8000)//if MSBit is set then negative temperature
        {
            temp &= 0x07ff;
            isnegative = true;
            temp = 0x800 - temp;
        }

        temp &= 0x07ff;
        tempCelsius = (float)((float)temp / 16.0);
        if (isnegative) tempCelsius *= -1;

        return tempCelsius;
    }
}
4

3 回答 3

0

我认为您需要将 ConnectToFirstFtdiDevice 中 FTDI 设备上的延迟计时器设置为最小值,我使用 16 毫秒,它帮助我解决了此类问题。purgeRX 只是一个软件缓冲区而不是硬件,因此不会阻止您在 FTDI USB 缓冲区中进行过时的测量。

于 2014-09-06T00:54:07.487 回答
0

应该在这里使用监视器/命令/响应设计模式。这里有重复的命令/响应序列,因此实现模式可以减少代码中存在的冗余。我将从带有事件处理程序的接口和命令/响应结构开始;即,如果 FTDI.deviceCount > 0,则触发事件处理程序。,跟进子依赖处理程序。在接收端,最低级别的.net 串行端口处理程序是基于 TDM 的。它会在此过程中丢失一些信息。为了完成,添加一个简单的数据包传递协议,例如两端带有校验和的 XOR。即在设备计算消息的校验和,发送命令,在另一端接收命令,计算校验和,与捕获的校验和进行比较。如果相同则 OK,否则,将捕获的命令回显到设备。反之亦然..

我的两美分 == 我的两美元,同样的事情

于 2016-08-02T18:41:29.053 回答
0

您无法通过调用 FtdiWrapper.GetRxBytesAvailable 获取可用字节。您需要在循环中调用 FtdiWrapper.Read 直到获得预期的字节,然后跳出循环。

于 2017-01-13T21:05:35.260 回答