0

我正在尝试从 MCU 获取数据,将它们保存到文件中并绘制它们。代码正常运行一段时间,然后随机挂起(有时在 1 秒后,有时在 1 分钟后......!)。此外,串行端口超时也不受尊重,即我没有收到任何超时异常。我正在使用 FTDI232RL 芯片。我唯一一次遇到超时异常是在程序运行时拔掉它。

代码:

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)。但是,我需要让它在某个时候运行 10 毫秒的间隔。这可能吗?关于如何解决我一开始提到的几个问题的任何想法?

提前致谢

更新

只是为了让您了解正在发生的事情:我评论了图表部分并运行了大约 5 分钟的程序,阅读间隔为 1 秒。所以我希望在输出文件中得到大约 5x60=300 个值,但我只得到了 39 个(即从 1 秒到 39 秒)。该程序仍在运行,但数据不再存储。测试是在发布模式而不是调试模式下完成的。在调试模式下,在 serialport->readbyte() 下设置断点不会重现问题。我的猜测是程序和MCU之间的时间问题。

4

1 回答 1

1

你犯了几个标准错误。首先,请勿端口打开时拔下电缆。许多 USB 仿真器不知道如何处理,FTDI 驱动程序对此尤其臭名昭著。他们只是让端口在使用时消失,这总是会给使用该端口的代码带来严重的心脏病发作。无法捕获的异常很常见。

其次,您正在访问工作线程中非线程安全的类的属性。Chart 控件只能在 UI 线程中使用,在工作人员中访问 ChartAreas 属性会给您带来很多痛苦。当您违反线程要求时,获得 InvalidOperationException 是非常典型的,但它并没有始终如一地实现。肮脏包括随机 AccessViolationExceptions、损坏的数据和死锁。

第三,你正在设定完全不切实际的目标。每 10 毫秒更新一次是没有意义的,人眼无法察觉。任何超过 50 毫秒的时间都会变得模糊。当你在电影院看电影时,它会以每秒 24 帧的速度显示。这种故障模式也令人不快,您最终会使用比它可以处理的更多更新来打击 UI 线程(或 Chart 控件)。副作用是 UI 停止绘制自己,它太忙于跟上大量的调用请求。并且您的程序消耗的内存量不断增加,更新队列无限增长。这最终会以 OOM 异常结束,但是消耗 2 吉字节需要一段时间。您需要防止这种情况发生,您需要限制调用的速率。一个简单的线程安全计数器可以解决这个问题。

第四,您在不考虑线程安全的情况下访问多个线程中收集的数据。当 UI 线程正在读取 ADC 数组内容时,worker 正在更改它。各种各样的痛苦,至少是坏数据。一个简单的解决方法是将数据的副本传递给 ReportProgress 方法。通常,通过使用 pull 而不是 push 来解决这类线程问题。通过让 UI 线程调整请求而不是试图让 UI 线程跟上来摆脱消防水管问题。

于 2013-02-13T14:28:12.127 回答