使用USBCAN通讯

使用ZLG USBCAN-E-U,通过C#实现通讯:

1、建一个CANDevice实体类,传递一些参数,并且保存一些设备的型号、ID,以及波特率等参数

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Runtime.InteropServices;

namespace ZLGUSBCAN
{
    [Serializable]
    public class CardType
    {
        public string TyPeName { get; set; }
        public uint TypeId { get; set; }
    }
    [Serializable]
    public class BoudRoute
    {
        public string RateName { get; set; }
        public UInt32 RateValue { get; set; }
    }

    [Serializable]
    public class CANDevice
    {        
        //private readonly uint devType;
        //private readonly uint devID;
        //private readonly uint canID;
        //private readonly uint baudRate;
        //private readonly uint sendTimeout;

        public uint DevType { get; set; }
        public uint DevID { get; set; }
        public uint CANID { get; set; }
        public uint BoudRate { get; set; }
        public uint SendTimeout { get; set; }
        //CAN参数
        public uint AccCode { get; set; }
        public uint AccMask { get; set; }
        public uint Reserved { get; set; }
        public byte Filter { get; set; }
        public byte Timing0 { get; set; }
        public byte Timing1 { get; set; }
        public byte Mode { get; set; }
        public List<CardType> CardTypeList { get; set; }
        public List<BoudRoute> BoudRateList { get; set; }

        public CANDevice(uint devType, uint devID, uint boudRate, uint sendTimeout, uint canID = 0)
        {
            this.DevType = devType;
            this.DevID = devID;
            this.CANID = canID;
            this.BoudRate = boudRate;
            this.SendTimeout = sendTimeout;

        }

        public CANDevice()
        {
            this.CardTypeList = new List<CardType>();           
            this.CardTypeList.Add(new CardType { TyPeName = "VCI_USBCAN_E_U", TypeId = 20 });
            this.CardTypeList.Add(new CardType { TyPeName = "VCI_USBCAN_2E_U", TypeId = 21 });

            this.BoudRateList = new List<BoudRoute>();
            this.BoudRateList.Add(new BoudRoute { RateName = "1000Kbps", RateValue = 0x060003 });
            this.BoudRateList.Add(new BoudRoute { RateName = "800Kbps", RateValue = 0x060004 });
            this.BoudRateList.Add(new BoudRoute { RateName = "500Kbps", RateValue = 0x060007 });
            this.BoudRateList.Add(new BoudRoute { RateName = "250Kbps", RateValue = 0x1C0008 });
            this.BoudRateList.Add(new BoudRoute { RateName = "125Kbps", RateValue = 0x1C0011 });
            this.BoudRateList.Add(new BoudRoute { RateName = "100Kbps", RateValue = 0x160023 });
            this.BoudRateList.Add(new BoudRoute { RateName = "50Kbps", RateValue = 0x1C002C });
            this.BoudRateList.Add(new BoudRoute { RateName = "20Kbps", RateValue = 0x1600B3 });
            this.BoudRateList.Add(new BoudRoute { RateName = "10Kbps", RateValue = 0x1C00E0 });
            this.BoudRateList.Add(new BoudRoute { RateName = "5Kbps", RateValue = 0x1C01C1 });           
        }   
    }
}
View Code

2、建一个ControlCAN,对周立功提供的接口函数进行二次开发

首先需要引入设备供应商提供的接口函数库,在C#中属于非托管,因此需要通过[DllImport("路径.dll")]这样的方式来引入。特别注意:该接口函数如果是32位,注意你的解决方案平台需要时X86;还有,你要将所有引用的文件都复制到路径下。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using Microsoft.Win32.SafeHandles;

namespace ZLGUSBCAN
{
    public class _VCI_CAN_OBJ
    {
        public uint ID { get; set; }
        public uint TimeStamp { get; set; }
        public byte TimeFlag { get; set; }
        /// <summary>
        /// 发送格式:0:正常发送 1:单次正常发送 2:自发自收 3.单次自发自收
        /// </summary>
        public byte SendType { get; set; }
        /// <summary>
        /// 帧格式:0:数据帧 1:远程帧
        /// </summary>
        public byte RemoteFlag { get; set; }
        /// <summary>
        /// 帧类型:0:标准帧 1为扩展帧,29位ID
        /// </summary>
        public byte ExternFlag { get; set; }
        public byte DataLen { get; set; }
        public byte[] Data { get; set; }
        public byte[] Reserved { get; set; }
    }

    public class _VCI_INIT_CONFIG
    {
        public uint AccCode { get; set; }
        public uint AccMask { get; set; }
        public byte Reserved { get; set; }
        public byte Filter { get; set; }
        public byte Timing0 { get; set; }
        public byte Timing1 { get; set; }
        public byte Mode { get; set; }
    }
    public class ControlCAN
    {
        #region 接口函数定义
        [StructLayoutAttribute(LayoutKind.Sequential)]
        struct VCI_CAN_OBJ
        {
            public uint ID;
            public uint TimeStamp;
            public byte TimeFlag;
            public byte SendType;//发送格式:0:正常发送 1:单次正常发送 2:自发自收 3.单次自发自收
            public byte RemoteFlag;//帧格式:0:数据帧 1:远程帧
            public byte ExternFlag;//帧类型:0:标准帧 1为扩展帧,29位ID
            public byte DataLen;
            [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 8)]
            public byte[] Data;
            [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 3)]
            public byte[] Reserved;
        }

        [StructLayoutAttribute(LayoutKind.Sequential)]
        struct VCI_INIT_CONFIG
        {
            public uint AccCode;
            public uint AccMask;
            public uint Reserved;
            public byte Filter;
            public byte Timing0;
            public byte Timing1;
            public byte Mode;
        }

        [StructLayoutAttribute(LayoutKind.Sequential)]
        struct VCI_FILTER_RECORD
        {
            public uint ExtFrame;
            public uint Start;
            public uint End;
        }

        [DllImport("kernel32.dll")]
        static extern uint GetLastError();

        [DllImport("ControlCAN.dll")]
        static extern uint VCI_OpenDevice(uint DevType, uint DevIndex, uint Reserved);//Reserved系统保留字段

        [DllImport("ControlCAN.dll")]
        static extern uint VCI_CloseDevice(uint DevType, uint DevIndex);

        [DllImport("ControlCAN.dll")]
        static extern uint VCI_StartCAN(uint DevType, uint DevIndex, uint CANIndex);

        [DllImport("ControlCAN.dll")]
        static extern uint VCI_ResetCAN(uint DevType, uint DevIndex, uint CANIndex);

        [DllImport("ControlCAN.dll")]
        static extern uint VCI_SetReference(uint DevType, uint DevIndex, uint CANIndex, uint RefType, IntPtr pData);

        //[DllImport("ControlCAN.dll")]
        //unsafe static extern UInt32 VCI_SetReference(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd, UInt32 RefType, byte* pData);

        [DllImport("ControlCAN.dll")]
        static extern uint VCI_Receive(uint DevType, uint DevIndex, uint CANIndex, [Out] VCI_CAN_OBJ[] pReceive, uint Len, int WaitTime);
        //参照官方
        [DllImport("ControlCAN.dll", CharSet = CharSet.Ansi)]
        static extern UInt32 VCI_Receive(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd, IntPtr pReceive, UInt32 Len, Int32 WaitTime);

        [DllImport("ControlCAN.dll")]
        static extern uint VCI_Transmit(uint DevType, uint DevIndex, uint CANIndex, [In] VCI_CAN_OBJ[] pSend, uint Len);

        [DllImport("ControlCAN.dll")]
        static extern uint VCI_InitCAN(uint DevType, uint DevIndex, uint CANIndex, ref VCI_INIT_CONFIG pInitConfig);

        [DllImport("ControlCAN.dll")]
        static extern uint VCI_GetReceiveNum(uint DevType, uint DevIndex, uint CANIndex);

        [DllImport("ControlCAN.dll")]
        static extern uint VCI_ClearBuffer(uint DevType, uint DevIndex, uint CANIndex);

        [DllImport("RC500USB.dll")]
        static extern byte RC500USB_init();

        [DllImport("RC500USB.dll")]
        static extern void RC500USB_exit();

        [DllImport("RC500USB.dll")]
        static extern byte RC500USB_request(byte mode, ref ushort tagtype);

        [DllImport("RC500USB.dll")]
        static extern byte RC500USB_anticoll(byte bcnt, ref uint snr);

        [DllImport("RC500USB.dll")]
        static extern byte RC500USB_select(uint snr, ref byte size);

        [DllImport("RC500USB.dll")]
        internal static extern byte RC500USB_authkey(byte mode, [In] byte[] key, byte secnr);

        [DllImport("RC500USB.dll")]
        internal static extern byte RC500USB_read(byte addr, [Out] byte[] data);

        [DllImport("RC500USB.dll")]
        internal static extern byte RC500USB_buzzer(byte contrl, byte opentm, byte closetm, byte repcnt);

        // serialport
        //Win32 io errors
        public const int ERROR_BROKEN_PIPE = 109;
        public const int ERROR_NO_DATA = 232;
        public const int ERROR_HANDLE_EOF = 38;
        public const int ERROR_IO_INCOMPLETE = 996;
        public const int ERROR_IO_PENDING = 997;
        public const int ERROR_FILE_EXISTS = 0x50;
        public const int ERROR_FILENAME_EXCED_RANGE = 0xCE;  // filename too long.
        public const int ERROR_MORE_DATA = 234;
        public const int ERROR_CANCELLED = 1223;
        public const int ERROR_FILE_NOT_FOUND = 2;
        public const int ERROR_PATH_NOT_FOUND = 3;
        public const int ERROR_ACCESS_DENIED = 5;
        public const int ERROR_INVALID_HANDLE = 6;
        public const int ERROR_NOT_ENOUGH_MEMORY = 8;
        public const int ERROR_BAD_COMMAND = 22;
        public const int ERROR_SHARING_VIOLATION = 32;
        public const int ERROR_OPERATION_ABORTED = 995;
        public const int ERROR_NO_ASSOCIATION = 1155;
        public const int ERROR_DLL_NOT_FOUND = 1157;
        public const int ERROR_DDE_FAIL = 1156;
        public const int ERROR_INVALID_PARAMETER = 87;
        public const int ERROR_PARTIAL_COPY = 299;

        // Since C# does not provide access to bitfields and the native DCB structure contains
        // a very necessary one, these are the positional offsets (from the right) of areas
        // of the 32-bit integer used in SerialStream's SetDcbFlag() and GetDcbFlag() methods.
        internal const int FBINARY = 0;
        internal const int FPARITY = 1;
        internal const int FOUTXCTSFLOW = 2;
        internal const int FOUTXDSRFLOW = 3;
        internal const int FDTRCONTROL = 4;
        internal const int FDSRSENSITIVITY = 6;
        internal const int FTXCONTINUEONXOFF = 7;
        internal const int FOUTX = 8;
        internal const int FINX = 9;
        internal const int FERRORCHAR = 10;
        internal const int FNULL = 11;
        internal const int FRTSCONTROL = 12;
        internal const int FABORTONOERROR = 14;
        internal const int FDUMMY2 = 15;

        // The following are unique to the SerialPort/SerialStream classes
        internal const byte ONESTOPBIT = 0;
        internal const byte ONE5STOPBITS = 1;
        internal const byte TWOSTOPBITS = 2;

        public const int FILE_READ_DATA = (0x0001),
        FILE_LIST_DIRECTORY = (0x0001),
        FILE_WRITE_DATA = (0x0002),
        FILE_ADD_FILE = (0x0002),
        FILE_APPEND_DATA = (0x0004),
        FILE_ADD_SUBDIRECTORY = (0x0004),
        FILE_CREATE_PIPE_INSTANCE = (0x0004),
        FILE_READ_EA = (0x0008),
        FILE_WRITE_EA = (0x0010),
        FILE_EXECUTE = (0x0020),
        FILE_TRAVERSE = (0x0020),
        FILE_DELETE_CHILD = (0x0040),
        FILE_READ_ATTRIBUTES = (0x0080),
        FILE_WRITE_ATTRIBUTES = (0x0100),
        FILE_SHARE_READ = 0x00000001,
        FILE_SHARE_WRITE = 0x00000002,
        FILE_SHARE_DELETE = 0x00000004,
        FILE_ATTRIBUTE_READONLY = 0x00000001,
        FILE_ATTRIBUTE_HIDDEN = 0x00000002,
        FILE_ATTRIBUTE_SYSTEM = 0x00000004,
        FILE_ATTRIBUTE_DIRECTORY = 0x00000010,
        FILE_ATTRIBUTE_ARCHIVE = 0x00000020,
        FILE_ATTRIBUTE_NORMAL = 0x00000080,
        FILE_ATTRIBUTE_TEMPORARY = 0x00000100,
        FILE_ATTRIBUTE_COMPRESSED = 0x00000800,
        FILE_ATTRIBUTE_OFFLINE = 0x00001000,
        FILE_NOTIFY_CHANGE_FILE_NAME = 0x00000001,
        FILE_NOTIFY_CHANGE_DIR_NAME = 0x00000002,
        FILE_NOTIFY_CHANGE_ATTRIBUTES = 0x00000004,
        FILE_NOTIFY_CHANGE_SIZE = 0x00000008,
        FILE_NOTIFY_CHANGE_LAST_WRITE = 0x00000010,
        FILE_NOTIFY_CHANGE_LAST_ACCESS = 0x00000020,
        FILE_NOTIFY_CHANGE_CREATION = 0x00000040,
        FILE_NOTIFY_CHANGE_SECURITY = 0x00000100,
        FILE_ACTION_ADDED = 0x00000001,
        FILE_ACTION_REMOVED = 0x00000002,
        FILE_ACTION_MODIFIED = 0x00000003,
        FILE_ACTION_RENAMED_OLD_NAME = 0x00000004,
        FILE_ACTION_RENAMED_NEW_NAME = 0x00000005,
        FILE_CASE_SENSITIVE_SEARCH = 0x00000001,
        FILE_CASE_PRESERVED_NAMES = 0x00000002,
        FILE_UNICODE_ON_DISK = 0x00000004,
        FILE_PERSISTENT_ACLS = 0x00000008,
        FILE_FILE_COMPRESSION = 0x00000010,
        OPEN_EXISTING = 3,
        OPEN_ALWAYS = 4,
        FILE_FLAG_WRITE_THROUGH = unchecked((int)0x80000000),
        FILE_FLAG_OVERLAPPED = 0x40000000,
        FILE_FLAG_NO_BUFFERING = 0x20000000,
        FILE_FLAG_RANDOM_ACCESS = 0x10000000,
        FILE_FLAG_SEQUENTIAL_SCAN = 0x08000000,
        FILE_FLAG_DELETE_ON_CLOSE = 0x04000000,
        FILE_FLAG_BACKUP_SEMANTICS = 0x02000000,
        FILE_FLAG_POSIX_SEMANTICS = 0x01000000,
        FILE_TYPE_UNKNOWN = 0x0000,
        FILE_TYPE_DISK = 0x0001,
        FILE_TYPE_CHAR = 0x0002,
        FILE_TYPE_PIPE = 0x0003,
        FILE_TYPE_REMOTE = unchecked((int)0x8000),
        FILE_VOLUME_IS_COMPRESSED = 0x00008000;

        // The following are unique to the SerialPort/SerialStream classes
        internal const int DTR_CONTROL_DISABLE = 0x00;
        internal const int DTR_CONTROL_ENABLE = 0x01;
        internal const int DTR_CONTROL_HANDSHAKE = 0x02;

        internal const int RTS_CONTROL_DISABLE = 0x00;
        internal const int RTS_CONTROL_ENABLE = 0x01;
        internal const int RTS_CONTROL_HANDSHAKE = 0x02;
        internal const int RTS_CONTROL_TOGGLE = 0x03;

        internal const int MS_CTS_ON = 0x10;
        internal const int MS_DSR_ON = 0x20;

        internal const byte DEFAULTXONCHAR = (byte)17;
        internal const byte DEFAULTXOFFCHAR = (byte)19;

        internal const byte EOFCHAR = (byte)26;

        [StructLayoutAttribute(LayoutKind.Sequential)]
        public struct COMMTIMEOUTS
        {
            public int ReadIntervalTimeout;
            public int ReadTotalTimeoutMultiplier;
            public int ReadTotalTimeoutConstant;
            public int WriteTotalTimeoutMultiplier;
            public int WriteTotalTimeoutConstant;
        }

        [StructLayout(LayoutKind.Sequential)]
        internal struct DCB
        {
            public uint DCBlength;
            public uint BaudRate;
            public uint Flags;
            public ushort wReserved;
            public ushort XonLim;
            public ushort XoffLim;
            public byte ByteSize;
            public byte Parity;
            public byte StopBits;
            public byte XonChar;
            public byte XoffChar;
            public byte ErrorChar;
            public byte EofChar;
            public byte EvtChar;
            public ushort wReserved1;
        }

        public const int GENERIC_READ = unchecked(((int)0x80000000));
        public const int GENERIC_WRITE = (0x40000000);

        internal const int PURGE_TXABORT = 0x0001;  // Kill the pending/current writes to the comm port.
        internal const int PURGE_RXABORT = 0x0002;  // Kill the pending/current reads to the comm port.
        internal const int PURGE_TXCLEAR = 0x0004;  // Kill the transmit queue if there.
        internal const int PURGE_RXCLEAR = 0x0008;  // Kill the typeahead buffer if there.
        #endregion

        private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

        public uint MaxFrames { get; set; } = 16;
        public int ReceiveTimeout { get; set; } = 2000;

        private int disposed = 0;
        private CANDevice objCANDevice;

        protected virtual void Dispose(bool disposing)
        {
            if (Interlocked.Exchange(ref disposed, 1) == 0)
            {
                if (Interlocked.Exchange(ref disposed, 1) == 0)
                {
                    //释放非托管资源
                    if (ControlCAN.VCI_CloseDevice(objCANDevice.DevType, objCANDevice.DevID) == 0)
                    {
                        log.Error("Failed to close CAN device");
                    }
                }
            }
        }

        public void Dispose()
        {
            ///<summary>
            /// 实现IDisposable中的Dispose方法
            ///</summary>
            Dispose(true);//必须为true
            //通知垃圾回收机制不再调用终结器(析构器)
            GC.SuppressFinalize(this);//禁止终结操作
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="devType"></param>
        /// <param name="devID"></param>
        /// <param name="baudRate"></param>
        /// <param name="sendTimeout"></param>
        /// <param name="canID">默认为0</param>
        public ControlCAN(CANDevice canDevice)
        {
            objCANDevice = canDevice;
        }

        /// <summary>
        /// 打开CAN设备
        /// </summary>
        /// <returns></returns>
        public bool OpenCAN()
        {
            if (VCI_OpenDevice(objCANDevice.DevType, objCANDevice.DevID, 0) == 0)
            {
                //log.Error("Failed to open CAN device");
                //uint error = GetLastError();
                return false;
            }
            return true;
        }

        /// <summary>
        /// 启动某一路CAN
        /// </summary>
        public bool StartCan(uint canId = 0)
        {
            if (Thread.VolatileRead(ref disposed) != 0)
            {
                throw new ObjectDisposedException("CanDevice already disposed");
            }

            if (canId != 0)
            {
                objCANDevice.CANID = canId;
            }

            if (!SetBaudRate(objCANDevice.BoudRate))
            {
                throw new Exception("波特率设置失败!");
            }
            if (!SetWorkingMode())
            {
                throw new Exception("工作模式设置失败!");
            }

            if (VCI_StartCAN(objCANDevice.DevType, objCANDevice.DevID, objCANDevice.CANID) == 0)
            {

                //log.Error("Failed to start CAN device");
                return false;
            }
            if (!SetSendTimeout())
            {
                throw new Exception("发送超时设置失败!");
            }
            ClearBuffer();

            return true;
        }

        /// <summary>
        /// 设置波特率
        /// </summary>
        /// <param name="baudRate">uint类型</param>
        /// <returns></returns>
        public bool SetBaudRate(uint baudRate)
        {
            if (Thread.VolatileRead(ref disposed) != 0)
            {
                throw new ObjectDisposedException("CanDevice already disposed");
            }

            IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(baudRate));

            try
            {
                Marshal.WriteInt32(ptr, (int)baudRate);
                if (VCI_SetReference(objCANDevice.DevType, objCANDevice.DevID, objCANDevice.CANID, 0, ptr) == 0)
                {
                    log.Error("Failed to set CAN baud rate");
                    return false;
                }
            }
            finally
            {
                Marshal.FreeHGlobal(ptr);
            }

            return true;
        }

        /// <summary>
        /// 设置工作模式,需提供工作模式Id
        /// 必须先设置波特率再设置工模式
        /// </summary>
        /// <param name="modeId"> =0 表示正常模式(相当于正常节点), =1 表示只听模式(只接收,不影响总线)</param>
        /// <returns></returns>
        public bool SetWorkingMode(byte modeId = 0)
        {
            if (Thread.VolatileRead(ref disposed) != 0)
            {
                throw new ObjectDisposedException("CanDevice already disposed");
            }

            VCI_INIT_CONFIG initConfig = new VCI_INIT_CONFIG();
            initConfig.Mode = modeId;//正常模式

            if (VCI_InitCAN(objCANDevice.DevType, objCANDevice.DevID, objCANDevice.CANID, ref initConfig) == 0)
            {
                log.Error("Failed to set CAN working mode");
                return false;
            }

            return true;
        }

        /// <summary>
        ///设置发送超时时间
        /// </summary>
        public bool SetSendTimeout(uint timeout = 2000)
        {
            if (Thread.VolatileRead(ref disposed) != 0)
            {
                throw new ObjectDisposedException("CanDevice already disposed");
            }

            IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(timeout));

            try
            {
                Marshal.WriteInt32(ptr, (int)timeout);
                if (VCI_SetReference(objCANDevice.DevType, objCANDevice.DevID, objCANDevice.CANID, 4, ptr) == 0)
                {
                    log.Error("Failed to set CAN send timeout");
                    return false;
                }
            }
            finally
            {
                Marshal.FreeHGlobal(ptr);
            }

            return true;
        }

        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="frames">帧结构体数组</param>
        /// 举例:VCI_CAN_OBJ[] frames=new VCI_CAN_OBJ[2];//将发送两帧数据
        /// frames[0].ID=0x00000001;//第一帧ID
        /// frames[0].SendType=0;//正常发送
        /// frames[0].RemoteFlag=0;//数据帧
        /// frames[0].ExternFlag=0;//标准帧
        /// frames[0].DataLen=1;//数据长度
        /// frames[0].Data[0]=0x56;//数据
        /// frames[1]~
        /// <returns></returns>
        public bool Transmit(_VCI_CAN_OBJ[] frames)
        {
            if (Thread.VolatileRead(ref disposed) != 0)
            {
                throw new ObjectDisposedException("CanDevice already disposed");
            }
            int length = frames.Length;
            VCI_CAN_OBJ[] obj = new VCI_CAN_OBJ[length];
            for (int i = 0; i < length; i++)
            {
                obj[i] = new VCI_CAN_OBJ
                {
                    ID = frames[i].ID,
                    TimeFlag = frames[i].TimeFlag,
                    TimeStamp = frames[i].TimeStamp,
                    SendType = frames[i].SendType,
                    RemoteFlag = frames[i].RemoteFlag,
                    ExternFlag = frames[i].ExternFlag,
                    Data = frames[i].Data,
                    DataLen = frames[i].DataLen,
                    Reserved = frames[i].Reserved
                };
            }

            return VCI_Transmit(objCANDevice.DevType, objCANDevice.DevID, objCANDevice.CANID, obj, (uint)length) != 0;
        }

        public bool Transmit(_VCI_CAN_OBJ frame)
        {
            if (Thread.VolatileRead(ref disposed) != 0)
            {
                throw new ObjectDisposedException("CanDevice already disposed");
            }

            VCI_CAN_OBJ[] frames = new VCI_CAN_OBJ[1];
            frames[0] = new VCI_CAN_OBJ
            {
                ID = frame.ID,
                TimeFlag = frame.TimeFlag,
                TimeStamp = frame.TimeStamp,
                SendType = frame.SendType,
                RemoteFlag = frame.RemoteFlag,
                ExternFlag = frame.ExternFlag,
                Data = frame.Data,
                DataLen = frame.DataLen,
                Reserved = frame.Reserved
            };

            return VCI_Transmit(objCANDevice.DevType, objCANDevice.DevID, objCANDevice.CANID, frames, (uint)frames.Length) != 0;
        }

        /// <summary>
        /// 设置CAN相关参数
        /// </summary>
        /// <param name="config"></param>
        /// <returns></returns>
        public bool InitCAN(_VCI_INIT_CONFIG config)
        {
            if (Thread.VolatileRead(ref disposed) != 0)
            {
                throw new ObjectDisposedException("CanDevice already disposed");
            }

            VCI_INIT_CONFIG obj = new VCI_INIT_CONFIG
            {
                AccCode = config.AccCode,
                AccMask = config.AccMask,
                Mode = config.Mode,
                Filter = config.Filter,
                Timing0 = config.Timing0,
                Timing1 = config.Timing1,
                Reserved = config.Reserved
            };

            if (VCI_InitCAN(objCANDevice.DevType, objCANDevice.DevID, objCANDevice.CANID, ref obj) == 0)
            {
                return false;
            }
            return true;
        }

        #region 参考ZLG官方示例
        /// <summary>
        /// 接收数据
        /// </summary>
        /// <returns></returns>
        public List<_VCI_CAN_OBJ> Receive()
        {
            //获取CAN通道缓冲区中已接收但未读取的帧数量
            uint receiveNum = VCI_GetReceiveNum(objCANDevice.DevType, objCANDevice.DevID, objCANDevice.CANID);
            if (receiveNum == 0) return null;

            //如果缓冲区未读取的帧数量大于规定的最大读取量,只能读取规定的最大量的帧数据,其它数据丢失
            uint needReceiveNum = receiveNum > MaxFrames ? MaxFrames : receiveNum;

            IntPtr pt = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(VCI_CAN_OBJ)) * (Int32)receiveNum);
            //返回值为实际读取的帧数量,数据填充至buf
            receiveNum = VCI_Receive(objCANDevice.DevType, objCANDevice.DevID, objCANDevice.CANID, pt, receiveNum, 100);

            List<_VCI_CAN_OBJ> result = new List<_VCI_CAN_OBJ>();
            //string str = "";
            for (int i = 0; i < receiveNum; i++)
            {
                VCI_CAN_OBJ obj = (VCI_CAN_OBJ)Marshal.PtrToStructure((IntPtr)((UInt32)pt + i * Marshal.SizeOf(typeof(VCI_CAN_OBJ))), typeof(VCI_CAN_OBJ));

               
                //foreach (var item in obj.Data)
                //{
                //    str += " " + System.Convert.ToString(item, 16);
                //}

                result.Add(new _VCI_CAN_OBJ
                {
                    ID = obj.ID,
                    TimeFlag = obj.TimeFlag,
                    TimeStamp = obj.TimeStamp,
                    SendType = obj.SendType,
                    RemoteFlag = obj.RemoteFlag,
                    ExternFlag = obj.ExternFlag,
                    Data = obj.Data,
                    DataLen = obj.DataLen,
                    Reserved = obj.Reserved
                });
            }
            return result;
        }

        /// <summary>
        /// 直接获取接收数据的string形式
        /// </summary>
        /// <returns></returns>
        public string OnlyReceiveData()
        {
            //获取CAN通道缓冲区中已接收但未读取的帧数量
            uint receiveNum = VCI_GetReceiveNum(objCANDevice.DevType, objCANDevice.DevID, objCANDevice.CANID);
            if (receiveNum == 0) return null;

            //如果缓冲区未读取的帧数量大于规定的最大读取量,只能读取规定的最大量的帧数据,其它数据丢失
            uint needReceiveNum = receiveNum > MaxFrames ? MaxFrames : receiveNum;

            IntPtr pt = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(VCI_CAN_OBJ)) * (Int32)receiveNum);
            //返回值为实际读取的帧数量,数据填充至buf
            receiveNum = VCI_Receive(objCANDevice.DevType, objCANDevice.DevID, objCANDevice.CANID, pt, receiveNum, 100);
           
            string str = "";
            for (int i = 0; i < receiveNum; i++)
            {
                VCI_CAN_OBJ obj = (VCI_CAN_OBJ)Marshal.PtrToStructure((IntPtr)((UInt32)pt + i * Marshal.SizeOf(typeof(VCI_CAN_OBJ))), typeof(VCI_CAN_OBJ));

                foreach (var item in obj.Data)
                {
                    str += " " + System.Convert.ToString(item, 16);
                }
            }
            return str;
        }
        #endregion

        #region 修改接收方法:调用的VCI_Receive参数不同
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public List<_VCI_CAN_OBJ> _Receive()
        {
            if (Thread.VolatileRead(ref disposed) != 0)
            {
                throw new ObjectDisposedException("CanDevice already disposed");
            }

            VCI_CAN_OBJ[] buf = new VCI_CAN_OBJ[MaxFrames];//MaxFrames为规定的最大接收数

            uint num = VCI_Receive(objCANDevice.DevType, objCANDevice.DevID, objCANDevice.CANID, buf, (uint)buf.Length,2000);
           
           VCI_CAN_OBJ[] frames = new VCI_CAN_OBJ[num];
            Array.Copy(buf, frames, num);

            List<_VCI_CAN_OBJ> result = new List<_VCI_CAN_OBJ>();
            foreach (var obj in frames)
            {
                result.Add(new _VCI_CAN_OBJ
                {
                    ID = obj.ID,
                    TimeFlag = obj.TimeFlag,
                    TimeStamp = obj.TimeStamp,
                    SendType = obj.SendType,
                    RemoteFlag = obj.RemoteFlag,
                    ExternFlag = obj.ExternFlag,
                    Data = obj.Data,
                    DataLen = obj.DataLen,
                    Reserved = obj.Reserved
                });
            }

            return result;
        }
        #endregion
        /// <summary>
        /// 查看缓存区是否存在未读取数据
        /// </summary>
        /// <returns></returns>
        public bool HasReceive()
        {
            return VCI_GetReceiveNum(objCANDevice.DevType, objCANDevice.DevID, objCANDevice.CANID) != 0;
        }

        /// <summary>
        /// 清除接收缓冲区数据
        /// </summary>
        /// <returns></returns>
        public bool ClearBuffer()
        {
            if (Thread.VolatileRead(ref disposed) != 0)
            {
                throw new ObjectDisposedException("CanDevice already disposed");
            }

            if (ControlCAN.VCI_ClearBuffer(objCANDevice.DevType, objCANDevice.DevID, objCANDevice.CANID) == 0)
            {
                log.Error("Failed to clear can.");
                return false;
            }

            return true;
        }

        /// <summary>
        /// 关闭CAN
        /// </summary>
        /// <returns></returns>
        public bool CloseCAN()
        {
            if (VCI_CloseDevice(objCANDevice.DevType, objCANDevice.DevID) == 0)
            {
                //log.Error("Failed to close CAN device");
                return false;
            }
            return true;
        }
    }
}
View Code

基于提供的接口函数,对连接、打开、发送、接收等方法进行再次封装。

3、这样就可以实现通讯了

我做了一个简单的winform界面,可以进行简单的参数设置,并且能发送、接收数据。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace ZLGUSBCAN
{
    public partial class Form1 : Form
    {
        private CANDevice objCANDevice = null;
        private ControlCAN objControlCAN = null;
        private bool isOpen = false;

        public Form1()
        {
            InitializeComponent();

            this.cboDevType.DataSource = new CANDevice().CardTypeList;
            this.cboDevType.ValueMember = "TypeId";
            this.cboDevType.DisplayMember = "TypeName";

            this.cboBaudRate.DataSource = new CANDevice().BoudRateList;
            this.cboBaudRate.ValueMember = "RateValue";
            this.cboBaudRate.DisplayMember = "RateName";

            this.cboDevType.SelectedIndex = 0;
            this.cboDevIndex.SelectedIndex = 0;
            this.cboCANIndex.SelectedIndex = 0;
            this.cboBaudRate.SelectedIndex = 0;
            this.cboFilter.SelectedIndex = 1;
            this.cboFilterMode.SelectedIndex = 0;
            this.cboFrameFormat.SelectedIndex = 0;
            this.cboFrameType.SelectedIndex = 0;
            this.cboSendType.SelectedIndex = 2;//自发自收
            this.cboMode.SelectedIndex = 0;

            receiverTimer.Start();
        }

        private void btnConnect_Click(object sender, EventArgs e)
        {
            try
            {
                if (isOpen)//CAN设备已打开
                {
                    if (objCANDevice != null && objControlCAN != null)
                    {
                        objControlCAN.CloseCAN();
                        btnConnect.Text = "连接";
                        btnStartCAN.Text = "启动CAN";
                        isOpen = false;
                    }
                }
                else//CAN设备未打开
                {
                    objCANDevice = new CANDevice(Convert.ToUInt32(this.cboDevType.SelectedValue), Convert.ToUInt32(this.cboDevIndex.Text), Convert.ToUInt32(this.cboBaudRate.SelectedValue), 2000, Convert.ToUInt32(this.cboCANIndex.Text));
                  
                    objControlCAN = new ControlCAN(objCANDevice);
                    
                    if (objControlCAN.OpenCAN())
                    {
                        btnConnect.Text = "关闭连接";
                        isOpen = true;
                    }
                    else
                        MessageBox.Show("设备打开失败!");
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }

        }

        private void btnStartCAN_Click(object sender, EventArgs e)
        {
            try
            {
                if (isOpen)
                {
                    if (objControlCAN.StartCan())
                    {
                        //初始化CAN参数,可以没有,有默认值
                        _VCI_INIT_CONFIG config = new _VCI_INIT_CONFIG()
                        {
                            AccCode = Convert.ToUInt32("0x" + this.txtAccCode.Text, 16),
                            AccMask = Convert.ToUInt32("0x" + this.txtAccMask.Text, 16),
                            Filter = (Byte)this.cboFilter.SelectedIndex,
                            Mode = (Byte)this.cboFilterMode.SelectedIndex,
                            Timing0 = System.Convert.ToByte("0x" + this.txtTime0.Text, 16),
                            Timing1 = System.Convert.ToByte("0x" + this.txtTime1.Text, 16)
                        };
                        objControlCAN.InitCAN(config);

                        this.btnStartCAN.Text = "打开成功";
                    }
                    else
                    {
                        MessageBox.Show("打开失败!");
                        return;
                    }
                }
                else
                    MessageBox.Show("请先连接CAN设备!");
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }

        }

        private void btnSend_Click(object sender, EventArgs e)
        {
            _VCI_CAN_OBJ frame = new _VCI_CAN_OBJ();
            //ControlCAN.VCI_CAN_OBJ frame = new ControlCAN.VCI_CAN_OBJ();
            try
            {
                frame.SendType = (byte)this.cboSendType.SelectedIndex;
                frame.RemoteFlag = (byte)this.cboFrameFormat.SelectedIndex;
                frame.ExternFlag = (byte)this.cboFrameType.SelectedIndex;
                frame.ID = Convert.ToUInt32("0x" + txtID.Text, 16);

                string[] dataStr = this.txtData.Text.Trim().Split(' ');
                frame.Data = new byte[dataStr.Length];
                frame.DataLen = (byte)dataStr.Length;

                for (int i = 0; i < dataStr.Length; i++)
                {
                    frame.Data[i] = Convert.ToByte("0x" + dataStr[i], 16);
                }

                if (objControlCAN.Transmit(frame))
                {
                    lbInfo.Items.Add($"发送 {DateTime.Now.ToShortTimeString()} 帧ID:{txtID.Text} 数据:{this.txtData.Text}");
                }
                else
                {
                    MessageBox.Show("发送失败!");
                    return;
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private void receiverTimer_Tick(object sender, EventArgs e)
        {
            if (objControlCAN != null && objControlCAN.HasReceive())
            {
                List<_VCI_CAN_OBJ> objOBJ = objControlCAN._Receive();
                foreach (var obj in objOBJ)
                {
                    string str = "接收 ";
                    str += "  帧ID:0x" + System.Convert.ToString((Int32)obj.ID, 16);
                    str += "  帧格式:";

                    if (obj.RemoteFlag == 0)
                    {
                        str += "数据: ";
                        foreach (var item in obj.Data)
                        {
                            str += " " + System.Convert.ToString(item, 16);
                        }
                    }
                    lbInfo.Items.Add(str);
                    lbInfo.SelectedIndex = lbInfo.Items.Count - 1;
                }
            }
        }
    }
}
View Code

有兴趣的盆友可以来看看:github地址

空间受限不能把项目全部上传,我仅将比较重要的CANDevice、ControlCAN,地址:点击,关于周立功的一些资料比如接口函数定义、相关库函数等,自己可以去官网找,留言我也可以发你~感谢!多多指教~

posted @ 2018-04-05 12:00  EasonDongH  阅读(5041)  评论(3编辑  收藏  举报