web-dev-qa-db-ja.com

serialport.open()使用時のI / O例外エラー

最終更新

ずっとファームウェアでした。ある程度恥ずかしいですが、私は前進できることを嬉しく思います。私は別の日のために学習をJavaオフにすることができます。私の答えは以下です。

[〜#〜]更新[〜#〜]

だから私は多かれ少なかれこれをあきらめています。それはAPIにまで及んでいるバグだと思いますが、私はその根底に到達するための時間、リソース、スキルセットを持っていません。 Windowsが中指を提供するだけのハードウェアがいくつかあると思います。私はEclipseをダウンロードし、Javaに切り替えて、それが機能するかどうかを確認しようとします。機能しない場合は、ここに表示されます。ただし、これを解決したいと思っています。これについて深く掘り下げる時間または傾向があります。あなたが思いついたものを見てみたいと思います。明らかに、私は時々ここでチェックします。コメントで「@」を必ず確認してください。警告した。


元の投稿

この問題に取り組んでいる人が他にも数人いることは知っていますが、誰かが私を助けてくれることを望んでいました。 COMポート に接続しようとしていますが、serialport.Open()コマンドを使用しようとすると、I/O例外が発生します。

System.IO.IOException: The parameter is incorrect.

   at System.IO.Ports.InternalResources.WinIOError(Int32 errorCode, String str)
   at System.IO.Ports.InternalResources.WinIOError()
   at System.IO.Ports.SerialStream.InitializeDCB(Int32 baudRate, Parity parity, Int32 dataBits, StopBits stopBits, Boolean discardNull)
   at System.IO.Ports.SerialStream..ctor(String portName, Int32 baudRate, Parity parity, Int32 dataBits, StopBits stopBits, Int32 readTimeout, Int32 writeTimeout, Handshake handshake, Boolean dtrEnable, Boolean rtsEnable, Boolean discardNull, Byte parityReplace)
   at System.IO.Ports.SerialPort.Open()
   at *programtitlehere.cs*:line 90

COMポートをエミュレートするためにStellaris LM4F232を使用しています。 Termite(ターミナルプログラム)を使用して開いたり、アクセスしたりすると、良い結果が得られますが、Visual Studioを使用しようとすると、接続できず、このエラーが発生します。今、私はこのエラーが何を意味するのかさえ本当に知りません、そして、どこか他の場所を読み込もうとしたにもかかわらず、私はまだ迷っています。

ここで何が起こっているのか誰かに説明してもらえますか?さらに多くのコードを含めることができますが、正直なところ、それほど多くはありません。シリアルポートデバイスのすべてのプロパティは通常どおりで、このデバイスでのみ発生します(同じ詳細でMSP430を問題なく使用できます)。

私のコードは、それを見たい人のために以下に示されています(これは実際のプログラムではなく単なる「サンドボックス」ですが、症状は同じです):

try
{
    serialPort1.PortName = "COM5";
    serialPort1.Open();
    if (serialPort1.IsOpen == true)
    {
        textBox1.Text = "CONNECTED";
    }
    else
    {
        textBox1.Text = "NOT CONNECTED";
    }
}
catch (Exception ex)
{
    MessageBox.Show("Error: " + ex.ToString(), "ERROR");
}

その他の設定はプロパティマネージャで行われます(唯一の違いは、ボーが230400に設定されていることです。その他の設定はすべてデフォルトです)。私はこれ(MSP430)でCOM4を開くことができます。これは、すべての目的と目的で同一のデバイスです。シロアリでCOM5を開くことができるので、接続は良好です。いいえ、私はそれらを同時に開こうとはしていません。さらに情報が必要な場合はお知らせください。投稿できます。

編集:私はこれを理解しようと試みて3日目ですが、まだ運がありません。私が見ることができるほど近いところにまったく違いがないときに、自分のCOMポートではなくターミナルプログラムを介してこのCOMポートにアクセスできる理由が本当にわかりません。 COMポートを「調査」して、そのプロパティを確認できるプログラムはありますか(つまり、Windowsマネージャーのほかに)。私はかなりイライラしてきており、これがわかるまでは、プロジェクトでまだスタンドになっています...

EDIT2:明らかな回避策を見つけましたが、まだ機能させることができません ここ 。今、私はいくつかの異なるI/Oエラーを受け取りますが、少なくともそれは動きです(それが進行中であるかどうかはわかりません)。これは.NETのバグであることも知りました。これは2.0以降に存在しています。私はまだどんな助けも大好きですが、私がそれを理解したら、私は戻って報告します。 Zachのコード(上記にリンクされた回避策)を以下に示します。

using System;
using System.IO;
using System.IO.Ports;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.Win32.SafeHandles;

namespace SerialPortTester
{
    public class SerialPortFixer : IDisposable
    {
        public static void Execute(string portName)
        {
            using (new SerialPortFixer(portName))
            {
            }
        }

        #region IDisposable Members

        public void Dispose()
        {
            if (m_Handle != null)
            {
                m_Handle.Close();
                m_Handle = null;
            }
        }

        #endregion

        #region Implementation

        private const int DcbFlagAbortOnError = 14;
        private const int CommStateRetries = 10;
        private SafeFileHandle m_Handle;

        private SerialPortFixer(string portName)
        {
            const int dwFlagsAndAttributes = 0x40000000;
            const int dwAccess = unchecked((int) 0xC0000000);

            if ((portName == null) || !portName.StartsWith("COM", StringComparison.OrdinalIgnoreCase))
            {
                throw new ArgumentException("Invalid Serial Port", "portName");
            }
            SafeFileHandle hFile = CreateFile(@"\\.\" + portName, dwAccess, 0, IntPtr.Zero, 3, dwFlagsAndAttributes,
                                              IntPtr.Zero);
            if (hFile.IsInvalid)
            {
                WinIoError();
            }
            try
            {
                int fileType = GetFileType(hFile);
                if ((fileType != 2) && (fileType != 0))
                {
                     throw new ArgumentException("Invalid Serial Port", "portName");
                }
                m_Handle = hFile;
                InitializeDcb();
            }
            catch
            {
                hFile.Close();
                m_Handle = null;
                throw;
            }
        }

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern int FormatMessage(int dwFlags, HandleRef lpSource, int dwMessageId, int dwLanguageId,
                                                StringBuilder lpBuffer, int nSize, IntPtr arguments);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern bool GetCommState(SafeFileHandle hFile, ref Dcb lpDcb);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern bool SetCommState(SafeFileHandle hFile, ref Dcb lpDcb);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern bool ClearCommError(SafeFileHandle hFile, ref int lpErrors, ref Comstat lpStat);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern SafeFileHandle CreateFile(string lpFileName, int dwDesiredAccess, int dwShareMode,
                                                        IntPtr securityAttrs, int dwCreationDisposition,
                                                        int dwFlagsAndAttributes, IntPtr hTemplateFile);

        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern int GetFileType(SafeFileHandle hFile);

        private void InitializeDcb()
        {
            Dcb dcb = new Dcb();
            GetCommStateNative(ref dcb);
            dcb.Flags &= ~(1u << DcbFlagAbortOnError);
            SetCommStateNative(ref dcb);
        }

        private static string GetMessage(int errorCode)
        {
            StringBuilder lpBuffer = new StringBuilder(0x200);
            if (
                FormatMessage(0x3200, new HandleRef(null, IntPtr.Zero), errorCode, 0, lpBuffer, lpBuffer.Capacity,
                              IntPtr.Zero) != 0)
            {
                return lpBuffer.ToString();
            }
            return "Unknown Error";
        }

        private static int MakeHrFromErrorCode(int errorCode)
        {
            return (int) (0x80070000 | (uint) errorCode);
        }

        private static void WinIoError()
        {
            int errorCode = Marshal.GetLastWin32Error();
            throw new IOException(GetMessage(errorCode), MakeHrFromErrorCode(errorCode));
        }

        private void GetCommStateNative(ref Dcb lpDcb)
        {
            int commErrors = 0;
            Comstat comStat = new Comstat();

            for (int i = 0; i < CommStateRetries; i++)
            {
                if (!ClearCommError(m_Handle, ref commErrors, ref comStat))
                {
                     WinIoError();
                }
                if (GetCommState(m_Handle, ref lpDcb))
                {
                     break;
                }
                if (i == CommStateRetries - 1)
                {
                     WinIoError();
                }
            }
        }

        private void SetCommStateNative(ref Dcb lpDcb)
        {
            int commErrors = 0;
            Comstat comStat = new Comstat();

            for (int i = 0; i < CommStateRetries; i++)
            {
                 if (!ClearCommError(m_Handle, ref commErrors, ref comStat))
                 {
                     WinIoError();
                 }
                 if (SetCommState(m_Handle, ref lpDcb))
                 {
                     break;
                 }
                 if (i == CommStateRetries - 1)
                 {
                     WinIoError();
                 }
            }
        }

        #region Nested type: COMSTAT

        [StructLayout(LayoutKind.Sequential)]
        private struct Comstat
        {
            public readonly uint Flags;
            public readonly uint cbInQue;
            public readonly uint cbOutQue;
        }

        #endregion

        #region Nested type: DCB

        [StructLayout(LayoutKind.Sequential)]
        private struct Dcb
        {
            public readonly uint DCBlength;
            public readonly uint BaudRate;
            public uint Flags;
            public readonly ushort wReserved;
            public readonly ushort XonLim;
            public readonly ushort XoffLim;
            public readonly byte ByteSize;
            public readonly byte Parity;
            public readonly byte StopBits;
            public readonly byte XonChar;
            public readonly byte XoffChar;
            public readonly byte ErrorChar;
            public readonly byte EofChar;
            public readonly byte EvtChar;
            public readonly ushort wReserved1;
        }

        #endregion

        #endregion
    }

    internal class Program
    {
        private static void Main(string[] args)
        {
            SerialPortFixer.Execute("COM1");
            using (SerialPort port = new SerialPort("COM1"))
            {
                port.Write("test");
            }
        }
    }
}

EDIT3:6日目:私はまだこれでプラグインしています。私の給水量は少ないですが、それでも私は苦労しています。きっと手助けが地平線上にあるに違いない。このジャーナルを見つけた人は誰でも私の遺物をカナダに持ち帰り、ニコールを見つけます。私は彼女を愛していると彼女に言ってください。

しかし、真剣に、私はこの問題の原因が何なのかわかりません。それが純粋に埋め込み側にあるのかと思います。多分それは SB On-The-Go (OTG)であるか、またはデバイスがホストにもなることができるためです。誰かがその問題に遭遇しましたか?しかし、なぜ私はTermite(私たちに参加するだけの視聴者のための端末プログラム)を使用できるのかについては説明していません。私は、a)機能し、b)a)を参照するオープンソースのターミナルプログラムを見つけようと努めてきました。いつものように、ここで問題を発見した場合は、2006年にさかのぼってこの問題が発生したと思われるフォーラムが無数に見つかったので、報告します。

EDIT4:したがって、与えられたアドバイスに従って、ポート監視ソフトウェアアプリケーションをダウンロードしました( Eltimaシリアルポートモニター を取得しました)。これは、ボーの問題のように見えます。

Screen capture from Eltima

しかし、奇妙なことに、どのボーを設定しても、それでも失敗します。また、誰かがアップ/ダウンの意味を説明できますか?グーグルで試しましたが、キーワードが一般的すぎます。いつものように、私はすべての変更を報告し続けます。

また、記録のために、Eltimaを使用して115200のボーで接続できます(Termiteと同じ)。残念ながら、これはVisual Studioでは機能しません。

EDIT5:私たちのプロットは驚きのひねりを加えています。 Termiteが問題のCOMポートとBLAMに接続するとどうなるかを監視していました。シロアリは私のプログラムとまったく同じエラーをスローしますが、それは無視します。天才ですよね?ずさんですが、うまくいきます。次に、IOExceptionを無視する方法を学ぶ必要があります。わかり次第報告します。

EDIT6:結局のところ、ボーレートの問題ですが、さらに深くなります。 Eltima Serial Port Monitoringソフトウェアを使用していますが、非常に直感的で使いやすいソフトウェアです。私はそれをお勧めします。後 いくつかの調査 この例外を無視して、.NETのライブラリを使用してシリアルポートに接続することはできないことを学びました。

そのため、Win32 APIをさらに深く理解し、独自のAPIを作成する必要があります。これについて触れているページをいくつか見つけましたが、正直なところ、これまでにこのようなことをしたことがないので、報告するまで少し時間がかかるかもしれませんが、間違いなくこれを理解して全員に返答するつもりです。この問題に苦しむ人が多すぎます。

私はまったく同じ症状を見ることができるフォーラムやウェブサイトをかなり見つけましたが、「うん、.NETはうんざりだ」と言うことを除けば、実際に多くのことをした人はいません。完全な静的ライブラリクラスを作成し、自分のWebサイト、ここ、および他のどこでも公開する予定です。うまくいけば、.NETが気付くでしょう(このバグは2.0以降に存在しています)。

20
tmwoods

そして、私たちのスリリングな物語は終わりを迎えます。それはずっとファームウェアでした(つまり、組み込みデバイスのコード)。いくつかの機能を変更し、基本的にコードを細かく切り刻み、追加し、完全にクリーンアップしました。コードは機能しています。 この写真 はかなりうまくまとめています。ファームウェアを呪ってください!!

ただし、私の(長い)質問で説明されているバグは、多くの人にとって依然として持続し、私はまだそれを持っている多くの人がいることを知っています。私が言えることは、幸運とファームウェアを4回チェックすることだけです(どうやら最近では3回チェックするだけでは不十分です)。

1
tmwoods

これはシリアルポートドライバからのもので、設定の1つに不満があります。ボーレートが良い候補であるので、ドライバーは115200までしか許可しない傾向があります。これが専用のCANバス製品である場合、これは制限ではないはずです。

これに取り組む最善の方法は、SysInternalsのPortMonユーティリティを使用することです。ドライバーに何が送信されているかを確認できます。まず、Terminateで確認します。これが、既知の作業ベースラインです。次に、PortMonで確認できるように、プログラムから送信された初期化コマンドがTermiteのコマンドと一致するまで、SerialPortプロパティをいじくり回します。順序ではなく、単なる値です。それでもうまくいかない場合は、駐車場に運び、車で何回か戻して、別のブランドを購入します。


更新:確かにボーレートの問題のように見えます。これは.NETの問題です。ターミナルエミュレータプログラムのようにドライバのエラーリターンコードを無視することはありません。 エミュレートシリアルポートと通信しているので、実際の値は重要ではありません。ただし、CANバスの速度に問題がある可能性があり、レートは可変であり、それらがどのようにネゴシエートされるかは不明です。これは、昔はDIPスイッチで行われる傾向がありましたが、ボーレート設定で速度を指定することをドライバーが希望している可能性があります。箱やマニュアルにそれについて何かがあるはずです。通常の速度は40、250、または500 Kbpsです。メーカーは確かに知っているでしょう、彼らに電話をしてください。

6
Hans Passant

このスレッドで報告されているのと同じような問題に直面しましたが、なんとか問題を解決できました!

VCPにSTM32F2xxを使用しています。

そして、確かにそれは私のファームウェアの問題です。シリアルポート設定をUSBコールバックに含めるのを忘れています!

PCとファームウェアからシリアルポートを接続するプロセス:

  1. PCがシリアルポート通信を開くと、PCは「構成エンドポイント」にコマンドを送信します
  2. ファームウェアでは、コールバックがあり、ファームウェアはすべてのUSB情報を提供します(USB記述子と呼ばれます)。
  3. USB情報は、各エンドポイントの構成です(例:待ち時間、データサイズの送信、USBの種類-高速または低速)。
  4. ファームウェアがすべての情報の送信を完了すると、PCが確認し、USB通信が正常に開きます
  5. 次に、PCはファームウェアからシリアルポート設定を取得するコマンドを送信します
  6. シリアルポート設定は-ボーレート、データパリティ、ビット長
  7. ファームウェアでは、シリアルポート設定をPCに返信する必要があります(ここで私の間違いが発生しました。シリアルポート設定をPCに返信しないでください
  8. 成功すると、PCはシリアルポート通信を開始します。
  9. 失敗した場合、PCはオープンシリアルポートエラーを出します(ただし、このエラーはバイパスされる場合があることに注意してください)

STM32ファームウェアコード:

static int8_t CDC_Control_FS  (uint8_t cmd, uint8_t* pbuf, uint16_t length)
{   
    switch (cmd) {
       case CDC_GET_LINE_CODING:     
        {
            //I was missing this part
            uint32_t baudrate = 9600;
            pbuf[0] = (uint8_t)(baudrate);
            pbuf[1] = (uint8_t)(baudrate >> 8);
            pbuf[2] = (uint8_t)(baudrate >> 16);
            pbuf[3] = (uint8_t)(baudrate >> 24);
            pbuf[4] = 0;
            pbuf[5] = 0;
            pbuf[6] = 8;
            break;
        }:
....
3
Tim

私も同じ状況に遭遇しました。/dev/ttyUSB0にある3G USBドングル(Huawei E303F)にシリアル通信を接続しようとしています。 Raspbian(raspberry-pi2)でモノラルを使用しています。開発用PCとmacOSでは、プログラムは正常に動作します。しかし、それをRaspbianにデプロイすると、Serial.Open()でIOException Broken Pipeエラーが発生しました。

私は3日間のデバッグを行い、すべての可能な解決策を試しました。最後に、設定する必要があることがわかりました...

serialPort.DtrEnable = true;
serialPort.RtsEnable = true;

.Open()を呼び出す前。これが私の同じ問題に直面している他の人々の助けになることを願っています。

3
tanutapi