using System; using System.Collections.Generic; using System.Drawing; using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Security.Cryptography.X509Certificates; using System.Text; using System.Threading; using ZLGAPI; namespace USBCANFD { internal class Program { public static IntPtr devHandle = IntPtr.Zero; // 设备句柄 public static IntPtr[] chnHandle = new IntPtr[10]; // 通道句柄 public static int chn_max = 2; // 最大通道数量 public static volatile bool isRunning = true; // 线程标志 public static uint isMerge = 0; // 合并接收标志 public static uint isBusload = 0; // 通道利用率使能 // 线程传递参数 class ThreadParams { public IntPtr dev { get; set; } // 设备句柄 public IntPtr chn { get; set; } // 通道句柄 public int chnNum { get; set; } // 通道号 } class ThreadBusload { public IntPtr dev { get; set; } // 设备句柄 public int chnNum { get; set; } // 通道号 } static void Main(string[] args) { uint ret; // 返回值 // 打开设备 方式一:通过索引顺序打开设备 Program.devHandle = ZLGCAN.ZCAN_OpenDevice(ZLGCAN.ZCAN_USBCANFD_200U, 0, 0); if (Program.devHandle == IntPtr.Zero) { Console.WriteLine("打开设备失败"); Console.ReadKey(); return; } else Console.WriteLine("打开设备成功"); // 打开设备 方式二:通过自定义序列号打开设备,需要首先执行SN_test函数设置自定义序列号 //Program.devHandle = ZLGCAN.ZCAN_OpenDeviceByName(ZLGCAN.ZCAN_USBCANFD_200U, "C#01"); //if (Program.devHandle == IntPtr.Zero) //{ // Console.WriteLine("打开设备失败"); // Console.ReadKey(); // return; //} //else // Console.WriteLine("打开设备成功"); //// 获取设备信息 //ZLGCAN.ZCAN_DEVICE_INFO info = new ZLGCAN.ZCAN_DEVICE_INFO(); //IntPtr P2Info = Marshal.AllocHGlobal(Marshal.SizeOf(info)); //if (1 == ZLGCAN.ZCAN_GetDeviceInf(Program.devHandle, P2Info)) //{ // info = (ZLGCAN.ZCAN_DEVICE_INFO)Marshal.PtrToStructure(P2Info, typeof(ZLGCAN.ZCAN_DEVICE_INFO)); // Console.WriteLine("设备型号 {0},设备固件版本 {1:X}", new string(info.str_hw_Type).TrimEnd('\0'), info.fw_Version); //} //// 自定义序列号 //SN_test(Program.devHandle); // 该函数用于检测设备是否在线 //uint status = ZLGCAN.ZCAN_IsDeviceOnLine(Program.devHandle); //Console.WriteLine("设备状态{0}", // status == 0 ? "ERR" : // status == 1 ? "OK" : // status == 2 ? "ONLINE" : // status == 3 ? "OFFLINE" : // status == 4 ? "UNSUPPORTED" : // status == 5 ? "BUFFER_TOO_SMALL" : "UNKNOW"); // 初始化并打开通道 for (int i = 0; i < Program.chn_max; i++) { Program.chnHandle[i] = Init_chn_USBCANFD(Program.devHandle, i); // 必须初始化完一个通道,再走另外一个通道!!! if (Program.chnHandle[i] == IntPtr.Zero) return; } // 接收线程处理 if (Program.isMerge == 1) // 合并接收 { // 设置合并接收 ret = ZLGCAN.ZCAN_SetValue(Program.devHandle, "0/set_device_recv_merge", "1"); if (ret != 1) { Console.WriteLine("设置合并接收失败"); Console.ReadKey(); } ThreadParams parameters = new ThreadParams { dev = Program.devHandle, chnNum = 0 }; Thread workerThread = new Thread(ReceiveThread_Merge); workerThread.IsBackground = true; // 设置为后台线程,不需要join workerThread.Start(parameters); // 启动线程 } else // 普通接收 { // 关闭合并接收 ret = ZLGCAN.ZCAN_SetValue(Program.devHandle, "0/set_device_recv_merge", "0"); if (ret != 1) { Console.WriteLine("关闭合并接收失败"); Console.ReadKey(); } for (int i = 0; i < Program.chn_max; i++) { ThreadParams parameters = new ThreadParams { chn = Program.chnHandle[i], chnNum = i }; Thread workerThread = new Thread(ReceiveThread); // 每通道独立线程接收 workerThread.IsBackground = true; // 设置为后台线程,不需要join workerThread.Start(parameters); // 启动线程 } } if (Program.isBusload == 1) // 通道利用率 { ThreadBusload Busload = new ThreadBusload { dev = Program.devHandle, chnNum = 0 }; Thread ThreadBusload = new Thread(Thread_Busload); ThreadBusload.IsBackground = true; // 设置为后台线程,不需要join ThreadBusload.Start(Busload); // 启动线程 } // 发送示例 Send_test(Program.chnHandle[0]); //Send_test_Merge(Program.devHandle); //// 定时发送 //AutoSend_test(Program.devHandle, 0); //// 队列发送 //QueueSend_test(Program.devHandle, Program.chnHandle[0]); // 阻塞等待 Console.ReadKey(); Program.isRunning = false; // 关闭通道 for (int i = 0; i < Program.chn_max; i++) { ret = ZLGCAN.ZCAN_ResetCAN(Program.chnHandle[i]); if (ret != 1) Console.WriteLine("关闭通道{0}失败", i); } // 关闭设备 ret = ZLGCAN.ZCAN_CloseDevice(Program.devHandle); if (ret != 1) { Console.WriteLine("关闭设备失败"); Console.ReadKey(); return; } } // 初始化CAN通道 static IntPtr Init_chn_USBCANFD(IntPtr dev, int chn_idx) { IntPtr temp = IntPtr.Zero; uint ret; // 返回值 // 仲裁域波特率 string path = String.Format("{0}/canfd_abit_baud_rate", chn_idx); ret = ZLGCAN.ZCAN_SetValue(dev, path, "500000"); // 500k if (ret != 1) { Console.WriteLine("设置仲裁域波特率失败"); Console.ReadKey(); return IntPtr.Zero; } // 数据域波特率 path = String.Format("{0}/canfd_dbit_baud_rate", chn_idx); ret = ZLGCAN.ZCAN_SetValue(dev, path, "2000000"); // 2M if (ret != 1) { Console.WriteLine("设置数据域波特率失败"); Console.ReadKey(); return IntPtr.Zero; } // 终端电阻 path = String.Format("{0}/initenal_resistance", chn_idx); string resistance = "1";//1-使能 0-禁能 ret = ZLGCAN.ZCAN_SetValue(dev, path, resistance); if (ret != 1) { Console.WriteLine("设置终端电阻失败"); Console.ReadKey(); return IntPtr.Zero; } // 初始化通道 ZLGCAN.ZCAN_CHANNEL_INIT_CONFIG InitConfig = new ZLGCAN.ZCAN_CHANNEL_INIT_CONFIG(); // 结构体 InitConfig.can_type = 1; // 1 - CANFD (USBCANFD 只能选择CANFD模式) InitConfig.config.canfd.mode = 0; // 0 - 正常模式,1 - 只听模式 IntPtr P2InitConfig = Marshal.AllocHGlobal(Marshal.SizeOf(InitConfig)); Marshal.StructureToPtr(InitConfig, P2InitConfig, true); // 转成指针 temp = ZLGCAN.ZCAN_InitCAN(dev, (uint)chn_idx, P2InitConfig); Marshal.FreeHGlobal(P2InitConfig); // 释放内存 if (temp == IntPtr.Zero) { Console.WriteLine("初始化通道失败"); Console.ReadKey(); return IntPtr.Zero; } Console.WriteLine("初始化通道成功"); //// 设置滤波 //value = "0"; // 0-标准帧 1-扩展帧 // path = String.Format("{0}/filter_mode", chn_idx); // ret = ZLGCAN.ZCAN_SetValue(deviceHandle, path, value); // value = "0x731"; // 起始 ID // path = String.Format("{0}/filter_start", chn_idx); // ret = ZLGCAN.ZCAN_SetValue(deviceHandle, path, value); // value = "0x7DF"; // 结束 ID // path = String.Format("{0}/filter_end", chn_idx); // ret = ZLGCAN.ZCAN_SetValue(deviceHandle, path, value); // value = "0"; // 使能滤波 // path = String.Format("{0}/filter_ack", chn_idx); // ret = ZLGCAN.ZCAN_SetValue(deviceHandle, path, value); // if (ret != 1) // { // Console.WriteLine("使能滤波失败"); // Console.ReadKey(); // return IntPtr.Zero; // } if (Program.isBusload == 1) // 通道利用率 { // 通道利用率上报 path = String.Format("{0}/set_bus_usage_enable", chn_idx); ret = ZLGCAN.ZCAN_SetValue(dev, path, "1"); // 总线利用率上报周期 path = String.Format("{0}/set_bus_usage_period", chn_idx); ret = ZLGCAN.ZCAN_SetValue(dev, path, "500"); } //打开通道 uint Ret = ZLGCAN.ZCAN_StartCAN(temp); if (Ret != 1) { Console.WriteLine("打开通道失败"); Console.ReadKey(); return IntPtr.Zero; } Console.WriteLine("打开通道成功"); return temp; } // 总线利用率 static void Thread_Busload(object obj) { ThreadBusload parameters = (ThreadBusload)obj; while (Program.isRunning) { IntPtr pUse = ZLGCAN.ZCAN_GetValue(Program.devHandle, parameters.chnNum +"/get_bus_usage/1" ); if (pUse != IntPtr.Zero) { ZLGCAN.BusUsage busUsage = (ZLGCAN.BusUsage)Marshal.PtrToStructure(pUse, typeof(ZLGCAN.BusUsage)); Console.WriteLine("总线利用率: {0}%,{1}帧/秒", busUsage.nBusUsage / 100, busUsage.nFrameCount); } Thread.Sleep(500); } } // 接收线程 static void ReceiveThread(object obj) { ThreadParams parameters = obj as ThreadParams; if (parameters == null) return; // 配置缓冲区大小 const int BUFFER_SIZE = 100; // 预计算结构体大小 int canStructSize = Marshal.SizeOf(typeof(ZLGCAN.ZCAN_Receive_Data)); int canfdStructSize = Marshal.SizeOf(typeof(ZLGCAN.ZCAN_ReceiveFD_Data)); // 分配非托管内存(循环外一次分配,复用避免频繁申请释放) IntPtr pCanBuffer = Marshal.AllocHGlobal(canStructSize * BUFFER_SIZE); // CAN接收缓冲区 IntPtr pCanfdBuffer = Marshal.AllocHGlobal(canfdStructSize * BUFFER_SIZE); // CANFD接收缓冲区 try { while (Program.isRunning) { uint recNum = ZLGCAN.ZCAN_GetReceiveNum(parameters.chn, 0); // 0-CAN if (recNum > 0) { // 限制接收数量不超过缓冲区大小(双重保险) uint actualReceive = ZLGCAN.ZCAN_Receive( parameters.chn, pCanBuffer, // 传入非托管缓冲区指针(C风格) BUFFER_SIZE, 10 ); if (actualReceive > 0) { for (int i = 0; i < actualReceive; i++) { // 计算当前CAN数据的指针 IntPtr pCurrentCan = (IntPtr)(pCanBuffer.ToInt64() + i * canStructSize); // 非托管内存转换为C#结构体 ZLGCAN.ZCAN_Receive_Data recData = (ZLGCAN.ZCAN_Receive_Data)Marshal.PtrToStructure( pCurrentCan, typeof(ZLGCAN.ZCAN_Receive_Data) ); // 可选:ID过滤 // uint canId = recData.frame.can_id & 0x1FFFFFFF; // if (canId != 0x731 && canId != 0x7B1) // continue; // 通道号和时间戳 Console.Write(String.Format("CHN:{0} [{1}] CAN ", parameters.chnNum, recData.timestamp)); // 帧类型(标准/扩展) Console.Write((recData.frame.can_id & (1 << 31)) != 0 ? "扩展帧 " : "标准帧 "); // 方向(Tx/Rx) Console.Write((recData.frame.__pad & 0x20) == 0x20 ? "Tx " : "Rx "); // 数据长度校验 int dataLen = Math.Min(recData.frame.can_dlc, recData.frame.data != null ? recData.frame.data.Length : 0); // ID和数据(保留原格式) Console.WriteLine(String.Format("ID:{0:X} {1}", recData.frame.can_id & 0x1FFFFFFF, Program.b_array2string(recData.frame.data, dataLen) )); } } } recNum = ZLGCAN.ZCAN_GetReceiveNum(parameters.chn, 1); // 1-CANFD if (recNum > 0) { uint actualReceive = ZLGCAN.ZCAN_ReceiveFD( parameters.chn, pCanfdBuffer, // CANFD非托管缓冲区指针 BUFFER_SIZE, 10 ); if (actualReceive > 0) { for (int i = 0; i < actualReceive; i++) { // 计算当前CANFD数据的指针 IntPtr pCurrentCanfd = (IntPtr)(pCanfdBuffer.ToInt64() + i * canfdStructSize); // 非托管内存转换为C#结构体 ZLGCAN.ZCAN_ReceiveFD_Data recFDData = (ZLGCAN.ZCAN_ReceiveFD_Data)Marshal.PtrToStructure( pCurrentCanfd, typeof(ZLGCAN.ZCAN_ReceiveFD_Data) ); // 通道号和时间戳 Console.Write(String.Format("CHN:{0} [{1}] CANFD", parameters.chnNum, recFDData.timestamp)); // CANFD加速标志 if ((recFDData.frame.flags & 0x01) == 0x01) Console.Write("加速"); // 帧类型(标准/扩展) Console.Write((recFDData.frame.can_id & (1 << 31)) != 0 ? " 扩展帧" : " 标准帧"); // 方向(Tx/Rx) Console.Write((recFDData.frame.flags & 0x20) == 0x20 ? " Tx " : " Rx "); // 数据长度校验 int dataLen = Math.Min(recFDData.frame.len, recFDData.frame.data != null ? recFDData.frame.data.Length : 0); // ID和数据 Console.WriteLine(String.Format("ID:{0:X} {1}", recFDData.frame.can_id & 0x1FFFFFFF, Program.b_array2string(recFDData.frame.data, dataLen) )); } } } ZLGAPI.ZLGCAN.ZCAN_CHANNEL_ERR_INFO errData = new ZLGAPI.ZLGCAN.ZCAN_CHANNEL_ERR_INFO{ error_code = 0, passive_ErrData = new byte[3], arLost_ErrData = 0 }; uint ret = ZLGAPI.ZLGCAN.ZCAN_ReadChannelErrInfo(parameters.chn, ref errData); Analyze_err_frame(errData); Thread.Sleep(10); } } finally { // 确保非托管内存必释放 Marshal.FreeHGlobal(pCanfdBuffer); Marshal.FreeHGlobal(pCanBuffer); } } // 接收线程(合并接收) static void ReceiveThread_Merge(object obj) { ThreadParams parameters = (ThreadParams)obj; // 预计算结构体大小 int dataObjSize = Marshal.SizeOf(typeof(ZLGCAN.ZCANDataObj)); int canfdSize = Marshal.SizeOf(typeof(ZLGCAN.ZCANCANFDData)); int linSize = Marshal.SizeOf(typeof(ZLGCAN.ZCANLINData)); // 分配非托管内存(循环外分配,复用避免频繁申请释放) IntPtr pDataObjs = Marshal.AllocHGlobal(dataObjSize * 100); // 写死100个最大,可以面对绝大部分情况,如果是动态长度容易崩溃 IntPtr pCanfdBuffer = Marshal.AllocHGlobal(canfdSize); IntPtr pLinBuffer = Marshal.AllocHGlobal(linSize); try { while (Program.isRunning) { // 获取待接收数据量,无数据则休眠后继续 uint recvNum = ZLGCAN.ZCAN_GetReceiveNum(parameters.dev, 2); if (recvNum == 0) { Thread.Sleep(10); continue; } // 接收数据 uint actualRecv = ZLGCAN.ZCAN_ReceiveData(parameters.dev, pDataObjs, 100, 10); if (actualRecv == 0) { continue; } // 遍历处理每个接收的数据 for (int i = 0; i < actualRecv; i++) { // 计算当前数据对象指针 IntPtr pCurrentData = (IntPtr)(pDataObjs.ToInt64() + i * dataObjSize); ZLGCAN.ZCANDataObj dataObj = (ZLGCAN.ZCANDataObj)Marshal.PtrToStructure(pCurrentData, typeof(ZLGCAN.ZCANDataObj)); // 根据数据类型处理 switch (dataObj.dataType) { case 1: // CAN/CANFD // 复制data到非托管缓冲区 Marshal.Copy(dataObj.data, 0, pCanfdBuffer, canfdSize); ZLGCAN.ZCANCANFDData canfdData = (ZLGCAN.ZCANCANFDData)Marshal.PtrToStructure(pCanfdBuffer, typeof(ZLGCAN.ZCANCANFDData)); //// 可选:ID过滤(调试用) //uint canId = canfdData.frame.can_id & 0x1FFFFFFF; //if (canId != 0x731 && canId != 0x7B1) // continue; // 通道和时间戳 Console.Write(String.Format("CHN:{0} [{1}] ", dataObj.chnl, canfdData.timeStamp)); // CAN/CANFD类型 + 加速标志 if ((canfdData.flag & 1) == 0) Console.Write("CAN"); else { Console.Write("CANFD"); if ((canfdData.frame.flags & 1) == 1) Console.Write("加速"); } // 帧类型(标准/扩展) Console.Write((canfdData.frame.can_id & (1 << 31)) != 0 ? " 扩展帧 " : " 标准帧 "); // 方向(TX/RX) Console.Write((canfdData.flag & (1 << 9)) != 0 ? "TX " : "RX "); // ID Console.Write(String.Format("ID:{0:X} ", canfdData.frame.can_id & 0x1FFFFFFF)); // 打印数据 int canDataLen = Math.Min(canfdData.frame.len, canfdData.frame.data != null ? canfdData.frame.data.Length : 0); for (int j = 0; j < canDataLen; j++) Console.Write(String.Format("{0:X2} ", canfdData.frame.data[j])); Console.WriteLine(); break; case 4: // LIN // 校验数据长度足够,避免越界复制 if (dataObj.data.Length < linSize) break; // 直接复制data到非托管缓冲区 Marshal.Copy(dataObj.data, 0, pLinBuffer, linSize); ZLGCAN.ZCANLINData linData = (ZLGCAN.ZCANLINData)Marshal.PtrToStructure(pLinBuffer, typeof(ZLGCAN.ZCANLINData)); // 拼接打印信息 Console.Write(String.Format("CHN:{0} [{1}] PID:{2:X} ", dataObj.chnl, linData.rxData.timeStamp, linData.pid.rawVal & 0x3F)); // 方向(TX/RX) Console.Write(linData.rxData.dir == 1 ? "TX Data: " : "RX Data: "); // 打印数据 int linDataLen = Math.Min(linData.rxData.datalen, linData.rxData.data != null ? linData.rxData.data.Length : 0); for (int j = 0; j < linDataLen; j++) Console.Write(String.Format("{0:X2} ", linData.rxData.data[j])); Console.WriteLine(); break; case 6: // LIN错误 Console.WriteLine("LIN 错误"); break; } } } } finally { // 确保非托管内存必释放(避免泄漏) Marshal.FreeHGlobal(pLinBuffer); Marshal.FreeHGlobal(pCanfdBuffer); Marshal.FreeHGlobal(pDataObjs); } } // 发送示例 static void Send_test(IntPtr chnHandle) { // CAN ZLGCAN.ZCAN_Transmit_Data[] can_data = new ZLGCAN.ZCAN_Transmit_Data[10]; IntPtr p2CanData = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZLGCAN.ZCAN_Transmit_Data)) * 10); byte[] sourceData = new byte[] { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 ,0x77,0x88}; for (int i = 0; i < 10; i++) { can_data[i].frame = new ZLGCAN.can_frame(); can_data[i].transmit_type = 0; // 0-正常发送 can_data[i].frame.can_id = MakeCanId((uint)0x10, 0, 0, 0); // ID can_data[i].frame.can_dlc = 8; // 数据长度 can_data[i].frame.__pad |= 0x20; // 发送回显 can_data[i].frame.data = new byte[8]; // 此成员必须new8个字节,否则无法运行,数据长度修改frame.can_dlc成员即可 Array.Copy(sourceData, can_data[i].frame.data, Math.Min(8, sourceData.Length)); Marshal.StructureToPtr(can_data[i], (IntPtr)(p2CanData + i * Marshal.SizeOf(typeof(ZLGCAN.ZCAN_Transmit_Data))), true); } uint ret = ZLGCAN.ZCAN_Transmit(chnHandle, p2CanData, 10); Console.WriteLine("发送 {0} 帧CAN报文", ret); // CANFD ZLGCAN.ZCAN_TransmitFD_Data[] canfd_data = new ZLGCAN.ZCAN_TransmitFD_Data[10]; IntPtr p2CanFDData = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZLGCAN.ZCAN_TransmitFD_Data)) * 10); for (int i = 0; i < 10; i++) { canfd_data[i].frame = new ZLGCAN.canfd_frame(); canfd_data[i].transmit_type = 0; // 0-正常发送 canfd_data[i].frame.can_id = MakeCanId((uint)0x10, 0, 0, 0); // ID canfd_data[i].frame.len = 64; // 数据长度 canfd_data[i].frame.flags |= 0x20; // 发送回显 canfd_data[i].frame.data = new byte[64]; // 此成员必须new64个字节,否则无法运行,数据长度修改frame.len成员即可 for (int j = 0; j < canfd_data[i].frame.len; j++) canfd_data[i].frame.data[j] = (byte)j; Marshal.StructureToPtr(canfd_data[i], (IntPtr)(p2CanFDData + i * Marshal.SizeOf(typeof(ZLGCAN.ZCAN_TransmitFD_Data))), true); } uint retfd = ZLGCAN.ZCAN_TransmitFD(chnHandle, p2CanFDData, 10); Console.WriteLine("发送 {0} 帧CANFD报文", retfd); Marshal.FreeHGlobal(p2CanData); Marshal.FreeHGlobal(p2CanFDData); } // 合并发送 static void Send_test_Merge(IntPtr dev) { const int FRAME_COUNT = 10; // 发送帧数量 ZLGCAN.ZCANDataObj[] dataObjs = new ZLGCAN.ZCANDataObj[FRAME_COUNT]; IntPtr pDataObjs = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZLGCAN.ZCANDataObj)) * FRAME_COUNT); // canfd_frame var baseFrame = new ZLGCAN.canfd_frame { can_id = MakeCanId(0x10, 0, 0, 0), // ID len = 64, // 长度 flags = 0x01, // 1-CANFD加速 data = new byte[64] }; for (int i = 0; i < baseFrame.data.Length; i++) { baseFrame.data[i] = (byte)(i % 0xFF); // 数据 } for (int i = 0; i < FRAME_COUNT; i++) { // ZCANCANFDData var canfdData = new ZLGCAN.ZCANCANFDData { timeStamp = 0, // 普通发送时 timeStamp 字段无意义 flag = 0, extraData = new byte[4], frame = baseFrame, frameType = 1, // 0-CAN 1-CANFD txEchoRequest = 1 // 1-发送回显 }; // ZCANDataObj dataObjs[i] = new ZLGCAN.ZCANDataObj { dataType = 1, // 1-CAN/CANFD chnl = 0, // 通道 flag = 0, // 未使用 data = new byte[92] }; // 结构体转字节数组 IntPtr pCanFd = Marshal.AllocHGlobal(Marshal.SizeOf(canfdData)); try { Marshal.StructureToPtr(canfdData, pCanFd, false); Marshal.Copy(pCanFd, dataObjs[i].data, 0, Marshal.SizeOf(canfdData)); } finally { Marshal.FreeHGlobal(pCanFd); } // 拷贝到非托管内存 Marshal.StructureToPtr(dataObjs[i], IntPtr.Add(pDataObjs, i * Marshal.SizeOf(typeof(ZLGCAN.ZCANDataObj))), true); } // 发送 uint sentCount = ZLGCAN.ZCAN_TransmitData(dev, pDataObjs, (uint)FRAME_COUNT); Console.WriteLine("合并发送 {0} 帧报文", sentCount); Marshal.FreeHGlobal(pDataObjs); } // 定时发送 static void AutoSend_test(IntPtr dev, int chn_idx) { // 第一条定时任务,CAN ZLGCAN.ZCAN_Transmit_Data can_data = new ZLGCAN.ZCAN_Transmit_Data(); can_data.frame = new ZLGCAN.can_frame(); can_data.transmit_type = 0; // 0-正常发送 can_data.frame.can_id = MakeCanId((uint)0x10, 0, 0, 0); // ID can_data.frame.can_dlc = 8; // 数据长度 can_data.frame.__pad |= 0x20; // 发送回显 can_data.frame.data = new byte[8]; for (int j = 0; j < can_data.frame.can_dlc; j++) can_data.frame.data[j] = (byte)j; ZLGCAN.ZCAN_AUTO_TRANSMIT_OBJ auto_can = new ZLGCAN.ZCAN_AUTO_TRANSMIT_OBJ { index = 0, enable = 1, interval = 500, obj = new ZLGCAN.ZCAN_Transmit_Data() }; auto_can.obj = can_data; string path = String.Format("{0}/auto_send", chn_idx); uint ret = ZLGCAN.ZCAN_SetValue(dev, path, ref auto_can); // 设置 if (ret != 1) { Console.WriteLine("设置定时发送失败"); } //// 第二条定时任务,CANFD //ZLGCAN.ZCAN_TransmitFD_Data canfd_data = new ZLGCAN.ZCAN_TransmitFD_Data(); //canfd_data.frame = new ZLGCAN.canfd_frame(); //canfd_data.transmit_type = 0; // 0-正常发送 //canfd_data.frame.can_id = MakeCanId((uint)0x20, 0, 0, 0); // ID //canfd_data.frame.len = 64; // 数据长度 //canfd_data.frame.flags |= 0x20; // 发送回显 //canfd_data.frame.data = new byte[64]; //for (int j = 0; j < canfd_data.frame.len; j++) // canfd_data.frame.data[j] = (byte)j; //ZLGCAN.ZCANFD_AUTO_TRANSMIT_OBJ auto_canfd = new ZLGCAN.ZCANFD_AUTO_TRANSMIT_OBJ //{ // index = 1, // enable = 1, // interval = 500, // obj = new ZLGCAN.ZCAN_TransmitFD_Data() //}; //auto_canfd.obj = canfd_data; //path = String.Format("{0}/auto_send_canfd", chn_idx); //ret = ZLGCAN.ZCAN_SetValue(dev, path, ref auto_canfd); // 设置 //if (ret != 1) //{ // Console.WriteLine("设置定时发送失败"); //} // 启动通道定时任务 path = String.Format("{0}/apply_auto_send", chn_idx); ret = ZLGCAN.ZCAN_SetValue(dev, path, "0"); if (ret != 1) { Console.WriteLine("启动定时发送失败"); } // 覆盖第一条定时任务 Thread.Sleep(2000); can_data.frame.can_id = MakeCanId((uint)0x222, 0, 0, 0); // 新ID auto_can.obj = can_data; path = String.Format("{0}/auto_send", chn_idx); ret = ZLGCAN.ZCAN_SetValue(dev, path, ref auto_can); if (ret != 1) { Console.WriteLine("第二次设置定时发送失败"); } else { Console.WriteLine("覆盖定时发送成功"); } Thread.Sleep(5000); // 清空定时发送 path = String.Format("{0}/clear_auto_send", 0); ret = ZLGCAN.ZCAN_SetValue(dev, path, "0"); if (ret != 1) { Console.WriteLine("清空定时发送失败"); } } // 设置队列发送,and队列发送示例 static void QueueSend_test(IntPtr dev, IntPtr chnHandle) { //// 使能队列发送 //uint ret = ZLGCAN.ZCAN_SetValue(Program.dev, "0/set_send_mode", "1"); // USBCANFD V1.01版本的才需要配置 //if (ret != 1) //{ // Console.WriteLine("设置队列发送失败"); // Console.ReadKey(); //} uint ret = 0; const int FRAME_COUNT = 100; // 发送帧数量 // CAN ZLGCAN.ZCAN_Transmit_Data[] can_data = new ZLGCAN.ZCAN_Transmit_Data[100]; IntPtr p2CanData = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZLGCAN.ZCAN_Transmit_Data)) * FRAME_COUNT); for (int i = 0; i < FRAME_COUNT; i++) { can_data[i].frame = new ZLGCAN.can_frame(); can_data[i].transmit_type = 0; // 0-正常发送 can_data[i].frame.can_id = MakeCanId((uint)0x10, 0, 0, 0); // ID can_data[i].frame.can_dlc = 8; // 数据长度 can_data[i].frame.__pad |= 0x20; // 发送回显 can_data[i].frame.__pad |= 0x80; // 设置队列发送 0x80单位ms,0xC0单位100us can_data[i].frame.__res0 = 0xA; // res1 和 res0两个字节存储时间间隔 can_data[i].frame.__res1 = 0x0; can_data[i].frame.data = new byte[8]; for (int j = 0; j < can_data[i].frame.can_dlc; j++) can_data[i].frame.data[j] = (byte)j; Marshal.StructureToPtr(can_data[i], (IntPtr)(p2CanData + i * Marshal.SizeOf(typeof(ZLGCAN.ZCAN_Transmit_Data))), true); } // 获取队列发送缓存 IntPtr free_count_p = ZLGCAN.ZCAN_GetValue(dev, "0/get_device_available_tx_count/1"); int free_count = Marshal.ReadInt32(free_count_p); Console.WriteLine("队列缓存剩余:{0} 帧", free_count); if (free_count > FRAME_COUNT) { ret = ZLGCAN.ZCAN_Transmit(chnHandle, p2CanData, 100); // 下发给设备 Console.WriteLine("发送 {0} 帧CAN报文", ret); } // CANFD ZLGCAN.ZCAN_TransmitFD_Data[] canfd_data = new ZLGCAN.ZCAN_TransmitFD_Data[FRAME_COUNT]; IntPtr p2CanFDData = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZLGCAN.ZCAN_TransmitFD_Data)) * FRAME_COUNT); for (int i = 0; i < FRAME_COUNT; i++) { canfd_data[i].frame = new ZLGCAN.canfd_frame(); canfd_data[i].transmit_type = 0; // 0-正常发送 canfd_data[i].frame.can_id = MakeCanId((uint)0x11, 0, 0, 0); // ID canfd_data[i].frame.len = 64; // 数据长度 canfd_data[i].frame.flags |= 0x20; // 发送回显 canfd_data[i].frame.flags |= 0x80; // 设置队列发送 0x80单位ms,0xC0单位100us canfd_data[i].frame.__res0 = 0x14; canfd_data[i].frame.__res1 = 0x0; canfd_data[i].frame.data = new byte[64]; for (int j = 0; j < canfd_data[i].frame.len; j++) canfd_data[i].frame.data[j] = (byte)j; Marshal.StructureToPtr(canfd_data[i], (IntPtr)(p2CanFDData + i * Marshal.SizeOf(typeof(ZLGCAN.ZCAN_TransmitFD_Data))), true); } // 获取队列发送缓存 free_count_p = ZLGCAN.ZCAN_GetValue(dev, "0/get_device_available_tx_count/1"); free_count = Marshal.ReadInt32(free_count_p); Console.WriteLine("队列缓存剩余:{0} 帧", free_count); if (free_count > FRAME_COUNT) { uint retfd = ZLGCAN.ZCAN_TransmitFD(chnHandle, p2CanFDData, 10); Console.WriteLine("发送 {0} 帧CANFD报文", retfd); } Thread.Sleep(2000); // 发送2秒后清空 ret = ZLGCAN.ZCAN_SetValue(dev, "0/clear_delay_send_queue", "0"); // 清空队列缓存 if (ret != 1) { Console.WriteLine("清空队列缓存失败"); } Marshal.FreeHGlobal(p2CanData); Marshal.FreeHGlobal(p2CanFDData); } // 错误帧解析 static void Analyze_err_frame(ZLGAPI.ZLGCAN.ZCAN_CHANNEL_ERR_INFO errData) { // 错误码非0时,先输出错误码(替换$为string.Format) if (errData.error_code != 0x0) { Console.Write(string.Format("错误码:{0},", errData.error_code)); } // 匹配错误码逻辑(与C++ switch完全一致) switch (errData.error_code) { case 0x1: Console.WriteLine("CAN 控制器内部FIFO溢出"); break; case 0x2: Console.WriteLine("CAN 控制器错误报警"); break; case 0x4: Console.Write("CAN 控制器消极错误,"); // 替换$为string.Format,多个参数按顺序占位 Console.WriteLine(string.Format("Rx ErrNUM: {0},Tx ErrNUM: {1}", errData.passive_ErrData[1], errData.passive_ErrData[2])); break; case 0x8: Console.WriteLine("CAN 控制器仲裁丢失"); break; case 0x10: Console.WriteLine("CAN 控制器总线错误"); break; case 0x20: Console.WriteLine("CAN 控制器总线关闭"); break; case 0x40: Console.WriteLine("CAN 缓存溢出"); break; default: break; } } // 自定义序列号 static void SN_test(IntPtr dev) { // 设置自定义sn号 if (0 == ZLGCAN.ZCAN_SetValue(dev, "0/set_cn", "C#01")) { Console.WriteLine("设置自定义序列号失败"); Console.ReadKey(); } // 获取自定义sn号 IntPtr P2CN = ZLGCAN.ZCAN_GetValue(dev, "0/get_cn/1"); string CN = Marshal.PtrToStringAnsi(P2CN); Console.WriteLine("设备的自定义序列号:{0}", CN); } // 构造CANID static uint MakeCanId(uint id, int eff, int rtr, int err) { uint ueff = (uint)(!!(Convert.ToBoolean(eff)) ? 1 : 0); // 0 标准帧,1 扩展帧 uint urtr = (uint)(!!(Convert.ToBoolean(rtr)) ? 1 : 0); // 0 数据帧,1 远程帧 uint uerr = (uint)(!!(Convert.ToBoolean(err)) ? 1 : 0); // 0 CAN帧,1 错误帧(目前只能设置为 0) return id | ueff << 31 | urtr << 30 | uerr << 29; } // byte[] -> string public static string b_array2string(byte[] ByteIn, int LEN) { // 空值校验(避免空引用/越界异常) if (ByteIn == null || ByteIn.Length == 0 || LEN == 0) return ""; // 限制实际处理长度 int actualLen = Math.Min(LEN, ByteIn.Length); // 用StringBuilder优化拼接 System.Text.StringBuilder sb = new System.Text.StringBuilder(actualLen * 3); // 预分配容量(每个字节占3字符:XX + 空格) for (int i = 0; i < actualLen; i++) { sb.Append(ByteIn[i].ToString("X2")); // 大写十六进制 if (i < actualLen - 1) // 最后一个字节后不添加空格 sb.Append(" "); } return sb.ToString(); } } }