串行端口查询和数据处理 [英] Serial Port Polling and Data handling
问题描述
我想从几个串口来自传感器通过微控制器读取。每个串口将获得超过2000次测量(每次测量是7个字节,都在十六进制)。和它们在同一时间烧制。现在我是从4个串口轮询。另外,我每次测量转换成字符串,并将其追加到一个StringBuilder。当我完成接收数据时,他们将在输出中一个文件。问题是CPU消耗是非常高的,范围从80%至100%。
我去,虽然一些文章,把Thread.sleep代码(100)在最后。它减少了CPU时间当没有数据到来。我也把在Thread.sleep代码每个轮询的结束的时候BytesToRead小于100,不仅有助于在一定程度上。
有人可以提出一个解决方案,从串行端口查询和处理,我得到的数据?也许追加每次我得到的东西会导致什么问题?
//我用单独的线程所有传感器
私人无效SensorThread(的SerialPort mySerialPort,应INT bytesPerMeasurement,文本框文本框中StringBuilder的数据)
{
textBox.BeginInvoke(新MethodInvoker(委托(){textBox.Text =;}));
INT读取动作;
INT吨;
字节[] DATAIN;
,而(mySerialPort.IsOpen)
{
试
{
如果(mySerialPort.BytesToRead!= 0)
{
//试图读取的字节
的固定数量读取动作= 0;
T = 0;
DATAIN =新的字节[bytesPerMeasurement]
T = mySerialPort.Read(DATAIN,0,bytesPerMeasurement);
读取动作+ = T;
,而(读取动作= bytesPerMeasurement!)
{
T = mySerialPort.Read(DATAIN,读取动作,bytesPerMeasurement - 读取动作);
读取动作+ = T;
}
//转换他们到十六进制字符串
StringBuilder的S =新的StringBuilder();
的foreach(在DATAIN字节二){s.Append(b.ToString(X)+,); }
变种线= s.ToString();
线串VAR =的String.Format({0} ---- {2},
线,
mySerialPort.BytesToRead);
data.Append(线串+\r\\\
); //追加测量一个巨大的StringBuilder ...需要一个解决方案。
////使用委托来改变UI线程...
textBox.BeginInvoke(新MethodInvoker(委托(){textBox.Text =行;}));
如果(mySerialPort.BytesToRead&下; = 100){Thread.sleep代码(100); }
}
,否则{Thread.sleep代码(100);}
}
赶上(异常前)
{
//的MessageBox .Show(ex.ToString());
}
}
}
这是不是做的好方法,它远不如在DataReceived事件检索工作。
港口有一个3阶段的过程,效果很好。
- 从串行口接收数据
- 等到你有数据
- 的有关大块解读数据
所以像
类DataCollector
{
私人只读动作<名单<字节>> _processMeasurement;
私人只读字符串_port;
私人的SerialPort _serialPort;
私人const int的SizeOfMeasurement = 4;
名单,LT;字节>数据=新的List<位>();
公共DataCollector(串口,动作<名单,LT;字节>> processMeasurement)
{
_processMeasurement = processMeasurement;
_serialPort =新的SerialPort(端口);
_serialPort.DataReceived + = SerialPortDataReceived;
}
私人无效SerialPortDataReceived(对象发件人,SerialDataReceivedEventArgs E)
{
,而(_serialPort.BytesToRead大于0)
{
变种数= _serialPort.BytesToRead;
变种字节=新的字节[统计]
_serialPort.Read(字节,0,计数);
AddBytes(字节);
}
}
私人无效AddBytes(字节[]字节)
{
Data.AddRange(字节);
,而(Data.Count> SizeOfMeasurement)
{
VAR measurementData = Data.GetRange(0,SizeOfMeasurement);
Data.RemoveRange(0,SizeOfMeasurement);
如果(_processMeasurement!= NULL)_processMeasurement(measurementData);
}
}
}
请注意:添加字节不断收集数据,直到你有足够的计算为一次测量,或者如果你的数据一阵,它拆分成单独的测量....这样你就可以得到1个字节一次,2下了,另外1个接下来,它会再采取一种把它变成一个测量。大多数的,如果你的微发送它在突发的时候,它会在为一体的,但有时它会被分成2。
那么什么地方可以做
VAR收集=新DataCollector(COM1,ProcessMeasurement);
和
私人无效ProcessMeasurement(列表<字节>字节)
{
//这将调用每一个测量,所以后来
//把东西到文本中... 。或做任何
}
I am trying to read from several serial ports from sensors through microcontrollers. Each serial port will receive more than 2000 measurements (each measurement is 7 bytes, all in hex). And they are firing at the same time. Right now I am polling from 4 serial ports. Also, I translate each measurement into String and append it to a Stringbuilder. When I finish receiving data, they will be ouput in to a file. The problem is the CPU consumption is very high, ranging from 80% to 100%.
I went though some articles and put Thread.Sleep(100) at the end. It reduces CPU time when there is no data coming. I also put Thread.Sleep at the end of each polling when the BytesToRead is smaller than 100. It only helps to a certain extent.
Can someone suggest a solution to poll from serial port and handle data that I get? Maybe appending every time I get something causes the problem?
//I use separate threads for all sensors
private void SensorThread(SerialPort mySerialPort, int bytesPerMeasurement, TextBox textBox, StringBuilder data)
{
textBox.BeginInvoke(new MethodInvoker(delegate() { textBox.Text = ""; }));
int bytesRead;
int t;
Byte[] dataIn;
while (mySerialPort.IsOpen)
{
try
{
if (mySerialPort.BytesToRead != 0)
{
//trying to read a fix number of bytes
bytesRead = 0;
t = 0;
dataIn = new Byte[bytesPerMeasurement];
t = mySerialPort.Read(dataIn, 0, bytesPerMeasurement);
bytesRead += t;
while (bytesRead != bytesPerMeasurement)
{
t = mySerialPort.Read(dataIn, bytesRead, bytesPerMeasurement - bytesRead);
bytesRead += t;
}
//convert them into hex string
StringBuilder s = new StringBuilder();
foreach (Byte b in dataIn) { s.Append(b.ToString("X") + ","); }
var line = s.ToString();
var lineString = string.Format("{0} ---- {2}",
line,
mySerialPort.BytesToRead);
data.Append(lineString + "\r\n");//append a measurement to a huge Stringbuilder...Need a solution for this.
////use delegate to change UI thread...
textBox.BeginInvoke(new MethodInvoker(delegate() { textBox.Text = line; }));
if (mySerialPort.BytesToRead <= 100) { Thread.Sleep(100); }
}
else{Thread.Sleep(100);}
}
catch (Exception ex)
{
//MessageBox.Show(ex.ToString());
}
}
}
this is not a good way to do it, it far better to work on the DataReceived event.
basically with serial ports there's a 3 stage process that works well.
- Receiving the Data from the serial port
- Waiting till you have a relevant chunk of data
- Interpreting the data
so something like
class DataCollector
{
private readonly Action<List<byte>> _processMeasurement;
private readonly string _port;
private SerialPort _serialPort;
private const int SizeOfMeasurement = 4;
List<byte> Data = new List<byte>();
public DataCollector(string port, Action<List<byte>> processMeasurement)
{
_processMeasurement = processMeasurement;
_serialPort = new SerialPort(port);
_serialPort.DataReceived +=SerialPortDataReceived;
}
private void SerialPortDataReceived(object sender, SerialDataReceivedEventArgs e)
{
while(_serialPort.BytesToRead > 0)
{
var count = _serialPort.BytesToRead;
var bytes = new byte[count];
_serialPort.Read(bytes, 0, count);
AddBytes(bytes);
}
}
private void AddBytes(byte[] bytes)
{
Data.AddRange(bytes);
while(Data.Count > SizeOfMeasurement)
{
var measurementData = Data.GetRange(0, SizeOfMeasurement);
Data.RemoveRange(0, SizeOfMeasurement);
if (_processMeasurement != null) _processMeasurement(measurementData);
}
}
}
Note: Add Bytes keeps collecting data till you have enough to count as a measurement, or if you get a burst of data, splits it up into seperate measurements.... so you can get 1 byte one time, 2 the next, and 1 more the next, and it will then take that an turn it into a measurement. Most of the time if your micro sends it in a burst, it will come in as one, but sometimes it will get split into 2.
then somewhere you can do
var collector = new DataCollector("COM1", ProcessMeasurement);
and
private void ProcessMeasurement(List<byte> bytes)
{
// this will get called for every measurement, so then
// put stuff into a text box.... or do whatever
}
这篇关于串行端口查询和数据处理的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!