基于C#和周立功USBCAN设备的完整上位机开发示例

基于C#和周立功USBCAN设备的完整上位机开发示例


一、环境配置

  1. 驱动安装

    • 下载安装ZLG官方驱动(需区分x86/x64)
    • 确保zlgcan.dll位于应用程序目录或系统PATH
  2. NuGet依赖

    dotnet add package CanKit.Core
    dotnet add package CanKit.Adapter.ZLG
    

二、核心代码实现

1. 设备管理类(DeviceManager.cs)

using CanKit.Core;
using CanKit.Core.Definitions;
using CanKit.Adapter.ZLG;
using System;
using System.Threading.Tasks;

public class DeviceManager : IDisposable
{
    private ICanBus _bus;
    private readonly string _deviceId = "zlg://ZCAN_USBCANFD_200U?index=0#ch0";
    private const int BaudRate = 500_000; // 500kbps

    public event Action<CanFrame> FrameReceived;
    public event Action<string> StatusChanged;

    public async Task ConnectAsync()
    {
        try
        {
            _bus = await CanBus.OpenAsync(_deviceId, cfg => cfg
                .TimingClassic(BaudRate)
                .EnableErrorInfo()
                .SetAsyncBufferCapacity(512));

            _bus.FrameReceived += OnFrameReceived;
            StatusChanged?.Invoke("设备已连接");
        }
        catch (Exception ex)
        {
            HandleError($"连接失败: {ex.Message}");
        }
    }

    private void OnFrameReceived(object sender, CanFrameReceivedEventArgs e)
    {
        FrameReceived?.Invoke(e.Frame);
    }

    public async Task SendTestFrameAsync()
    {
        var frame = new CanClassicFrame(
            id: 0x123,
            data: new byte[] { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88 },
            timestamp: DateTimeOffset.Now.ToUnixTimeMilliseconds()
        );
        await _bus.TransmitAsync(frame);
    }

    public void Disconnect()
    {
        _bus?.Close();
        StatusChanged?.Invoke("设备已断开");
    }

    private void HandleError(string message)
    {
        StatusChanged?.Invoke($"错误: {message}");
        // 记录日志到文件
        File.AppendAllText("error.log", $"{DateTime.Now}: {message}\n");
    }

    public void Dispose()
    {
        _bus?.Dispose();
    }
}

三、用户界面实现(WPF示例)

1. 主窗口XAML

<Window x:Class="USBCANApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        Title="USBCAN上位机" Height="600" Width="1200">
    <DockPanel>
        <!-- 状态栏 -->
        <StatusBar DockPanel.Dock="Bottom">
            <TextBlock Text="{Binding Status}" Margin="5"/>
        </StatusBar>

        <!-- 数据监控区 -->
        <TabControl>
            <TabItem Header="实时数据">
                <DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Frames}">
                    <DataGrid.Columns>
                        <DataGridTextColumn Header="时间戳" Binding="{Binding Timestamp}"/>
                        <DataGridTextColumn Header="ID" Binding="{Binding Id:X}"/>
                        <DataGridTextColumn Header="数据" Binding="{Binding Data}"/>
                    </DataGrid.Columns>
                </DataGrid>
            </TabItem>
        </TabControl>

        <!-- 控制面板 -->
        <StackPanel Orientation="Horizontal" VerticalAlignment="Top" DockPanel.Dock="Top">
            <Button Content="连接设备" Command="{Binding ConnectCommand}"/>
            <Button Content="发送测试帧" Command="{Binding SendTestCommand}"/>
            <Button Content="断开连接" Command="{Binding DisconnectCommand}"/>
        </StackPanel>
    </DockPanel>
</Window>

2. ViewModel实现

using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;

public class MainViewModel : INotifyPropertyChanged
{
    private readonly DeviceManager _deviceManager = new DeviceManager();
    public ObservableCollection<CanFrame> Frames { get; } = new();

    public string Status { get; private set; } = "未连接";

    public ICommand ConnectCommand => new RelayCommand(async () => await ConnectAsync());
    public ICommand SendTestCommand => new RelayCommand(async () => await SendTestAsync());
    public ICommand DisconnectCommand => new RelayCommand(() => Disconnect());

    public MainViewModel()
    {
        _deviceManager.FrameReceived += OnFrameReceived;
        _deviceManager.StatusChanged += OnStatusChanged;
    }

    private async Task ConnectAsync()
    {
        await _deviceManager.ConnectAsync();
        Status = "已连接";
    }

    private async Task SendTestAsync()
    {
        await _deviceManager.SendTestFrameAsync();
    }

    private void Disconnect()
    {
        _deviceManager.Disconnect();
        Status = "已断开";
    }

    private void OnFrameReceived(CanFrame frame)
    {
        Frames.Add(frame);
    }

    private void OnStatusChanged(string status)
    {
        Status = status;
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string? propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

四、关键功能扩展

1. CAN FD支持

// 修改连接配置
var config = new ZCANChannelConfig
{
    canfd = new ZCAN_FDConfig
    {
        mode = ZCAN_FDMode.FD,
        nominal_bitrate = 5_000_000, // 5Mbps
        data_bitrate = 20_000_000    // 20Mbps
    }
};
await _bus.OpenAsync(_deviceId, config);

2. 数据滤波配置

// 添加范围滤波
_config.RangeFilter = new RangeFilter
{
    StartId = 0x100,
    EndId = 0x1FF,
    FilterType = FilterType.Standard
};

// 或使用掩码滤波
_config.MaskFilter = new MaskFilter
{
    Acr = 0x18FF50E5,
    Amr = 0x1FFFFF00
};

3. 周期发送任务

var periodicTask = _bus.SchedulePeriodic(
    new CanClassicFrame(0x321, new byte[] { 0xAA, 0x55 }),
    interval: TimeSpan.FromMilliseconds(100)
);

// 停止周期任务
periodicTask.Stop();

五、错误处理机制

1. 异常捕获

try
{
    await _bus.TransmitAsync(frame);
}
catch (CanTimeoutException ex)
{
    LogError($"发送超时: {ex.Message}");
}
catch (CanProtocolException ex)
{
    LogError($"协议错误: {ex.Message}");
}

2. 错误帧订阅

_bus.ErrorFrameReceived += (sender, error) =>
{
    var errorInfo = new StringBuilder();
    errorInfo.AppendLine($"错误码: 0x{error.ErrorInfo.ErrorFlag:X4}");
    errorInfo.AppendLine($"错误类型: {error.ErrorInfo.ErrorType}");
    errorInfo.AppendLine($"时间戳: {error.Timestamp}");
    File.AppendAllText("can_errors.log", errorInfo.ToString());
};

六、性能优化

  1. 双缓冲队列

    private ConcurrentQueue<CanFrame> _buffer = new();
    
    // 接收线程
    Task.Run(() => 
    {
        while (true)
        {
            var frame = _bus.Receive();
            _buffer.Enqueue(frame);
        }
    });
    
  2. 硬件加速

    _bus.SetHardwareFilter(new HardwareFilter
    {
        FilterType = FilterType.Standard,
        StartId = 0x100,
        EndId = 0x1FF
    });
    

七、部署与测试

1. 安装包配置

<!-- Inno Setup脚本 -->
[Files]
Source: "USBCANApp.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "zlgcan.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "error.log"; DestDir: "{app}"; Flags: ignoreversion

[Icons]
Name: "{group}\USBCAN监控"; Filename: "{app}\USBCANApp.exe"

2. 压力测试

// 模拟高负载测试
Parallel.For(0, 1000, i =>
{
    var task = _bus.TransmitAsync(new CanClassicFrame(
        id: 0x200, 
        data: new byte[8] { 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55 }
    ));
});

参考代码 基于C#的周立功USBCAN上位机程序例程 www.youwenfan.com/contentcnr/112207.html

八、扩展功能建议

  1. DBC文件解析

    集成CANdb++解析库,实现信号值自动解码

  2. Web监控接口

    使用ASP.NET Core构建REST API提供数据访问

  3. OPC UA集成

    通过OPC Foundation库实现工业协议对接

 

专注于matlab/simulink,电子电路,编程