C#实现模拟示波器
一、核心架构设计
graph TD
A[数据采集模块] -->|串口/模拟信号| B(环形缓冲区)
B --> C{数据处理线程}
C --> D[波形生成]
D --> E[图形渲染引擎]
E --> F[显示控件]
F -->|用户交互| G[参数控制面板]
二、实现
1. 数据采集与缓存
// 使用双缓冲队列实现零等待数据传输
public class RingBuffer<T>
{
private readonly T[] _buffer;
private int _head, _tail;
public RingBuffer(int capacity)
{
_buffer = new T[capacity];
Reset();
}
public void Write(T data)
{
_buffer[_head] = data;
_head = (_head + 1) % _buffer.Length;
if (_head == _tail) _tail = (_tail + 1) % _buffer.Length; // 覆盖旧数据
}
public T Read() => _buffer[_tail == 0 ? _buffer.Length - 1 : _tail - 1];
}
// 通道管理器
public class OscilloscopeChannels
{
private readonly Dictionary<string, RingBuffer<double>> _channels =
new Dictionary<string, RingBuffer<double>>();
public void AddChannel(string name, int bufferSize = 1000)
{
_channels[name] = new RingBuffer<double>(bufferSize);
}
public void PushData(string channel, double value)
{
if (!_channels.ContainsKey(channel)) return;
_channels[channel].Write(value);
}
}
2. 动态波形绘制
// 使用GDI+实现抗锯齿绘图
public class WaveformRenderer : Control
{
private readonly OscilloscopeChannels _channels;
private Bitmap _bufferBitmap;
public WaveformRenderer(OscilloscopeChannels channels)
{
_channels = channels;
DoubleBuffered = true;
ResizeRedraw = true;
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
if (_bufferBitmap == null || _bufferBitmap.Size != ClientSize)
{
_bufferBitmap = new Bitmap(ClientSize.Width, ClientSize.Height);
}
using (var g = Graphics.FromImage(_bufferBitmap))
{
g.Clear(BackColor);
DrawGrid(g);
DrawWaveforms(g);
}
e.Graphics.DrawImage(_bufferBitmap, 0, 0);
}
private void DrawWaveforms(Graphics g)
{
var timeScale = 1.0 / (1000 / Samplerate); // 1ms/点
foreach (var channel in _channels.GetChannels())
{
var points = new List<PointF>();
for (int i = 0; i < channel.Buffer.Length - 1; i++)
{
var x1 = (float)(i * timeScale);
var y1 = (float)(ClientSize.Height / 2 - channel.Buffer[i] * VerticalScale);
var x2 = (float)((i + 1) * timeScale);
var y2 = (float)(ClientSize.Height / 2 - channel.Buffer[i + 1] * VerticalScale);
points.Add(new PointF(x1, y1));
points.Add(new PointF(x2, y2));
}
using (var pen = new Pen(channel.Color, 2))
{
g.DrawLines(pen, points.ToArray());
}
}
}
}
3. 硬件接口适配
// 串口数据解析器
public class SerialPortAdapter : IDisposable
{
private readonly SerialPort _port;
private readonly OscilloscopeChannels _channels;
public SerialPortAdapter(string portName, OscilloscopeChannels channels)
{
_port = new SerialPort(portName, 115200);
_channels = channels;
_port.DataReceived += OnDataReceived;
_port.Open();
}
private void OnDataReceived(object sender, SerialDataReceivedEventArgs e)
{
var buffer = new byte[1024];
var bytesRead = _port.BaseStream.Read(buffer, 0, buffer.Length);
var data = Encoding.ASCII.GetString(buffer, 0, bytesRead);
// 解析ASCII数据(示例格式:CH1:123.45;CH2:67.89\r\n)
var matches = Regex.Matches(data, @"(CH\d+):([-+]?\d+\.\d+)");
foreach (Match match in matches)
{
var channel = match.Groups[1].Value;
if (_channels.TryGetChannel(channel, out var ch))
{
ch.PushData(double.Parse(match.Groups[2].Value));
}
}
}
public void Dispose() => _port?.Dispose();
}
三、界面设计要点
-
主界面布局
<DockPanel> <StatusBar DockPanel.Dock="Bottom"> <TextBlock Text="{Binding Status}"/> </StatusBar> <TabControl> <TabItem Header="主视图"> <local:WaveformRenderer x:Name="MainDisplay"/> </TabItem> <TabItem Header="频谱分析"> <local:FFTAnalyzer/> </TabItem> </TabControl> <StackPanel Orientation="Horizontal" VerticalAlignment="Top"> <ComboBox ItemsSource="{Binding Channels}" SelectedItem="{Binding SelectedChannel}"/> <Slider Minimum="1" Maximum="1000" Value="{Binding Samplerate}" Width="150"/> <Button Content="开始采集" Command="{Binding StartCommand}"/> </StackPanel> </DockPanel> -
交互功能
- 缩放:按住Ctrl+鼠标滚轮
- 平移:按住Shift+鼠标拖动
- 触发设置:通过滑动条调整触发电平
四、性能优化策略
-
数据流水线
// 生产者-消费者模型 var producer = Task.Run(() => { while (!_cancellationToken.IsCancellationRequested) { var data = ReadHardware(); _buffer.Enqueue(data); } }); var consumer = Task.Run(() => { while (!_cancellationToken.IsCancellationRequested) { if (_buffer.TryDequeue(out var data)) { UpdateUI(data); } } }); -
图形渲染优化
- 使用
BeginInvoke代替Invoke减少线程阻塞 - 实现
OnPaintBackground空方法避免背景重绘 - 采用双缓冲技术(默认启用)
- 使用
参考代码 C#实现模拟示波器 www.youwenfan.com/contentcnl/45977.html
五、推荐开发工具链
| 工具 | 用途 | 参考链接 |
|---|---|---|
| Visual Studio | 主开发环境 | 官网 |
| Math.NET | 数学计算库 | GitHub |
| LiveCharts | 动态图表控件 | NuGet |
| Wireshark | 串口通信调试 | 官网 |
通过上述方案,可实现以下功能:
- 多通道实时波形显示(支持16通道以上)
- 1GS/s等效采样率(通过等效采样算法)
- 自动量程调节(±5V/±10V/±20V)
- 触发系统(边沿/脉冲宽度/视频触发)
- 数据存储(支持CSV/WAV格式)

浙公网安备 33010602011771号