数码相册——电子书多线程支持多输入
- 硬件平台:韦东山嵌入式Linxu开发板(S3C2440.v3)
- 软件平台:运行于VMware Workstation 12 Player下UbuntuLTS16.04_x64 系统
- 参考资料:《嵌入式Linux应用开发手册》、《嵌入式Linux应用开发手册第2版》、【多线程编写】
- 开发环境:Linux 3.4.2内核、arm-linux-gcc 4.3.2工具链
- 源码仓库:https://gitee.com/d_1254436976/Embedded-Linux-Phase-3
目录
一、前言
在【1.7 数码相册—电子书(3)—轮询方式支持多输入】和【1.7 数码相册—电子书(4)—select支持多输入】中,我们采用了轮询方式支持多输入,并且采用select函数
改进了轮询方式的CPU占用率过高的问题。
在这篇博客中,采用多线程的方式,来改进了轮询方式的CPU占用率过高的问题,同时修改touchscreen.c
支持滑动翻页,分为如下三个线程:
- 主线程:负责初始化各种设备,创建子线程,并协调它们进行显示电子书的效果,等待子线程的输入信息,处于休眠。
stdin
输入子线程:负责响应stdin
输入设备的输入事件的子线程,当发生输入事件时,主线程处于休眠状态,执行此线程的任务,执行完毕后则重新唤醒主线程。touchscreen
输入子线程:负责响应touchscreen
输入设备的输入事件的子线程,当发生输入事件时,主线程处于休眠状态,执行此线程的任务,执行完毕后则重新唤醒主线程。
并且两个子线程同一时间能只能执行一个。
二、修改代码
1、修改管理者input_manager.c
文件
1.1 创建线程
此函数是在main.c
中被调用,初始化所有输入设备的时候创建对应子线程。
/* 初始化所有支持的Input设备 */
int AllInputDeviceInit()
{
int error;
PT_InputOpr ptTmp;
error = -1;
ptTmp = s_ptInputOprHead;
while (ptTmp) {
if (ptTmp->DeviceInit() == 0) {
/* 创建子线程 */
error = pthread_create(&ptTmp->threadId, NULL, InputEventThreadFunction, ptTmp->GetInputEvent);
if (error != 0) {
printf("pthread_creat error ,error code : %d\n", error);
return error;
}
}
ptTmp = ptTmp->ptNext;
}
return 0;
}
1.2 输入事件线程函数
此函数在发生对应子线程的时候被执行。
/* 输入事件线程函数 */
static void *InputEventThreadFunction(void *pvoid)
{
T_InputEvent tmpInputEvent;
/* 定义函数指针 */
int (*GetInputEvent)(PT_InputEvent ptInputEvent);
GetInputEvent = (int (*)(PT_InputEvent))pvoid;
while(1) {
/* 调用函数获得输入事件 */
if (GetInputEvent(&tmpInputEvent) == 0) {
/* 唤醒主线程,把tmpInputEvent的值赋给一个全局变量 */
/* 访问临界资源前,先获得互斥量 */
pthread_mutex_lock(&s_tMutex); //加锁
s_tInputEvent = tmpInputEvent;
/* 唤醒主线程 */
pthread_cond_signal(&s_tCondvar); //发送信号给处于阻塞等待状态的主线程
/* 释放互斥量 */
pthread_mutex_unlock(&s_tMutex); //解锁
}
}
return NULL;
}
1.3 获取输入事件的函数
此函数在发生对应子线程的时候被执行,通过1.2中的函数的GetInputEvent(&tmpInputEvent)
,被调用。
/* 获取输入事件 */
int GetInputEvent(PT_InputEvent ptInputEvent)
{
/* 休眠 */
/* 进入临界资源前,获得互斥量 */
pthread_mutex_lock(&s_tMutex);
/* pthread_cond_wait会先解除之前的pthread_mutex_lock锁定的s_tMutex,
* 然后阻塞在等待队列里休眠,直到再次被唤醒
* (大多数情况下是等待的条件成立而被唤醒,唤醒后,该进程会先锁定pthread_mutex_lock(&s_tMutex)
*/
pthread_cond_wait(&s_tCondvar, &s_tMutex);
/* 被唤醒之后(执行完子线程对应的InputEventThreadFunction()函数)返回数据 */
*ptInputEvent = s_tInputEvent;
/* 释放互斥量 */
pthread_mutex_unlock(&s_tMutex); //解锁
return 0;
}
2、修改touchscreen.c
设备文件,支持滑动翻页
通过调用tslib
库:
- 获取触摸屏滑动按下的第一个点
startPressured
和松开最后一个点endReleased
; - 计算二者的x坐标的差值,
delta = endReleased.x - startPressured.x
,标准为滑动距离为触摸屏x分辨率的1/5; - 若
delta > s_Xres / 3
滑动,往右滑,则翻到上一页; - 若
delta < (0 - s_Xres / 3)
,往左滑,则翻到下一页;
/**
* @Description: 采用查询的方式获读取touchscreen数据
* @param ptInputEvent - 表示input设备的结构体.
* @return 有数据:0 无数据:-1
*/
static int TouchScreenGetInputEvent(PT_InputEvent ptInputEvent)
{
int ret;
int delta;
int startflag;
int endflag;
struct ts_sample samp;
struct ts_sample startPressured;
struct ts_sample endReleased;
endflag = startflag = 0;
while(1) {
/* 如果无数据则休眠 */
ret = ts_read(s_pTSDev, &samp, 1);
if (ret == 1) {
/* 第一次按下 */
if ((samp.pressure > 0) && (startflag == 0)) {
/* 记录数据 */
startPressured = samp;
startflag = 1;
}
/* 最后松开 */
if (samp.pressure <= 0) {
endReleased = samp;
endflag = 1;
}
/* 松开处理数据 */
if (!startflag && endflag == 1)
return -1; //异常情况
else if((startflag == 1) && (endflag == 1)) {
delta = endReleased.x - startPressured.x; //计算差值
ptInputEvent->time = endReleased.tv;
ptInputEvent->type = INPUT_TYPE_TOUCHSCREEN;
if (delta > s_Xres / 3)
ptInputEvent->val = INPUT_VALUE_UP; //上翻
else if(delta < (0 - s_Xres / 3))
ptInputEvent->val = INPUT_VALUE_DOWN; //下翻
else
ptInputEvent->val = INPUT_VALUE_UNKONW; //未知
return 0;
}
} else
return -1;
}
return 0;
}
三、编译与烧写
1、编译
执行make
,得到可执行文件show_file
2、运行
由于使用到触摸屏,需要调用tslib库来进行校准
- 执行
./show_file -l
,显示出当前支持的设备
- 执行
./shoe_file -s 16 -h HZK16 -f ./MSYH.TTF hz.txt
执行ps -T
,可以看到此时的三个线程:
在没有任何外部输入的时候,CPU占用率为0,处于休眠
有输入时,才唤醒程序,同时也支持滑动(在这里展示不了)。