Add USB CAN Helper files

This commit is contained in:
筱傑 2019-05-06 15:38:11 +08:00 committed by GitHub
parent 3107ef88dc
commit 0e551a9285
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 636 additions and 0 deletions

BIN
CANHelper/ControlCAN.dll Normal file

Binary file not shown.

View File

@ -0,0 +1,346 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Threading;
namespace USBCAN
{
/// <summary>
/// CAN帮助类
/// </summary>
/// <example>
/// 使用方法:
/// <code>
/// CANHelper.Instance.Initialize(); // 在首次使用时需要初始化,初始化失败时会抛出异常
/// CANHelper.Instance.ConsumptionFrameEvent += (CAN_API.VCI_CAN_OBJ frame){ /* TODO:在这里消费掉数据帧,注意,这里是异步调用 */ }
/// CANHelper.Instance.SendData(0x1111, data); // 向CAN发送数据具体方法参阅注释
/// </code>
/// </example>
public sealed class CANHelper : IDisposable
{
/// <summary>
/// CAN实例
/// </summary>
public static readonly CANHelper Instance = new CANHelper();
#region
/// <summary>
/// CAN是否打开
/// </summary>
public bool IsOpen { private set; get; } = false;
#endregion
#region
/// <summary>
/// Flag: 标识Disposed是否已经被调用
/// </summary>
private bool _IsDisposed = false;
/// <summary>
/// 公开的Dispose方法
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// 实际Dispose方法
/// </summary>
/// <param name="disposing">是否由用户调用</param>
private void Dispose(bool disposing)
{
if (_IsDisposed)
return;
if (disposing)
{
// 释放托管成员
}
// 释放非托管成员
// 关闭CAN设备
CloseDevice();
// 标识为已执行Dispose
_IsDisposed = true;
}
/// <summary>
/// 析构
/// </summary>
~CANHelper()
{
// 释放非托管内存
Dispose(false);
}
#endregion
#region
/// <summary>
/// 初始化并打开CAN设备
/// </summary>
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<CAN_API.VCI_CAN_OBJ>(128);
// 生产者消费者开始工作
StartWork();
}
/// <summary>
/// 关闭CAN设备
/// </summary>
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
/// <summary>
/// 读取错误信息
/// </summary>
/// <returns></returns>
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<string> errMsgList = new List<string>();
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);
}
}
/// <summary>
/// 向CAN发送数据帧
/// </summary>
/// <param name="frameID">发送帧ID</param>
/// <param name="data">数据数组数组长度必须为8</param>
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);
}
/// <summary>
/// 向CAN发送数据帧
/// </summary>
/// <param name="frameInfo">帧信息</param>
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 -
/// <summary>
/// 帧缓冲区(生产者消费者队列)
/// </summary>
private BlockingCollection<CAN_API.VCI_CAN_OBJ> _FrameBuffer;
/// <summary>
/// 生产者线程
/// </summary>
private Thread _ProducerThread;
/// <summary>
/// 消费者线程
/// </summary>
private Thread _ConsumerThread;
/// <summary>
/// 消费帧事件委托
/// </summary>
/// <param name="frame">报文帧</param>
public delegate void ConsumptionFrameEventHandler(CAN_API.VCI_CAN_OBJ frame);
/// <summary>
/// 消费帧事件
/// </summary>
public event ConsumptionFrameEventHandler ConsumptionFrameEvent;
/// <summary>
/// 开始工作线程
/// </summary>
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();
}
/// <summary>
/// 生产者
/// </summary>
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)));
}
}
}
}
/// <summary>
/// 消费者
/// </summary>
private void Consumer()
{
while (IsOpen)
{
// 从队列中获取帧(该方法会线程安全的阻塞,当有数据装入时立刻返回)
var frame = _FrameBuffer.Take();
// 消费帧
if (ConsumptionFrameEvent != null)
ConsumptionFrameEvent(frame);
}
}
#endregion
#region
/// <summary>
/// 设备类型
/// </summary>
private const uint _DeviceType = (uint)CAN_API.PCIDeviceType.VCI_USBCAN1;
/// <summary>
/// 设备ID
/// </summary>
private const uint _DeviceInd = 0;
/// <summary>
/// 第几路CAN
/// </summary>
private const uint _CANInd = 0;
#endregion
}
}

262
CANHelper/USBCAN/CAN_API.cs Normal file
View File

@ -0,0 +1,262 @@
using System;
using System.Runtime.InteropServices;
namespace USBCAN
{
public sealed class CAN_API
{
#region
/// <summary>
/// 接口卡类型定义
/// </summary>
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
}
//函数调用返回状态值
/// <summary>
/// 正常状态
/// </summary>
public static readonly int STATUS_OK = 1;
/// <summary>
/// 发生错误
/// </summary>
public static readonly int STATUS_ERR = 0;
/// <summary>
/// 错误类型
/// </summary>
public enum ErrorType
{
// --------------- CAN错误码 -------------------
/// <summary>
/// CAN错误码:CAN控制器内部FIFO溢出
/// </summary>
ERR_CAN_OVERFLOW = 0x0001,
/// <summary>
/// CAN错误码:CAN控制器错误报警
/// </summary>
ERR_CAN_ERRALARM = 0x0002,
/// <summary>
/// CAN错误码:CAN控制器消极错误
/// </summary>
ERR_CAN_PASSIVE = 0x0004,
/// <summary>
/// CAN错误码:CAN控制器仲裁丢失
/// </summary>
ERR_CAN_LOSE = 0x0008,
/// <summary>
/// CAN错误码:CAN控制器总线错误
/// </summary>
ERR_CAN_BUSERR = 0x0010,
// --------------- 通用错误码 -------------------
/// <summary>
/// 通用错误码:设备已经打开
/// </summary>
ERR_DEVICEOPENED = 0x0100,
/// <summary>
/// 通用错误码:打开设备错误
/// </summary>
ERR_DEVICEOPEN = 0x0200,
/// <summary>
/// 通用错误码:设备没有打开
/// </summary>
ERR_DEVICENOTOPEN = 0x0400,
/// <summary>
/// 通用错误码:缓冲区溢出
/// </summary>
ERR_BUFFEROVERFLOW = 0x0800,
/// <summary>
/// 通用错误码:此设备不存在
/// </summary>
ERR_DEVICENOTEXIST = 0x1000,
/// <summary>
/// 通用错误码:装载动态库失败
/// </summary>
ERR_LOADKERNELDLL = 0x2000,
/// <summary>
/// 通用错误码:执行命令失败错误码
/// </summary>
ERR_CMDFAILED = 0x4000,
/// <summary>
/// 通用错误码:内存不足
/// </summary>
ERR_BUFFERCREATE = 0x8000
}
/// <summary>
/// ZLGCAN系列接口卡信息
/// </summary>
[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;
}
/// <summary>
/// CAN信息帧
/// </summary>
[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;
}
/// <summary>
/// CAN控制器状态
/// </summary>
[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;
}
/// <summary>
/// 错误信息
/// </summary>
[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;
}
/// <summary>
/// 初始化CAN的配置信息
/// </summary>
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct VCI_INIT_CONFIG
{
/// <summary>
/// 验收码
/// </summary>
public uint AccCode;
/// <summary>
/// 屏蔽码
/// </summary>
public uint AccMask;
/// <summary>
/// 预留填0
/// </summary>
public uint Reserved;
/// <summary>
/// 滤波方式
/// </summary>
public byte Filter;
public byte Timing0;
public byte Timing1;
/// <summary>
/// 模式0:正常|1:只听
/// </summary>
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
}
}

View File

@ -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

Binary file not shown.