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