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/115144954RegistryKey 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
{// GLOBALconst 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 com9dword 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 variablebyte 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); elsewriteLineEx(0,kERROR,"An error occurred during opening of the serial port %d.", port); // configure the serial port// - just take the panel contentif(Rs232Configure(port,baudrate,dataBits,stopBits,parity)==1)writeLineEx(0,kINFO, "Serial port %d successfully initialized.", port); elsewriteLineEx(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 volumesif(Rs232SetHandshake(port, kHANDSHAKE_DISABLED, 0, 0, 0, 0))writeLineEx(0,kINFO, "Handshake parameters for serial port %d successfully configured.", port); elsewriteLineEx(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); elsewriteLineEx(0,kERROR,"An error occurred during setting the receiver buffer for serial port %d.", port);
}
8.測試
可以正確識別到串口3