none
ボーレートを途中で再設定したときにリードが失敗する RRS feed

  • 質問

  • マイコンとシリアルポートクラスを使用して通信をしています。
    ボーレート変更コマンドがあり、マイコンのボーレート設定を変更した後に
    マイコン側からの返信を受けるのですが、そのときにホスト側のボーレートを
    変更しなければなりません。
    シリアルポートをオープンしたままだとボーレートを変更してもだめなようなので
    いったんクローズしてから、オープンしなおしてマイコンからの返信をリードするように
    変更しましたが、ボーレートが変更しなくてもリードが失敗するようになりました。
    うまく解決する方法を教えてください。

    MSDN会員ですがのインシデントを全部消費してしまったのでここで質問させていただきます。


    int baudrate=19200;
    int[] BaudRate = { 2400, 4800, 9600, 19200 };

            public Form1()
            {
                InitializeComponent();
                timeBeginPeriod(1); //1msの精度を出す
                comboBox1.Items.Add("COM1");
                comboBox1.Items.Add("COM2");
                comboBox1.Items.Add("COM3");
                comboBox1.Items.Add("COM4");
                comboBox1.Items.Add("COM5");
                comboBox1.Items.Add("COM6");
                comboBox1.Items.Add("COM7");
                comboBox1.Items.Add("COM8");
                comboBox1.Items.Add("COM9");
                comboBox1.Items.Add("COM10");
                comboBox1.SelectedIndex = 2;  //最初はCOM3
               
                comboBox2.Items.Add("2400");
                comboBox2.Items.Add("4800");
                comboBox2.Items.Add("9600");
                comboBox2.Items.Add("19200");
                comboBox2.SelectedIndex = 3;  //最初は19200
            }



    private void comboBox2_SelectedIndexChanged(object sender, EventArgs e) 

        int index = 3;
         index = comboBox2.SelectedIndex; //コンボボックスでのボーレートの位置の取得
      serialPort1.BaudRate=baudrate;  //現在のボーレート
        try
       {
             serialPort1.Open();
       ]
       catch
       {
             MessageBox.Show("PORT OPEN ERROR");
       }
      byte[] wd = new byte[15];
      //ボーレートを設定するコマンド
       wd[0]=0x57;
       wd[1]=1;
       ---
       wd[5]=(byte)index;  //ボーレートの設定
       wd[6]=(byte)(wd[1]+wd[2]+wd[3]+wd[4]+wd5]); //チェックサム
       try
      {
        serialPort1.Write(WriteData, 0, 7); //コマンド送信
     }
      catch
      {
            MessageBox.Show("WRITE ERROR");
      }
      マイコン側はコマンドを受け付けて違うボーレート(もしくはボーレート)でACK、NCKコマンド列を返す

     //serialPort1.BaudRate=BaudRate[index];  //ボーレートの変更(オープンしたままだとボーレートは変更できない?)

     //下記のようにしたが同じボーレートでもリードを失敗するようになってしまった。
     serialPort1.Close();  //いったんクローズ

      serialPort1.BaudRate=BaudRate[index]; //ボーレートの設定
     try
    {
         serialPort1.Open();  //ポートのオープン
    }
    catch
    {
         MessageBox.Show("PORT OPEN ERROR");
    }
    //オープンに時間がかかるのではと思いマイコン側の送信のタイミングを
    //遅らせてみるが(500msくらいまで遅くした)でもだめ????
     try
     {
          byte[] rd = new byte[10];
          int rdata;
          int i;
          for(i=0;i<10;i++)
       {
            rdata=serialPort1.ReadByte
            rd[i]=(byte)rd;
         
         コマンド解析処理
            ACKなら MessageBox.Show("ACK"); baudrate=BaudRate[index];
                    NACKなら MessageBox.Show("NACK");
       }
     }
     catch
     {
       MesssageBox.Show("READ ERROR");
     }

    メッセージボックスで"READ ERROR"で終了してしまう。



    2009年7月9日 3:29

回答

  • 試してみましたがPC側のBaudRateは切替できているのは確認できました。

    PC1台にシリアルポートが4個付いてる状態でクロスケーブルをつなぎ、送受信の組合せを行ってテストしてます。
    COM1 PC本体のシリアルポート
    COM2 PC本体のシリアルポート(増設)
    COM6 USBシリアル変換ケーブル(A社製)
    COM7 USBシリアル変換ケーブル(B社製)

    シリアルポートを開いたままBaudRateを切り替えても有効みたいですよ。

    PC側がおかしいのか、マイコン側がおかしいのかを順番に切り分けてみましょう。。
    1. パソコンのシリアルポートとパソコンのシリアルポートをクロスケーブルでつないでPCのボーレート切替のチェック。
    2. マイコンのボーレート切替処理の部分を飛ばして、ボーレート固定のまま応答可能かチェック。
    3. マイコンがボーレート切替処理を行ったら、マイコンから適当なデータを繰返し送信するようにしておき、パソコン側の受信ボーレートを順に切り替えてみて、どのボーレートでマイコンが送信しているか調べてみる。
    ハードウェアの問題も絡んでくるときは、問題の切り分けが大事ですよ。

    以下テストに使ったコード

    namespace ConsoleApplication1
    {
        class Program
        {
            static void Main(string[] args)
            {
                System.Threading.Thread threadA = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(PortActionA));
                System.Threading.Thread threadB = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(PortActionB));
    
                threadB.Start("COM1");
                threadA.Start("COM2");
    
                threadB.Join();
                threadA.Join();          
            }
    
            private static void PortActionA(object portName)
            {
                System.IO.Ports.SerialPort port = new System.IO.Ports.SerialPort((string)portName );
                port.ErrorReceived += delegate(object sender, System.IO.Ports.SerialErrorReceivedEventArgs e)
                {
                    Console.WriteLine("B:" + e.EventType.ToString()); //ボーレートが違うならフレームエラーになるはず
                };
    
                port.BaudRate = 4800;
                port.Open();
                System.Threading.Thread.Sleep(100); //PortBが開くのをちょっと待つ
                try
                {
                    string line;
                    //PortAは送信してから異なるボーレートでの受信待ち
                    PortBaudrareWriteLine(port, 9600, "ABC");
                    line = PortBaudrareReadLine(port, 38400, 2000);
                    Console.WriteLine("A:" + line);
                    PortBaudrareWriteLine(port, 19200, "GHI"); //異なるボーレートで送信してみる
                }
                catch (System.TimeoutException)
                {
                    Console.WriteLine("A:TimeOut");
                }
                finally
                {
                    port.Close();
                }
            }
    
            private static void PortActionB(object portName)
            {
                System.IO.Ports.SerialPort port = new System.IO.Ports.SerialPort((string)portName );
                port.ErrorReceived += delegate(object sender ,System.IO.Ports.SerialErrorReceivedEventArgs e){
                    Console.WriteLine("B:" + e.EventType.ToString()); //ボーレートが違うならフレームエラーになるはず
                };
                port.BaudRate = 19200;
                port.Open();
           
                try
                {
                    string line;
                    //PortBは受信してから異なるボーレートで折り返し
                    line = PortBaudrareReadLine(port, 9600, 2000);
                    Console.WriteLine("B:" + line);
                    PortBaudrareWriteLine(port, 38400, line + "DEF");
                    line = PortBaudrareReadLine(port, 38400, 2000); //異なるボーレートで受信してみる
                    Console.WriteLine("B:" + line);
    
                }
                catch (System.TimeoutException)
                {
                    Console.WriteLine("B:TimeOut");
                    return;
                }
                finally
                {
                    port.Close();
                }
            }
    
            /// <summary>ボーレートを指定して文字列を1行受信する</summary>
            /// <param name="port">受信したいシリアルポート</param>
            /// <param name="baudrate">ボーレート</param>
            /// <param name="timeoutMs">タイムアウト(ミリ秒)</param>
            /// <returns>受信した行</returns>
            private static string PortBaudrareReadLine(System.IO.Ports.SerialPort port, int baudrate ,int timeoutMs)
            {
                if(port.BaudRate !=  baudrate)
                {
                    System.Threading.Thread.Sleep(100); //ボーレート切替の余裕を持たせる
                    port.BaudRate = baudrate ;
                }
    
                port.ReadTimeout = timeoutMs;
                return port.ReadLine();
            }
    
            /// <summary>ボーレートを指定して文字列を1行送信する</summary>
            /// <param name="port">送信したいシリアルポート</param>
            /// <param name="baudrate">ボーレート</param>
            /// <param name="line)">送信したい文字列</param>
            private static void PortBaudrareWriteLine(System.IO.Ports.SerialPort port, int baudrate, string line)
            {
                if (port.BaudRate != baudrate)
                {
                    while (port.BytesToWrite > 0) { System.Threading.Thread.Sleep(10); };//一応書き込みバッファが空になるのを待っておく
                    System.Threading.Thread.Sleep(100); //ボーレート切替の余裕を持たせる
                    port.BaudRate = baudrate;
                }
    
                port.WriteLine(line);
                while (port.BytesToWrite > 0) { System.Threading.Thread.Sleep(10); };//一応書き込みバッファが空になるのを待っておく
            }
        }
    }
    


    #とはいってもUSBシリアルのボーレートをいじくってたら突然PCがリセットすると言う現象が発生したのは一体…
    • 編集済み gekkaMVP 2009年7月9日 10:42 ソースコードにErrorReceivedの処理を追加
    • 回答としてマーク suntower 2009年7月15日 8:10
    2009年7月9日 10:26
  • ボーレート変更機能はホストのプログラミングが複雑になるのと
    RS-485変換のハードのタイミングに依存してしまうため
    ボーレートは固定で行くことになりました。
    いろいろアドバイスありがとうございました。
    • 回答としてマーク suntower 2009年7月15日 8:09
    2009年7月15日 8:09

すべての返信

  • 試してみましたがPC側のBaudRateは切替できているのは確認できました。

    PC1台にシリアルポートが4個付いてる状態でクロスケーブルをつなぎ、送受信の組合せを行ってテストしてます。
    COM1 PC本体のシリアルポート
    COM2 PC本体のシリアルポート(増設)
    COM6 USBシリアル変換ケーブル(A社製)
    COM7 USBシリアル変換ケーブル(B社製)

    シリアルポートを開いたままBaudRateを切り替えても有効みたいですよ。

    PC側がおかしいのか、マイコン側がおかしいのかを順番に切り分けてみましょう。。
    1. パソコンのシリアルポートとパソコンのシリアルポートをクロスケーブルでつないでPCのボーレート切替のチェック。
    2. マイコンのボーレート切替処理の部分を飛ばして、ボーレート固定のまま応答可能かチェック。
    3. マイコンがボーレート切替処理を行ったら、マイコンから適当なデータを繰返し送信するようにしておき、パソコン側の受信ボーレートを順に切り替えてみて、どのボーレートでマイコンが送信しているか調べてみる。
    ハードウェアの問題も絡んでくるときは、問題の切り分けが大事ですよ。

    以下テストに使ったコード

    namespace ConsoleApplication1
    {
        class Program
        {
            static void Main(string[] args)
            {
                System.Threading.Thread threadA = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(PortActionA));
                System.Threading.Thread threadB = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(PortActionB));
    
                threadB.Start("COM1");
                threadA.Start("COM2");
    
                threadB.Join();
                threadA.Join();          
            }
    
            private static void PortActionA(object portName)
            {
                System.IO.Ports.SerialPort port = new System.IO.Ports.SerialPort((string)portName );
                port.ErrorReceived += delegate(object sender, System.IO.Ports.SerialErrorReceivedEventArgs e)
                {
                    Console.WriteLine("B:" + e.EventType.ToString()); //ボーレートが違うならフレームエラーになるはず
                };
    
                port.BaudRate = 4800;
                port.Open();
                System.Threading.Thread.Sleep(100); //PortBが開くのをちょっと待つ
                try
                {
                    string line;
                    //PortAは送信してから異なるボーレートでの受信待ち
                    PortBaudrareWriteLine(port, 9600, "ABC");
                    line = PortBaudrareReadLine(port, 38400, 2000);
                    Console.WriteLine("A:" + line);
                    PortBaudrareWriteLine(port, 19200, "GHI"); //異なるボーレートで送信してみる
                }
                catch (System.TimeoutException)
                {
                    Console.WriteLine("A:TimeOut");
                }
                finally
                {
                    port.Close();
                }
            }
    
            private static void PortActionB(object portName)
            {
                System.IO.Ports.SerialPort port = new System.IO.Ports.SerialPort((string)portName );
                port.ErrorReceived += delegate(object sender ,System.IO.Ports.SerialErrorReceivedEventArgs e){
                    Console.WriteLine("B:" + e.EventType.ToString()); //ボーレートが違うならフレームエラーになるはず
                };
                port.BaudRate = 19200;
                port.Open();
           
                try
                {
                    string line;
                    //PortBは受信してから異なるボーレートで折り返し
                    line = PortBaudrareReadLine(port, 9600, 2000);
                    Console.WriteLine("B:" + line);
                    PortBaudrareWriteLine(port, 38400, line + "DEF");
                    line = PortBaudrareReadLine(port, 38400, 2000); //異なるボーレートで受信してみる
                    Console.WriteLine("B:" + line);
    
                }
                catch (System.TimeoutException)
                {
                    Console.WriteLine("B:TimeOut");
                    return;
                }
                finally
                {
                    port.Close();
                }
            }
    
            /// <summary>ボーレートを指定して文字列を1行受信する</summary>
            /// <param name="port">受信したいシリアルポート</param>
            /// <param name="baudrate">ボーレート</param>
            /// <param name="timeoutMs">タイムアウト(ミリ秒)</param>
            /// <returns>受信した行</returns>
            private static string PortBaudrareReadLine(System.IO.Ports.SerialPort port, int baudrate ,int timeoutMs)
            {
                if(port.BaudRate !=  baudrate)
                {
                    System.Threading.Thread.Sleep(100); //ボーレート切替の余裕を持たせる
                    port.BaudRate = baudrate ;
                }
    
                port.ReadTimeout = timeoutMs;
                return port.ReadLine();
            }
    
            /// <summary>ボーレートを指定して文字列を1行送信する</summary>
            /// <param name="port">送信したいシリアルポート</param>
            /// <param name="baudrate">ボーレート</param>
            /// <param name="line)">送信したい文字列</param>
            private static void PortBaudrareWriteLine(System.IO.Ports.SerialPort port, int baudrate, string line)
            {
                if (port.BaudRate != baudrate)
                {
                    while (port.BytesToWrite > 0) { System.Threading.Thread.Sleep(10); };//一応書き込みバッファが空になるのを待っておく
                    System.Threading.Thread.Sleep(100); //ボーレート切替の余裕を持たせる
                    port.BaudRate = baudrate;
                }
    
                port.WriteLine(line);
                while (port.BytesToWrite > 0) { System.Threading.Thread.Sleep(10); };//一応書き込みバッファが空になるのを待っておく
            }
        }
    }
    


    #とはいってもUSBシリアルのボーレートをいじくってたら突然PCがリセットすると言う現象が発生したのは一体…
    • 編集済み gekkaMVP 2009年7月9日 10:42 ソースコードにErrorReceivedの処理を追加
    • 回答としてマーク suntower 2009年7月15日 8:10
    2009年7月9日 10:26
  • 自宅ではsntwr 会社ではsuntowrで同一人物です。

    わざわざ実験していただいて恐縮です。
    今はマイコンの機能側の実装の方が重要ですので、
    今はペンディングとしています。

    あとはTurboCの無償版でも並行して同じソフトを
    作っているので、この機能を実装してみようかと
    考えています。APIでシリアル通信するということです。

    それでうまくいったら
    C#のシリアルポートクラスは使用しないで
    APIを使用してやろうかなとも考えています。
    API宣言が面倒ですけど、しょうがない。

    ハード的にはRS485通信です。
    USB-RS485変換機(ビューマンデータ製)------ハード側のRS485-RS232変換回路---マイコン(H8/Tiny)のシリアルポート
    という構成になっています。

    2009年7月9日 22:55
  • もう遅いかもしれませんが、気になったので投稿します。
    try
     {
          byte[] rd = new byte[10];
          int rdata;
          int i;
          for(i=0;i<10;i++)

            rdata=serialPort1.ReadByte
            rd[i]=(byte)rd;
         
    コマンド解析処理
    ACKなら MessageBox.Show("ACK"); baudrate=BaudRate[index];
                    NACKなら MessageBox.Show("NACK");

     }
     catch
     {
    MesssageBox.Show("READ ERROR");
     }

    上記のReadByte以外で例外が発生していて、READ ERRORが発生しているとは考えられないですか?
    自分は嵌ったことがあるのですが、tryで囲った部分はどこでも例外が発生すれば、catchにつかまります。
    デバッグモードでステップ実行すれば、意外な行でcatchに飛ぶかもしれません。
    2009年7月13日 6:58
  • ボーレート変更機能は動作が複雑になることと
    RS-485変換機などのハードのタイミングも
    かわるため、ボーレートの変更機能は実装せず
    ボーレートは固定で行くことになりました
    2009年7月15日 8:07
  • ボーレート変更機能はホストのプログラミングが複雑になるのと
    RS-485変換のハードのタイミングに依存してしまうため
    ボーレートは固定で行くことになりました。
    いろいろアドバイスありがとうございました。
    • 回答としてマーク suntower 2009年7月15日 8:09
    2009年7月15日 8:09