web-dev-qa-db-fra.com

Erreur d'exception d'E / S lors de l'utilisation de serialport.open ()

MISE À JOUR FINALE

C'était notre firmware tout le temps. Embarrassant dans une certaine mesure, mais je suis heureux que nous puissions aller de l'avant et je peux mettre l'apprentissage Java off pour un autre jour. Ma réponse est ci-dessous.

[~ # ~] mise à jour [~ # ~]

J'ai donc plus ou moins renoncé à cela. Je pense que c'est un bug qui descend dans l'API, mais je n'ai ni le temps, ni les ressources, ni les compétences nécessaires pour aller au fond des choses. Je pense qu'il existe du matériel auquel Windows donne juste le majeur. J'ai téléchargé Eclipse, je suis passé à Java et j'essaierai de voir si cela fonctionne. Sinon, vous me reviendrez ici. Cependant, j'aimerais absolument résoudre ce problème et donc si quelqu'un a le temps ou l'envie de creuser profondément dans celui-ci, j'adorerais voir ce que vous proposez. Évidemment, je reviendrai ici de temps en temps. S'il vous plaît assurez-vous de me '@' dans vos commentaires donc je suis alerté.


POSTE ORIGINAL

Je sais qu'il y a quelques autres personnes qui s'occupent de ce problème, mais j'espérais que quelqu'un pourrait m'aider. J'essaie de me connecter à un port COM , mais j'obtiens une exception d'E/S lorsque j'essaie d'utiliser la commande serialport.Open():

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

J'utilise un Stellaris LM4F232 pour émuler un port COM. Je peux ouvrir, accéder et obtenir de bons résultats en utilisant Termite (un programme de terminal), mais chaque fois que j'essaie avec Visual Studio, il ne se connecte même pas, et j'obtiens cette erreur. Maintenant, je ne sais même pas vraiment ce que cette erreur signifie et malgré les tentatives de lecture ailleurs, je me sens toujours perdu.

Quelqu'un peut-il m'expliquer ce qui se passe ici et peut-être que je peux commencer à essayer de comprendre cela? Je peux inclure plus de code, mais pour être honnête, il n'y a pas grand-chose là-bas; toutes les propriétés du périphérique de port série sont comme normales, et cela ne se produit qu'avec ce périphérique (je peux utiliser un MSP430 sans problème avec les mêmes détails).

Mon code est affiché ci-dessous pour les personnes qui aimeraient le voir (notez que ce n'est qu'un "bac à sable", pas le programme réel, mais les symptômes sont identiques):

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");
}

et les autres paramètres sont effectués avec le gestionnaire de propriétés (la seule différence est que baud est réglé sur 230400; tous les autres sont par défaut). Je peux ouvrir COM4 avec cela (un MSP430) qui, à toutes fins utiles, est un appareil identique. Je peux ouvrir COM5 avec Termite, donc je sais que la connexion est bonne). Et non, je n'essaye pas de les ouvrir en même temps. Si vous avez besoin de plus d'informations, faites-le moi savoir et je pourrai en publier d'autres.

EDIT: Je suis au troisième jour d'essayer de comprendre cela et toujours pas de chance. Je ne comprends pas vraiment pourquoi je peux accéder à ce port COM via un programme de terminal et pas le mien alors que, autant que je sache, il n'y a absolument aucune différence. Existe-t-il un programme qui peut "examiner" un port COM pour en voir les propriétés (outre le gestionnaire Windows, je veux dire)? Je suis assez frustré et je suis en quelque sorte au point mort dans mon projet jusqu'à ce que je comprenne cela ...

EDIT2: J'ai trouvé une solution de contournement apparente, mais je n'ai pas encore réussi à la faire fonctionner ici . Maintenant, j'obtiens quelques erreurs d'E/S différentes, mais au moins c'est du mouvement (je ne sais pas si c'est une progression). J'ai également appris qu'il s'agit d'un bogue .NET, qui existe depuis 2.0. J'adorerais toujours toute aide, mais si je le découvre, je ferai rapport. Le code de Zach (la solution de contournement liée ci-dessus) est illustré ci-dessous:

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: Jour 6: Je suis toujours en train de me débrouiller. Mes rations d'eau sont faibles, mais je continue de lutter. Je pense que l'aide doit sûrement être à l'horizon. Celui qui trouve ce journal ramène mes restes au Canada et retrouve Nicole. Dis-lui que je l'aime.

Mais sérieusement, je n'ai aucune idée de la cause de ce problème. Je me demande si c'est purement du côté intégré; peut-être parce que c'est SB On-The-Go (OTG), ou parce que l'appareil est également capable d'être un hôte. Quelqu'un a-t-il rencontré ce problème? Cela n'explique pas pourquoi je peux utiliser Termite (un programme terminal, pour les téléspectateurs qui viennent de nous rejoindre). J'ai essayé de trouver un programme de terminal open source qui a) fonctionne et b) voir a). Comme d'habitude, je ferai rapport si je découvre le problème ici, car j'ai maintenant trouvé d'innombrables forums où il semble que les gens ont eu ce problème depuis 2006.

EDIT4: Donc, selon les conseils donnés, j'ai téléchargé une application logicielle de surveillance de port (j'ai obtenu Eltima Serial Port Monitor ), et cela ressemble à un problème de baud:

Screen capture from Eltima

Mais étrangement, peu importe le baud que je mets, il échoue toujours. Et quelqu'un peut-il aussi expliquer ce que signifie la fonction haut/bas? J'ai essayé de le googler, mais les mots clés sont trop généraux. Comme d'habitude, je continuerai de signaler tout changement.

Aussi, pour mémoire, je peux me connecter en utilisant Eltima à un baud de 115200 (comme Termite). Malheureusement, cela ne fonctionne pas dans Visual Studio.

EDIT5: Notre intrigue prend une tournure surprise. Je surveillais ce qui se passe lorsque Termite se connecte au port COM en question et BLAM! Termite lance exactement la même erreur que mon programme, mais il ignore it. Génie, non? Sloppy, mais ça marche. Maintenant, je dois apprendre à ignorer les IOExceptions. Je ferai rapport quand j'aurai compris.

EDIT6: Donc, il s'avère que c'est un problème de débit en bauds, mais cela va plus loin. J'utilise le logiciel Eltima Serial Port Monitoring, et il est très intuitif et facile à utiliser. Je le recommanderais. Après quelques recherches J'ai appris que vous ne pouvez pas ignorer cette exception et toujours vous connecter au port série en utilisant la bibliothèque de .NET.

Je dois donc approfondir l'API Win32 et écrire la mienne. J'ai trouvé quelques pages qui traitent de cela, mais pour être honnête, je n'ai jamais rien fait de tel auparavant, donc cela peut prendre un certain temps avant de faire un rapport, mais je vais certainement le comprendre et revenir à tout le monde. Il y a beaucoup trop de personnes qui souffrent de ce problème.

J'ai trouvé pas mal de forums et de sites Web où je peux voir exactement les mêmes symptômes, mais personne n'a vraiment fait grand-chose à part dire "Ouais, .NET craint". Je prévois d'écrire une classe de bibliothèque statique complète, puis de publier sur mon site Web, ici et partout où je peux. Espérons que .NET en tiendra compte (ce bogue existe depuis 2.0).

20
tmwoods

Et donc notre histoire passionnante se termine. C'était un firmware tout le temps (c'est-à-dire le code sur le périphérique intégré). Nous avons changé quelques fonctions et essentiellement fouillé, haché, ajouté et complètement nettoyé le code et le tour est joué, le code fonctionne. Cette photo résume assez bien. Maudissez-vous le firmware !!

Cependant, le bogue décrit dans ma (longue) question persiste encore pour beaucoup de gens et je sais qu'il y a beaucoup de gens qui l'ont encore. Tout ce que je peux dire, c'est bonne chance et quadruple vérifier votre firmware (apparemment une triple vérification ce n'est pas suffisant de nos jours).

1
tmwoods

Cela vient du pilote du port série, il n'est pas satisfait de l'un des paramètres. Le débit en baud étant un bon candidat, les conducteurs ont tendance à n'autoriser que jusqu'à 115 200. Bien que cela ne devrait pas être une restriction lorsqu'il s'agit d'un produit de bus CAN dédié.

La meilleure façon de résoudre ce problème est d'utiliser l'utilitaire PortMon de SysInternals, vous pouvez voir ce qui est envoyé au pilote. Observez-le d'abord pour Terminate, c'est votre base de référence connue. Ensuite, bricolez avec les propriétés SerialPort jusqu'à ce que les commandes d'initialisation, telles que vous les voyez dans PortMon, envoyées par votre programme correspondent à celles de Termite. Juste les valeurs, pas l'ordre. Si cela ne fonctionne pas non plus, apportez-le au parking et revenez dessus avec votre voiture plusieurs fois et achetez une autre marque.


Mise à jour: cela ressemble certainement à un problème de vitesse de transmission. C'est un problème dans .NET, il n'ignorera pas le code de retour d'erreur du pilote comme le font vos programmes d'émulation de terminal. La valeur réelle ne devrait pas avoir d'importance puisque vous parlez à un port série émulé. Il y a cependant un problème possible avec la vitesse du bus CAN, les tarifs sont variables et je ne sais pas comment ils sont négociés. Cela avait tendance à être fait avec les commutateurs DIP dans le passé, il se pourrait bien que le pilote veuille que vous spécifiiez la vitesse via le paramètre de vitesse de transmission. Il devrait y avoir quelque chose à ce sujet sur la boîte ou dans le manuel. Les vitesses typiques sont de 40, 250 ou 500 Kbps. Le fabricant saurait certainement, appelez-les.

6
Hans Passant

Je fais face à un problème similaire à celui signalé dans ce fil, mais j'ai réussi à résoudre le problème!

J'utilise STM32F2xx pour le VCP!

Et en effet c'est mon problème de firmware, j'oublie d'inclure les paramètres du port série dans mon rappel USB!

Le processus de connexion du port série à partir du PC et du firmware:

  1. Lorsqu'un PC ouvre une communication par port série, le PC envoie une commande au "point de terminaison de configuration"
  2. Dans le firmware, il y aurait un rappel et le firmware fournira toutes les informations USB (ils l'appellent descripteur USB)
  3. Les informations USB sont la configuration de chaque point de terminaison (ex: latence, transmission de la taille des données, type d'USB - haute vitesse ou basse vitesse)
  4. Une fois que le micrologiciel a envoyé toutes les informations, le PC acquitte et la communication USB est ouverte avec succès
  5. Ensuite, le PC enverra une commande pour obtenir les paramètres du port série à partir du firmware
  6. Les paramètres du port série sont - débit en bauds, parité des données, longueur des bits
  7. Dans le firmware, il devrait répondre aux paramètres du port série au PC (mon erreur se produit ici, je ne répondrai pas aux paramètres du port série au PC)
  8. En cas de succès, le PC démarre la communication du port série!
  9. En cas d'échec, le PC affichera une erreur de port série ouvert (mais, notez que cette erreur est parfois contournée)

Dans le code du micrologiciel 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

J'ai rencontré la même situation. J'essaie de connecter la communication série à mon dongle USB 3G (Huawei E303F) à/dev/ttyUSB0. J'utilise mono en Raspbian (raspberry-pi2). Dans mon développement PC et macOS, mon programme fonctionne bien. Mais quand je le déploie dans Raspbian, j'ai eu l'erreur IOException Broken Pipe sur Serial.Open ().

J'ai pris mes trois jours de débogage et j'ai essayé toutes les solutions possibles. Enfin, je trouve que je dois régler ...

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

Avant les appels .Ouvrez (). J'espère que cela pourra aider d'autres personnes confrontées au même problème que le mien.

3
tanutapi