C# 上位机 CAN 通信实战,Kvaser 硬件+多主模式数据采集

486 阅读5分钟

前言

随着工业自动化和物联网技术的迅猛发展,CAN(Controller Area Network)总线作为一种可靠且高效的通信协议,在汽车电子、工业控制等领域得到了广泛应用。为了实现上位机与CAN总线设备之间的通信,通常需要使用专门的硬件设备和相应的软件工具。

本文将详细介绍如何通过上位机实现CAN通信,包括所需的硬件设备和软件方法,并提供一个基于C#和Kvaser CAN卡的具体示例。

正文

1、硬件方法

上位机实现CAN通信通常需要借助CAN控制器或CAN总线适配器等硬件设备。

CAN控制器是一种专门用于实现CAN通信的芯片,具有CAN总线接口和处理器接口等功能。

CAN总线适配器则是一种将CAN总线信号转换为标准串行接口(如USB或PCI)的设备,可以通过串口通信实现CAN通信。

CAN总线的发展经历了多个阶段,从最初的CAN 2.0标准(发布于1991年,支持最多8字节的数据段),到2015年的CAN FD标准(支持最多64字节的数据段),再到2020年的CAN XL标准。

CAN总线采用多主通讯模式,每个CAN节点都能自主收发数据,并通过ID仲裁机制确保优先级高的数据能够实时传输。

2、软件方法

上位机实现CAN通信还需要使用相应的CAN通信软件。这些软件通常包括驱动程序、通信协议和应用程序三个主要部分:

  • 驱动程序:用于控制CAN控制器或CAN总线适配器的工作,实现CAN通信的底层控制。

  • 通信协议:规定了CAN通信的数据格式和传输方式,确保通信过程的标准化。

  • 应用程序:实现了具体的CAN通信功能,如数据采集、控制指令发送等。

通过安装和配置相应的CAN通信软件,上位机可以实现与CAN总线设备的通信功能。

代码示例

以下是一个基于C#和Kvaser CAN卡实现CAN通信的示例代码:

using Kvaser.CanLib;
using System.Text;

namespace CanDemo
{
    // https://blog.csdn.net/supposing962464/article/details/124387781
    // D:Program FilesKvaserCanlibdotnetx64netstandard2.0Kvaser.CanLib.dll
    class KvaserApi
    {
        int hnd = 0; // Kvaser通道句柄
        bool CanState = false; // CAN状态
        Thread readCANThread; // 创建数据监听控制线程

        public class CanMsg // 定义CanMsg包
        {
            public int ID;
            public byte[] Data;
            public string DataType;
            public long TimeStamp;
        }

        public void InitKvaser(int BaudRate)
        {
            int freq = 0;
            if (BaudRate == 50) // 波特率50K对应freq为-7
                freq = -7;
            if (BaudRate == 100) // 波特率100K对应freq为-5
                freq = -5;
            if (BaudRate == 125) // 波特率125K对应freq为-4
                freq = -4;
            if (BaudRate == 250) // 波特率250K对应freq为-3
                freq = -3;
            if (BaudRate == 500) // 波特率500K对应freq为-2
                freq = -2;
            if (BaudRate == 1000) // 波特率1000K对应freq为-1
                freq = -1;

            Canlib.canStatus stat = new Canlib.canStatus();
            Canlib.canInitializeLibrary(); // Kvaser软件库初始化
            hnd = Canlib.canOpenChannel(0, Canlib.canOPEN_ACCEPT_VIRTUAL); // 打开CAN通道
            stat = Canlib.canSetBusParams(hnd, freq, 0, 0, 0, 0, 0); // 设置CAN参数
            if (stat == Canlib.canStatus.canOK)
                CanState = true;
            Canlib.canBusOn(hnd); // 启动CAN BUS总线
            Canlib.canResetBus(hnd); // 重置CAN BUS总线
            Canlib.canFlushReceiveQueue(hnd); // 清空缓存区

            if (CanState == false)
            {
                Console.WriteLine("CAN启动失败!请连接CAN卡或重新插拔CAN卡!");
                return;
            }

            canWrite(hnd, 111, new byte[] { 1, 1, 1, 1, 1 }, "标准帧");

            readCANThread = new Thread(new ThreadStart(DataReadCAN));
            readCANThread.IsBackground = true;
            readCANThread.Start(); // 启动CAN接收
        }

        public void CloseKvaser()
        {
            Canlib.canBusOff(hnd); // 关闭CAN总线
            Canlib.canClose(hnd); // 关闭CAN通道
            Canlib.canUnloadLibrary(); // 卸载软件库
            CanState = false;
            if (readCANThread != null)
                readCANThread.Abort(); // 退出监听线程
        }

        public bool canWrite(int hnd, int ID, byte[] data, string dataType)
        {
            bool writeResult = false;
            Canlib.canStatus stat = Canlib.canStatus.canERR_NOMSG;
            if (dataType == "标准帧")
                stat = Canlib.canWrite(hnd, ID, data, data.Length, Canlib.canMSG_STD);
            if (dataType == "扩展帧")
                stat = Canlib.canWrite(hnd, ID, data, data.Length, Canlib.canMSG_EXT);
            if (stat == Canlib.canStatus.canOK)
                writeResult = true;
            return writeResult;
        }

        public void canRead(int hnd, int filterID)
        {
            int dlc, flags;
            byte[] msg = new byte[8];
            int IDReceive = filterID;
            long time;
            Canlib.canStatus stat;
            if (filterID == -1)
                stat = Canlib.canRead(hnd, out IDReceive, msg, out dlc, out flags, out time);
            else
                stat = Canlib.canReadSpecific(hnd, filterID, msg, out dlc, out flags, out time);

            if (stat == Canlib.canStatus.canOK)
            {
                CanMsg canmsg = new CanMsg() { TimeStamp = time };
                canmsg.ID = IDReceive;
                canmsg.Data = msg;

                if (flags == 2)
                    canmsg.DataType = "标准帧";
                if (flags == 4)
                    canmsg.DataType = "扩展帧";

                string hex = ToHexString(canmsg.Data, canmsg.Data.Length, true);
                Console.WriteLine($"recv,TimeStamp={canmsg.TimeStamp},DataType={canmsg.DataType},ID={canmsg.ID},Data={hex}");
            }
        }

        private void DataReadCAN()
        {
            while (true)
            {
                canRead(hnd, -1);
            }
        }

        public static string ToHexString(byte[] bytes, int length, bool space)
        {
            string strFill = space ? " " : "";
            string hexString = string.Empty;
            if (bytes != null)
            {
                StringBuilder strB = new StringBuilder();

                for (int i = 0; i < length; i++)
                {
                    strB.Append(bytes[i].ToString("X2") + strFill);
                }
                hexString = strB.ToString();
            }

            hexString = hexString.Trim();

            return hexString;
        }
    }
}

总结

本文详细介绍了上位机实现CAN通信的方法,包括硬件设备的选择和软件的配置。通过使用CAN控制器或CAN总线适配器,结合相应的驱动程序和通信协议,上位机可以轻松实现与CAN总线设备的通信。

另外,本文还提供了一个基于C#和Kvaser CAN卡的具体示例代码,展示了如何进行CAN通信的基本操作,如初始化、发送和接收数据等。

关键词

CAN通信、上位机、Kvaser、C#、CAN控制器、CAN总线适配器、驱动程序、通信协议、应用程序、数据采集

最后

如果你觉得这篇文章对你有帮助,不妨点个赞支持一下!你的支持是我继续分享知识的动力。如果有任何疑问或需要进一步的帮助,欢迎随时留言。

也可以加入微信公众号 [DotNet技术匠] 社区,与其他热爱技术的同行一起交流心得,共同成长!

优秀是一种习惯,欢迎大家留言学习!

作者:上位机李工

出处:mp.weixin.qq.com/s/dVDxqjIrlHhI9KazDSRVSw

声明:网络内容,仅供学习,尊重版权,侵权速删,歉意致谢!