基于C# WinForm实现串口数据读取与实时折线图显示
一、系统架构设计
graph TD
A[串口设备] -->|RS232/USB| B{串口通信模块}
B --> C[数据解析器]
C --> D[实时数据缓冲区]
D --> E[Chart控件更新]
E --> F[用户界面]
二、核心代码实现
1. 串口通信模块(SerialPortManager.cs)
using System.IO.Ports;
using System.Threading.Tasks;
public class SerialPortManager : IDisposable
{
private SerialPort _port;
private readonly BlockingCollection<byte[]> _buffer = new(100);
private CancellationTokenSource _cts;
public event Action<byte[]> DataReceived;
public void Connect(string portName, int baudRate = 115200)
{
_port = new SerialPort(portName, baudRate, Parity.None, 8, StopBits.One)
{
ReadTimeout = 1000,
WriteTimeout = 1000
};
_cts = new CancellationTokenSource();
_ = Task.Run(() => ReadDataAsync(_cts.Token));
}
private async Task ReadDataAsync(CancellationToken token)
{
try
{
while (!token.IsCancellationRequested)
{
int bytesAvailable = _port.BytesToRead;
if (bytesAvailable > 0)
{
byte[] chunk = new byte[bytesAvailable];
_port.Read(chunk, 0, bytesAvailable);
_buffer.Add(chunk, token);
DataReceived?.Invoke(chunk);
}
await Task.Delay(10, token);
}
}
catch (OperationCanceledException) { }
catch (Exception ex)
{
HandleError($"串口读取错误: {ex.Message}");
}
}
public void Disconnect() => _port?.Close();
public void Dispose() => _port?.Dispose();
private void HandleError(string message)
{
// 实现错误处理逻辑
}
}
2. 数据解析与图表更新(MainForm.cs)
using System.Windows.Forms.DataVisualization.Charting;
public partial class MainForm : Form
{
private readonly SerialPortManager _serialManager;
private readonly CircularBuffer<float> _dataBuffer = new(1000);
private Chart _chart;
private Series _series;
public MainForm()
{
InitializeComponent();
InitializeChart();
_serialManager = new SerialPortManager();
_serialManager.DataReceived += OnDataReceived;
}
private void InitializeChart()
{
_chart = new Chart { Dock = DockStyle.Fill };
_series = new Series("传感器数据")
{
ChartType = SeriesChartType.Spline,
BorderWidth = 2,
Color = Color.Blue
};
_chart.Series.Add(_series);
_chart.ChartAreas[0].AxisX.Title = "时间";
_chart.ChartAreas[0].AxisY.Title = "数值";
_chart.ChartAreas[0].AxisX.Interval = 1;
_chart.ChartAreas[0].AxisY.Interval = 5;
this.Controls.Add(_chart);
}
private void OnDataReceived(byte[] data)
{
if (InvokeRequired)
{
Invoke(new Action(() => OnDataReceived(data)));
return;
}
// 假设数据格式:温度高字节+温度低字节(16位)
if (data.Length >= 2)
{
short raw = (short)(data[0] << 8 | data[1]);
float value = raw / 10.0f;
_dataBuffer.Enqueue(value);
UpdateChart();
}
}
private void UpdateChart()
{
if (_dataBuffer.Count > _chart.Series[0].Points.Count)
{
_chart.Series[0].Points.AddY(_dataBuffer.Last());
if (_chart.Series[0].Points.Count > 1000)
{
_chart.Series[0].Points.RemoveAt(0);
}
}
// 启用双缓冲减少闪烁
_chart.DoubleBuffered = true;
_chart.Invalidate();
}
private void btnConnect_Click(object sender, EventArgs e)
{
_serialManager.Connect("COM3");
}
private void btnDisconnect_Click(object sender, EventArgs e)
{
_serialManager.Disconnect();
}
}
三、关键功能实现
1. 数据缓冲区(CircularBuffer.cs)
public class CircularBuffer<T>
{
private readonly T[] _buffer;
private int _head;
private int _tail;
private int _count;
public CircularBuffer(int capacity)
{
_buffer = new T[capacity];
}
public void Enqueue(T item)
{
_buffer[_head] = item;
_head = (_head + 1) % _buffer.Length;
if (_count < _buffer.Length) _count++;
else _tail = (_tail + 1) % _buffer.Length;
}
public T Dequeue() => _buffer[_tail];
public T Last() => _buffer[(_tail + _count - 1) % _buffer.Length];
public int Count => _count;
}
2. 性能优化配置
// 在Form_Load事件中配置
private void MainForm_Load(object sender, EventArgs e)
{
// 启用双缓冲
this.DoubleBuffered = true;
// 设置控件更新模式
_chart.Series[0].ChartType = SeriesChartType.FastLine;
_chart.ChartAreas[0].AxisX.ScrollBar.Enabled = true;
_chart.ChartAreas[0].AxisX.ScaleView.Size = 60; // 显示最近60秒数据
}
// 定时清理旧数据
private void timerCleanup_Tick(object sender, EventArgs e)
{
while (_dataBuffer.Count > 1000)
{
_dataBuffer.Dequeue();
_chart.Series[0].Points.RemoveAt(0);
}
}
四、界面设计(MainForm.Designer.cs)
partial class MainForm
{
private System.ComponentModel.IContainer components = null;
private Button btnConnect;
private Button btnDisconnect;
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
private void InitializeComponent()
{
this.btnConnect = new System.Windows.Forms.Button();
this.btnDisconnect = new System.Windows.Forms.Button();
this.SuspendLayout();
// 连接按钮
this.btnConnect.Location = new System.Drawing.Point(20, 20);
this.btnConnect.Size = new System.Drawing.Size(75, 23);
this.btnConnect.Text = "连接";
this.btnConnect.Click += new System.EventHandler(this.btnConnect_Click);
// 断开按钮
this.btnDisconnect.Location = new System.Drawing.Point(101, 20);
this.btnDisconnect.Size = new System.Drawing.Size(75, 23);
this.btnDisconnect.Text = "断开";
this.btnDisconnect.Click += new System.EventHandler(this.btnDisconnect_Click);
// 主布局
this.Controls.Add(this.btnDisconnect);
this.Controls.Add(this.btnConnect);
this.ClientSize = new System.Drawing.Size(800, 600);
this.Name = "MainForm";
this.Text = "串口实时监控";
this.ResumeLayout(false);
}
}
五、扩展功能实现
1. 多通道支持
// 修改数据解析逻辑
private void OnDataReceived(byte[] data)
{
if (data.Length >= 4)
{
short tempRaw = (short)(data[0] << 8 | data[1]);
short humiRaw = (short)(data[2] << 8 | data[3]);
float temp = tempRaw / 10.0f;
float humi = humiRaw / 10.0f;
_tempBuffer.Enqueue(temp);
_humiBuffer.Enqueue(humi);
UpdateCharts();
}
}
private void UpdateCharts()
{
if (_chart.Series.Count < 2)
{
_chart.Series.Add(new Series("湿度") { ChartType = SeriesChartType.Spline, Color = Color.Red });
}
// 更新温度曲线
if (_tempBuffer.Count > _chart.Series[0].Points.Count)
{
_chart.Series[0].Points.AddY(_tempBuffer.Last());
_chart.Series[0].Points.RemoveAt(0);
}
// 更新湿度曲线
if (_humiBuffer.Count > _chart.Series[1].Points.Count)
{
_chart.Series[1].Points.AddY(_humiBuffer.Last());
_chart.Series[1].Points.RemoveAt(0);
}
}
2. 数据存储集成
// 添加SQLite存储
public class DataStorage
{
private readonly string _dbPath = "data.sqlite";
public void SaveData(float temp, float humi)
{
using (var conn = new SQLiteConnection($"Data Source={_dbPath};Version=3;"))
{
conn.Open();
var cmd = new SQLiteCommand(
"INSERT INTO sensor_data (timestamp, temperature, humidity) " +
"VALUES (datetime('now'), @temp, @humi)", conn);
cmd.Parameters.AddWithValue("@temp", temp);
cmd.Parameters.AddWithValue("@humi", humi);
cmd.ExecuteNonQuery();
}
}
}
// 在OnDataReceived中调用
private void OnDataReceived(byte[] data)
{
// ...解析数据...
_storage.SaveData(temp, humi);
}
六、调试与优化建议
1. 性能监控
// 在状态栏显示性能指标
private void UpdatePerformanceMetrics()
{
var cpuUsage = new PerformanceCounter("Processor", "% Processor Time", "_Total");
var ramUsage = new PerformanceCounter("Memory", "Available MBytes");
lblStatus.Text = $"CPU: {cpuUsage.NextValue()}% | 内存: {ramUsage.NextValue()}MB";
}
2. 异常处理
// 添加串口状态监控
private void CheckSerialPortStatus()
{
if (!_serialManager.IsOpen)
{
btnConnect.Enabled = true;
btnDisconnect.Enabled = false;
lblStatus.Text = "串口未连接";
}
else
{
btnConnect.Enabled = false;
btnDisconnect.Enabled = true;
lblStatus.Text = "数据接收中...";
}
}
七、部署方案
1. 安装包配置(Inno Setup脚本)
[Files]
Source: "YourApp.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "zlgcan.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "data.sqlite"; DestDir: "{app}"; Flags: ignoreversion
[Icons]
Name: "{group}\实时监控"; Filename: "{app}\YourApp.exe"
2. 系统服务化
public class BackgroundService : ServiceBase
{
protected override void OnStart(string[] args)
{
_serialManager.Connect("COM3");
_timer.Start();
}
protected override void OnStop()
{
_serialManager.Disconnect();
_timer.Stop();
}
}
参考代码 使用C#的winform读取串口,并用chart控件显示实时数据折线图 www.youwenfan.com/contentcnr/112224.html
八、测试用例
1. 模拟数据测试
// 生成测试数据
private void GenerateTestData()
{
var rand = new Random();
var timer = new Timer(100);
timer.Elapsed += (s, e) => {
var temp = 20 + rand.NextDouble() * 10;
var humi = 40 + rand.NextDouble() * 20;
_serialManager.RaiseDataReceived(new byte[]
{ (byte)(temp >> 8), (byte)temp, (byte)(humi >> 8), (byte)humi });
};
timer.Start();
}
2. 压力测试
// 高负载数据测试
private void HighLoadTest()
{
var buffer = new byte[1024 * 1024]; // 1MB数据块
Parallel.For(0, 1000, i => {
_serialManager.RaiseDataReceived(buffer);
});
}
浙公网安备 33010602011771号