使用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 }); } } }
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; } } }
基于提供的接口函数,对连接、打开、发送、接收等方法进行再次封装。
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; } } } } }
有兴趣的盆友可以来看看:github地址
空间受限不能把项目全部上传,我仅将比较重要的CANDevice、ControlCAN,地址:点击,关于周立功的一些资料比如接口函数定义、相关库函数等,自己可以去官网找,留言我也可以发你~感谢!多多指教~