C#开发usb通知之bulk传输
usb通信分为4种传输方式,下位机通信协议用的是块传输,也就是bulk传输,C#下实现的usb通信使用的是开源的LibUsbDotNet,主要的就是需要在C#中添加LibUsbDotNet.dll引用文件,安装后的LibUsbDotNet里面有
我是参考bulk传输实例,这个需要libusb-1.0.dll文件放在程序的根目录下或者在windows/system32/目录下,否则会报错,提示找不到这个文件:
using System; using System.Runtime.InteropServices; using MonoLibUsb.Transfer; using Usb = MonoLibUsb.MonoUsbApi; namespace MonoLibUsb.ShowInfo { internal enum TestMode { Sync, Async } internal class BulkReadWrite { #region DEVICE SETUP private const int MY_CONFIG = 1; private const byte MY_EP_READ = 0x81;//读端点口地址 private const byte MY_EP_WRITE = 0x01;//写端点口地址 private const int MY_INTERFACE = 0;//接口地址 private const short MY_PID = 0x0053;//设备的VID PID信息 private const short MY_VID = 0x04d8; #endregion #region TEST SETUP private const int MY_TIMEOUT = 2000;//读写超时时间 private const int TEST_LOOP_COUNT = 1; private const int TEST_READ_LEN = 64; private const bool TEST_REST_DEVICE = true; private const int TEST_WRITE_LEN = 8; private static TestMode TEST_MODE = TestMode.Async; #endregion private static MonoUsbSessionHandle sessionHandle = null; private static void fillTestData(byte[] data, int len) { int i; for (i = 0; i < len; i++) data[i] = (byte) (65 + (i & 0xf)); } private static void memset(byte[] data, int value, int len) { int i; for (i = 0; i < len; i++) data[i] = (byte) (value); } // This function originated from bulk_transfer_cb() // in sync.c of the Libusb-1.0 source code. private static void bulkTransferCB(MonoUsbTransfer transfer) { Marshal.WriteInt32(transfer.PtrUserData, 1); /* caller interprets results and frees transfer */ } // This function originated from do_sync_bulk_transfer() // in sync.c of the Libusb-1.0 source code. private static MonoUsbError doBulkAsyncTransfer(MonoUsbDeviceHandle dev_handle, byte endpoint, byte[] buffer, int length, out int transferred, int timeout) { transferred = 0; MonoUsbTransfer transfer = new MonoUsbTransfer(0); if (transfer.IsInvalid) return MonoUsbError.ErrorNoMem; MonoUsbTransferDelegate monoUsbTransferCallbackDelegate = bulkTransferCB; int[] userCompleted = new int[] {0}; GCHandle gcUserCompleted = GCHandle.Alloc(userCompleted, GCHandleType.Pinned); MonoUsbError e; GCHandle gcBuffer = GCHandle.Alloc(buffer, GCHandleType.Pinned); transfer.FillBulk( dev_handle, endpoint, gcBuffer.AddrOfPinnedObject(), length, monoUsbTransferCallbackDelegate, gcUserCompleted.AddrOfPinnedObject(), timeout); e = transfer.Submit(); if ((int) e < 0) { transfer.Free(); gcUserCompleted.Free(); return e; } int r; Console.WriteLine("Transfer Submitted.."); while (userCompleted[0] == 0) { e = (MonoUsbError) (r = Usb.HandleEvents(sessionHandle)); if (r < 0) { if (e == MonoUsbError.ErrorInterrupted) continue; transfer.Cancel(); while (userCompleted[0] == 0) if (Usb.HandleEvents(sessionHandle) < 0) break; transfer.Free(); gcUserCompleted.Free(); return e; } } transferred = transfer.ActualLength; e = MonoUsbApi.MonoLibUsbErrorFromTransferStatus(transfer.Status); transfer.Free(); gcUserCompleted.Free(); return e; } public static int Main(string[] args) { MonoUsbDeviceHandle device_handle = null; int r = 0; int transferred; byte[] testWriteData = new byte[TEST_WRITE_LEN]; byte[] testReadData = new byte[TEST_READ_LEN]; if (args.Length > 0) { switch (args[0].ToLower()) { case "sync": TEST_MODE = TestMode.Sync; break; case "async": TEST_MODE = TestMode.Async; break; } } fillTestData(testWriteData, TEST_WRITE_LEN); memset(testReadData, 0, TEST_READ_LEN); int loopCount = 0; do { try { do { sessionHandle=new MonoUsbSessionHandle(); if (sessionHandle.IsInvalid) throw new Exception("Invalid session handle."); Console.WriteLine("Opening Device.."); device_handle = MonoUsbApi.OpenDeviceWithVidPid(sessionHandle, MY_VID, MY_PID); if ((device_handle == null) || device_handle.IsInvalid) break; // If TEST_REST_DEVICE = True, reset the device and re-open if (TEST_REST_DEVICE) { MonoUsbApi.ResetDevice(device_handle); device_handle.Close(); device_handle = MonoUsbApi.OpenDeviceWithVidPid(sessionHandle, MY_VID, MY_PID); if ((device_handle == null) || device_handle.IsInvalid) break; } // Set configuration Console.WriteLine("Set Config.."); r = MonoUsbApi.SetConfiguration(device_handle, MY_CONFIG); if (r != 0) break; // Claim interface Console.WriteLine("Set Interface.."); r = MonoUsbApi.ClaimInterface(device_handle, MY_INTERFACE); if (r != 0) break; ///////////////////// // Write test data // ///////////////////// int packetCount = 0; int transferredTotal = 0; do { Console.WriteLine("Sending test data.."); // If the Async TEST_MODE enumeration is set, use // the internal transfer function if (TEST_MODE == TestMode.Async) { r = (int)doBulkAsyncTransfer(device_handle, MY_EP_WRITE, testWriteData, TEST_WRITE_LEN, out transferred, MY_TIMEOUT); } else { // Use the sync bulk transfer API function r = MonoUsbApi.BulkTransfer(device_handle, MY_EP_WRITE, testWriteData, TEST_WRITE_LEN, out transferred, MY_TIMEOUT); } if (r == 0) { packetCount++; transferredTotal += transferred; } // Keep writing data until an error occurs or // 4 packets have been sent. } while (r == 0 && packetCount < 5); if (r == (int) MonoUsbError.ErrorTimeout) { // This is considered normal operation Console.WriteLine("Write Timed Out. {0} packet(s) written ({1} bytes)", packetCount, transferredTotal); } else if (r != (int) MonoUsbError.ErrorTimeout && r != 0) { // An error, other than ErrorTimeout was received. Console.WriteLine("Write failed:{0}", (MonoUsbError) r); break; } //////////////////// // Read test data // //////////////////// Console.WriteLine("Reading test data.."); packetCount = 0; transferredTotal = 0; do { // If the Async TEST_MODE enumeration is set, use // the internal transfer function if (TEST_MODE == TestMode.Async) { r = (int) doBulkAsyncTransfer(device_handle, MY_EP_READ, testReadData, TEST_READ_LEN, out transferred, MY_TIMEOUT); } else { // Use the sync bulk transfer API function r = MonoUsbApi.BulkTransfer(device_handle, MY_EP_READ, testReadData, TEST_READ_LEN, out transferred, MY_TIMEOUT); } if (r == (int) MonoUsbError.ErrorTimeout) { // This is considered normal operation Console.WriteLine("Read Timed Out. {0} packet(s) read ({1} bytes)", packetCount, transferredTotal); } else if (r != 0) { // An error, other than ErrorTimeout was received. Console.WriteLine("Read failed:{0}", (MonoUsbError)r); } else { transferredTotal += transferred; packetCount++; // Display test data. Console.Write("Received: "); Console.WriteLine(System.Text.Encoding.Default.GetString(testReadData, 0, transferred)); } // Keep reading data until an error occurs, (ErrorTimeout) } while (r == 0); } while (false); } finally { // Free and close resources if (device_handle != null) { if (!device_handle.IsInvalid) { MonoUsbApi.ReleaseInterface(device_handle, MY_INTERFACE); device_handle.Close(); } } if (sessionHandle!=null) { sessionHandle.Close(); sessionHandle = null; } } // Run the entire test TEST_LOOP_COUNT times. } while (++loopCount < TEST_LOOP_COUNT); Console.WriteLine("\nDone! [Press any key to exit]"); Console.ReadKey(); return r; } } }
我在winfrom的主要思路和上面差不多,但是还有有点区别的,毕竟控制端程序和桌面程序在代码上还是有点区别的。由于项目的要求只能放部分代码
1.设置相关的通信信息,和上面的差不多
//设置设备信息,包括接口、断点等信息 private void setDevice() { sessionHandle = new MonoUsbSessionHandle(); if (sessionHandle.IsInvalid) throw new Exception("Invalid session handle."); //Console.WriteLine("Opening Device.."); device_handle = MonoUsbApi.OpenDeviceWithVidPid(sessionHandle, MY_VID, MY_PID); if ((device_handle == null) || device_handle.IsInvalid) { Console.WriteLine("device_handle为空"); isContinue = false; labelInfo.Text = "未找到设备"; ScanBtn.Enabled = false; } else { //设置配置信息 r = MonoUsbApi.SetConfiguration(device_handle, MY_CONFIG); if (r != 0) { Console.WriteLine("config出错"); }; // 索赔接口 r = MonoUsbApi.ClaimInterface(device_handle, MY_INTERFACE); if (r != 0) { Console.WriteLine("interface出错"); }; labelInfo.Text = "成功打开设备"; } }
2.然后在按钮的监听器里面实现发送命令的方法:
private void sendCommand() { if (device_handle == null) { setDevice(); } byte[] data = DataFix.getCommand(interTimes, scanCounts, manual); //r = (int)doBulkAsyncTransfer(device_handle, MY_EP_WRITE, command, command.Length, out transferred, MY_TIMEOUT); r = MonoUsbApi.BulkTransfer(device_handle, MY_EP_WRITE, data, data.Length, out transferred, MY_TIMEOUT); if (r == (int)MonoUsbError.ErrorTimeout) { // 写入超时错误信息 //Console.WriteLine("Write Timed Out. {0} packet(s) written ({1} bytes)", packetCount, transferredTotal); } else if (r != (int)MonoUsbError.ErrorTimeout && r != 0) { Console.WriteLine("Write failed:{0}", (MonoUsbError)r); } resultList.Clear(); }
3.其实最终的就是第三点,你需要在程序启动的时候就开启一个新的线程,然后里面实现while(true)循环的监听端点口数据的返回
#region 开启一个新的线程 xferData = new Thread(DataGetFromPoint); xferData.IsBackground = true; xferData.Priority = ThreadPriority.Highest; xferData.Start(); #endregion
DataGetFromPoint()方法
public void DataGetFromPoint() { bufDataIn = new byte[inLength]; while (isContinue) { //Console.WriteLine("Reading data...."); r = MonoUsbApi.BulkTransfer(device_handle, MY_EP_READ, bufDataIn, inLength, out transferred, MY_TIMEOUT); if (r == (int)MonoUsbError.ErrorTimeout) { //这个可以看出是一个正常的操作 //Console.WriteLine("Read Timed Out."); } else if (r != 0) { //除了写入超时错误的信息外,其他的信息会在这里显示 Console.WriteLine("Read failed:{0}", (MonoUsbError)r); } else { Console.writeln(DataFix.byteToHexString(bufDataIn));//字节数组转换成16进制字符串显示 } //Thread.Sleep(1);//如果线程休眠的话,导致接受的数据不完整.... } }