基于C# WinForm實現串口數據讀取與實時折線圖顯示
更新時間:2026年02月27日 08:55:18 作者:foundbug999
這篇文章主要介紹了如何使用C# WinForms開發(fā)一個串口數據讀取和實時顯示的系統,包括系統架構設計、核心代碼實現、關鍵功能、界面設計、擴展功能、調試優(yōu)化、部署方案以及測試用例,需要的朋友可以參考下
一、系統架構設計

二、核心代碼實現
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;
}
// 假設數據格式:溫度高字節(jié)+溫度低字節(jié)(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. 數據緩沖區(qū)(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. 性能優(yōu)化配置
// 在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 = "串口實時監(jiān)控";
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);
}
六、調試與優(yōu)化建議
1. 性能監(jiān)控
// 在狀態(tài)欄顯示性能指標
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. 異常處理
// 添加串口狀態(tài)監(jiān)控
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}\實時監(jiān)控"; 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();
}
}
八、測試用例
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);
});
}
以上就是基于C# WinForm實現串口數據讀取與實時折線圖顯示的詳細內容,更多關于C# WinForm串口數據讀取與折線圖顯示的資料請關注腳本之家其它相關文章!
相關文章
C# 委托中 Invoke/BeginInvoke/EndInvoke和DynamicInvoke&
在C#中,委托(Delegate)提供了多種調用方式,包括 Invoke、BeginInvoke、EndInvoke 和 DynamicInvoke,每種調用方式都有其特定的用途和適用場景,下面將詳細介紹這些方法的區(qū)別與聯系,感興趣的朋友一起看看吧2025-03-03
C# 利用ICSharpCode.SharpZipLib實現在線壓縮和解壓縮
本文主要主要介紹了利用ICSharpCode.SharpZipLib第三方的DLL庫實現在線壓縮和解壓縮的功能,并做了相關的代碼演示。2016-04-04
簡單C#代碼實現modbus-rtu通訊發(fā)送數據方式
這篇文章主要介紹了簡單C#代碼實現modbus-rtu通訊發(fā)送數據方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-01-01
c#中winform根據郵箱地址和密碼一鍵發(fā)送email的實現
本文主要介紹了c#winform根據郵箱地址和密碼一鍵發(fā)送email的實現,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2022-07-07

