在【1.7 数码相册—电子书(1)—实现】和【1.7 数码相册—电子书(2)—编写通用的Makefile】这两篇博文中,我们实现了在开发板中显示电子书,但是仍然存在一些缺点,这节课针对这些缺点来进行改进。
实现: 1、电子书支持多种输入方式,如标准串口输入实现翻页退出操作,触摸LCD实现翻页操作; 2、在main.c中采用轮询的方式支持多种输入方式。对于上述完成主要功能的5个部分:encoding编码部分、fonts获取字体点阵部分、display显示部分、input输入部分、draw协调部分
encoding编码部分:去文件中获得编码信息,对于每个文件,保存的时候,系统对自动或手动的根据编码规范,对文件的信息进行编码:如保存为ASCII、GBK、UTF-8、UTF16LE、UTF16BE;fonts获取字体点阵部分:根据获得的编码信息得到字体数据(LCD上表示为点阵);display显示部分:把字体数据(点阵)显示在LCD上;input输入部分:管理不同的输入方式,定制不同的输入方式所实现的控制,满足多种控制场合;draw协调部分 :组织各个模块部分进行合作,实现电子书的显示、翻页功能等。由于整个分为如下3部分:顶层目录的Makefile、顶层目录的Makefile.build、各级子目录的Makefile。
所以对于新添加的input输入部分需要进行如下修改:其目录下也需要添加该目录下的Makefile,对于顶层目录的Makeifle也需要添加obj-y += /input,具体的在编译的时候会介绍。
而对于顶层的Makefile.build,不需要进行修改,因为它是一个比较通用的文件,根据顶层目录的Makefile信息进入到各个子目录下进行预处理、编译、汇编,最后每个子目录与顶层得到的built-in.o进行链接,最后得到可执行文件show_file.
下面就来介绍添加的input输入部分。
在这个文件:
定义一个结构体变量,拓展文件可以根据自己的情况:分配结构体、设置结构体、注册结构体;定义一些函数,供外部文件调用。 #ifndef _INPUT_MANAGER_H #define _INPUT_MANAGER_H #include <sys/time.h> #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_UNKONW -1 /* 输入事件信息结构体 */ typedef struct InputEvent { struct timeval time; int type; int val; }T_InputEvent, *PT_InputEvent; /* 输入事件结构体 */ typedef struct InputOpr { char *name; int (*DeviceInit)(void); int (*DeviceExit)(void); int (*GetInputEvent)(PT_InputEvent ptInputEvent); struct InputOpr *ptNext; }T_InputOpr, *PT_InputOpr; int RegisterInputOpr(PT_InputOpr ptFontOpr); int InputInit(void); void ShowInputOpr(void); int GetInputEvent(PT_InputEvent ptInputEvent); int AllInputDeviceInit(void); int StdinInit(void); int TouchScreenInit(void); #endif /* _INPUT_MANAGER_H */在这个函数中,为stdin设备进行:分配结构体、设置结构体、注册结构体
/** * @file stdin.c * @brief 标准输入input设备的处初始化,处理过程与取消 * @version 1.0 (版本声明) * @author Dk * @date July 1,2020 */ #include <termios.h> #include <unistd.h> #include <stdio.h> #include "input_manager.h" /** * @Description: 获取标准串口输入的初始化,设置模式使其可以采用非阻塞的方式获取输入 * @return 成功:0 */ static int StdinDevInit(void) { struct termios ttystate; /* 获得终端的状态 */ tcgetattr(STDIN_FILENO, &ttystate); /* 关闭标准模式,并设置位为 1,表示接受最小用户数输入为1 */ ttystate.c_lflag &= ~ICANON; ttystate.c_cc[VMIN] = 1; /* 设置术语状态 */ tcsetattr(STDIN_FILENO, TCSANOW, &ttystate); return 0; } /** * @Description: 获取标准串口输入的退出,恢复原本的模式 * @return 成功:0 */ static int StdinDevExit(void) { struct termios ttystate; /* 获得终端的状态 */ tcgetattr(STDIN_FILENO, &ttystate); /* 打开标准模式 */ ttystate.c_lflag |= ICANON; /* 设置术语状态 */ tcsetattr(STDIN_FILENO, TCSANOW, &ttystate); return 0; } /** * @Description: 根据标准输入的结果进行相应处理,采用非阻塞的方式获取输入 * @param ptInputEvent - 表示input设备的结构体. * @return 描述符fd在描述符集fds中:0 不在:-1 */ static int StdinGetInputEvent(PT_InputEvent ptInputEvent) { struct timeval tv; fd_set fds; char c; /* 输入(stdin)执行无阻塞检查,而不会将超时0 */ tv.tv_sec = 0; tv.tv_usec = 0; /* 指定的文件描述符集清空 */ FD_ZERO(&fds); /* 在文件描述符集合中增加一个新的fds */ FD_SET(STDIN_FILENO, &fds); /* 测试指定的fds可读 */ select(STDIN_FILENO+1, &fds, NULL, NULL, &tv); /* 测试指定的fds是否在该集合中 */ if (FD_ISSET(STDIN_FILENO, &fds)) { /* 存在,处理数据 */ ptInputEvent->type = INPUT_TYPE_STDIN; c = fgetc(stdin); if (c == 'u') { ptInputEvent->val = INPUT_VALUE_UP; gettimeofday(&ptInputEvent->time, NULL); } else if (c == 'n') { ptInputEvent->val = INPUT_VALUE_DOWN; gettimeofday(&ptInputEvent->time, NULL); } else if (c == 'q') { ptInputEvent->val = INPUT_VALUE_EXIT; gettimeofday(&ptInputEvent->time, NULL); } else { ptInputEvent->val = INPUT_VALUE_UNKONW; gettimeofday(&ptInputEvent->time, NULL); } return 0; }else return -1; } static T_InputOpr s_tStdinOpr = { .name = "stdin", .DeviceInit = StdinDevInit, .DeviceExit = StdinDevExit, .GetInputEvent = StdinGetInputEvent, }; /** * @Description: 标准输入初始化函数,供上层调用注册设备 * @return 成功:0 */ int StdinInit(void) { return RegisterInputOpr(&s_tStdinOpr); }在这个函数中,为touchscreen设备进行:分配结构体、设置结构体、注册结构体
/** * @file touchscreen.c * @brief touchscreen input设备的处初始化,处理过程与取消,参考tslib中的ts_print.c * @version 1.0 (版本声明) * @author Dk * @date July 1,2020 */ #include <stdlib.h> #include <tslib.h> #include "input_manager.h" #include "config.h" #include "draw.h" static struct tsdev *s_pTSDev; //touchscreen设备 static int s_Xres; //LCD x方向的分辨率 static int s_Xres; //LCD y方向的分辨率 /** * @Description: 获取touchscreen输入的初始化,设置模式使其可以采用非阻塞的方式获取输入 * @return 成功:0 失败: -1 * @note 由于需要获取到LCD的分辨率,所以这个函数被调用之前,SelectAndInitDisplay必须被调用,才可以获取到数据 */ static int TouchScreenDevInit(void) { char *pTSName = NULL; /* 根据环境变量获得设备名,并以非阻塞的方式打开 */ if((pTSName = getenv("TSLIB_TSDEVICE")) != NULL) s_pTSDev = ts_open(pTSName, 1); else s_pTSDev = ts_open("/dev/event0", 1); if (!s_pTSDev) { DBG_PRINTF("ts_open error!\n"); return -1; } if (ts_config(s_pTSDev)) { DBG_PRINTF("ts_config error!\n"); return -1; } /* 获取LCD分辨率 */ if (GetDispResolution(&s_Xres, &s_Xres)) return -1; return 0; } /** * @Description: touchscreen输入的退出,恢复原本的模式 * @return 成功:0 */ static int TouchScreenDevExit(void) { return 0; } /** * @Description: 判断上一次事件与此次事件相隔的时间是否超过500ms * @param ppreTime - 存储上一次事件时间的结构体指针,pcurTime - 此次存储事件时间的结构体指针 * @return 两次事件时间间隔超过500ms:0 无超过:1 */ static int isOutOf500ms(struct timeval *ppreTime, struct timeval *pcurTime) { int prems; int curms; /* tv_sec - 秒, tv_usec - 微秒 */ prems = ppreTime->tv_sec * 1000 + ppreTime->tv_usec / 1000; curms = pcurTime->tv_sec * 1000 + pcurTime->tv_usec / 1000; return (curms > (prems + 500)); } /** * @Description: 采用查询的方式获读取touchscreen数据 * @param ptInputEvent - 表示input设备的结构体. * @return 有数据:0 无数据:-1 */ static int TouchScreenGetInputEvent(PT_InputEvent ptInputEvent) { int ret; struct ts_sample samp; static struct timeval preTime; ret = ts_read(s_pTSDev, &samp, 1); /* 无数据返回 */ if (ret < 0) { DBG_PRINTF("ts_read no data!\n"); return -1; } /* 有数据处理 */ if ((isOutOf500ms(&preTime, &samp.tv))) { /* 两次事件时间间隔超过500ms */ ptInputEvent->type = INPUT_TYPE_TOUCHSCREEN; preTime = samp.tv; ptInputEvent->time = samp.tv; /* 根据读取到的LCD y坐标值,进行对应操作 * y < y分辨率的1/3,则为上翻 * y > y分辨率的2/3,则为下翻 * 其他情况,则为未定义操作 */ if (samp.y < (s_Xres / 3)) ptInputEvent->val = INPUT_VALUE_UP; else if (samp.y > (2 * s_Xres / 3)) ptInputEvent->val = INPUT_VALUE_DOWN; else ptInputEvent->val = INPUT_VALUE_UNKONW; return 0; } else return -1; return 0; } static T_InputOpr s_tTouchScreenOpr = { .name = "touchscreen", .DeviceInit = TouchScreenDevInit, .DeviceExit = TouchScreenDevExit, .GetInputEvent = TouchScreenGetInputEvent, }; /** * @Description: touchscreen input设备初始化函数,供上层调用注册设备 * @return 成功:0 */ int TouchScreenInit(void) { return RegisterInputOpr(&s_tTouchScreenOpr); }执行make,得到可执行文件show_file
由于使用到触摸屏,需要调用tslib库来进行校准
执行./show_file -l,显示出当前支持的设备 执行./shoe_file -s 16 -h HZK16 -f ./MSYH.TTF hz.txt 通过串口输入,可以立刻根据输入的值进行操作,不需要回车键后才进行操作; 通过触摸屏输入,点击屏幕的上1/3部分进行上页显示,点击屏幕的下1/3部分进行下页显示。