C# 开发CANopen主站

C#开发CANopen主站(SDO收发数据)

一、准备工作

  • 熟悉CANopen相关知识,可以参考另一篇博客《CANopen学习笔记》
  • 获取周立功或者广成科技的上位机二次开发包及驱动
  • 熟悉二次开发包(以周立功二次开发包为例)
  • 了解开发需求

二、修改二次开发包(以周立功USBCAN为例)

因为用不到CAN的高速功能(CAN_FD),因此相关的都可以删除或者注释掉。
修改后,最重要的三个函数和一个数据接收委托事件就是

  • CANDeviceStart():启动CAN
  • CANDeviceClose():关闭CAN
  • CANDataSend:发送CAN
  • USBCANReceiveData:CAN接收委托事件
using NLog;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace MCUCommunication.USBCAN.ZLGCAN
{
    public class UsbCANUtil
    {
        protected Logger UsbCANLogger { get; set; }
        //定义线程委托,用于接收数据处理
        public delegate void USBCANReceiveData(byte[] revdata);
        public delegate void RxMessage(ZCAN_Receive_Data msg, bool bridge = false);
        public delegate void DisconnectDelegate(CommunicationHelper.CommunicatinType type);
        #region CAN参数定义
        const int NULL = 0;
        //const int CANFD_BRS = 0x01; /* bit rate switch (second bitrate for payload data) */
        //const int CANFD_ESI = 0x02; /* error state indicator of the transmitting node */
        /* CAN payload length and DLC definitions according to ISO 11898-1 */
        //const int CAN_MAX_DLC = 8;
        const int CAN_MAX_DLEN = 8;
        /* CAN FD payload length and DLC definitions according to ISO 11898-7 */
        //const int CANFD_MAX_DLC = 15;
        //const int CANFD_MAX_DLEN = 64;
        const uint CAN_EFF_FLAG = 0x80000000U; /* EFF/SFF is set in the MSB */
        const uint CAN_RTR_FLAG = 0x40000000U; /* remote transmission request */
        const uint CAN_ERR_FLAG = 0x20000000U; /* error message frame */
        const uint CAN_ID_FLAG = 0x1FFFFFFFU; /* id */
        public DeviceInfo[] kDeviceType =
        {
            new DeviceInfo(Define.ZCAN_USBCAN1, 1),
            new DeviceInfo(Define.ZCAN_USBCAN2, 2),
            new DeviceInfo(Define.ZCAN_USBCAN_E_U, 1),
            new DeviceInfo(Define.ZCAN_USBCAN_2E_U, 2),
            //目前本上位机只支持周立功USBCAN的四种设备,后期根据需求再添加
            //new DeviceInfo(Define.ZCAN_PCIECANFD_100U, 1),
            //new DeviceInfo(Define.ZCAN_PCIECANFD_200U, 2),
            //new DeviceInfo(Define.ZCAN_PCIECANFD_400U, 4),
            //new DeviceInfo(Define.ZCAN_USBCANFD_200U, 2),
            //new DeviceInfo(Define.ZCAN_USBCANFD_100U, 1),
            //new DeviceInfo(Define.ZCAN_USBCANFD_MINI, 1),
            //new DeviceInfo(Define.ZCAN_CANETTCP, 1),
            //new DeviceInfo(Define.ZCAN_CANETUDP, 1),
            //new DeviceInfo(Define.ZCAN_CLOUD, 1)
        };
        uint[] kAbitTiming =
        {
            0x00018B2E,//1Mbps
	        0x00018E3A,//800kbps
	        0x0001975E,//500kbps
	        0x0001AFBE,//250kbps
	        0x0041AFBE,//125kbps
	        0x0041BBEE,//100kbps
	        0x00C1BBEE //50kbps
        };
        uint[] kDbitTiming =
        {
            0x00010207,//5Mbps
	        0x0001020A,//4Mbps
	        0x0041020A,//2Mbps
	        0x0081830E //1Mbps
        };
        byte[] kTiming0 =
        {
            0x00, //1000kbps
            0x00, //800kbps
            0x00, //500kbps
            0x01, //250kbps
            0x03, //125kbps
            0x04, //100kbps
            0x09, //50kbps
            0x18, //20kbps
            0x31, //10kbps
            0xBF  //5kbps
        };
        byte[] kTiming1 =
        {
            0x14,//1000kbps
            0x16,//800kbps
            0x1C,//500kbps
            0x1C,//250kbps
            0x1C,//125kbps
            0x1C,//100kbps
            0x1C,//50kbps
            0x1C,//20kbps
            0x1C,//10kbps
            0xFF //5kbps
        };
        uint[] kBaudrate =
        {
            1000000,//1000kbps
            800000,//800kbps
            500000,//500kbps
            250000,//250kbps
            125000,//125kbps
            100000,//100kbps
            50000,//50kbps
            20000,//20kbps
            10000,//10kbps
            5000 //5kbps
        };
        #endregion
        int channel_index_;
        IntPtr device_handle_;
        IntPtr channel_handle_;
        IProperty property_;
        RecvDataThread recv_data_thread_;
        private int DeviceTypeIndex;//设备类型索引,对应ComboBox下拉框
        private int DeviceIndex=0;//设备索引,对应ComboBox下拉框,本上位机只针对一个USBCAN设备调试,不存在接入多个相同设备的情况,因此设备索引一直为0
        private int BuadRateIndex;//波特率索引,对应ComboBox下拉框
        public event USBCANReceiveData USBCANReceiveEvent;
        public event RxMessage rxmessage;
        public event DisconnectDelegate disconnectEvent;
        private static readonly Lazy<UsbCANUtil> lazy = new Lazy<UsbCANUtil>(() => new UsbCANUtil());
        public static UsbCANUtil Instance => lazy.Value;
        public ManualResetEvent manualResetEvent = new ManualResetEvent(false);
        #region 构造函数
        public UsbCANUtil()
        {            
        }
        #endregion
        #region 生成CAN ID
        public uint MakeCanId(uint id, int eff, int rtr, int err)//1:extend frame 0:standard frame
        {
            uint ueff = (uint)(!!(Convert.ToBoolean(eff)) ? 1 : 0);
            uint urtr = (uint)(!!(Convert.ToBoolean(rtr)) ? 1 : 0);
            uint uerr = (uint)(!!(Convert.ToBoolean(err)) ? 1 : 0);
            return id | ueff << 31 | urtr << 30 | uerr << 29;
        }
        #endregion
        #region 判断拓展帧还是标准帧
        public bool IsEFF(uint id)//1:extend frame 0:standard frame
        {
            return !!Convert.ToBoolean((id & CAN_EFF_FLAG));
        }
        #endregion
        #region 判断是远程帧还是数据帧
        public bool IsRTR(uint id)//1:remote frame 0:data frame
        {
            return !!Convert.ToBoolean((id & CAN_RTR_FLAG));
        }
        #endregion
        #region 判断是错误帧还是正常帧
        public bool IsERR(uint id)//1:error frame 0:normal frame
        {
            return !!Convert.ToBoolean((id & CAN_ERR_FLAG));
        }
        #endregion
        #region 获取ID
        public uint GetId(uint id)
        {
            return id & CAN_ID_FLAG;
        }
        #endregion
        #region CAN设备关闭
        public void CANDeviceClose()
        {
            //if (CANDeviceOpen())
            //{
            //    Method.ZCAN_CloseDevice(device_handle_);
            //}
            Method.ZCAN_CloseDevice(device_handle_);
        }
        #endregion
        #region CAN设备开启
        /// <summary>
        /// CAN设备开启
        /// </summary>
        /// <returns>设备是否开启</returns>
        public bool CANDeviceOpen()
        {
            //开启USBCAN端口
            uint device_type_index_ = (uint)DeviceTypeIndex;
            uint device_index_;
            device_index_ = (uint)DeviceIndex;
            try
            {
                device_handle_ = Method.ZCAN_OpenDevice(kDeviceType[device_type_index_].device_type, device_index_, 0);
            }
            catch (Exception)
            {
                MessageBox.Show("DLL运行不匹配,请选择其他版本APP!");
                return false;
            }
            if (NULL == (int)device_handle_)
            {
                //MessageBox.Show("打开设备失败,请检查设备类型和设备索引号是否正确", "提示",
                //        MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                //UsbCANLogger.Info("打开设备失败,请检查设备类型和设备索引号是否正确");
                return false;
            }
            return true;
        }
        #endregion
        #region 初始化CAN设备
        private bool InitCanDevice()
        {
            if (!CANDeviceOpen())
            {
                //MessageBox.Show("设备还没打开", "提示", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                //UsbCANLogger.Info("设备还没打开");
                return false;
            }
            try
            {
                //uint type = kDeviceType[DeviceIndex].device_type;
                IntPtr ptr = Method.GetIProperty(device_handle_);
                if (NULL == (int)ptr)
                {
                    MessageBox.Show("设置指定路径属性失败", "提示", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                    //UsbCANLogger.Info("设置指定路径属性失败");
                    return false;
                }
                property_ = (IProperty)Marshal.PtrToStructure((IntPtr)((UInt32)ptr), typeof(IProperty));
                if (!setBaudrate(kBaudrate[BuadRateIndex]))
                {
                    MessageBox.Show("设置波特率失败", "提示", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                    //UsbCANLogger.Info("设置波特率失败");
                    return false;
                }
                ZCAN_CHANNEL_INIT_CONFIG config_ = new ZCAN_CHANNEL_INIT_CONFIG();
                config_.canfd.mode = (byte)0;//0 正常模式 ,1 只听模式
                config_.can_type = Define.TYPE_CAN;
                config_.can.timing0 = kTiming0[BuadRateIndex];
                config_.can.timing1 = kTiming1[BuadRateIndex];
                config_.can.filter = 0;
                config_.can.acc_code = 0;
                config_.can.acc_mask = 0xFFFFFFFF;
                IntPtr pConfig = Marshal.AllocHGlobal(Marshal.SizeOf(config_));
                Marshal.StructureToPtr(config_, pConfig, true);
                //int size = sizeof(ZCAN_CHANNEL_INIT_CONFIG);
                //IntPtr ptr = System.Runtime.InteropServices.Marshal.AllocCoTaskMem(size);
                //System.Runtime.InteropServices.Marshal.StructureToPtr(config_, ptr, true);
                channel_handle_ = Method.ZCAN_InitCAN(device_handle_, (uint)channel_index_, pConfig);
                Marshal.FreeHGlobal(pConfig);
                if (NULL == (int)channel_handle_)
                {
                    //MessageBox.Show("初始化CAN失败", "提示", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                    //UsbCANLogger.Info("初始化CAN失败");
                    return false;
                }
            }
            catch (Exception ex)
            {
                UsbCANLogger.Info(ex.Message);
                return false;
            }
            return true;
        }
        #endregion
        #region 启动CAN
        public bool CANDeviceStart()
        {
            if (!InitCanDevice())
            {
                //MessageBox.Show("初始化CAN失败", "提示", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                //UsbCANLogger.Info("初始化CAN失败");
                return false;
            }
            if (Method.ZCAN_StartCAN(channel_handle_) != Define.STATUS_OK)
            {
                //MessageBox.Show("启动CAN失败", "提示", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                //UsbCANLogger.Info("启动CAN失败");
                return false;
            }
            if (null == recv_data_thread_)
            {
                recv_data_thread_ = new RecvDataThread();
                recv_data_thread_.setChannelHandle(channel_handle_);
                recv_data_thread_.setStart(true);
                recv_data_thread_.RecvCANData += this.AddData;
                //recv_data_thread_.RecvFDData += this.AddData;
            }
            else
            {
                recv_data_thread_.setChannelHandle(channel_handle_);
            }
            return true;
        }
        #endregion
        #region 复位CAN
        public bool CANDeviceReset()
        {
            if (Method.ZCAN_ResetCAN(channel_handle_) != Define.STATUS_OK)
            {
                //MessageBox.Show("复位失败", "提示", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                //UsbCANLogger.Info("复位失败");
                return false;
            }
            return true;
        }
        #endregion
        #region 数据发送
        public bool CANDataSend(string IDstr,string DataText)
        {
            if (DataText.Length == 0)
            {
                //MessageBox.Show("数据为空", "提示", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                //UsbCANLogger.Info("数据为空");
                return false;
            }
            uint id = (uint)System.Convert.ToInt32(IDstr, 16);
            string data = DataText;
            uint result; //发送的帧数
            ZCAN_Transmit_Data can_data = new ZCAN_Transmit_Data();
            can_data.frame.can_id = MakeCanId(id, 0, 0, 0);  
            can_data.frame.data = new byte[8];
            can_data.frame.can_dlc = (byte)SplitData(data, ref can_data.frame.data, CAN_MAX_DLEN);
            can_data.transmit_type = (uint)0;  //0正常发送 1单次发送 2自发自收 3单次自发自收
            IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(can_data));
            Marshal.StructureToPtr(can_data, ptr, true);
            result = Method.ZCAN_Transmit(channel_handle_, ptr, 1);
            Marshal.FreeHGlobal(ptr);
            if (result != 1)
            {
                //MessageBox.Show("发送数据失败", "提示", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                //UsbCANLogger.Info("发送数据失败");
                AddErr();
                return false;
            }
            return true;
        }
        public bool CANDataSend(uint id, byte[] Data)
        {
            if (Data.Length == 0 && Data.Length > 8)
            {
                //MessageBox.Show("数据为空,或者超出范围", "提示", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                //UsbCANLogger.Info("数据为空,或者超出范围");
                return false;
            }
            uint result=0; //发送的帧数
            ZCAN_Transmit_Data can_data = new ZCAN_Transmit_Data();
            can_data.frame.can_id = MakeCanId(id, 0, 0, 0);
            can_data.frame.data = Data;
            can_data.frame.can_dlc = (byte)Data.Length;
            can_data.transmit_type = (uint)0;  //0正常发送 1单次发送 2自发自收 3单次自发自收
            IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(can_data));
            Marshal.StructureToPtr(can_data, ptr, true);
            lock (recv_data_thread_)
            {
                try
                {
                    int times = 0;
                    var flag = manualResetEvent.WaitOne(TimeSpan.FromMilliseconds(3));
                    if (flag)
                    {
                        times = 0;
                        //IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(can_data));
                        //Marshal.StructureToPtr(can_data, ptr, true);
                        result = Method.ZCAN_Transmit(channel_handle_, ptr, 1);
                        manualResetEvent.Reset();
                        Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fffff :::  1  :::") + string.Format("{0:X2} {1:X2}", Data[1], Data[2]) + "///" + result);
                        Marshal.FreeHGlobal(ptr);
                        if (result != 1)
                        {
                            AddErr();
                            return false;
                        }
                        else
                        {
                            SaveDataToLog(can_data);
                        }
                    }
                    else
                    {
                        while (!flag)
                        {
                            //times++;
                            //IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(can_data));
                            //Marshal.StructureToPtr(can_data, ptr, true);
                            result = Method.ZCAN_Transmit(channel_handle_, ptr, 1);
                            manualResetEvent.Reset();
                            Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fffff ::: 2 :::") + string.Format("{0:X2} {1:X2}", Data[1], Data[2]) + "///" + result);
                            //Marshal.FreeHGlobal(ptr);
                            if (result != 1)
                            {
                                times++;
                                AddErr();
                                if (times>0)
                                {
                                    Marshal.FreeHGlobal(ptr);
                                    times = 0;
                                    CommunicationHelper.Instance.DeviceDisConnect(CommunicationHelper.CommunicatinType.USBCAN);
                                    if (disconnectEvent != null)
                                    {
                                        disconnectEvent(CommunicationHelper.CommunicatinType.USBCAN);
                                    }
                                    return false;
                                }
                            }
                            else
                            {
                                SaveDataToLog(can_data);
                                Marshal.FreeHGlobal(ptr);
                                break;
                            }
                        }
                    }
                }
                catch (Exception)
                {
                }
        }
            return true;
        }
        #endregion
        #region 更改CAN设备的通道数
        public void SetCANDeviceChannel(int kDeviceTypeindex,int Channel)
        {
            try
            {
                int Num = (int)kDeviceType[kDeviceTypeindex].channel_count;
                if (Channel >= Num)
                {
                    return;
                }
                channel_index_ = Channel;
            }
            catch (Exception ex)
            {
                MessageBox.Show("出现错误!");
            }
        }
        #endregion
        #region SetDeviceTypeIndex
        public void SetDeviceTypeIndex(int deviceTypeIndex)
        {
            DeviceTypeIndex = deviceTypeIndex;
        }
        #endregion
        #region SetBuadRateIndex
        public void SetBuadRateIndex(int buadRateIndex)
        {
            BuadRateIndex = buadRateIndex;
        }
        #endregion
        #region 获取设备的通道数并保存到列表中,ComboBox源
        public List<int> GetCANDeviceChannelNum(int kDeviceTypeIndex)
        {
            int Num = (int)kDeviceType[kDeviceTypeIndex].channel_count;
            var list = new List<int>();
            for (int i = 0; i < Num; i++)
            {
                list.Add(i);
            }
            return list;
        }
        #endregion
        //设置波特率
        private bool setBaudrate(UInt32 baud)
        {
            string path = channel_index_ + "/baud_rate";
            string value = baud.ToString();
            byte[] byteArray = System.Text.Encoding.Default.GetBytes(value);
            //char* pathCh = (char*)System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(path).ToPointer();
            //char* valueCh = (char*)System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(value).ToPointer();
            return 1 == property_.SetValue(path, byteArray);
        }
        private void AddData(ZCAN_Receive_Data[] data, uint len)
        {
            //string list_box_data_ = "";
            for (uint i = 0; i < len; ++i)
            {
                byte[] revData=new byte[11];//ID + DLC +Data,最长11个字节
                ZCAN_Receive_Data can = data[i];
                uint id = data[i].frame.can_id;
                revData[0]= (byte)(id / 256);
                revData[1]= (byte)(id % 256);
                revData[2]= (byte)can.frame.can_dlc;
                byte[] tarData = new byte[3+ can.frame.can_dlc];
                string str = string.Format("{0:X2} {1:X2} {2:X2} ", revData[0], revData[1], revData[2]);
                for (uint j = 0; j < can.frame.can_dlc; ++j)
                {
                    revData[3 + j] = (byte)can.frame.data[j];
                    str += string.Format("{0:X2} ", revData[3 + j]);
                }
                Array.Copy(revData,tarData,tarData.Length);//拷贝数组,不是每个Data都是8个字节
                Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fffff :::")+str);
                CommunicationHelper.Instance.DebugLogAdd(str);
                manualResetEvent.Set();         //Set() 方法的调用使得ManualResetEvent对象的bool变量值为True,所有线程被释放并继续执行
                manualResetEvent.Reset();       //如果我们想多次发送信号,那么我们必须在调用Set()方法后立即调用Reset()方法。
                if (USBCANReceiveEvent != null)
                {
                    USBCANReceiveEvent(tarData);
                }
                if (rxmessage != null)
                {
                    rxmessage(can);
                }                
            }
        }
        private string AddErr()
        {
            ZCAN_CHANNEL_ERROR_INFO pErrInfo = new ZCAN_CHANNEL_ERROR_INFO();
            IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(pErrInfo));
            Marshal.StructureToPtr(pErrInfo, ptr, true);
            if (Method.ZCAN_ReadChannelErrInfo(channel_handle_, ptr) != Define.STATUS_OK)
            {
                //UsbCANLogger.Info("获取错误信息失败");
                MessageBox.Show("获取错误信息失败", "提示", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            }
            Marshal.FreeHGlobal(ptr);
            string errorInfo = String.Format("错误码:{0:D1}", pErrInfo.error_code);
            //UsbCANLogger.Info(errorInfo);
            //MessageBox.Show(errorInfo);
            return errorInfo;
        }
        //拆分text到发送data数组
        private int SplitData(string data, ref byte[] transData, int maxLen)
        {
            string[] dataArray = data.Split(' ');
            for (int i = 0; (i < maxLen) && (i < dataArray.Length); i++)
            {
                transData[i] = Convert.ToByte(dataArray[i].Substring(0, 2), 16);
            }
            return dataArray.Length;
        }
        private void SaveDataToLog(ZCAN_Transmit_Data can_data)
        {
            byte[] SendData = new byte[11];
            SendData[0] = (byte)(can_data.frame.can_id / 256);
            SendData[1] = (byte)(can_data.frame.can_id % 256);
            SendData[2] = (byte)can_data.frame.can_dlc;
            Array.Copy(can_data.frame.data, 0, SendData, 3, can_data.frame.can_dlc);
            var str = CommunicationHelper.Instance.ByteToStr(SendData);
            CommunicationHelper.Instance.DebugLogAdd(str);
        }
    }
}

三、修改接收线程文件

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;
using System.Runtime.InteropServices;
using System.Threading;
using System.IO;
namespace MCUCommunication.USBCAN.ZLGCAN
{
    //接收数据线程类
    public class RecvDataThread
    {
        [UnmanagedFunctionPointer(CallingConvention.StdCall)]
        public delegate void RecvCANDataEventHandler(ZCAN_Receive_Data[] data, uint len);//CAN数据接收事件委托
        [UnmanagedFunctionPointer(CallingConvention.StdCall)]
        public delegate void RecvFDDataEventHandler(ZCAN_ReceiveFD_Data[] data, uint len);//CANFD数据接收事件委托
        const int TYPE_CAN = 0;
        const int TYPE_CANFD = 1;
        bool m_bStart;
        IntPtr channel_handle_;
        Thread recv_thread_;
        static object locker = new object();
        public static RecvCANDataEventHandler OnRecvCANDataEvent;
        //public static RecvFDDataEventHandler OnRecvFDDataEvent;
        public RecvDataThread()
        {
        }
        public event RecvCANDataEventHandler RecvCANData
        {
            add { OnRecvCANDataEvent += new RecvCANDataEventHandler(value); }
            remove { OnRecvCANDataEvent -= new RecvCANDataEventHandler(value); }
        }
        //public event RecvFDDataEventHandler RecvFDData
        //{
        //    add { OnRecvFDDataEvent += new RecvFDDataEventHandler(value); }
        //    remove { OnRecvFDDataEvent -= new RecvFDDataEventHandler(value); }
        //}
        public void setStart(bool start)
        {
            m_bStart = start;
            if (start)
            {
                recv_thread_ = new Thread(RecvDataFunc);
                recv_thread_.IsBackground = true;
                recv_thread_.Start();
            }
            else
            {
                recv_thread_.Join();
                recv_thread_ = null;
            }
        }
        public void setChannelHandle(IntPtr channel_handle)
        {
            lock(locker)
            {
                channel_handle_ = channel_handle;
            }
        }
        //数据接收函数
        protected void RecvDataFunc()
        {
            ZCAN_Receive_Data[] can_data = new ZCAN_Receive_Data[10000];
            //ZCAN_ReceiveFD_Data[] canfd_data = new ZCAN_ReceiveFD_Data[10000];
            uint len;
            try
            {
                while (m_bStart)
                {
                    lock (locker)
                    {
                        len = Method.ZCAN_GetReceiveNum(channel_handle_, TYPE_CAN);
                        if (len > 0)
                        {
                            int size = Marshal.SizeOf(typeof(ZCAN_Receive_Data));
                            IntPtr ptr = Marshal.AllocHGlobal((int)len * size);
                            len = Method.ZCAN_Receive(channel_handle_, ptr, len, 50);
                            for (int i = 0; i < len; ++i)
                            {
                                can_data[i] = (ZCAN_Receive_Data)Marshal.PtrToStructure(
                                    (IntPtr)((Int64)ptr + i * size), typeof(ZCAN_Receive_Data));
                            }
                            //SaveDataToLog(can_data[0]);
                            OnRecvCANDataEvent(can_data, len);
                            Marshal.FreeHGlobal(ptr);
                        }
                        //len = Method.ZCAN_GetReceiveNum(channel_handle_, TYPE_CANFD);
                        //if (len > 0)
                        //{
                        //    int size = Marshal.SizeOf(typeof(ZCAN_ReceiveFD_Data));
                        //    IntPtr ptr = Marshal.AllocHGlobal((int)len * size);
                        //    len = Method.ZCAN_ReceiveFD(channel_handle_, ptr, len, 50);
                        //    for (int i = 0; i < len; ++i)
                        //    {
                        //        canfd_data[i] = (ZCAN_ReceiveFD_Data)Marshal.PtrToStructure(
                        //            (IntPtr)((UInt32)ptr+i*size), typeof(ZCAN_ReceiveFD_Data));
                        //    }
                        //    OnRecvFDDataEvent(canfd_data, len);
                        //    Marshal.FreeHGlobal(ptr);
                        //}
                    }
                    Thread.Sleep(1);
                }
            }
            catch (Exception ex)
            {
            }
        }
        private void SaveDataToLog(ZCAN_Receive_Data can_data)
        {
            byte[] SendData = new byte[11];
            SendData[0] = (byte)(can_data.frame.can_id / 256);
            SendData[1] = (byte)(can_data.frame.can_id % 256);
            SendData[2] = (byte)can_data.frame.can_dlc;
            Array.Copy(can_data.frame.data, 0, SendData, 3, can_data.frame.can_dlc);
            var str = CommunicationHelper.Instance.ByteToStr(SendData);
            CommunicationHelper.Instance.DebugLogAdd(str);
        }
    }
}

四、添加读取DLL的封装文件(二次开发包复制过来即可)

using System;
using System.Runtime.InteropServices;
namespace MCUCommunication.USBCAN.ZLGCAN
{
    [StructLayout(LayoutKind.Sequential)]
    public struct ZCAN
    {
        public uint acc_code;
        public uint acc_mask;
        public uint reserved;
        public byte filter;
        public byte timing0;
        public byte timing1;
        public byte mode;
    };
    [StructLayout(LayoutKind.Sequential)]
    public struct CANFD
    {
        public uint acc_code;
        public uint acc_mask;
        public uint abit_timing;
        public uint dbit_timing;
        public uint brp;
        public byte filter;
        public byte mode;
        public UInt16 pad;
        public uint reserved;
    };
    [StructLayout(LayoutKind.Sequential)]
    public struct can_frame
    {
        public uint can_id;  /* 32 bit MAKE_CAN_ID + EFF/RTR/ERR flags */
        public byte can_dlc; /* frame payload length in byte (0 .. CAN_MAX_DLEN) */
        public byte __pad;   /* padding */
        public byte __res0;  /* reserved / padding */
        public byte __res1;  /* reserved / padding */
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
        public byte[] data/* __attribute__((aligned(8)))*/;
    };
    [StructLayout(LayoutKind.Sequential)]
    public struct canfd_frame
    {
        public uint can_id;  /* 32 bit MAKE_CAN_ID + EFF/RTR/ERR flags */
        public byte len;     /* frame payload length in byte */
        public byte flags;   /* additional flags for CAN FD,i.e error code */
        public byte __res0;  /* reserved / padding */
        public byte __res1;  /* reserved / padding */
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
        public byte[] data/* __attribute__((aligned(8)))*/;
    };
    [StructLayout(LayoutKind.Explicit)]
    public struct ZCAN_CHANNEL_INIT_CONFIG
    {
        [FieldOffset(0)]
        public uint can_type; //type:TYPE_CAN TYPE_CANFD
        [FieldOffset(4)]
        public ZCAN can;
        [FieldOffset(4)]
        public CANFD canfd;
    };
    [StructLayout(LayoutKind.Sequential)]
    public struct ZCAN_Transmit_Data
    {
        public can_frame frame;
        public uint transmit_type;
    };
    [StructLayout(LayoutKind.Sequential)]
    public struct ZCAN_Receive_Data
    {
        public can_frame frame;
        public UInt64 timestamp;//us
    };
    [StructLayout(LayoutKind.Sequential)]
    public struct ZCAN_TransmitFD_Data
    {
        public canfd_frame frame;
        public uint transmit_type;
    };
    public struct DeviceInfo
    {
        public uint device_type;  //设备类型
        public uint channel_count;//设备的通道个数
        public DeviceInfo(uint type, uint count)
        {
            device_type = type;
            channel_count = count;
        }
    };
    [StructLayout(LayoutKind.Sequential)]
    public struct ZCAN_ReceiveFD_Data
    {
        public canfd_frame frame;
        public UInt64 timestamp;//us
    };
    [StructLayout(LayoutKind.Sequential)]
    public struct ZCAN_CHANNEL_ERROR_INFO
    {
        public uint error_code;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
        public byte[] passive_ErrData;
        public byte arLost_ErrData;
    } ;
    //for zlg cloud
    [StructLayout(LayoutKind.Sequential)]
    public struct ZCLOUD_DEVINFO
    {
        public int devIndex;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
        public char[] type;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
        public char[] id;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
        public char[] owner;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
        public char[] model;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
        public char[] fwVer;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
        public char[] hwVer;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
        public char[] serial;
        public byte canNum;
        public int status;             // 0:online, 1:offline
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
        public byte[] bCanUploads;   // each channel enable can upload
        public byte bGpsUpload;
    };
    //[StructLayout(LayoutKind.Sequential)]
    //public struct ZCLOUD_DEV_GROUP_INFO
    //{
    //    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
    //    public char[] groupName;
    //    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
    //    public char[] desc;
    //    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
    //    public char[] groupId;
    //    //public ZCLOUD_DEVINFO *pDevices;
    //    public IntPtr pDevices;
    //    public uint devSize;
    //};
    [StructLayout(LayoutKind.Sequential)]
    public struct ZCLOUD_USER_DATA
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
        public char[] username;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
        public char[] mobile;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
        public char[] dllVer;
        //[MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
        // public char[] email;
        // public IntPtr pDevGroups;
        // public uint devGroupSize;
        public uint devCnt;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 100)]
        public ZCLOUD_DEVINFO[] devices;
    };
    public class Define
    {
        public const int TYPE_CAN = 0;
        public const int TYPE_CANFD = 1;
        public const int ZCAN_USBCAN1 = 3;
        public const int ZCAN_USBCAN2 = 4;
        public const int ZCAN_CANETUDP = 12;
        public const int ZCAN_CANETTCP = 17;
        public const int ZCAN_USBCAN_E_U = 20;
        public const int ZCAN_USBCAN_2E_U = 21;
        public const int ZCAN_PCIECANFD_100U = 38;
        public const int ZCAN_PCIECANFD_200U = 39;
        public const int ZCAN_PCIECANFD_400U = 40;
        public const int ZCAN_USBCANFD_200U = 41;
        public const int ZCAN_USBCANFD_100U = 42;
        public const int ZCAN_USBCANFD_MINI = 43;
        public const int ZCAN_CLOUD = 46;
        public const int ZCAN_CANFDNET_400U_TCP = 52;
        public const int ZCAN_CANFDNET_400U_UDP = 53;
        public const int STATUS_ERR = 0;
        public const int STATUS_OK = 1;
    };
    public class Method
    {
        [DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)]
        public static extern IntPtr ZCAN_OpenDevice(uint device_type, uint device_index, uint reserved);
        [DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)]
        public static extern uint ZCAN_CloseDevice(IntPtr device_handle);
        [DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)]
        // pInitConfig -> ZCAN_CHANNEL_INIT_CONFIG
        public static extern IntPtr ZCAN_InitCAN(IntPtr device_handle, uint can_index, IntPtr pInitConfig);
        [DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)]
        public static extern uint ZCAN_StartCAN(IntPtr channel_handle);
        [DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)]
        public static extern uint ZCAN_ResetCAN(IntPtr channel_handle);
        [DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)]
        // pTransmit -> ZCAN_Transmit_Data
        public static extern uint ZCAN_Transmit(IntPtr channel_handle, IntPtr pTransmit, uint len);
        [DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)]
        // pTransmit -> ZCAN_TransmitFD_Data
        public static extern uint ZCAN_TransmitFD(IntPtr channel_handle, IntPtr pTransmit, uint len);
        [DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)]
        public static extern uint ZCAN_GetReceiveNum(IntPtr channel_handle, byte type);
        [DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)]
        public static extern uint ZCAN_Receive(IntPtr channel_handle, IntPtr data, uint len, int wait_time = -1);
        [DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)]
        public static extern uint ZCAN_ReceiveFD(IntPtr channel_handle, IntPtr data, uint len, int wait_time = -1);
        [DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)]
        // pErrInfo -> ZCAN_CHANNEL_ERROR_INFO
        public static extern uint ZCAN_ReadChannelErrInfo(IntPtr channel_handle, IntPtr pErrInfo);
        [DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)]
        public static extern IntPtr GetIProperty(IntPtr device_handle);
        [DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)]
        public static extern bool ZCLOUD_IsConnected();
        [DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)]
        public static extern void ZCLOUD_SetServerInfo(string httpAddr, ushort httpPort,
            string mqttAddr, ushort mqttPort);
        [DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)]
        public static extern uint ZCLOUD_ConnectServer(string username, string password);
        [DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)]
        public static extern uint ZCLOUD_DisconnectServer();
        [DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)]
        public static extern IntPtr ZCLOUD_GetUserData();
    }
    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate int SetValueFunc(string path, byte[] value);
    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate IntPtr GetValueFunc(string path);
    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate IntPtr GetPropertysFunc(string path, string value);
    public struct IProperty
    {
        public SetValueFunc SetValue;
        public GetValueFunc GetValue;
        public GetPropertysFunc GetPropertys;
    };
}

需要注意的是,生成的exe同等级文件夹路径下需要添加dll库文件
在这里插入图片描述
在这里插入图片描述
还需要注意dll库兼容的是64位或32位系统(若出现无法读取dll文件的情况,大概率就是dll与系统不匹配的问题)
在这里插入图片描述

五、添加CANopen相关文件和修改

using DCMotorControlSystem.Commoms.Protocols;
using MCUCommunication.USBCAN.ECAN;
using MCUCommunication.USBCAN.ZLGCAN;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using static MCUCommunication.CommunicationHelper;
namespace MCUCommunication.Protocols
{
    public class CanOpenSDOLib
    {
        private static readonly Lazy<CanOpenSDOLib> lazy = new Lazy<CanOpenSDOLib>(() => new CanOpenSDOLib());
        public static CanOpenSDOLib Instance => lazy.Value;
        private Queue<SDO> sdo_queue = new Queue<SDO>();
        public Dictionary<UInt16, SDO> SDOcallbacks = new Dictionary<ushort, SDO>();
        ConcurrentQueue<CanPacket> packetqueue = new ConcurrentQueue<CanPacket>();
        public delegate void PacketEvent(CanPacket p, DateTime dt);
        public event PacketEvent packetevent;
        public delegate void SDOEvent(CanPacket p, DateTime dt);
        public event SDOEvent sdoevent;        
        public delegate void SDODelegate(CanPacket p);
        public event SDODelegate SDODelegateevent;
        public byte[] DownLoadData = null;
        bool threadrun = true;
        private readonly object lockThread = new object();
        public CanOpenSDOLib()
        {
        }
        /// <summary>
        /// Send a Can packet on the bus
        /// </summary>
        /// <param name="p"></param>
        public bool SendPacket(CommunicatinType type,CanPacket p, bool bridge = false)
        {
            var flag = false;
            switch (type)
            {
                case CommunicatinType.USBCAN:
                    ZCAN_Receive_Data msg = p.ToMsg();
                    flag = UsbCANUtil.Instance.CANDataSend(msg.frame.can_id, msg.frame.data);
                    if (flag)
                    {
                        Driver_rxmessage(msg, bridge);
                    }
                    break;
                case CommunicatinType.ECAN:
                    CAN_OBJ obj = p.ToMsg2();
                    flag = ECANUtil.Instance.DataSend(obj.ID,obj.data);
                    if (flag)
                    {
                        Driver_rxmessage(obj, bridge);
                    }
                    break;
                default:
                    break;
            }            
            return flag;
        }
        /// <summary>
        /// Recieved message callback handler
        /// </summary>
        /// <param name="msg">CanOpen message recieved from the bus</param>
        private void Driver_rxmessage(ZCAN_Receive_Data msg, bool bridge = false)
        {
            //Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fffff ::: 2 :::") + string.Format("Driver_rxmessage {0:X2} {1:X2}", msg.frame.data[1], msg.frame.data[2]));
            //packetqueue.Enqueue(new CanPacket(msg, bridge));
            var p = new CanPacket(msg, bridge);
            packetqueue.Enqueue(new CanPacket(msg, bridge));
            if (SDODelegateevent != null)
            {
                SDODelegateevent(p);
            }
        }
        /// <summary>
        /// Recieved message callback handler
        /// </summary>
        /// <param name="msg">CanOpen message recieved from the bus</param>
        private void Driver_rxmessage(CAN_OBJ msg, bool bridge = false)
        {
            var p = new CanPacket(msg, bridge);
            packetqueue.Enqueue(new CanPacket(msg, bridge));
            if (SDODelegateevent != null)
            {
                SDODelegateevent(p);
            }
        }
        public void open(CommunicatinType type)
        {
            switch (type)
            {
                case CommunicatinType.USBCAN:
                    UsbCANUtil.Instance.rxmessage += Driver_rxmessage;
                    break;
                case CommunicatinType.SerialPort:
                    break;
                case CommunicatinType.ECAN:
                    ECANUtil.Instance.rxmessage += Driver_rxmessage;
                    break;
                default:
                    break;
            }
            threadrun = true;
            Thread thread = new Thread(new ThreadStart(asyncprocess));
            //Thread thread = new Thread(new ThreadStart(syncprocess)); 
            thread.Start();
        }   
        /// <summary>
        /// Close the CanOpen CanFestival driver
        /// </summary>
        public void close(CommunicatinType type)
        {
            threadrun = false;
            switch (type)
            {
                case CommunicatinType.USBCAN:
                    UsbCANUtil.Instance.rxmessage -= Driver_rxmessage;
                    break;
                case CommunicatinType.SerialPort:
                    break;
                case CommunicatinType.ECAN:
                    ECANUtil.Instance.rxmessage -= Driver_rxmessage;
                    break;
                default:
                    break;
            }
        }
        void asyncprocess()
        {
            while (threadrun)
            {
                CanPacket cp;
                //while (threadrun && packetqueue.IsEmpty && sdo_queue.Count == 0 && SDO.isEmpty())
                while (packetqueue.IsEmpty && sdo_queue.Count == 0 && SDO.isEmpty())
                {
                    System.Threading.Thread.Sleep(1);
                }
                while (packetqueue.TryDequeue(out cp))
                {
                    //SDO replies 0x601-0x67F
                    if (cp.cob >= 0x580 && cp.cob < 0x600)
                    {
                        if (cp.len != 8)
                            return;
                        lock (sdo_queue)
                        {
                            if (SDOcallbacks.ContainsKey(cp.cob))
                            {
                                if (SDOcallbacks[cp.cob].SDOProcess(cp))
                                {
                                    SDOcallbacks.Remove(cp.cob);
                                }
                            }
                            if (sdoevent != null)
                                sdoevent(cp, DateTime.Now);
                        }
                    }
                    //SDO 发送
                    if (cp.cob >= 0x600 && cp.cob < 0x680)
                    {
                        if (sdoevent != null)
                            sdoevent(cp, DateTime.Now);
                    }
                    SDO.kick_SDO();
                    lock (sdo_queue)
                    {
                        if (sdo_queue.Count > 0)
                        {
                            SDO sdoobj = sdo_queue.Peek();
                            if (!SDOcallbacks.ContainsKey((UInt16)(sdoobj.node + 0x580)))
                            {
                                sdoobj = sdo_queue.Dequeue();
                                SDOcallbacks.Add((UInt16)(sdoobj.node + 0x580), sdoobj);
                                sdoobj.sendSDO();
                            }
                        }
                    }
                    //System.Threading.Thread.Sleep(1);
                }
            }
        }
        #region SDOHelpers
        /// <summary>
        /// Write to a node via SDO
        /// </summary>
        /// <param name="node">Node ID</param>
        /// <param name="index">Object Dictionary Index</param>
        /// <param name="subindex">Object Dictionary sub index</param>
        /// <param name="udata">UInt32 data to send</param>
        /// <param name="completedcallback">Call back on finished/error event</param>
        /// <returns>SDO class that is used to perform the packet handshake, contains error/status codes</returns>
        public SDO SDOwrite(byte node, UInt16 Keycode, byte subindex, UInt32 udata, Action<SDO> completedcallback)
        {
            byte[] bytes = BitConverter.GetBytes(udata);
            return SDOwrite(node, Keycode, subindex, bytes, completedcallback);
        }
        /// <summary>
        /// Write to a node via SDO
        /// </summary>
        /// <param name="node">Node ID</param>
        /// <param name="index">Object Dictionary Index</param>
        /// <param name="subindex">Object Dictionary sub index</param>
        /// <param name="udata">Int64 data to send</param>
        /// <param name="completedcallback">Call back on finished/error event</param>
        /// <returns>SDO class that is used to perform the packet handshake, contains error/status codes</returns>
        public SDO SDOwrite(byte node, UInt16 Keycode, byte subindex, Int64 udata, Action<SDO> completedcallback)
        {
            byte[] bytes = BitConverter.GetBytes(udata);
            return SDOwrite(node, Keycode, subindex, bytes, completedcallback);
        }
        /// <summary>
        /// Write to a node via SDO
        /// </summary>
        /// <param name="node">Node ID</param>
        /// <param name="index">Object Dictionary Index</param>
        /// <param name="subindex">Object Dictionary sub index</param>
        /// <param name="udata">UInt64 data to send</param>
        /// <param name="completedcallback">Call back on finished/error event</param>
        /// <returns>SDO class that is used to perform the packet handshake, contains error/status codes</returns>
        public SDO SDOwrite(byte node, UInt16 Keycode, byte subindex, UInt64 udata, Action<SDO> completedcallback)
        {
            byte[] bytes = BitConverter.GetBytes(udata);
            return SDOwrite(node, Keycode, subindex, bytes, completedcallback);
        }
        /// <summary>
        /// Write to a node via SDO
        /// </summary>
        /// <param name="node">Node ID</param>
        /// <param name="index">Object Dictionary Index</param>
        /// <param name="subindex">Object Dictionary sub index</param>
        /// <param name="udata">Int32 data to send</param>
        /// <param name="completedcallback">Call back on finished/error event</param>
        /// <returns>SDO class that is used to perform the packet handshake, contains error/status codes</returns>
        public SDO SDOwrite(byte node, UInt16 Keycode, byte subindex, Int32 udata, Action<SDO> completedcallback)
        {
            byte[] bytes = BitConverter.GetBytes(udata);
            return SDOwrite(node, Keycode, subindex, bytes, completedcallback);
        }
        /// <summary>
        /// Write to a node via SDO
        /// </summary>
        /// <param name="node">Node ID</param>
        /// <param name="index">Object Dictionary Index</param>
        /// <param name="subindex">Object Dictionary sub index</param>
        /// <param name="udata">UInt16 data to send</param>
        /// <param name="completedcallback">Call back on finished/error event</param>
        /// <returns>SDO class that is used to perform the packet handshake, contains error/status codes</returns>
        public SDO SDOwrite(byte node, UInt16 Keycode, byte subindex, Int16 udata, Action<SDO> completedcallback)
        {
            byte[] bytes = BitConverter.GetBytes(udata);
            return SDOwrite(node, Keycode, subindex, bytes, completedcallback);
        }
        /// <summary>
        /// Write to a node via SDO
        /// </summary>
        /// <param name="node">Node ID</param>
        /// <param name="index">Object Dictionary Index</param>
        /// <param name="subindex">Object Dictionary sub index</param>
        /// <param name="udata">UInt16 data to send</param>
        /// <param name="completedcallback">Call back on finished/error event</param>
        /// <returns>SDO class that is used to perform the packet handshake, contains error/status codes</returns>
        public SDO SDOwrite(byte node, UInt16 Keycode, byte subindex, UInt16 udata, Action<SDO> completedcallback)
        {
            byte[] bytes = BitConverter.GetBytes(udata);
            return SDOwrite(node, Keycode, subindex, bytes, completedcallback);
        }
        /// <summary>
        /// Write to a node via SDO
        /// </summary>
        /// <param name="node">Node ID</param>
        /// <param name="index">Object Dictionary Index</param>
        /// <param name="subindex">Object Dictionary sub index</param>
        /// <param name="udata">float data to send</param>
        /// <param name="completedcallback">Call back on finished/error event</param>
        /// <returns>SDO class that is used to perform the packet handshake, contains error/status codes</returns>
        public SDO SDOwrite(byte node, UInt16 Keycode, byte subindex, float ddata, Action<SDO> completedcallback)
        {
            byte[] bytes = BitConverter.GetBytes(ddata);
            return SDOwrite(node, Keycode, subindex, bytes, completedcallback);
        }
        /// <summary>
        /// Write to a node via SDO
        /// </summary>
        /// <param name="node">Node ID</param>
        /// <param name="index">Object Dictionary Index</param>
        /// <param name="subindex">Object Dictionary sub index</param>
        /// <param name="udata">a byte of data to send</param>
        /// <param name="completedcallback">Call back on finished/error event</param>
        /// <returns>SDO class that is used to perform the packet handshake, contains error/statu
### 回答1: Moons的CANopen主站是一款用于CANopen通信协议的主控设备。CANopen是一种针对控制器区域网络(CAN)的通信协议,用于实现工业自动化设备之间的数据交换和通信。 作为主站,Moons的CANopen主控设备具备以下功能。首先,它能够与其他CANopen设备进行数据通信,实现设备之间的数据传输和控制命令的发送与接收。通过CANopen协议,Moons的主站可以与多个从站设备进行连接,互相交换数据和信息。 其次,Moons的CANopen主站支持各种不同的CANopen通信对象和数据类型,包括进程变量、SDO(服务数据对象)、PDO(过程数据对象)等。它能够对这些对象进行配置和管理,达到对从站设备进行监控和控制的目的。同时,Moons的主站还支持CANopen网络管理协议,可以对设备进行配置和节点管理,并实现设备的启动和停止等操作。 此外,Moons的CANopen主站还具备网络诊断和错误处理能力。当从站设备发生错误或通信故障时,主站能够检测并进行相应的错误处理。它可以通过CANopen协议中定义的错误码和诊断信息,快速识别和定位问题,并采取相应的纠正措施。 总的来说,Moons的CANopen主站是一款功能强大的CANopen通信主控设备。它能够实现多个从站设备之间的数据交换和控制命令的发送与接收,并具备丰富的配置和管理能力,方便用户对设备进行监控和控制。通过其网络诊断和错误处理功能,主站能够快速识别和解决通信问题,提高设备的可靠性和稳定性。 ### 回答2: Moons的CANopen主站是一款先进的控制系统,用于管理和控制CANopen规约的设备网络。CANopen是一种广泛应用于工业领域的通信协议,能够实现设备之间的相互通信和数据交换。 Moons的CANopen主站具有以下特点和功能: 1. 灵活性:它支持多种CANopen网络拓扑结构,如主-从结构、多主结构等,能够适应不同的应用场景和网络配置。 2. 可扩展性:它可以连接多个CANopen从站设备,实现对这些设备的集中管理和控制,适用于大规模设备网络的场景。 3. 高性能:它使用先进的通信协议和算法,能够实现高速的数据传输和响应速度,确保设备之间的实时通信和高效运行。 4. 易用性:它提供友好的用户界面和配置工具,帮助用户轻松地进行网络配置、设备管理和参数设置,提高了设备的可操作性和易用性。 5. 可靠性:它具有自动诊断和故障检测功能,能够监测网络中的错误和异常情况,并及时采取相应的措施,提高了系统的可靠性和稳定性。 总之,Moons的CANopen主站是一款功能强大、性能优越的控制系统,能够实现对CANopen网络中的设备进行集中管理和控制,加强设备之间的通信和协同工作,提高了工业生产的效率和可靠性。 ### 回答3: moons是一个生产CanOpen主站设备的公司。CanOpen是一种面向现场总线的通信协议,常用于工业自动化领域。CanOpen主站是指能够通过CanOpen协议与从站(例如传感器、执行器等设备)进行通信的控制器或设备。 moons的CanOpen主站是一款功能强大、性能稳定的设备。它具有以下特点和优势: 首先,moons的CanOpen主站具有高度的兼容性。它支持与各种CanOpen从站设备进行通信,并兼容多种CanOpen标准和版本。这使得它可以与市场上广泛使用的CanOpen设备无缝集成,方便用户进行设备的组网和通信配置。 其次,moons的CanOpen主站具有灵活的通信功能。它支持多通道的连接,能够同时与多个CanOpen从站进行通信。同时,它支持多种通信速率和数据传输模式的配置,可以根据实际情况进行灵活调整,以满足不同应用场景的需求。 此外,moons的CanOpen主站还具有高度可靠的通信性能。它采用高性能的通信芯片和可靠的通信协议,能够实时监控从站设备的状态和数据,并能够进行快速响应和处理。这样可以保证通信的稳定性和可靠性,确保数据的准确传输和设备的正常工作。 最后,moons的CanOpen主站还提供了丰富的配置和监控功能。它具有友好的人机界面和易于操作的配置工具,用户可以通过简单的设置和调整来实现对主站和从站设备的配置和监控。同时,它还支持故障诊断和报警功能,能够及时发现和处理通信故障和异常情况。 总之,moons的CanOpen主站是一款功能强大、性能稳定的设备,能够满足工业自动化领域对CanOpen通信的需求。它的高兼容性、灵活性、可靠性和丰富的配置功能,使得用户可以方便地实现设备的组网和通信控制,提高工作效率和生产质量。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

~晓广~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值