一、系统IO另外一个应用实例。----触摸屏
1、触摸屏设备文件
/dev/input/event0
2、关于触摸屏的一些概念
(1)事件 --event
当一些外接设备(比如鼠标、键盘、触摸屏 wifi 按键)接入到嵌入式平台(开发板)时,而且当这些设备的状态(触摸屏被摸了一下,键盘被按下 wifi 被连接)发生变化时,称之为事件的发生。
(2)输入子系统--input
当事件发生的时候,就是由输入子系统 帮助 系统自动计算这些事件产生的值。
3、如果我们想要把触摸屏的坐标读取出来 ?
(1)打开触摸屏设备文件
int tsfd = open("/dev/input/event0");
(2)分析 触摸屏的数据是什么数据类型?
int char
(3)读取触摸屏数据
read(tsfd,buf,1024);
(4)根据拿到的触摸屏数据做出相应的处理
if(x >200 && x<400 )
{
printf("打开qq\n");
}
(5)关闭触摸屏文件
4、触摸屏数据类型 到底 是什么?
如果想要知道从文件event0中读取出来的数据是什么类型,首先必须先知道输入子系统计算完这个结果之后,这个结果是以什么方式存放在event0这个文件。
(1)在linux中,输入子系统如何描述一个事件?
使用结构体去描述,该结构体是被定义在 /usr/include/linux/input.h:
事件结构体
struct input_event {
struct timeval time; 事件发生的时间
__u16 type; 事件的类型
__u16 code; 事件的编码
__s32 value; 事件的值
};
(2)事件的类型:
/*
* Event types
*/
#define EV_SYN 0x00 异步通信
#define EV_KEY 0x01 按键事件 压力事件
#define EV_REL 0x02
#define EV_ABS 0x03 触摸屏事件
#define EV_MSC 0x04
#define EV_SW 0x05
#define EV_LED 0x11
#define EV_SND 0x12
#define EV_REP 0x14
#define EV_FF 0x15
#define EV_PWR 0x16
#define EV_FF_STATUS 0x17
#define EV_MAX 0x1f
#define EV_CNT (EV_MAX+1)
(3)code 事件的编码(对事件进行进一步描述):
#define ABS_X 0x00 触摸屏事件发生之后,进一步描述是X轴事件
#define ABS_Y 0x01 触摸屏事件发生之后,进一步描述是Y轴事件
#define BTN_TOUCH 0x14a 压力事件 330
(4)value; 事件的值:
BTN_TOUCH 压力事件中
按下 value 1
松开 value 0
5、练习
(1)用手按一次,显示一次坐标,怎么做?自己将触摸屏的代码进行封装成一个接口(函数)。
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <sys/mman.h>
#include <linux/input.h>//触摸屏头文件
int ts_func(int *x,int *y)
{
//1)打开触摸屏设备文件 ---
int tsfd = open("/dev/input/event0",O_RDWR);
if(tsfd == -1)
{
printf("open /dev/input/event0 error\n");
return -1;
}
while(1)
{
//2)分析 触摸屏的数据是什么数据类型???
//输入子系统计算完结果之后会将这个结果存储在结构体中
struct input_event info;
//3)读取触摸屏数据
read(tsfd,&info,sizeof(struct input_event));
//判断当前发生的是触摸屏事件,接着判断触发的是X轴事件
if(info.type == EV_ABS && info.code == ABS_X)
*x = info.value;
//判断当前发生的是触摸屏事件,接着判断触发的是Y轴事件
if(info.type == EV_ABS && info.code == ABS_Y)
*y = info.value;
//松开的时候,才进行打印 type:1 code:330 value:0
if(info.type == EV_KEY && info.code == BTN_TOUCH && info.value == 0){
//如果你的板子是黑色的,那么需要进行坐标转换(1024,600) ---(800,480)
//*x = (*x *1.0)/1024 * 800 ;
//*y = (*y *1.0)/600 * 480 ;
printf("(%d,%d)\n",*x,*y);
break;
}
}
//5)关闭触摸屏文件
close(tsfd);
}
int main()
{
int ts_x,ts_y;
while(1)
{
ts_func(&ts_x,&ts_y); //调用一次,得到一个坐标值
if(ts_x<300)
printf("左边\n");
else if(ts_x>=300 && ts_x<600)
printf("右边\n");
else{
printf("退出\n");
break;
}
}
return 0;
}
结果:
(2)修改色盲检测系统:点击左边,显示上一种颜色,点击右边,显示下一种颜色。
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <sys/mman.h>
#include <linux/input.h>//触摸屏头文件
/*
练习2:修改色盲检测系统
点击左边,显示上一种颜色
点击右边,显示下一种颜色
*/
#define COLORCOUNT 4
#define BLACK 0x00000000//黑色
#define RED 0x00ff0000//红色
#define GREEN 0x0000ff00//绿色
#define YELLOW 0x00ffff00//黄色
int ts_func(int *x,int *y)
{
//1)打开触摸屏设备文件 ---
int tsfd = open("/dev/input/event0",O_RDWR);
if(tsfd == -1)
{
printf("open /dev/input/event0 error\n");
return -1;
}
while(1)
{
//2)分析 触摸屏的数据是什么数据类型???
//输入子系统计算完结果之后会将这个结果存储在结构体中
struct input_event info;
//3)读取触摸屏数据
read(tsfd,&info,sizeof(struct input_event));
//判断当前发生的是触摸屏事件,接着判断触发的是X轴事件
if(info.type == EV_ABS && info.code == ABS_X)
*x = info.value;
//判断当前发生的是触摸屏事件,接着判断触发的是Y轴事件
if(info.type == EV_ABS && info.code == ABS_Y)
*y = info.value;
//松开的时候,才进行打印 type:1 code:330 value:0
if(info.type == EV_KEY && info.code == BTN_TOUCH && info.value == 0){
//如果你的板子是黑色的,那么需要进行坐标转换(1024,600) ---(800,480)
//*x = (*x *1.0)/1024 * 800 ;
//*y = (*y *1.0)/600 * 480 ;
printf("(%d,%d)\n",*x,*y);
break;
}
}
//5)关闭触摸屏文件
close(tsfd);
}
int main()
{
//红色
int colorbuf[800*480];
int i;
int ts_x,ts_y;
//1、打开液晶屏文件/dev/fb0,
int lcdfd = open("/dev/fb0",O_RDWR);
if(lcdfd == -1)
{
printf("open lcd error\n");
return -1;
}
//将液晶屏文件 通过内存映射mmap的方式 映射到虚拟内存空间的某一块空间上,得到这一块内存的起始地址,后续我们操作这片内存空间,就相当于直接操作液晶屏文件
int *lcd_p = mmap(NULL, //你要映射的内存空间的起始地址,为NULL系统自动给你分配
800*480*4,//你要映射的内存空间的大小
PROT_READ|PROT_WRITE,//映射的权限
MAP_SHARED,//1、进程共享 2、对应的文件会同步发生变化
lcdfd,//映射液晶屏文件
0//偏移量 ,默认为0
);
//int lcd_p[800*480]; ---液晶屏数组
if(lcd_p == MAP_FAILED)
{
printf("mmap lcd error\n");
return -1;
}
int color[COLORCOUNT] = {BLACK,RED,GREEN,YELLOW};
int flag=0;
while(1)
{
ts_func(&ts_x,&ts_y); //调用一次,得到一个坐标值
//相当于直接操作液晶屏文件lcd_p
for(i=0; i<800*480; i++)
{
lcd_p[i] = color[flag];//
}
if(ts_x<400)//左边
{
printf("左边\n");
flag--;
//如果当前是在开头 flag== -1,那么点击左边,回到末尾
if(flag == -1)
flag = COLORCOUNT-1; //3
}
else{//右边
printf("右边\n");
flag++;
//如果当前是在末尾 flag == 4,那么点击右边,回到开头
if(flag == COLORCOUNT)
flag = 0;
}
}
//解除映射
munmap(lcd_p, 800*480*4);
//3、关闭文件
close(lcdfd);
}
二、标准IO
1、系统IO和标准IO的区别
(1)系统IO接口具有通用性 和 简约性,适用于绝大部分的文件,功能比较简洁,单一。一般用来访问硬件设备文件。
(2)标准IO接口功能丰富,其实是系统IO的二次封装,属于标准C库中的函数接口。 所以在man手册中第三章。一般用来访问普通文件。
2、如何使用标准IO访问普通文件?(fopen)
打开文件 --- man 3 fopen
头文件:
#include <stdio.h>
函数原型:
FILE *fopen(const char *path, const char *mode);
函数作用:
打开或者创建一个文件。
参数:
path---》你要打开的文件的路径名。 路径+名字
mode---》你要以什么方式打开这个文件。
r 以可读的方式打开文件. 文件光标在开头,文件必须要存在,如果不存在会返回失败
r+ 以可读可写的方式打开文件,文件光标在开头,文件必须要存在,如果不存在会返回失败
w 以可写的方式打开文件,如果文件不存在则创建,如果存在则清空. 文件光标在开头
w+ 以可读可写的方式打开文件. 如果文件不存在则创建,如果存在则清空.文件光标在开头
a 以追加的方式打开文件(可写). 如果文件不存在则创建. 文件光标在末尾
a+ 以追加的方式打开文件(可读可写). 如果文件不存在则创建. 文件光标在末尾
返回值:
成功返回 文件指针fp
失败返回 NULL
注意:
fopen("./1.txt", "rb+"); //b 以二进制的方式打开文件 ---》原封不动地读取 和 写入
fopen("./1.txt", "r+"); // 以文本的方式打开文件
linux: \n---占一个字符 0x0A window: \n 占两个字符
以后写程序时,fopen第二个参数 写上一个 b
rb+
wb+
wb
3、关闭文件(fclose)
#include <stdio.h>
int fclose(FILE *fp);
#include<stdio.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <sys/mman.h>
int main()
{
//打开文件
FILE *fp = fopen("./b.txt","w+");
if(fp == NULL)
{
printf("fopen error\n");
return -1;
}
//关闭文件
fclose(fp);
return 0;
}
三、默认打开的三个 文件
(1)系统IO ----文件描述符
其实在系统启动的时候,就会默认打开3个文件,分别是 “标准输入” “标准输出” "标准出错"。
这些其实就是 宏定义 ,在/usr/include/unistd.h 头文件中:
/* Standard file descriptors. */
#define STDIN_FILENO 0 /* Standard input. */ “标准输入” ---》对象:键盘
#define STDOUT_FILENO 1 /* Standard output. */ “标准输出” ---》对象:屏幕
#define STDERR_FILENO 2 /* Standard error output. */ "标准出错" ---》对象:屏幕
(2)标准IO-----文件指针
/* Standard streams. */
extern struct _IO_FILE *stdin; /* Standard input stream. */
extern struct _IO_FILE *stdout; /* Standard output stream. */
extern struct _IO_FILE *stderr; /* Standard error output stream. */
/* C89/C99 say they're macros. Make them happy. */
#define stdin stdin “标准输入” ---文件指针
#define stdout stdout “标准输出”
#define stderr stderr "标准出错"
总结:
文件描述符 文件指针
标准输入 STDIN_FILENO stdin
标准输出 STDOUT_FILENO stdout
标准出错 STDERR_FILENO stderr
四、关于标准IO的读写操作
1、按块数 读取文件(fread)
man 3 fread
#include <stdio.h>
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
参数:
ptr:将读取的数据存储到这里
size:每一块的大小
nmemb:多少块数据
stream: 你要读取哪个文件,将这个文件的文件指针传递进来
返回值:
函数正常返回读取到的块数,以下这几种情况将会返回不等于块数的返回值:
1、文件读到末尾了,剩余的内容不够读满那么多块,那么他就会返回读取到的多少块的整块,
而后面不满一块的内容,虽然也会读取,但是他不会记录在返回值中
判断文件是否读完了 feof int feof(FILE *stream);
2、文件读取出错了
判断文件是否出错 int ferror(FILE *stream);
比如:
例子1:
文件的大小 98个字节
fread(buf,20,5,fp); //read(fd,buf,100);
返回值:4
前面4块是整块,最后一块只有18个字节,不是整块,也会读取,但是不会记录在返回值中
文件的大小 98个字节
fread(buf,100,1,fp);
返回值: 0
#include<stdio.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <sys/mman.h>
int main()
{
//打开文件
FILE *fp = fopen("./b.txt","r");
if(fp == NULL)
{
printf("fopen error\n");
return -1;
}
char buf[1024]={0};
int ret = fread(buf,100,1,fp);
printf("ret:%d\n",ret);
printf("buf:%s\n",buf);
//关闭文件
fclose(fp);
return 0;
}
2、写入文件(fwrite)
man 3 fwrite
#include <stdio.h>
size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
参数:
ptr: 你要写入的数据
size:每一块的大小
nmemb:多少块数据
stream:你要将数据写入哪个文件,将这个文件的文件指针传递进来
返回值:
函数正常返回写入的块数,以下有一种情况返回不等于块数的返回值:
文件写入出错了
3、练习
使用标准IO实现 文件拷贝(./fcopy srcfile destfile)
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
//使用标准IO实现 文件拷贝
// ./fcopy srcfile destfile
#define MEMSIZE (1024*1024*5)
//程序内存的初始化
char *initMem()
{
char *buf = malloc(MEMSIZE); //buf 数据缓冲区
if(buf == NULL)
{
printf("malloc error\n");
return NULL;
}
return buf;
}
void freeMem(char *buf)
{
free(buf);
}
int main(int argc,char*argv[])
{
//1、先判断当前命令行有没有传输了源文件和目标文件
if(argc<3)
{
//如果没有传递进来,则提示
printf("请传输源文件 和 目标文件的名字\n");
return -1;
}
//2、以可读的方式 打开源文件
FILE *srcfp = fopen(argv[1],"rb");
if(srcfp == NULL)
{
printf("%s open error\n",argv[1]);
return -1;
}
//3、以可写的方式 打开目标文件,如果没有存在则创建,如果存在则清空
FILE *destfp = fopen(argv[2],"wb");
if(destfp == NULL)
{
printf("%s open error\n",argv[2]);
return -1;
}
char *buf = initMem(); //buf 数据缓冲区
if(buf == NULL)
{
printf("malloc error\n");
return -1;
}
int r_ret ;
int readByteCount=0;//记录读取的字节数
int pos1,pos2;
//4、循环地读取源文件数据
while(1)
{
bzero(buf,MEMSIZE);
//记录文件当前的位置
pos1 = ftell(srcfp);
//从源文件读取数据
r_ret = fread(buf,MEMSIZE/5,5,srcfp);
if(r_ret<5)//fread函数正常返回读取到的块数,以下这几种情况将会返回不等于块数的返回值
{
//如果源文件读取完了,则退出
if(feof(srcfp) != 0)
{
//再次记录文件的位置
pos2 = ftell(srcfp);
readByteCount += pos2-pos1;
printf("last byte:%d\n",pos2-pos1);
//把剩余的部分写入目标文件
fwrite(buf,pos2-pos1,1,destfp);
printf("已读:%f M\n",readByteCount*1.0/1024/1024);
break;
}
//2、文件读取出错了
if(ferror(srcfp) !=0)
{
printf("%s fread error\n",argv[1]);
break;
}
}
readByteCount += MEMSIZE;
//将读取出来的数据写入到目标文件
fwrite(buf,MEMSIZE/5,5,destfp);
printf("已读:%f M\n",readByteCount*1.0/1024/1024);
}
freeMem(buf);
//5、关闭两个文件
fclose(srcfp);
fclose(destfp);
return 0;
}