使用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 });
}
}
}
复制代码
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; }
///
/// 发送格式:0:正常发送 1:单次正常发送 2:自发自收 3.单次自发自收
///
public byte SendType { get; set; }
///
/// 帧格式:0:数据帧 1:远程帧
///
public byte RemoteFlag { get; set; }
///
/// 帧类型:0:标准帧 1为扩展帧,29位ID
///
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,地址:点击,关于周立功的一些资料比如接口函数定义、相关库函数等,自己可以去官网找,留言我也可以发你感谢!多多指教