调用.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 代码

cs 复制代码
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

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中访问

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

cs 复制代码
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

相关推荐
MasterNeverDown5 小时前
.net 微服务jeager链路跟踪
微服务·架构·.net
板栗栗-711 小时前
从根源破解“找不到 vcruntime140.dll 无法执行”问题:原因分析、安全修复工具推荐及预防指南
安全·dll·dll修复工具·dll修复·dll错误
喵叔哟13 小时前
51.【.NET8 实战--孢子记账--从单体到微服务--转向微服务】--新增功能--登录注册扩展
数据库·微服务·.net
时光追逐者14 小时前
.NET 使用 CsvHelper 快速读取和写入 CSV 文件
c#·.net·.net core·csv
界面开发小八哥1 天前
文档控件DevExpress Office File API v25.1新本亮点:重磅升级各类API
c#·.net·界面控件·devexpress·ui开发
追逐时光者2 天前
.NET 使用 CsvHelper 快速读取和写入 CSV 文件
后端·.net
Kookoos2 天前
差分隐私在运营指标:ABP 的 DP 计数器与噪声预算
.net·差分隐私·abp vnext·拉普拉斯机制·隐私预算
蒋星熠2 天前
.NET技术深度解析:现代企业级开发指南
人工智能·python·深度学习·微服务·ai·性能优化·.net
板栗栗-73 天前
设计软件启动失败?“找不到vcruntime140.dll,无法继续执行代码” 场景化解决方案来了
dll·dll修复·dll错误·dll缺失·dll解决工具
我是唐青枫3 天前
从 Skip Take 到 Keyset:C# 分页原理与实践
开发语言·c#·.net