数码相框(十一、多种方法支持多输入之select方式输入)

注:本人已购买韦东山老师第三期项目视频,内容来源《数码相框项目视频》,只用于学习记录,如有侵权,请联系删除。

在上一节我们使用了轮询的方式获取标准输入和LCD触摸屏输入的输入事件,以控制电子书的上下翻页。轮序的方式存在着CPU占用率高的缺陷,使得CPU无法再干更多的事情,造成CPU资源的浪费。这一节使用 select 方式 获取输入事件。

1.在 ubuntu 终端输入 man select 命令,可以查看到 select 系统调用的相关解释,如下:

#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

int select(int nfds, fd_set *readfds, fd_set *writefds,
           fd_set *exceptfds, struct timeval *timeout);

参数:

  • nfds:最大文件句柄 + 1 (打开文件会返回一个整数fd,而 nfds = fd + 1);
  • readfds:被监测是否可读的文件;
  • writefds:被监测是否可写的文件;
  • exceptfds:被监测是否异常的文件;
  • timeout:超时时间(监测多长时间(没有可读/可写/异常的话,timeout时间到就返回));

2.参考 man 手册中的 select 例子,进行源码修改,man 手册 select 例子的代码如下:(或者参考《UNIX 环境高级编程》中的 I/O 多路转接)

#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

int main(void)
{
	fd_set rfds;
	struct timeval tv;
	int retval;

	/* Watch stdin (fd 0) to see when it has input. */
	FD_ZERO(&rfds);
	FD_SET(0, &rfds);

	/* Wait up to five seconds. */

	tv.tv_sec = 5;
	tv.tv_usec = 0;

	retval = select(1, &rfds, NULL, NULL, &tv);
	/* Don't rely on the value of tv now! */

	if (retval == -1)
		perror("select()");
	else if (retval)
		printf("Data is available now.\n");
		/* FD_ISSET(0, &rfds) will be true. */
	else
		printf("No data within five seconds.\n");

	exit(EXIT_SUCCESS);
}

3.修改源码
(1) 修改 T_InputOpr 结构体,添加文件句柄 iFd,代码如下:

typedef struct InputOpr {
	char *name;
	int iFd;                /* 文件句柄 */
	int (*DeviceInit)(void);
	int (*DeviceExit)(void);
	int (*GetInputEvent)(PT_InputEvent ptInputEvent);
	struct InputOpr *ptNext;
}T_InputOpr, *PT_InputOpr;

(2) 修改 input_manager.c:
① 添加全局变量:

static fd_set g_tRFds;
static int g_iMaxFd = -1;

② 修改 AllInputDevicesInit 函数:

int AllInputDevicesInit(void)
{
	PT_InputOpr ptCur;
	int iError = -1;

	FD_ZERO(&g_tRFds); /* 清零 */ 
	
	if (!g_ptInputOprHead)
	{
		DBG_PRINTF("don't have InputOpr\n");
		return -1;
	}
	else
	{
		ptCur = g_ptInputOprHead;
		do{
			if (0 == ptCur->DeviceInit())
			{
				FD_SET(ptCur->iFd, &g_tRFds); /* 设置想要监测的文件 */
				if (g_iMaxFd < ptCur->iFd)
					g_iMaxFd = ptCur->iFd;
				
				iError = 0;
			}
			ptCur  = ptCur->ptNext;
		}while(ptCur);
	}

	g_iMaxFd++; /* 最大文件句柄加1,因为select函数的参数nfds是最大文件句柄加1 */
	
	return iError;
}

③ 修改 GetInputEvent 函数:

int GetInputEvent(PT_InputEvent ptInputEvent)
{
	/* 使用select函数监测stdin、touchscreen,
	 * 有数据时再调用它的GetInputEvent函数来获得具体事件 
	 */ 
	fd_set tRFds;
	int iRet;
	PT_InputOpr ptCur = g_ptInputOprHead;
	
	tRFds = g_tRFds;

	/* 参考:UNIX环境高级编程I/O多路转接 
	 * 只检测可读的文件,其他情况不管,也不需要超时时间
	 * 休眠监测,不会占用 CPU 资源 
	 */
	iRet = select(g_iMaxFd, &tRFds , NULL, NULL, NULL);
	/* 返回值大于 0,表示检测的文件中至少有一个有数据可以读 */
	if (iRet > 0)
	{
		while (ptCur)
		{
			/*  查看 ptTmp->iFd 是否被置位,如果被置位,表明有数据 */
			if (FD_ISSET(ptCur->iFd, &tRFds))
			{
				if (0 == ptCur->GetInputEvent(ptInputEvent))
				{
					return 0;
				}
			}
			/* 否则,进行下一个结点访问 */
			ptCur = ptCur->ptNext;
		}
	}
	return -1;
}

(2) 修改 stdin.c 的 StdinDeviceInit 函数:

/* stdin以非阻塞的方式获得数据 */
static int StdinDeviceInit(void)
{
	struct termios tTTYState;

	/* get the terminal state */
	tcgetattr(STDIN_FILENO, &tTTYState);

	/* turn off canonical mode */
	tTTYState.c_lflag &= ~ICANON;

	/* minimum of number input read */
	tTTYState.c_cc[VMIN] = 1;

	/* set the terminal attributes */
	tcsetattr(STDIN_FILENO, TCSANOW, &tTTYState);
		
	/* 添加记录标准输入设备的文件句柄, 设置 iFd 为 STDIN_FILENO(0)*/
	g_tStdinInputOpr.iFd = STDIN_FILENO;
	
	return 0;
}

(3) 修改 touchscreen.c 的 StdinDeviceInit 函数:

static int TouchScreenDeviceInit(void)
{
#if 0  /* tslib-1.4 版本 */
	char *pcTSName = NULL;

	/* 根据环境变量获取输入设备 */
	if ((pcTSName = getenv("TSLIB_TSDEVICE")) != NULL) 
	{
		/* 以非阻塞方式打开输入设备 */
        g_ptTSDev = ts_open(pcTSName, 1);
    }
	else /* 如果环境变量没有设置输入设备 */
	{
		g_ptTSDev = ts_open("/dev/input/event0", 1);
	}

	if (!g_ptTSDev) 
	{
		DBG_PRINTF("ts_open error!\n");
		return -1;
	}

	if (ts_config(g_ptTSDev)) {
		DBG_PRINTF("ts_config error\n");
		return -1;
	}
#else /* tslib-1.21 版本 */
	g_ptTSDev = ts_setup(NULL, 1);
#endif
	/* 添加:设置 iFd 
	 * 我们用 ts_open 或者 ts_setup 得到的是 g_tTSDev,该结构体中利用 ts_fd 返回文件句柄
	 */
	g_tTouchScreenInputOpr.iFd = ts_fd(g_ptTSDev);
	
	if (GetDispResolution(&g_iXres, &g_iYres))
	{
		return -1;
	}
	
	return 0;
}

4.测试:

  • ① 安装触摸屏驱动;

  • ② 在开发板启动telnet服务,为了登录进去观察CPU占用率:telnetd -l /bin/sh,然后使用 MobaXterm 通过 telnet 登录开发板,登录后如下图所示:
    在这里插入图片描述

  • ③ 运行电子书程序:./show_file -s 24 -d fb -f MSYH.TTF -h HZK16 utf16le.txt

  • ④ telnet 上开发板后,在 MobaXterm 输入 top 命令观察 CPU 占用率,如下图所示:
    没有输入时:
    在这里插入图片描述
    当点击屏幕或者有标准输入时:
    在这里插入图片描述
    明显看到 CUP 占用率有变化,而且变化不是太大!这就是 select 的好处! select 是一种休眠监测,不会占用 CPU 的资源,适用于很多简单场合

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Louis@L.M.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值