第三阶段应用层——1.7 数码相册—电子书(3)—轮询方式支持多输入
数码相册——电子书轮询方式支持多输入
- 硬件平台:韦东山嵌入式Linxu开发板(S3C2440.v3)
- 软件平台:运行于VMware Workstation 12 Player下UbuntuLTS16.04_x64 系统
- 参考资料:《嵌入式Linux应用开发手册》、《嵌入式Linux应用开发手册第2版》、《gcc中文手册》
- 开发环境:Linux 3.4.2内核、arm-linux-gcc 4.3.2工具链
- 源码仓库:https://gitee.com/d_1254436976/Embedded-Linux-Phase-3
目录
- 数码相册——电子书轮询方式支持多输入
- 一、前言
- 二、框架图
- 1、软件框架图
- 2、Makefile框架图
- 三、input输入部分
- 1、管理者头文件`input_manager.h`
- 2、管理者实现文件`input_manager.c`
- 3、input设备——stdin
- 4、input设备——touchscreen
- 四、修改
- 1、main.c文件修改
- 1.1 显示支持信息部分
- 1.2 循环部分
- 2、draw.c文件修改
- 2.1 初始化部分修改
- 3、Makefile修改
- 3.1 /input目录下Makefile
- 3.2 顶层目录Makefile
- 五、编译与运行
- 1、编译
- 2、运行
一、前言
在【1.7 数码相册—电子书(1)—实现】和【1.7 数码相册—电子书(2)—编写通用的Makefile】这两篇博文中,我们实现了在开发板中显示电子书,但是仍然存在一些缺点,这节课针对这些缺点来进行改进。
- 实现:
1、电子书支持多种输入方式,如标准串口输入实现翻页退出操作,触摸LCD实现翻页操作;
2、在main.c
中采用轮询的方式支持多种输入方式。
二、框架图
1、软件框架图
对于上述完成主要功能的5个部分:encoding编码部分、fonts获取字体点阵部分、display显示部分、input输入部分、draw协调部分
- encoding编码部分:去文件中获得编码信息,对于每个文件,保存的时候,系统对自动或手动的根据编码规范,对文件的信息进行编码:如保存为
ASCII、GBK、UTF-8、UTF16LE、UTF16BE
; - fonts获取字体点阵部分:根据获得的编码信息得到字体数据(LCD上表示为点阵);
- display显示部分:把字体数据(点阵)显示在LCD上;
- input输入部分:管理不同的输入方式,定制不同的输入方式所实现的控制,满足多种控制场合;
- draw协调部分 :组织各个模块部分进行合作,实现电子书的显示、翻页功能等。
2、Makefile框架图
由于整个分为如下3部分:顶层目录的Makefile、顶层目录的Makefile.build、各级子目录的Makefile。
所以对于新添加的input输入部分需要进行如下修改:其目录下也需要添加该目录下的Makefile,对于顶层目录的Makeifle
也需要添加obj-y += /input
,具体的在编译的时候会介绍。
而对于顶层的Makefile.build
,不需要进行修改,因为它是一个比较通用的文件,根据顶层目录的Makefile信息进入到各个子目录下进行预处理、编译、汇编,最后每个子目录与顶层得到的built-in.o
进行链接,最后得到可执行文件show_file
.
下面就来介绍添加的input输入部分。
三、input输入部分
1、管理者头文件input_manager.h
在这个文件:
- 定义一个结构体变量,拓展文件可以根据自己的情况:分配结构体、设置结构体、注册结构体;
- 定义一些函数,供外部文件调用。
#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 */
2、管理者实现文件input_manager.c
- 注册函数
每个输入方式的结构体,可以调用这个函数把结构体注册到链表中。
/* 函数名: 注册函数* 函数功能:构建一个链表:把多个拓展文件的结构体“串”起来* 函数实现:根据传入的结点,首先判断该链表头是否为空* 空则,头结点指向传入的节点,且把节点的ptNext域指向NULL* 不空则,尾插法插入链表*/
int RegisterInputOpr(PT_InputOpr ptInputOpr)
{PT_InputOpr ptTmp;if (!s_ptInputOprHead) {s_ptInputOprHead = ptInputOpr;ptInputOpr->ptNext = NULL;} else {ptTmp = s_ptInputOprHead;while (ptTmp->ptNext){ptTmp = ptTmp->ptNext;}ptTmp->ptNext = ptInputOpr;ptInputOpr->ptNext = NULL;}return 0;
}
- 显示函数
通过这个函数,可以把链表中所有input方式的名字打印出来,供用户查看所支持的input方式。
/* 显示支持拓展文件的名字 */
void ShowInputOpr(void)
{int i = 0;PT_InputOpr ptTmp = s_ptInputOprHead;while (ptTmp) {printf("%02d %s\n", i++, ptTmp->name);ptTmp = ptTmp->ptNext;}
}
- 初始化函数
调用此函数,可以把对应的input方式结构体放入链表中。
/* 初始化函数 */
int InputInit(void)
{int error;error = StdinInit();error |= TouchScreenInit();return error;
}
- 调用input设备事件函数
/* 调用所有的Input设备的事件发生函数,即轮询的方式获取LCD或标准输入的数据 */
int GetInputEvent(PT_InputEvent ptInputEvent)
{/* 把链表中的InputOpr的GetInputEvent都调用一次,一旦有数据立即返回 */PT_InputOpr ptTmp;ptTmp = s_ptInputOprHead;while (ptTmp) {if (ptTmp->GetInputEvent(ptInputEvent) == 0)return 0;ptTmp = ptTmp->ptNext;}return -1;
}
- 设备初始化函数
初始化每个设备的函数(设置input设备、调整模式等)
/* 初始化所有支持的Input设备 */
int AllInputDeviceInit()
{int error;PT_InputOpr ptTmp;error = -1;ptTmp = s_ptInputOprHead;while (ptTmp) {if (ptTmp->DeviceInit() == 0)error = 0;ptTmp = ptTmp->ptNext;}return 0;
}
- 完整文件
#include <config.h>
#include <string.h>
#include <stdlib.h>#include "input_manager.h"static PT_InputOpr s_ptInputOprHead; //链表头/* 函数名: 注册函数* 函数功能:构建一个链表:把多个拓展文件的结构体“串”起来* 函数实现:根据传入的结点,首先判断该链表头是否为空* 空则,头结点指向传入的节点,且把节点的ptNext域指向NULL* 不空则,尾插法插入链表*/
int RegisterInputOpr(PT_InputOpr ptInputOpr)
{PT_InputOpr ptTmp;if (!s_ptInputOprHead) {s_ptInputOprHead = ptInputOpr;ptInputOpr->ptNext = NULL;} else {ptTmp = s_ptInputOprHead;while (ptTmp->ptNext){ptTmp = ptTmp->ptNext;}ptTmp->ptNext = ptInputOpr;ptInputOpr->ptNext = NULL;}return 0;
}/* 显示支持拓展文件的名字 */
void ShowInputOpr(void)
{int i = 0;PT_InputOpr ptTmp = s_ptInputOprHead;while (ptTmp) {printf("%02d %s\n", i++, ptTmp->name);ptTmp = ptTmp->ptNext;}
}/* 初始化函数 */
int InputInit(void)
{int error;error = StdinInit();error |= TouchScreenInit();return error;
}/* 调用所有的Input设备的事件发生函数,即轮询的方式获取LCD或标准输入的数据 */
int GetInputEvent(PT_InputEvent ptInputEvent)
{/* 把链表中的InputOpr的GetInputEvent都调用一次,一旦有数据立即返回 */PT_InputOpr ptTmp;ptTmp = s_ptInputOprHead;while (ptTmp) {if (ptTmp->GetInputEvent(ptInputEvent) == 0)return 0;ptTmp = ptTmp->ptNext;}return -1;
}/* 初始化所有支持的Input设备 */
int AllInputDeviceInit()
{int error;PT_InputOpr ptTmp;error = -1;ptTmp = s_ptInputOprHead;while (ptTmp) {if (ptTmp->DeviceInit() == 0)error = 0;ptTmp = ptTmp->ptNext;}return 0;
}
3、input设备——stdin
在这个函数中,为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;}elsereturn -1;
}static T_InputOpr s_tStdinOpr = {.name = "stdin",.DeviceInit = StdinDevInit,.DeviceExit = StdinDevExit,.GetInputEvent = StdinGetInputEvent,
};/*** @Description: 标准输入初始化函数,供上层调用注册设备* @return 成功:0*/
int StdinInit(void)
{return RegisterInputOpr(&s_tStdinOpr);
}
4、input设备——touchscreen
在这个函数中,为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);elses_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;elseptInputEvent->val = INPUT_VALUE_UNKONW;return 0;} elsereturn -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);
}
四、修改
1、main.c文件修改
1.1 显示支持信息部分
- 修改前:
```c/* 显示当前支持信息 */if (List){printf("supported display:\n");ShowDispOpr();printf("supported font:\n");ShowFontOpr();printf("supported encoding:\n");ShowEncodingOpr();return 0;}
- 修改后:
/* 显示当前支持信息 */if (List){printf("supported display:\n");ShowDispOpr();printf("supported font:\n");ShowFontOpr();printf("supported encoding:\n");ShowEncodingOpr();printf("supported input:\n");ShowInputOpr();return 0;}
1.2 循环部分
- 修改前:只支持串口标准输入控制
/* 循环根据输入信息进行对应操作* n: 显示下一页信息* u: 显示上一页信息* q: 退出程序*/while (1){printf("Enter 'n' to show next page, 'u' to show previous page, 'q' to exit: ");do {Opr = getchar(); } while ((Opr != 'n') && (Opr != 'u') && (Opr != 'q'));if (Opr == 'n')ShowNextPage();else if (Opr == 'u')ShowPrePage(); else return 0; }
- 修改后:通过轮询的方式,调用
GetInputEvent(&InputEvent)
,不断的判断串口和LCD是否有输入,根据输入的标志位来进行相关操作。
/* 循环根据输入信息进行对应操作* n: 显示下一页信息* u: 显示上一页信息* q: 退出程序*/printf("Enter 'n' to show next page, 'u' to show previous page, 'q' to exit\n\r");printf("Touch the upper third of the touch screen to show previous page\n\r");printf("Touch the bottom third of the touch screen to show next page\n\r");while (1){if (GetInputEvent(&InputEvent) == 0) {if (InputEvent.val == INPUT_VALUE_DOWN)ShowNextPage();else if (InputEvent.val == INPUT_VALUE_UP)ShowPrePage(); else if (InputEvent.val == INPUT_VALUE_EXIT) {printf("\n\r");return 0;}}}
2、draw.c文件修改
2.1 初始化部分修改
- 修改前:
int DrawInit(void)
{int error;/* 初始化 */error = DisplayInit();if (error) {printf("DisplayInit error!\n");return -1;}error = FontsInit();if (error) {printf("FontsInit error!\n");return -1;}error = EncodingInit();if (error) {printf("EncodingInit error!\n");return -1;}return 0;
}
- 修改后:添加了input部分的初始化
int DrawInit(void)
{int error;/* 初始化 */error = DisplayInit();if (error) {printf("DisplayInit error!\n");return -1;}error = FontsInit();if (error) {printf("FontsInit error!\n");return -1;}error = EncodingInit();if (error) {printf("EncodingInit error!\n");return -1;}error = InputInit();if (error) {printf("InputInit error!\n");return -1;}return 0;
}
3、Makefile修改
3.1 /input目录下Makefile
# 子目录Makefileobj-y += input_manager.o
obj-y += stdin.o
obj-y += touchscreen.o
3.2 顶层目录Makefile
- 添加库:
LDFLAGS := -lm -lfreetype -lts
- 添加input目录:
obj-y += input/
- 完整文件:
# 顶层目录Makefile
#
# 目的:
# 1、每个子目录都会建立一个Makefile,包含当前目录下.c文件.h文件
# 2、顶层目录下# 交叉编译工具链
CROSS_COMPILE = arm-linux-# 定义一些变量 $(CROSS_COMPILE):取出该变量的值 $(CROSS_COMPILE)gcc->arm-linux-gcc
AS = $(CROSS_COMPILE)as
LD = $(CROSS_COMPILE)ld
CC = $(CROSS_COMPILE)gcc
CPP = $(CC) -E
AR = $(CROSS_COMPILE)ar
NM = $(CROSS_COMPILE)nmSTRIP = $(CROSS_COMPILE)strip
OBJCOPY = $(CROSS_COMPILE)objcopy
OBJDUMP = $(CROSS_COMPILE)objdump# 取出变量的值
export AS LD CC CPP AR NM
export STRIP OBJCOPY OBJDUMP# := 简单扩展型变量的值是一次扫描永远使用
# CFLAGS 方便您利用隐含规则指定编译C语言源程序的旗标
# -Wall 帮助列出所有警告信息
# -02 多优化一些,除了涉及空间和速度交换的优化选项,执行几乎所有的优化工作
# -g 可以认为它是缺省推荐的选项
CFLAGS := -Wall -O2 -g# 把当前目录下的include目录下指定为系统目录
# += 为已经定以过的变量的值追加更多的文本
# -I 指定搜寻包含makefile文件的路径
# $(shell pwd) 执行shell命令的pwd命令,获取执行命令的结果
CFLAGS += -I $(shell pwd)/include# LDFLAGS 用于调用linker(‘ld’)的编译器的额外标志
# -l 连接库的搜寻目录 -lm调用math库 -lfreetype调用freetype库
LDFLAGS := -lm -lfreetype -lts# 取出变量的值
export CFLAGS LDFLAGSTOPDIR := $(shell pwd)
export TOPDIR# TARGET 目标变量
# := 简单扩展型变量的值是一次扫描永远使使用
TARGET := show_fileobj-y += main.o
obj-y += display/
obj-y += draw/
obj-y += encoding/
obj-y += fonts/
obj-y += input/# 规则 arm-linux-gcc -lm -lfreetype -o show_file built-in.o
all : make -C ./ -f $(TOPDIR)/Makefile.build$(CC) $(LDFLAGS) -o $(TARGET) built-in.o# clean 删除所有make正常创建的文件
# 规则 找到所有make创建出来.o文件进行删除
# 删除show_file
clean :rm -f $(shell find -name "*.o")rm -f $(TARGET)# distclean 删除所有正常创建的文件
# 配置文件或为编译正常创建的准备文件,甚至makefile文件自身不能创建的文件
# 规则 找到所有mkae创建出来的.o文件进行删除
# 删除show_file
distclean:rm -f $(shell find -name "*.o")rm -f $(shell find -name "*.d")rm -f $(TARGET)
五、编译与运行
1、编译
执行make
,得到可执行文件show_file
2、运行
由于使用到触摸屏,需要调用tslib
库来进行校准
- 执行
./show_file -l
,显示出当前支持的设备
- 执行
./shoe_file -s 16 -h HZK16 -f ./MSYH.TTF hz.txt
通过串口输入,可以立刻根据输入的值进行操作,不需要回车键后才进行操作;
通过触摸屏输入,点击屏幕的上1/3部分进行上页显示,点击屏幕的下1/3部分进行下页显示。
第三阶段应用层——1.7 数码相册—电子书(3)—轮询方式支持多输入相关推荐
- 第三阶段应用层——2.6 视频监控—CMOS摄像头的硬件原理
视频监控-CMOS摄像头的硬件原理 硬件平台:韦东山嵌入式Linxu开发板(S3C2440.v3) 软件平台:运行于VMware Workstation 12 Player下UbuntuLTS16.0 ...
- 第三阶段应用层——2.7 视频监控—从零写CMOS摄像头驱动
视频监控-从零写CMOS摄像头驱动 硬件平台:韦东山嵌入式Linxu开发板(S3C2440.v3),OV7740摄像头 软件平台:运行于VMware Workstation 12 Player下Ubu ...
- 第三阶段应用层——2.4 视频监控—从0写USB摄像头驱动(1)-描述符的分析与打印
视频监控-从0写USB摄像头驱动(1)-描述符的分析与打印 硬件平台:韦东山嵌入式Linxu开发板(S3C2440.v3) 软件平台:运行于VMware Workstation 12 Player下U ...
- Linux运维 第三阶段 (一) 网络配置及openssl加密
Linux运维 第三阶段 (一) 网络配置及openssl加密 主机接入网络:IP,netmask,gateway,hostname,DNS1,DNS2,DNS3,route,dhcp(dynamic ...
- mysql三阶段提交实现_基于两阶段提交的分布式事务实现(UP-2PC)
引言:分布式事务是分布式数据库的基础性功能,在2017年上海MySQL嘉年华(IMG)和中国数据库大会(DTCC2018)中作者都对银联UPSQL Proxy的分布式事务做了简要介绍,受限于交流形式难 ...
- 中国石油大学(北京)-《计算机网络应用基础》第三阶段在线作业
第三阶段在线作业 单选题 (共20道题) 收起 1.(2.5分) 以下关于VPN说法正确的是 A.VPN指的是用户自己租用线路,和公共网络物理上完全隔离的.安全的线路 B.VPN指的是用户通过公用网络 ...
- 中国石油大学《计算机应用基础#》第三阶段在线作业
第三阶段在线作业 单选题 (共20道题) 收起 1.(2.5分) PowerPoint演示文稿的作者必须非常注意幻灯片集的两个要素是(). A.内容和设计 B.内容和模板 C.内容和视觉效果 D.问题 ...
- java 高并发第三阶段实战_JAVA多线程编程实战视频-第三阶段(共80节)
高并发编程第三阶段01讲 AtomicInteger多线程下测试讲解 高并发编程第三阶段02讲 AtomicInteger API详解,以及CAS算法详细介绍 高并发编程第三阶段03讲 利用CAS构造 ...
- 自学it18大数据笔记-第三阶段Spark-day04——会持续更新……
笔记为自学时随手记录,如有错误,欢迎指正,不胜感激!现已广州转移至上海,欢迎小伙伴们加qq或微博沟通交流(QQ,微博和博客同名) 笔记分享:自学it18大数据笔记-第三阶段Spark-day04--会 ...
- 5G全球声量升级:Verizon宣布固定+移动齐步走,中国第三阶段试验倒计时
就在CES2018国际消费类电子产品展和2018世界移动大会即将召开的时候,5G的信号在全球范围也来了一次集体释放. 1月2日,工业和信息化部召开评审会,为有效指导5G第三阶段试验做准备.同一天,韩国 ...
最新文章
- 通过sessionid获取session php,php如何返回sessionID和如何通过sessionID获取相关的session...
- 一个演示A星相关的寻路的网站
- 近几年,关于基于Imagenet数据集图像分类的模型总结
- VTK:IO之GenericDataObjectReader
- yum 升级curl_CentOS 6/7 更新curl
- 《从缺陷中学习C/C++》——6.3 数组传参时的sizeof
- MySQL 四种事务隔离级的说明
- 阶段5 3.微服务项目【学成在线】_day02 CMS前端开发_22-CMS前端页面查询开发-Api调用-跨域解决...
- nginx 多php项目配置文件,nginx,_nginx配置多目录访问,nginx - phpStudy
- 2021Eclipse最新下载与安装教程
- android 距离感应器控制屏幕熄灭_华为nova3e、小米6X、OPPOA7x对比,选IPS屏幕还是TFT屏幕,自选...
- UWB超宽带 DW1000 通道和带宽
- 我的游戏学习日志10——数字游戏策划(5)游戏策划的概念与分工
- 思维导图超级学习力提升宝典
- BUUCTF·[WUSTCTF2020]大数计算·WP
- noip模拟题11.5
- java 24字母_java 时间格式化中的模式字母
- 【原创达人】ZUK HD-1 双发声单元圈铁耳机
- 流氓软件卸载了又偷偷冒出来,dllhost.exe暗藏安装玄机
- project 2003