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

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

这一节主要讲述如何使用标准输入和LCD触摸屏操作电子书,实现电子书的上下翻页。多种方法支持多输入控制电子书翻页的程序框架如下图所示:
在这里插入图片描述
如上图所示:

  • ① 首先定义一个T_InputOpr结构体,结构体代码如下:
    typedef struct InputOpr {
    	char *name;                                       /* 名字 */
    	int (*DeviceInit)(void);                          /* 设备初始化 */
    	int (*DeviceExit)(void);                          /* 设备退出 */
    	int (*GetInputEvent)(PT_InputEvent ptInputEvent); /* 获取输入事件 */
    	struct InputOpr *ptNext;                          /* 用于管理T_InputOpr的结构体链表指针 */
    }T_InputOpr, *PT_InputOpr;
    
  • ② 定义输入事件,相关代码如下:
    /* 输入类型 */
    #define INPUT_TYPE_STDIN		0     /* 标准输入 */
    #define INPUT_TYPE_TOUCHSCREEN	1     /* 触摸屏输入 */
    
    /* 输入值 */
    #define INPUT_VALUE_UP			0    /* 上翻 */
    #define INPUT_VALUE_DOWN		1    /* 下翻 */
    #define INPUT_VALUE_EXIT		2    /* 退出 */
    #define INPUT_VALUE_UNKNOW      -1   /* 无法识别 */
    
    /* 定义InputEvent  输入事件结构体 */
    typedef struct InputEvent{
    	struct timeval tTime;  /* 时间 */
    	int iType;   /* stdin, touchscreen */
    	int iVal;    /* 输入事件值 */
    }T_InputEvent,*PT_InputEvent;
    
  • ③ 在标准输入stdin.c 中,输入“n”控制电子书下翻,输入“u”控制电子输入上翻,输入“q”控制电子输入下翻;在LCD触摸屏touchscreen.c中,把LCD触摸屏分成上、中、下三部分,点击屏幕上半部控制电子输入上翻,点击屏幕下半部控制电子输入下翻,点击屏幕中间无反应。
  • ④ 在input_manager.c中,通过T_InputOpr结构体链表管理输入设备 stdin.c 和 touchscreen.c。

控制电子书的上下翻页,CPU需要知道我们输入的控制事件。那么它怎么知道有上翻下翻的事件呢?方式一:轮询方式(不断查询按键或者触摸屏有没有操作)

使用轮询方式输入:
stdin.c:
(1) 对于标准输入,我们不能使用普通的输入函数,如 scanf,getchar 等,因为这些都是阻塞式输入,每次,只有输入回车换行之后才能把缓冲区中的值输出。我们需要stdin以非阻塞的方式输入,stdin设备初始化代码如下:

#include <input_manager.h>
#include <termios.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/types.h>
#include <stdio.h>

/* 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);
	
	return 0;
}

(2) 当程序退出后,我们需要恢复原来的阻塞方式,代码如下:

static int StdinDeviceExit(void)
{
	struct termios tTTYState;

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

	/* turn on canonical mode */
	tTTYState.c_lflag |= ICANON;

	/* set the terminal attributes */
	tcsetattr(STDIN_FILENO, TCSANOW, &tTTYState);
	
	return 0;
}

(3) 获取输入事件,代码如下:

static int StdinGetInputEvent(PT_InputEvent ptInputEvent)
{
	/* 如果有数据就读取、处理、返回
	 * 如果没有数据,立刻返回,不等待
	 */
	struct timeval tTV;
	fd_set tFDs;
	char c;
	
	/* 等待时间设为0,不等待 */
	tTV.tv_sec  = 0;
	tTV.tv_usec = 0;
	FD_ZERO(&tFDs);
	FD_SET(STDIN_FILENO, &tFDs);
	
	select(STDIN_FILENO + 1, &tFDs, NULL, NULL, &tTV);

	if (FD_ISSET(STDIN_FILENO, &tFDs))
	{
		/* 处理数据 */
		ptInputEvent->iType = INPUT_TYPE_STDIN;
		gettimeofday(&ptInputEvent->tTime, NULL); 
		
		c = fgetc(stdin);
		if ( c == 'u')
		{
			ptInputEvent->iVal = INPUT_VALUE_UP;	
		}
		else if (c == 'n')
		{
			ptInputEvent->iVal = INPUT_VALUE_DOWN;
		}
		else if (c == 'q')
		{
			ptInputEvent->iVal = INPUT_VALUE_EXIT;
		}
		else
		{
			ptInputEvent->iVal = INPUT_VALUE_UNKNOW;
		}

		return 0;
	}
	else
	{
		return -1;
	}
}

(4) 注册T_InputOpr结构体:

static T_InputOpr g_tStdinInputOpr = {
	.name 		   = "stdin",
	.DeviceInit    = StdinDeviceInit,
	.DeviceExit    = StdinDeviceExit,
	.GetInputEvent = StdinGetInputEvent,
};

int StdinInit(void)
{
	return RegisterInputOpr(&g_tStdinInputOpr);
}

touchscreen.c:
(1) 对于应用程序的触摸屏设备的初始化,可以参考 tslib 里的 ts_print.c 文件,touchscreen的初始化代码如下:

/* 参考tslib里的ts_print.c
 * 注意:由于要用到LCD的分辨率,所有TouchScreenDeviceInit函数要在
 *       SelectAndInitDisplay函数之后调用
 */
static int TouchScreenDeviceInit(void)
{
/* 其中#if里的代码是 tslib-1.4 触摸屏的初始化代码,#else里的是tslib-1.21触摸屏的初始化代码 */
#if 0 
	char *pcTSName = NULL;

	/* 根据环境变量获取输入设备 */
	if ((pcTSName = getenv("TSLIB_TSDEVICE")) != NULL) 
	{
		/* 以非阻塞方式打开输入设备, 其中参数1表示以非阻塞方式打开*/
        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
	/* 以非阻塞方式初始化输入设备, 其中参数1表示以非阻塞方式 */
	g_ptTSDev = ts_setup(NULL, 1);
#endif
	if (GetDispResolution(&g_iXres, &g_iYres))
	{
		return -1;
	}
	
	return 0;
}

/* 其中GetDispResolution在draw.c定义,用于获取LCD屏幕的分辨率 */
int GetDispResolution(int *piXres, int *piYres)
{
	if (g_ptDispOpr)
	{
		*piXres = g_ptDispOpr->iXres;
		*piYres = g_ptDispOpr->iYres;
		return 0;
	}
	else 
	{
		return -1;
	}
}

(2) 退出函数,可以不做任何事情,只做填充T_InputOpr结构体的作用,代码如下:

static int TouchScreenDeviceExit(void)
{
	return 0;
}

(3) 获取输入事件:在采样触摸屏的点的时候,手指一次触摸,会有多个点,我们必须有一个时间控制。这里,使用的是使用系统内部的时钟,实现 500ms 的计时,即当手指按下时间大于 500ms 时,我们才采样;否则,不予理会。代码如下:

static int isOutOf500ms(struct timeval *ptPreTime, struct timeval *ptNowTime)
{
	unsigned int iPreMs;
	unsigned int iNowMs;

	iPreMs = ptPreTime->tv_sec * 1000 + ptPreTime->tv_usec / 1000;
	iNowMs = ptNowTime->tv_sec * 1000 + ptNowTime->tv_usec / 1000;

	return (iNowMs > iPreMs + 500);
}

static int TouchScreenGetInputEvent(PT_InputEvent ptInputEvent)
{
	struct ts_sample tSamp;
	int iRet;

	static struct timeval tPreTime; /* 上一次的时间值 */
	
	/* 读取数据 */
	iRet = ts_read(g_ptTSDev, &tSamp, 1);
	if (iRet < 0) {
		DBG_PRINTF("ts_read error!\n");
		return -1;
	}

	/* 处理数据 */
	if (isOutOf500ms(&tPreTime, &tSamp.tv))
	{
		/* 如果此处触摸事件的发生时间, 距上次事件超过了500ms */
		tPreTime = tSamp.tv;
		ptInputEvent->tTime = tSamp.tv;
		ptInputEvent->iType = INPUT_TYPE_TOUCHSCREEN;

		if (tSamp.y < g_iYres / 3)
		{
			/* 上翻 */
			ptInputEvent->iVal = INPUT_VALUE_UP;
		}
		else if (tSamp.y > g_iYres * 2 / 3)
		{
			/* 下翻 */
			ptInputEvent->iVal = INPUT_VALUE_DOWN;
		}
		else
		{
			/* 无反应 */
			ptInputEvent->iVal = INPUT_VALUE_UNKNOW;
		}

		return 0;
	}
	else
	{
		return -1;
	}
}

(4) 注册T_InputOpr结构体:

static T_InputOpr g_tTouchScreenInputOpr = {
	.name = "touchscreen",
	.DeviceInit = TouchScreenDeviceInit,
	.DeviceExit = TouchScreenDeviceExit,
	.GetInputEvent = TouchScreenGetInputEvent,
};

int TouchScreenInit(void)
{
	return RegisterInputOpr(&g_tTouchScreenInputOpr);
}

input_manager.c 的代码如下:

#include <input_manager.h>
#include <config.h>
#include <string.h>
#include <stdio.h>


static PT_InputOpr g_ptInputOprHead = NULL;

int RegisterInputOpr(PT_InputOpr ptInputOpr)
{
	PT_InputOpr ptCur;
	
	if (!g_ptInputOprHead)
	{
		g_ptInputOprHead   = ptInputOpr;
		ptInputOpr->ptNext = NULL;
	}
	else
	{
		ptCur = g_ptInputOprHead;
		while (ptCur->ptNext)
		{
			ptCur = ptCur->ptNext;
		}
		ptCur->ptNext = ptInputOpr;
		ptInputOpr->ptNext = NULL;
	}
	
	return 0;
}

void ShowInputOpr(void)
{
	PT_InputOpr ptCur;
	int i = 0;
	
	if (!g_ptInputOprHead)
	{
		printf("don't have InputOpr\n");
		return;
	}
	else
	{
		ptCur = g_ptInputOprHead;
		do{
			printf("%02d %s\n", i++, ptCur->name);
			ptCur = ptCur->ptNext;
		}while(ptCur);
	}
}

PT_InputOpr GetInputOpr(char *pcName)
{
	PT_InputOpr ptCur;
	
	if (!g_ptInputOprHead)
	{
		return NULL;
	}
	else
	{
		ptCur = g_ptInputOprHead;
		do
		{
			if (strcmp(ptCur->name, pcName) == 0)
				return ptCur;
			else
				ptCur = ptCur->ptNext;
		}
		while (ptCur);	
	}
	return NULL;

}

int AllInputDevicesInit(void)
{
	PT_InputOpr ptCur;
	int iError = -1;
	
	if (!g_ptInputOprHead)
	{
		DBG_PRINTF("don't have InputOpr\n");
		return -1;
	}
	else
	{
		ptCur = g_ptInputOprHead;
		do{
			if (0 == ptCur->DeviceInit())
			{
				iError = 0;
			}
			ptCur  = ptCur->ptNext;
		}while(ptCur);
	}

	return iError;
}

int GetInputEvent(PT_InputEvent ptInputEvent)
{
	/* 轮询方式  */
    /* 把链表中的InputOpr的GetInputEvent都调用一次,一旦有数据就返回*/
	PT_InputOpr ptCur;
	
	if (!g_ptInputOprHead)
	{
		DBG_PRINTF("don't have InputOpr\n");
		return -1;
	}
	else
	{
		ptCur = g_ptInputOprHead;
		do{
			if (0 == ptCur->GetInputEvent(ptInputEvent))
			{
				return 0;
			}
			ptCur  = ptCur->ptNext;
		}while(ptCur);
	}
	return -1;
}

int InputInit(void)
{
	int iError;

	iError = StdinInit();
	iError |= TouchScreenInit();

	return iError;
}

main.c 的代码如下:

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <config.h>
#include <draw.h>
#include <encoding_manager.h>
#include <fonts_manager.h>
#include <disp_manager.h>
#include <input_manager.h>
#include <string.h>

/* ./show_file [-s Size] [-d display] [-f font_file] [-h HZK] <text_file> */
int main(int argc, char **argv)
{
	int iError;
	unsigned int dwFontSize = 16;
	char acHzkFile[128];
	char acFreetypeFile[128];
	char acTextFile[128];
	char acDisplay[128];
	
	int bList = 0;

	T_InputEvent tInputEvent;
	
	acHzkFile[0]  = '\0';
	acFreetypeFile[0] = '\0';
	acTextFile[0] = '\0';
	strcpy(acDisplay, "fb");
	
	while ((iError = getopt(argc, argv, "ls:f:h:d:")) != -1)
	{
		switch(iError)
		{
			case 'l':
			{
				bList = 1;
				break;
			}
			case 's':
			{
				  dwFontSize = strtoul(optarg, NULL, 0);
				  break;
			}
			case 'f':
			{
				  strncpy(acFreetypeFile, optarg, 128);
				  acFreetypeFile[127] = '\0';
				  break;
			}
			case 'h':
			{
					strncpy(acHzkFile, optarg, 128);
					acHzkFile[127] = '\0';
					break;
			}
			case 'd':
			{
				strncpy(acDisplay, optarg, 128);
				acDisplay[127] = '\0';
				break;
			}
			default:
			{
				  printf("Usage: %s [-s Size] [-d display] [-f font_file] [-h HZK] <text_file>\n", argv[0]);
				  printf("Usage: %s -l\n", argv[0]);
				  return -1;
				  break;
			}
		}
	}

	if (!bList && optind >= argc)
	{
		printf("Usage: %s [-s Size] [-d display] [-f font_file] [-h HZK] <text_file>\n", argv[0]);
		printf("Usage: %s -l\n", argv[0]);
		return -1;
	}

	iError = DisplayInit();
	if (iError)
	{
		DBG_PRINTF("DisplayInit error!\n");
		return -1;
	}

	iError = FontsInit();
	if (iError)
	{
		DBG_PRINTF("FontsInit error!\n");
		return -1;
	}

	iError = EncodingInit();
	if (iError)
	{
		DBG_PRINTF("EncodingInit error!\n");
		return -1;
	}

	iError = InputInit();
	if (iError)
	{
		DBG_PRINTF("InputInit error!\n");
		return -1;		
	}
	
	if (bList)
	{
		printf("supported display:\n");
		ShowDispOpr();

		printf("supported font:\n");
		ShowFontOpr();

		printf("supported encoding:\n");
		ShowEncodingOpr();

		printf("supported input:\n");
		ShowInputOpr();
		return 0;
	}
	
	strncpy(acTextFile, argv[optind], 128);
	acTextFile[127] = '\0';
	
	iError = OpenTextFile(acTextFile);
	if (iError)
	{
		DBG_PRINTF("OpenTextFile error!\n");
		return -1;
	}

	iError = SetTextDetail(acHzkFile, acFreetypeFile, dwFontSize);
	if (iError)
	{
		DBG_PRINTF("SetTextDetail error!\n");
		return -1;
	}

	iError = SelectAndInitDisplay(acDisplay);
	if (iError)
	{
		DBG_PRINTF("SelectAndInitDisplay error!\n");
		return -1;
	}
	

	iError = AllInputDevicesInit();
	if (iError)
	{
		DBG_PRINTF("AllInputDevicesInit error!\n");
		return -1;
	}
	
	iError = ShowNextPage();
	if (iError)
	{
		DBG_PRINTF("Error to show first page\n");
		return -1;
	}

	printf("Enter 'n' to show next page, 'u' to show previous page, 'q' to exit:\n");	
	while (1)
	{
		//printf("Enter 'n' to show next page, 'u' to show previous page, 'q' to exit: ");

		if (0 == GetInputEvent(&tInputEvent))
		{
			if (tInputEvent.iVal == INPUT_VALUE_DOWN)
			{
				ShowNextPage();
			}
			else if (tInputEvent.iVal == INPUT_VALUE_UP)
			{
				ShowPrePage();			
			}
			else if (tInputEvent.iVal == INPUT_VALUE_EXIT)
			{
				return 0;
			}
		}
	}
	return 0;	
}

测试:

  • ① 安装触摸屏驱动;

  • ② 在开发板启动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 占用率,如下图所示:
    在这里插入图片描述
    从上图可知,show_file 程序的CPU占用率为 99%,可见该程序的CPU占用率是非常高的。所以,轮询方式存在明显的缺陷:占用较大的CPU资源。

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Louis@L.M.

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

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

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

打赏作者

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

抵扣说明:

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

余额充值