diff --git a/CANHelper/ControlCAN.dll b/CANHelper/ControlCAN.dll new file mode 100644 index 0000000..daf472a Binary files /dev/null and b/CANHelper/ControlCAN.dll differ diff --git a/CANHelper/USBCAN/CANHelper.cs b/CANHelper/USBCAN/CANHelper.cs new file mode 100644 index 0000000..4e0bf77 --- /dev/null +++ b/CANHelper/USBCAN/CANHelper.cs @@ -0,0 +1,346 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Threading; + +namespace USBCAN +{ + /// + /// CAN帮助类 + /// + /// + /// 使用方法: + /// + /// CANHelper.Instance.Initialize(); // 在首次使用时需要初始化,初始化失败时会抛出异常 + /// CANHelper.Instance.ConsumptionFrameEvent += (CAN_API.VCI_CAN_OBJ frame){ /* TODO:在这里消费掉数据帧,注意,这里是异步调用 */ } + /// CANHelper.Instance.SendData(0x1111, data); // 向CAN发送数据,具体方法参阅注释 + /// + /// + public sealed class CANHelper : IDisposable + { + /// + /// CAN实例 + /// + public static readonly CANHelper Instance = new CANHelper(); + + #region 公开属性 + /// + /// CAN是否打开 + /// + public bool IsOpen { private set; get; } = false; + #endregion + + #region 释放 + /// + /// Flag: 标识Disposed是否已经被调用 + /// + private bool _IsDisposed = false; + + /// + /// 公开的Dispose方法 + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// 实际Dispose方法 + /// + /// 是否由用户调用 + private void Dispose(bool disposing) + { + if (_IsDisposed) + return; + + if (disposing) + { + // 释放托管成员 + } + + // 释放非托管成员 + + // 关闭CAN设备 + CloseDevice(); + + // 标识为已执行Dispose + _IsDisposed = true; + } + + /// + /// 析构 + /// + ~CANHelper() + { + // 释放非托管内存 + Dispose(false); + } + #endregion + + #region 打开与关闭 + /// + /// 初始化并打开CAN设备 + /// + public void Initialize() + { + // 如果已经打开,直接返回 + if (IsOpen) + return; + + // 打开设备 + if (CAN_API.VCI_OpenDevice(_DeviceType, _DeviceInd, _CANInd) == CAN_API.STATUS_ERR) + throw new Exception("CAN设备打开失败,错误信息:" + ReadErrorMessage()); + + // 构造CAN配置信息 + CAN_API.VCI_INIT_CONFIG pInitConfig = new CAN_API.VCI_INIT_CONFIG + { + AccCode = 0x00000000, // 表示全部接收(全部接收: AccCode:0x00000000) + AccMask = 0xFFFFFFFF, // ( AccMask:0xFFFFFFFF) + Reserved = 0x00, // 保留,填0 + Filter = 0x01, // 滤波方式 01 + Timing0 = 0x01, // ( 相当于波特率1000kbps ) + Timing1 = 0x14, // ( 相当于波特率1000kbps ) + Mode = 0x00 // 正常模式; 0:正常模式,可以IO。 1:表示只听模式(只接收,不影响总线) + }; + // 初始化CAN + if (CAN_API.VCI_InitCAN(_DeviceType, _DeviceInd, _CANInd, ref pInitConfig) == CAN_API.STATUS_ERR) + throw new Exception("CAN初始化失败,错误信息:" + ReadErrorMessage()); + + // 启动CAN + if (CAN_API.VCI_StartCAN(_DeviceType, _DeviceInd, _CANInd) == CAN_API.STATUS_ERR) + throw new Exception("CAN启动失败,错误信息:" + ReadErrorMessage()); + + // 若未出错,标识打开 + IsOpen = true; + + // 初始化消息帧缓冲区,上限为128帧报文,若满了还未消费则阻塞 + _FrameBuffer = new BlockingCollection(128); + + // 生产者消费者开始工作 + StartWork(); + } + + /// + /// 关闭CAN设备 + /// + public void CloseDevice() + { + if (IsOpen) + { + // 关闭设备 + if (CAN_API.VCI_CloseDevice(_DeviceType, _DeviceInd) == CAN_API.STATUS_ERR) + throw new Exception("CAN设备关闭失败,错误信息:" + ReadErrorMessage()); + + IsOpen = false; + } + } + #endregion + + #region 公开方法 + /// + /// 读取错误信息 + /// + /// + public string ReadErrorMessage() + { + CAN_API.VCI_ERR_INFO errInfo = new CAN_API.VCI_ERR_INFO(); + try + { + // 尝试读取错误信息 + if (CAN_API.VCI_ReadErrInfo(_DeviceType, _DeviceInd, _CANInd, ref errInfo) == CAN_API.STATUS_ERR) + return "读取错误信息失败"; + } + catch (Exception ex) + { + return string.Format("读取错误信息时发生异常({0})", ex.Message); + } + + if (errInfo.ErrCode == 0x00) + { + // 若无错误信息,则返回‘无错误信息’ + return "无错误信息"; + } + + // 由于可能同时出现多种错误,使用按位与的方式读取错误信息 + List errMsgList = new List(); + if ((errInfo.ErrCode & (uint)CAN_API.ErrorType.ERR_CAN_OVERFLOW) != 0) + errMsgList.Add("CAN控制器内部FIFO溢出"); + if ((errInfo.ErrCode & (uint)CAN_API.ErrorType.ERR_CAN_ERRALARM) != 0) + errMsgList.Add("CAN控制器错误报警"); + if ((errInfo.ErrCode & (uint)CAN_API.ErrorType.ERR_CAN_PASSIVE) != 0) + errMsgList.Add("CAN控制器消极错误"); + if ((errInfo.ErrCode & (uint)CAN_API.ErrorType.ERR_CAN_LOSE) != 0) + errMsgList.Add("CAN控制器仲裁丢失"); + if ((errInfo.ErrCode & (uint)CAN_API.ErrorType.ERR_CAN_BUSERR) != 0) + errMsgList.Add("CAN控制器总线错误"); + if ((errInfo.ErrCode & (uint)CAN_API.ErrorType.ERR_DEVICEOPENED) != 0) + errMsgList.Add("设备已经打开"); + if ((errInfo.ErrCode & (uint)CAN_API.ErrorType.ERR_DEVICEOPEN) != 0) + errMsgList.Add("打开设备错误"); + if ((errInfo.ErrCode & (uint)CAN_API.ErrorType.ERR_DEVICENOTOPEN) != 0) + errMsgList.Add("设备没有打开"); + if ((errInfo.ErrCode & (uint)CAN_API.ErrorType.ERR_BUFFEROVERFLOW) != 0) + errMsgList.Add("缓冲区溢出"); + if ((errInfo.ErrCode & (uint)CAN_API.ErrorType.ERR_DEVICENOTEXIST) != 0) + errMsgList.Add("此设备不存在"); + if ((errInfo.ErrCode & (uint)CAN_API.ErrorType.ERR_LOADKERNELDLL) != 0) + errMsgList.Add("装载动态库失败"); + if ((errInfo.ErrCode & (uint)CAN_API.ErrorType.ERR_CMDFAILED) != 0) + errMsgList.Add("执行命令失败"); + if ((errInfo.ErrCode & (uint)CAN_API.ErrorType.ERR_BUFFERCREATE) != 0) + errMsgList.Add("内存不足"); + + if (errMsgList.Count == 0) + { + // 若未检测到错误信息,则返回‘未知错误’ + return "未知错误"; + } + else + { + // 否则将错误信息以'|'拼接返回 + return string.Join("|", errMsgList); + } + } + + /// + /// 向CAN发送数据帧 + /// + /// 发送帧ID + /// 数据数组(数组长度必须为8) + public void SendData(uint frameID, byte[] data) + { + CAN_API.VCI_CAN_OBJ frameInfo = new CAN_API.VCI_CAN_OBJ + { + ID = frameID, // 帧ID + SendType = 0, // 正常发送 + RemoteFlag = 0, // 非远程帧 + ExternFlag = 0, // 非扩展帧 + DataLen = 8, // 数据长度 + Data = data, // 数据 + Reserved = new byte[3] // 预留 + }; + SendData(frameInfo); + } + + /// + /// 向CAN发送数据帧 + /// + /// 帧信息 + public void SendData(CAN_API.VCI_CAN_OBJ frameInfo) + { + // 发送一帧数据 + if (CAN_API.VCI_Transmit(_DeviceType, _DeviceInd, _CANInd, ref frameInfo, 1) == CAN_API.STATUS_ERR) + throw new Exception("数据发送失败,错误信息:" + ReadErrorMessage()); + } + + #endregion + + #region 生产者消费者模式 - 生产数据帧,发出事件消费数据帧 + /// + /// 帧缓冲区(生产者消费者队列) + /// + private BlockingCollection _FrameBuffer; + /// + /// 生产者线程 + /// + private Thread _ProducerThread; + /// + /// 消费者线程 + /// + private Thread _ConsumerThread; + /// + /// 消费帧事件委托 + /// + /// 报文帧 + public delegate void ConsumptionFrameEventHandler(CAN_API.VCI_CAN_OBJ frame); + /// + /// 消费帧事件 + /// + public event ConsumptionFrameEventHandler ConsumptionFrameEvent; + + /// + /// 开始工作线程 + /// + private void StartWork() + { + // 启动生产者与消费者线程 + // 优先级设置为高 + + _ProducerThread = new Thread(new ThreadStart(Producer)) + { + IsBackground = true, + Priority = ThreadPriority.Highest + }; + _ConsumerThread = new Thread(new ThreadStart(Consumer)) + { + IsBackground = true, + Priority = ThreadPriority.Highest + }; + _ProducerThread.Start(); + _ConsumerThread.Start(); + } + + /// + /// 生产者 + /// + private void Producer() + { + // 分配一个缓冲区,最大能同时容纳100帧数据 + IntPtr readBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(CAN_API.VCI_CAN_OBJ)) * 100); ; + while (IsOpen) + { + // var n = CAN_API.VCI_GetReceiveNum(_DeviceType, _DeviceInd, _CANInd); + // 接收数据,超时时间100ms,最大接收100个 + uint len = CAN_API.VCI_Receive(_DeviceType, _DeviceInd, _CANInd, readBuffer, 100, 100); + + if (len == 0) + { + // 如果未读到数据,读取错误信息 + ReadErrorMessage(); + } + else + { + // 将读取到的每一帧数据构造帧对象VCI_CAN_OBJ,装入帧缓冲区中(生产产品到库存) + for (int i = 0; i < len; i++) + { + // 实例化帧对象,装入帧缓冲区 + _FrameBuffer.Add((CAN_API.VCI_CAN_OBJ)Marshal.PtrToStructure((IntPtr)((uint)readBuffer + i * Marshal.SizeOf(typeof(CAN_API.VCI_CAN_OBJ))), typeof(CAN_API.VCI_CAN_OBJ))); + } + } + } + } + + /// + /// 消费者 + /// + private void Consumer() + { + while (IsOpen) + { + // 从队列中获取帧(该方法会线程安全的阻塞,当有数据装入时立刻返回) + var frame = _FrameBuffer.Take(); + // 消费帧 + if (ConsumptionFrameEvent != null) + ConsumptionFrameEvent(frame); + } + } + #endregion + + #region 私有成员 + /// + /// 设备类型 + /// + private const uint _DeviceType = (uint)CAN_API.PCIDeviceType.VCI_USBCAN1; + /// + /// 设备ID + /// + private const uint _DeviceInd = 0; + /// + /// 第几路CAN + /// + private const uint _CANInd = 0; + #endregion + } +} diff --git a/CANHelper/USBCAN/CAN_API.cs b/CANHelper/USBCAN/CAN_API.cs new file mode 100644 index 0000000..a111c98 --- /dev/null +++ b/CANHelper/USBCAN/CAN_API.cs @@ -0,0 +1,262 @@ +using System; +using System.Runtime.InteropServices; + + +namespace USBCAN +{ + public sealed class CAN_API + { + #region 数据类型定义 + /// + /// 接口卡类型定义 + /// + public enum PCIDeviceType + { + VCI_PCI5121 = 1, + VCI_PCI9810 = 2, + VCI_USBCAN1 = 3, + VCI_USBCAN2 = 4, + VCI_PCI9820 = 5, + VCI_CAN232 = 6, + VCI_PCI5110 = 7, + VCI_CANLITE = 8, + VCI_ISA9620 = 9, + VCI_ISA5420 = 10, + VCI_PC104CAN = 11, + VCI_CANETE = 12, + VCI_DNP9810 = 13, + VCI_PCI9840 = 14, + VCI_PCI9820I = 16 + } + + //函数调用返回状态值 + /// + /// 正常状态 + /// + public static readonly int STATUS_OK = 1; + /// + /// 发生错误 + /// + public static readonly int STATUS_ERR = 0; + + /// + /// 错误类型 + /// + public enum ErrorType + { + // --------------- CAN错误码 ------------------- + /// + /// CAN错误码:CAN控制器内部FIFO溢出 + /// + ERR_CAN_OVERFLOW = 0x0001, + /// + /// CAN错误码:CAN控制器错误报警 + /// + ERR_CAN_ERRALARM = 0x0002, + /// + /// CAN错误码:CAN控制器消极错误 + /// + ERR_CAN_PASSIVE = 0x0004, + /// + /// CAN错误码:CAN控制器仲裁丢失 + /// + ERR_CAN_LOSE = 0x0008, + /// + /// CAN错误码:CAN控制器总线错误 + /// + ERR_CAN_BUSERR = 0x0010, + + // --------------- 通用错误码 ------------------- + /// + /// 通用错误码:设备已经打开 + /// + ERR_DEVICEOPENED = 0x0100, + /// + /// 通用错误码:打开设备错误 + /// + ERR_DEVICEOPEN = 0x0200, + /// + /// 通用错误码:设备没有打开 + /// + ERR_DEVICENOTOPEN = 0x0400, + /// + /// 通用错误码:缓冲区溢出 + /// + ERR_BUFFEROVERFLOW = 0x0800, + /// + /// 通用错误码:此设备不存在 + /// + ERR_DEVICENOTEXIST = 0x1000, + /// + /// 通用错误码:装载动态库失败 + /// + ERR_LOADKERNELDLL = 0x2000, + /// + /// 通用错误码:执行命令失败错误码 + /// + ERR_CMDFAILED = 0x4000, + /// + /// 通用错误码:内存不足 + /// + ERR_BUFFERCREATE = 0x8000 + } + + /// + /// ZLGCAN系列接口卡信息 + /// + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] + public struct VCI_BOARD_INFO + { + public ushort hw_Version; + public ushort fw_Version; + public ushort dr_Version; + public ushort in_Version; + public ushort irq_Num; + public byte can_Num; + [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst = 20)] + public string str_Serial_Num; + [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst = 40)] + public string str_hw_Type; + [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst = 4, ArraySubType = System.Runtime.InteropServices.UnmanagedType.U2)] + public ushort[] Reserved; + } + + /// + /// CAN信息帧 + /// + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] + public struct VCI_CAN_OBJ + { + public uint ID; + public uint TimeStamp; + public byte TimeFlag; + public byte SendType; + public byte RemoteFlag;//是否是远程帧 + public byte ExternFlag;//是否是扩展帧 + public byte DataLen; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8, ArraySubType = UnmanagedType.I1)] + public byte[] Data; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3, ArraySubType = UnmanagedType.I1)] + public byte[] Reserved; + } + + /// + /// CAN控制器状态 + /// + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] + public struct VCI_CAN_STATUS + { + public byte ErrInterrupt; + public byte regMode; + public byte regStatus; + public byte regALCapture; + public byte regECCapture; + public byte regEWLimit; + public byte regRECounter; + public byte regTECounter; + public uint Reserved; + } + + /// + /// 错误信息 + /// + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] + public struct VCI_ERR_INFO + { + public uint ErrCode; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3, ArraySubType = UnmanagedType.I1)] + public byte[] Passive_ErrData; + + public byte ArLost_ErrData; + } + + /// + /// 初始化CAN的配置信息 + /// + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] + public struct VCI_INIT_CONFIG + { + /// + /// 验收码 + /// + public uint AccCode; + /// + /// 屏蔽码 + /// + public uint AccMask; + /// + /// 预留,填0 + /// + public uint Reserved; + /// + /// 滤波方式 + /// + public byte Filter; + public byte Timing0; + public byte Timing1; + /// + /// 模式:0:正常|1:只听 + /// + public byte Mode; + } + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] + public struct CHGDESIPANDPORT + { + [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst = 10)] + public string szpwd; + + [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst = 20)] + public string szdesip; + + public int desport; + } + #endregion + + #region API函数 + + [DllImport("ControlCAN.dll", SetLastError = true, EntryPoint = "VCI_OpenDevice", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] + public static extern uint VCI_OpenDevice(uint DeviceType, uint DeviceInd, uint Reserved); + + [DllImport("ControlCAN.dll", SetLastError = true, EntryPoint = "VCI_CloseDevice", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] + public static extern uint VCI_CloseDevice(uint DeviceType, uint DeviceInd); + + [DllImport("ControlCAN.dll", SetLastError = true, EntryPoint = "VCI_ResetCAN", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] + public static extern uint VCI_ResetCAN(uint DeviceType, uint DeviceInd, uint CANInd); + + [DllImport("ControlCAN.dll", SetLastError = true, EntryPoint = "VCI_InitCAN", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] + public static extern uint VCI_InitCAN(uint DeviceType, uint DeviceInd, uint CANInd, ref VCI_INIT_CONFIG pInitConfig); + + [DllImport("ControlCAN.dll", SetLastError = true, EntryPoint = "VCI_Transmit", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] + public static extern uint VCI_Transmit(uint DeviceType, uint DeviceInd, uint CANInd, ref VCI_CAN_OBJ pSend, uint Len); + + [DllImport("ControlCAN.dll", SetLastError = true, EntryPoint = "VCI_Receive", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] + public static extern uint VCI_Receive(uint DeviceType, uint DeviceInd, uint CANInd, IntPtr pReceive, uint Len, int WaitTime); + + [DllImport("ControlCAN.dll", SetLastError = true, EntryPoint = "VCI_GetReceiveNum", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] + public static extern uint VCI_GetReceiveNum(uint DeviceType, uint DeviceInd, uint CANInd); + + [DllImport("ControlCAN.dll", SetLastError = true, EntryPoint = "VCI_ClearBuffer", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] + public static extern uint VCI_ClearBuffer(uint DeviceType, uint DeviceInd, uint CANInd); + + [DllImport("ControlCAN.dll", SetLastError = true, EntryPoint = "VCI_ReadErrInfo", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] + public static extern uint VCI_ReadErrInfo(uint DeviceType, uint DeviceInd, uint CANInd, ref VCI_ERR_INFO pErrInfo); + + [DllImport("ControlCAN.dll", SetLastError = true, EntryPoint = "VCI_StartCAN", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] + public static extern uint VCI_StartCAN(uint DeviceType, uint DeviceInd, uint CANInd); + + [DllImport("ControlCAN.dll", SetLastError = true, EntryPoint = "VCI_SetReference", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] + public static extern uint VCI_SetReference(uint DeviceType, uint DeviceInd, uint CANInd, uint RefType, object pData); + + [DllImport("ControlCAN.dll", SetLastError = true, EntryPoint = "VCI_GetReference", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] + public static extern uint VCI_GetReference(uint DeviceType, uint DeviceInd, uint CANInd, uint RefType, object pData); + + [DllImport("ControlCAN.dll", SetLastError = true, EntryPoint = "VCI_ReadCANStatus", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] + public static extern uint VCI_ReadCANStatus(uint DeviceType, uint DeviceInd, uint CANInd, ref VCI_CAN_STATUS pCANStatus); + + [DllImport("ControlCAN.dll", SetLastError = true, EntryPoint = "VCI_ReadBoardInfo", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] + public static extern uint VCI_ReadBoardInfo(uint DeviceType, uint DeviceInd, ref VCI_BOARD_INFO pInfo); + + #endregion + } +} diff --git a/CANHelper/kerneldlls/kerneldll.ini b/CANHelper/kerneldlls/kerneldll.ini new file mode 100644 index 0000000..ed6204d --- /dev/null +++ b/CANHelper/kerneldlls/kerneldll.ini @@ -0,0 +1,28 @@ +[KERNELDLL] +COUNT=26 +1=PCI5121.dll +2=PCI9810B.dll +3=USBCAN.dll +4=USBCAN.dll +5=PCI9820B.dll +6=CAN232.dll +7=PCI5121.dll +8=CANLite.dll +9=ISA9620B.dll +10=ISA5420.dll +11=PC104CAN.dll +12=CANETE.dll +13=DNP9810B.dll +14=PCI9840B.dll +15=PC104C2.dll +16=PCI9820I.dll +17=CANET_TCP.dll +18=pec9920.dll +19=pci50xx_u.dll +20=USBCAN_E.dll +21=USBCAN_E.dll +22=pci50xx_u.dll +23=topcliff_can.dll +24=pcie9221.dll +25=CANWIFI_TCP.dll +26=CANWIFI_UDP.dll diff --git a/CANHelper/kerneldlls/usbcan.dll b/CANHelper/kerneldlls/usbcan.dll new file mode 100644 index 0000000..0d5e36a Binary files /dev/null and b/CANHelper/kerneldlls/usbcan.dll differ