用C# WinForm给FPGA做个“眼睛”:手把手教你实现AD7606数据采集与波形显示上位机
·
用C# WinForm打造FPGA数据可视化中枢:AD7606采集与波形显示的实战指南
当FPGA完成AD7606模数转换后,原始数据就像未经雕琢的玉石——工程师需要专业的工具来"看见"信号本质。本文将构建一个具备工业级稳定性的C# WinForm上位机,它不仅是数据的可视化终端,更是FPGA系统的控制中枢。不同于简单的串口调试工具,我们将实现 实时波形渲染 、 多通道数据分离 和 硬件交互控制 三位一体的专业解决方案。
1. 工程架构设计与环境准备
1.1 硬件系统组成
本方案采用的硬件配置组合在工业测量领域具有典型代表性:
| 组件类型 | 型号规格 | 关键参数 |
|---|---|---|
| FPGA核心板 | Intel Cyclone IV EP4CE10 | 50MHz主频,10K逻辑单元 |
| ADC模块 | AD7606BSTZ | 16位精度,8通道同步采样 |
| 通信接口 | RS-232串口 | 波特率可调(默认9600bps) |
| 电源管理 | LM317稳压电路 | ±5V模拟供电,3.3V数字供电 |
硬件连接要点 :
- AD7606的并行数据线接入FPGA GPIO bank
- FPGA通过UART转串口芯片(CH340G)连接PC
- 确保模拟地与数字地单点连接
1.2 软件开发环境配置
推荐使用Visual Studio 2019+社区版进行开发,需特别注意以下NuGet包安装:
Install-Package System.IO.Ports -Version 6.0.0
Install-Package LiveCharts.WinForms -Version 2.0.0-beta10
Install-Package Newtonsoft.Json -Version 13.0.1
提示:LiveCharts相比默认的Chart控件具有更好的实时渲染性能,在10kHz采样率下仍能流畅显示
2. 核心通信协议实现
2.1 自定义数据帧结构设计
FPGA与上位机采用二进制协议传输,帧格式设计如下:
[Header(0xAA)][ChannelID][DataHigh][DataLow][Checksum]
对应C#的结构体封装:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct AdcFrame
{
public byte Header; // 固定0xAA
public byte Channel; // 通道编号(0-7)
public byte DataH; // 数据高字节
public byte DataL; // 数据低字节
public byte CheckSum;// 校验和(异或校验)
}
2.2 串口通信的线程安全实现
创建双缓冲队列处理接收数据,避免UI线程阻塞:
private ConcurrentQueue<AdcFrame> _dataQueue = new ConcurrentQueue<AdcFrame>();
private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
byte[] buffer = new byte[serialPort.BytesToRead];
serialPort.Read(buffer, 0, buffer.Length);
// 帧同步与解析
for(int i=0; i<buffer.Length-4; i++)
{
if(buffer[i] == 0xAA && ValidateChecksum(buffer, i))
{
AdcFrame frame = new AdcFrame {
Header = buffer[i],
Channel = buffer[i+1],
DataH = buffer[i+2],
DataL = buffer[i+3],
CheckSum = buffer[i+4]
};
_dataQueue.Enqueue(frame);
}
}
}
3. 实时波形显示系统
3.1 高性能绘图方案选型
对比三种常见绘图方案的性能指标:
| 技术方案 | 刷新率上限 | 内存占用 | 多通道支持 | 适用场景 |
|---|---|---|---|---|
| Windows Forms Chart | 200Hz | 低 | 有限 | 低速静态数据显示 |
| LiveCharts | 10kHz | 中 | 优秀 | 实时动态波形 |
| DirectX绘图 | 100kHz+ | 高 | 自定义 | 超高速采集系统 |
本方案采用LiveCharts实现8通道独立显示:
private CartesianChart _chart;
private SeriesCollection _series = new SeriesCollection();
// 初始化8条折线
for(int i=0; i<8; i++)
{
_series.Add(new LineSeries {
Title = $"CH{i}",
Values = new ChartValues<double>(),
PointGeometry = null
});
}
3.2 动态数据更新策略
采用定时器分时更新策略,平衡CPU负载与实时性:
private void UpdateTimer_Tick(object sender, EventArgs e)
{
if(_dataQueue.Count > 1000) // 防止队列积压
{
_dataQueue = new ConcurrentQueue<AdcFrame>(
_dataQueue.Skip(_dataQueue.Count - 1000));
}
List<AdcFrame> frames = new List<AdcFrame>();
while(_dataQueue.TryDequeue(out AdcFrame frame))
{
frames.Add(frame);
}
// 按通道分组处理
var groups = frames.GroupBy(f => f.Channel);
foreach(var group in groups)
{
int ch = group.Key;
var values = group.Select(f => (double)((f.DataH << 8) | f.DataL));
_series[ch].Values.AddRange(values);
// 保持固定点数
if(_series[ch].Values.Count > 500)
{
_series[ch].Values.RemoveRange(0,
_series[ch].Values.Count - 500);
}
}
}
4. 硬件控制与交互功能
4.1 FPGA状态控制指令集
设计简洁的指令协议控制FPGA外设:
| 指令代码 | 功能描述 | 参数范围 |
|---|---|---|
| 0x10 | LED控制 | 0x00-0xFF(位图) |
| 0x20 | 采样率设置 | 1-10000(Hz) |
| 0x30 | 量程选择 | 0:±5V,1:±10V |
C#发送指令的封装方法:
public void SendCommand(byte cmd, byte[] parameters)
{
byte[] frame = new byte[parameters.Length + 3];
frame[0] = 0x55; // 命令头
frame[1] = cmd;
frame[2] = (byte)parameters.Length;
Array.Copy(parameters, 0, frame, 3, parameters.Length);
// 计算校验和
byte checksum = 0;
foreach(byte b in frame) checksum ^= b;
List<byte> packet = new List<byte>(frame);
packet.Add(checksum);
serialPort.Write(packet.ToArray(), 0, packet.Count);
}
4.2 用户操作界面优化
设计符合工程习惯的控制面板:
// 采样率设置控件
private NumericUpDown nudSampleRate = new NumericUpDown()
{
Minimum = 1,
Maximum = 10000,
Increment = 100,
Value = 1000
};
// 量程选择RadioButton
private RadioButton rbRange5V = new RadioButton()
{
Text = "±5V",
Checked = true
};
5. 高级功能扩展
5.1 数据持久化方案
采用二进制+CSV双格式存储策略:
public void SaveData(string filename, DataFormat format)
{
var frames = _dataQueue.ToArray();
if(format == DataFormat.Binary)
{
using(var fs = File.Create(filename))
{
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(fs, frames);
}
}
else
{
var lines = frames.Select(f =>
$"{f.Channel},{f.DataH << 8 | f.DataL}");
File.WriteAllLines(filename, lines);
}
}
5.2 网络远程监控集成
通过SignalR实现Web端实时监控:
public class DataHub : Hub
{
public async Task SendAdcData(AdcFrame frame)
{
await Clients.All.SendAsync("ReceiveData",
frame.Channel,
(frame.DataH << 8) | frame.DataL);
}
}
在WinForm中配置跨线程更新:
private IHubProxy _hubProxy;
private void InitSignalR()
{
var connection = new HubConnection("http://remote-server:5000/data");
_hubProxy = connection.CreateHubProxy("DataHub");
connection.Start().Wait();
}
private void ForwardData(AdcFrame frame)
{
_hubProxy.Invoke("SendAdcData", frame);
}
更多推荐



所有评论(0)