使用单声道时的 Serial.IO.Ports 问题,适用于 dotnet core 3.1 / arm / raspberry pi 4

Posted

技术标签:

【中文标题】使用单声道时的 Serial.IO.Ports 问题,适用于 dotnet core 3.1 / arm / raspberry pi 4【英文标题】:Serial.IO.Ports issue when using mono, works with dotnet core 3.1 / arm / raspberry pi 4 【发布时间】:2021-07-14 12:57:35 【问题描述】:

在 Raspberry Pi 4 和串行 I/O 上使用 mono vs dotnet 时遇到了一个奇怪的问题。 使用 VS 2019 编译并使用 dotnet 运行时,它按预期工作,但使用单声道编译时,我从未收到任何数据。对于dotnet,我也使用了dotnet add package System.IO.Ports

这是组成项目的两个文件:

using System;
using System.Text;
using SerialPortListener.Serial;

namespace radio

    class Program
    
        private static SerialPortManager _spManager;

        static void Main(string[] args)
        
            Console.WriteLine("Starting");

            _spManager = new SerialPortManager();
            _spManager.NewSerialDataRecieved += new EventHandler<SerialDataEventArgs>(_spManager_NewSerialDataRecieved);
            _spManager.StartListening();

            Console.WriteLine("Waiting here forever...");
            while (true) ;
        

        static void _spManager_NewSerialDataRecieved(object sender, SerialDataEventArgs e)
        
            string str = Encoding.ASCII.GetString(e.Data);
            Console.WriteLine(str);
        
    

using System;
using System.IO.Ports;

namespace SerialPortListener.Serial

    /// <summary>
    /// Manager for serial port data
    /// </summary>
    public class SerialPortManager : IDisposable
    
        public SerialPortManager()
        
        

        ~SerialPortManager()
        
            Dispose(false);
        

        #region Fields
        private SerialPort _serialPort;
        public event EventHandler<SerialDataEventArgs> NewSerialDataRecieved;
        #endregion

        #region Event handlers
        void _serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        
            Console.WriteLine("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");

            int dataLength = _serialPort.BytesToRead;
            byte[] data = new byte[dataLength];
            int nbrDataRead = _serialPort.Read(data, 0, dataLength);
            if (nbrDataRead == 0)
                return;

            NewSerialDataRecieved?.Invoke(this, new SerialDataEventArgs(data));
        

        #endregion

        #region Methods
        /// <summary>
        /// Connects to a serial port defined through the current settings
        /// </summary>
        public void StartListening()
        
            if (_serialPort != null && _serialPort.IsOpen)
                _serialPort.Close();

            _serialPort = new SerialPort("/dev/ttyUSB0", 115200, Parity.None, 8, StopBits.One)
            
                Handshake = Handshake.None,
                DtrEnable = false,
                RtsEnable = false,
                ReadTimeout = 400,
            ;
            _serialPort.DataReceived += new SerialDataReceivedEventHandler(_serialPort_DataReceived);
            _serialPort.Open();
        

        /// <summary>
        /// Closes the serial port
        /// </summary>n
        public void StopListening()
        
            _serialPort.Close();
        

        // Call to release serial port
        public void Dispose()
        
            Dispose(true);
        

        // Part of basic design pattern for implementing Dispose
        protected virtual void Dispose(bool disposing)
        
            if (disposing)
            
                _serialPort.DataReceived -= new SerialDataReceivedEventHandler(_serialPort_DataReceived);
            

            if (_serialPort != null)
            
                if (_serialPort.IsOpen)
                    _serialPort.Close();

                _serialPort.Dispose();
            
        

        #endregion
    

    /// <summary>
    /// EventArgs used to send bytes recieved on serial port
    /// </summary>
    public class SerialDataEventArgs : EventArgs
    
        public SerialDataEventArgs(byte[] dataInByteArray)
        
            Data = dataInByteArray;
        

        /// <summary>
        /// Byte array containing data from serial port
        /// </summary>
        public byte[] Data;
    


使用 mcs Program.cs SerialPortManager.csmono Program.exe 编译它,我得到了这个:

/dab/source/radio# mono Program.exe 开始 永远在这里等着……

使用 dotnet radio.dll,它工作正常。这是一小段摘录:

/dab/empeg# dotnet radio.dll 开始 永远在这里等待... 啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊 serial_notify_thread.cpp: 180:@@ #70008000 0:01:46 serial_notify_thread.cpp: 180:@@ #70008000 0:04:47 serial_notify_thread.cpp: 180:@@ #70008000 0:01:46 serial_notify_thread.cpp: 180:@@ #70008000 0:04:48 serial_notify_thread.cpp: 180:@@ # Poll wait 轮询失败

啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊

不确定我做错了什么,或者为什么它适用于 dotnet,而不是单声道。 Raspberry Pi Zero 上没有我需要使用单声道作为 dotnet 的任何想法。

约翰

【问题讨论】:

一如既往。发布此消息 5 分钟后,我找到了此 antanas.veiverys.com/…,它解决了我的问题。看起来单串口实现缺少事件触发部分。 很高兴你找到它!如果您不介意,您可以将链接添加到您的答案中吗?评论往往会被忽略;)欢迎您! 【参考方案1】:

完全修复:

using System;
using System.Text;
using SerialPortListener.Serial;

namespace radio

    class Program
    
        private static SerialPortManager _spManager;

        static void Main(string[] args)
        
            Console.WriteLine("Starting");

            _spManager = new SerialPortManager();
            _spManager.NewSerialDataRecieved += new EventHandler<SerialDataEventArgs>(_spManager_NewSerialDataRecieved);
            _spManager.StartListening();

            Console.WriteLine("Waiting here forever...");
            while (true) ;
        

        static void _spManager_NewSerialDataRecieved(object sender, SerialDataEventArgs e)
        
            string str = Encoding.ASCII.GetString(e.Data);
            Console.WriteLine(str);
        
    

using System;
using System.IO.Ports;

namespace SerialPortListener.Serial

    /// <summary>
    /// Manager for serial port data
    /// </summary>
    public class SerialPortManager : IDisposable
    
        public SerialPortManager()
        
        

        ~SerialPortManager()
        
            Dispose(false);
        

        #region Fields
        private EnhancedSerialPort _serialPort;
        public event EventHandler<SerialDataEventArgs> NewSerialDataRecieved;
        #endregion

        #region Event handlers
        void _serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        
            Console.WriteLine("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");

            int dataLength = _serialPort.BytesToRead;
            byte[] data = new byte[dataLength];
            int nbrDataRead = _serialPort.Read(data, 0, dataLength);
            if (nbrDataRead == 0)
                return;

            NewSerialDataRecieved?.Invoke(this, new SerialDataEventArgs(data));
        

        #endregion

        #region Methods
        /// <summary>
        /// Connects to a serial port defined through the current settings
        /// </summary>
        public void StartListening()
        
            if (_serialPort != null && _serialPort.IsOpen)
                _serialPort.Close();

            _serialPort = new EnhancedSerialPort("/dev/ttyUSB0", 115200, Parity.None, 8, StopBits.One)
            
                ReadTimeout = 400,
            ;
            _serialPort.DataReceived += new SerialDataReceivedEventHandler(_serialPort_DataReceived);
            _serialPort.Open();
        

        /// <summary>
        /// Closes the serial port
        /// </summary>n
        public void StopListening()
        
            _serialPort.Close();
        

        // Call to release serial port
        public void Dispose()
        
            Dispose(true);
        

        // Part of basic design pattern for implementing Dispose
        protected virtual void Dispose(bool disposing)
        
            if (disposing)
            
                _serialPort.DataReceived -= new SerialDataReceivedEventHandler(_serialPort_DataReceived);
            

            if (_serialPort != null)
            
                if (_serialPort.IsOpen)
                    _serialPort.Close();

                _serialPort.Dispose();
            
        

        #endregion
    

    /// <summary>
    /// EventArgs used to send bytes recieved on serial port
    /// </summary>
    public class SerialDataEventArgs : EventArgs
    
        public SerialDataEventArgs(byte[] dataInByteArray)
        
            Data = dataInByteArray;
        

        /// <summary>
        /// Byte array containing data from serial port
        /// </summary>
        public byte[] Data;
    

// /*
// Copyright 2013 Antanas Veiverys www.veiverys.com
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// */
//
using System;
using System.IO.Ports;
using System.ComponentModel;
using System.IO;
using System.Runtime.InteropServices;
using System.Reflection;

namespace SerialPortListener.Serial

    public class EnhancedSerialPort : SerialPort
    
        public EnhancedSerialPort() : base()
        
        

        public EnhancedSerialPort(IContainer container) : base(container)
        
        

        public EnhancedSerialPort(string portName) : base(portName)
        
        

        public EnhancedSerialPort(string portName, int baudRate) : base(portName, baudRate)
        
        

        public EnhancedSerialPort(string portName, int baudRate, Parity parity) : base(portName, baudRate, parity)
        
        

        public EnhancedSerialPort(string portName, int baudRate, Parity parity, int dataBits) : base(portName, baudRate, parity, dataBits)
        
        

        public EnhancedSerialPort(string portName, int baudRate, Parity parity, int dataBits, StopBits stopBits) : base(portName, baudRate, parity, dataBits, stopBits)
        
        

        // private member access via reflection
        int fd;
        FieldInfo disposedFieldInfo;
        object data_received;

        public new void Open()
        
            base.Open();

            if (IsWindows == false)
            
                FieldInfo fieldInfo = BaseStream.GetType().GetField("fd", BindingFlags.Instance | BindingFlags.NonPublic);
                fd = (int)fieldInfo.GetValue(BaseStream);
                disposedFieldInfo = BaseStream.GetType().GetField("disposed", BindingFlags.Instance | BindingFlags.NonPublic);
                fieldInfo = typeof(SerialPort).GetField("data_received", BindingFlags.Instance | BindingFlags.NonPublic);
                data_received = fieldInfo.GetValue(this);

                new System.Threading.Thread(new System.Threading.ThreadStart(this.EventThreadFunction)).Start();
            
        

        static bool IsWindows
        
            get
            
                PlatformID id = Environment.OSVersion.Platform;
                return id == PlatformID.Win32Windows || id == PlatformID.Win32NT; // WinCE not supported
            
        

        private void EventThreadFunction()
        
            do
            
                try
                
                    var _stream = BaseStream;
                    if (_stream == null)
                    
                        return;
                    
                    if (Poll(_stream, ReadTimeout))
                    
                        OnDataReceived(null);
                    
                
                catch
                
                    return;
                
            
            while (IsOpen);
        

        void OnDataReceived(SerialDataReceivedEventArgs args)
        
            SerialDataReceivedEventHandler handler = (SerialDataReceivedEventHandler)Events[data_received];

            if (handler != null)
            
                handler(this, args);
            
        

        [DllImport("MonoPosixHelper", SetLastError = true)]
        static extern bool poll_serial(int fd, out int error, int timeout);

        private bool Poll(Stream stream, int timeout)
        
            CheckDisposed(stream);
            if (IsOpen == false)
            
                throw new Exception("port is closed");
            
            int error;

            bool poll_result = poll_serial(fd, out error, ReadTimeout);
            if (error == -1)
            
                ThrowIOException();
            
            return poll_result;
        

        [DllImport("libc")]
        static extern IntPtr strerror(int errnum);

        static void ThrowIOException()
        
            int errnum = Marshal.GetLastWin32Error();
            string error_message = Marshal.PtrToStringAnsi(strerror(errnum));

            throw new IOException(error_message);
        

        void CheckDisposed(Stream stream)
        
            bool disposed = (bool)disposedFieldInfo.GetValue(stream);
            if (disposed)
            
                throw new ObjectDisposedException(stream.GetType().FullName);
            
        
    

【讨论】:

以上是关于使用单声道时的 Serial.IO.Ports 问题,适用于 dotnet core 3.1 / arm / raspberry pi 4的主要内容,如果未能解决你的问题,请参考以下文章

windows上没有单声道的单声道sqlite?

AVPlayer 播放单声道音频立体声->单声道

谁知道mp3单声道怎么转立体声

使用单声道输入和立体声输出设置音频单元 iOS

Android音视频系列(七):PCM音频单声道与双声道的相互转换

FFMPEG:两个单声道到一个立体声文件,其持续时间是这两个单声道文件中最长的