调用.net DLL让CANoe自动识别串口号

1.前言

CANoe9.0用CAPL控制数控电源_canoe读取程控电源电流值-CSDN博客

之前做CAPL通过串口控制数控电源,存在一个缺点:更换电脑需要改串口号

CSDN上有类似的博客,不过要收费,本文根据VID和PID来自动获取串口号,代码少,使用起来更方便

本文可以告诉大家

(1)C#中如何根据VID和PID来获取串口号

(2)CAPL如何调用C#的DLL

(3)如何获取设备的VID和PID

(4)如何创建C#的DLL

2.开发环境

2.1硬件环境

科睿源 KA3005P

2.2软件环境

Win10 + CANoe12.0 + VS2013

3.参考资料

CANoe Help文档

4.自动识别串口原理

4.1方案一

枚举所有串口,分别询问每个串口是否是指定设备,优点是通用,缺点是速度慢

4.2方案二

根据设备的VID和PID获取串口号,优点是速度快,缺点是只适合USB转串口,如果存在多个同类设备,仍然需要每个询问

由于KA3005P是USB接口的虚拟串口,因此我这里选择方案二

5.创建C# DLL

5.1 注意事项

5.2 创建DLL工程

5.3 代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.IO.Ports;
using UCANHelper;

namespace SerialPortAutoDetect
{
    public class SerialPortAutoDetect
    {
        public static int GetAllPorts(int[] ports, int maxLength)
        {
            var port_names = SerialPort.GetPortNames();
            int i = 0;
            for (i = 0; i < ports.Length && i < maxLength; i++)
            {
                string num = port_names[i].Replace("COM", "");
                ports[i] = Convert.ToInt32(num);
            }
            return i;
        }
        public static int GetPortsByVidPid(int[] ports, int maxLength, ushort vid, ushort pid)
        {
            List<string> names = USB_Help.ComPortNames(vid.ToString("X4"), pid.ToString("X4"));
            int i = 0;
            for (i = 0; i < names.Count && i < maxLength; i++)
            {
                string num = names[i].Replace("COM", "");
                ports[i] = Convert.ToInt32(num);
            }
            return i;
        }
    }
}

USB_Help.cs

using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace UCANHelper
{
    class USB_Help
    {
        #region 根据VID PID通过注册表获取端口号
        public static List<string> ComPortNames(String VID, String PID)
        {//https://cloud.tencent.com/developer/ask/sof/115144954
            RegistryKey rk1 = Registry.LocalMachine;
            RegistryKey rk2 = rk1.OpenSubKey("SYSTEM\\CurrentControlSet\\Enum");

            String pattern = String.Format("^VID_{0}.PID_{1}", VID, PID);
            Regex _rx = new Regex(pattern, RegexOptions.IgnoreCase);
            List<string> ports = new List<string>();
            string[] pc_com_ports = SerialPort.GetPortNames();
            foreach (String s3 in rk2.GetSubKeyNames())
            {
                RegistryKey rk3 = rk2.OpenSubKey(s3);
                foreach (String s in rk3.GetSubKeyNames())
                {
                    if (_rx.Match(s).Success)
                    {
                        RegistryKey rk4 = rk3.OpenSubKey(s);
                        foreach (String s2 in rk4.GetSubKeyNames())
                        {
                            RegistryKey rk5 = rk4.OpenSubKey(s2);
                            RegistryKey rk6 = rk5.OpenSubKey("Device Parameters");
                            string port_name = (string)rk6.GetValue("PortName");
                            if (port_name != null && pc_com_ports.Contains<string>(port_name))
                            {
                                ports.Add(port_name);
                            }
                        }
                    }
                }
            }
            return ports;
        }
        #endregion
    }
}

编译,生成SerialPortAutoDetect.dll

6.获取设备的VID和PID

在设备管理器中,找到端口,右击属性

7.CAPL中访问

仅有初始化部分,其他的参考之前的博客

includes
{
  #pragma netlibrary("..\DLL\SerialPortAutoDetect.dll")
}

variables
{
  // GLOBAL
  const int kBUFFER_SIZE = 1000;
  const int kINFO        = 1;
  const int kWARN        = 2;
  const int kERROR       = 3;
  
  const int kHANDSHAKE_DISABLED = 0;
  const int kHANDSHAKE_RTSCTS   = 33;
  
  // define for dp serial port com9
  dword port = 6;
  const dword baudrate = 9600;
  const dword dataBits = 8;
  const dword stopBits = 1;
  const dword parity = 0;//0:none 1:even 0:odd
   
  // data is copied from callback buffer to gReceiverBuffer (collects data)
  byte gReceiverCallbackBuffer[kBUFFER_SIZE];
  
  byte gReceivedBuffer[kBUFFER_SIZE];
  dword gReceivedIndex= 0;
  
  // state variable
  byte gSending = 0;
  
  byte gGetValueSt = 0;
  byte gSetValueSt = 0;
  
  msTimer t100ms;
  msTimer t20ms;
  
  dword vid = 0x0416;
  dword pid = 0x5011;
}

on preStart
{
  InitSerialPort();  
}

on start
{
  setTimer(t100ms,100);
}
//RS232 Init
InitSerialPort()
{
  long ports[10];
  long count=0;
  count=SerialPortAutoDetect::SerialPortAutoDetect::GetPortsByVidPid(ports,10,vid,pid);
  if(count > 0)
  {
    port=ports[0];
    writeLineEx(0,kINFO,"Find %d serial port, use first serial port: %d.", count, port);
  }
  else
  {
    writeLineEx(0,kINFO,"Can not find any serial port, use default serial port %d.", port);
  }    
  // close serial port (port may have changed, former port shall not remain open)
  if(Rs232Close(port)!=1)
    writeLineEx(0,kERROR,"An error occurred during closing of the serial port %d.", port);    

  // set state (close aborts all open requests)
  gSending = 0;

  // open the serial port (comes up with Windows defaults)
  if(Rs232Open(port)==1)
    writeLineEx(0,kINFO, "Serial port %d successfully opened.", port);    
  else
    writeLineEx(0,kERROR,"An error occurred during opening of the serial port %d.", port);    

  // configure the serial port
  // - just take the panel content
  if(Rs232Configure(port,baudrate,dataBits,stopBits,parity)==1)
    writeLineEx(0,kINFO, "Serial port %d successfully initialized.", port);    
  else
    writeLineEx(0,kERROR,"An error occurred during initialization of the serial port %d.", port);    
  
  // port, handshake, xonLim, xoffLim, xonChar, xoffChar, writeTimeout
  // without last timeout parameter: use default timeout
  // for transmission of small amounts of data one may not need to use handshake ! 
  // e.g. 33 for RTS/CTS as second parameter for large volumes of data, 0 for small volumes
  if(Rs232SetHandshake(port, kHANDSHAKE_DISABLED, 0, 0, 0, 0))
    writeLineEx(0,kINFO, "Handshake parameters for serial port %d successfully configured.", port);    
  else
    writeLineEx(0,kERROR,"An error occurred during the serial port %d configuration of handshake parameters.", port);

  // set buffer for reception (otherwise callback would not work)
  if(Rs232Receive(port, gReceiverCallbackBuffer, kBUFFER_SIZE))
    writeLineEx(0,kINFO, "Receiver buffer for serial port %d successfully set.", port);    
  else
    writeLineEx(0,kERROR,"An error occurred during setting the receiver buffer for serial port %d.", port);
}

8.测试

可以正确识别到串口3

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值