HID 设备的双usb 触摸方案分享

针对越来越多的商显项目,由于有触摸的交互需求,那么触控方案选择也就比较重要。目前市面流行的主要
有红外框的,电容屏的。单系统(安卓 or OPS)的又分为串口,usb口。双系统(安卓 + OPS) 则有“串口+USB”,“双USB” 方案。如下:

在这里插入图片描述
其中图一,是单系统连接,比较简单,打开设备(/dev/ttySx , /dev/hidarwx ,其中<x = 0,1,2,3 … >),就可以获取到触摸数据的信息。
图二,图三,是双系统方案,一个相同点是,连接OPS 的都是USB 接口,传输过来的都是标准的HID事件,OPS 就可以直接使用,另外触摸板连接安卓主板的通道都是双向的,包含触摸数据(touch panel -> Android),指令控制数据(Android -> touch panel),到OPS端都是虚线,这个是针对通道不在OPS时,触摸数据是不会传送到OPS的,只有在OPS通道时才是通的。
另外针对,不同类型的触摸数据,处理方式不一样。标准的HID事件 OPS 可以直接支持,Android在kernel开启

+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_PROPERTIES=y
+CONFIG_HID_MULTITOUCH=y

宏之后可以支持。串口的触摸数据则需要将坐标点的属性解析出来,
虚拟成input 事件写入Android的/dev/uinput 设备达到效果,当然也可以封装成标准的HID事件向Android 的usb 口传输出去控制其他外设。

关于串口触摸数据的解析,转发可以参考:https://blog.csdn.net/kehyuanyu/article/details/97892667
下面我们分享图三的触摸方案。

触摸流程

在这里插入图片描述
如上图,touch panel连接android usb1 有两个intf分别对应两个设备节点(/dev/hidraw(N) 和/dev/hidraw(N+1)),两个设备对应的是一个传输触摸数据(/dev/hidraw(N+1)),一个发送控制指令(/dev/hidraw(N))。 总体流程就是: 触摸后,数据从touch panel 两个usb 接口流出,/dev/hidraw(N+1)中的HID事件直接到了安卓,而OPS能否收到事件取决于/dev/hidraw(N) 节点给的控制指令,在这里需要说明一下,默认情况下触摸框是直传模式,usb1 和 usb2都有数据的且是一样的, 切换到touch panel 为透传模式时,ops 收到的数据就是从/dev/hidraw(N) 转发过去的数据。如下:
在这里插入图片描述

设备ID获取

Android设备,使用自带工具lsusb 在硬件联通成功条件下既可获取到vid,pid信息
在这里插入图片描述
在OPS 端,通过bus hound 或者 usb tree,或者设备管理器都可以获取到vid,pid信息
在这里插入图片描述
连接Android 和 OPS 的两个usb 对应的vid,pid 分别是0x28e1,0xb100;0x2757, 0x0107

HID触摸协议

在这里插入图片描述
通过bus hound 工具,抓取usb2 端口的标准数模数据格式。每包64 bytes:
byte[0] 为包头,也即reportID 默认为0x02
byte[1~10] 10个字节存储一个点的属性
byte[1] 点的状态,值一般为:0x04( up event), 0x07( duwn/move event)
byte[2] 为点的ID,第一个为0x00,后面依次类推
byte[3],byte[4] 为点x 坐标的地位和高位, 也即 (byte[3] & 0xFF) | (byte[4] << 8 & 0xFF00)
byte[5],byte[6] 为点y 坐标的地位和高位, 也即 (byte[5] & 0xFF) | (byte[6] << 8 & 0xFF00)
byte[7],byte[8] 为点x 宽度的地位和高位, 也即 (byte[7] & 0xFF) | (byte[8] << 8 & 0xFF00)
byte[9],byte[10] 为点y 宽度的地位和高位, 也即 (byte[9] & 0xFF) | (byte[10] << 8 & 0xFF00)
byte[11 ~ 20] 为第二点的属性,没有时填充0x00
byte[21 ~ 30] 为第三点的属性,没有时填充0x00
byte[31 ~ 40] 为第四点的属性,没有时填充0x00
byte[41 ~ 50] 为第五点的属性,没有时填充0x00
byte[51 ~ 60] 为第六点的属性,没有时填充0x00
byte[61~62] 为一般为校验码,具体有触摸框供应商提供;
byte[63] 为总共点的个数
了解了协议我们就可以操控设备了

1. 打开设备

BOOL MSrv_DualUsbTouch::openDualUsbDev()
{
	int nRet = -1;
	int nIndex = 0;
	int nCount = 0;
	int nHidDevHandle = -1;
	char devPath[HID_PATH_SIZE];
	struct hidraw_devinfo info;
	BOOL bFindWriteHandle = FALSE;
	BOOL bFindReadHandle  = FALSE;
	for(nIndex = 0; nIndex < HID_PATH_COUNT; nIndex++)
	{
		memset(devPath,0x00,sizeof(devPath));
		memset(&info,0x00,sizeof(info));
		snprintf(devPath, sizeof(devPath), "%s%d", HID_DEV_PATH, nIndex);
		nHidDevHandle = open(devPath , O_RDWR);
		if(nHidDevHandle < 0)
		{
			dbg_info("open %s failed.", devPath);
			close(nHidDevHandle);
			continue;
		}
		else
		{
			dbg_warn("open %s succuss, handle : 0x%x.\n", devPath,nHidDevHandle);
		}

		nRet = ioctl(nHidDevHandle, HIDIOCGRAWINFO , &info);
		if(nRet < 0)
		{
			dbg_err("get hid dev [%s] info failed, nRet = %d.",devPath,nRet);
			close(nHidDevHandle);
			continue;
		}

		if(m_HidDev_Vendor_id == info.vendor && m_HidDev_Product_id == (info.product & 0xFFFF) && (nCount < 2))
		{
			if(!bFindWriteHandle)
			{
				m_HidDevWriteHandle = nHidDevHandle;
				dbg_warn("find the hid dev [%s] ==> vid&pid -> [%x:%x], write-handle = 0x%x.", devPath, info.vendor , \
							info.product&0xFFFF,nHidDevHandle);
				bFindWriteHandle = TRUE;
				continue;
			}

			if((bFindWriteHandle) && (!bFindReadHandle))
			{
				m_HidDevReadHandle = nHidDevHandle;
				dbg_warn("find the hid dev [%s] ==> vid&pid -> [%x:%x], read-handle = 0x%x .", devPath, info.vendor , \
							info.product&0xFFFF,nHidDevHandle);
				bFindReadHandle = TRUE;
				break;
			}
		}
	}

	if(bFindWriteHandle && bFindReadHandle && (m_HidDevReadHandle > 0) && (bFindWriteHandle > 0))
	{
		return TRUE;
	}
	else
	{
		dbg_err("can`t find the hid dev [vid&pid -> %x:%x], read-handle = 0x%x , write-handle = 0x%x .", \
			m_HidDev_Vendor_id , m_HidDev_Product_id , m_HidDevReadHandle,m_HidDevWriteHandle);
		return FALSE;		
	}
}

遍历/dev/hidrawX 设备节点,读取设备属性HIDIOCGRAWINFO,匹配m_HidDev_Vendor_id, m_HidDev_Product_id ,这里也即是0x28e1,0xb100。 打开后可以拿到句柄。

2. 配置usb模式

BOOL MSrv_DualUsbTouch::setupForwardMode()
{
	int nRet  = 0;
	int nLoop = 0;
	if(m_HidDevWriteHandle <= 0 || m_HidDevReadHandle <= 0)
	{
		dbg_err("hid dev was non inited, continue to init !!!\n");
		goto reopen;
	}

setup:
	if(m_HidDevWriteHandle > 0)
	{
		memset(s_CmdBuf,0x00,sizeof(s_CmdBuf));
		s_CmdBuf[0] = 0x06; 
		s_CmdBuf[1] = 0xA8;
		s_CmdBuf[2] = 0x01;
		s_CmdBuf[3] = 0x00;
		write_lock();
		nRet = write(m_HidDevWriteHandle,s_CmdBuf,BUFF_MAX);
		write_unlock();
	}

	if(nRet > 0)
	{
		dbg_info("setup forward mode succuss, nRet = %d. handle = 0x%x. \n",nRet,m_HidDevWriteHandle);
		m_current_DataMode = EM_FORWARD;
		return TRUE;
	}
	else
	{
		dbg_err("write %d to slave usb, handle = 0x%x. \n",nRet,m_HidDevWriteHandle);
		if(nLoop > 5)
		{
			return FALSE;
		}
		nLoop++;
		usleep(nLoop*500*1000);
		goto reopen;
	}

reopen:
	cleanUpTheDev();
	openDualUsbDev();
	goto setup;

}

配置转发模式,指令为“0x06 0xA8 0x01 0x00,后面加60 个字节的0x00” 。此处是往/dev/hidrawN 里面写入指令切换触摸框的工作模式为转发模式。此处加了一些reopen 的处理,主要是针对硬件不稳定,导致hidrawN 设备文件经常发生变化的问题。如果需要切换为直发模式(usb1,usb2都收到触摸框同样的HID数据)
只需要将s_CmdBuf[2] = 0x01改成s_CmdBuf[2] = 0x00即可。

3. 获取HID数据

int  MSrv_DualUsbTouch::recvTouchData(uint8_t* buf, uint8_t nLen)
{
	int nRet = 0;
	if (NULL == buf || nLen <= 0)
	{
		dbg_err("param error! \n");
		return -1;
	}
	nRet = read(m_HidDevReadHandle, buf, nLen);
	return nRet;
}

m_HidDevReadHandle 为设备/dev/hidraw(N+1) 文件句柄,源源不断获取usb1 接口出来的以0x02 开头的触摸数据。

4. 发送数据

BOOL MSrv_DualUsbTouch::packagePassthroughData(uint8_t * inBuf, uint8_t* outBuf, uint8_t nLen)
{
	int nIndex = 0;
	if(inBuf == NULL || outBuf == NULL || nLen < 0)
	{
		return FALSE;
	}
	memset(outBuf,0x00,sizeof(outBuf));
	for(nIndex = 0; nIndex < BUFF_MAX - 2; nIndex++)
	{
		outBuf[nIndex + 2] = inBuf[nIndex];
	}
	outBuf[0] = 0x06;
	outBuf[1] = 0x18;

	return TRUE;
}

在转发模式,将usb1读取的62 bytes有效数据,拼接0x06,0x18 ,往/dev/hidraw(N) 发送,touch panel 收到后,组装成标准HID信息发给usb2的OPS设备。

int  MSrv_DualUsbTouch::forwardTouchDataToSalve(uint8_t* inBuf, uint8_t nLen)
{
	int nRet   = -1;
    uint8_t TmpBuf[BUFF_MAX] = {0};

	if(isOlation(inBuf, nLen))
	{
		return -2;
	}
	packagePassthroughData(inBuf, TmpBuf, nLen);
	printHex(BUFF_MAX/4,TmpBuf);
	nRet = passthrough(TmpBuf,nLen);
	return nRet;
}

int MSrv_DualUsbTouch::passthrough(uint8_t* buff, uint8_t nSize)
{
	int nRet = -1;
	write_lock();
	nRet = write(m_HidDevWriteHandle,buff,nSize);
	write_unlock();
	dbg_info("write %d bytes to usb2, handle: 0x%x. \n", nRet, m_HidDevWriteHandle);
	return nRet;
}

封装后,forwardTouchDataToSalve 完成发送。

5.转发测试

在这里插入图片描述
Android端,数据的收,转发。
在这里插入图片描述
上图为,ops 端收到的转发过来的消息。

6. 区域触控

由于屏幕只有一个,一些全局图标,不管在哪个通道下都需要去响应,但是全局图标下的应用不需要响应。这个场景下,全局图标这个区域就只能在usb1 下响应,USB2 下不响应。

BOOL MSrv_DualUsbTouch::isOlation(uint8_t* inBuf, uint8_t nLen)
{
	BOOL bRet       = FALSE;
	uint8_t nIndex  = 0;
	uint32_t uPos_x = ((inBuf[4] << 8) & 0xFF00 ) | (inBuf[3] & 0xFF);
	uint32_t uPos_y = ((inBuf[6] << 8) & 0xFF00 ) | (inBuf[5] & 0xFF);

	for(nIndex = 0; nIndex < sizeof(sLimitedArea)/sizeof(sLimitedArea[0]); nIndex++)
	{
		if(!sLimitedArea[nIndex].bUsed)
		{
			continue;
		}

		if(uPos_x >= sLimitedArea[nIndex].m_limitedArea.x && uPos_x <= sLimitedArea[nIndex].m_limitedArea.width  &&\
			uPos_y >= sLimitedArea[nIndex].m_limitedArea.y && uPos_y <= sLimitedArea[nIndex].m_limitedArea.height)
		{
			bRet = TRUE;
			break;
		}
	}
	return bRet;
}

转发数据前,比较坐标点是否在隔离区域(sLimitedArea)内,在则不转发。从而达到这种效果。

坐标系

在这里插入图片描述
hid 编程参考:
https://www.cnblogs.com/arnoldlu/p/11418391.html
http://libusb.sourceforge.net/api-1.0/api.html

固定 hid 设备节点,可参考博文:
https://blog.csdn.net/buding_code/article/details/55046648?utm_source=blogxgwz1

发布了105 篇原创文章 · 获赞 20 · 访问量 34万+

猜你喜欢

转载自blog.csdn.net/kehyuanyu/article/details/104620337