昨天说到自己会更新自己这几个月对于WPF的学习过程和一些项目实际开发所学习的东西,两个星期前换了工作,没想到现在来到新的XX公司,找我就是为了做WPF,但是一来到项目组,做的第一个任务,其实本身和WPF关系不大,想来想去还是觉得就放在WPF博客园分类这一块吧,毕竟这是缘分嘛,我将倒序自己学习WPF过程和一些小总结
今天先说说前几天做的关于外部设备的一些内容吧。。。。。。
昨天说到自己会更新自己这几个月对于WPF的学习过程和一些项目实际开发所学习的东西,两个星期前换了工作,没想到现在来到新的XX公司,找我就是为了做WPF,但是一来到项目组,做的第一个任务,其实本身和WPF关系不大,想来想去还是觉得就放在WPF博客园分类这一块吧,毕竟这是缘分嘛,我将倒序自己学习WPF过程和一些小总结
今天先说说前几天做的关于外部设备的一些内容吧。。。。。。
转载于:https://www.cnblogs.com/zhengrunqiang/p/3468724.html
原文地址:http://blog.csdn.net/zhengmeifu/article/details/7192823
1.USB 设备硬件部分
a.这个硬件的标识是用的 Vender ID 和 Product ID, 即“厂家标识”和“产品标识”
b.这个硬件规定了各个 End Point (端点) 的性质, 读/写 及 类型 (Control/Interrupt/Bulk/Isochronous)
c.这个硬件的固件里面有 DeviceIoControl 的实现部分, 规定了这个函数的具体参数和动作2.USB 设备驱动
①硬件接口
a.需要识别 Vender ID 和 Product ID
b.对每个 EndPoint 的每个 I/O 分配一个 Pipe, 并且起一个名字作为软件接口
c.做 DeviceIoControl 的接口
②软件接口
a.GUID, 驱动程序的标识, 每个驱动程序使用不同的 GUID, GUID 是识别驱动的, 与硬件无关 (驱动程序升级版本 GUID 不能修改)
b.硬件接口里面的 b: Pipe 名字是软件接口, 这个 Pipe 名字纯粹由驱动定义的, 和硬件无关, 升级驱动不能改 Pipe 的名字
c.硬件接口里面的 c 的各个参数也是软件的接口, 这些参数是由硬件带来的, 不是驱动规定的, 当然也可以在驱动里面转义, 隐藏设备的真实情况
③这个驱动程序是用 WinDDK 编译的, 可以用文本编辑器或其他开发工具的编辑器编程序代码, 然后调用 WinDDK 编译3.读写 USB 口的程序
①与驱动的接口
a.利用驱动程序里面的 GUID 找出设备的文件名, 用 CreateFile 函数打开设备。我前面的程序里面的 OpenUsbDevice 就是这个作用
b.通过 a.得到的设备文件名和驱动程序里面的 Pipe 名打开 Pipe, 访问这个 Pipe 对应的 USB 端点 (读写数据)
c.使用 a.的 CreateFile 得到的句柄, 通过 DeviceIoControl 实现设备规定的动作
②有关需要的资料
a.Vender ID, Product ID 和 GUID 一般在驱动程序的 .inf 文件里面能看到, 如果找不到就需要和厂家联系
b.Pipe 的名字是驱动程序规定的, 需要有驱动程序的资料才能知道
c.DeviceIoControl 的参数需要有驱动程序的资料或者硬件资料才能知道
③这个程序一般用 C/C++ 直接编写, 如果使用其他语言(VB/PB等)需要调用 C/C++ 编的 DLL
其他相关内容:USB 驱动程序可以到注册表里面找到:
"HKEY_LOCAL_MACHINE//SYSTEM//ControlSet001//Enum//USB//Vid_厂家标识&Pid_产品标识//驱动程序"里面的 ClassGUID 就是驱动程序的 GUID 标识, 例如 {36FC9E60-C465-11CF-8056-444553540000}
相当于程序的: DEFINE_GUID(USB_DRIVER_GUID, 0x36FC9E60,0xC465,0x11CF,0x80,0x56,0x44,0x45,0x53,0x54,0x00,0x00);
另外在这个注册表键里面还可找到有关设备的其他描述, 例如 DeviceDesc = "USB Mass Storage Device" 等
现在好多设备都有USB接口,在Android系统的手机或者开发板上可以实现通过USB来控制设备。一般手机上没有USB接口,需要使用OTG功能的转接线扩展出USB母口;一般Android开发板上带有USB口就可以直接使用了。
USB连接中需要知道对应USB设备的vendorId(设备厂商Id)和productId(设备产品Id),这两个id主要是用于连接和过滤设备。那么下面我们就先来看下拿到设备怎样找设备的vendorId和productId:
(1)先将设备通过USB口连接到电脑上;
(2)我的电脑->管理->设备管理器->找到链接的usb设备->右键属性->详细信息->硬件ID->其中的VID就是厂商id、PID是产品id。如下图所示:
这个ID是16进制的,需要转换成10进制的在 xml---device_filter.xml文件里配置。
在res目录下新建文件夹,名字是xml,然后再xml文件夹里新建device_filter.xml文件
<?xml version="1.0" encoding="utf-8"?> <resources xmlns:android="http://schemas.android.com/apk/res/android"> <!--设备过滤文件--> <!-- devices vendor-id= 0x1F3A product-id=0x1007--> <usb-device vendor-id="7994" product-id="4103"/> </resources>
这里的每一个usb-device对应一个usb设备;如果应用需要对多个设备使用,这里可以添加多个usb-device对应的usb设备的vendor-id和product-id;如果应用可以对同一个厂商的所有设备使用,这里可以只写vendor-id(个人是这么理解的)。
前面的准备工作做好了就可以进行下面的开发了:
(1)在AndroidManifest.xml中申明USB权限:
<!-- 声明使用usb --> <uses-feature android:name="android.hardware.usb.host" android:required="true" />
(2)在AndroidManifest.xml中对应Activity中添加下面内容:
<!--获取USB操作的通知--> <intent-filter> <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"/> </intent-filter> <!-- android设备的信息过滤 --> <meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" android:resource="@xml/usb_xml"/>
通过设备的vendorId(设备厂商Id)和productId(设备产品Id)找到设备
/** * 找到自定设备 */ public UsbDevice getUsbDevice(int vendorId, int productId) { //1)创建usbManager if (usbManager == null) usbManager = (UsbManager) mContext.getSystemService(Context.USB_SERVICE); //2)获取到所有设备 选择出满足的设备 HashMap<String, UsbDevice> deviceList = usbManager.getDeviceList(); Iterator<UsbDevice> deviceIterator = deviceList.values().iterator(); while (deviceIterator.hasNext()) { UsbDevice device = deviceIterator.next(); Log.e(TAG, "vendorID--" + device.getVendorId() + "ProductId--" + device.getProductId()); if (device.getVendorId() == vendorId && device.getProductId() == productId) { return device; // 获取USBDevice } } statue = USBContent.usb_find_this_fail; return null; }
查找所有连接的USB设备,与上面的通过id查找设备 两者根据实际情况来选用。
/** * 查找本机所有的USB设备 */ public List<UsbDevice> getUsbDevices() { //1)创建usbManager if (usbManager == null) usbManager = (UsbManager) mContext.getSystemService(Context.USB_SERVICE); //2)获取到所有设备 选择出满足的设备 HashMap<String, UsbDevice> deviceList = usbManager.getDeviceList(); Iterator<UsbDevice> deviceIterator = deviceList.values().iterator(); //创建返回数据 List<UsbDevice> lists = new ArrayList<>(); while (deviceIterator.hasNext()) { UsbDevice device = deviceIterator.next(); Log.e(TAG, "vendorID--" + device.getVendorId() + "ProductId--" + device.getProductId()); lists.add(device); } return lists; }
连接USB设备
/** * 根据指定的vendorId和productId连接USB设备 * * @param vendorId 产商id * @param productId 产品id */ public int connection(int vendorId, int productId) { usbDevice = getUsbDevice(vendorId, productId); //3)查找设备接口 if (usbDevice == null) { Log.e(TAG, "未找到目标设备,请确保供应商ID" + vendorId + "和产品ID" + productId + "是否配置正确"); return statue; } UsbInterface usbInterface = null; for (int i = 0; i < usbDevice.getInterfaceCount(); i++) { //一个设备上面一般只有一个接口,有两个端点,分别接受和发送数据 usbInterface = usbDevice.getInterface(i); Log.e("USBHelper","usbInterface.getEndpointCount()="+usbInterface.getEndpointCount()); break; } //4)获取usb设备的通信通道endpoint for (int i = 0; i < usbInterface.getEndpointCount(); i++) { UsbEndpoint ep = usbInterface.getEndpoint(i); switch (ep.getType()) { case UsbConstants.USB_ENDPOINT_XFER_BULK://USB端口传输 if (UsbConstants.USB_DIR_OUT == ep.getDirection()) {//输出 epBulkOut = ep; Log.e(TAG, "获取发送数据的端点"); } else { epBulkIn = ep; Log.e(TAG, "获取接收数据的端点"); } break; case UsbConstants.USB_ENDPOINT_XFER_CONTROL://控制端点 epControl = ep; Log.e(TAG, "find the ControlEndPoint:" + "index:" + i + "," + epControl.getEndpointNumber()); break; case UsbConstants.USB_ENDPOINT_XFER_INT://中断端点 if (ep.getDirection() == UsbConstants.USB_DIR_OUT) {//输出 epIntEndpointOut = ep; Log.e(TAG, "find the InterruptEndpointOut:" + "index:" + i + "," + epIntEndpointOut.getEndpointNumber()); } if (ep.getDirection() == UsbConstants.USB_DIR_IN) { epIntEndpointIn = ep; Log.e(TAG, "find the InterruptEndpointIn:" + "index:" + i + "," + epIntEndpointIn.getEndpointNumber()); } break; default: break; } } //5)打开conn连接通道 if (usbManager.hasPermission(usbDevice)) { //有权限,那么打开 conn = usbManager.openDevice(usbDevice); } else {//没有权限 usbManager.requestPermission(usbDevice, intent);// 先去获取权限 if (usbManager.hasPermission(usbDevice)) { // 权限获取成功 conn = usbManager.openDevice(usbDevice); } else { Log.e(TAG, "USB授权失败"); statue = USBContent.usb_permission_fail; } } if (null == conn) { Log.e(TAG, "不能连接到设备"); statue = USBContent.usb_open_fail; return statue; } //打开设备 if (conn.claimInterface(usbInterface, true)) { if (conn != null)// 到此你的android设备已经连上设备 Log.e(TAG, "open设备成功!"); final String mySerial = conn.getSerial(); Log.e(TAG, "设备serial number:" + mySerial); statue = USBContent.usb_ok; } else { Log.e(TAG, "无法打开连接通道。"); statue = USBContent.usb_passway_fail; conn.close(); } return statue; }
发送数据
/** * 通过USB发送数据 */ public void sendData(byte[] buffer) { if (conn == null || epBulkOut == null) return; int res = conn.bulkTransfer(epBulkOut, buffer, buffer.length, 1000); Log.e(TAG, "res="+res); if (res >= 0) { //0 或者正数表示成功 Log.e(TAG, "发送成功"); statue = USBContent.usb_permission_ok; } else { Log.e(TAG, "发送失败的"); statue = USBContent.usb_permission_fail; } }
使用完关闭USB连接
/** * 关闭USB连接 */ public void close() { if (conn != null) { //关闭USB设备 conn.close(); conn = null; } if (mContext != null && broadcastReceiver != null) { mContext.unregisterReceiver(broadcastReceiver); } }
到这里USB的连接、通过USB向硬件设备发送数据就完成了,在此做下记录,方便以后参考使用。大家如果看到了此文章,如果发现哪里有问题欢迎指正。(本人也是刚刚接触USB通信,对USB通信了解有限,上面这个是用到了亲自试验的记录一下)
感谢参考文章:https://blog.csdn.net/u013057253/article/details/82725585
还有一篇USB协议基本知识文章个人感觉写的也不错,对刚接触USB的会有帮助,附上个链接https://blog.csdn.net/u010142953/article/details/82627591
ros项目使用usb接口串口通讯的传感器时,我们都希望有个固定的设备名供程序调用。
由于usb为即插即用设备,节点注册的ID不同,会导致设备名称会不同。
linux系统下如何让系统绑定固有的usb设备,使用设定好的设备名称?如何永久修改设备权限?
解决办法
编写规则文件,放到系统配置路径下,进行设置后,即可一劳永逸。
规则配置路径:
/etc/udev/rules.d/
规则文件以.rules作为扩展名。由于这些文件有优先级,所以一般在前面加上一个数字,系统按照数字进行排序,于是数字小的排前面优先级比较高。
规则格式:
KERNEL=="ttyUSB*", ATTRS{idVendor}=="xxxx", ATTRS{idProduct}=="xxxx", MODE:="0777", SYMLINK+="device_name"
其中
ATTRS{idVendor}=="xxxx"
ATTRS{idProduct}=="xxxx" 从查询到的结果里进行配置
规则文件的写法可以看这篇文章http://blog.csdn.net/smfwuxiao/article/details/7640614
查询命令
lsusb -vvv
查看自己设备的idVendor和idProduct
规则文件配置完成后
执行命令
service udev reload
service udev restart
查看效果
ls /dev/device_name
然后在ros里的launch文件参数可以固定为 /dev/device_name
例如创建规则
80-zzz-chaosheng.rules
其内容为
KERNEL=="ttyUSB?", ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="7523", MODE="0666", SYMLINK+="zzz_chaoshenglidar"
注意1a86 7523 是输入的我查询的结果,需要根据自己硬件的实际情况填写
SYMLINK+=zzz_chaoshenglidar 就是自定义的设备名称
ros的launch文件 serial_port参数定义为/dev/zzz_chaoshenglidar可以实现对相应硬件的调用设置,而不必插拔usb后在查看到底是那个端口号反复修改launch文件。<launch> <node name="hcsr04_serial_lidar" pkg="chaosheng_lidar" type="hcsr04_serial_lidar" output="screen"> <param name="serial_port" type="string" value="/dev/zzz_chaoshenglidar"/> <param name="serial_baudrate" type="int" value="115200"/> <param name="frame_id" type="string" value="laser"/> <param name="inverted" type="bool" value="false"/> <param name="angle_compensate" type="bool" value="true"/> </node> </launch>
开源riki底盘树莓派中系统使用的规则配置示例:
58-riki.rules
KERNEL=="ttyACM?", SUBSYSTEM=="tty", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="0483", ATTRS{serial}=="4294967295", MODE="0660" SYMLINK+="rikibase"
KERNEL=="ttyUSB?", SUBSYSTEM=="tty", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60", ATTRS{serial}=="0001", MODE="0660" SYMLINK+="rikilidar"
KERNEL=="ttyUSB?", SUBSYSTEM=="tty", ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="7523", MODE="0660" SYMLINK+="rikistm32base"
49-teensy.rules
ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789B]?", ENV{ID_MM_DEVICE_IGNORE}="1"
ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789A]?", ENV{MTP_NO_PROBE}="1"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789ABCD]?", MODE:="0666"
KERNEL=="ttyACM*", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789B]?", MODE:="0666"
10-local-rpi.rules
SUBSYSTEM=="vchiq", GROUP="video", MODE="0660"
40-scratch.rules
ATTRS{idVendor}=="0694", ATTRS{idProduct}=="0003", SUBSYSTEMS=="usb", ACTION=="add", MODE="0666", GROUP="plugdev"
99-com.rules
SUBSYSTEM=="input", GROUP="input", MODE="0660"
SUBSYSTEM=="i2c-dev", GROUP="i2c", MODE="0660"
SUBSYSTEM=="spidev", GROUP="spi", MODE="0660"
SUBSYSTEM=="bcm2835-gpiomem", GROUP="gpio", MODE="0660"
SUBSYSTEM=="gpio*", PROGRAM="/bin/sh -c '\
chown -R root:gpio /sys/class/gpio && chmod -R 770 /sys/class/gpio;\
chown -R root:gpio /sys/devices/virtual/gpio && chmod -R 770 /sys/devices/virtual/gpio;\
chown -R root:gpio /sys$devpath && chmod -R 770 /sys$devpath\
'"
KERNEL=="ttyAMA[01]", PROGRAM="/bin/sh -c '\
ALIASES=/proc/device-tree/aliases; \
if cmp -s $ALIASES/uart0 $ALIASES/serial0; then \
echo 0;\
elif cmp -s $ALIASES/uart0 $ALIASES/serial1; then \
echo 1; \
else \
exit 1; \
fi\
'", SYMLINK+="serial%c"