2013-02-13 82 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