2013-02-13 84 views
0

我试图从MCU获取数据,将它们保存到文件并绘制它们。该代码正常运行一段时间,然后随机挂起(有时在1秒后,有时在1分钟后!)。此外,serialport超时不受尊重,即我没有收到任何超时异常。我正在使用FTDI232RL芯片。唯一一次出现超时异常的情况是,我在程序运行时拔下它。winforms:从串口读取并绘制实时数据。许多错误/错误

代码:

private: System::Void START_Click(System::Object^ sender, System::EventArgs^ e) { 
       seconds=0; 
       minutes=0; 
       hours=0; 
       days=0; 
       t=0; 


       if((this->comboBox4->Text == String::Empty)||(this->textBox2->Text == String::Empty)||(this->textBox3->Text == String::Empty)){ 
        this->textBox1->Text="please select port, save file directory and logging interval"; 
        timer1->Enabled=false; 

       } 





       else{ // start assigning 

        w=Convert::ToDouble(this->textBox3->Text); 
        double q=fmod(w*1000,10); 
        if(q!=0){ 
         MessageBox::Show("The logging interval must be a multiple of 0.01s"); 
        } 
        else{ 
         period=static_cast<int>(w*1000); 
         this->interval->Interval = period; 
         try{ // first make sure port isn't busy/open 
          if(!this->serialPort1->IsOpen){ 
           // select the port whose name is in comboBox4 (select port) 
           this->serialPort1->PortName=this->comboBox4->Text; 


           //open the port 
           this->serialPort1->Open(); 


           this->serialPort1->ReadTimeout = period+1; 
           this->serialPort1->WriteTimeout = period+1; 

           String^ name_ = this->serialPort1->PortName; 
           START=gcnew String("S"); 

           this->textBox1->Text="Logging started"; 
           timer1->Enabled=true; 
           interval->Enabled=true; 

           myStream=new ofstream(directory,ios::out); 
           *myStream<<"time(ms);ADC1;ADC2;ADC3;ADC4;ADC5;ADC6;ADC7;ADC8;"; 
           *myStream<<endl; 
           chart1->Series["ADC1"]->Points->Clear(); 
           chart1->Series["ADC2"]->Points->Clear(); 
           chart1->Series["ADC3"]->Points->Clear(); 
           chart1->Series["ADC4"]->Points->Clear(); 
           chart1->Series["ADC5"]->Points->Clear(); 
           chart1->Series["ADC6"]->Points->Clear(); 
           chart1->Series["ADC7"]->Points->Clear(); 
           chart1->Series["ADC8"]->Points->Clear(); 

           backgroundWorker1->RunWorkerAsync(); 

          } 
          else 
          { 
           this->textBox1->Text="Warning: port is busy or isn't open"; 
           timer1->Enabled=false; 
           interval->Enabled=false; 
          } 
         } 
         catch(UnauthorizedAccessException^) 
         { 
          this->textBox1->Text="Unauthorized access"; 
          timer1->Enabled=false; 
          interval->Enabled=false; 
         } 
        } 

       } 
      } 



private: System::Void backgroundWorker1_DoWork(System::Object^ sender, System::ComponentModel::DoWorkEventArgs^ e) { 

       while(!backgroundWorker1->CancellationPending){ 
        if(backgroundWorker1->CancellationPending){ 
         e->Cancel=true; 
         return; 
        } 
        t+=period; 
        if(t<10*period){ 
         this->chart1->ChartAreas["ChartArea1"]->AxisX->Minimum=0; 
         this->chart1->ChartAreas["ChartArea1"]->AxisX->Maximum=t+10*period; 
        } 
        else { 
         this->chart1->ChartAreas["ChartArea1"]->AxisX->Minimum=t-10*period; 
         this->chart1->ChartAreas["ChartArea1"]->AxisX->Maximum=t+10*period; 
        } 
        *myStream<<t<<";"; 


        for (int n=0;n<8;n++){ 
         adc_array[n]= this->serialPort1->ReadByte(); 

        } 

        Array::Copy(adc_array,ADC,8); 

        for(int f=0; f<8; f++){ 
         *myStream<<ADC[f]<<";"; 
        } 

        *myStream<<endl; 

        backgroundWorker1->ReportProgress(t); 

       } 
      } 


private: System::Void backgroundWorker1_ProgressChanged(System::Object^ sender, System::ComponentModel::ProgressChangedEventArgs^ e) { 
       chart1->Series["ADC1"]->Points->AddXY(t,ADC[0]); 
       chart1->Series["ADC2"]->Points->AddXY(t,ADC[1]); 
       chart1->Series["ADC3"]->Points->AddXY(t,ADC[2]); 
       chart1->Series["ADC4"]->Points->AddXY(t,ADC[3]); 
       chart1->Series["ADC5"]->Points->AddXY(t,ADC[4]); 
       chart1->Series["ADC6"]->Points->AddXY(t,ADC[5]); 
       chart1->Series["ADC7"]->Points->AddXY(t,ADC[6]); 
       chart1->Series["ADC8"]->Points->AddXY(t,ADC[7]); 
     } 

则允许用户在用于数据采集秒,以限定的时间间隔(在此代码间隔为w后转换为双)。在这种情况下,程序向MCU发送一个脉冲请求新的数据传输。到目前为止,我一直在测试这个间隔1秒(注意,在每个间隔期间,MCU发送8帧,每个帧代表一个ADC)。但是,我需要在某个时间点运行10ms的时间间隔。这可能吗?关于如何解决我在开头提到的几个问题的任何想法?

在此先感谢

UPDATE

只给你发生了什么事的想法: 我评论的图表部分和运行程序约5分钟,以1秒的读取时间间隔。所以我期望在输出文件中得到5x60 = 300的值,但我只有39(即从1s开始到39s)。该程序仍在运行,但数据不再被存储。 测试是在发布模式而不是调试模式下完成的。在调试模式下,在serialport-> readbyte()下设置一个断点,不会重现问题。我的猜测是这是程序和MCU之间的时间问题。

回答

1

您正在犯几个标准错误。首先,做不是当端口打开时拔掉电缆。许多USB仿真器不知道如何处理,FTDI驱动程序尤其臭名昭着。他们只是使端口在使用中消失,这总是会给使用该端口的代码造成严重的心脏病。不可捕捉的异常是常见的。

其次,你正在访问不是线程安全的工作线程类的属性。 Chart控件仅用于UI线程,访问工作中的ChartAreas属性会给你带来很多苦难。在违反线程要求时,获取InvalidOperationException是非常典型的,然而它并没有得到一致的实现。 Nastiness包括随机AccessViolationExceptions,数据损坏和死锁。

第三,要设置完全不切实际的目标。每10毫秒追求一次更新是毫无意义的,人眼无法感知到这一点。任何过去的50毫秒都会变成模糊。当您在电影院观看电影时,它会以每秒24帧的速度显示。这种失败模式也是不愉快的,你最终会达到一个点,你用更多的更新触发UI线程(或图表控件)比它可以处理。副作用是UI停止绘画本身,​​它忙于跟上大量的调用请求。并且您的程序消耗的内存量不断增加,更新队列不受限制地增长。这最终会以OOM异常结束,但是消耗2 jigabytes需要一段时间。您需要防止这种情况发生,您需要调节您调用的速率。一个简单的线程安全计数器可以解决这个问题。

四,您正在访问您在多个线程收集数据,没有考虑线程安全的护理。在UI线程正在读取时,ADC数组内容正在被工作人员更改。由此产生各种各样的痛苦,至少数据不好。一个简单的解决方法是将数据的副本传递给ReportProgress方法。一般来说,通过使用pull而不是push来解决这些线程问题。通过让UI线程调整请求的速度而不是尝试让UI线程跟上,消除消防软管问题。

+0

谢谢你的回复Hans。从我的理解,我需要做到以下几点:1)改变轴的最小和最大值必须在主线程内完成 2)reportprogress(adc_array)而不是t 3)图表不能刷新那么快,所以我将执行以下操作:将每个10x8数据存储在一个临时数组中(让它称为temp),然后绘制它们。其实我相信reportprogress(temp)会更好,不是吗? – 2013-02-13 16:06:45