在上一篇博客中,我才用了轮询的方法实现LCD文本的多输入控制。主程序如下:
while (1)
{
if (0 == GetInputEvent(&tInputEvent))
{
if (tInputEvent.iVal == INPUT_VAL_DOWN)
{
ShowNextPage();
}
else if (tInputEvent.iVal == INPUT_VAL_UP)
{
ShowPrePage();
}
else if (tInputEvent.iVal == INPUT_VAL_EXIT)
{
return 0;
}
}
}
这样的做的明显缺陷是:
- cpu一直在等待查询的结果。导致CPU的占用率非常高。在实际的工程中这是不可接受的。
因此,我们采用了select和poll机制。
select机制:
参考:UNIX环境高级编程 I/O多路转接
监测多个文件,只要有某一个文件可读/可写/异常或超时,即返回
int select(int nfds,fd_set *readfds,fd_set *writefds,fd_set *exceptfds,struct timeval *timeout);
nfds:最大文件句柄+1
readfds:监视这些文件描述符的读变化,即在这个集合中是否有我们关心的文件描述符可读了。
writefds:被监测是否可写的文件,同上。
exceptfds:被监测是否有异常的文件,同上。
timeout:超时时间。当写为0的时候表示查询:不管有没有变化都返回。当写为NULL的时候表示:无论等多长时间,知道我关心的这些集合中有文件描述符发生了期望的变化。
返回值:当有文件描述符发生了期望的变化,返回变化的数量。
为什么select可以降低CPU的占用率?因为select的监测是休眠监测。
程序修改
- select函数中,需要提供被监测是否有异常的文件,这就要两个输入设备提供其句柄。
因此,要在应用程序底层获取设备句柄。
static int TSDevInit(void)
{
char *pcTsdevice=NULL;
if((pcTsdevice=getenv("TSLIB_TSDEVICE"))!= NULL)/*在环境链表中寻找变量名为TSLIB_TSDEVICE的设备*/
{
g_ptTsDev = ts_open(pcTsdevice,1);/*非拥塞的打开*/
}
else/*环境链表中,TSLIB_TSDEVICE未注册*/
{
g_ptTsDev = ts_open("/dev/input/event0", 1);/*则以键盘作为输入*/
}
if (!g_ptTsDev) {
printf("TS_open error\n");
return -1;
}
if (ts_config(g_ptTsDev)) {
printf("ts_config error\n");
return -1;
}
if (GetDispResolution(&g_iXres, &g_iYres))/*获取显示屏的分辨率*/
{
return -1;
}
/*分辨率由FBDeviceInit函数从底层获取,因此触摸屏需要在fb(lcd)初始化之后再初始化*/
g_tTSDevInptOpr.iFd=ts_fd(g_ptTsDev);/*获取句柄*/
return 0;
}
- 在input_manager.h中,对fd_set进行初始化。
typedef struct InputOpr {
unsigned int iFd;
unsigned char* name;
int (*InputDevInit)(void);
int (*InputDevExit)(void);
int (*InputDevEventIdel)(PT_InputEventOpr pt_InputEventOpr);
struct InputOpr *ptNext;
}T_InputOpr,*PT_InputOpr;
- 在事件处理函数GetInputEvent中,调用select函数;当select函数被调用,随即进入休眠状态,当有事件发生的时候,select将发生事件的fd返回。而之前的程序中,需要在主程序中循环调用GetInputEvent函数,在GetInputEvent需要不断的查询当前设备的状态。
初始化函数:
int AllInputDevicesInit(void)
{
PT_InputOpr ptTemp = g_ptInputOprHead;
g_iMaxFd=0;
FD_ZERO(&g_tRfds);
while(ptTemp)
{
if(0 == ptTemp->InputDevInit())/*调用成功*/
{
FD_SET(ptTemp->iFd, &g_tRfds);
if(ptTemp->iFd > g_iMaxFd)
g_iMaxFd=ptTemp->iFd;
ptTemp = ptTemp->ptNext;
}
else
return -1;
}
return 0;
}
事件处理函数:
int GetInputEvent(PT_InputEventOpr ptInputEvent)/*事件处理,或者叫事件查询*/
{
PT_InputOpr ptTemp = g_ptInputOprHead;
int retval;
/*有事件再返回,超时系数设置为0*/
retval=select(g_iMaxFd+1,&g_tRfds,NULL,NULL,NULL);
/*事件发生返回后查询是哪个事件发生了*/
if(retval)
{
while(ptTemp)
{
/*事件发生,结构体中相应的位置位*/
if(FD_ISSET(ptTemp->iFd,&g_tRfds))
{
ptTemp->InputDevEventIdel(ptInputEvent);
return 0;
}
ptTemp = ptTemp->ptNext;/*没有发生该事件随即查询链表中另一个事件*/
}
}
return -1;
}