我正在尝试从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]);
}
允许用户定义数据采集的间隔(以秒为单位)(在转换为double后,此间隔为w)。在这种情况下,程序向MCU发送脉冲,请求新的数据传输。到目前为止,我已经测试了这个间隔1秒钟(注意,在每个间隔期间,MCU发送8帧,每帧代表一个ADC)。但是,我需要让它在某个时刻以10ms的间隔运行。这有可能吗?关于如何解决我刚开始提到的一些问题的任何想法?
提前致谢
更新
只是为了让您知道发生了什么: 我评论了图表部分并运行了大约5分钟的程序,读取间隔为1秒。所以我希望在输出文件中得到大约5x60 = 300的值,但我只得到39(即从1s到39s开始)。该程序仍在运行,但数据不再存储。 测试是在发布模式下完成的,而不是调试模式。在调试模式下,在serialport-&gt; readbyte()下设置断点不会重现该问题。我猜这是程序和MCU之间的时间问题。
答案 0 :(得分:1)
你犯了几个标准错误。首先,打开端口时, NOT 拔掉电缆。许多USB仿真器不知道如何处理,FTDI驱动程序尤其臭名昭着。它们只是让端口在使用时消失,这总是会给使用端口的代码造成严重的心脏病发作。无法捕获的异常很常见。
其次,您正在访问工作线程中非线程安全的类的属性。 Chart控件只能在UI线程中使用,访问worker中的ChartAreas属性会给你带来很多痛苦。当您违反线程要求时,获取InvalidOperationException是非常典型的,但是它并未始终如一地实现。 Nastiness包括随机的AccessViolationExceptions,损坏的数据和死锁。
第三,你设定了完全不切实际的目标。每10毫秒追求一次更新毫无意义,人眼无法察觉。任何超过50毫秒的事情都会变成模糊。当您在电影院观看电影时,它会被利用,它以每秒24帧的速度显示。失败模式也是令人不愉快的,你最终会达到一个点,你正在用更多的更新来处理UI线程(或图表控件)。副作用是UI停止绘画本身,它太忙于试图跟上大量的调用请求。并且程序消耗的内存量不断增加,更新队列不受限制地增长。这最终会以OOM异常结束,但是需要一段时间才能消耗2千兆字节。您需要防止这种情况发生,您需要限制调用的速率。一个简单的线程安全计数器可以解决这个问题。
第四,您正在访问您在多个线程中收集的数据,而不考虑线程安全性。当UI线程正在读取时,工作人员正在更改ADC数组内容。从中获得各种痛苦,至少是糟糕的数据。一个简单的解决方法是将数据的副本传递给ReportProgress方法。通常,通过使用pull而不是push来解决这些类型的线程问题。通过让UI线程调整请求速度而不是试图让UI线程跟上来消除火灾问题。